1 --Upgrade Script for 2.1 to 2.2-alpha2
3 -- Don't require use of -vegversion=something
4 \set eg_version '''2.2'''
6 -- DROP objects that might have existed from a prior run of 0526
8 DROP TABLE IF EXISTS config.db_patch_dependencies;
9 ALTER TABLE config.upgrade_log DROP COLUMN IF EXISTS applied_to;
10 DROP FUNCTION IF EXISTS evergreen.upgrade_list_applied_deprecates(TEXT);
11 DROP FUNCTION IF EXISTS evergreen.upgrade_list_applied_supersedes(TEXT);
14 INSERT INTO config.upgrade_log (version) VALUES ('2.2-beta2');
16 INSERT INTO config.upgrade_log (version) VALUES ('0526'); --miker
18 CREATE TABLE config.db_patch_dependencies (
19 db_patch TEXT PRIMARY KEY,
24 CREATE OR REPLACE FUNCTION evergreen.array_overlap_check (/* field */) RETURNS TRIGGER AS $$
30 EXECUTE 'SELECT COUNT(*) FROM '|| TG_TABLE_SCHEMA ||'.'|| TG_TABLE_NAME ||' WHERE '|| fld ||' && ($1).'|| fld INTO cnt USING NEW;
32 RAISE EXCEPTION 'Cannot insert duplicate array into field % of table %', fld, TG_TABLE_SCHEMA ||'.'|| TG_TABLE_NAME;
38 CREATE TRIGGER no_overlapping_sups
39 BEFORE INSERT OR UPDATE ON config.db_patch_dependencies
40 FOR EACH ROW EXECUTE PROCEDURE evergreen.array_overlap_check ('supersedes');
42 CREATE TRIGGER no_overlapping_deps
43 BEFORE INSERT OR UPDATE ON config.db_patch_dependencies
44 FOR EACH ROW EXECUTE PROCEDURE evergreen.array_overlap_check ('deprecates');
46 ALTER TABLE config.upgrade_log
47 ADD COLUMN applied_to TEXT;
49 -- Provide a named type for patching functions
50 CREATE TYPE evergreen.patch AS (patch TEXT);
52 -- List applied db patches that are deprecated by (and block the application of) my_db_patch
53 CREATE OR REPLACE FUNCTION evergreen.upgrade_list_applied_deprecates ( my_db_patch TEXT ) RETURNS SETOF evergreen.patch AS $$
54 SELECT DISTINCT l.version
55 FROM config.upgrade_log l
56 JOIN config.db_patch_dependencies d ON (l.version::TEXT[] && d.deprecates)
60 -- List applied db patches that are superseded by (and block the application of) my_db_patch
61 CREATE OR REPLACE FUNCTION evergreen.upgrade_list_applied_supersedes ( my_db_patch TEXT ) RETURNS SETOF evergreen.patch AS $$
62 SELECT DISTINCT l.version
63 FROM config.upgrade_log l
64 JOIN config.db_patch_dependencies d ON (l.version::TEXT[] && d.supersedes)
68 -- List applied db patches that deprecates (and block the application of) my_db_patch
69 CREATE OR REPLACE FUNCTION evergreen.upgrade_list_applied_deprecated ( my_db_patch TEXT ) RETURNS TEXT AS $$
71 FROM config.db_patch_dependencies
72 WHERE ARRAY[$1]::TEXT[] && deprecates
75 -- List applied db patches that supersedes (and block the application of) my_db_patch
76 CREATE OR REPLACE FUNCTION evergreen.upgrade_list_applied_superseded ( my_db_patch TEXT ) RETURNS TEXT AS $$
78 FROM config.db_patch_dependencies
79 WHERE ARRAY[$1]::TEXT[] && supersedes
82 -- Make sure that no deprecated or superseded db patches are currently applied
83 CREATE OR REPLACE FUNCTION evergreen.upgrade_verify_no_dep_conflicts ( my_db_patch TEXT ) RETURNS BOOL AS $$
85 FROM (SELECT * FROM evergreen.upgrade_list_applied_deprecates( $1 )
87 SELECT * FROM evergreen.upgrade_list_applied_supersedes( $1 )
89 SELECT * FROM evergreen.upgrade_list_applied_deprecated( $1 )
91 SELECT * FROM evergreen.upgrade_list_applied_superseded( $1 ))x
94 -- Raise an exception if there are, in fact, dep/sup confilct
95 CREATE OR REPLACE FUNCTION evergreen.upgrade_deps_block_check ( my_db_patch TEXT, my_applied_to TEXT ) RETURNS BOOL AS $$
100 IF NOT evergreen.upgrade_verify_no_dep_conflicts( my_db_patch ) THEN
101 SELECT STRING_AGG(patch, ', ') INTO deprecates FROM evergreen.upgrade_list_applied_deprecates(my_db_patch);
102 SELECT STRING_AGG(patch, ', ') INTO supersedes FROM evergreen.upgrade_list_applied_supersedes(my_db_patch);
104 Upgrade script % can not be applied:
105 applied deprecated scripts %
106 applied superseded scripts %
110 ARRAY_AGG(evergreen.upgrade_list_applied_deprecates(my_db_patch)),
111 ARRAY_AGG(evergreen.upgrade_list_applied_supersedes(my_db_patch)),
112 evergreen.upgrade_list_applied_deprecated(my_db_patch),
113 evergreen.upgrade_list_applied_superseded(my_db_patch);
116 INSERT INTO config.upgrade_log (version, applied_to) VALUES (my_db_patch, my_applied_to);
121 -- Evergreen DB patch 0536.schema.lazy_circ-barcode_lookup.sql
123 -- FIXME: insert description of change, if needed
126 -- check whether patch can be applied
127 INSERT INTO config.upgrade_log (version) VALUES ('0536');
129 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype) VALUES ( 'circ.staff_client.actor_on_checkout', 'Load patron from Checkout', 'When scanning barcodes into Checkout auto-detect if a new patron barcode is scanned and auto-load the new patron.', 'bool');
131 CREATE TABLE config.barcode_completion (
132 id SERIAL PRIMARY KEY,
133 active BOOL NOT NULL DEFAULT true,
134 org_unit INT NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
137 length INT NOT NULL DEFAULT 0,
139 padding_end BOOL NOT NULL DEFAULT false,
140 asset BOOL NOT NULL DEFAULT true,
141 actor BOOL NOT NULL DEFAULT true
144 CREATE TYPE evergreen.barcode_set AS (type TEXT, id BIGINT, barcode TEXT);
146 CREATE OR REPLACE FUNCTION evergreen.get_barcodes(select_ou INT, type TEXT, in_barcode TEXT) RETURNS SETOF evergreen.barcode_set AS $$
151 asset_barcodes TEXT[];
152 actor_barcodes TEXT[];
153 do_asset BOOL = false;
154 do_serial BOOL = false;
155 do_booking BOOL = false;
156 do_actor BOOL = false;
157 completion_set config.barcode_completion%ROWTYPE;
160 IF position('asset' in type) > 0 THEN
163 IF position('serial' in type) > 0 THEN
166 IF position('booking' in type) > 0 THEN
169 IF do_asset OR do_serial OR do_booking THEN
170 asset_barcodes = asset_barcodes || in_barcode;
172 IF position('actor' in type) > 0 THEN
174 actor_barcodes = actor_barcodes || in_barcode;
177 barcode_len := length(in_barcode);
179 FOR completion_set IN
180 SELECT * FROM config.barcode_completion
182 AND org_unit IN (SELECT aou.id FROM actor.org_unit_ancestors(select_ou) aou)
184 IF completion_set.prefix IS NULL THEN
185 completion_set.prefix := '';
187 IF completion_set.suffix IS NULL THEN
188 completion_set.suffix := '';
190 IF completion_set.length = 0 OR completion_set.padding IS NULL OR length(completion_set.padding) = 0 THEN
191 cur_barcode = completion_set.prefix || in_barcode || completion_set.suffix;
193 completion_len = completion_set.length - length(completion_set.prefix) - length(completion_set.suffix);
194 IF completion_len >= barcode_len THEN
195 IF completion_set.padding_end THEN
196 cur_barcode = rpad(in_barcode, completion_len, completion_set.padding);
198 cur_barcode = lpad(in_barcode, completion_len, completion_set.padding);
200 cur_barcode = completion_set.prefix || cur_barcode || completion_set.suffix;
203 IF completion_set.actor THEN
204 actor_barcodes = actor_barcodes || cur_barcode;
206 IF completion_set.asset THEN
207 asset_barcodes = asset_barcodes || cur_barcode;
211 IF do_asset AND do_serial THEN
212 RETURN QUERY SELECT 'asset'::TEXT, id, barcode FROM ONLY asset.copy WHERE barcode = ANY(asset_barcodes) AND deleted = false;
213 RETURN QUERY SELECT 'serial'::TEXT, id, barcode FROM serial.unit WHERE barcode = ANY(asset_barcodes) AND deleted = false;
215 RETURN QUERY SELECT 'asset'::TEXT, id, barcode FROM asset.copy WHERE barcode = ANY(asset_barcodes) AND deleted = false;
217 RETURN QUERY SELECT 'serial'::TEXT, id, barcode FROM serial.unit WHERE barcode = ANY(asset_barcodes) AND deleted = false;
220 RETURN QUERY SELECT 'booking'::TEXT, id::BIGINT, barcode FROM booking.resource WHERE barcode = ANY(asset_barcodes);
223 RETURN QUERY SELECT 'actor'::TEXT, c.usr::BIGINT, c.barcode FROM actor.card c JOIN actor.usr u ON c.usr = u.id WHERE c.barcode = ANY(actor_barcodes) AND c.active AND NOT u.deleted ORDER BY usr;
229 COMMENT ON FUNCTION evergreen.get_barcodes(INT, TEXT, TEXT) IS $$
230 Given user input, find an appropriate barcode in the proper class.
232 Will add prefix/suffix information to do so, and return all results.
237 INSERT INTO config.upgrade_log (version) VALUES ('0537'); --miker
239 DROP FUNCTION evergreen.upgrade_deps_block_check(text,text);
240 DROP FUNCTION evergreen.upgrade_verify_no_dep_conflicts(text);
241 DROP FUNCTION evergreen.upgrade_list_applied_deprecated(text);
242 DROP FUNCTION evergreen.upgrade_list_applied_superseded(text);
244 -- List applied db patches that deprecates (and block the application of) my_db_patch
245 CREATE FUNCTION evergreen.upgrade_list_applied_deprecated ( my_db_patch TEXT ) RETURNS SETOF TEXT AS $$
247 FROM config.db_patch_dependencies
248 WHERE ARRAY[$1]::TEXT[] && deprecates
251 -- List applied db patches that supersedes (and block the application of) my_db_patch
252 CREATE FUNCTION evergreen.upgrade_list_applied_superseded ( my_db_patch TEXT ) RETURNS SETOF TEXT AS $$
254 FROM config.db_patch_dependencies
255 WHERE ARRAY[$1]::TEXT[] && supersedes
258 -- Make sure that no deprecated or superseded db patches are currently applied
259 CREATE FUNCTION evergreen.upgrade_verify_no_dep_conflicts ( my_db_patch TEXT ) RETURNS BOOL AS $$
261 FROM (SELECT * FROM evergreen.upgrade_list_applied_deprecates( $1 )
263 SELECT * FROM evergreen.upgrade_list_applied_supersedes( $1 )
265 SELECT * FROM evergreen.upgrade_list_applied_deprecated( $1 )
267 SELECT * FROM evergreen.upgrade_list_applied_superseded( $1 ))x
270 -- Raise an exception if there are, in fact, dep/sup confilct
271 CREATE FUNCTION evergreen.upgrade_deps_block_check ( my_db_patch TEXT, my_applied_to TEXT ) RETURNS BOOL AS $$
273 IF NOT evergreen.upgrade_verify_no_dep_conflicts( my_db_patch ) THEN
275 Upgrade script % can not be applied:
276 applied deprecated scripts %
277 applied superseded scripts %
281 ARRAY_ACCUM(evergreen.upgrade_list_applied_deprecates(my_db_patch)),
282 ARRAY_ACCUM(evergreen.upgrade_list_applied_supersedes(my_db_patch)),
283 evergreen.upgrade_list_applied_deprecated(my_db_patch),
284 evergreen.upgrade_list_applied_superseded(my_db_patch);
287 INSERT INTO config.upgrade_log (version, applied_to) VALUES (my_db_patch, my_applied_to);
293 INSERT INTO config.upgrade_log (version) VALUES ('0544');
295 INSERT INTO config.usr_setting_type
296 ( name, opac_visible, label, description, datatype) VALUES
297 ( 'circ.collections.exempt',
299 oils_i18n_gettext('circ.collections.exempt', 'Collections: Exempt', 'cust', 'description'),
300 oils_i18n_gettext('circ.collections.exempt', 'User is exempt from collections tracking/processing', 'cust', 'description'),
306 SELECT evergreen.upgrade_deps_block_check('0545', :eg_version);
308 INSERT INTO permission.perm_list VALUES
309 (507, 'ABORT_TRANSIT_ON_LOST', oils_i18n_gettext(507, 'Allows a user to abort a transit on a copy with status of LOST', 'ppl', 'description')),
310 (508, 'ABORT_TRANSIT_ON_MISSING', oils_i18n_gettext(508, 'Allows a user to abort a transit on a copy with status of MISSING', 'ppl', 'description'));
312 --- stock Circulation Administrator group
314 INSERT INTO permission.grp_perm_map ( grp, perm, depth, grantable )
320 FROM permission.perm_list
321 WHERE code in ('ABORT_TRANSIT_ON_LOST', 'ABORT_TRANSIT_ON_MISSING');
323 -- Evergreen DB patch 0546.schema.sip_statcats.sql
326 -- check whether patch can be applied
327 SELECT evergreen.upgrade_deps_block_check('0546', :eg_version);
329 CREATE TABLE actor.stat_cat_sip_fields (
330 field CHAR(2) PRIMARY KEY,
332 one_only BOOL NOT NULL DEFAULT FALSE
334 COMMENT ON TABLE actor.stat_cat_sip_fields IS $$
335 Actor Statistical Category SIP Fields
337 Contains the list of valid SIP Field identifiers for
338 Statistical Categories.
340 ALTER TABLE actor.stat_cat
341 ADD COLUMN sip_field CHAR(2) REFERENCES actor.stat_cat_sip_fields(field) ON UPDATE CASCADE ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
342 ADD COLUMN sip_format TEXT;
344 CREATE FUNCTION actor.stat_cat_check() RETURNS trigger AS $func$
346 sipfield actor.stat_cat_sip_fields%ROWTYPE;
349 IF NEW.sip_field IS NOT NULL THEN
350 SELECT INTO sipfield * FROM actor.stat_cat_sip_fields WHERE field = NEW.sip_field;
351 IF sipfield.one_only THEN
352 SELECT INTO use_count count(id) FROM actor.stat_cat WHERE sip_field = NEW.sip_field AND id != NEW.id;
353 IF use_count > 0 THEN
354 RAISE EXCEPTION 'Sip field cannot be used twice';
360 $func$ LANGUAGE PLPGSQL;
362 CREATE TRIGGER actor_stat_cat_sip_update_trigger
363 BEFORE INSERT OR UPDATE ON actor.stat_cat FOR EACH ROW
364 EXECUTE PROCEDURE actor.stat_cat_check();
366 CREATE TABLE asset.stat_cat_sip_fields (
367 field CHAR(2) PRIMARY KEY,
369 one_only BOOL NOT NULL DEFAULT FALSE
371 COMMENT ON TABLE asset.stat_cat_sip_fields IS $$
372 Asset Statistical Category SIP Fields
374 Contains the list of valid SIP Field identifiers for
375 Statistical Categories.
378 ALTER TABLE asset.stat_cat
379 ADD COLUMN sip_field CHAR(2) REFERENCES asset.stat_cat_sip_fields(field) ON UPDATE CASCADE ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
380 ADD COLUMN sip_format TEXT;
382 CREATE FUNCTION asset.stat_cat_check() RETURNS trigger AS $func$
384 sipfield asset.stat_cat_sip_fields%ROWTYPE;
387 IF NEW.sip_field IS NOT NULL THEN
388 SELECT INTO sipfield * FROM asset.stat_cat_sip_fields WHERE field = NEW.sip_field;
389 IF sipfield.one_only THEN
390 SELECT INTO use_count count(id) FROM asset.stat_cat WHERE sip_field = NEW.sip_field AND id != NEW.id;
391 IF use_count > 0 THEN
392 RAISE EXCEPTION 'Sip field cannot be used twice';
398 $func$ LANGUAGE PLPGSQL;
400 CREATE TRIGGER asset_stat_cat_sip_update_trigger
401 BEFORE INSERT OR UPDATE ON asset.stat_cat FOR EACH ROW
402 EXECUTE PROCEDURE asset.stat_cat_check();
406 SELECT evergreen.upgrade_deps_block_check('0548', :eg_version); -- dbwells
408 \qecho This redoes the original part 1 of 0547 which did not apply to rel_2_1,
409 \qecho and is being added for the sake of clarity
411 -- delete errant inserts from 0545 (group 4 is NOT the circulation admin group)
412 DELETE FROM permission.grp_perm_map WHERE grp = 4 AND perm IN (
413 SELECT id FROM permission.perm_list
414 WHERE code in ('ABORT_TRANSIT_ON_LOST', 'ABORT_TRANSIT_ON_MISSING')
417 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable)
419 pgt.id, perm.id, aout.depth, TRUE
421 permission.grp_tree pgt,
422 permission.perm_list perm,
423 actor.org_unit_type aout
425 pgt.name = 'Circulation Administrator' AND
426 aout.name = 'Consortium' AND
428 'ABORT_TRANSIT_ON_LOST',
429 'ABORT_TRANSIT_ON_MISSING'
432 FROM permission.grp_perm_map AS map
435 AND map.perm = perm.id
438 -- Evergreen DB patch XXXX.data.transit-checkin-interval.sql
440 -- New org unit setting "circ.transit.min_checkin_interval"
441 -- New TRANSIT_CHECKIN_INTERVAL_BLOCK.override permission
445 -- check whether patch can be applied
446 SELECT evergreen.upgrade_deps_block_check('0549', :eg_version);
448 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype ) VALUES (
449 'circ.transit.min_checkin_interval',
451 'circ.transit.min_checkin_interval',
452 'Circ: Minimum Transit Checkin Interval',
457 'circ.transit.min_checkin_interval',
458 'In-Transit items checked in this close to the transit start time will be prevented from checking in',
465 INSERT INTO permission.perm_list ( id, code, description ) VALUES (
467 'TRANSIT_CHECKIN_INTERVAL_BLOCK.override',
470 'Allows a user to override the TRANSIT_CHECKIN_INTERVAL_BLOCK event',
476 -- add the perm to the default circ admin group
477 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable)
479 pgt.id, perm.id, aout.depth, TRUE
481 permission.grp_tree pgt,
482 permission.perm_list perm,
483 actor.org_unit_type aout
485 pgt.name = 'Circulation Administrator' AND
486 aout.name = 'System' AND
487 perm.code IN ( 'TRANSIT_CHECKIN_INTERVAL_BLOCK.override' );
490 -- check whether patch can be applied
491 SELECT evergreen.upgrade_deps_block_check('0550', :eg_version);
493 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype ) VALUES (
494 'org.patron_opt_boundary',
496 'org.patron_opt_boundary',
497 'Circ: Patron Opt-In Boundary',
502 'org.patron_opt_boundary',
503 'This determines at which depth above which patrons must be opted in, and below which patrons will be assumed to be opted in.',
510 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype ) VALUES (
511 'org.patron_opt_default',
513 'org.patron_opt_default',
514 'Circ: Patron Opt-In Default',
519 'org.patron_opt_default',
520 'This is the default depth at which a patron is opted in; it is calculated as an org unit relative to the current workstation.',
527 -- Evergreen DB patch 0562.schema.copy_active_date.sql
532 -- check whether patch can be applied
533 SELECT evergreen.upgrade_deps_block_check('0562', :eg_version);
535 ALTER TABLE asset.copy
536 ADD COLUMN active_date TIMESTAMP WITH TIME ZONE;
538 ALTER TABLE auditor.asset_copy_history
539 ADD COLUMN active_date TIMESTAMP WITH TIME ZONE;
541 ALTER TABLE auditor.serial_unit_history
542 ADD COLUMN active_date TIMESTAMP WITH TIME ZONE;
544 ALTER TABLE config.copy_status
545 ADD COLUMN copy_active BOOL NOT NULL DEFAULT FALSE;
547 ALTER TABLE config.circ_matrix_weights
548 ADD COLUMN item_age NUMERIC(6,2) NOT NULL DEFAULT 0.0;
550 ALTER TABLE config.hold_matrix_weights
551 ADD COLUMN item_age NUMERIC(6,2) NOT NULL DEFAULT 0.0;
553 -- The two defaults above were to stop erroring on NOT NULL
555 ALTER TABLE config.circ_matrix_weights
556 ALTER COLUMN item_age DROP DEFAULT;
558 ALTER TABLE config.hold_matrix_weights
559 ALTER COLUMN item_age DROP DEFAULT;
561 ALTER TABLE config.circ_matrix_matchpoint
562 ADD COLUMN item_age INTERVAL;
564 ALTER TABLE config.hold_matrix_matchpoint
565 ADD COLUMN item_age INTERVAL;
567 --Removed dupe asset.acp_status_changed
569 CREATE OR REPLACE FUNCTION asset.acp_created()
570 RETURNS TRIGGER AS $$
572 IF NEW.active_date IS NULL AND NEW.status IN (SELECT id FROM config.copy_status WHERE copy_active = true) THEN
573 NEW.active_date := now();
575 IF NEW.status_changed_time IS NULL THEN
576 NEW.status_changed_time := now();
582 CREATE TRIGGER acp_created_trig
583 BEFORE INSERT ON asset.copy
584 FOR EACH ROW EXECUTE PROCEDURE asset.acp_created();
586 CREATE TRIGGER sunit_created_trig
587 BEFORE INSERT ON serial.unit
588 FOR EACH ROW EXECUTE PROCEDURE asset.acp_created();
590 --Removed dupe action.hold_request_permit_test
592 CREATE OR REPLACE FUNCTION action.find_circ_matrix_matchpoint( context_ou INT, item_object asset.copy, user_object actor.usr, renewal BOOL ) RETURNS action.found_circ_matrix_matchpoint AS $func$
594 cn_object asset.call_number%ROWTYPE;
595 rec_descriptor metabib.rec_descriptor%ROWTYPE;
596 cur_matchpoint config.circ_matrix_matchpoint%ROWTYPE;
597 matchpoint config.circ_matrix_matchpoint%ROWTYPE;
598 weights config.circ_matrix_weights%ROWTYPE;
600 my_item_age INTERVAL;
601 denominator NUMERIC(6,2);
603 result action.found_circ_matrix_matchpoint;
606 result.success = false;
609 SELECT INTO cn_object * FROM asset.call_number WHERE id = item_object.call_number;
610 SELECT INTO rec_descriptor * FROM metabib.rec_descriptor WHERE record = cn_object.record;
612 -- Pre-generate this so we only calc it once
613 IF user_object.dob IS NOT NULL THEN
614 SELECT INTO user_age age(user_object.dob);
618 SELECT INTO my_item_age age(coalesce(item_object.active_date, now()));
620 -- Grab the closest set circ weight setting.
621 SELECT INTO weights cw.*
622 FROM config.weight_assoc wa
623 JOIN config.circ_matrix_weights cw ON (cw.id = wa.circ_weights)
624 JOIN actor.org_unit_ancestors_distance( context_ou ) d ON (wa.org_unit = d.id)
629 -- No weights? Bad admin! Defaults to handle that anyway.
630 IF weights.id IS NULL THEN
632 weights.org_unit := 10.0;
633 weights.circ_modifier := 5.0;
634 weights.marc_type := 4.0;
635 weights.marc_form := 3.0;
636 weights.marc_bib_level := 2.0;
637 weights.marc_vr_format := 2.0;
638 weights.copy_circ_lib := 8.0;
639 weights.copy_owning_lib := 8.0;
640 weights.user_home_ou := 8.0;
641 weights.ref_flag := 1.0;
642 weights.juvenile_flag := 6.0;
643 weights.is_renewal := 7.0;
644 weights.usr_age_lower_bound := 0.0;
645 weights.usr_age_upper_bound := 0.0;
646 weights.item_age := 0.0;
649 -- Determine the max (expected) depth (+1) of the org tree and max depth of the permisson tree
650 -- If you break your org tree with funky parenting this may be wrong
651 -- Note: This CTE is duplicated in the find_hold_matrix_matchpoint function, and it may be a good idea to split it off to a function
652 -- We use one denominator for all tree-based checks for when permission groups and org units have the same weighting
653 WITH all_distance(distance) AS (
654 SELECT depth AS distance FROM actor.org_unit_type
656 SELECT distance AS distance FROM permission.grp_ancestors_distance((SELECT id FROM permission.grp_tree WHERE parent IS NULL))
658 SELECT INTO denominator MAX(distance) + 1 FROM all_distance;
660 -- Loop over all the potential matchpoints
661 FOR cur_matchpoint IN
663 FROM config.circ_matrix_matchpoint m
664 /*LEFT*/ JOIN permission.grp_ancestors_distance( user_object.profile ) upgad ON m.grp = upgad.id
665 /*LEFT*/ JOIN actor.org_unit_ancestors_distance( context_ou ) ctoua ON m.org_unit = ctoua.id
666 LEFT JOIN actor.org_unit_ancestors_distance( cn_object.owning_lib ) cnoua ON m.copy_owning_lib = cnoua.id
667 LEFT JOIN actor.org_unit_ancestors_distance( item_object.circ_lib ) iooua ON m.copy_circ_lib = iooua.id
668 LEFT JOIN actor.org_unit_ancestors_distance( user_object.home_ou ) uhoua ON m.user_home_ou = uhoua.id
671 -- AND (m.grp IS NULL OR upgad.id IS NOT NULL) -- Optional Permission Group?
673 -- AND (m.org_unit IS NULL OR ctoua.id IS NOT NULL) -- Optional Org Unit?
674 AND (m.copy_owning_lib IS NULL OR cnoua.id IS NOT NULL)
675 AND (m.copy_circ_lib IS NULL OR iooua.id IS NOT NULL)
676 AND (m.user_home_ou IS NULL OR uhoua.id IS NOT NULL)
678 AND (m.is_renewal IS NULL OR m.is_renewal = renewal)
679 -- Static User Checks
680 AND (m.juvenile_flag IS NULL OR m.juvenile_flag = user_object.juvenile)
681 AND (m.usr_age_lower_bound IS NULL OR (user_age IS NOT NULL AND m.usr_age_lower_bound < user_age))
682 AND (m.usr_age_upper_bound IS NULL OR (user_age IS NOT NULL AND m.usr_age_upper_bound > user_age))
683 -- Static Item Checks
684 AND (m.circ_modifier IS NULL OR m.circ_modifier = item_object.circ_modifier)
685 AND (m.marc_type IS NULL OR m.marc_type = COALESCE(item_object.circ_as_type, rec_descriptor.item_type))
686 AND (m.marc_form IS NULL OR m.marc_form = rec_descriptor.item_form)
687 AND (m.marc_bib_level IS NULL OR m.marc_bib_level = rec_descriptor.bib_level)
688 AND (m.marc_vr_format IS NULL OR m.marc_vr_format = rec_descriptor.vr_format)
689 AND (m.ref_flag IS NULL OR m.ref_flag = item_object.ref)
690 AND (m.item_age IS NULL OR (my_item_age IS NOT NULL AND m.item_age > my_item_age))
693 CASE WHEN upgad.distance IS NOT NULL THEN 2^(2*weights.grp - (upgad.distance/denominator)) ELSE 0.0 END +
695 CASE WHEN ctoua.distance IS NOT NULL THEN 2^(2*weights.org_unit - (ctoua.distance/denominator)) ELSE 0.0 END +
696 CASE WHEN cnoua.distance IS NOT NULL THEN 2^(2*weights.copy_owning_lib - (cnoua.distance/denominator)) ELSE 0.0 END +
697 CASE WHEN iooua.distance IS NOT NULL THEN 2^(2*weights.copy_circ_lib - (iooua.distance/denominator)) ELSE 0.0 END +
698 CASE WHEN uhoua.distance IS NOT NULL THEN 2^(2*weights.user_home_ou - (uhoua.distance/denominator)) ELSE 0.0 END +
699 -- Circ Type -- Note: 4^x is equiv to 2^(2*x)
700 CASE WHEN m.is_renewal IS NOT NULL THEN 4^weights.is_renewal ELSE 0.0 END +
701 -- Static User Checks
702 CASE WHEN m.juvenile_flag IS NOT NULL THEN 4^weights.juvenile_flag ELSE 0.0 END +
703 CASE WHEN m.usr_age_lower_bound IS NOT NULL THEN 4^weights.usr_age_lower_bound ELSE 0.0 END +
704 CASE WHEN m.usr_age_upper_bound IS NOT NULL THEN 4^weights.usr_age_upper_bound ELSE 0.0 END +
705 -- Static Item Checks
706 CASE WHEN m.circ_modifier IS NOT NULL THEN 4^weights.circ_modifier ELSE 0.0 END +
707 CASE WHEN m.marc_type IS NOT NULL THEN 4^weights.marc_type ELSE 0.0 END +
708 CASE WHEN m.marc_form IS NOT NULL THEN 4^weights.marc_form ELSE 0.0 END +
709 CASE WHEN m.marc_vr_format IS NOT NULL THEN 4^weights.marc_vr_format ELSE 0.0 END +
710 CASE WHEN m.ref_flag IS NOT NULL THEN 4^weights.ref_flag ELSE 0.0 END +
711 -- Item age has a slight adjustment to weight based on value.
712 -- This should ensure that a shorter age limit comes first when all else is equal.
713 -- NOTE: This assumes that intervals will normally be in days.
714 CASE WHEN m.item_age IS NOT NULL THEN 4^weights.item_age - 1 + 86400/EXTRACT(EPOCH FROM m.item_age) ELSE 0.0 END DESC,
715 -- Final sort on id, so that if two rules have the same sorting in the previous sort they have a defined order
716 -- This prevents "we changed the table order by updating a rule, and we started getting different results"
719 -- Record the full matching row list
720 row_list := row_list || cur_matchpoint.id;
722 -- No matchpoint yet?
723 IF matchpoint.id IS NULL THEN
724 -- Take the entire matchpoint as a starting point
725 matchpoint := cur_matchpoint;
726 CONTINUE; -- No need to look at this row any more.
729 -- Incomplete matchpoint?
730 IF matchpoint.circulate IS NULL THEN
731 matchpoint.circulate := cur_matchpoint.circulate;
733 IF matchpoint.duration_rule IS NULL THEN
734 matchpoint.duration_rule := cur_matchpoint.duration_rule;
736 IF matchpoint.recurring_fine_rule IS NULL THEN
737 matchpoint.recurring_fine_rule := cur_matchpoint.recurring_fine_rule;
739 IF matchpoint.max_fine_rule IS NULL THEN
740 matchpoint.max_fine_rule := cur_matchpoint.max_fine_rule;
742 IF matchpoint.hard_due_date IS NULL THEN
743 matchpoint.hard_due_date := cur_matchpoint.hard_due_date;
745 IF matchpoint.total_copy_hold_ratio IS NULL THEN
746 matchpoint.total_copy_hold_ratio := cur_matchpoint.total_copy_hold_ratio;
748 IF matchpoint.available_copy_hold_ratio IS NULL THEN
749 matchpoint.available_copy_hold_ratio := cur_matchpoint.available_copy_hold_ratio;
751 IF matchpoint.renewals IS NULL THEN
752 matchpoint.renewals := cur_matchpoint.renewals;
754 IF matchpoint.grace_period IS NULL THEN
755 matchpoint.grace_period := cur_matchpoint.grace_period;
759 -- Check required fields
760 IF matchpoint.circulate IS NOT NULL AND
761 matchpoint.duration_rule IS NOT NULL AND
762 matchpoint.recurring_fine_rule IS NOT NULL AND
763 matchpoint.max_fine_rule IS NOT NULL THEN
764 -- All there? We have a completed match.
765 result.success := true;
768 -- Include the assembled matchpoint, even if it isn't complete
769 result.matchpoint := matchpoint;
771 -- Include (for debugging) the full list of matching rows
772 result.buildrows := row_list;
774 -- Hand the result back to caller
777 $func$ LANGUAGE plpgsql;
779 CREATE OR REPLACE FUNCTION action.find_hold_matrix_matchpoint(pickup_ou integer, request_ou integer, match_item bigint, match_user integer, match_requestor integer)
783 requestor_object actor.usr%ROWTYPE;
784 user_object actor.usr%ROWTYPE;
785 item_object asset.copy%ROWTYPE;
786 item_cn_object asset.call_number%ROWTYPE;
787 my_item_age INTERVAL;
788 rec_descriptor metabib.rec_descriptor%ROWTYPE;
789 matchpoint config.hold_matrix_matchpoint%ROWTYPE;
790 weights config.hold_matrix_weights%ROWTYPE;
791 denominator NUMERIC(6,2);
793 SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
794 SELECT INTO requestor_object * FROM actor.usr WHERE id = match_requestor;
795 SELECT INTO item_object * FROM asset.copy WHERE id = match_item;
796 SELECT INTO item_cn_object * FROM asset.call_number WHERE id = item_object.call_number;
797 SELECT INTO rec_descriptor * FROM metabib.rec_descriptor WHERE record = item_cn_object.record;
799 SELECT INTO my_item_age age(coalesce(item_object.active_date, now()));
801 -- The item's owner should probably be the one determining if the item is holdable
802 -- How to decide that is debatable. Decided to default to the circ library (where the item lives)
803 -- This flag will allow for setting it to the owning library (where the call number "lives")
804 PERFORM * FROM config.internal_flag WHERE name = 'circ.holds.weight_owner_not_circ' AND enabled;
806 -- Grab the closest set circ weight setting.
808 -- Default to circ library
809 SELECT INTO weights hw.*
810 FROM config.weight_assoc wa
811 JOIN config.hold_matrix_weights hw ON (hw.id = wa.hold_weights)
812 JOIN actor.org_unit_ancestors_distance( item_object.circ_lib ) d ON (wa.org_unit = d.id)
817 -- Flag is set, use owning library
818 SELECT INTO weights hw.*
819 FROM config.weight_assoc wa
820 JOIN config.hold_matrix_weights hw ON (hw.id = wa.hold_weights)
821 JOIN actor.org_unit_ancestors_distance( item_cn_object.owning_lib ) d ON (wa.org_unit = d.id)
827 -- No weights? Bad admin! Defaults to handle that anyway.
828 IF weights.id IS NULL THEN
829 weights.user_home_ou := 5.0;
830 weights.request_ou := 5.0;
831 weights.pickup_ou := 5.0;
832 weights.item_owning_ou := 5.0;
833 weights.item_circ_ou := 5.0;
834 weights.usr_grp := 7.0;
835 weights.requestor_grp := 8.0;
836 weights.circ_modifier := 4.0;
837 weights.marc_type := 3.0;
838 weights.marc_form := 2.0;
839 weights.marc_bib_level := 1.0;
840 weights.marc_vr_format := 1.0;
841 weights.juvenile_flag := 4.0;
842 weights.ref_flag := 0.0;
843 weights.item_age := 0.0;
846 -- Determine the max (expected) depth (+1) of the org tree and max depth of the permisson tree
847 -- If you break your org tree with funky parenting this may be wrong
848 -- Note: This CTE is duplicated in the find_circ_matrix_matchpoint function, and it may be a good idea to split it off to a function
849 -- We use one denominator for all tree-based checks for when permission groups and org units have the same weighting
850 WITH all_distance(distance) AS (
851 SELECT depth AS distance FROM actor.org_unit_type
853 SELECT distance AS distance FROM permission.grp_ancestors_distance((SELECT id FROM permission.grp_tree WHERE parent IS NULL))
855 SELECT INTO denominator MAX(distance) + 1 FROM all_distance;
857 -- To ATTEMPT to make this work like it used to, make it reverse the user/requestor profile ids.
858 -- This may be better implemented as part of the upgrade script?
859 -- Set usr_grp = requestor_grp, requestor_grp = 1 or something when this flag is already set
860 -- Then remove this flag, of course.
861 PERFORM * FROM config.internal_flag WHERE name = 'circ.holds.usr_not_requestor' AND enabled;
864 -- Note: This, to me, is REALLY hacky. I put it in anyway.
865 -- If you can't tell, this is a single call swap on two variables.
866 SELECT INTO user_object.profile, requestor_object.profile
867 requestor_object.profile, user_object.profile;
870 -- Select the winning matchpoint into the matchpoint variable for returning
871 SELECT INTO matchpoint m.*
872 FROM config.hold_matrix_matchpoint m
873 /*LEFT*/ JOIN permission.grp_ancestors_distance( requestor_object.profile ) rpgad ON m.requestor_grp = rpgad.id
874 LEFT JOIN permission.grp_ancestors_distance( user_object.profile ) upgad ON m.usr_grp = upgad.id
875 LEFT JOIN actor.org_unit_ancestors_distance( pickup_ou ) puoua ON m.pickup_ou = puoua.id
876 LEFT JOIN actor.org_unit_ancestors_distance( request_ou ) rqoua ON m.request_ou = rqoua.id
877 LEFT JOIN actor.org_unit_ancestors_distance( item_cn_object.owning_lib ) cnoua ON m.item_owning_ou = cnoua.id
878 LEFT JOIN actor.org_unit_ancestors_distance( item_object.circ_lib ) iooua ON m.item_circ_ou = iooua.id
879 LEFT JOIN actor.org_unit_ancestors_distance( user_object.home_ou ) uhoua ON m.user_home_ou = uhoua.id
882 -- AND (m.requestor_grp IS NULL OR upgad.id IS NOT NULL) -- Optional Requestor Group?
883 AND (m.usr_grp IS NULL OR upgad.id IS NOT NULL)
885 AND (m.pickup_ou IS NULL OR (puoua.id IS NOT NULL AND (puoua.distance = 0 OR NOT m.strict_ou_match)))
886 AND (m.request_ou IS NULL OR (rqoua.id IS NOT NULL AND (rqoua.distance = 0 OR NOT m.strict_ou_match)))
887 AND (m.item_owning_ou IS NULL OR (cnoua.id IS NOT NULL AND (cnoua.distance = 0 OR NOT m.strict_ou_match)))
888 AND (m.item_circ_ou IS NULL OR (iooua.id IS NOT NULL AND (iooua.distance = 0 OR NOT m.strict_ou_match)))
889 AND (m.user_home_ou IS NULL OR (uhoua.id IS NOT NULL AND (uhoua.distance = 0 OR NOT m.strict_ou_match)))
890 -- Static User Checks
891 AND (m.juvenile_flag IS NULL OR m.juvenile_flag = user_object.juvenile)
892 -- Static Item Checks
893 AND (m.circ_modifier IS NULL OR m.circ_modifier = item_object.circ_modifier)
894 AND (m.marc_type IS NULL OR m.marc_type = COALESCE(item_object.circ_as_type, rec_descriptor.item_type))
895 AND (m.marc_form IS NULL OR m.marc_form = rec_descriptor.item_form)
896 AND (m.marc_bib_level IS NULL OR m.marc_bib_level = rec_descriptor.bib_level)
897 AND (m.marc_vr_format IS NULL OR m.marc_vr_format = rec_descriptor.vr_format)
898 AND (m.ref_flag IS NULL OR m.ref_flag = item_object.ref)
899 AND (m.item_age IS NULL OR (my_item_age IS NOT NULL AND m.item_age > my_item_age))
902 CASE WHEN rpgad.distance IS NOT NULL THEN 2^(2*weights.requestor_grp - (rpgad.distance/denominator)) ELSE 0.0 END +
903 CASE WHEN upgad.distance IS NOT NULL THEN 2^(2*weights.usr_grp - (upgad.distance/denominator)) ELSE 0.0 END +
905 CASE WHEN puoua.distance IS NOT NULL THEN 2^(2*weights.pickup_ou - (puoua.distance/denominator)) ELSE 0.0 END +
906 CASE WHEN rqoua.distance IS NOT NULL THEN 2^(2*weights.request_ou - (rqoua.distance/denominator)) ELSE 0.0 END +
907 CASE WHEN cnoua.distance IS NOT NULL THEN 2^(2*weights.item_owning_ou - (cnoua.distance/denominator)) ELSE 0.0 END +
908 CASE WHEN iooua.distance IS NOT NULL THEN 2^(2*weights.item_circ_ou - (iooua.distance/denominator)) ELSE 0.0 END +
909 CASE WHEN uhoua.distance IS NOT NULL THEN 2^(2*weights.user_home_ou - (uhoua.distance/denominator)) ELSE 0.0 END +
910 -- Static User Checks -- Note: 4^x is equiv to 2^(2*x)
911 CASE WHEN m.juvenile_flag IS NOT NULL THEN 4^weights.juvenile_flag ELSE 0.0 END +
912 -- Static Item Checks
913 CASE WHEN m.circ_modifier IS NOT NULL THEN 4^weights.circ_modifier ELSE 0.0 END +
914 CASE WHEN m.marc_type IS NOT NULL THEN 4^weights.marc_type ELSE 0.0 END +
915 CASE WHEN m.marc_form IS NOT NULL THEN 4^weights.marc_form ELSE 0.0 END +
916 CASE WHEN m.marc_vr_format IS NOT NULL THEN 4^weights.marc_vr_format ELSE 0.0 END +
917 CASE WHEN m.ref_flag IS NOT NULL THEN 4^weights.ref_flag ELSE 0.0 END +
918 -- Item age has a slight adjustment to weight based on value.
919 -- This should ensure that a shorter age limit comes first when all else is equal.
920 -- NOTE: This assumes that intervals will normally be in days.
921 CASE WHEN m.item_age IS NOT NULL THEN 4^weights.item_age - 86400/EXTRACT(EPOCH FROM m.item_age) ELSE 0.0 END DESC,
922 -- Final sort on id, so that if two rules have the same sorting in the previous sort they have a defined order
923 -- This prevents "we changed the table order by updating a rule, and we started getting different results"
926 -- Return just the ID for now
927 RETURN matchpoint.id;
929 $func$ LANGUAGE 'plpgsql';
931 DROP INDEX IF EXISTS config.ccmm_once_per_paramset;
933 DROP INDEX IF EXISTS config.chmm_once_per_paramset;
935 CREATE UNIQUE INDEX ccmm_once_per_paramset ON config.circ_matrix_matchpoint (org_unit, grp, COALESCE(circ_modifier, ''), COALESCE(marc_type, ''), COALESCE(marc_form, ''), COALESCE(marc_bib_level,''), COALESCE(marc_vr_format, ''), COALESCE(copy_circ_lib::TEXT, ''), COALESCE(copy_owning_lib::TEXT, ''), COALESCE(user_home_ou::TEXT, ''), COALESCE(ref_flag::TEXT, ''), COALESCE(juvenile_flag::TEXT, ''), COALESCE(is_renewal::TEXT, ''), COALESCE(usr_age_lower_bound::TEXT, ''), COALESCE(usr_age_upper_bound::TEXT, ''), COALESCE(item_age::TEXT, '')) WHERE active;
937 CREATE UNIQUE INDEX chmm_once_per_paramset ON config.hold_matrix_matchpoint (COALESCE(user_home_ou::TEXT, ''), COALESCE(request_ou::TEXT, ''), COALESCE(pickup_ou::TEXT, ''), COALESCE(item_owning_ou::TEXT, ''), COALESCE(item_circ_ou::TEXT, ''), COALESCE(usr_grp::TEXT, ''), COALESCE(requestor_grp::TEXT, ''), COALESCE(circ_modifier, ''), COALESCE(marc_type, ''), COALESCE(marc_form, ''), COALESCE(marc_bib_level, ''), COALESCE(marc_vr_format, ''), COALESCE(juvenile_flag::TEXT, ''), COALESCE(ref_flag::TEXT, ''), COALESCE(item_age::TEXT, '')) WHERE active;
939 UPDATE config.copy_status SET copy_active = true WHERE id IN (0, 1, 7, 8, 10, 12, 15);
941 INSERT into config.org_unit_setting_type
942 ( name, label, description, datatype ) VALUES
943 ( 'circ.holds.age_protect.active_date', 'Holds: Use Active Date for Age Protection', 'When calculating age protection rules use the active date instead of the creation date.', 'bool');
945 -- Assume create date when item is in status we would update active date for anyway
946 UPDATE asset.copy SET active_date = create_date WHERE status IN (SELECT id FROM config.copy_status WHERE copy_active = true);
948 -- Assume create date for any item with circs
949 UPDATE asset.copy SET active_date = create_date WHERE id IN (SELECT id FROM extend_reporter.full_circ_count WHERE circ_count > 0);
951 -- Assume create date for status change time while we are at it. Because being created WAS a change in status.
952 UPDATE asset.copy SET status_changed_time = create_date WHERE status_changed_time IS NULL;
954 -- Evergreen DB patch 0564.data.delete_empty_volume.sql
956 -- New org setting cat.volume.delete_on_empty
959 -- check whether patch can be applied
960 SELECT evergreen.upgrade_deps_block_check('0564', :eg_version);
962 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype )
964 'cat.volume.delete_on_empty',
965 oils_i18n_gettext('cat.volume.delete_on_empty', 'Cat: Delete volume with last copy', 'coust', 'label'),
966 oils_i18n_gettext('cat.volume.delete_on_empty', 'Automatically delete a volume when the last linked copy is deleted', 'coust', 'description'),
971 -- Evergreen DB patch 0565.schema.action-trigger.event_definition.hold-cancel-no-target-notification.sql
973 -- New action trigger event definition: Hold Cancelled (No Target) Email Notification
976 -- check whether patch can be applied
977 SELECT evergreen.upgrade_deps_block_check('0565', :eg_version);
979 INSERT INTO action_trigger.event_definition (id, active, owner, name, hook, validator, reactor, delay, delay_field, group_field, template)
980 VALUES (38, FALSE, 1,
981 'Hold Cancelled (No Target) Email Notification',
982 'hold_request.cancel.expire_no_target',
983 'HoldIsCancelled', 'SendEmail', '30 minutes', 'cancel_time', 'usr',
986 [%- user = target.0.usr -%]
987 To: [%- params.recipient_email || user.email %]
988 From: [%- params.sender_email || default_sender %]
989 Subject: Hold Request Cancelled
991 Dear [% user.family_name %], [% user.first_given_name %]
992 The following holds were cancelled because no items were found to fullfil the hold.
994 [% FOR hold IN target %]
995 Title: [% hold.bib_rec.bib_record.simple_record.title %]
996 Author: [% hold.bib_rec.bib_record.simple_record.author %]
997 Library: [% hold.pickup_lib.name %]
998 Request Date: [% date.format(helpers.format_date(hold.rrequest_time), '%Y-%m-%d') %]
1003 INSERT INTO action_trigger.environment (event_def, path) VALUES
1006 (38, 'bib_rec.bib_record.simple_record');
1008 -- Evergreen DB patch XXXX.data.ou_setting_generate_overdue_on_lost.sql.sql
1010 -- check whether patch can be applied
1011 SELECT evergreen.upgrade_deps_block_check('0567', :eg_version);
1013 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype ) VALUES (
1014 'circ.lost.generate_overdue_on_checkin',
1016 'circ.lost.generate_overdue_on_checkin',
1017 'Circ: Lost Checkin Generates New Overdues',
1022 'circ.lost.generate_overdue_on_checkin',
1023 'Enabling this setting causes retroactive creation of not-yet-existing overdue fines on lost item checkin, up to the point of checkin time (or max fines is reached). This is different than "restore overdue on lost", because it only creates new overdue fines. Use both settings together to get the full complement of overdue fines for a lost item',
1030 -- Evergreen DB patch 0572.vandelay-record-matching-and-quality.sql
1034 -- check whether patch can be applied
1035 SELECT evergreen.upgrade_deps_block_check('0572', :eg_version);
1037 CREATE OR REPLACE FUNCTION evergreen.array_remove_item_by_value(inp ANYARRAY, el ANYELEMENT) RETURNS anyarray AS $$ SELECT ARRAY_ACCUM(x.e) FROM UNNEST( $1 ) x(e) WHERE x.e <> $2; $$ LANGUAGE SQL;
1039 CREATE TABLE vandelay.match_set (
1040 id SERIAL PRIMARY KEY,
1042 owner INT NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE,
1043 mtype TEXT NOT NULL DEFAULT 'biblio', -- 'biblio','authority','mfhd'?, others?
1044 CONSTRAINT name_once_per_owner_mtype UNIQUE (name, owner, mtype)
1047 -- Table to define match points, either FF via SVF or tag+subfield
1048 CREATE TABLE vandelay.match_set_point (
1049 id SERIAL PRIMARY KEY,
1050 match_set INT REFERENCES vandelay.match_set (id) ON DELETE CASCADE,
1051 parent INT REFERENCES vandelay.match_set_point (id),
1052 bool_op TEXT CHECK (bool_op IS NULL OR (bool_op IN ('AND','OR','NOT'))),
1053 svf TEXT REFERENCES config.record_attr_definition (name),
1056 negate BOOL DEFAULT FALSE,
1057 quality INT NOT NULL DEFAULT 1, -- higher is better
1058 CONSTRAINT vmsp_need_a_subfield_with_a_tag CHECK ((tag IS NOT NULL AND subfield IS NOT NULL) OR tag IS NULL),
1059 CONSTRAINT vmsp_need_a_tag_or_a_ff_or_a_bo CHECK (
1060 (tag IS NOT NULL AND svf IS NULL AND bool_op IS NULL) OR
1061 (tag IS NULL AND svf IS NOT NULL AND bool_op IS NULL) OR
1062 (tag IS NULL AND svf IS NULL AND bool_op IS NOT NULL)
1066 CREATE TABLE vandelay.match_set_quality (
1067 id SERIAL PRIMARY KEY,
1068 match_set INT NOT NULL REFERENCES vandelay.match_set (id) ON DELETE CASCADE,
1069 svf TEXT REFERENCES config.record_attr_definition,
1072 value TEXT NOT NULL,
1073 quality INT NOT NULL DEFAULT 1, -- higher is better
1074 CONSTRAINT vmsq_need_a_subfield_with_a_tag CHECK ((tag IS NOT NULL AND subfield IS NOT NULL) OR tag IS NULL),
1075 CONSTRAINT vmsq_need_a_tag_or_a_ff CHECK ((tag IS NOT NULL AND svf IS NULL) OR (tag IS NULL AND svf IS NOT NULL))
1077 CREATE UNIQUE INDEX vmsq_def_once_per_set ON vandelay.match_set_quality (match_set, COALESCE(tag,''), COALESCE(subfield,''), COALESCE(svf,''), value);
1081 ALTER TABLE vandelay.queue ADD COLUMN match_set INT REFERENCES vandelay.match_set (id) ON UPDATE CASCADE ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
1082 ALTER TABLE vandelay.queued_record ADD COLUMN quality INT NOT NULL DEFAULT 0;
1083 ALTER TABLE vandelay.bib_attr_definition DROP COLUMN ident;
1085 CREATE TABLE vandelay.import_error (
1086 code TEXT PRIMARY KEY,
1087 description TEXT NOT NULL -- i18n
1090 ALTER TABLE vandelay.queued_bib_record
1091 ADD COLUMN import_error TEXT REFERENCES vandelay.import_error (code) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
1092 ADD COLUMN error_detail TEXT;
1094 ALTER TABLE vandelay.bib_match
1095 DROP COLUMN field_type,
1096 DROP COLUMN matched_attr,
1097 ADD COLUMN quality INT NOT NULL DEFAULT 1,
1098 ADD COLUMN match_score INT NOT NULL DEFAULT 0;
1100 ALTER TABLE vandelay.import_item
1101 ADD COLUMN import_error TEXT REFERENCES vandelay.import_error (code) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
1102 ADD COLUMN error_detail TEXT,
1103 ADD COLUMN imported_as BIGINT REFERENCES asset.copy (id) DEFERRABLE INITIALLY DEFERRED,
1104 ADD COLUMN import_time TIMESTAMP WITH TIME ZONE;
1106 ALTER TABLE vandelay.merge_profile ADD COLUMN lwm_ratio NUMERIC;
1108 CREATE OR REPLACE FUNCTION vandelay.marc21_record_type( marc TEXT ) RETURNS config.marc21_rec_type_map AS $func$
1115 retval config.marc21_rec_type_map%ROWTYPE;
1117 ldr := oils_xpath_string( '//*[local-name()="leader"]', marc );
1119 IF ldr IS NULL OR ldr = '' THEN
1120 SELECT * INTO retval FROM config.marc21_rec_type_map WHERE code = 'BKS';
1124 SELECT * INTO tval_rec FROM config.marc21_ff_pos_map WHERE fixed_field = 'Type' LIMIT 1; -- They're all the same
1125 SELECT * INTO bval_rec FROM config.marc21_ff_pos_map WHERE fixed_field = 'BLvl' LIMIT 1; -- They're all the same
1128 tval := SUBSTRING( ldr, tval_rec.start_pos + 1, tval_rec.length );
1129 bval := SUBSTRING( ldr, bval_rec.start_pos + 1, bval_rec.length );
1131 -- RAISE NOTICE 'type %, blvl %, ldr %', tval, bval, ldr;
1133 SELECT * INTO retval FROM config.marc21_rec_type_map WHERE type_val LIKE '%' || tval || '%' AND blvl_val LIKE '%' || bval || '%';
1136 IF retval.code IS NULL THEN
1137 SELECT * INTO retval FROM config.marc21_rec_type_map WHERE code = 'BKS';
1142 $func$ LANGUAGE PLPGSQL;
1144 CREATE OR REPLACE FUNCTION vandelay.marc21_extract_fixed_field( marc TEXT, ff TEXT ) RETURNS TEXT AS $func$
1151 rtype := (vandelay.marc21_record_type( marc )).code;
1152 FOR ff_pos IN SELECT * FROM config.marc21_ff_pos_map WHERE fixed_field = ff AND rec_type = rtype ORDER BY tag DESC LOOP
1153 IF ff_pos.tag = 'ldr' THEN
1154 val := oils_xpath_string('//*[local-name()="leader"]', marc);
1155 IF val IS NOT NULL THEN
1156 val := SUBSTRING( val, ff_pos.start_pos + 1, ff_pos.length );
1160 FOR tag_data IN SELECT value FROM UNNEST( oils_xpath( '//*[@tag="' || UPPER(ff_pos.tag) || '"]/text()', marc ) ) x(value) LOOP
1161 val := SUBSTRING( tag_data.value, ff_pos.start_pos + 1, ff_pos.length );
1165 val := REPEAT( ff_pos.default_val, ff_pos.length );
1171 $func$ LANGUAGE PLPGSQL;
1173 CREATE OR REPLACE FUNCTION vandelay.marc21_extract_all_fixed_fields( marc TEXT ) RETURNS SETOF biblio.record_ff_map AS $func$
1178 output biblio.record_ff_map%ROWTYPE;
1180 rtype := (vandelay.marc21_record_type( marc )).code;
1182 FOR ff_pos IN SELECT * FROM config.marc21_ff_pos_map WHERE rec_type = rtype ORDER BY tag DESC LOOP
1183 output.ff_name := ff_pos.fixed_field;
1184 output.ff_value := NULL;
1186 IF ff_pos.tag = 'ldr' THEN
1187 output.ff_value := oils_xpath_string('//*[local-name()="leader"]', marc);
1188 IF output.ff_value IS NOT NULL THEN
1189 output.ff_value := SUBSTRING( output.ff_value, ff_pos.start_pos + 1, ff_pos.length );
1191 output.ff_value := NULL;
1194 FOR tag_data IN SELECT value FROM UNNEST( oils_xpath( '//*[@tag="' || UPPER(ff_pos.tag) || '"]/text()', marc ) ) x(value) LOOP
1195 output.ff_value := SUBSTRING( tag_data, ff_pos.start_pos + 1, ff_pos.length );
1196 IF output.ff_value IS NULL THEN output.ff_value := REPEAT( ff_pos.default_val, ff_pos.length ); END IF;
1198 output.ff_value := NULL;
1206 $func$ LANGUAGE PLPGSQL;
1208 CREATE OR REPLACE FUNCTION vandelay.marc21_physical_characteristics( marc TEXT) RETURNS SETOF biblio.marc21_physical_characteristics AS $func$
1212 ptype config.marc21_physical_characteristic_type_map%ROWTYPE;
1213 psf config.marc21_physical_characteristic_subfield_map%ROWTYPE;
1214 pval config.marc21_physical_characteristic_value_map%ROWTYPE;
1215 retval biblio.marc21_physical_characteristics%ROWTYPE;
1218 _007 := oils_xpath_string( '//*[@tag="007"]', marc );
1220 IF _007 IS NOT NULL AND _007 <> '' THEN
1221 SELECT * INTO ptype FROM config.marc21_physical_characteristic_type_map WHERE ptype_key = SUBSTRING( _007, 1, 1 );
1223 IF ptype.ptype_key IS NOT NULL THEN
1224 FOR psf IN SELECT * FROM config.marc21_physical_characteristic_subfield_map WHERE ptype_key = ptype.ptype_key LOOP
1225 SELECT * INTO pval FROM config.marc21_physical_characteristic_value_map WHERE ptype_subfield = psf.id AND value = SUBSTRING( _007, psf.start_pos + 1, psf.length );
1227 IF pval.id IS NOT NULL THEN
1230 retval.ptype := ptype.ptype_key;
1231 retval.subfield := psf.id;
1232 retval.value := pval.id;
1242 $func$ LANGUAGE PLPGSQL;
1244 CREATE TYPE vandelay.flat_marc AS ( tag CHAR(3), ind1 TEXT, ind2 TEXT, subfield TEXT, value TEXT );
1245 CREATE OR REPLACE FUNCTION vandelay.flay_marc ( TEXT ) RETURNS SETOF vandelay.flat_marc AS $func$
1248 use MARC::File::XML (BinaryEncoding => 'UTF-8');
1252 MARC::Charset->assume_unicode(1);
1255 my $r = MARC::Record->new_from_xml( $xml );
1257 return_next( { tag => 'LDR', value => $r->leader } );
1259 for my $f ( $r->fields ) {
1260 if ($f->is_control_field) {
1261 return_next({ tag => $f->tag, value => $f->data });
1263 for my $s ($f->subfields) {
1266 ind1 => $f->indicator(1),
1267 ind2 => $f->indicator(2),
1268 subfield => $s->[0],
1272 if ( $f->tag eq '245' and $s->[0] eq 'a' ) {
1273 my $trim = $f->indicator(2) || 0;
1276 ind1 => $f->indicator(1),
1277 ind2 => $f->indicator(2),
1279 value => substr( $s->[1], $trim )
1288 $func$ LANGUAGE PLPERLU;
1290 CREATE OR REPLACE FUNCTION vandelay.flatten_marc ( marc TEXT ) RETURNS SETOF vandelay.flat_marc AS $func$
1292 output vandelay.flat_marc%ROWTYPE;
1295 FOR field IN SELECT * FROM vandelay.flay_marc( marc ) LOOP
1296 output.ind1 := field.ind1;
1297 output.ind2 := field.ind2;
1298 output.tag := field.tag;
1299 output.subfield := field.subfield;
1300 IF field.subfield IS NOT NULL AND field.tag NOT IN ('020','022','024') THEN -- exclude standard numbers and control fields
1301 output.value := naco_normalize(field.value, field.subfield);
1303 output.value := field.value;
1306 CONTINUE WHEN output.value IS NULL;
1311 $func$ LANGUAGE PLPGSQL;
1313 CREATE OR REPLACE FUNCTION vandelay.extract_rec_attrs ( xml TEXT, attr_defs TEXT[]) RETURNS hstore AS $_$
1315 transformed_xml TEXT;
1318 xfrm config.xml_transform%ROWTYPE;
1320 new_attrs HSTORE := ''::HSTORE;
1321 attr_def config.record_attr_definition%ROWTYPE;
1324 FOR attr_def IN SELECT * FROM config.record_attr_definition WHERE name IN (SELECT * FROM UNNEST(attr_defs)) ORDER BY format LOOP
1326 IF attr_def.tag IS NOT NULL THEN -- tag (and optional subfield list) selection
1327 SELECT ARRAY_TO_STRING(ARRAY_ACCUM(x.value), COALESCE(attr_def.joiner,' ')) INTO attr_value
1328 FROM vandelay.flatten_marc(xml) AS x
1329 WHERE x.tag LIKE attr_def.tag
1331 WHEN attr_def.sf_list IS NOT NULL
1332 THEN POSITION(x.subfield IN attr_def.sf_list) > 0
1339 ELSIF attr_def.fixed_field IS NOT NULL THEN -- a named fixed field, see config.marc21_ff_pos_map.fixed_field
1340 attr_value := vandelay.marc21_extract_fixed_field(xml, attr_def.fixed_field);
1342 ELSIF attr_def.xpath IS NOT NULL THEN -- and xpath expression
1344 SELECT INTO xfrm * FROM config.xml_transform WHERE name = attr_def.format;
1346 -- See if we can skip the XSLT ... it's expensive
1347 IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
1348 -- Can't skip the transform
1349 IF xfrm.xslt <> '---' THEN
1350 transformed_xml := oils_xslt_process(xml,xfrm.xslt);
1352 transformed_xml := xml;
1355 prev_xfrm := xfrm.name;
1358 IF xfrm.name IS NULL THEN
1359 -- just grab the marcxml (empty) transform
1360 SELECT INTO xfrm * FROM config.xml_transform WHERE xslt = '---' LIMIT 1;
1361 prev_xfrm := xfrm.name;
1364 attr_value := oils_xpath_string(attr_def.xpath, transformed_xml, COALESCE(attr_def.joiner,' '), ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]);
1366 ELSIF attr_def.phys_char_sf IS NOT NULL THEN -- a named Physical Characteristic, see config.marc21_physical_characteristic_*_map
1367 SELECT m.value::TEXT INTO attr_value
1368 FROM vandelay.marc21_physical_characteristics(xml) v
1369 JOIN config.marc21_physical_characteristic_value_map m ON (m.id = v.value)
1370 WHERE v.subfield = attr_def.phys_char_sf
1371 LIMIT 1; -- Just in case ...
1375 -- apply index normalizers to attr_value
1377 SELECT n.func AS func,
1378 n.param_count AS param_count,
1380 FROM config.index_normalizer n
1381 JOIN config.record_attr_index_norm_map m ON (m.norm = n.id)
1382 WHERE attr = attr_def.name
1384 EXECUTE 'SELECT ' || normalizer.func || '(' ||
1385 quote_literal( attr_value ) ||
1387 WHEN normalizer.param_count > 0
1388 THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
1391 ')' INTO attr_value;
1395 -- Add the new value to the hstore
1396 new_attrs := new_attrs || hstore( attr_def.name, attr_value );
1402 $_$ LANGUAGE PLPGSQL;
1404 CREATE OR REPLACE FUNCTION vandelay.extract_rec_attrs ( xml TEXT ) RETURNS hstore AS $_$
1405 SELECT vandelay.extract_rec_attrs( $1, (SELECT ARRAY_ACCUM(name) FROM config.record_attr_definition));
1408 -- Everything between this comment and the beginning of the definition of
1409 -- vandelay.match_bib_record() is strictly in service of that function.
1410 CREATE TYPE vandelay.match_set_test_result AS (record BIGINT, quality INTEGER);
1412 CREATE OR REPLACE FUNCTION vandelay.match_set_test_marcxml(
1413 match_set_id INTEGER, record_xml TEXT
1414 ) RETURNS SETOF vandelay.match_set_test_result AS $$
1425 tags_rstore := vandelay.flatten_marc_hstore(record_xml);
1426 svf_rstore := vandelay.extract_rec_attrs(record_xml);
1428 CREATE TEMPORARY TABLE _vandelay_tmp_qrows (q INTEGER);
1429 CREATE TEMPORARY TABLE _vandelay_tmp_jrows (j TEXT);
1431 -- generate the where clause and return that directly (into wq), and as
1432 -- a side-effect, populate the _vandelay_tmp_[qj]rows tables.
1433 wq := vandelay.get_expr_from_match_set(match_set_id);
1435 query_ := 'SELECT bre.id AS record, ';
1437 -- qrows table is for the quality bits we add to the SELECT clause
1438 SELECT ARRAY_TO_STRING(
1439 ARRAY_ACCUM('COALESCE(n' || q::TEXT || '.quality, 0)'), ' + '
1440 ) INTO coal FROM _vandelay_tmp_qrows;
1442 -- our query string so far is the SELECT clause and the inital FROM.
1443 -- no JOINs yet nor the WHERE clause
1444 query_ := query_ || coal || ' AS quality ' || E'\n' ||
1445 'FROM biblio.record_entry bre ';
1447 -- jrows table is for the joins we must make (and the real text conditions)
1448 SELECT ARRAY_TO_STRING(ARRAY_ACCUM(j), E'\n') INTO joins
1449 FROM _vandelay_tmp_jrows;
1451 -- add those joins and the where clause to our query.
1452 query_ := query_ || joins || E'\n' || 'WHERE ' || wq || ' AND not bre.deleted';
1454 -- this will return rows of record,quality
1455 FOR rec IN EXECUTE query_ USING tags_rstore, svf_rstore LOOP
1459 DROP TABLE _vandelay_tmp_qrows;
1460 DROP TABLE _vandelay_tmp_jrows;
1464 $$ LANGUAGE PLPGSQL;
1466 CREATE OR REPLACE FUNCTION vandelay.flatten_marc_hstore(
1468 ) RETURNS HSTORE AS $$
1472 ARRAY_ACCUM(tag || (COALESCE(subfield, ''))),
1476 SELECT tag, subfield, ARRAY_ACCUM(value)::TEXT AS value
1477 FROM vandelay.flatten_marc(record_xml)
1478 GROUP BY tag, subfield ORDER BY tag, subfield
1482 $$ LANGUAGE PLPGSQL;
1484 CREATE OR REPLACE FUNCTION vandelay.get_expr_from_match_set(
1485 match_set_id INTEGER
1486 ) RETURNS TEXT AS $$
1488 root vandelay.match_set_point;
1490 SELECT * INTO root FROM vandelay.match_set_point
1491 WHERE parent IS NULL AND match_set = match_set_id;
1493 RETURN vandelay.get_expr_from_match_set_point(root);
1495 $$ LANGUAGE PLPGSQL;
1497 CREATE OR REPLACE FUNCTION vandelay.get_expr_from_match_set_point(
1498 node vandelay.match_set_point
1499 ) RETURNS TEXT AS $$
1505 child vandelay.match_set_point;
1507 SELECT ARRAY_ACCUM(id) INTO children FROM vandelay.match_set_point
1508 WHERE parent = node.id;
1510 IF ARRAY_LENGTH(children, 1) > 0 THEN
1511 this_op := vandelay._get_expr_render_one(node);
1514 WHILE children[i] IS NOT NULL LOOP
1515 SELECT * INTO child FROM vandelay.match_set_point
1516 WHERE id = children[i];
1518 q := q || ' ' || this_op || ' ';
1521 q := q || vandelay.get_expr_from_match_set_point(child);
1525 ELSIF node.bool_op IS NULL THEN
1526 PERFORM vandelay._get_expr_push_qrow(node);
1527 PERFORM vandelay._get_expr_push_jrow(node);
1528 RETURN vandelay._get_expr_render_one(node);
1533 $$ LANGUAGE PLPGSQL;
1535 CREATE OR REPLACE FUNCTION vandelay._get_expr_push_qrow(
1536 node vandelay.match_set_point
1537 ) RETURNS VOID AS $$
1540 INSERT INTO _vandelay_tmp_qrows (q) VALUES (node.id);
1542 $$ LANGUAGE PLPGSQL;
1544 CREATE OR REPLACE FUNCTION vandelay._get_expr_push_jrow(
1545 node vandelay.match_set_point
1546 ) RETURNS VOID AS $$
1559 IF node.tag IS NOT NULL THEN
1561 IF node.subfield IS NOT NULL THEN
1562 tagkey := tagkey || node.subfield;
1566 my_alias := 'n' || node.id::TEXT;
1568 jrow := 'LEFT JOIN (SELECT *, ' || node.quality ||
1569 ' AS quality FROM metabib.';
1570 IF node.tag IS NOT NULL THEN
1571 jrow := jrow || 'full_rec) ' || my_alias || ' ON (' ||
1572 my_alias || '.record = bre.id AND ' || my_alias || '.tag = ''' ||
1574 IF node.subfield IS NOT NULL THEN
1575 jrow := jrow || ' AND ' || my_alias || '.subfield = ''' ||
1576 node.subfield || '''';
1578 jrow := jrow || ' AND (' || my_alias || '.value ' || op ||
1579 ' ANY(($1->''' || tagkey || ''')::TEXT[])))';
1581 jrow := jrow || 'record_attr) ' || my_alias || ' ON (' ||
1582 my_alias || '.id = bre.id AND (' ||
1583 my_alias || '.attrs->''' || node.svf ||
1584 ''' ' || op || ' $2->''' || node.svf || '''))';
1586 INSERT INTO _vandelay_tmp_jrows (j) VALUES (jrow);
1588 $$ LANGUAGE PLPGSQL;
1590 CREATE OR REPLACE FUNCTION vandelay._get_expr_render_one(
1591 node vandelay.match_set_point
1592 ) RETURNS TEXT AS $$
1596 IF node.bool_op IS NOT NULL THEN
1597 RETURN node.bool_op;
1599 RETURN '(n' || node.id::TEXT || '.id IS NOT NULL)';
1602 $$ LANGUAGE PLPGSQL;
1604 CREATE OR REPLACE FUNCTION vandelay.match_bib_record() RETURNS TRIGGER AS $func$
1606 incoming_existing_id TEXT;
1607 test_result vandelay.match_set_test_result%ROWTYPE;
1611 IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
1615 DELETE FROM vandelay.bib_match WHERE queued_record = NEW.id;
1617 SELECT q.match_set INTO match_set FROM vandelay.bib_queue q WHERE q.id = NEW.queue;
1619 IF match_set IS NOT NULL THEN
1620 NEW.quality := vandelay.measure_record_quality( NEW.marc, match_set );
1623 -- Perfect matches on 901$c exit early with a match with high quality.
1624 incoming_existing_id :=
1625 oils_xpath_string('//*[@tag="901"]/*[@code="c"][1]', NEW.marc);
1627 IF incoming_existing_id IS NOT NULL AND incoming_existing_id != '' THEN
1628 SELECT id INTO tmp_rec FROM biblio.record_entry WHERE id = incoming_existing_id::bigint;
1629 IF tmp_rec IS NOT NULL THEN
1630 INSERT INTO vandelay.bib_match (queued_record, eg_record, match_score, quality)
1635 -- note: no match_set means quality==0
1636 vandelay.measure_record_quality( b.marc, match_set )
1637 FROM biblio.record_entry b
1638 WHERE id = incoming_existing_id::bigint;
1642 IF match_set IS NULL THEN
1646 FOR test_result IN SELECT * FROM
1647 vandelay.match_set_test_marcxml(match_set, NEW.marc) LOOP
1649 INSERT INTO vandelay.bib_match ( queued_record, eg_record, match_score, quality )
1653 test_result.quality,
1654 vandelay.measure_record_quality( b.marc, match_set )
1655 FROM biblio.record_entry b
1656 WHERE id = test_result.record;
1662 $func$ LANGUAGE PLPGSQL;
1664 CREATE OR REPLACE FUNCTION vandelay.measure_record_quality ( xml TEXT, match_set_id INT ) RETURNS INT AS $_$
1668 test vandelay.match_set_quality%ROWTYPE;
1671 FOR test IN SELECT * FROM vandelay.match_set_quality WHERE match_set = match_set_id LOOP
1672 IF test.tag IS NOT NULL THEN
1673 FOR rvalue IN SELECT value FROM vandelay.flatten_marc( xml ) WHERE tag = test.tag AND subfield = test.subfield LOOP
1674 IF test.value = rvalue THEN
1675 out_q := out_q + test.quality;
1679 IF test.value = vandelay.extract_rec_attrs(xml, ARRAY[test.svf]) -> test.svf THEN
1680 out_q := out_q + test.quality;
1687 $_$ LANGUAGE PLPGSQL;
1690 CREATE OR REPLACE FUNCTION vandelay.overlay_bib_record ( import_id BIGINT, eg_id BIGINT, merge_profile_id INT ) RETURNS BOOL AS $$
1692 merge_profile vandelay.merge_profile%ROWTYPE;
1693 dyn_profile vandelay.compile_profile%ROWTYPE;
1703 SELECT q.marc INTO v_marc
1704 FROM vandelay.queued_record q
1705 JOIN vandelay.bib_match m ON (m.queued_record = q.id AND q.id = import_id)
1708 IF v_marc IS NULL THEN
1709 -- RAISE NOTICE 'no marc for vandelay or bib record';
1713 IF vandelay.template_overlay_bib_record( v_marc, eg_id, merge_profile_id) THEN
1714 UPDATE vandelay.queued_bib_record
1715 SET imported_as = eg_id,
1717 WHERE id = import_id;
1719 editor_string := (oils_xpath('//*[@tag="905"]/*[@code="u"]/text()',v_marc))[1];
1721 IF editor_string IS NOT NULL AND editor_string <> '' THEN
1722 SELECT usr INTO editor_id FROM actor.card WHERE barcode = editor_string;
1724 IF editor_id IS NULL THEN
1725 SELECT id INTO editor_id FROM actor.usr WHERE usrname = editor_string;
1728 IF editor_id IS NOT NULL THEN
1729 UPDATE biblio.record_entry SET editor = editor_id WHERE id = eg_id;
1736 -- RAISE NOTICE 'update of biblio.record_entry failed';
1741 $$ LANGUAGE PLPGSQL;
1744 CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_record_with_best ( import_id BIGINT, merge_profile_id INT, lwm_ratio_value_p NUMERIC ) RETURNS BOOL AS $$
1747 lwm_ratio_value NUMERIC;
1750 lwm_ratio_value := COALESCE(lwm_ratio_value_p, 0.0);
1752 PERFORM * FROM vandelay.queued_bib_record WHERE import_time IS NOT NULL AND id = import_id;
1755 -- RAISE NOTICE 'already imported, cannot auto-overlay'
1759 SELECT m.eg_record INTO eg_id
1760 FROM vandelay.bib_match m
1761 JOIN vandelay.queued_bib_record qr ON (m.queued_record = qr.id)
1762 JOIN vandelay.bib_queue q ON (qr.queue = q.id)
1763 JOIN biblio.record_entry r ON (r.id = m.eg_record)
1764 WHERE m.queued_record = import_id
1765 AND qr.quality::NUMERIC / COALESCE(NULLIF(m.quality,0),1)::NUMERIC >= lwm_ratio_value
1766 ORDER BY m.match_score DESC, -- required match score
1767 qr.quality::NUMERIC / COALESCE(NULLIF(m.quality,0),1)::NUMERIC DESC, -- quality tie breaker
1768 m.id -- when in doubt, use the first match
1771 IF eg_id IS NULL THEN
1772 -- RAISE NOTICE 'incoming record is not of high enough quality';
1776 RETURN vandelay.overlay_bib_record( import_id, eg_id, merge_profile_id );
1778 $$ LANGUAGE PLPGSQL;
1780 CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_record_with_best ( import_id BIGINT, merge_profile_id INT, lwm_ratio_value_p NUMERIC ) RETURNS BOOL AS $$
1783 lwm_ratio_value NUMERIC;
1786 lwm_ratio_value := COALESCE(lwm_ratio_value_p, 0.0);
1788 PERFORM * FROM vandelay.queued_bib_record WHERE import_time IS NOT NULL AND id = import_id;
1791 -- RAISE NOTICE 'already imported, cannot auto-overlay'
1795 SELECT m.eg_record INTO eg_id
1796 FROM vandelay.bib_match m
1797 JOIN vandelay.queued_bib_record qr ON (m.queued_record = qr.id)
1798 JOIN vandelay.bib_queue q ON (qr.queue = q.id)
1799 JOIN biblio.record_entry r ON (r.id = m.eg_record)
1800 WHERE m.queued_record = import_id
1801 AND qr.quality::NUMERIC / COALESCE(NULLIF(m.quality,0),1)::NUMERIC >= lwm_ratio_value
1802 ORDER BY m.match_score DESC, -- required match score
1803 qr.quality::NUMERIC / COALESCE(NULLIF(m.quality,0),1)::NUMERIC DESC, -- quality tie breaker
1804 m.id -- when in doubt, use the first match
1807 IF eg_id IS NULL THEN
1808 -- RAISE NOTICE 'incoming record is not of high enough quality';
1812 RETURN vandelay.overlay_bib_record( import_id, eg_id, merge_profile_id );
1814 $$ LANGUAGE PLPGSQL;
1817 CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_queue_with_best ( queue_id BIGINT, merge_profile_id INT, lwm_ratio_value NUMERIC ) RETURNS SETOF BIGINT AS $$
1819 queued_record vandelay.queued_bib_record%ROWTYPE;
1822 FOR queued_record IN SELECT * FROM vandelay.queued_bib_record WHERE queue = queue_id AND import_time IS NULL LOOP
1824 IF vandelay.auto_overlay_bib_record_with_best( queued_record.id, merge_profile_id, lwm_ratio_value ) THEN
1825 RETURN NEXT queued_record.id;
1833 $$ LANGUAGE PLPGSQL;
1835 CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_queue_with_best ( import_id BIGINT, merge_profile_id INT ) RETURNS SETOF BIGINT AS $$
1836 SELECT vandelay.auto_overlay_bib_queue_with_best( $1, $2, p.lwm_ratio ) FROM vandelay.merge_profile p WHERE id = $2;
1839 CREATE OR REPLACE FUNCTION vandelay.ingest_bib_marc ( ) RETURNS TRIGGER AS $$
1845 IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
1849 FOR adef IN SELECT * FROM vandelay.bib_attr_definition LOOP
1851 SELECT extract_marc_field('vandelay.queued_bib_record', id, adef.xpath, adef.remove) INTO value FROM vandelay.queued_bib_record WHERE id = NEW.id;
1852 IF (value IS NOT NULL AND value <> '') THEN
1853 INSERT INTO vandelay.queued_bib_record_attr (record, field, attr_value) VALUES (NEW.id, adef.id, value);
1860 $$ LANGUAGE PLPGSQL;
1862 CREATE OR REPLACE FUNCTION vandelay.ingest_bib_items ( ) RETURNS TRIGGER AS $func$
1865 item_data vandelay.import_item%ROWTYPE;
1868 IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
1872 SELECT item_attr_def INTO attr_def FROM vandelay.bib_queue WHERE id = NEW.queue;
1874 FOR item_data IN SELECT * FROM vandelay.ingest_items( NEW.id::BIGINT, attr_def ) LOOP
1875 INSERT INTO vandelay.import_item (
1899 item_data.definition,
1900 item_data.owning_lib,
1902 item_data.call_number,
1903 item_data.copy_number,
1906 item_data.circulate,
1908 item_data.deposit_amount,
1913 item_data.circ_modifier,
1914 item_data.circ_as_type,
1915 item_data.alert_message,
1917 item_data.priv_note,
1918 item_data.opac_visible
1924 $func$ LANGUAGE PLPGSQL;
1926 CREATE OR REPLACE FUNCTION vandelay.cleanup_bib_marc ( ) RETURNS TRIGGER AS $$
1928 IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
1932 DELETE FROM vandelay.queued_bib_record_attr WHERE record = OLD.id;
1933 DELETE FROM vandelay.import_item WHERE record = OLD.id;
1935 IF TG_OP = 'UPDATE' THEN
1940 $$ LANGUAGE PLPGSQL;
1944 DROP TRIGGER zz_match_bibs_trigger ON vandelay.queued_bib_record;
1945 CREATE TRIGGER zz_match_bibs_trigger
1946 BEFORE INSERT OR UPDATE ON vandelay.queued_bib_record
1947 FOR EACH ROW EXECUTE PROCEDURE vandelay.match_bib_record();
1949 CREATE OR REPLACE FUNCTION vandelay.ingest_authority_marc ( ) RETURNS TRIGGER AS $$
1955 IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
1959 FOR adef IN SELECT * FROM vandelay.authority_attr_definition LOOP
1961 SELECT extract_marc_field('vandelay.queued_authority_record', id, adef.xpath, adef.remove) INTO value FROM vandelay.queued_authority_record WHERE id = NEW.id;
1962 IF (value IS NOT NULL AND value <> '') THEN
1963 INSERT INTO vandelay.queued_authority_record_attr (record, field, attr_value) VALUES (NEW.id, adef.id, value);
1970 $$ LANGUAGE PLPGSQL;
1972 ALTER TABLE vandelay.authority_attr_definition DROP COLUMN ident;
1973 ALTER TABLE vandelay.queued_authority_record
1974 ADD COLUMN import_error TEXT REFERENCES vandelay.import_error (code) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
1975 ADD COLUMN error_detail TEXT;
1977 ALTER TABLE vandelay.authority_match DROP COLUMN matched_attr;
1979 CREATE OR REPLACE FUNCTION vandelay.cleanup_authority_marc ( ) RETURNS TRIGGER AS $$
1981 IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
1985 DELETE FROM vandelay.queued_authority_record_attr WHERE record = OLD.id;
1986 IF TG_OP = 'UPDATE' THEN
1991 $$ LANGUAGE PLPGSQL;
1993 CREATE OR REPLACE FUNCTION authority.flatten_marc ( rid BIGINT ) RETURNS SETOF authority.full_rec AS $func$
1995 auth authority.record_entry%ROWTYPE;
1996 output authority.full_rec%ROWTYPE;
1999 SELECT INTO auth * FROM authority.record_entry WHERE id = rid;
2001 FOR field IN SELECT * FROM vandelay.flatten_marc( auth.marc ) LOOP
2002 output.record := rid;
2003 output.ind1 := field.ind1;
2004 output.ind2 := field.ind2;
2005 output.tag := field.tag;
2006 output.subfield := field.subfield;
2007 output.value := field.value;
2012 $func$ LANGUAGE PLPGSQL;
2014 CREATE OR REPLACE FUNCTION biblio.flatten_marc ( rid BIGINT ) RETURNS SETOF metabib.full_rec AS $func$
2016 bib biblio.record_entry%ROWTYPE;
2017 output metabib.full_rec%ROWTYPE;
2020 SELECT INTO bib * FROM biblio.record_entry WHERE id = rid;
2022 FOR field IN SELECT * FROM vandelay.flatten_marc( bib.marc ) LOOP
2023 output.record := rid;
2024 output.ind1 := field.ind1;
2025 output.ind2 := field.ind2;
2026 output.tag := field.tag;
2027 output.subfield := field.subfield;
2028 output.value := field.value;
2033 $func$ LANGUAGE PLPGSQL;
2035 -----------------------------------------------
2036 -- Seed data for import errors
2037 -----------------------------------------------
2039 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'general.unknown', oils_i18n_gettext('general.unknown', 'Import or Overlay failed', 'vie', 'description') );
2040 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.item.duplicate.barcode', oils_i18n_gettext('import.item.duplicate.barcode', 'Import failed due to barcode collision', 'vie', 'description') );
2041 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.item.invalid.circ_modifier', oils_i18n_gettext('import.item.invalid.circ_modifier', 'Import failed due to invalid circulation modifier', 'vie', 'description') );
2042 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.item.invalid.location', oils_i18n_gettext('import.item.invalid.location', 'Import failed due to invalid copy location', 'vie', 'description') );
2043 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.duplicate.sysid', oils_i18n_gettext('import.duplicate.sysid', 'Import failed due to system id collision', 'vie', 'description') );
2044 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.duplicate.tcn', oils_i18n_gettext('import.duplicate.sysid', 'Import failed due to system id collision', 'vie', 'description') );
2045 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'overlay.missing.sysid', oils_i18n_gettext('overlay.missing.sysid', 'Overlay failed due to missing system id', 'vie', 'description') );
2046 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.auth.duplicate.acn', oils_i18n_gettext('import.auth.duplicate.acn', 'Import failed due to Accession Number collision', 'vie', 'description') );
2047 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.xml.malformed', oils_i18n_gettext('import.xml.malformed', 'Malformed record cause Import failure', 'vie', 'description') );
2048 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'overlay.xml.malformed', oils_i18n_gettext('overlay.xml.malformed', 'Malformed record cause Overlay failure', 'vie', 'description') );
2049 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'overlay.record.quality', oils_i18n_gettext('overlay.record.quality', 'New record had insufficient quality', 'vie', 'description') );
2052 ----------------------------------------------------------------
2053 -- Seed data for queued record/item exports
2054 ----------------------------------------------------------------
2056 INSERT INTO action_trigger.hook (key,core_type,description,passive) VALUES (
2057 'vandelay.queued_bib_record.print',
2060 'vandelay.queued_bib_record.print',
2061 'Print output has been requested for records in an Importer Bib Queue.',
2068 'vandelay.queued_bib_record.csv',
2071 'vandelay.queued_bib_record.csv',
2072 'CSV output has been requested for records in an Importer Bib Queue.',
2079 'vandelay.queued_bib_record.email',
2082 'vandelay.queued_bib_record.email',
2083 'An email has been requested for records in an Importer Bib Queue.',
2090 'vandelay.queued_auth_record.print',
2093 'vandelay.queued_auth_record.print',
2094 'Print output has been requested for records in an Importer Authority Queue.',
2101 'vandelay.queued_auth_record.csv',
2104 'vandelay.queued_auth_record.csv',
2105 'CSV output has been requested for records in an Importer Authority Queue.',
2112 'vandelay.queued_auth_record.email',
2115 'vandelay.queued_auth_record.email',
2116 'An email has been requested for records in an Importer Authority Queue.',
2123 'vandelay.import_items.print',
2126 'vandelay.import_items.print',
2127 'Print output has been requested for Import Items from records in an Importer Bib Queue.',
2134 'vandelay.import_items.csv',
2137 'vandelay.import_items.csv',
2138 'CSV output has been requested for Import Items from records in an Importer Bib Queue.',
2145 'vandelay.import_items.email',
2148 'vandelay.import_items.email',
2149 'An email has been requested for Import Items from records in an Importer Bib Queue.',
2157 INSERT INTO action_trigger.event_definition (
2172 'Print Output for Queued Bib Records',
2173 'vandelay.queued_bib_record.print',
2181 Queue ID: [% target.0.queue.id %]
2182 Queue Name: [% target.0.queue.name %]
2183 Queue Type: [% target.0.queue.queue_type %]
2184 Complete? [% target.0.queue.complete %]
2186 [% FOR vqbr IN target %]
2188 Title of work | [% helpers.get_queued_bib_attr('title',vqbr.attributes) %]
2189 Author of work | [% helpers.get_queued_bib_attr('author',vqbr.attributes) %]
2190 Language of work | [% helpers.get_queued_bib_attr('language',vqbr.attributes) %]
2191 Pagination | [% helpers.get_queued_bib_attr('pagination',vqbr.attributes) %]
2192 ISBN | [% helpers.get_queued_bib_attr('isbn',vqbr.attributes) %]
2193 ISSN | [% helpers.get_queued_bib_attr('issn',vqbr.attributes) %]
2194 Price | [% helpers.get_queued_bib_attr('price',vqbr.attributes) %]
2195 Accession Number | [% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) %]
2196 TCN Value | [% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) %]
2197 TCN Source | [% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) %]
2198 Internal ID | [% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) %]
2199 Publisher | [% helpers.get_queued_bib_attr('publisher',vqbr.attributes) %]
2200 Publication Date | [% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) %]
2201 Edition | [% helpers.get_queued_bib_attr('edition',vqbr.attributes) %]
2202 Item Barcode | [% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) %]
2210 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2215 INSERT INTO action_trigger.event_definition (
2230 'CSV Output for Queued Bib Records',
2231 'vandelay.queued_bib_record.csv',
2238 "Title of work","Author of work","Language of work","Pagination","ISBN","ISSN","Price","Accession Number","TCN Value","TCN Source","Internal ID","Publisher","Publication Date","Edition","Item Barcode"
2239 [% FOR vqbr IN target %]"[% helpers.get_queued_bib_attr('title',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('author',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('language',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('pagination',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('isbn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('issn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('price',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('publisher',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('edition',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) | replace('"', '""') %]"
2245 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2250 INSERT INTO action_trigger.event_definition (
2265 'Email Output for Queued Bib Records',
2266 'vandelay.queued_bib_record.email',
2273 [%- SET user = target.0.queue.owner -%]
2274 To: [%- params.recipient_email || user.email || 'root@localhost' %]
2275 From: [%- params.sender_email || default_sender %]
2276 Subject: Bibs from Import Queue
2278 Queue ID: [% target.0.queue.id %]
2279 Queue Name: [% target.0.queue.name %]
2280 Queue Type: [% target.0.queue.queue_type %]
2281 Complete? [% target.0.queue.complete %]
2283 [% FOR vqbr IN target %]
2285 Title of work | [% helpers.get_queued_bib_attr('title',vqbr.attributes) %]
2286 Author of work | [% helpers.get_queued_bib_attr('author',vqbr.attributes) %]
2287 Language of work | [% helpers.get_queued_bib_attr('language',vqbr.attributes) %]
2288 Pagination | [% helpers.get_queued_bib_attr('pagination',vqbr.attributes) %]
2289 ISBN | [% helpers.get_queued_bib_attr('isbn',vqbr.attributes) %]
2290 ISSN | [% helpers.get_queued_bib_attr('issn',vqbr.attributes) %]
2291 Price | [% helpers.get_queued_bib_attr('price',vqbr.attributes) %]
2292 Accession Number | [% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) %]
2293 TCN Value | [% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) %]
2294 TCN Source | [% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) %]
2295 Internal ID | [% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) %]
2296 Publisher | [% helpers.get_queued_bib_attr('publisher',vqbr.attributes) %]
2297 Publication Date | [% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) %]
2298 Edition | [% helpers.get_queued_bib_attr('edition',vqbr.attributes) %]
2299 Item Barcode | [% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) %]
2307 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2310 ,( 41, 'queue.owner')
2313 INSERT INTO action_trigger.event_definition (
2328 'Print Output for Queued Authority Records',
2329 'vandelay.queued_auth_record.print',
2337 Queue ID: [% target.0.queue.id %]
2338 Queue Name: [% target.0.queue.name %]
2339 Queue Type: [% target.0.queue.queue_type %]
2340 Complete? [% target.0.queue.complete %]
2342 [% FOR vqar IN target %]
2344 Record Identifier | [% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) %]
2352 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2357 INSERT INTO action_trigger.event_definition (
2372 'CSV Output for Queued Authority Records',
2373 'vandelay.queued_auth_record.csv',
2381 [% FOR vqar IN target %]"[% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) | replace('"', '""') %]"
2387 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2392 INSERT INTO action_trigger.event_definition (
2407 'Email Output for Queued Authority Records',
2408 'vandelay.queued_auth_record.email',
2415 [%- SET user = target.0.queue.owner -%]
2416 To: [%- params.recipient_email || user.email || 'root@localhost' %]
2417 From: [%- params.sender_email || default_sender %]
2418 Subject: Authorities from Import Queue
2420 Queue ID: [% target.0.queue.id %]
2421 Queue Name: [% target.0.queue.name %]
2422 Queue Type: [% target.0.queue.queue_type %]
2423 Complete? [% target.0.queue.complete %]
2425 [% FOR vqar IN target %]
2427 Record Identifier | [% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) %]
2435 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2438 ,( 44, 'queue.owner')
2441 INSERT INTO action_trigger.event_definition (
2456 'Print Output for Import Items from Queued Bib Records',
2457 'vandelay.import_items.print',
2460 'record.queue.owner',
2465 Queue ID: [% target.0.record.queue.id %]
2466 Queue Name: [% target.0.record.queue.name %]
2467 Queue Type: [% target.0.record.queue.queue_type %]
2468 Complete? [% target.0.record.queue.complete %]
2470 [% FOR vii IN target %]
2472 Import Item ID | [% vii.id %]
2473 Title of work | [% helpers.get_queued_bib_attr('title',vii.record.attributes) %]
2474 ISBN | [% helpers.get_queued_bib_attr('isbn',vii.record.attributes) %]
2475 Attribute Definition | [% vii.definition %]
2476 Import Error | [% vii.import_error %]
2477 Import Error Detail | [% vii.error_detail %]
2478 Owning Library | [% vii.owning_lib %]
2479 Circulating Library | [% vii.circ_lib %]
2480 Call Number | [% vii.call_number %]
2481 Copy Number | [% vii.copy_number %]
2482 Status | [% vii.status.name %]
2483 Shelving Location | [% vii.location.name %]
2484 Circulate | [% vii.circulate %]
2485 Deposit | [% vii.deposit %]
2486 Deposit Amount | [% vii.deposit_amount %]
2487 Reference | [% vii.ref %]
2488 Holdable | [% vii.holdable %]
2489 Price | [% vii.price %]
2490 Barcode | [% vii.barcode %]
2491 Circulation Modifier | [% vii.circ_modifier %]
2492 Circulate As MARC Type | [% vii.circ_as_type %]
2493 Alert Message | [% vii.alert_message %]
2494 Public Note | [% vii.pub_note %]
2495 Private Note | [% vii.priv_note %]
2496 OPAC Visible | [% vii.opac_visible %]
2504 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2506 ,( 45, 'record.attributes')
2507 ,( 45, 'record.queue')
2508 ,( 45, 'record.queue.owner')
2511 INSERT INTO action_trigger.event_definition (
2526 'CSV Output for Import Items from Queued Bib Records',
2527 'vandelay.import_items.csv',
2530 'record.queue.owner',
2534 "Import Item ID","Title of work","ISBN","Attribute Definition","Import Error","Import Error Detail","Owning Library","Circulating Library","Call Number","Copy Number","Status","Shelving Location","Circulate","Deposit","Deposit Amount","Reference","Holdable","Price","Barcode","Circulation Modifier","Circulate As MARC Type","Alert Message","Public Note","Private Note","OPAC Visible"
2535 [% FOR vii IN target %]"[% vii.id | replace('"', '""') %]","[% helpers.get_queued_bib_attr('title',vii.record.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('isbn',vii.record.attributes) | replace('"', '""') %]","[% vii.definition | replace('"', '""') %]","[% vii.import_error | replace('"', '""') %]","[% vii.error_detail | replace('"', '""') %]","[% vii.owning_lib | replace('"', '""') %]","[% vii.circ_lib | replace('"', '""') %]","[% vii.call_number | replace('"', '""') %]","[% vii.copy_number | replace('"', '""') %]","[% vii.status.name | replace('"', '""') %]","[% vii.location.name | replace('"', '""') %]","[% vii.circulate | replace('"', '""') %]","[% vii.deposit | replace('"', '""') %]","[% vii.deposit_amount | replace('"', '""') %]","[% vii.ref | replace('"', '""') %]","[% vii.holdable | replace('"', '""') %]","[% vii.price | replace('"', '""') %]","[% vii.barcode | replace('"', '""') %]","[% vii.circ_modifier | replace('"', '""') %]","[% vii.circ_as_type | replace('"', '""') %]","[% vii.alert_message | replace('"', '""') %]","[% vii.pub_note | replace('"', '""') %]","[% vii.priv_note | replace('"', '""') %]","[% vii.opac_visible | replace('"', '""') %]"
2541 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2543 ,( 46, 'record.attributes')
2544 ,( 46, 'record.queue')
2545 ,( 46, 'record.queue.owner')
2548 INSERT INTO action_trigger.event_definition (
2563 'Email Output for Import Items from Queued Bib Records',
2564 'vandelay.import_items.email',
2567 'record.queue.owner',
2571 [%- SET user = target.0.record.queue.owner -%]
2572 To: [%- params.recipient_email || user.email || 'root@localhost' %]
2573 From: [%- params.sender_email || default_sender %]
2574 Subject: Import Items from Import Queue
2576 Queue ID: [% target.0.record.queue.id %]
2577 Queue Name: [% target.0.record.queue.name %]
2578 Queue Type: [% target.0.record.queue.queue_type %]
2579 Complete? [% target.0.record.queue.complete %]
2581 [% FOR vii IN target %]
2583 Import Item ID | [% vii.id %]
2584 Title of work | [% helpers.get_queued_bib_attr('title',vii.record.attributes) %]
2585 ISBN | [% helpers.get_queued_bib_attr('isbn',vii.record.attributes) %]
2586 Attribute Definition | [% vii.definition %]
2587 Import Error | [% vii.import_error %]
2588 Import Error Detail | [% vii.error_detail %]
2589 Owning Library | [% vii.owning_lib %]
2590 Circulating Library | [% vii.circ_lib %]
2591 Call Number | [% vii.call_number %]
2592 Copy Number | [% vii.copy_number %]
2593 Status | [% vii.status.name %]
2594 Shelving Location | [% vii.location.name %]
2595 Circulate | [% vii.circulate %]
2596 Deposit | [% vii.deposit %]
2597 Deposit Amount | [% vii.deposit_amount %]
2598 Reference | [% vii.ref %]
2599 Holdable | [% vii.holdable %]
2600 Price | [% vii.price %]
2601 Barcode | [% vii.barcode %]
2602 Circulation Modifier | [% vii.circ_modifier %]
2603 Circulate As MARC Type | [% vii.circ_as_type %]
2604 Alert Message | [% vii.alert_message %]
2605 Public Note | [% vii.pub_note %]
2606 Private Note | [% vii.priv_note %]
2607 OPAC Visible | [% vii.opac_visible %]
2614 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2616 ,( 47, 'record.attributes')
2617 ,( 47, 'record.queue')
2618 ,( 47, 'record.queue.owner')
2623 SELECT evergreen.upgrade_deps_block_check('0574', :eg_version);
2625 UPDATE action_trigger.event_definition SET template =
2629 table { border-collapse: collapse; }
2630 td { padding: 5px; border-bottom: 1px solid #888; }
2631 th { font-weight: bold; }
2634 # Sort the holds into copy-location buckets
2635 # In the main print loop, sort each bucket by callnumber before printing
2636 SET holds_list = [];
2638 SET current_location = target.0.current_copy.location.id;
2640 IF current_location != hold.current_copy.location.id;
2641 SET current_location = hold.current_copy.location.id;
2642 holds_list.push(loc_data);
2647 'callnumber' => hold.current_copy.call_number.label
2649 loc_data.push(hold_data);
2651 holds_list.push(loc_data)
2658 <th>Shelving Location</th>
2659 <th>Call Number</th>
2660 <th>Barcode/Part</th>
2665 [% FOR loc_data IN holds_list %]
2666 [% FOR hold_data IN loc_data.sort('callnumber') %]
2668 SET hold = hold_data.hold;
2669 SET copy_data = helpers.get_copy_bib_basics(hold.current_copy.id);
2672 <td>[% copy_data.title | truncate %]</td>
2673 <td>[% copy_data.author | truncate %]</td>
2674 <td>[% hold.current_copy.location.name %]</td>
2675 <td>[% hold.current_copy.call_number.label %]</td>
2676 <td>[% hold.current_copy.barcode %]
2677 [% FOR part IN hold.current_copy.parts %]
2678 [% part.part.label %]
2681 <td>[% hold.usr.card.barcode %]</td>
2690 INSERT INTO action_trigger.environment (
2694 (35, 'current_copy.parts'),
2695 (35, 'current_copy.parts.part')
2699 -- Evergreen DB patch XXXX.schema.authority-control-sets.sql
2701 -- Schema upgrade to add Authority Control Set functionality
2705 -- check whether patch can be applied
2706 SELECT evergreen.upgrade_deps_block_check('0575', :eg_version);
2708 CREATE TABLE authority.control_set (
2709 id SERIAL PRIMARY KEY,
2710 name TEXT NOT NULL UNIQUE, -- i18n
2711 description TEXT -- i18n
2714 CREATE TABLE authority.control_set_authority_field (
2715 id SERIAL PRIMARY KEY,
2716 main_entry INT REFERENCES authority.control_set_authority_field (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2717 control_set INT NOT NULL REFERENCES authority.control_set (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2718 tag CHAR(3) NOT NULL,
2720 sf_list TEXT NOT NULL,
2721 name TEXT NOT NULL, -- i18n
2722 description TEXT -- i18n
2725 CREATE TABLE authority.control_set_bib_field (
2726 id SERIAL PRIMARY KEY,
2727 authority_field INT NOT NULL REFERENCES authority.control_set_authority_field (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2728 tag CHAR(3) NOT NULL
2731 CREATE TABLE authority.thesaurus (
2732 code TEXT PRIMARY KEY, -- MARC21 thesaurus code
2733 control_set INT NOT NULL REFERENCES authority.control_set (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2734 name TEXT NOT NULL UNIQUE, -- i18n
2735 description TEXT -- i18n
2738 CREATE TABLE authority.browse_axis (
2739 code TEXT PRIMARY KEY,
2740 name TEXT UNIQUE NOT NULL, -- i18n
2741 sorter TEXT REFERENCES config.record_attr_definition (name) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2745 CREATE TABLE authority.browse_axis_authority_field_map (
2746 id SERIAL PRIMARY KEY,
2747 axis TEXT NOT NULL REFERENCES authority.browse_axis (code) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2748 field INT NOT NULL REFERENCES authority.control_set_authority_field (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED
2751 ALTER TABLE authority.record_entry ADD COLUMN control_set INT REFERENCES authority.control_set (id) ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED;
2752 ALTER TABLE authority.rec_descriptor DROP COLUMN char_encoding, ADD COLUMN encoding_level TEXT, ADD COLUMN thesaurus TEXT;
2754 CREATE INDEX authority_full_rec_value_index ON authority.full_rec (value);
2755 CREATE OR REPLACE RULE protect_authority_rec_delete AS ON DELETE TO authority.record_entry DO INSTEAD (UPDATE authority.record_entry SET deleted = TRUE WHERE OLD.id = authority.record_entry.id; DELETE FROM authority.full_rec WHERE record = OLD.id);
2757 CREATE OR REPLACE FUNCTION authority.normalize_heading( marcxml TEXT, no_thesaurus BOOL ) RETURNS TEXT AS $func$
2759 acsaf authority.control_set_authority_field%ROWTYPE;
2767 thes_code := vandelay.marc21_extract_fixed_field(marcxml,'Subj');
2768 IF thes_code IS NULL THEN
2772 SELECT control_set INTO cset FROM authority.thesaurus WHERE code = thes_code;
2778 FOR acsaf IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset AND main_entry IS NULL LOOP
2779 tag_used := acsaf.tag;
2780 FOR sf IN SELECT * FROM regexp_split_to_table(acsaf.sf_list,'') LOOP
2781 tmp_text := oils_xpath_string('//*[@tag="'||tag_used||'"]/*[@code="'||sf||'"]', marcxml);
2782 IF tmp_text IS NOT NULL AND tmp_text <> '' THEN
2783 heading_text := heading_text || E'\u2021' || sf || ' ' || tmp_text;
2786 EXIT WHEN heading_text <> '';
2789 IF thes_code = 'z' THEN
2790 thes_code := oils_xpath_string('//*[@tag="040"]/*[@code="f"][1]', marcxml);
2793 IF heading_text <> '' THEN
2794 IF no_thesaurus IS TRUE THEN
2795 heading_text := tag_used || ' ' || public.naco_normalize(heading_text);
2797 heading_text := tag_used || '_' || thes_code || ' ' || public.naco_normalize(heading_text);
2800 heading_text := 'NOHEADING_' || thes_code || ' ' || MD5(marcxml);
2803 RETURN heading_text;
2805 $func$ LANGUAGE PLPGSQL IMMUTABLE;
2807 CREATE OR REPLACE FUNCTION authority.simple_normalize_heading( marcxml TEXT ) RETURNS TEXT AS $func$
2808 SELECT authority.normalize_heading($1, TRUE);
2809 $func$ LANGUAGE SQL IMMUTABLE;
2811 CREATE OR REPLACE FUNCTION authority.normalize_heading( marcxml TEXT ) RETURNS TEXT AS $func$
2812 SELECT authority.normalize_heading($1, FALSE);
2813 $func$ LANGUAGE SQL IMMUTABLE;
2815 CREATE OR REPLACE VIEW authority.tracing_links AS
2816 SELECT main.record AS record,
2818 main.tag AS main_tag,
2819 oils_xpath_string('//*[@tag="'||main.tag||'"]/*[local-name()="subfield"]', are.marc) AS main_value,
2820 substr(link.value,1,1) AS relationship,
2821 substr(link.value,2,1) AS use_restriction,
2822 substr(link.value,3,1) AS deprecation,
2823 substr(link.value,4,1) AS display_restriction,
2825 link.tag AS link_tag,
2826 oils_xpath_string('//*[@tag="'||link.tag||'"]/*[local-name()="subfield"]', are.marc) AS link_value,
2827 authority.normalize_heading(are.marc) AS normalized_main_value
2828 FROM authority.full_rec main
2829 JOIN authority.record_entry are ON (main.record = are.id)
2830 JOIN authority.control_set_authority_field main_entry
2831 ON (main_entry.tag = main.tag
2832 AND main_entry.main_entry IS NULL
2833 AND main.subfield = 'a' )
2834 JOIN authority.control_set_authority_field sub_entry
2835 ON (main_entry.id = sub_entry.main_entry)
2836 JOIN authority.full_rec link
2837 ON (link.record = main.record
2838 AND link.tag = sub_entry.tag
2839 AND link.subfield = 'w' );
2841 CREATE OR REPLACE FUNCTION authority.generate_overlay_template (source_xml TEXT) RETURNS TEXT AS $f$
2844 main_entry authority.control_set_authority_field%ROWTYPE;
2845 bib_field authority.control_set_bib_field%ROWTYPE;
2846 auth_id INT DEFAULT oils_xpath_string('//*[@tag="901"]/*[local-name()="subfield" and @code="c"]', source_xml)::INT;
2847 replace_data XML[] DEFAULT '{}'::XML[];
2848 replace_rules TEXT[] DEFAULT '{}'::TEXT[];
2851 IF auth_id IS NULL THEN
2855 -- Default to the LoC controll set
2856 SELECT COALESCE(control_set,1) INTO cset FROM authority.record_entry WHERE id = auth_id;
2858 FOR main_entry IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset LOOP
2859 auth_field := XPATH('//*[@tag="'||main_entry.tag||'"][1]',source_xml::XML);
2860 IF ARRAY_LENGTH(auth_field,1) > 0 THEN
2861 FOR bib_field IN SELECT * FROM authority.control_set_bib_field WHERE authority_field = main_entry.id LOOP
2862 replace_data := replace_data || XMLELEMENT( name datafield, XMLATTRIBUTES(bib_field.tag AS tag), XPATH('//*[local-name()="subfield"]',auth_field[1])::XML[]);
2863 replace_rules := replace_rules || ( bib_field.tag || main_entry.sf_list || E'[0~\\)' || auth_id || '$]' );
2871 XMLATTRIBUTES('http://www.loc.gov/MARC21/slim' AS xmlns),
2872 XMLELEMENT( name leader, '00881nam a2200193 4500'),
2876 XMLATTRIBUTES( '905' AS tag, ' ' AS ind1, ' ' AS ind2),
2879 XMLATTRIBUTES('r' AS code),
2880 ARRAY_TO_STRING(replace_rules,',')
2885 $f$ STABLE LANGUAGE PLPGSQL;
2887 CREATE OR REPLACE FUNCTION authority.generate_overlay_template ( BIGINT ) RETURNS TEXT AS $func$
2888 SELECT authority.generate_overlay_template( marc ) FROM authority.record_entry WHERE id = $1;
2889 $func$ LANGUAGE SQL;
2891 CREATE OR REPLACE FUNCTION vandelay.add_field ( target_xml TEXT, source_xml TEXT, field TEXT, force_add INT ) RETURNS TEXT AS $_$
2894 use MARC::File::XML (BinaryEncoding => 'UTF-8');
2898 MARC::Charset->assume_unicode(1);
2900 my $target_xml = shift;
2901 my $source_xml = shift;
2902 my $field_spec = shift;
2903 my $force_add = shift || 0;
2905 my $target_r = MARC::Record->new_from_xml( $target_xml );
2906 my $source_r = MARC::Record->new_from_xml( $source_xml );
2908 return $target_xml unless ($target_r && $source_r);
2910 my @field_list = split(',', $field_spec);
2913 for my $f (@field_list) {
2914 $f =~ s/^\s*//; $f =~ s/\s*$//;
2915 if ($f =~ /^(.{3})(\w*)(?:\[([^]]*)\])?$/) {
2921 $match =~ s/^\s*//; $match =~ s/\s*$//;
2922 $fields{$field} = { sf => [ split('', $sf) ] };
2924 my ($msf,$mre) = split('~', $match);
2925 if (length($msf) > 0 and length($mre) > 0) {
2926 $msf =~ s/^\s*//; $msf =~ s/\s*$//;
2927 $mre =~ s/^\s*//; $mre =~ s/\s*$//;
2928 $fields{$field}{match} = { sf => $msf, re => qr/$mre/ };
2934 for my $f ( keys %fields) {
2935 if ( @{$fields{$f}{sf}} ) {
2936 for my $from_field ($source_r->field( $f )) {
2937 my @tos = $target_r->field( $f );
2939 next if (exists($fields{$f}{match}) and !$force_add);
2940 my @new_fields = map { $_->clone } $source_r->field( $f );
2941 $target_r->insert_fields_ordered( @new_fields );
2943 for my $to_field (@tos) {
2944 if (exists($fields{$f}{match})) {
2945 next unless (grep { $_ =~ $fields{$f}{match}{re} } $to_field->subfield($fields{$f}{match}{sf}));
2947 my @new_sf = map { ($_ => $from_field->subfield($_)) } grep { defined($from_field->subfield($_)) } @{$fields{$f}{sf}};
2948 $to_field->add_subfields( @new_sf );
2953 my @new_fields = map { $_->clone } $source_r->field( $f );
2954 $target_r->insert_fields_ordered( @new_fields );
2958 $target_xml = $target_r->as_xml_record;
2959 $target_xml =~ s/^<\?.+?\?>$//mo;
2960 $target_xml =~ s/\n//sgo;
2961 $target_xml =~ s/>\s+</></sgo;
2965 $_$ LANGUAGE PLPERLU;
2968 CREATE INDEX by_heading ON authority.record_entry (authority.simple_normalize_heading(marc)) WHERE deleted IS FALSE or deleted = FALSE;
2970 INSERT INTO config.metabib_field ( id, field_class, name, label, format, xpath, search_field, facet_field) VALUES
2971 (28, 'identifier', 'authority_id', oils_i18n_gettext(28, 'Authority Record ID', 'cmf', 'label'), 'marcxml', '//marc:datafield/marc:subfield[@code="0"]', FALSE, TRUE);
2973 INSERT INTO config.marc21_rec_type_map (code, type_val, blvl_val) VALUES ('AUT','z',' ');
2974 INSERT INTO config.marc21_rec_type_map (code, type_val, blvl_val) VALUES ('MFHD','uvxy',' ');
2976 INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('ELvl', 'ldr', 'AUT', 17, 1, ' ');
2977 INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('Subj', '008', 'AUT', 11, 1, '|');
2978 INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('RecStat', 'ldr', 'AUT', 5, 1, 'n');
2980 INSERT INTO config.metabib_field_index_norm_map (field,norm,pos)
2984 FROM config.metabib_field m,
2985 config.index_normalizer i
2986 WHERE i.func = 'remove_paren_substring'
2989 SELECT SETVAL('authority.control_set_id_seq'::TEXT, 100);
2990 SELECT SETVAL('authority.control_set_authority_field_id_seq'::TEXT, 1000);
2991 SELECT SETVAL('authority.control_set_bib_field_id_seq'::TEXT, 1000);
2993 INSERT INTO authority.control_set (id, name, description) VALUES (
2995 oils_i18n_gettext('1','LoC','acs','name'),
2996 oils_i18n_gettext('1','Library of Congress standard authority record control semantics','acs','description')
2999 INSERT INTO authority.control_set_authority_field (id, control_set, main_entry, tag, sf_list, name) VALUES
3002 (1, 1, NULL, '100', 'abcdefklmnopqrstvxyz', oils_i18n_gettext('1','Heading -- Personal Name','acsaf','name')),
3003 (2, 1, NULL, '110', 'abcdefgklmnoprstvxyz', oils_i18n_gettext('2','Heading -- Corporate Name','acsaf','name')),
3004 (3, 1, NULL, '111', 'acdefgklnpqstvxyz', oils_i18n_gettext('3','Heading -- Meeting Name','acsaf','name')),
3005 (4, 1, NULL, '130', 'adfgklmnoprstvxyz', oils_i18n_gettext('4','Heading -- Uniform Title','acsaf','name')),
3006 (5, 1, NULL, '150', 'abvxyz', oils_i18n_gettext('5','Heading -- Topical Term','acsaf','name')),
3007 (6, 1, NULL, '151', 'avxyz', oils_i18n_gettext('6','Heading -- Geographic Name','acsaf','name')),
3008 (7, 1, NULL, '155', 'avxyz', oils_i18n_gettext('7','Heading -- Genre/Form Term','acsaf','name')),
3009 (8, 1, NULL, '180', 'vxyz', oils_i18n_gettext('8','Heading -- General Subdivision','acsaf','name')),
3010 (9, 1, NULL, '181', 'vxyz', oils_i18n_gettext('9','Heading -- Geographic Subdivision','acsaf','name')),
3011 (10, 1, NULL, '182', 'vxyz', oils_i18n_gettext('10','Heading -- Chronological Subdivision','acsaf','name')),
3012 (11, 1, NULL, '185', 'vxyz', oils_i18n_gettext('11','Heading -- Form Subdivision','acsaf','name')),
3013 (12, 1, NULL, '148', 'avxyz', oils_i18n_gettext('12','Heading -- Chronological Term','acsaf','name')),
3015 -- See Also From tracings
3016 (21, 1, 1, '500', 'abcdefiklmnopqrstvwxyz4', oils_i18n_gettext('21','See Also From Tracing -- Personal Name','acsaf','name')),
3017 (22, 1, 2, '510', 'abcdefgiklmnoprstvwxyz4', oils_i18n_gettext('22','See Also From Tracing -- Corporate Name','acsaf','name')),
3018 (23, 1, 3, '511', 'acdefgiklnpqstvwxyz4', oils_i18n_gettext('23','See Also From Tracing -- Meeting Name','acsaf','name')),
3019 (24, 1, 4, '530', 'adfgiklmnoprstvwxyz4', oils_i18n_gettext('24','See Also From Tracing -- Uniform Title','acsaf','name')),
3020 (25, 1, 5, '550', 'abivwxyz4', oils_i18n_gettext('25','See Also From Tracing -- Topical Term','acsaf','name')),
3021 (26, 1, 6, '551', 'aivwxyz4', oils_i18n_gettext('26','See Also From Tracing -- Geographic Name','acsaf','name')),
3022 (27, 1, 7, '555', 'aivwxyz4', oils_i18n_gettext('27','See Also From Tracing -- Genre/Form Term','acsaf','name')),
3023 (28, 1, 8, '580', 'ivwxyz4', oils_i18n_gettext('28','See Also From Tracing -- General Subdivision','acsaf','name')),
3024 (29, 1, 9, '581', 'ivwxyz4', oils_i18n_gettext('29','See Also From Tracing -- Geographic Subdivision','acsaf','name')),
3025 (30, 1, 10, '582', 'ivwxyz4', oils_i18n_gettext('30','See Also From Tracing -- Chronological Subdivision','acsaf','name')),
3026 (31, 1, 11, '585', 'ivwxyz4', oils_i18n_gettext('31','See Also From Tracing -- Form Subdivision','acsaf','name')),
3027 (32, 1, 12, '548', 'aivwxyz4', oils_i18n_gettext('32','See Also From Tracing -- Chronological Term','acsaf','name')),
3030 (41, 1, 1, '700', 'abcdefghjklmnopqrstvwxyz25', oils_i18n_gettext('41','Established Heading Linking Entry -- Personal Name','acsaf','name')),
3031 (42, 1, 2, '710', 'abcdefghklmnoprstvwxyz25', oils_i18n_gettext('42','Established Heading Linking Entry -- Corporate Name','acsaf','name')),
3032 (43, 1, 3, '711', 'acdefghklnpqstvwxyz25', oils_i18n_gettext('43','Established Heading Linking Entry -- Meeting Name','acsaf','name')),
3033 (44, 1, 4, '730', 'adfghklmnoprstvwxyz25', oils_i18n_gettext('44','Established Heading Linking Entry -- Uniform Title','acsaf','name')),
3034 (45, 1, 5, '750', 'abvwxyz25', oils_i18n_gettext('45','Established Heading Linking Entry -- Topical Term','acsaf','name')),
3035 (46, 1, 6, '751', 'avwxyz25', oils_i18n_gettext('46','Established Heading Linking Entry -- Geographic Name','acsaf','name')),
3036 (47, 1, 7, '755', 'avwxyz25', oils_i18n_gettext('47','Established Heading Linking Entry -- Genre/Form Term','acsaf','name')),
3037 (48, 1, 8, '780', 'vwxyz25', oils_i18n_gettext('48','Subdivision Linking Entry -- General Subdivision','acsaf','name')),
3038 (49, 1, 9, '781', 'vwxyz25', oils_i18n_gettext('49','Subdivision Linking Entry -- Geographic Subdivision','acsaf','name')),
3039 (50, 1, 10, '782', 'vwxyz25', oils_i18n_gettext('50','Subdivision Linking Entry -- Chronological Subdivision','acsaf','name')),
3040 (51, 1, 11, '785', 'vwxyz25', oils_i18n_gettext('51','Subdivision Linking Entry -- Form Subdivision','acsaf','name')),
3041 (52, 1, 12, '748', 'avwxyz25', oils_i18n_gettext('52','Established Heading Linking Entry -- Chronological Term','acsaf','name')),
3043 -- See From tracings
3044 (61, 1, 1, '400', 'abcdefiklmnopqrstvwxyz4', oils_i18n_gettext('61','See Also Tracing -- Personal Name','acsaf','name')),
3045 (62, 1, 2, '410', 'abcdefgiklmnoprstvwxyz4', oils_i18n_gettext('62','See Also Tracing -- Corporate Name','acsaf','name')),
3046 (63, 1, 3, '411', 'acdefgiklnpqstvwxyz4', oils_i18n_gettext('63','See Also Tracing -- Meeting Name','acsaf','name')),
3047 (64, 1, 4, '430', 'adfgiklmnoprstvwxyz4', oils_i18n_gettext('64','See Also Tracing -- Uniform Title','acsaf','name')),
3048 (65, 1, 5, '450', 'abivwxyz4', oils_i18n_gettext('65','See Also Tracing -- Topical Term','acsaf','name')),
3049 (66, 1, 6, '451', 'aivwxyz4', oils_i18n_gettext('66','See Also Tracing -- Geographic Name','acsaf','name')),
3050 (67, 1, 7, '455', 'aivwxyz4', oils_i18n_gettext('67','See Also Tracing -- Genre/Form Term','acsaf','name')),
3051 (68, 1, 8, '480', 'ivwxyz4', oils_i18n_gettext('68','See Also Tracing -- General Subdivision','acsaf','name')),
3052 (69, 1, 9, '481', 'ivwxyz4', oils_i18n_gettext('69','See Also Tracing -- Geographic Subdivision','acsaf','name')),
3053 (70, 1, 10, '482', 'ivwxyz4', oils_i18n_gettext('70','See Also Tracing -- Chronological Subdivision','acsaf','name')),
3054 (71, 1, 11, '485', 'ivwxyz4', oils_i18n_gettext('71','See Also Tracing -- Form Subdivision','acsaf','name')),
3055 (72, 1, 12, '448', 'aivwxyz4', oils_i18n_gettext('72','See Also Tracing -- Chronological Term','acsaf','name'));
3057 INSERT INTO authority.browse_axis (code,name,description,sorter) VALUES
3058 ('title','Title','Title axis','titlesort'),
3059 ('author','Author','Author axis','titlesort'),
3060 ('subject','Subject','Subject axis','titlesort'),
3061 ('topic','Topic','Topic Subject axis','titlesort');
3063 INSERT INTO authority.browse_axis_authority_field_map (axis,field) VALUES
3074 INSERT INTO authority.control_set_bib_field (tag, authority_field)
3075 SELECT '100', id FROM authority.control_set_authority_field WHERE tag IN ('100')
3077 SELECT '600', id FROM authority.control_set_authority_field WHERE tag IN ('100','180','181','182','185')
3079 SELECT '700', id FROM authority.control_set_authority_field WHERE tag IN ('100')
3081 SELECT '800', id FROM authority.control_set_authority_field WHERE tag IN ('100')
3084 SELECT '110', id FROM authority.control_set_authority_field WHERE tag IN ('110')
3086 SELECT '610', id FROM authority.control_set_authority_field WHERE tag IN ('110')
3088 SELECT '710', id FROM authority.control_set_authority_field WHERE tag IN ('110')
3090 SELECT '810', id FROM authority.control_set_authority_field WHERE tag IN ('110')
3093 SELECT '111', id FROM authority.control_set_authority_field WHERE tag IN ('111')
3095 SELECT '611', id FROM authority.control_set_authority_field WHERE tag IN ('111')
3097 SELECT '711', id FROM authority.control_set_authority_field WHERE tag IN ('111')
3099 SELECT '811', id FROM authority.control_set_authority_field WHERE tag IN ('111')
3102 SELECT '130', id FROM authority.control_set_authority_field WHERE tag IN ('130')
3104 SELECT '240', id FROM authority.control_set_authority_field WHERE tag IN ('130')
3106 SELECT '630', id FROM authority.control_set_authority_field WHERE tag IN ('130')
3108 SELECT '730', id FROM authority.control_set_authority_field WHERE tag IN ('130')
3110 SELECT '830', id FROM authority.control_set_authority_field WHERE tag IN ('130')
3113 SELECT '648', id FROM authority.control_set_authority_field WHERE tag IN ('148')
3116 SELECT '650', id FROM authority.control_set_authority_field WHERE tag IN ('150','180','181','182','185')
3118 SELECT '651', id FROM authority.control_set_authority_field WHERE tag IN ('151','180','181','182','185')
3120 SELECT '655', id FROM authority.control_set_authority_field WHERE tag IN ('155','180','181','182','185')
3123 INSERT INTO authority.thesaurus (code, name, control_set) VALUES
3124 ('a', oils_i18n_gettext('a','Library of Congress Subject Headings','at','name'), 1),
3125 ('b', oils_i18n_gettext('b',$$LC subject headings for children's literature$$,'at','name'), 1), -- silly vim '
3126 ('c', oils_i18n_gettext('c','Medical Subject Headings','at','name'), 1),
3127 ('d', oils_i18n_gettext('d','National Agricultural Library subject authority file','at','name'), 1),
3128 ('k', oils_i18n_gettext('k','Canadian Subject Headings','at','name'), 1),
3129 ('n', oils_i18n_gettext('n','Not applicable','at','name'), 1),
3130 ('r', oils_i18n_gettext('r','Art and Architecture Thesaurus','at','name'), 1),
3131 ('s', oils_i18n_gettext('s','Sears List of Subject Headings','at','name'), 1),
3132 ('v', oils_i18n_gettext('v','Repertoire de vedettes-matiere','at','name'), 1),
3133 ('z', oils_i18n_gettext('z','Other','at','name'), 1),
3134 ('|', oils_i18n_gettext('|','No attempt to code','at','name'), 1);
3136 CREATE OR REPLACE FUNCTION authority.map_thesaurus_to_control_set () RETURNS TRIGGER AS $func$
3138 IF NEW.control_set IS NULL THEN
3139 SELECT control_set INTO NEW.control_set
3140 FROM authority.thesaurus
3141 WHERE vandelay.marc21_extract_fixed_field(NEW.marc,'Subj') = code;
3146 $func$ LANGUAGE PLPGSQL;
3148 CREATE TRIGGER map_thesaurus_to_control_set BEFORE INSERT OR UPDATE ON authority.record_entry FOR EACH ROW EXECUTE PROCEDURE authority.map_thesaurus_to_control_set ();
3150 CREATE OR REPLACE FUNCTION authority.reingest_authority_rec_descriptor( auth_id BIGINT ) RETURNS VOID AS $func$
3152 DELETE FROM authority.rec_descriptor WHERE record = auth_id;
3153 INSERT INTO authority.rec_descriptor (record, record_status, encoding_level, thesaurus)
3155 vandelay.marc21_extract_fixed_field(marc,'RecStat'),
3156 vandelay.marc21_extract_fixed_field(marc,'ELvl'),
3157 vandelay.marc21_extract_fixed_field(marc,'Subj')
3158 FROM authority.record_entry
3162 $func$ LANGUAGE PLPGSQL;
3164 --Removed dupe authority.indexing_ingest_or_delete
3166 -- Evergreen DB patch 0577.schema.vandelay-item-import-copy-loc-ancestors.sql
3168 -- Ingest items copy location inheritance
3171 -- check whether patch can be applied
3172 SELECT evergreen.upgrade_deps_block_check('0577', :eg_version); -- berick
3174 CREATE OR REPLACE FUNCTION vandelay.ingest_items ( import_id BIGINT, attr_def_id BIGINT ) RETURNS SETOF vandelay.import_item AS $$
3185 deposit_amount TEXT;
3198 tmp_attr_set RECORD;
3199 attr_set vandelay.import_item%ROWTYPE;
3205 SELECT * INTO attr_def FROM vandelay.import_item_attr_definition WHERE id = attr_def_id;
3209 attr_set.definition := attr_def.id;
3211 -- Build the combined XPath
3215 WHEN attr_def.owning_lib IS NULL THEN 'null()'
3216 WHEN LENGTH( attr_def.owning_lib ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.owning_lib || '"]'
3217 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.owning_lib
3222 WHEN attr_def.circ_lib IS NULL THEN 'null()'
3223 WHEN LENGTH( attr_def.circ_lib ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_lib || '"]'
3224 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_lib
3229 WHEN attr_def.call_number IS NULL THEN 'null()'
3230 WHEN LENGTH( attr_def.call_number ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.call_number || '"]'
3231 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.call_number
3236 WHEN attr_def.copy_number IS NULL THEN 'null()'
3237 WHEN LENGTH( attr_def.copy_number ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.copy_number || '"]'
3238 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.copy_number
3243 WHEN attr_def.status IS NULL THEN 'null()'
3244 WHEN LENGTH( attr_def.status ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.status || '"]'
3245 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.status
3250 WHEN attr_def.location IS NULL THEN 'null()'
3251 WHEN LENGTH( attr_def.location ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.location || '"]'
3252 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.location
3257 WHEN attr_def.circulate IS NULL THEN 'null()'
3258 WHEN LENGTH( attr_def.circulate ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circulate || '"]'
3259 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circulate
3264 WHEN attr_def.deposit IS NULL THEN 'null()'
3265 WHEN LENGTH( attr_def.deposit ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.deposit || '"]'
3266 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.deposit
3271 WHEN attr_def.deposit_amount IS NULL THEN 'null()'
3272 WHEN LENGTH( attr_def.deposit_amount ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.deposit_amount || '"]'
3273 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.deposit_amount
3278 WHEN attr_def.ref IS NULL THEN 'null()'
3279 WHEN LENGTH( attr_def.ref ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.ref || '"]'
3280 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.ref
3285 WHEN attr_def.holdable IS NULL THEN 'null()'
3286 WHEN LENGTH( attr_def.holdable ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.holdable || '"]'
3287 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.holdable
3292 WHEN attr_def.price IS NULL THEN 'null()'
3293 WHEN LENGTH( attr_def.price ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.price || '"]'
3294 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.price
3299 WHEN attr_def.barcode IS NULL THEN 'null()'
3300 WHEN LENGTH( attr_def.barcode ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.barcode || '"]'
3301 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.barcode
3306 WHEN attr_def.circ_modifier IS NULL THEN 'null()'
3307 WHEN LENGTH( attr_def.circ_modifier ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_modifier || '"]'
3308 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_modifier
3313 WHEN attr_def.circ_as_type IS NULL THEN 'null()'
3314 WHEN LENGTH( attr_def.circ_as_type ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_as_type || '"]'
3315 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_as_type
3320 WHEN attr_def.alert_message IS NULL THEN 'null()'
3321 WHEN LENGTH( attr_def.alert_message ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.alert_message || '"]'
3322 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.alert_message
3327 WHEN attr_def.opac_visible IS NULL THEN 'null()'
3328 WHEN LENGTH( attr_def.opac_visible ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.opac_visible || '"]'
3329 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.opac_visible
3334 WHEN attr_def.pub_note IS NULL THEN 'null()'
3335 WHEN LENGTH( attr_def.pub_note ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.pub_note || '"]'
3336 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.pub_note
3340 WHEN attr_def.priv_note IS NULL THEN 'null()'
3341 WHEN LENGTH( attr_def.priv_note ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.priv_note || '"]'
3342 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.priv_note
3347 owning_lib || '|' ||
3349 call_number || '|' ||
3350 copy_number || '|' ||
3355 deposit_amount || '|' ||
3360 circ_modifier || '|' ||
3361 circ_as_type || '|' ||
3362 alert_message || '|' ||
3367 -- RAISE NOTICE 'XPath: %', xpath;
3371 FROM oils_xpath_table( 'id', 'marc', 'vandelay.queued_bib_record', xpath, 'id = ' || import_id )
3372 AS t( id INT, ol TEXT, clib TEXT, cn TEXT, cnum TEXT, cs TEXT, cl TEXT, circ TEXT,
3373 dep TEXT, dep_amount TEXT, r TEXT, hold TEXT, pr TEXT, bc TEXT, circ_mod TEXT,
3374 circ_as TEXT, amessage TEXT, note TEXT, pnote TEXT, opac_vis TEXT )
3377 tmp_attr_set.pr = REGEXP_REPLACE(tmp_attr_set.pr, E'[^0-9\\.]', '', 'g');
3378 tmp_attr_set.dep_amount = REGEXP_REPLACE(tmp_attr_set.dep_amount, E'[^0-9\\.]', '', 'g');
3380 tmp_attr_set.pr := NULLIF( tmp_attr_set.pr, '' );
3381 tmp_attr_set.dep_amount := NULLIF( tmp_attr_set.dep_amount, '' );
3383 SELECT id INTO attr_set.owning_lib FROM actor.org_unit WHERE shortname = UPPER(tmp_attr_set.ol); -- INT
3384 SELECT id INTO attr_set.circ_lib FROM actor.org_unit WHERE shortname = UPPER(tmp_attr_set.clib); -- INT
3385 SELECT id INTO attr_set.status FROM config.copy_status WHERE LOWER(name) = LOWER(tmp_attr_set.cs); -- INT
3388 -- search up the org unit tree for a matching copy location
3390 WITH RECURSIVE anscestor_depth AS (
3394 FROM actor.org_unit ou
3395 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
3396 WHERE ou.id = COALESCE(attr_set.owning_lib, attr_set.circ_lib)
3401 FROM actor.org_unit ou
3402 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
3403 JOIN anscestor_depth ot ON (ot.parent_ou = ou.id)
3404 ) SELECT cpl.id INTO attr_set.location
3405 FROM anscestor_depth a
3406 JOIN asset.copy_location cpl ON (cpl.owning_lib = a.id)
3407 WHERE LOWER(cpl.name) = LOWER(tmp_attr_set.cl)
3408 ORDER BY a.depth DESC
3411 attr_set.circulate :=
3412 LOWER( SUBSTRING( tmp_attr_set.circ, 1, 1)) IN ('t','y','1')
3413 OR LOWER(tmp_attr_set.circ) = 'circulating'; -- BOOL
3416 LOWER( SUBSTRING( tmp_attr_set.dep, 1, 1 ) ) IN ('t','y','1')
3417 OR LOWER(tmp_attr_set.dep) = 'deposit'; -- BOOL
3419 attr_set.holdable :=
3420 LOWER( SUBSTRING( tmp_attr_set.hold, 1, 1 ) ) IN ('t','y','1')
3421 OR LOWER(tmp_attr_set.hold) = 'holdable'; -- BOOL
3423 attr_set.opac_visible :=
3424 LOWER( SUBSTRING( tmp_attr_set.opac_vis, 1, 1 ) ) IN ('t','y','1')
3425 OR LOWER(tmp_attr_set.opac_vis) = 'visible'; -- BOOL
3428 LOWER( SUBSTRING( tmp_attr_set.r, 1, 1 ) ) IN ('t','y','1')
3429 OR LOWER(tmp_attr_set.r) = 'reference'; -- BOOL
3431 attr_set.copy_number := tmp_attr_set.cnum::INT; -- INT,
3432 attr_set.deposit_amount := tmp_attr_set.dep_amount::NUMERIC(6,2); -- NUMERIC(6,2),
3433 attr_set.price := tmp_attr_set.pr::NUMERIC(8,2); -- NUMERIC(8,2),
3435 attr_set.call_number := tmp_attr_set.cn; -- TEXT
3436 attr_set.barcode := tmp_attr_set.bc; -- TEXT,
3437 attr_set.circ_modifier := tmp_attr_set.circ_mod; -- TEXT,
3438 attr_set.circ_as_type := tmp_attr_set.circ_as; -- TEXT,
3439 attr_set.alert_message := tmp_attr_set.amessage; -- TEXT,
3440 attr_set.pub_note := tmp_attr_set.note; -- TEXT,
3441 attr_set.priv_note := tmp_attr_set.pnote; -- TEXT,
3442 attr_set.alert_message := tmp_attr_set.amessage; -- TEXT,
3444 RETURN NEXT attr_set;
3453 $$ LANGUAGE PLPGSQL;
3456 -- Evergreen DB patch XXXX.data.org-setting-ui.circ.billing.uncheck_bills_and_unfocus_payment_box.sql
3458 -- New org setting ui.circ.billing.uncheck_bills_and_unfocus_payment_box
3461 -- check whether patch can be applied
3462 SELECT evergreen.upgrade_deps_block_check('0584', :eg_version);
3464 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype )
3466 'ui.circ.billing.uncheck_bills_and_unfocus_payment_box',
3468 'ui.circ.billing.uncheck_bills_and_unfocus_payment_box',
3469 'GUI: Uncheck bills by default in the patron billing interface',
3474 'ui.circ.billing.uncheck_bills_and_unfocus_payment_box',
3475 'Uncheck bills by default in the patron billing interface,'
3476 || ' and focus on the Uncheck All button instead of the'
3477 || ' Payment Received field.',
3485 -- check whether patch can be applied
3486 SELECT evergreen.upgrade_deps_block_check('0585', :eg_version);
3488 INSERT into config.org_unit_setting_type
3489 ( name, label, description, datatype ) VALUES
3490 ( 'circ.checkout_fills_related_hold_exact_match_only',
3491 'Checkout Fills Related Hold On Valid Copy Only',
3492 'When filling related holds on checkout only match on items that are valid for opportunistic capture for the hold. Without this set a Title or Volume hold could match when the item is not holdable. With this set only holdable items will match.',
3496 -- check whether patch can be applied
3497 SELECT evergreen.upgrade_deps_block_check('0586', :eg_version);
3499 INSERT INTO permission.perm_list (id, code, description) VALUES (
3504 'Allows a user to authenticate and get a long-lived session (length configured in opensrf.xml)',
3510 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable)
3512 pgt.id, perm.id, aout.depth, FALSE
3514 permission.grp_tree pgt,
3515 permission.perm_list perm,
3516 actor.org_unit_type aout
3518 pgt.name = 'Users' AND
3519 aout.name = 'Consortium' AND
3520 perm.code = 'PERSISTENT_LOGIN';
3523 \qecho If this transaction succeeded, your users (staff and patrons) now have
3524 \qecho the PERSISTENT_LOGIN permission by default.
3528 -- Evergreen DB patch XXXX.data.org-setting-circ.offline.skip_foo_if_newer_status_changed_time.sql
3530 -- New org setting circ.offline.skip_checkout_if_newer_status_changed_time
3531 -- New org setting circ.offline.skip_renew_if_newer_status_changed_time
3532 -- New org setting circ.offline.skip_checkin_if_newer_status_changed_time
3535 -- check whether patch can be applied
3536 SELECT evergreen.upgrade_deps_block_check('0593', :eg_version);
3538 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype )
3540 'circ.offline.skip_checkout_if_newer_status_changed_time',
3542 'circ.offline.skip_checkout_if_newer_status_changed_time',
3543 'Offline: Skip offline checkout if newer item Status Changed Time.',
3548 'circ.offline.skip_checkout_if_newer_status_changed_time',
3549 'Skip offline checkout transaction (raise exception when'
3550 || ' processing) if item Status Changed Time is newer than the'
3551 || ' recorded transaction time. WARNING: The Reshelving to'
3552 || ' Available status rollover will trigger this.',
3558 'circ.offline.skip_renew_if_newer_status_changed_time',
3560 'circ.offline.skip_renew_if_newer_status_changed_time',
3561 'Offline: Skip offline renewal if newer item Status Changed Time.',
3566 'circ.offline.skip_renew_if_newer_status_changed_time',
3567 'Skip offline renewal transaction (raise exception when'
3568 || ' processing) if item Status Changed Time is newer than the'
3569 || ' recorded transaction time. WARNING: The Reshelving to'
3570 || ' Available status rollover will trigger this.',
3576 'circ.offline.skip_checkin_if_newer_status_changed_time',
3578 'circ.offline.skip_checkin_if_newer_status_changed_time',
3579 'Offline: Skip offline checkin if newer item Status Changed Time.',
3584 'circ.offline.skip_checkin_if_newer_status_changed_time',
3585 'Skip offline checkin transaction (raise exception when'
3586 || ' processing) if item Status Changed Time is newer than the'
3587 || ' recorded transaction time. WARNING: The Reshelving to'
3588 || ' Available status rollover will trigger this.',
3595 -- Evergreen DB patch YYYY.schema.acp_status_date_changed.sql
3597 -- Change trigger which updates copy status_changed_time to ignore the
3598 -- Reshelving->Available status rollover
3600 -- FIXME: 0039.schema.acp_status_date_changed.sql defines this the first time
3601 -- around, but along with the column itself, etc. And it gets modified with
3602 -- 0562.schema.copy_active_date.sql. Not sure how to use the supercedes /
3603 -- deprecate stuff for upgrade scripts, if it's even applicable when a given
3604 -- upgrade script is doing so much.
3606 -- check whether patch can be applied
3607 SELECT evergreen.upgrade_deps_block_check('0594', :eg_version);
3609 CREATE OR REPLACE FUNCTION asset.acp_status_changed()
3610 RETURNS TRIGGER AS $$
3612 IF NEW.status <> OLD.status AND NOT (NEW.status = 0 AND OLD.status = 7) THEN
3613 NEW.status_changed_time := now();
3614 IF NEW.active_date IS NULL AND NEW.status IN (SELECT id FROM config.copy_status WHERE copy_active = true) THEN
3615 NEW.active_date := now();
3620 $$ LANGUAGE plpgsql;
3622 -- Evergreen DB patch 0595.data.org-setting-ui.patron_search.result_cap.sql
3624 -- New org setting ui.patron_search.result_cap
3627 -- check whether patch can be applied
3628 SELECT evergreen.upgrade_deps_block_check('0595', :eg_version);
3630 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype )
3632 'ui.patron_search.result_cap',
3634 'ui.patron_search.result_cap',
3635 'GUI: Cap results in Patron Search at this number.',
3640 'ui.patron_search.result_cap',
3641 'So for example, if you search for John Doe, normally you would get'
3642 || ' at most 50 results. This setting allows you to raise or lower'
3650 -- Evergreen DB patch 0596.schema.vandelay-item-import-error-detail.sql
3652 -- check whether patch can be applied
3653 SELECT evergreen.upgrade_deps_block_check('0596', :eg_version);
3655 INSERT INTO vandelay.import_error ( code, description ) VALUES (
3656 'import.item.invalid.status', oils_i18n_gettext('import.item.invalid.status', 'Invalid value for "status"', 'vie', 'description') );
3657 INSERT INTO vandelay.import_error ( code, description ) VALUES (
3658 'import.item.invalid.price', oils_i18n_gettext('import.item.invalid.price', 'Invalid value for "price"', 'vie', 'description') );
3659 INSERT INTO vandelay.import_error ( code, description ) VALUES (
3660 'import.item.invalid.deposit_amount', oils_i18n_gettext('import.item.invalid.deposit_amount', 'Invalid value for "deposit_amount"', 'vie', 'description') );
3661 INSERT INTO vandelay.import_error ( code, description ) VALUES (
3662 'import.item.invalid.owning_lib', oils_i18n_gettext('import.item.invalid.owning_lib', 'Invalid value for "owning_lib"', 'vie', 'description') );
3663 INSERT INTO vandelay.import_error ( code, description ) VALUES (
3664 'import.item.invalid.circ_lib', oils_i18n_gettext('import.item.invalid.circ_lib', 'Invalid value for "circ_lib"', 'vie', 'description') );
3665 INSERT INTO vandelay.import_error ( code, description ) VALUES (
3666 'import.item.invalid.copy_number', oils_i18n_gettext('import.item.invalid.copy_number', 'Invalid value for "copy_number"', 'vie', 'description') );
3667 INSERT INTO vandelay.import_error ( code, description ) VALUES (
3668 'import.item.invalid.circ_as_type', oils_i18n_gettext('import.item.invalid.circ_as_type', 'Invalid value for "circ_as_type"', 'vie', 'description') );
3670 CREATE OR REPLACE FUNCTION vandelay.ingest_items ( import_id BIGINT, attr_def_id BIGINT ) RETURNS SETOF vandelay.import_item AS $$
3681 deposit_amount TEXT;
3694 tmp_attr_set RECORD;
3695 attr_set vandelay.import_item%ROWTYPE;
3702 SELECT * INTO attr_def FROM vandelay.import_item_attr_definition WHERE id = attr_def_id;
3706 attr_set.definition := attr_def.id;
3708 -- Build the combined XPath
3712 WHEN attr_def.owning_lib IS NULL THEN 'null()'
3713 WHEN LENGTH( attr_def.owning_lib ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.owning_lib || '"]'
3714 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.owning_lib
3719 WHEN attr_def.circ_lib IS NULL THEN 'null()'
3720 WHEN LENGTH( attr_def.circ_lib ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_lib || '"]'
3721 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_lib
3726 WHEN attr_def.call_number IS NULL THEN 'null()'
3727 WHEN LENGTH( attr_def.call_number ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.call_number || '"]'
3728 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.call_number
3733 WHEN attr_def.copy_number IS NULL THEN 'null()'
3734 WHEN LENGTH( attr_def.copy_number ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.copy_number || '"]'
3735 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.copy_number
3740 WHEN attr_def.status IS NULL THEN 'null()'
3741 WHEN LENGTH( attr_def.status ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.status || '"]'
3742 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.status
3747 WHEN attr_def.location IS NULL THEN 'null()'
3748 WHEN LENGTH( attr_def.location ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.location || '"]'
3749 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.location
3754 WHEN attr_def.circulate IS NULL THEN 'null()'
3755 WHEN LENGTH( attr_def.circulate ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circulate || '"]'
3756 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circulate
3761 WHEN attr_def.deposit IS NULL THEN 'null()'
3762 WHEN LENGTH( attr_def.deposit ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.deposit || '"]'
3763 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.deposit
3768 WHEN attr_def.deposit_amount IS NULL THEN 'null()'
3769 WHEN LENGTH( attr_def.deposit_amount ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.deposit_amount || '"]'
3770 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.deposit_amount
3775 WHEN attr_def.ref IS NULL THEN 'null()'
3776 WHEN LENGTH( attr_def.ref ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.ref || '"]'
3777 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.ref
3782 WHEN attr_def.holdable IS NULL THEN 'null()'
3783 WHEN LENGTH( attr_def.holdable ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.holdable || '"]'
3784 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.holdable
3789 WHEN attr_def.price IS NULL THEN 'null()'
3790 WHEN LENGTH( attr_def.price ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.price || '"]'
3791 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.price
3796 WHEN attr_def.barcode IS NULL THEN 'null()'
3797 WHEN LENGTH( attr_def.barcode ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.barcode || '"]'
3798 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.barcode
3803 WHEN attr_def.circ_modifier IS NULL THEN 'null()'
3804 WHEN LENGTH( attr_def.circ_modifier ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_modifier || '"]'
3805 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_modifier
3810 WHEN attr_def.circ_as_type IS NULL THEN 'null()'
3811 WHEN LENGTH( attr_def.circ_as_type ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_as_type || '"]'
3812 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_as_type
3817 WHEN attr_def.alert_message IS NULL THEN 'null()'
3818 WHEN LENGTH( attr_def.alert_message ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.alert_message || '"]'
3819 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.alert_message
3824 WHEN attr_def.opac_visible IS NULL THEN 'null()'
3825 WHEN LENGTH( attr_def.opac_visible ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.opac_visible || '"]'
3826 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.opac_visible
3831 WHEN attr_def.pub_note IS NULL THEN 'null()'
3832 WHEN LENGTH( attr_def.pub_note ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.pub_note || '"]'
3833 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.pub_note
3837 WHEN attr_def.priv_note IS NULL THEN 'null()'
3838 WHEN LENGTH( attr_def.priv_note ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.priv_note || '"]'
3839 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.priv_note
3844 owning_lib || '|' ||
3846 call_number || '|' ||
3847 copy_number || '|' ||
3852 deposit_amount || '|' ||
3857 circ_modifier || '|' ||
3858 circ_as_type || '|' ||
3859 alert_message || '|' ||
3866 FROM oils_xpath_table( 'id', 'marc', 'vandelay.queued_bib_record', xpath, 'id = ' || import_id )
3867 AS t( id INT, ol TEXT, clib TEXT, cn TEXT, cnum TEXT, cs TEXT, cl TEXT, circ TEXT,
3868 dep TEXT, dep_amount TEXT, r TEXT, hold TEXT, pr TEXT, bc TEXT, circ_mod TEXT,
3869 circ_as TEXT, amessage TEXT, note TEXT, pnote TEXT, opac_vis TEXT )
3872 attr_set.import_error := NULL;
3873 attr_set.error_detail := NULL;
3874 attr_set.deposit_amount := NULL;
3875 attr_set.copy_number := NULL;
3876 attr_set.price := NULL;
3878 IF tmp_attr_set.pr != '' THEN
3879 tmp_str = REGEXP_REPLACE(tmp_attr_set.pr, E'[^0-9\\.]', '', 'g');
3880 IF tmp_str = '' THEN
3881 attr_set.import_error := 'import.item.invalid.price';
3882 attr_set.error_detail := tmp_attr_set.pr; -- original value
3883 RETURN NEXT attr_set; CONTINUE;
3885 attr_set.price := tmp_str::NUMERIC(8,2);
3888 IF tmp_attr_set.dep_amount != '' THEN
3889 tmp_str = REGEXP_REPLACE(tmp_attr_set.dep_amount, E'[^0-9\\.]', '', 'g');
3890 IF tmp_str = '' THEN
3891 attr_set.import_error := 'import.item.invalid.deposit_amount';
3892 attr_set.error_detail := tmp_attr_set.dep_amount;
3893 RETURN NEXT attr_set; CONTINUE;
3895 attr_set.deposit_amount := tmp_str::NUMERIC(8,2);
3898 IF tmp_attr_set.cnum != '' THEN
3899 tmp_str = REGEXP_REPLACE(tmp_attr_set.cnum, E'[^0-9]', '', 'g');
3900 IF tmp_str = '' THEN
3901 attr_set.import_error := 'import.item.invalid.copy_number';
3902 attr_set.error_detail := tmp_attr_set.cnum;
3903 RETURN NEXT attr_set; CONTINUE;
3905 attr_set.copy_number := tmp_str::INT;
3908 IF tmp_attr_set.ol != '' THEN
3909 SELECT id INTO attr_set.owning_lib FROM actor.org_unit WHERE shortname = UPPER(tmp_attr_set.ol); -- INT
3911 attr_set.import_error := 'import.item.invalid.owning_lib';
3912 attr_set.error_detail := tmp_attr_set.ol;
3913 RETURN NEXT attr_set; CONTINUE;
3917 IF tmp_attr_set.clib != '' THEN
3918 SELECT id INTO attr_set.circ_lib FROM actor.org_unit WHERE shortname = UPPER(tmp_attr_set.clib); -- INT
3920 attr_set.import_error := 'import.item.invalid.circ_lib';
3921 attr_set.error_detail := tmp_attr_set.clib;
3922 RETURN NEXT attr_set; CONTINUE;
3926 IF tmp_attr_set.cs != '' THEN
3927 SELECT id INTO attr_set.status FROM config.copy_status WHERE LOWER(name) = LOWER(tmp_attr_set.cs); -- INT
3929 attr_set.import_error := 'import.item.invalid.status';
3930 attr_set.error_detail := tmp_attr_set.cs;
3931 RETURN NEXT attr_set; CONTINUE;
3935 IF tmp_attr_set.circ_mod != '' THEN
3936 SELECT code INTO attr_set.circ_modifier FROM config.circ_modifier WHERE code = tmp_attr_set.circ_mod;
3938 attr_set.import_error := 'import.item.invalid.circ_modifier';
3939 attr_set.error_detail := tmp_attr_set.circ_mod;
3940 RETURN NEXT attr_set; CONTINUE;
3944 IF tmp_attr_set.circ_as != '' THEN
3945 SELECT code INTO attr_set.circ_as_type FROM config.coded_value_map WHERE ctype = 'item_type' AND code = tmp_attr_set.circ_as;
3947 attr_set.import_error := 'import.item.invalid.circ_as_type';
3948 attr_set.error_detail := tmp_attr_set.circ_as;
3949 RETURN NEXT attr_set; CONTINUE;
3953 IF tmp_attr_set.cl != '' THEN
3955 -- search up the org unit tree for a matching copy location
3956 WITH RECURSIVE anscestor_depth AS (
3960 FROM actor.org_unit ou
3961 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
3962 WHERE ou.id = COALESCE(attr_set.owning_lib, attr_set.circ_lib)
3967 FROM actor.org_unit ou
3968 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
3969 JOIN anscestor_depth ot ON (ot.parent_ou = ou.id)
3970 ) SELECT cpl.id INTO attr_set.location
3971 FROM anscestor_depth a
3972 JOIN asset.copy_location cpl ON (cpl.owning_lib = a.id)
3973 WHERE LOWER(cpl.name) = LOWER(tmp_attr_set.cl)
3974 ORDER BY a.depth DESC
3978 attr_set.import_error := 'import.item.invalid.location';
3979 attr_set.error_detail := tmp_attr_set.cs;
3980 RETURN NEXT attr_set; CONTINUE;
3984 attr_set.circulate :=
3985 LOWER( SUBSTRING( tmp_attr_set.circ, 1, 1)) IN ('t','y','1')
3986 OR LOWER(tmp_attr_set.circ) = 'circulating'; -- BOOL
3989 LOWER( SUBSTRING( tmp_attr_set.dep, 1, 1 ) ) IN ('t','y','1')
3990 OR LOWER(tmp_attr_set.dep) = 'deposit'; -- BOOL
3992 attr_set.holdable :=
3993 LOWER( SUBSTRING( tmp_attr_set.hold, 1, 1 ) ) IN ('t','y','1')
3994 OR LOWER(tmp_attr_set.hold) = 'holdable'; -- BOOL
3996 attr_set.opac_visible :=
3997 LOWER( SUBSTRING( tmp_attr_set.opac_vis, 1, 1 ) ) IN ('t','y','1')
3998 OR LOWER(tmp_attr_set.opac_vis) = 'visible'; -- BOOL
4001 LOWER( SUBSTRING( tmp_attr_set.r, 1, 1 ) ) IN ('t','y','1')
4002 OR LOWER(tmp_attr_set.r) = 'reference'; -- BOOL
4004 attr_set.call_number := tmp_attr_set.cn; -- TEXT
4005 attr_set.barcode := tmp_attr_set.bc; -- TEXT,
4006 attr_set.alert_message := tmp_attr_set.amessage; -- TEXT,
4007 attr_set.pub_note := tmp_attr_set.note; -- TEXT,
4008 attr_set.priv_note := tmp_attr_set.pnote; -- TEXT,
4009 attr_set.alert_message := tmp_attr_set.amessage; -- TEXT,
4011 RETURN NEXT attr_set;
4020 $$ LANGUAGE PLPGSQL;
4022 CREATE OR REPLACE FUNCTION vandelay.ingest_bib_items ( ) RETURNS TRIGGER AS $func$
4025 item_data vandelay.import_item%ROWTYPE;
4028 IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
4032 SELECT item_attr_def INTO attr_def FROM vandelay.bib_queue WHERE id = NEW.queue;
4034 FOR item_data IN SELECT * FROM vandelay.ingest_items( NEW.id::BIGINT, attr_def ) LOOP
4035 INSERT INTO vandelay.import_item (
4061 item_data.definition,
4062 item_data.owning_lib,
4064 item_data.call_number,
4065 item_data.copy_number,
4068 item_data.circulate,
4070 item_data.deposit_amount,
4075 item_data.circ_modifier,
4076 item_data.circ_as_type,
4077 item_data.alert_message,
4079 item_data.priv_note,
4080 item_data.opac_visible,
4081 item_data.import_error,
4082 item_data.error_detail
4088 $func$ LANGUAGE PLPGSQL;
4090 -- Evergreen DB patch XXXX.schema.vandelay.bib_match_isxn_caseless.sql
4093 -- check whether patch can be applied
4094 SELECT evergreen.upgrade_deps_block_check('0597', :eg_version);
4096 CREATE INDEX metabib_full_rec_isxn_caseless_idx
4097 ON metabib.real_full_rec (LOWER(value))
4098 WHERE tag IN ('020', '022', '024');
4101 CREATE OR REPLACE FUNCTION vandelay.flatten_marc_hstore(
4103 ) RETURNS HSTORE AS $$
4107 ARRAY_ACCUM(tag || (COALESCE(subfield, ''))),
4113 CASE WHEN tag IN ('020', '022', '024') THEN -- caseless
4114 ARRAY_ACCUM(LOWER(value))::TEXT
4116 ARRAY_ACCUM(value)::TEXT
4118 FROM vandelay.flatten_marc(record_xml)
4119 GROUP BY tag, subfield ORDER BY tag, subfield
4123 $$ LANGUAGE PLPGSQL;
4125 CREATE OR REPLACE FUNCTION vandelay._get_expr_push_jrow(
4126 node vandelay.match_set_point
4127 ) RETURNS VOID AS $$
4135 -- remember $1 is tags_rstore, and $2 is svf_rstore
4145 IF node.tag IS NOT NULL THEN
4146 caseless := (node.tag IN ('020', '022', '024'));
4148 IF node.subfield IS NOT NULL THEN
4149 tagkey := tagkey || node.subfield;
4153 my_alias := 'n' || node.id::TEXT;
4155 jrow := 'LEFT JOIN (SELECT *, ' || node.quality ||
4156 ' AS quality FROM metabib.';
4157 IF node.tag IS NOT NULL THEN
4158 jrow := jrow || 'full_rec) ' || my_alias || ' ON (' ||
4159 my_alias || '.record = bre.id AND ' || my_alias || '.tag = ''' ||
4161 IF node.subfield IS NOT NULL THEN
4162 jrow := jrow || ' AND ' || my_alias || '.subfield = ''' ||
4163 node.subfield || '''';
4165 jrow := jrow || ' AND (';
4168 jrow := jrow || 'LOWER(' || my_alias || '.value) ' || op;
4170 jrow := jrow || my_alias || '.value ' || op;
4173 jrow := jrow || ' ANY(($1->''' || tagkey || ''')::TEXT[])))';
4175 jrow := jrow || 'record_attr) ' || my_alias || ' ON (' ||
4176 my_alias || '.id = bre.id AND (' ||
4177 my_alias || '.attrs->''' || node.svf ||
4178 ''' ' || op || ' $2->''' || node.svf || '''))';
4180 INSERT INTO _vandelay_tmp_jrows (j) VALUES (jrow);
4182 $$ LANGUAGE PLPGSQL;
4184 -- Evergreen DB patch 0598.schema.vandelay_one_match_per.sql
4188 -- check whether patch can be applied
4189 SELECT evergreen.upgrade_deps_block_check('0598', :eg_version);
4191 CREATE OR REPLACE FUNCTION vandelay.match_set_test_marcxml(
4192 match_set_id INTEGER, record_xml TEXT
4193 ) RETURNS SETOF vandelay.match_set_test_result AS $$
4204 tags_rstore := vandelay.flatten_marc_hstore(record_xml);
4205 svf_rstore := vandelay.extract_rec_attrs(record_xml);
4207 CREATE TEMPORARY TABLE _vandelay_tmp_qrows (q INTEGER);
4208 CREATE TEMPORARY TABLE _vandelay_tmp_jrows (j TEXT);
4210 -- generate the where clause and return that directly (into wq), and as
4211 -- a side-effect, populate the _vandelay_tmp_[qj]rows tables.
4212 wq := vandelay.get_expr_from_match_set(match_set_id);
4214 query_ := 'SELECT DISTINCT(bre.id) AS record, ';
4216 -- qrows table is for the quality bits we add to the SELECT clause
4217 SELECT ARRAY_TO_STRING(
4218 ARRAY_ACCUM('COALESCE(n' || q::TEXT || '.quality, 0)'), ' + '
4219 ) INTO coal FROM _vandelay_tmp_qrows;
4221 -- our query string so far is the SELECT clause and the inital FROM.
4222 -- no JOINs yet nor the WHERE clause
4223 query_ := query_ || coal || ' AS quality ' || E'\n' ||
4224 'FROM biblio.record_entry bre ';
4226 -- jrows table is for the joins we must make (and the real text conditions)
4227 SELECT ARRAY_TO_STRING(ARRAY_ACCUM(j), E'\n') INTO joins
4228 FROM _vandelay_tmp_jrows;
4230 -- add those joins and the where clause to our query.
4231 query_ := query_ || joins || E'\n' || 'WHERE ' || wq || ' AND not bre.deleted';
4233 -- this will return rows of record,quality
4234 FOR rec IN EXECUTE query_ USING tags_rstore, svf_rstore LOOP
4238 DROP TABLE _vandelay_tmp_qrows;
4239 DROP TABLE _vandelay_tmp_jrows;
4243 $$ LANGUAGE PLPGSQL;
4245 -- Evergreen DB patch 0606.schema.czs_use_perm_column.sql
4247 -- This adds a column to config.z3950_source called use_perm.
4248 -- The idea is that if a permission is set for a given source,
4249 -- then staff will need the referenced permission to use that
4253 -- check whether patch can be applied
4254 SELECT evergreen.upgrade_deps_block_check('0606', :eg_version);
4256 ALTER TABLE config.z3950_source
4257 ADD COLUMN use_perm INT REFERENCES permission.perm_list (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
4259 COMMENT ON COLUMN config.z3950_source.use_perm IS $$
4260 If set, this permission is required for the source to be listed in the staff
4261 client Z39.50 interface. Similar to permission.grp_tree.application_perm.
4264 -- Evergreen DB patch 0608.data.vandelay-export-error-match-info.sql
4269 -- check whether patch can be applied
4270 SELECT evergreen.upgrade_deps_block_check('0608', :eg_version);
4272 -- Add vqbr.import_error, vqbr.error_detail, and vqbr.matches.size to queue print output
4274 UPDATE action_trigger.event_definition SET template = $$
4277 Queue ID: [% target.0.queue.id %]
4278 Queue Name: [% target.0.queue.name %]
4279 Queue Type: [% target.0.queue.queue_type %]
4280 Complete? [% target.0.queue.complete %]
4282 [% FOR vqbr IN target %]
4284 Title of work | [% helpers.get_queued_bib_attr('title',vqbr.attributes) %]
4285 Author of work | [% helpers.get_queued_bib_attr('author',vqbr.attributes) %]
4286 Language of work | [% helpers.get_queued_bib_attr('language',vqbr.attributes) %]
4287 Pagination | [% helpers.get_queued_bib_attr('pagination',vqbr.attributes) %]
4288 ISBN | [% helpers.get_queued_bib_attr('isbn',vqbr.attributes) %]
4289 ISSN | [% helpers.get_queued_bib_attr('issn',vqbr.attributes) %]
4290 Price | [% helpers.get_queued_bib_attr('price',vqbr.attributes) %]
4291 Accession Number | [% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) %]
4292 TCN Value | [% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) %]
4293 TCN Source | [% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) %]
4294 Internal ID | [% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) %]
4295 Publisher | [% helpers.get_queued_bib_attr('publisher',vqbr.attributes) %]
4296 Publication Date | [% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) %]
4297 Edition | [% helpers.get_queued_bib_attr('edition',vqbr.attributes) %]
4298 Item Barcode | [% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) %]
4299 Import Error | [% vqbr.import_error %]
4300 Error Detail | [% vqbr.error_detail %]
4301 Match Count | [% vqbr.matches.size %]
4309 -- Do the same for the CVS version
4311 UPDATE action_trigger.event_definition SET template = $$
4313 "Title of work","Author of work","Language of work","Pagination","ISBN","ISSN","Price","Accession Number","TCN Value","TCN Source","Internal ID","Publisher","Publication Date","Edition","Item Barcode","Import Error","Error Detail","Match Count"
4314 [% FOR vqbr IN target %]"[% helpers.get_queued_bib_attr('title',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('author',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('language',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('pagination',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('isbn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('issn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('price',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('publisher',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('edition',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) | replace('"', '""') %]","[% vqbr.import_error | replace('"', '""') %]","[% vqbr.error_detail | replace('"', '""') %]","[% vqbr.matches.size %]"
4319 -- Add matches to the env for both
4320 INSERT INTO action_trigger.environment (event_def, path) VALUES (39, 'matches');
4321 INSERT INTO action_trigger.environment (event_def, path) VALUES (40, 'matches');
4324 -- Evergreen DB patch XXXX.data.acq-copy-creator-from-receiver.sql
4326 -- check whether patch can be applied
4327 SELECT evergreen.upgrade_deps_block_check('0609', :eg_version);
4329 ALTER TABLE acq.lineitem_detail
4330 ADD COLUMN receiver INT REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED;
4333 -- Evergreen DB patch XXXX.data.acq-copy-creator-from-receiver.sql
4335 -- check whether patch can be applied
4336 SELECT evergreen.upgrade_deps_block_check('0610', :eg_version);
4338 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype ) VALUES (
4339 'acq.copy_creator_uses_receiver',
4341 'acq.copy_creator_uses_receiver',
4342 'Acq: Set copy creator as receiver',
4347 'acq.copy_creator_uses_receiver',
4348 'When receiving a copy in acquisitions, set the copy "creator" to be the staff that received the copy',
4355 -- Evergreen DB patch 0611.data.magic_macros.sql
4357 -- check whether patch can be applied
4358 SELECT evergreen.upgrade_deps_block_check('0611', :eg_version);
4360 INSERT into config.org_unit_setting_type
4361 ( name, label, description, datatype ) VALUES
4363 'circ.staff_client.receipt.header_text',
4365 'circ.staff_client.receipt.header_text',
4366 'Receipt Template: Content of header_text include',
4371 'circ.staff_client.receipt.header_text',
4372 'Text/HTML/Macros to be inserted into receipt templates in place of %INCLUDE(header_text)%',
4379 'circ.staff_client.receipt.footer_text',
4381 'circ.staff_client.receipt.footer_text',
4382 'Receipt Template: Content of footer_text include',
4387 'circ.staff_client.receipt.footer_text',
4388 'Text/HTML/Macros to be inserted into receipt templates in place of %INCLUDE(footer_text)%',
4395 'circ.staff_client.receipt.notice_text',
4397 'circ.staff_client.receipt.notice_text',
4398 'Receipt Template: Content of notice_text include',
4403 'circ.staff_client.receipt.notice_text',
4404 'Text/HTML/Macros to be inserted into receipt templates in place of %INCLUDE(notice_text)%',
4411 'circ.staff_client.receipt.alert_text',
4413 'circ.staff_client.receipt.alert_text',
4414 'Receipt Template: Content of alert_text include',
4419 'circ.staff_client.receipt.alert_text',
4420 'Text/HTML/Macros to be inserted into receipt templates in place of %INCLUDE(alert_text)%',
4427 'circ.staff_client.receipt.event_text',
4429 'circ.staff_client.receipt.event_text',
4430 'Receipt Template: Content of event_text include',
4435 'circ.staff_client.receipt.event_text',
4436 'Text/HTML/Macros to be inserted into receipt templates in place of %INCLUDE(event_text)%',
4443 -- Evergreen DB patch 0612.schema.authority_overlay_protection.sql
4447 -- check whether patch can be applied
4448 SELECT evergreen.upgrade_deps_block_check('0612', :eg_version);
4450 -- FIXME: add/check SQL statements to perform the upgrade
4452 -- Function to generate an ephemeral overlay template from an authority record
4453 CREATE OR REPLACE FUNCTION authority.generate_overlay_template (source_xml TEXT) RETURNS TEXT AS $f$
4456 main_entry authority.control_set_authority_field%ROWTYPE;
4457 bib_field authority.control_set_bib_field%ROWTYPE;
4458 auth_id INT DEFAULT oils_xpath_string('//*[@tag="901"]/*[local-name()="subfield" and @code="c"]', source_xml)::INT;
4459 replace_data XML[] DEFAULT '{}'::XML[];
4460 replace_rules TEXT[] DEFAULT '{}'::TEXT[];
4463 IF auth_id IS NULL THEN
4467 -- Default to the LoC controll set
4468 SELECT control_set INTO cset FROM authority.record_entry WHERE id = auth_id;
4470 -- if none, make a best guess
4471 IF cset IS NULL THEN
4472 SELECT control_set INTO cset
4473 FROM authority.control_set_authority_field
4475 SELECT UNNEST(XPATH('//*[starts-with(@tag,"1")]/@tag',marc::XML)::TEXT[])
4476 FROM authority.record_entry
4482 -- if STILL none, no-op change
4483 IF cset IS NULL THEN
4486 XMLATTRIBUTES('http://www.loc.gov/MARC21/slim' AS xmlns),
4487 XMLELEMENT( name leader, '00881nam a2200193 4500'),
4490 XMLATTRIBUTES( '905' AS tag, ' ' AS ind1, ' ' AS ind2),
4493 XMLATTRIBUTES('d' AS code),
4500 FOR main_entry IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset LOOP
4501 auth_field := XPATH('//*[@tag="'||main_entry.tag||'"][1]',source_xml::XML);
4502 IF ARRAY_LENGTH(auth_field,1) > 0 THEN
4503 FOR bib_field IN SELECT * FROM authority.control_set_bib_field WHERE authority_field = main_entry.id LOOP
4504 replace_data := replace_data || XMLELEMENT( name datafield, XMLATTRIBUTES(bib_field.tag AS tag), XPATH('//*[local-name()="subfield"]',auth_field[1])::XML[]);
4505 replace_rules := replace_rules || ( bib_field.tag || main_entry.sf_list || E'[0~\\)' || auth_id || '$]' );
4513 XMLATTRIBUTES('http://www.loc.gov/MARC21/slim' AS xmlns),
4514 XMLELEMENT( name leader, '00881nam a2200193 4500'),
4518 XMLATTRIBUTES( '905' AS tag, ' ' AS ind1, ' ' AS ind2),
4521 XMLATTRIBUTES('r' AS code),
4522 ARRAY_TO_STRING(replace_rules,',')
4527 $f$ STABLE LANGUAGE PLPGSQL;
4531 -- Evergreen DB patch 0613.schema.vandelay_isxn_normalization.sql
4535 -- check whether patch can be applied
4536 SELECT evergreen.upgrade_deps_block_check('0613', :eg_version);
4538 CREATE OR REPLACE FUNCTION vandelay.flatten_marc_hstore(
4540 ) RETURNS HSTORE AS $func$
4544 ARRAY_ACCUM(tag || (COALESCE(subfield, ''))),
4548 SELECT tag, subfield, ARRAY_ACCUM(value)::TEXT AS value
4551 CASE WHEN tag = '020' THEN -- caseless -- isbn
4552 LOWER((REGEXP_MATCHES(value,$$^(\S{10,17})$$))[1] || '%')
4553 WHEN tag = '022' THEN -- caseless -- issn
4554 LOWER((REGEXP_MATCHES(value,$$^(\S{4}[- ]?\S{4})$$))[1] || '%')
4555 WHEN tag = '024' THEN -- caseless -- upc (other)
4560 FROM vandelay.flatten_marc(record_xml)) x
4561 GROUP BY tag, subfield ORDER BY tag, subfield
4565 $func$ LANGUAGE PLPGSQL;
4567 CREATE OR REPLACE FUNCTION vandelay._get_expr_push_jrow(
4568 node vandelay.match_set_point
4569 ) RETURNS VOID AS $$
4577 -- remember $1 is tags_rstore, and $2 is svf_rstore
4581 IF node.tag IS NOT NULL THEN
4582 caseless := (node.tag IN ('020', '022', '024'));
4584 IF node.subfield IS NOT NULL THEN
4585 tagkey := tagkey || node.subfield;
4603 my_alias := 'n' || node.id::TEXT;
4605 jrow := 'LEFT JOIN (SELECT *, ' || node.quality ||
4606 ' AS quality FROM metabib.';
4607 IF node.tag IS NOT NULL THEN
4608 jrow := jrow || 'full_rec) ' || my_alias || ' ON (' ||
4609 my_alias || '.record = bre.id AND ' || my_alias || '.tag = ''' ||
4611 IF node.subfield IS NOT NULL THEN
4612 jrow := jrow || ' AND ' || my_alias || '.subfield = ''' ||
4613 node.subfield || '''';
4615 jrow := jrow || ' AND (';
4618 jrow := jrow || 'LOWER(' || my_alias || '.value) ' || op;
4620 jrow := jrow || my_alias || '.value ' || op;
4623 jrow := jrow || ' ANY(($1->''' || tagkey || ''')::TEXT[])))';
4625 jrow := jrow || 'record_attr) ' || my_alias || ' ON (' ||
4626 my_alias || '.id = bre.id AND (' ||
4627 my_alias || '.attrs->''' || node.svf ||
4628 ''' ' || op || ' $2->''' || node.svf || '''))';
4630 INSERT INTO _vandelay_tmp_jrows (j) VALUES (jrow);
4632 $$ LANGUAGE PLPGSQL;
4636 -- Evergreen DB patch XXXX.schema.generic-mapping-index-normalizer.sql
4639 -- check whether patch can be applied
4640 SELECT evergreen.upgrade_deps_block_check('0615', :eg_version);
4642 -- evergreen.generic_map_normalizer
4644 CREATE OR REPLACE FUNCTION evergreen.generic_map_normalizer ( TEXT, TEXT ) RETURNS TEXT AS $f$
4648 my $default = $string;
4651 while (/^\s*?(.*?)\s*?=>\s*?(\S+)\s*/) {
4655 $map{$2} = [split(/\s*,\s*/, $1)];
4660 for my $key ( keys %map ) {
4661 return $key if (grep { $_ eq $string } @{ $map{$key} });
4666 $f$ LANGUAGE PLPERLU;
4668 -- evergreen.generic_map_normalizer
4670 INSERT INTO config.index_normalizer (name, description, func, param_count) VALUES (
4671 'Generic Mapping Normalizer',
4672 'Map values or sets of values to new values',
4673 'generic_map_normalizer',
4678 SELECT evergreen.upgrade_deps_block_check('0616', :eg_version);
4680 CREATE OR REPLACE FUNCTION actor.org_unit_prox_update () RETURNS TRIGGER as $$
4684 IF TG_OP = 'DELETE' THEN
4686 DELETE FROM actor.org_unit_proximity WHERE (from_org = OLD.id or to_org= OLD.id);
4690 IF TG_OP = 'UPDATE' THEN
4692 IF NEW.parent_ou <> OLD.parent_ou THEN
4694 DELETE FROM actor.org_unit_proximity WHERE (from_org = OLD.id or to_org= OLD.id);
4695 INSERT INTO actor.org_unit_proximity (from_org, to_org, prox)
4696 SELECT l.id, r.id, actor.org_unit_proximity(l.id,r.id)
4697 FROM actor.org_unit l, actor.org_unit r
4698 WHERE (l.id = NEW.id or r.id = NEW.id);
4704 IF TG_OP = 'INSERT' THEN
4706 INSERT INTO actor.org_unit_proximity (from_org, to_org, prox)
4707 SELECT l.id, r.id, actor.org_unit_proximity(l.id,r.id)
4708 FROM actor.org_unit l, actor.org_unit r
4709 WHERE (l.id = NEW.id or r.id = NEW.id);
4716 $$ LANGUAGE plpgsql;
4719 CREATE TRIGGER proximity_update_tgr AFTER INSERT OR UPDATE OR DELETE ON actor.org_unit FOR EACH ROW EXECUTE PROCEDURE actor.org_unit_prox_update ();
4722 SELECT evergreen.upgrade_deps_block_check('0617', :eg_version);
4724 -- add notify columns to booking.reservation
4725 ALTER TABLE booking.reservation
4726 ADD COLUMN email_notify BOOLEAN NOT NULL DEFAULT FALSE;
4728 -- create the hook and validator
4729 INSERT INTO action_trigger.hook (key, core_type, description, passive)
4730 VALUES ('reservation.available', 'bresv', 'A reservation is available for pickup', false);
4731 INSERT INTO action_trigger.validator (module, description)
4732 VALUES ('ReservationIsAvailable','Checked that a reserved resource is available for checkout');
4734 -- create org unit setting to toggle checkbox display
4735 INSERT INTO config.org_unit_setting_type (name, label, description, datatype)
4736 VALUES ('booking.allow_email_notify', 'booking.allow_email_notify', 'Permit email notification when a reservation is ready for pickup.', 'bool');
4739 SELECT evergreen.upgrade_deps_block_check('0618', :eg_version);
4741 UPDATE config.org_unit_setting_type SET description = E'The Regular Expression for validation on the day_phone field in patron registration. Note: The first capture group will be used for the "last 4 digits of phone number" feature, if enabled. Ex: "[2-9]\\d{2}-\\d{3}-(\\d{4})( x\\d+)?" will ignore the extension on a NANP number.' WHERE name = 'ui.patron.edit.au.day_phone.regex';
4743 UPDATE config.org_unit_setting_type SET description = 'The Regular Expression for validation on phone fields in patron registration. Applies to all phone fields without their own setting. NOTE: See description of the day_phone regex for important information about capture groups with it.' WHERE name = 'ui.patron.edit.phone.regex';
4745 UPDATE config.org_unit_setting_type SET description = oils_i18n_gettext('patron.password.use_phone', 'By default, use the last 4 alphanumeric characters of the patrons phone number as the default password when creating new users. The exact characters used may be configured via the "GUI: Regex for day_phone field on patron registration" setting.', 'coust', 'description') WHERE name = 'patron.password.use_phone';
4747 -- Evergreen DB patch 0619.schema.au_last_update_time.sql
4749 -- check whether patch can be applied
4750 SELECT evergreen.upgrade_deps_block_check('0619', :eg_version);
4752 -- Add new column last_update_time to actor.usr, with trigger to maintain it
4753 -- Add corresponding new column to auditor.actor_usr_history
4755 ALTER TABLE actor.usr
4756 ADD COLUMN last_update_time TIMESTAMPTZ;
4758 ALTER TABLE auditor.actor_usr_history
4759 ADD COLUMN last_update_time TIMESTAMPTZ;
4761 CREATE OR REPLACE FUNCTION actor.au_updated()
4762 RETURNS TRIGGER AS $$
4764 NEW.last_update_time := now();
4767 $$ LANGUAGE plpgsql;
4769 CREATE TRIGGER au_update_trig
4770 BEFORE INSERT OR UPDATE ON actor.usr
4771 FOR EACH ROW EXECUTE PROCEDURE actor.au_updated();
4773 -- Evergreen DB patch XXXX.data.opac_payment_history_age_limit.sql
4776 SELECT evergreen.upgrade_deps_block_check('0621', :eg_version);
4778 INSERT into config.org_unit_setting_type (name, label, description, datatype)
4780 'opac.payment_history_age_limit',
4781 oils_i18n_gettext('opac.payment_history_age_limit',
4782 'OPAC: Payment History Age Limit', 'coust', 'label'),
4783 oils_i18n_gettext('opac.payment_history_age_limit',
4784 'The OPAC should not display payments by patrons that are older than any interval defined here.', 'coust', 'label'),
4788 -- Updates config.org_unit_setting_type to remove the old tag prefixes for once
4789 -- groups have been added.
4792 SELECT evergreen.upgrade_deps_block_check('0622', :eg_version);
4794 INSERT INTO config.settings_group (name, label) VALUES
4795 ('sys', oils_i18n_gettext('config.settings_group.system', 'System', 'coust', 'label')),
4796 ('gui', oils_i18n_gettext('config.settings_group.gui', 'GUI', 'coust', 'label')),
4797 ('lib', oils_i18n_gettext('config.settings_group.lib', 'Library', 'coust', 'label')),
4798 ('sec', oils_i18n_gettext('config.settings_group.sec', 'Security', 'coust', 'label')),
4799 ('cat', oils_i18n_gettext('config.settings_group.cat', 'Cataloging', 'coust', 'label')),
4800 ('holds', oils_i18n_gettext('config.settings_group.holds', 'Holds', 'coust', 'label')),
4801 ('circ', oils_i18n_gettext('config.settings_group.circulation', 'Circulation', 'coust', 'label')),
4802 ('self', oils_i18n_gettext('config.settings_group.self', 'Self Check', 'coust', 'label')),
4803 ('opac', oils_i18n_gettext('config.settings_group.opac', 'OPAC', 'coust', 'label')),
4804 ('prog', oils_i18n_gettext('config.settings_group.program', 'Program', 'coust', 'label')),
4805 ('glob', oils_i18n_gettext('config.settings_group.global', 'Global', 'coust', 'label')),
4806 ('finance', oils_i18n_gettext('config.settings_group.finances', 'Finances', 'coust', 'label')),
4807 ('credit', oils_i18n_gettext('config.settings_group.ccp', 'Credit Card Processing', 'coust', 'label')),
4808 ('serial', oils_i18n_gettext('config.settings_group.serial', 'Serials', 'coust', 'label')),
4809 ('recall', oils_i18n_gettext('config.settings_group.recall', 'Recalls', 'coust', 'label')),
4810 ('booking', oils_i18n_gettext('config.settings_group.booking', 'Booking', 'coust', 'label')),
4811 ('offline', oils_i18n_gettext('config.settings_group.offline', 'Offline', 'coust', 'label')),
4812 ('receipt_template', oils_i18n_gettext('config.settings_group.receipt_template', 'Receipt Template', 'coust', 'label'));
4814 UPDATE config.org_unit_setting_type SET grp = 'lib', label='Set copy creator as receiver' WHERE name = 'acq.copy_creator_uses_receiver';
4815 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'acq.default_circ_modifier';
4816 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'acq.default_copy_location';
4817 UPDATE config.org_unit_setting_type SET grp = 'finance' WHERE name = 'acq.fund.balance_limit.block';
4818 UPDATE config.org_unit_setting_type SET grp = 'finance' WHERE name = 'acq.fund.balance_limit.warn';
4819 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'acq.holds.allow_holds_from_purchase_request';
4820 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'acq.tmp_barcode_prefix';
4821 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'acq.tmp_callnumber_prefix';
4822 UPDATE config.org_unit_setting_type SET grp = 'sec' WHERE name = 'auth.opac_timeout';
4823 UPDATE config.org_unit_setting_type SET grp = 'sec' WHERE name = 'auth.persistent_login_interval';
4824 UPDATE config.org_unit_setting_type SET grp = 'sec' WHERE name = 'auth.staff_timeout';
4825 UPDATE config.org_unit_setting_type SET grp = 'booking' WHERE name = 'booking.allow_email_notify';
4826 UPDATE config.org_unit_setting_type SET grp = 'gui' WHERE name = 'cat.bib.alert_on_empty';
4827 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Delete bib if all copies are deleted via Acquisitions lineitem cancellation.' WHERE name = 'cat.bib.delete_on_no_copy_via_acq_lineitem_cancel';
4828 UPDATE config.org_unit_setting_type SET grp = 'prog' WHERE name = 'cat.bib.keep_on_empty';
4829 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Default Classification Scheme' WHERE name = 'cat.default_classification_scheme';
4830 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Default copy status (fast add)' WHERE name = 'cat.default_copy_status_fast';
4831 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Default copy status (normal)' WHERE name = 'cat.default_copy_status_normal';
4832 UPDATE config.org_unit_setting_type SET grp = 'finance' WHERE name = 'cat.default_item_price';
4833 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Spine and pocket label font family' WHERE name = 'cat.label.font.family';
4834 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Spine and pocket label font size' WHERE name = 'cat.label.font.size';
4835 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Spine and pocket label font weight' WHERE name = 'cat.label.font.weight';
4836 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Defines the control number identifier used in 003 and 035 fields.' WHERE name = 'cat.marc_control_number_identifier';
4837 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Spine label maximum lines' WHERE name = 'cat.spine.line.height';
4838 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Spine label left margin' WHERE name = 'cat.spine.line.margin';
4839 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Spine label line width' WHERE name = 'cat.spine.line.width';
4840 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Delete volume with last copy' WHERE name = 'cat.volume.delete_on_empty';
4841 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Toggle off the patron summary sidebar after first view.' WHERE name = 'circ.auto_hide_patron_summary';
4842 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Block Renewal of Items Needed for Holds' WHERE name = 'circ.block_renews_for_holds';
4843 UPDATE config.org_unit_setting_type SET grp = 'booking', label='Elbow room' WHERE name = 'circ.booking_reservation.default_elbow_room';
4844 UPDATE config.org_unit_setting_type SET grp = 'finance' WHERE name = 'circ.charge_lost_on_zero';
4845 UPDATE config.org_unit_setting_type SET grp = 'finance' WHERE name = 'circ.charge_on_damaged';
4846 UPDATE config.org_unit_setting_type SET grp = 'circ' WHERE name = 'circ.checkout_auto_renew_age';
4847 UPDATE config.org_unit_setting_type SET grp = 'circ' WHERE name = 'circ.checkout_fills_related_hold';
4848 UPDATE config.org_unit_setting_type SET grp = 'circ' WHERE name = 'circ.checkout_fills_related_hold_exact_match_only';
4849 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'circ.claim_never_checked_out.mark_missing';
4850 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'circ.claim_return.copy_status';
4851 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'circ.damaged.void_ovedue';
4852 UPDATE config.org_unit_setting_type SET grp = 'finance' WHERE name = 'circ.damaged_item_processing_fee';
4853 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Do not include outstanding Claims Returned circulations in lump sum tallies in Patron Display.' WHERE name = 'circ.do_not_tally_claims_returned';
4854 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Hard boundary' WHERE name = 'circ.hold_boundary.hard';
4855 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Soft boundary' WHERE name = 'circ.hold_boundary.soft';
4856 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Expire Alert Interval' WHERE name = 'circ.hold_expire_alert_interval';
4857 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Expire Interval' WHERE name = 'circ.hold_expire_interval';
4858 UPDATE config.org_unit_setting_type SET grp = 'circ' WHERE name = 'circ.hold_shelf_status_delay';
4859 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Soft stalling interval' WHERE name = 'circ.hold_stalling.soft';
4860 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Hard stalling interval' WHERE name = 'circ.hold_stalling_hard';
4861 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Use Active Date for Age Protection' WHERE name = 'circ.holds.age_protect.active_date';
4862 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Behind Desk Pickup Supported' WHERE name = 'circ.holds.behind_desk_pickup_supported';
4863 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Canceled holds display age' WHERE name = 'circ.holds.canceled.display_age';
4864 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Canceled holds display count' WHERE name = 'circ.holds.canceled.display_count';
4865 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Clear shelf copy status' WHERE name = 'circ.holds.clear_shelf.copy_status';
4866 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Bypass hold capture during clear shelf process' WHERE name = 'circ.holds.clear_shelf.no_capture_holds';
4867 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Default Estimated Wait' WHERE name = 'circ.holds.default_estimated_wait_interval';
4868 UPDATE config.org_unit_setting_type SET grp = 'holds' WHERE name = 'circ.holds.default_shelf_expire_interval';
4869 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Block hold request if hold recipient privileges have expired' WHERE name = 'circ.holds.expired_patron_block';
4870 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Has Local Copy Alert' WHERE name = 'circ.holds.hold_has_copy_at.alert';
4871 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Has Local Copy Block' WHERE name = 'circ.holds.hold_has_copy_at.block';
4872 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Maximum library target attempts' WHERE name = 'circ.holds.max_org_unit_target_loops';
4873 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Minimum Estimated Wait' WHERE name = 'circ.holds.min_estimated_wait_interval';
4874 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Org Unit Target Weight' WHERE name = 'circ.holds.org_unit_target_weight';
4875 UPDATE config.org_unit_setting_type SET grp = 'recall', label='An array of fine amount, fine interval, and maximum fine.' WHERE name = 'circ.holds.recall_fine_rules';
4876 UPDATE config.org_unit_setting_type SET grp = 'recall', label='Truncated loan period.' WHERE name = 'circ.holds.recall_return_interval';
4877 UPDATE config.org_unit_setting_type SET grp = 'recall', label='Circulation duration that triggers a recall.' WHERE name = 'circ.holds.recall_threshold';
4878 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Use weight-based hold targeting' WHERE name = 'circ.holds.target_holds_by_org_unit_weight';
4879 UPDATE config.org_unit_setting_type SET grp = 'holds' WHERE name = 'circ.holds.target_skip_me';
4880 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Reset request time on un-cancel' WHERE name = 'circ.holds.uncancel.reset_request_time';
4881 UPDATE config.org_unit_setting_type SET grp = 'holds', label='FIFO' WHERE name = 'circ.holds_fifo';
4882 UPDATE config.org_unit_setting_type SET grp = 'gui' WHERE name = 'circ.item_checkout_history.max';
4883 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Lost Checkin Generates New Overdues' WHERE name = 'circ.lost.generate_overdue_on_checkin';
4884 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Lost items usable on checkin' WHERE name = 'circ.lost_immediately_available';
4885 UPDATE config.org_unit_setting_type SET grp = 'finance' WHERE name = 'circ.lost_materials_processing_fee';
4886 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Void lost max interval' WHERE name = 'circ.max_accept_return_of_lost';
4887 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Cap Max Fine at Item Price' WHERE name = 'circ.max_fine.cap_at_price';
4888 UPDATE config.org_unit_setting_type SET grp = 'circ' WHERE name = 'circ.max_patron_claim_return_count';
4889 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Item Status for Missing Pieces' WHERE name = 'circ.missing_pieces.copy_status';
4890 UPDATE config.org_unit_setting_type SET grp = 'sec' WHERE name = 'circ.obscure_dob';
4891 UPDATE config.org_unit_setting_type SET grp = 'offline', label='Skip offline checkin if newer item Status Changed Time.' WHERE name = 'circ.offline.skip_checkin_if_newer_status_changed_time';
4892 UPDATE config.org_unit_setting_type SET grp = 'offline', label='Skip offline checkout if newer item Status Changed Time.' WHERE name = 'circ.offline.skip_checkout_if_newer_status_changed_time';
4893 UPDATE config.org_unit_setting_type SET grp = 'offline', label='Skip offline renewal if newer item Status Changed Time.' WHERE name = 'circ.offline.skip_renew_if_newer_status_changed_time';
4894 UPDATE config.org_unit_setting_type SET grp = 'sec', label='Offline: Patron Usernames Allowed' WHERE name = 'circ.offline.username_allowed';
4895 UPDATE config.org_unit_setting_type SET grp = 'sec', label='Maximum concurrently active self-serve password reset requests per user' WHERE name = 'circ.password_reset_request_per_user_limit';
4896 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Require matching email address for password reset requests' WHERE name = 'circ.password_reset_request_requires_matching_email';
4897 UPDATE config.org_unit_setting_type SET grp = 'sec', label='Maximum concurrently active self-serve password reset requests' WHERE name = 'circ.password_reset_request_throttle';
4898 UPDATE config.org_unit_setting_type SET grp = 'sec', label='Self-serve password reset request time-to-live' WHERE name = 'circ.password_reset_request_time_to_live';
4899 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Patron Registration: Cloned patrons get address copy' WHERE name = 'circ.patron_edit.clone.copy_address';
4900 UPDATE config.org_unit_setting_type SET grp = 'circ' WHERE name = 'circ.patron_invalid_address_apply_penalty';
4901 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'circ.pre_cat_copy_circ_lib';
4902 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'circ.reshelving_complete.interval';
4903 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Restore overdues on lost item return' WHERE name = 'circ.restore_overdue_on_lost_return';
4904 UPDATE config.org_unit_setting_type SET grp = 'self', label='Pop-up alert for errors' WHERE name = 'circ.selfcheck.alert.popup';
4905 UPDATE config.org_unit_setting_type SET grp = 'self', label='Audio Alerts' WHERE name = 'circ.selfcheck.alert.sound';
4906 UPDATE config.org_unit_setting_type SET grp = 'self' WHERE name = 'circ.selfcheck.auto_override_checkout_events';
4907 UPDATE config.org_unit_setting_type SET grp = 'self', label='Block copy checkout status' WHERE name = 'circ.selfcheck.block_checkout_on_copy_status';
4908 UPDATE config.org_unit_setting_type SET grp = 'self', label='Patron Login Timeout (in seconds)' WHERE name = 'circ.selfcheck.patron_login_timeout';
4909 UPDATE config.org_unit_setting_type SET grp = 'self', label='Require Patron Password' WHERE name = 'circ.selfcheck.patron_password_required';
4910 UPDATE config.org_unit_setting_type SET grp = 'self', label='Require patron password' WHERE name = 'circ.selfcheck.require_patron_password';
4911 UPDATE config.org_unit_setting_type SET grp = 'self', label='Workstation Required' WHERE name = 'circ.selfcheck.workstation_required';
4912 UPDATE config.org_unit_setting_type SET grp = 'circ' WHERE name = 'circ.staff_client.actor_on_checkout';
4913 UPDATE config.org_unit_setting_type SET grp = 'prog' WHERE name = 'circ.staff_client.do_not_auto_attempt_print';
4914 UPDATE config.org_unit_setting_type SET grp = 'receipt_template', label='Content of alert_text include' WHERE name = 'circ.staff_client.receipt.alert_text';
4915 UPDATE config.org_unit_setting_type SET grp = 'receipt_template', label='Content of event_text include' WHERE name = 'circ.staff_client.receipt.event_text';
4916 UPDATE config.org_unit_setting_type SET grp = 'receipt_template', label='Content of footer_text include' WHERE name = 'circ.staff_client.receipt.footer_text';
4917 UPDATE config.org_unit_setting_type SET grp = 'receipt_template', label='Content of header_text include' WHERE name = 'circ.staff_client.receipt.header_text';
4918 UPDATE config.org_unit_setting_type SET grp = 'receipt_template', label='Content of notice_text include' WHERE name = 'circ.staff_client.receipt.notice_text';
4919 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Minimum Transit Checkin Interval' WHERE name = 'circ.transit.min_checkin_interval';
4920 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Patron Merge Deactivate Card' WHERE name = 'circ.user_merge.deactivate_cards';
4921 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Patron Merge Address Delete' WHERE name = 'circ.user_merge.delete_addresses';
4922 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Patron Merge Barcode Delete' WHERE name = 'circ.user_merge.delete_cards';
4923 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Void lost item billing when returned' WHERE name = 'circ.void_lost_on_checkin';
4924 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Void processing fee on lost item return' WHERE name = 'circ.void_lost_proc_fee_on_checkin';
4925 UPDATE config.org_unit_setting_type SET grp = 'finance', label='Void overdue fines when items are marked lost' WHERE name = 'circ.void_overdue_on_lost';
4926 UPDATE config.org_unit_setting_type SET grp = 'finance' WHERE name = 'credit.payments.allow';
4927 UPDATE config.org_unit_setting_type SET grp = 'credit', label='Enable AuthorizeNet payments' WHERE name = 'credit.processor.authorizenet.enabled';
4928 UPDATE config.org_unit_setting_type SET grp = 'credit', label='AuthorizeNet login' WHERE name = 'credit.processor.authorizenet.login';
4929 UPDATE config.org_unit_setting_type SET grp = 'credit', label='AuthorizeNet password' WHERE name = 'credit.processor.authorizenet.password';
4930 UPDATE config.org_unit_setting_type SET grp = 'credit', label='AuthorizeNet server' WHERE name = 'credit.processor.authorizenet.server';
4931 UPDATE config.org_unit_setting_type SET grp = 'credit', label='AuthorizeNet test mode' WHERE name = 'credit.processor.authorizenet.testmode';
4932 UPDATE config.org_unit_setting_type SET grp = 'credit', label='Name default credit processor' WHERE name = 'credit.processor.default';
4933 UPDATE config.org_unit_setting_type SET grp = 'credit', label='Enable PayflowPro payments' WHERE name = 'credit.processor.payflowpro.enabled';
4934 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayflowPro login/merchant ID' WHERE name = 'credit.processor.payflowpro.login';
4935 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayflowPro partner' WHERE name = 'credit.processor.payflowpro.partner';
4936 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayflowPro password' WHERE name = 'credit.processor.payflowpro.password';
4937 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayflowPro test mode' WHERE name = 'credit.processor.payflowpro.testmode';
4938 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayflowPro vendor' WHERE name = 'credit.processor.payflowpro.vendor';
4939 UPDATE config.org_unit_setting_type SET grp = 'credit', label='Enable PayPal payments' WHERE name = 'credit.processor.paypal.enabled';
4940 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayPal login' WHERE name = 'credit.processor.paypal.login';
4941 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayPal password' WHERE name = 'credit.processor.paypal.password';
4942 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayPal signature' WHERE name = 'credit.processor.paypal.signature';
4943 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayPal test mode' WHERE name = 'credit.processor.paypal.testmode';
4944 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Format Dates with this pattern.' WHERE name = 'format.date';
4945 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Format Times with this pattern.' WHERE name = 'format.time';
4946 UPDATE config.org_unit_setting_type SET grp = 'glob' WHERE name = 'global.default_locale';
4947 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'global.juvenile_age_threshold';
4948 UPDATE config.org_unit_setting_type SET grp = 'glob' WHERE name = 'global.password_regex';
4949 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Disable the ability to save list column configurations locally.' WHERE name = 'gui.disable_local_save_columns';
4950 UPDATE config.org_unit_setting_type SET grp = 'lib', label='Courier Code' WHERE name = 'lib.courier_code';
4951 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'notice.telephony.callfile_lines';
4952 UPDATE config.org_unit_setting_type SET grp = 'opac', label='Allow pending addresses' WHERE name = 'opac.allow_pending_address';
4953 UPDATE config.org_unit_setting_type SET grp = 'glob' WHERE name = 'opac.barcode_regex';
4954 UPDATE config.org_unit_setting_type SET grp = 'opac', label='Use fully compressed serial holdings' WHERE name = 'opac.fully_compressed_serial_holdings';
4955 UPDATE config.org_unit_setting_type SET grp = 'opac', label='Org Unit Hiding Depth' WHERE name = 'opac.org_unit_hiding.depth';
4956 UPDATE config.org_unit_setting_type SET grp = 'opac', label='Payment History Age Limit' WHERE name = 'opac.payment_history_age_limit';
4957 UPDATE config.org_unit_setting_type SET grp = 'prog' WHERE name = 'org.bounced_emails';
4958 UPDATE config.org_unit_setting_type SET grp = 'sec', label='Patron Opt-In Boundary' WHERE name = 'org.patron_opt_boundary';
4959 UPDATE config.org_unit_setting_type SET grp = 'sec', label='Patron Opt-In Default' WHERE name = 'org.patron_opt_default';
4960 UPDATE config.org_unit_setting_type SET grp = 'sec' WHERE name = 'patron.password.use_phone';
4961 UPDATE config.org_unit_setting_type SET grp = 'serial', label='Previous Issuance Copy Location' WHERE name = 'serial.prev_issuance_copy_location';
4962 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Work Log: Maximum Patrons Logged' WHERE name = 'ui.admin.patron_log.max_entries';
4963 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Work Log: Maximum Actions Logged' WHERE name = 'ui.admin.work_log.max_entries';
4964 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Horizontal layout for Volume/Copy Creator/Editor.' WHERE name = 'ui.cat.volume_copy_editor.horizontal';
4965 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Uncheck bills by default in the patron billing interface' WHERE name = 'ui.circ.billing.uncheck_bills_and_unfocus_payment_box';
4966 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Record In-House Use: Maximum # of uses allowed per entry.' WHERE name = 'ui.circ.in_house_use.entry_cap';
4967 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Record In-House Use: # of uses threshold for Are You Sure? dialog.' WHERE name = 'ui.circ.in_house_use.entry_warn';
4968 UPDATE config.org_unit_setting_type SET grp = 'gui' WHERE name = 'ui.circ.patron_summary.horizontal';
4969 UPDATE config.org_unit_setting_type SET grp = 'gui' WHERE name = 'ui.circ.show_billing_tab_on_bills';
4970 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Suppress popup-dialogs during check-in.' WHERE name = 'ui.circ.suppress_checkin_popups';
4971 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Button bar' WHERE name = 'ui.general.button_bar';
4972 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Default Hotkeyset' WHERE name = 'ui.general.hotkeyset';
4973 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Idle timeout' WHERE name = 'ui.general.idle_timeout';
4974 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Default Country for New Addresses in Patron Editor' WHERE name = 'ui.patron.default_country';
4975 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Default Ident Type for Patron Registration' WHERE name = 'ui.patron.default_ident_type';
4976 UPDATE config.org_unit_setting_type SET grp = 'sec', label='Default level of patrons'' internet access' WHERE name = 'ui.patron.default_inet_access_level';
4977 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show active field on patron registration' WHERE name = 'ui.patron.edit.au.active.show';
4978 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest active field on patron registration' WHERE name = 'ui.patron.edit.au.active.suggest';
4979 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show alert_message field on patron registration' WHERE name = 'ui.patron.edit.au.alert_message.show';
4980 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest alert_message field on patron registration' WHERE name = 'ui.patron.edit.au.alert_message.suggest';
4981 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show alias field on patron registration' WHERE name = 'ui.patron.edit.au.alias.show';
4982 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest alias field on patron registration' WHERE name = 'ui.patron.edit.au.alias.suggest';
4983 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show barred field on patron registration' WHERE name = 'ui.patron.edit.au.barred.show';
4984 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest barred field on patron registration' WHERE name = 'ui.patron.edit.au.barred.suggest';
4985 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show claims_never_checked_out_count field on patron registration' WHERE name = 'ui.patron.edit.au.claims_never_checked_out_count.show';
4986 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest claims_never_checked_out_count field on patron registration' WHERE name = 'ui.patron.edit.au.claims_never_checked_out_count.suggest';
4987 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show claims_returned_count field on patron registration' WHERE name = 'ui.patron.edit.au.claims_returned_count.show';
4988 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest claims_returned_count field on patron registration' WHERE name = 'ui.patron.edit.au.claims_returned_count.suggest';
4989 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Example for day_phone field on patron registration' WHERE name = 'ui.patron.edit.au.day_phone.example';
4990 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Regex for day_phone field on patron registration' WHERE name = 'ui.patron.edit.au.day_phone.regex';
4991 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Require day_phone field on patron registration' WHERE name = 'ui.patron.edit.au.day_phone.require';
4992 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show day_phone field on patron registration' WHERE name = 'ui.patron.edit.au.day_phone.show';
4993 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest day_phone field on patron registration' WHERE name = 'ui.patron.edit.au.day_phone.suggest';
4994 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show calendar widget for dob field on patron registration' WHERE name = 'ui.patron.edit.au.dob.calendar';
4995 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Require dob field on patron registration' WHERE name = 'ui.patron.edit.au.dob.require';
4996 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show dob field on patron registration' WHERE name = 'ui.patron.edit.au.dob.show';
4997 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest dob field on patron registration' WHERE name = 'ui.patron.edit.au.dob.suggest';
4998 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Example for email field on patron registration' WHERE name = 'ui.patron.edit.au.email.example';
4999 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Regex for email field on patron registration' WHERE name = 'ui.patron.edit.au.email.regex';
5000 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Require email field on patron registration' WHERE name = 'ui.patron.edit.au.email.require';
5001 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show email field on patron registration' WHERE name = 'ui.patron.edit.au.email.show';
5002 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest email field on patron registration' WHERE name = 'ui.patron.edit.au.email.suggest';
5003 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Example for evening_phone field on patron registration' WHERE name = 'ui.patron.edit.au.evening_phone.example';
5004 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Regex for evening_phone field on patron registration' WHERE name = 'ui.patron.edit.au.evening_phone.regex';
5005 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Require evening_phone field on patron registration' WHERE name = 'ui.patron.edit.au.evening_phone.require';
5006 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show evening_phone field on patron registration' WHERE name = 'ui.patron.edit.au.evening_phone.show';
5007 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest evening_phone field on patron registration' WHERE name = 'ui.patron.edit.au.evening_phone.suggest';
5008 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show ident_value field on patron registration' WHERE name = 'ui.patron.edit.au.ident_value.show';
5009 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest ident_value field on patron registration' WHERE name = 'ui.patron.edit.au.ident_value.suggest';
5010 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show ident_value2 field on patron registration' WHERE name = 'ui.patron.edit.au.ident_value2.show';
5011 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest ident_value2 field on patron registration' WHERE name = 'ui.patron.edit.au.ident_value2.suggest';
5012 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show juvenile field on patron registration' WHERE name = 'ui.patron.edit.au.juvenile.show';
5013 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest juvenile field on patron registration' WHERE name = 'ui.patron.edit.au.juvenile.suggest';
5014 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show master_account field on patron registration' WHERE name = 'ui.patron.edit.au.master_account.show';
5015 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest master_account field on patron registration' WHERE name = 'ui.patron.edit.au.master_account.suggest';
5016 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Example for other_phone field on patron registration' WHERE name = 'ui.patron.edit.au.other_phone.example';
5017 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Regex for other_phone field on patron registration' WHERE name = 'ui.patron.edit.au.other_phone.regex';
5018 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Require other_phone field on patron registration' WHERE name = 'ui.patron.edit.au.other_phone.require';
5019 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show other_phone field on patron registration' WHERE name = 'ui.patron.edit.au.other_phone.show';
5020 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest other_phone field on patron registration' WHERE name = 'ui.patron.edit.au.other_phone.suggest';
5021 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show second_given_name field on patron registration' WHERE name = 'ui.patron.edit.au.second_given_name.show';
5022 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest second_given_name field on patron registration' WHERE name = 'ui.patron.edit.au.second_given_name.suggest';
5023 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show suffix field on patron registration' WHERE name = 'ui.patron.edit.au.suffix.show';
5024 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest suffix field on patron registration' WHERE name = 'ui.patron.edit.au.suffix.suggest';
5025 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Require county field on patron registration' WHERE name = 'ui.patron.edit.aua.county.require';
5026 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Example for post_code field on patron registration' WHERE name = 'ui.patron.edit.aua.post_code.example';
5027 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Regex for post_code field on patron registration' WHERE name = 'ui.patron.edit.aua.post_code.regex';
5028 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Default showing suggested patron registration fields' WHERE name = 'ui.patron.edit.default_suggested';
5029 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Example for phone fields on patron registration' WHERE name = 'ui.patron.edit.phone.example';
5030 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Regex for phone fields on patron registration' WHERE name = 'ui.patron.edit.phone.regex';
5031 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Require at least one address for Patron Registration' WHERE name = 'ui.patron.registration.require_address';
5032 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Cap results in Patron Search at this number.' WHERE name = 'ui.patron_search.result_cap';
5033 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Require staff initials for entry/edit of item/patron/penalty notes/messages.' WHERE name = 'ui.staff.require_initials';
5034 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Unified Volume/Item Creator/Editor' WHERE name = 'ui.unified_volume_copy_editor';
5035 UPDATE config.org_unit_setting_type SET grp = 'gui', label='URL for remote directory containing list column settings.' WHERE name = 'url.remote_column_settings';
5040 SELECT evergreen.upgrade_deps_block_check('0623', :eg_version);
5043 CREATE TABLE config.org_unit_setting_type_log (
5044 id BIGSERIAL PRIMARY KEY,
5045 date_applied TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
5046 org INT REFERENCES actor.org_unit (id),
5047 original_value TEXT,
5049 field_name TEXT REFERENCES config.org_unit_setting_type (name)
5052 -- Log each change in oust to oustl, so admins can see what they messed up if someting stops working.
5053 CREATE OR REPLACE FUNCTION ous_change_log() RETURNS TRIGGER AS $ous_change_log$
5057 -- Check for which setting is being updated, and log it.
5058 SELECT INTO original value FROM actor.org_unit_setting WHERE name = NEW.name AND org_unit = NEW.org_unit;
5060 INSERT INTO config.org_unit_setting_type_log (org,original_value,new_value,field_name) VALUES (NEW.org_unit, original, NEW.value, NEW.name);
5064 $ous_change_log$ LANGUAGE plpgsql;
5066 CREATE TRIGGER log_ous_change
5067 BEFORE INSERT OR UPDATE ON actor.org_unit_setting
5068 FOR EACH ROW EXECUTE PROCEDURE ous_change_log();
5070 CREATE OR REPLACE FUNCTION ous_delete_log() RETURNS TRIGGER AS $ous_delete_log$
5074 -- Check for which setting is being updated, and log it.
5075 SELECT INTO original value FROM actor.org_unit_setting WHERE name = OLD.name AND org_unit = OLD.org_unit;
5077 INSERT INTO config.org_unit_setting_type_log (org,original_value,new_value,field_name) VALUES (OLD.org_unit, original, 'null', OLD.name);
5081 $ous_delete_log$ LANGUAGE plpgsql;
5083 CREATE TRIGGER log_ous_del
5084 BEFORE DELETE ON actor.org_unit_setting
5085 FOR EACH ROW EXECUTE PROCEDURE ous_delete_log();
5087 -- Evergreen DB patch 0625.data.opac_staff_saved_search_size.sql
5090 SELECT evergreen.upgrade_deps_block_check('0625', :eg_version);
5092 INSERT into config.org_unit_setting_type (name, grp, label, description, datatype)
5094 'opac.staff_saved_search.size', 'opac',
5095 oils_i18n_gettext('opac.staff_saved_search.size',
5096 'OPAC: Number of staff client saved searches to display on left side of results and record details pages', 'coust', 'label'),
5097 oils_i18n_gettext('opac.staff_saved_search.size',
5098 'If unset, the OPAC (only when wrapped in the staff client!) will default to showing you your ten most recent searches on the left side of the results and record details pages. If you actually don''t want to see this feature at all, set this value to zero at the top of your organizational tree.', 'coust', 'description'),
5102 -- Evergreen DB patch 0626.schema.bookbag-goodies.sql
5105 SELECT evergreen.upgrade_deps_block_check('0626', :eg_version);
5107 ALTER TABLE container.biblio_record_entry_bucket
5108 ADD COLUMN description TEXT;
5110 ALTER TABLE container.call_number_bucket
5111 ADD COLUMN description TEXT;
5113 ALTER TABLE container.copy_bucket
5114 ADD COLUMN description TEXT;
5116 ALTER TABLE container.user_bucket
5117 ADD COLUMN description TEXT;
5119 INSERT INTO action_trigger.hook (key, core_type, description, passive)
5121 'container.biblio_record_entry_bucket.csv',
5124 'container.biblio_record_entry_bucket.csv',
5125 'Produce a CSV file representing a bookbag',
5132 INSERT INTO action_trigger.reactor (module, description)
5137 'Facilitates produce a CSV file representing a bookbag by introducing an "items" variable into the TT environment, sorted as dictated according to user params',
5143 INSERT INTO action_trigger.event_definition (
5145 name, hook, reactor,
5149 'Bookbag CSV', 'container.biblio_record_entry_bucket.csv', 'ContainerCSV',
5153 # target is the bookbag itself. The 'items' variable does not need to be in
5154 # the environment because a special reactor will take care of filling it in.
5157 bibxml = helpers.xml_doc(item.target_biblio_record_entry.marc);
5159 FOR part IN bibxml.findnodes('//*[@tag="245"]/*[@code="a" or @code="b"]');
5160 title = title _ part.textContent;
5162 author = bibxml.findnodes('//*[@tag="100"]/*[@code="a"]').textContent;
5164 helpers.csv_datum(title) %],[% helpers.csv_datum(author) %],[% FOR note IN item.notes; helpers.csv_datum(note.note); ","; END; "\n";
5169 -- Evergreen DB patch 0627.data.patron-password-reset-msg.sql
5171 -- Updates password reset template to match TPAC reset form
5174 -- check whether patch can be applied
5175 SELECT evergreen.upgrade_deps_block_check('0627', :eg_version);
5177 UPDATE action_trigger.event_definition SET template =
5180 [%- user = target.usr -%]
5181 To: [%- params.recipient_email || user.email %]
5182 From: [%- params.sender_email || user.home_ou.email || default_sender %]
5183 Subject: [% user.home_ou.name %]: library account password reset request
5185 You have received this message because you, or somebody else, requested a reset
5186 of your library system password. If you did not request a reset of your library
5187 system password, just ignore this message and your current password will
5190 If you did request a reset of your library system password, please perform
5191 the following steps to continue the process of resetting your password:
5193 1. Open the following link in a web browser: https://[% params.hostname %]/eg/opac/password_reset/[% target.uuid %]
5194 The browser displays a password reset form.
5196 2. Enter your new password in the password reset form in the browser. You must
5197 enter the password twice to ensure that you do not make a mistake. If the
5198 passwords match, you will then be able to log in to your library system account
5199 with the new password.
5202 WHERE id = 20; -- Password reset request notification
5205 SELECT evergreen.upgrade_deps_block_check('0630', :eg_version);
5207 INSERT into config.org_unit_setting_type (name, grp, label, description, datatype) VALUES
5208 ( 'circ.transit.suppress_hold', 'circ',
5209 oils_i18n_gettext('circ.transit.suppress_hold',
5210 'Suppress Hold Transits Group',
5212 oils_i18n_gettext('circ.transit.suppress_hold',
5213 'If set to a non-empty value, Hold Transits will be suppressed between this OU and others with the same value. If set to an empty value, transits will not be suppressed.',
5214 'coust', 'description'),
5216 ,( 'circ.transit.suppress_non_hold', 'circ',
5217 oils_i18n_gettext('circ.transit.suppress_non_hold',
5218 'Suppress Non-Hold Transits Group',
5220 oils_i18n_gettext('circ.transit.suppress_non_hold',
5221 'If set to a non-empty value, Non-Hold Transits will be suppressed between this OU and others with the same value. If set to an empty value, transits will not be suppressed.',
5222 'coust', 'description'),
5226 -- check whether patch can be applied
5227 SELECT evergreen.upgrade_deps_block_check('0632', :eg_version);
5229 INSERT INTO config.org_unit_setting_type (name, grp, label, description, datatype) VALUES
5230 ( 'opac.username_regex', 'glob',
5231 oils_i18n_gettext('opac.username_regex',
5232 'Patron username format',
5234 oils_i18n_gettext('opac.username_regex',
5235 'Regular expression defining the patron username format, used for patron registration and self-service username changing only',
5236 'coust', 'description'),
5238 ,( 'opac.lock_usernames', 'glob',
5239 oils_i18n_gettext('opac.lock_usernames',
5242 oils_i18n_gettext('opac.lock_usernames',
5243 'If enabled username changing via the OPAC will be disabled',
5244 'coust', 'description'),
5246 ,( 'opac.unlimit_usernames', 'glob',
5247 oils_i18n_gettext('opac.unlimit_usernames',
5248 'Allow multiple username changes',
5250 oils_i18n_gettext('opac.unlimit_usernames',
5251 'If enabled (and Lock Usernames is not set) patrons will be allowed to change their username when it does not look like a barcode. Otherwise username changing in the OPAC will only be allowed when the patron''s username looks like a barcode.',
5252 'coust', 'description'),
5256 -- Evergreen DB patch 0635.data.opac.jump-to-details-setting.sql
5260 -- check whether patch can be applied
5261 SELECT evergreen.upgrade_deps_block_check('0635', :eg_version);
5263 INSERT INTO config.org_unit_setting_type ( name, grp, label, description, datatype )
5265 'opac.staff.jump_to_details_on_single_hit',
5268 'opac.staff.jump_to_details_on_single_hit',
5269 'Jump to details on 1 hit (staff client)',
5274 'opac.staff.jump_to_details_on_single_hit',
5275 'When a search yields only 1 result, jump directly to the record details page. This setting only affects the OPAC within the staff client',
5281 'opac.patron.jump_to_details_on_single_hit',
5284 'opac.patron.jump_to_details_on_single_hit',
5285 'Jump to details on 1 hit (public)',
5290 'opac.patron.jump_to_details_on_single_hit',
5291 'When a search yields only 1 result, jump directly to the record details page. This setting only affects the public OPAC',
5298 -- Evergreen DB patch 0636.data.grace_period_extend.sql
5300 -- OU setting turns on grace period auto extension. By default they only do so
5301 -- when the grace period ends on a closed date, but there are two modifiers to
5304 -- The first modifier causes grace periods to extend for all closed dates that
5305 -- they intersect. This is "grace periods are only consumed by open days."
5307 -- The second modifier causes a grace period that ends just before a closed
5308 -- day, with or without extension having happened, to include the closed day
5309 -- (and any following it) as well. This is mainly so that a backdate into the
5310 -- closed period following the grace period will assume the "best case" of the
5311 -- item having been returned after hours on the last day of the closed date.
5315 -- check whether patch can be applied
5316 SELECT evergreen.upgrade_deps_block_check('0636', :eg_version);
5318 INSERT INTO config.org_unit_setting_type(name, grp, label, description, datatype) VALUES
5320 ( 'circ.grace.extend', 'circ',
5321 oils_i18n_gettext('circ.grace.extend',
5322 'Auto-Extend Grace Periods',
5324 oils_i18n_gettext('circ.grace.extend',
5325 'When enabled grace periods will auto-extend. By default this will be only when they are a full day or more and end on a closed date, though other options can alter this.',
5326 'coust', 'description'),
5329 ,( 'circ.grace.extend.all', 'circ',
5330 oils_i18n_gettext('circ.grace.extend.all',
5331 'Auto-Extending Grace Periods extend for all closed dates',
5333 oils_i18n_gettext('circ.grace.extend.all',
5334 'If enabled and Grace Periods auto-extending is turned on grace periods will extend past all closed dates they intersect, within hard-coded limits. This basically becomes "grace periods can only be consumed by closed dates".',
5335 'coust', 'description'),
5338 ,( 'circ.grace.extend.into_closed', 'circ',
5339 oils_i18n_gettext('circ.grace.extend.into_closed',
5340 'Auto-Extending Grace Periods include trailing closed dates',
5342 oils_i18n_gettext('circ.grace.extend.into_closed',
5343 'If enabled and Grace Periods auto-extending is turned on grace periods will include closed dates that directly follow the last day of the grace period, to allow a backdate into the closed dates to assume "returned after hours on the last day of the grace period, and thus still within it" automatically.',
5344 'coust', 'description'),
5348 -- XXXX.schema-acs-nfi.sql
5350 SELECT evergreen.upgrade_deps_block_check('0640', :eg_version);
5352 -- AFTER UPDATE OR INSERT trigger for authority.record_entry
5353 CREATE OR REPLACE FUNCTION authority.indexing_ingest_or_delete () RETURNS TRIGGER AS $func$
5356 IF NEW.deleted IS TRUE THEN -- If this authority is deleted
5357 DELETE FROM authority.bib_linking WHERE authority = NEW.id; -- Avoid updating fields in bibs that are no longer visible
5358 DELETE FROM authority.full_rec WHERE record = NEW.id; -- Avoid validating fields against deleted authority records
5359 DELETE FROM authority.simple_heading WHERE record = NEW.id;
5360 -- Should remove matching $0 from controlled fields at the same time?
5361 RETURN NEW; -- and we're done
5364 IF TG_OP = 'UPDATE' THEN -- re-ingest?
5365 PERFORM * FROM config.internal_flag WHERE name = 'ingest.reingest.force_on_same_marc' AND enabled;
5367 IF NOT FOUND AND OLD.marc = NEW.marc THEN -- don't do anything if the MARC didn't change
5371 -- Propagate these updates to any linked bib records
5372 PERFORM authority.propagate_changes(NEW.id) FROM authority.record_entry WHERE id = NEW.id;
5374 DELETE FROM authority.simple_heading WHERE record = NEW.id;
5377 INSERT INTO authority.simple_heading (record,atag,value,sort_value)
5378 SELECT record, atag, value, sort_value FROM authority.simple_heading_set(NEW.marc);
5380 -- Flatten and insert the afr data
5381 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_full_rec' AND enabled;
5383 PERFORM authority.reingest_authority_full_rec(NEW.id);
5384 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_rec_descriptor' AND enabled;
5386 PERFORM authority.reingest_authority_rec_descriptor(NEW.id);
5392 $func$ LANGUAGE PLPGSQL;
5394 -- Entries that need to respect an NFI
5395 UPDATE authority.control_set_authority_field SET nfi = '2'
5396 WHERE id IN (4,24,44,64);
5398 DROP TRIGGER authority_full_rec_fti_trigger ON authority.full_rec;
5399 CREATE TRIGGER authority_full_rec_fti_trigger
5400 BEFORE UPDATE OR INSERT ON authority.full_rec
5401 FOR EACH ROW EXECUTE PROCEDURE oils_tsearch2('keyword');
5403 CREATE OR REPLACE FUNCTION authority.normalize_heading( marcxml TEXT, no_thesaurus BOOL ) RETURNS TEXT AS $func$
5405 acsaf authority.control_set_authority_field%ROWTYPE;
5414 auth_id INT DEFAULT oils_xpath_string('//*[@tag="901"]/*[local-name()="subfield" and @code="c"]', marcxml)::INT;
5416 SELECT control_set INTO cset FROM authority.record_entry WHERE id = auth_id;
5418 IF cset IS NULL THEN
5419 SELECT control_set INTO cset
5420 FROM authority.control_set_authority_field
5421 WHERE tag IN ( SELECT UNNEST(XPATH('//*[starts-with(@tag,"1")]/@tag',marcxml::XML)::TEXT[]))
5425 thes_code := vandelay.marc21_extract_fixed_field(marcxml,'Subj');
5426 IF thes_code IS NULL THEN
5428 ELSIF thes_code = 'z' THEN
5429 thes_code := COALESCE( oils_xpath_string('//*[@tag="040"]/*[@code="f"][1]', marcxml), '' );
5433 FOR acsaf IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset AND main_entry IS NULL LOOP
5434 tag_used := acsaf.tag;
5435 nfi_used := acsaf.nfi;
5437 FOR sf IN SELECT * FROM regexp_split_to_table(acsaf.sf_list,'') LOOP
5438 tmp_text := oils_xpath_string('//*[@tag="'||tag_used||'"]/*[@code="'||sf||'"]', marcxml);
5440 IF first_sf AND tmp_text IS NOT NULL AND nfi_used IS NOT NULL THEN
5442 tmp_text := SUBSTRING(
5447 oils_xpath_string('//*[@tag="'||tag_used||'"]/@ind'||nfi_used, marcxml),
5462 IF tmp_text IS NOT NULL AND tmp_text <> '' THEN
5463 heading_text := heading_text || E'\u2021' || sf || ' ' || tmp_text;
5466 EXIT WHEN heading_text <> '';
5469 IF heading_text <> '' THEN
5470 IF no_thesaurus IS TRUE THEN
5471 heading_text := tag_used || ' ' || public.naco_normalize(heading_text);
5473 heading_text := tag_used || '_' || COALESCE(nfi_used,'-') || '_' || thes_code || ' ' || public.naco_normalize(heading_text);
5476 heading_text := 'NOHEADING_' || thes_code || ' ' || MD5(marcxml);
5479 RETURN heading_text;
5481 $func$ LANGUAGE PLPGSQL IMMUTABLE;
5483 CREATE OR REPLACE FUNCTION authority.simple_normalize_heading( marcxml TEXT ) RETURNS TEXT AS $func$
5484 SELECT authority.normalize_heading($1, TRUE);
5485 $func$ LANGUAGE SQL IMMUTABLE;
5487 CREATE OR REPLACE FUNCTION authority.normalize_heading( marcxml TEXT ) RETURNS TEXT AS $func$
5488 SELECT authority.normalize_heading($1, FALSE);
5489 $func$ LANGUAGE SQL IMMUTABLE;
5492 CREATE TABLE authority.simple_heading (
5493 id BIGSERIAL PRIMARY KEY,
5494 record BIGINT NOT NULL REFERENCES authority.record_entry (id),
5495 atag INT NOT NULL REFERENCES authority.control_set_authority_field (id),
5496 value TEXT NOT NULL,
5497 sort_value TEXT NOT NULL,
5498 index_vector tsvector NOT NULL
5500 CREATE TRIGGER authority_simple_heading_fti_trigger
5501 BEFORE UPDATE OR INSERT ON authority.simple_heading
5502 FOR EACH ROW EXECUTE PROCEDURE oils_tsearch2('keyword');
5504 CREATE INDEX authority_simple_heading_index_vector_idx ON authority.simple_heading USING GIST (index_vector);
5505 CREATE INDEX authority_simple_heading_value_idx ON authority.simple_heading (value);
5506 CREATE INDEX authority_simple_heading_sort_value_idx ON authority.simple_heading (sort_value);
5508 CREATE OR REPLACE FUNCTION authority.simple_heading_set( marcxml TEXT ) RETURNS SETOF authority.simple_heading AS $func$
5510 res authority.simple_heading%ROWTYPE;
5511 acsaf authority.control_set_authority_field%ROWTYPE;
5521 auth_id INT DEFAULT oils_xpath_string('//*[@tag="901"]/*[local-name()="subfield" and @code="c"]', marcxml)::INT;
5524 res.record := auth_id;
5526 SELECT control_set INTO cset
5527 FROM authority.control_set_authority_field
5528 WHERE tag IN ( SELECT UNNEST(XPATH('//*[starts-with(@tag,"1")]/@tag',marcxml::XML)::TEXT[]) )
5531 FOR acsaf IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset LOOP
5533 res.atag := acsaf.id;
5534 tag_used := acsaf.tag;
5535 nfi_used := acsaf.nfi;
5537 FOR tmp_xml IN SELECT UNNEST(XPATH('//*[@tag="'||tag_used||'"]', marcxml::XML)) LOOP
5540 FOR sf IN SELECT * FROM regexp_split_to_table(acsaf.sf_list,'') LOOP
5541 heading_text := heading_text || COALESCE( ' ' || oils_xpath_string('//*[@code="'||sf||'"]',tmp_xml::TEXT), '');
5544 heading_text := public.naco_normalize(heading_text);
5546 IF nfi_used IS NOT NULL THEN
5548 sort_text := SUBSTRING(
5553 oils_xpath_string('//*[@tag="'||tag_used||'"]/@ind'||nfi_used, marcxml),
5565 sort_text := heading_text;
5568 IF heading_text IS NOT NULL AND heading_text <> '' THEN
5569 res.value := heading_text;
5570 res.sort_value := sort_text;
5580 $func$ LANGUAGE PLPGSQL IMMUTABLE;
5582 -- Support function used to find the pivot for alpha-heading-browse style searching
5583 CREATE OR REPLACE FUNCTION authority.simple_heading_find_pivot( a INT[], q TEXT ) RETURNS TEXT AS $$
5585 sort_value_row RECORD;
5590 t_term := public.naco_normalize(q);
5592 SELECT CASE WHEN ash.sort_value LIKE t_term || '%' THEN 1 ELSE 0 END
5593 + CASE WHEN ash.value LIKE t_term || '%' THEN 1 ELSE 0 END AS rank,
5596 FROM authority.simple_heading ash
5597 WHERE ash.atag = ANY (a)
5598 AND ash.sort_value >= t_term
5599 ORDER BY rank DESC, ash.sort_value
5602 SELECT CASE WHEN ash.sort_value LIKE t_term || '%' THEN 1 ELSE 0 END
5603 + CASE WHEN ash.value LIKE t_term || '%' THEN 1 ELSE 0 END AS rank,
5606 FROM authority.simple_heading ash
5607 WHERE ash.atag = ANY (a)
5608 AND ash.value >= t_term
5609 ORDER BY rank DESC, ash.sort_value
5612 IF value_row.rank > sort_value_row.rank THEN
5613 RETURN value_row.sort_value;
5615 RETURN sort_value_row.sort_value;
5618 $$ LANGUAGE PLPGSQL;
5621 CREATE OR REPLACE FUNCTION authority.simple_heading_browse_center( atag_list INT[], q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 9 ) RETURNS SETOF BIGINT AS $$
5623 pivot_sort_value TEXT;
5624 boffset INT DEFAULT 0;
5625 aoffset INT DEFAULT 0;
5626 blimit INT DEFAULT 0;
5627 alimit INT DEFAULT 0;
5630 pivot_sort_value := authority.simple_heading_find_pivot(atag_list,q);
5633 blimit := pagesize / 2;
5636 IF pagesize % 2 <> 0 THEN
5637 alimit := alimit + 1;
5643 boffset := pagesize / 2;
5646 IF pagesize % 2 <> 0 THEN
5647 boffset := boffset + 1;
5653 -- "bottom" half of the browse results
5656 row_number() over ()
5657 FROM authority.simple_heading ash
5658 WHERE ash.atag = ANY (atag_list)
5659 AND ash.sort_value < pivot_sort_value
5660 ORDER BY ash.sort_value DESC
5662 OFFSET ABS(page) * pagesize - boffset
5663 ) x ORDER BY row_number DESC;
5668 -- "bottom" half of the browse results
5670 FROM authority.simple_heading ash
5671 WHERE ash.atag = ANY (atag_list)
5672 AND ash.sort_value >= pivot_sort_value
5673 ORDER BY ash.sort_value
5675 OFFSET ABS(page) * pagesize - aoffset;
5678 $$ LANGUAGE PLPGSQL ROWS 10;
5680 CREATE OR REPLACE FUNCTION authority.simple_heading_browse_top( atag_list INT[], q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5682 pivot_sort_value TEXT;
5685 pivot_sort_value := authority.simple_heading_find_pivot(atag_list,q);
5689 -- "bottom" half of the browse results
5692 row_number() over ()
5693 FROM authority.simple_heading ash
5694 WHERE ash.atag = ANY (atag_list)
5695 AND ash.sort_value < pivot_sort_value
5696 ORDER BY ash.sort_value DESC
5698 OFFSET (ABS(page) - 1) * pagesize
5699 ) x ORDER BY row_number DESC;
5704 -- "bottom" half of the browse results
5706 FROM authority.simple_heading ash
5707 WHERE ash.atag = ANY (atag_list)
5708 AND ash.sort_value >= pivot_sort_value
5709 ORDER BY ash.sort_value
5711 OFFSET ABS(page) * pagesize ;
5714 $$ LANGUAGE PLPGSQL ROWS 10;
5716 CREATE OR REPLACE FUNCTION authority.simple_heading_search_rank( atag_list INT[], q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5718 FROM authority.simple_heading ash,
5719 public.naco_normalize($2) t(term),
5720 plainto_tsquery('keyword'::regconfig,$2) ptsq(term)
5721 WHERE ash.atag = ANY ($1)
5722 AND ash.index_vector @@ ptsq.term
5723 ORDER BY ts_rank_cd(ash.index_vector,ptsq.term,14)::numeric
5724 + CASE WHEN ash.sort_value LIKE t.term || '%' THEN 2 ELSE 0 END
5725 + CASE WHEN ash.value LIKE t.term || '%' THEN 1 ELSE 0 END DESC
5728 $$ LANGUAGE SQL ROWS 10;
5730 CREATE OR REPLACE FUNCTION authority.simple_heading_search_heading( atag_list INT[], q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5732 FROM authority.simple_heading ash,
5733 public.naco_normalize($2) t(term),
5734 plainto_tsquery('keyword'::regconfig,$2) ptsq(term)
5735 WHERE ash.atag = ANY ($1)
5736 AND ash.index_vector @@ ptsq.term
5737 ORDER BY ash.sort_value
5740 $$ LANGUAGE SQL ROWS 10;
5743 CREATE OR REPLACE FUNCTION authority.axis_authority_tags(a TEXT) RETURNS INT[] AS $$
5744 SELECT ARRAY_ACCUM(field) FROM authority.browse_axis_authority_field_map WHERE axis = $1;
5747 CREATE OR REPLACE FUNCTION authority.axis_authority_tags_refs(a TEXT) RETURNS INT[] AS $$
5750 (SELECT ARRAY_ACCUM(x.id) FROM authority.control_set_authority_field x WHERE x.main_entry = a.field)
5752 FROM authority.browse_axis_authority_field_map a
5758 CREATE OR REPLACE FUNCTION authority.btag_authority_tags(btag TEXT) RETURNS INT[] AS $$
5759 SELECT ARRAY_ACCUM(authority_field) FROM authority.control_set_bib_field WHERE tag = $1
5762 CREATE OR REPLACE FUNCTION authority.btag_authority_tags_refs(btag TEXT) RETURNS INT[] AS $$
5764 ARRAY[a.authority_field],
5765 (SELECT ARRAY_ACCUM(x.id) FROM authority.control_set_authority_field x WHERE x.main_entry = a.authority_field)
5767 FROM authority.control_set_bib_field a
5773 CREATE OR REPLACE FUNCTION authority.atag_authority_tags(atag TEXT) RETURNS INT[] AS $$
5774 SELECT ARRAY_ACCUM(id) FROM authority.control_set_authority_field WHERE tag = $1
5777 CREATE OR REPLACE FUNCTION authority.atag_authority_tags_refs(atag TEXT) RETURNS INT[] AS $$
5780 (SELECT ARRAY_ACCUM(x.id) FROM authority.control_set_authority_field x WHERE x.main_entry = a.id)
5782 FROM authority.control_set_authority_field a
5787 CREATE OR REPLACE FUNCTION authority.axis_browse_center( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 9 ) RETURNS SETOF BIGINT AS $$
5788 SELECT * FROM authority.simple_heading_browse_center(authority.axis_authority_tags($1), $2, $3, $4)
5789 $$ LANGUAGE SQL ROWS 10;
5791 CREATE OR REPLACE FUNCTION authority.btag_browse_center( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 9 ) RETURNS SETOF BIGINT AS $$
5792 SELECT * FROM authority.simple_heading_browse_center(authority.btag_authority_tags($1), $2, $3, $4)
5793 $$ LANGUAGE SQL ROWS 10;
5795 CREATE OR REPLACE FUNCTION authority.atag_browse_center( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 9 ) RETURNS SETOF BIGINT AS $$
5796 SELECT * FROM authority.simple_heading_browse_center(authority.atag_authority_tags($1), $2, $3, $4)
5797 $$ LANGUAGE SQL ROWS 10;
5799 CREATE OR REPLACE FUNCTION authority.axis_browse_center_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 9 ) RETURNS SETOF BIGINT AS $$
5800 SELECT * FROM authority.simple_heading_browse_center(authority.axis_authority_tags_refs($1), $2, $3, $4)
5801 $$ LANGUAGE SQL ROWS 10;
5803 CREATE OR REPLACE FUNCTION authority.btag_browse_center_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 9 ) RETURNS SETOF BIGINT AS $$
5804 SELECT * FROM authority.simple_heading_browse_center(authority.btag_authority_tags_refs($1), $2, $3, $4)
5805 $$ LANGUAGE SQL ROWS 10;
5807 CREATE OR REPLACE FUNCTION authority.atag_browse_center_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 9 ) RETURNS SETOF BIGINT AS $$
5808 SELECT * FROM authority.simple_heading_browse_center(authority.atag_authority_tags_refs($1), $2, $3, $4)
5809 $$ LANGUAGE SQL ROWS 10;
5812 CREATE OR REPLACE FUNCTION authority.axis_browse_top( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5813 SELECT * FROM authority.simple_heading_browse_top(authority.axis_authority_tags($1), $2, $3, $4)
5814 $$ LANGUAGE SQL ROWS 10;
5816 CREATE OR REPLACE FUNCTION authority.btag_browse_top( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5817 SELECT * FROM authority.simple_heading_browse_top(authority.btag_authority_tags($1), $2, $3, $4)
5818 $$ LANGUAGE SQL ROWS 10;
5820 CREATE OR REPLACE FUNCTION authority.atag_browse_top( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5821 SELECT * FROM authority.simple_heading_browse_top(authority.atag_authority_tags($1), $2, $3, $4)
5822 $$ LANGUAGE SQL ROWS 10;
5824 CREATE OR REPLACE FUNCTION authority.axis_browse_top_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5825 SELECT * FROM authority.simple_heading_browse_top(authority.axis_authority_tags_refs($1), $2, $3, $4)
5826 $$ LANGUAGE SQL ROWS 10;
5828 CREATE OR REPLACE FUNCTION authority.btag_browse_top_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5829 SELECT * FROM authority.simple_heading_browse_top(authority.btag_authority_tags_refs($1), $2, $3, $4)
5830 $$ LANGUAGE SQL ROWS 10;
5832 CREATE OR REPLACE FUNCTION authority.atag_browse_top_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5833 SELECT * FROM authority.simple_heading_browse_top(authority.atag_authority_tags_refs($1), $2, $3, $4)
5834 $$ LANGUAGE SQL ROWS 10;
5837 CREATE OR REPLACE FUNCTION authority.axis_search_rank( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5838 SELECT * FROM authority.simple_heading_search_rank(authority.axis_authority_tags($1), $2, $3, $4)
5839 $$ LANGUAGE SQL ROWS 10;
5841 CREATE OR REPLACE FUNCTION authority.btag_search_rank( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5842 SELECT * FROM authority.simple_heading_search_rank(authority.btag_authority_tags($1), $2, $3, $4)
5843 $$ LANGUAGE SQL ROWS 10;
5845 CREATE OR REPLACE FUNCTION authority.atag_search_rank( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5846 SELECT * FROM authority.simple_heading_search_rank(authority.atag_authority_tags($1), $2, $3, $4)
5847 $$ LANGUAGE SQL ROWS 10;
5849 CREATE OR REPLACE FUNCTION authority.axis_search_rank_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5850 SELECT * FROM authority.simple_heading_search_rank(authority.axis_authority_tags_refs($1), $2, $3, $4)
5851 $$ LANGUAGE SQL ROWS 10;
5853 CREATE OR REPLACE FUNCTION authority.btag_search_rank_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5854 SELECT * FROM authority.simple_heading_search_rank(authority.btag_authority_tags_refs($1), $2, $3, $4)
5855 $$ LANGUAGE SQL ROWS 10;
5857 CREATE OR REPLACE FUNCTION authority.atag_search_rank_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5858 SELECT * FROM authority.simple_heading_search_rank(authority.atag_authority_tags_refs($1), $2, $3, $4)
5859 $$ LANGUAGE SQL ROWS 10;
5862 CREATE OR REPLACE FUNCTION authority.axis_search_heading( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5863 SELECT * FROM authority.simple_heading_search_heading(authority.axis_authority_tags($1), $2, $3, $4)
5864 $$ LANGUAGE SQL ROWS 10;
5866 CREATE OR REPLACE FUNCTION authority.btag_search_heading( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5867 SELECT * FROM authority.simple_heading_search_heading(authority.btag_authority_tags($1), $2, $3, $4)
5868 $$ LANGUAGE SQL ROWS 10;
5870 CREATE OR REPLACE FUNCTION authority.atag_search_heading( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5871 SELECT * FROM authority.simple_heading_search_heading(authority.atag_authority_tags($1), $2, $3, $4)
5872 $$ LANGUAGE SQL ROWS 10;
5874 CREATE OR REPLACE FUNCTION authority.axis_search_heading_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5875 SELECT * FROM authority.simple_heading_search_heading(authority.axis_authority_tags_refs($1), $2, $3, $4)
5876 $$ LANGUAGE SQL ROWS 10;
5878 CREATE OR REPLACE FUNCTION authority.btag_search_heading_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5879 SELECT * FROM authority.simple_heading_search_heading(authority.btag_authority_tags_refs($1), $2, $3, $4)
5880 $$ LANGUAGE SQL ROWS 10;
5882 CREATE OR REPLACE FUNCTION authority.atag_search_heading_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5883 SELECT * FROM authority.simple_heading_search_heading(authority.atag_authority_tags_refs($1), $2, $3, $4)
5884 $$ LANGUAGE SQL ROWS 10;
5888 -- Evergreen DB patch 0641.schema.org_unit_setting_json_check.sql
5892 -- check whether patch can be applied
5893 SELECT evergreen.upgrade_deps_block_check('0641', :eg_version);
5895 ALTER TABLE actor.org_unit_setting ADD CONSTRAINT aous_must_be_json CHECK ( is_json(value) );
5897 -- Evergreen DB patch 0642.data.acq-worksheet-hold-count.sql
5899 -- check whether patch can be applied
5900 SELECT evergreen.upgrade_deps_block_check('0642', :eg_version);
5902 UPDATE action_trigger.event_definition SET template =
5905 [%- SET li = target; -%]
5906 <div class="wrapper">
5907 <div class="summary" style='font-size:110%; font-weight:bold;'>
5909 <div>Title: [% helpers.get_li_attr("title", "", li.attributes) %]</div>
5910 <div>Author: [% helpers.get_li_attr("author", "", li.attributes) %]</div>
5911 <div class="count">Item Count: [% li.lineitem_details.size %]</div>
5912 <div class="lineid">Lineitem ID: [% li.id %]</div>
5913 <div>Open Holds: [% helpers.bre_open_hold_count(li.eg_bib_id) %]</div>
5915 [% IF li.distribution_formulas.size > 0 %]
5916 [% SET forms = [] %]
5917 [% FOREACH form IN li.distribution_formulas; forms.push(form.formula.name); END %]
5918 <div>Distribution Formulas: [% forms.join(',') %]</div>
5921 [% IF li.lineitem_notes.size > 0 %]
5924 [%- FOR note IN li.lineitem_notes -%]
5926 [% IF note.alert_text %]
5927 [% note.alert_text.code -%]
5928 [% IF note.value -%]
5945 <th>Call Number</th>
5947 <th>Shelving Location</th>
5953 [% FOREACH detail IN li.lineitem_details.sort('owning_lib') %]
5955 IF detail.eg_copy_id;
5956 SET copy = detail.eg_copy_id;
5957 SET cn_label = copy.call_number.label;
5960 SET cn_label = detail.cn_label;
5964 <!-- acq.lineitem_detail.id = [%- detail.id -%] -->
5965 <td style='padding:5px;'>[% detail.owning_lib.shortname %]</td>
5966 <td style='padding:5px;'>[% IF copy.barcode %]<span class="barcode" >[% detail.barcode %]</span>[% END %]</td>
5967 <td style='padding:5px;'>[% IF cn_label %]<span class="cn_label" >[% cn_label %]</span>[% END %]</td>
5968 <td style='padding:5px;'>[% IF detail.fund %]<span class="fund">[% detail.fund.code %] ([% detail.fund.year %])</span>[% END %]</td>
5969 <td style='padding:5px;'>[% copy.location.name %]</td>
5970 <td style='padding:5px;'>[% IF detail.recv_time %]<span class="recv_time">[% detail.recv_time %]</span>[% END %]</td>
5971 <td style='padding:5px;'>[% detail.note %]</td>
5981 SELECT evergreen.upgrade_deps_block_check('0643', :eg_version);
5989 FROM authority.record_entry
5992 AND id NOT IN (SELECT DISTINCT record FROM authority.simple_heading)
5994 INSERT INTO authority.simple_heading (record,atag,value,sort_value)
5995 SELECT record, atag, value, sort_value FROM authority.simple_heading_set(x);
6002 SELECT evergreen.upgrade_deps_block_check('0644', :eg_version);
6004 INSERT into config.org_unit_setting_type (name, grp, label, description, datatype) VALUES
6005 ( 'circ.holds.target_when_closed', 'circ',
6006 oils_i18n_gettext('circ.holds.target_when_closed',
6007 'Target copies for a hold even if copy''s circ lib is closed',
6009 oils_i18n_gettext('circ.holds.target_when_closed',
6010 'If this setting is true at a given org unit or one of its ancestors, the hold targeter will target copies from this org unit even if the org unit is closed (according to the actor.org_unit.closed_date table).',
6011 'coust', 'description'),
6013 ( 'circ.holds.target_when_closed_if_at_pickup_lib', 'circ',
6014 oils_i18n_gettext('circ.holds.target_when_closed_if_at_pickup_lib',
6015 'Target copies for a hold even if copy''s circ lib is closed IF the circ lib is the hold''s pickup lib',
6017 oils_i18n_gettext('circ.holds.target_when_closed_if_at_pickup_lib',
6018 'If this setting is true at a given org unit or one of its ancestors, the hold targeter will target copies from this org unit even if the org unit is closed (according to the actor.org_unit.closed_date table) IF AND ONLY IF the copy''s circ lib is the same as the hold''s pickup lib.',
6019 'coust', 'description'),
6023 -- Evergreen DB patch XXXX.data.hold-notification-cleanup-mod.sql
6025 -- check whether patch can be applied
6026 SELECT evergreen.upgrade_deps_block_check('0647', :eg_version);
6028 INSERT INTO action_trigger.cleanup ( module, description ) VALUES (
6029 'CreateHoldNotification',
6031 'CreateHoldNotification',
6032 'Creates a hold_notification record for each notified hold',
6038 UPDATE action_trigger.event_definition
6040 cleanup_success = 'CreateHoldNotification'
6042 id = 5 -- stock hold-ready email event_def
6043 AND cleanup_success IS NULL; -- don't clobber any existing cleanup mod
6045 -- Evergreen DB patch XXXX.schema.unnest-hold-permit-upgrade-script-repair.sql
6047 -- This patch makes no changes to the baseline schema and is
6048 -- only meant to repair a previous upgrade script.
6051 -- check whether patch can be applied
6052 SELECT evergreen.upgrade_deps_block_check('0651', :eg_version);
6054 --Removed dupe action.hold_request_permit_test
6056 -- Evergreen DB patch XXXX.data.vandelay-queue-bib-bucket-type.sql
6059 -- check whether patch can be applied
6060 SELECT evergreen.upgrade_deps_block_check('0652', :eg_version);
6062 INSERT INTO container.biblio_record_entry_bucket_type (code, label) VALUES (
6064 oils_i18n_gettext('vandelay_queue', 'Vandelay Queue', 'cbrebt', 'label')
6067 -- Evergreen DB patch XXXX.schema.unapi-indb-optional-org.sql
6069 -- check whether patch can be applied
6070 SELECT evergreen.upgrade_deps_block_check('0653', :eg_version);
6072 CREATE OR REPLACE FUNCTION evergreen.org_top() RETURNS SETOF actor.org_unit AS $$ SELECT * FROM actor.org_unit WHERE parent_ou IS NULL LIMIT 1; $$ LANGUAGE SQL ROWS 1;
6074 CREATE OR REPLACE FUNCTION unapi.biblio_record_entry_feed ( id_list BIGINT[], format TEXT, includes TEXT[], org TEXT DEFAULT '-', depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE, title TEXT DEFAULT NULL, description TEXT DEFAULT NULL, creator TEXT DEFAULT NULL, update_ts TEXT DEFAULT NULL, unapi_url TEXT DEFAULT NULL, header_xml XML DEFAULT NULL ) RETURNS XML AS $F$
6076 layout unapi.bre_output_layout%ROWTYPE;
6077 transform config.xml_transform%ROWTYPE;
6080 xmlns_uri TEXT := 'http://open-ils.org/spec/feed-xml/v1';
6082 element_list TEXT[];
6085 IF org = '-' OR org IS NULL THEN
6086 SELECT shortname INTO org FROM evergreen.org_top();
6089 SELECT id INTO ouid FROM actor.org_unit WHERE shortname = org;
6090 SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format;
6092 IF layout.name IS NULL THEN
6096 SELECT * INTO transform FROM config.xml_transform WHERE name = layout.transform;
6097 xmlns_uri := COALESCE(transform.namespace_uri,xmlns_uri);
6099 -- Gather the bib xml
6100 SELECT XMLAGG( unapi.bre(i, format, '', includes, org, depth, slimit, soffset, include_xmlns)) INTO tmp_xml FROM UNNEST( id_list ) i;
6102 IF layout.title_element IS NOT NULL THEN
6103 EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.title_element ||', CASE WHEN $4 THEN XMLATTRIBUTES( $1 AS xmlns) ELSE NULL END, $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, title, include_xmlns;
6106 IF layout.description_element IS NOT NULL THEN
6107 EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.description_element ||', CASE WHEN $4 THEN XMLATTRIBUTES( $1 AS xmlns) ELSE NULL END, $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, description, include_xmlns;
6110 IF layout.creator_element IS NOT NULL THEN
6111 EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.creator_element ||', CASE WHEN $4 THEN XMLATTRIBUTES( $1 AS xmlns) ELSE NULL END, $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, creator, include_xmlns;
6114 IF layout.update_ts_element IS NOT NULL THEN
6115 EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.update_ts_element ||', CASE WHEN $4 THEN XMLATTRIBUTES( $1 AS xmlns) ELSE NULL END, $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, update_ts, include_xmlns;
6118 IF unapi_url IS NOT NULL THEN
6119 EXECUTE $$SELECT XMLCONCAT( XMLELEMENT( name link, XMLATTRIBUTES( 'http://www.w3.org/1999/xhtml' AS xmlns, 'unapi-server' AS rel, $1 AS href, 'unapi' AS title)), $2)$$ INTO tmp_xml USING unapi_url, tmp_xml::XML;
6122 IF header_xml IS NOT NULL THEN tmp_xml := XMLCONCAT(header_xml,tmp_xml::XML); END IF;
6124 element_list := regexp_split_to_array(layout.feed_top,E'\\.');
6125 FOR i IN REVERSE ARRAY_UPPER(element_list, 1) .. 1 LOOP
6126 EXECUTE 'SELECT XMLELEMENT( name '|| quote_ident(element_list[i]) ||', CASE WHEN $4 THEN XMLATTRIBUTES( $1 AS xmlns) ELSE NULL END, $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, include_xmlns;
6129 RETURN tmp_xml::XML;
6131 $F$ LANGUAGE PLPGSQL;
6133 CREATE OR REPLACE FUNCTION unapi.bre ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT DEFAULT '-', depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
6135 me biblio.record_entry%ROWTYPE;
6136 layout unapi.bre_output_layout%ROWTYPE;
6137 xfrm config.xml_transform%ROWTYPE;
6146 IF org = '-' OR org IS NULL THEN
6147 SELECT shortname INTO org FROM evergreen.org_top();
6150 SELECT id INTO ouid FROM actor.org_unit WHERE shortname = org;
6152 IF ouid IS NULL THEN
6156 IF format = 'holdings_xml' THEN -- the special case
6157 output := unapi.holdings_xml( obj_id, ouid, org, depth, includes, slimit, soffset, include_xmlns);
6161 SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format;
6163 IF layout.name IS NULL THEN
6167 SELECT * INTO xfrm FROM config.xml_transform WHERE name = layout.transform;
6169 SELECT * INTO me FROM biblio.record_entry WHERE id = obj_id;
6171 -- grab SVF if we need them
6172 IF ('mra' = ANY (includes)) THEN
6173 axml := unapi.mra(obj_id,NULL,NULL,NULL,NULL);
6178 -- grab hodlings if we need them
6179 IF ('holdings_xml' = ANY (includes)) THEN
6180 hxml := unapi.holdings_xml(obj_id, ouid, org, depth, evergreen.array_remove_item_by_value(includes,'holdings_xml'), slimit, soffset, include_xmlns);
6186 -- generate our item node
6189 IF format = 'marcxml' THEN
6191 IF tmp_xml !~ E'<marc:' THEN -- If we're not using the prefixed namespace in this record, then remove all declarations of it
6192 tmp_xml := REGEXP_REPLACE(tmp_xml, ' xmlns:marc="http://www.loc.gov/MARC21/slim"', '', 'g');
6195 tmp_xml := oils_xslt_process(me.marc, xfrm.xslt)::XML;
6198 top_el := REGEXP_REPLACE(tmp_xml, E'^.*?<((?:\\S+:)?' || layout.holdings_element || ').*$', E'\\1');
6200 IF axml IS NOT NULL THEN
6201 tmp_xml := REGEXP_REPLACE(tmp_xml, '</' || top_el || '>(.*?)$', axml || '</' || top_el || E'>\\1');
6204 IF hxml IS NOT NULL THEN -- XXX how do we configure the holdings position?
6205 tmp_xml := REGEXP_REPLACE(tmp_xml, '</' || top_el || '>(.*?)$', hxml || '</' || top_el || E'>\\1');
6208 IF ('bre.unapi' = ANY (includes)) THEN
6209 output := REGEXP_REPLACE(
6211 '</' || top_el || '>(.*?)',
6215 'http://www.w3.org/1999/xhtml' AS xmlns,
6216 'unapi-id' AS class,
6217 'tag:open-ils.org:U2@bre/' || obj_id || '/' || org AS title
6219 )::TEXT || '</' || top_el || E'>\\1'
6225 output := REGEXP_REPLACE(output::TEXT,E'>\\s+<','><','gs')::XML;
6228 $F$ LANGUAGE PLPGSQL;
6233 SELECT evergreen.upgrade_deps_block_check('0654', :eg_version);
6235 INSERT INTO permission.perm_list ( id, code, description ) VALUES
6236 ( 514, 'UPDATE_PATRON_ACTIVE_CARD', oils_i18n_gettext( 514,
6237 'Allows a user to manually adjust a patron''s active cards', 'ppl', 'description')),
6238 ( 515, 'UPDATE_PATRON_PRIMARY_CARD', oils_i18n_gettext( 515,
6239 'Allows a user to manually adjust a patron''s primary card', 'ppl', 'description'));
6241 -- Evergreen DB patch 0655.config.bib_source.can_have_copies.sql
6243 -- This column introduces the ability to prevent bib records associated
6244 -- with specific bib sources from being able to have volumes or MFHD
6245 -- records attached to them.
6248 -- check whether patch can be applied
6249 SELECT evergreen.upgrade_deps_block_check('0655', :eg_version);
6251 ALTER TABLE config.bib_source
6252 ADD COLUMN can_have_copies BOOL NOT NULL DEFAULT TRUE;
6254 -- Evergreen DB patch XXXX.LP893315_schema.function.filter_deleted_acns_from_unapi.holdings_xml.sql
6256 -- Prevent deleted call numbers from hiding active call numbers / copies / URIs
6259 -- check whether patch can be applied
6260 SELECT evergreen.upgrade_deps_block_check('0656', :eg_version);
6262 CREATE OR REPLACE FUNCTION unapi.holdings_xml (bid BIGINT, ouid INT, org TEXT, depth INT DEFAULT NULL, includes TEXT[] DEFAULT NULL::TEXT[], slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE) RETURNS XML AS $F$
6266 CASE WHEN $8 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
6267 CASE WHEN ('bre' = ANY ($5)) THEN 'tag:open-ils.org:U2@bre/' || $1 || '/' || $3 ELSE NULL END AS id
6271 (SELECT XMLAGG(XMLELEMENT::XML) FROM (
6274 XMLATTRIBUTES('public' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
6276 FROM asset.opac_ou_record_copy_count($2, $1)
6280 XMLATTRIBUTES('staff' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
6282 FROM asset.staff_ou_record_copy_count($2, $1)
6287 WHEN ('bmp' = ANY ($5)) THEN
6289 name monograph_parts,
6290 (SELECT XMLAGG(bmp) FROM (
6291 SELECT unapi.bmp( id, 'xml', 'monograph_part', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($5,'bre'), 'holdings_xml'), $3, $4, $6, $7, FALSE)
6292 FROM biblio.monograph_part
6300 (SELECT XMLAGG(acn) FROM (
6301 SELECT unapi.acn(acn.id,'xml','volume',array_remove_item_by_value( evergreen.array_remove_item_by_value($5,'holdings_xml'),'bre'), $3, $4, $6, $7, FALSE)
6302 FROM asset.call_number acn
6303 WHERE acn.record = $1
6304 AND acn.deleted IS FALSE
6308 JOIN actor.org_unit_descendants(
6313 FROM actor.org_unit_type aout
6314 JOIN actor.org_unit aou ON (aou.ou_type = aout.id AND aou.id = $2)
6317 ) aoud ON (acp.circ_lib = aoud.id)
6320 ORDER BY label_sortkey
6325 CASE WHEN ('ssub' = ANY ($5)) THEN
6328 (SELECT XMLAGG(ssub) FROM (
6329 SELECT unapi.ssub(id,'xml','subscription','{}'::TEXT[], $3, $4, $6, $7, FALSE)
6330 FROM serial.subscription
6331 WHERE record_entry = $1
6335 CASE WHEN ('acp' = ANY ($5)) THEN
6337 name foreign_copies,
6338 (SELECT XMLAGG(acp) FROM (
6339 SELECT unapi.acp(p.target_copy,'xml','copy','{}'::TEXT[], $3, $4, $6, $7, FALSE)
6340 FROM biblio.peer_bib_copy_map p
6341 JOIN asset.copy c ON (p.target_copy = c.id)
6342 WHERE NOT c.deleted AND peer_record = $1
6349 -- Evergreen DB patch 0657.schema.address-alert.sql
6352 -- check whether patch can be applied
6353 SELECT evergreen.upgrade_deps_block_check('0657', :eg_version);
6355 CREATE TABLE actor.address_alert (
6356 id SERIAL PRIMARY KEY,
6357 owner INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
6358 active BOOL NOT NULL DEFAULT TRUE,
6359 match_all BOOL NOT NULL DEFAULT TRUE,
6360 alert_message TEXT NOT NULL,
6368 mailing_address BOOL NOT NULL DEFAULT FALSE,
6369 billing_address BOOL NOT NULL DEFAULT FALSE
6372 CREATE OR REPLACE FUNCTION actor.address_alert_matches (
6381 mailing_address BOOL DEFAULT FALSE,
6382 billing_address BOOL DEFAULT FALSE
6383 ) RETURNS SETOF actor.address_alert AS $$
6386 FROM actor.address_alert
6389 AND owner IN (SELECT id FROM actor.org_unit_ancestors($1))
6391 (NOT mailing_address AND NOT billing_address)
6392 OR (mailing_address AND $9)
6393 OR (billing_address AND $10)
6398 AND COALESCE($2, '') ~* COALESCE(street1, '.*')
6399 AND COALESCE($3, '') ~* COALESCE(street2, '.*')
6400 AND COALESCE($4, '') ~* COALESCE(city, '.*')
6401 AND COALESCE($5, '') ~* COALESCE(county, '.*')
6402 AND COALESCE($6, '') ~* COALESCE(state, '.*')
6403 AND COALESCE($7, '') ~* COALESCE(country, '.*')
6404 AND COALESCE($8, '') ~* COALESCE(post_code, '.*')
6418 ORDER BY actor.org_unit_proximity(owner, $1)
6423 DROP FUNCTION actor.address_alert_matches(INT, TEXT, TEXT, TEXT, TEXT, TEXT, TEXT, TEXT, BOOL, BOOL);
6424 DROP TABLE actor.address_alert;
6426 -- Evergreen DB patch 0659.add_create_report_perms.sql
6428 -- Add a permission to control the ability to create report templates
6431 -- check whether patch can be applied
6432 SELECT evergreen.upgrade_deps_block_check('0659', :eg_version);
6434 -- FIXME: add/check SQL statements to perform the upgrade
6435 INSERT INTO permission.perm_list ( id, code, description ) VALUES
6436 ( 516, 'CREATE_REPORT_TEMPLATE', oils_i18n_gettext( 516,
6437 'Allows a user to create report templates', 'ppl', 'description' ));
6439 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable)
6440 SELECT grp, 516, depth, grantable
6441 FROM permission.grp_perm_map
6444 FROM permission.perm_list
6445 WHERE code = 'RUN_REPORTS'
6450 SELECT evergreen.upgrade_deps_block_check('0660', :eg_version);
6452 UPDATE action_trigger.event_definition SET template = $$
6454 # target is the bookbag itself. The 'items' variable does not need to be in
6455 # the environment because a special reactor will take care of filling it in.
6458 bibxml = helpers.unapi_bre(item.target_biblio_record_entry, {flesh => '{mra}'});
6460 FOR part IN bibxml.findnodes('//*[@tag="245"]/*[@code="a" or @code="b"]');
6461 title = title _ part.textContent;
6463 author = bibxml.findnodes('//*[@tag="100"]/*[@code="a"]').textContent;
6464 item_type = bibxml.findnodes('//*[local-name()="attributes"]/*[local-name()="field"][@name="item_type"]').getAttribute('coded-value');
6466 helpers.csv_datum(title) %],[% helpers.csv_datum(author) %],[% helpers.csv_datum(item_type) %],[% FOR note IN item.notes; helpers.csv_datum(note.note); ","; END; "\n";
6469 WHERE reactor = 'ContainerCSV';
6471 -- Evergreen DB patch 0661.data.yaous-opac-tag-circed-items.sql
6473 -- Add org unit setting that enables users who have opted in to
6474 -- tracking their circulation history to see which items they
6475 -- have previously checked out in search results.
6478 -- check whether patch can be applied
6479 SELECT evergreen.upgrade_deps_block_check('0661', :eg_version);
6481 INSERT into config.org_unit_setting_type
6482 (name, grp, label, description, datatype)
6484 'opac.search.tag_circulated_items',
6487 'opac.search.tag_circulated_items',
6488 'Tag Circulated Items in Results',
6493 'opac.search.tag_circulated_items',
6494 'When a user is both logged in and has opted in to circulation history tracking, turning on this setting will cause previous (or currently) circulated items to be highlighted in search results',
6502 -- Evergreen DB patch 0662.schema.coded-value-map-index-normalizer.sql
6505 -- check whether patch can be applied
6506 SELECT evergreen.upgrade_deps_block_check('0662', :eg_version);
6508 -- create the normalizer
6509 CREATE OR REPLACE FUNCTION evergreen.coded_value_map_normalizer( input TEXT, ctype TEXT )
6511 SELECT COALESCE(value,$1)
6512 FROM config.coded_value_map
6513 WHERE ctype = $2 AND code = $1;
6516 -- register the normalizer
6517 INSERT INTO config.index_normalizer (name, description, func, param_count) VALUES (
6518 'Coded Value Map Normalizer',
6519 'Applies coded_value_map mapping of values',
6520 'coded_value_map_normalizer',
6524 -- Evergreen DB patch 0663.schema.archive_circ_stat_cats.sql
6526 -- Enables users to set copy and patron stat cats to be archivable
6527 -- for the purposes of statistics even after the circs are aged.
6530 -- check whether patch can be applied
6531 SELECT evergreen.upgrade_deps_block_check('0663', :eg_version);
6535 CREATE TABLE action.archive_actor_stat_cat (
6536 id BIGSERIAL PRIMARY KEY,
6537 xact BIGINT NOT NULL,
6538 stat_cat INT NOT NULL,
6542 CREATE TABLE action.archive_asset_stat_cat (
6543 id BIGSERIAL PRIMARY KEY,
6544 xact BIGINT NOT NULL,
6545 stat_cat INT NOT NULL,
6549 -- Add columns to existing tables
6551 -- Archive Flag Columns
6552 ALTER TABLE actor.stat_cat
6553 ADD COLUMN checkout_archive BOOL NOT NULL DEFAULT FALSE;
6554 ALTER TABLE asset.stat_cat
6555 ADD COLUMN checkout_archive BOOL NOT NULL DEFAULT FALSE;
6557 -- Circulation copy column
6558 ALTER TABLE action.circulation
6559 ADD COLUMN copy_location INT NULL REFERENCES asset.copy_location(id) DEFERRABLE INITIALLY DEFERRED;
6561 -- Create trigger function to auto-fill the copy_location field
6562 CREATE OR REPLACE FUNCTION action.fill_circ_copy_location () RETURNS TRIGGER AS $$
6564 SELECT INTO NEW.copy_location location FROM asset.copy WHERE id = NEW.target_copy;
6567 $$ LANGUAGE PLPGSQL;
6569 -- Create trigger function to auto-archive stat cat entries
6570 CREATE OR REPLACE FUNCTION action.archive_stat_cats () RETURNS TRIGGER AS $$
6572 INSERT INTO action.archive_actor_stat_cat(xact, stat_cat, value)
6573 SELECT NEW.id, asceum.stat_cat, asceum.stat_cat_entry
6574 FROM actor.stat_cat_entry_usr_map asceum
6575 JOIN actor.stat_cat sc ON asceum.stat_cat = sc.id
6576 WHERE NEW.usr = asceum.target_usr AND sc.checkout_archive;
6577 INSERT INTO action.archive_asset_stat_cat(xact, stat_cat, value)
6578 SELECT NEW.id, ascecm.stat_cat, asce.value
6579 FROM asset.stat_cat_entry_copy_map ascecm
6580 JOIN asset.stat_cat sc ON ascecm.stat_cat = sc.id
6581 JOIN asset.stat_cat_entry asce ON ascecm.stat_cat_entry = asce.id
6582 WHERE NEW.target_copy = ascecm.owning_copy AND sc.checkout_archive;
6585 $$ LANGUAGE PLPGSQL;
6588 CREATE TRIGGER fill_circ_copy_location_tgr BEFORE INSERT ON action.circulation FOR EACH ROW EXECUTE PROCEDURE action.fill_circ_copy_location();
6589 CREATE TRIGGER archive_stat_cats_tgr AFTER INSERT ON action.circulation FOR EACH ROW EXECUTE PROCEDURE action.archive_stat_cats();
6591 -- Ensure all triggers are disabled for speedy updates!
6592 ALTER TABLE action.circulation DISABLE TRIGGER ALL;
6594 -- Update view to use circ's copy_location field instead of the copy's current copy_location field
6595 CREATE OR REPLACE VIEW action.all_circulation AS
6596 SELECT id,usr_post_code, usr_home_ou, usr_profile, usr_birth_year, copy_call_number, copy_location,
6597 copy_owning_lib, copy_circ_lib, copy_bib_record, xact_start, xact_finish, target_copy,
6598 circ_lib, circ_staff, checkin_staff, checkin_lib, renewal_remaining, grace_period, due_date,
6599 stop_fines_time, checkin_time, create_time, duration, fine_interval, recurring_fine,
6600 max_fine, phone_renewal, desk_renewal, opac_renewal, duration_rule, recurring_fine_rule,
6601 max_fine_rule, stop_fines, workstation, checkin_workstation, checkin_scan_time, parent_circ
6602 FROM action.aged_circulation
6604 SELECT DISTINCT circ.id,COALESCE(a.post_code,b.post_code) AS usr_post_code, p.home_ou AS usr_home_ou, p.profile AS usr_profile, EXTRACT(YEAR FROM p.dob)::INT AS usr_birth_year,
6605 cp.call_number AS copy_call_number, circ.copy_location, cn.owning_lib AS copy_owning_lib, cp.circ_lib AS copy_circ_lib,
6606 cn.record AS copy_bib_record, circ.xact_start, circ.xact_finish, circ.target_copy, circ.circ_lib, circ.circ_staff, circ.checkin_staff,
6607 circ.checkin_lib, circ.renewal_remaining, circ.grace_period, circ.due_date, circ.stop_fines_time, circ.checkin_time, circ.create_time, circ.duration,
6608 circ.fine_interval, circ.recurring_fine, circ.max_fine, circ.phone_renewal, circ.desk_renewal, circ.opac_renewal, circ.duration_rule,
6609 circ.recurring_fine_rule, circ.max_fine_rule, circ.stop_fines, circ.workstation, circ.checkin_workstation, circ.checkin_scan_time,
6611 FROM action.circulation circ
6612 JOIN asset.copy cp ON (circ.target_copy = cp.id)
6613 JOIN asset.call_number cn ON (cp.call_number = cn.id)
6614 JOIN actor.usr p ON (circ.usr = p.id)
6615 LEFT JOIN actor.usr_address a ON (p.mailing_address = a.id)
6616 LEFT JOIN actor.usr_address b ON (p.billing_address = b.id);
6618 -- Update action.circulation with real copy_location numbers instead of all NULL
6619 DO $$BEGIN RAISE WARNING 'We are about to do an update on every row in action.circulation. This may take a while. %', timeofday(); END;$$;
6620 UPDATE action.circulation circ SET copy_location = ac.location FROM asset.copy ac WHERE ac.id = circ.target_copy;
6622 -- Set not null/default on new column, re-enable triggers
6623 ALTER TABLE action.circulation
6624 ALTER COLUMN copy_location SET NOT NULL,
6625 ALTER COLUMN copy_location SET DEFAULT 1,
6628 -- Evergreen DB patch 0664.schema.hold-current-shelf-lib.sql
6633 -- check whether patch can be applied
6634 SELECT evergreen.upgrade_deps_block_check('0664', :eg_version);
6636 -- add the new column
6637 ALTER TABLE action.hold_request ADD COLUMN current_shelf_lib
6638 INT REFERENCES actor.org_unit DEFERRABLE INITIALLY DEFERRED;
6640 -- Add some others before the UPDATE we are about to do breaks our ability to add columns
6641 -- But we need this table first.
6642 CREATE TABLE config.sms_carrier (
6643 id SERIAL PRIMARY KEY,
6647 active BOOLEAN DEFAULT TRUE
6650 ALTER TABLE action.hold_request ADD COLUMN sms_notify TEXT;
6651 ALTER TABLE action.hold_request ADD COLUMN sms_carrier INT REFERENCES config.sms_carrier (id);
6652 ALTER TABLE action.hold_request ADD CONSTRAINT sms_check CHECK (
6654 OR sms_carrier IS NOT NULL -- and implied sms_notify IS NOT NULL
6659 -- set the value for current_shelf_lib on existing shelved holds
6660 UPDATE action.hold_request
6661 SET current_shelf_lib = pickup_lib
6664 action.hold_request.shelf_time IS NOT NULL
6665 AND action.hold_request.capture_time IS NOT NULL
6666 AND action.hold_request.current_copy IS NOT NULL
6667 AND action.hold_request.fulfillment_time IS NULL
6668 AND action.hold_request.cancel_time IS NULL
6669 AND asset.copy.id = action.hold_request.current_copy
6670 AND asset.copy.status = 8; -- on holds shelf
6673 SELECT evergreen.upgrade_deps_block_check('0666', :eg_version);
6675 -- 950.data.seed-values.sql
6676 INSERT INTO config.settings_group (name, label) VALUES
6681 'SMS Text Messages',
6688 INSERT INTO config.org_unit_setting_type (name, grp, label, description, datatype) VALUES
6694 'Enable features that send SMS text messages.',
6700 'Current features that use SMS include hold-ready-for-pickup notifications and a "Send Text" action for call numbers in the OPAC. If this setting is not enabled, the SMS options will not be offered to the user. Unless you are carefully silo-ing patrons and their use of the OPAC, the context org for this setting should be the top org in the org hierarchy, otherwise patrons can trample their user settings when jumping between orgs.',
6707 'sms.disable_authentication_requirement.callnumbers',
6710 'sms.disable_authentication_requirement.callnumbers',
6711 'Disable auth requirement for texting call numbers.',
6716 'sms.disable_authentication_requirement.callnumbers',
6717 'Disable authentication requirement for sending call number information via SMS from the OPAC.',
6725 -- 090.schema.action.sql
6726 -- 950.data.seed-values.sql
6727 INSERT INTO config.usr_setting_type (name,grp,opac_visible,label,description,datatype,fm_class) VALUES (
6728 'opac.default_sms_carrier',
6732 'opac.default_sms_carrier',
6733 'Default SMS/Text Carrier',
6738 'opac.default_sms_carrier',
6739 'Default SMS/Text Carrier',
6747 INSERT INTO config.usr_setting_type (name,grp,opac_visible,label,description,datatype) VALUES (
6748 'opac.default_sms_notify',
6752 'opac.default_sms_notify',
6753 'Default SMS/Text Number',
6758 'opac.default_sms_notify',
6759 'Default SMS/Text Number',
6766 INSERT INTO config.usr_setting_type (name,grp,opac_visible,label,description,datatype) VALUES (
6767 'opac.default_phone',
6771 'opac.default_phone',
6772 'Default Phone Number',
6777 'opac.default_phone',
6778 'Default Phone Number',
6785 SELECT setval( 'config.sms_carrier_id_seq', 1000 );
6786 INSERT INTO config.sms_carrier VALUES
6803 'opensrf+$number@localhost',
6822 '$number@pcs.rogers.com',
6835 'Rogers Wireless (Alternate)',
6839 '1$number@mms.rogers.com',
6856 '$number@msg.telus.com',
6875 '$number@msg.telus.com',
6905 'Bell Mobility & Solo Mobile',
6909 '$number@txt.bell.ca',
6922 'Bell Mobility & Solo Mobile (Alternate)',
6926 '$number@txt.bellmobility.ca',
6943 '$number@sms.wirefree.informe.ca',
6960 '$number@mobiletxt.ca',
6977 '$number@sms.sasktel.com',
6994 '$number@text.mtsmobility.com',
7011 '$number@vmobile.ca',
7030 '$number@msg.iridium.com',
7047 '$number@msg.globalstarusa.com',
7064 '$number@bulletinmessenger.net', -- International Formatted number
7081 '$number@api.panaceamobile.com',
7100 '$number@cbeyond.sprintpcs.com',
7113 'General Communications, Inc.',
7117 '$number@mobile.gci.net',
7130 'Golden State Cellular',
7134 '$number@gscsms.com',
7141 'Cincinnati, Ohio, USA',
7151 '$number@gocbw.com',
7164 'Hawaiian Telcom Wireless',
7168 '$number@hawaii.sprintpcs.com',
7181 'i wireless (T-Mobile)',
7185 '$number.iws@iwspcs.net',
7198 'i-wireless (Sprint PCS)',
7202 '$number@iwirelesshometext.com',
7219 '$number@mymetropcs.com',
7236 '$number@mobile.kajeet.net',
7253 '$number@SMS.elementmobile.net',
7270 '$number@echoemail.net',
7287 '$number@myboostmobile.com',
7304 '$number@bellsouth.com',
7317 'Bluegrass Cellular',
7321 '$number@sms.bluecell.com',
7334 'AT&T Enterprise Paging',
7338 '$number@page.att.net',
7351 'AT&T Mobility/Wireless',
7355 '$number@txt.att.net',
7368 'AT&T Global Smart Messaging Suite',
7372 '$number@sms.smartmessagingsuite.com',
7385 'Alltel (Allied Wireless)',
7389 '$number@sms.alltelwireless.com',
7402 'Alaska Communications',
7406 '$number@msg.acsalaska.com',
7423 '$number@paging.acswireless.com',
7436 'Cingular (GoPhone prepaid)',
7440 '$number@cingulartext.com',
7453 'Cingular (Postpaid)',
7457 '$number@cingular.com',
7470 'Cellular One (Dobson) / O2 / Orange',
7474 '$number@mobile.celloneusa.com',
7491 '$number@csouth1.com',
7508 '$number@cellcom.quiktxt.com',
7521 'Chariton Valley Wireless',
7525 '$number@sms.cvalley.net',
7542 '$number@sms.mycricket.com',
7555 'Cleartalk Wireless',
7559 '$number@sms.cleartalk.us',
7576 '$number@sms.edgewireless.com',
7593 '$number@rinasms.com',
7610 '$number@tmomail.net',
7623 'Straight Talk / PagePlus Cellular',
7627 '$number@vtext.com',
7640 'South Central Communications',
7644 '$number@rinasms.com',
7661 '$number@smtext.com',
7678 '$number@messaging.sprintpcs.com',
7695 '$number@messaging.nextel.com',
7712 '$number@zsend.com', -- nine digit number
7729 '$number@qwestmp.com',
7746 '$number@email.uscc.net',
7763 '$number@utext.com',
7780 '$number@teleflip.com',
7797 '$number@vmobl.com',
7814 '$number@vtext.com',
7831 '$number@usamobility.net',
7848 '$number@viaerosms.com',
7865 '$number@mmst5.tracfone.com',
7878 'Centennial Wireless',
7882 '$number@cwemail.com',
7886 -- South Korea and USA
7891 'South Korea and USA',
7901 '$number@myhelio.com',
7906 INSERT INTO permission.perm_list ( id, code, description ) VALUES
7909 'ADMIN_SMS_CARRIER',
7912 'Allows a user to add/create/delete SMS Carrier entries.',
7919 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable)
7921 pgt.id, perm.id, aout.depth, TRUE
7923 permission.grp_tree pgt,
7924 permission.perm_list perm,
7925 actor.org_unit_type aout
7927 pgt.name = 'Global Administrator' AND
7928 aout.name = 'Consortium' AND
7929 perm.code = 'ADMIN_SMS_CARRIER';
7931 INSERT INTO action_trigger.reactor (
7936 'Send an SMS text message based on a user-defined template'
7939 INSERT INTO action_trigger.event_definition (
7954 'Hold Ready for Pickup SMS Notification',
7958 'CreateHoldNotification',
7963 [%- user = target.0.usr -%]
7964 From: [%- params.sender_email || default_sender %]
7965 To: [%- params.recipient_email || helpers.get_sms_gateway_email(target.0.sms_carrier,target.0.sms_notify) %]
7966 Subject: [% target.size %] hold(s) ready
7968 [% FOR hold IN target %][%-
7969 bibxml = helpers.xml_doc( hold.current_copy.call_number.record.marc );
7971 FOR part IN bibxml.findnodes(''//*[@tag="245"]/*[@code="a"]'');
7972 title = title _ part.textContent;
7974 author = bibxml.findnodes(''//*[@tag="100"]/*[@code="a"]'').textContent;
7975 %][% hold.usr.first_given_name %]:[% title %] @ [% hold.pickup_lib.name %]
7980 INSERT INTO action_trigger.environment (
7984 currval('action_trigger.event_definition_id_seq'),
7985 'current_copy.call_number.record.simple_record'
7987 currval('action_trigger.event_definition_id_seq'),
7990 currval('action_trigger.event_definition_id_seq'),
7991 'pickup_lib.billing_address'
7994 INSERT INTO action_trigger.hook(
8000 'acn.format.sms_text',
8003 'acn.format.sms_text',
8004 'A text message has been requested for a call number.',
8011 INSERT INTO action_trigger.event_definition (
8023 'acn.format.sms_text',
8027 From: [%- params.sender_email || default_sender %]
8028 To: [%- params.recipient_email || helpers.get_sms_gateway_email(user_data.sms_carrier,user_data.sms_notify) %]
8029 Subject: Call Number
8032 bibxml = helpers.xml_doc( target.record.marc );
8034 FOR part IN bibxml.findnodes(''//*[@tag="245"]/*[@code="a" or @code="b"]'');
8035 title = title _ part.textContent;
8037 author = bibxml.findnodes(''//*[@tag="100"]/*[@code="a"]'').textContent;
8039 Call Number: [% target.label %]
8040 Location: [% helpers.get_most_populous_location( target.id ).name %]
8041 Library: [% target.owning_lib.name %]
8046 Author: [% author %]
8051 INSERT INTO action_trigger.environment (
8055 currval('action_trigger.event_definition_id_seq'),
8056 'record.simple_record'
8058 currval('action_trigger.event_definition_id_seq'),
8059 'owning_lib.billing_address'
8063 -- DELETE FROM actor.usr_setting WHERE name = 'opac.default_phone' OR name in ( SELECT name FROM config.usr_setting_type WHERE grp = 'sms' ); DELETE FROM config.usr_setting_type WHERE name = 'opac.default_phone' OR grp = 'sms'; DELETE FROM actor.org_unit_setting WHERE name in ( SELECT name FROM config.org_unit_setting_type WHERE grp = 'sms' ); DELETE FROM config.org_unit_setting_type_log WHERE field_name in ( SELECT name FROM config.org_unit_setting_type WHERE grp = 'sms' ); DELETE FROM config.org_unit_setting_type WHERE grp = 'sms'; DELETE FROM config.settings_group WHERE name = 'sms'; DELETE FROM permission.grp_perm_map WHERE perm = 519; DELETE FROM permission.perm_list WHERE id = 519; ALTER TABLE action.hold_request DROP CONSTRAINT sms_check; ALTER TABLE action.hold_request DROP COLUMN sms_notify; ALTER TABLE action.hold_request DROP COLUMN sms_carrier; DROP TABLE config.sms_carrier; DELETE FROM action_trigger.event WHERE event_def = ( SELECT id FROM action_trigger.event_definition WHERE name = 'Hold Ready for Pickup SMS Notification' ); DELETE FROM action_trigger.environment WHERE event_def = ( SELECT id FROM action_trigger.event_definition WHERE name = 'Hold Ready for Pickup SMS Notification' ); DELETE FROM action_trigger.event_definition WHERE name = 'Hold Ready for Pickup SMS Notification'; DELETE FROM action_trigger.event WHERE event_def IN ( SELECT id FROM action_trigger.event_definition WHERE hook = 'acn.format.sms_text' ); DELETE FROM action_trigger.environment WHERE event_def IN ( SELECT id FROM action_trigger.event_definition WHERE hook = 'acn.format.sms_text' ); DELETE FROM action_trigger.event_definition WHERE hook = 'acn.format.sms_text'; DELETE FROM action_trigger.hook WHERE key = 'acn.format.sms_text'; DELETE FROM action_trigger.reactor WHERE module = 'SendSMS'; DELETE FROM config.upgrade_log WHERE version = 'XXXX';
8066 SELECT evergreen.upgrade_deps_block_check('0667', :eg_version);
8068 ALTER TABLE config.standing_penalty ADD staff_alert BOOL NOT NULL DEFAULT FALSE;
8071 -- for backwards compat, set all blocking penalties to alerts
8072 UPDATE config.standing_penalty SET staff_alert = TRUE
8073 WHERE id = 20 OR block_list IS NOT NULL;
8075 -- Evergreen DB patch 0668.schema.fix_indb_hold_permit.sql
8077 -- FIXME: insert description of change, if needed
8081 -- check whether patch can be applied
8082 SELECT evergreen.upgrade_deps_block_check('0668', :eg_version);
8084 -- FIXME: add/check SQL statements to perform the upgrade
8085 CREATE OR REPLACE FUNCTION action.hold_request_permit_test( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT, retargetting BOOL ) RETURNS SETOF action.matrix_test_result AS $func$
8088 user_object actor.usr%ROWTYPE;
8089 age_protect_object config.rule_age_hold_protect%ROWTYPE;
8090 standing_penalty config.standing_penalty%ROWTYPE;
8091 transit_range_ou_type actor.org_unit_type%ROWTYPE;
8092 transit_source actor.org_unit%ROWTYPE;
8093 item_object asset.copy%ROWTYPE;
8094 item_cn_object asset.call_number%ROWTYPE;
8095 item_status_object config.copy_status%ROWTYPE;
8096 item_location_object asset.copy_location%ROWTYPE;
8097 ou_skip actor.org_unit_setting%ROWTYPE;
8098 result action.matrix_test_result;
8099 hold_test config.hold_matrix_matchpoint%ROWTYPE;
8100 use_active_date TEXT;
8101 age_protect_date TIMESTAMP WITH TIME ZONE;
8103 hold_transit_prox INT;
8104 frozen_hold_count INT;
8105 context_org_list INT[];
8108 SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
8109 SELECT INTO context_org_list ARRAY_ACCUM(id) FROM actor.org_unit_full_path( pickup_ou );
8111 result.success := TRUE;
8113 -- Fail if we couldn't find a user
8114 IF user_object.id IS NULL THEN
8115 result.fail_part := 'no_user';
8116 result.success := FALSE;
8122 SELECT INTO item_object * FROM asset.copy WHERE id = match_item;
8124 -- Fail if we couldn't find a copy
8125 IF item_object.id IS NULL THEN
8126 result.fail_part := 'no_item';
8127 result.success := FALSE;
8133 SELECT INTO matchpoint_id action.find_hold_matrix_matchpoint(pickup_ou, request_ou, match_item, match_user, match_requestor);
8134 result.matchpoint := matchpoint_id;
8136 SELECT INTO ou_skip * FROM actor.org_unit_setting WHERE name = 'circ.holds.target_skip_me' AND org_unit = item_object.circ_lib;
8138 -- Fail if the circ_lib for the item has circ.holds.target_skip_me set to true
8139 IF ou_skip.id IS NOT NULL AND ou_skip.value = 'true' THEN
8140 result.fail_part := 'circ.holds.target_skip_me';
8141 result.success := FALSE;
8147 -- Fail if user is barred
8148 IF user_object.barred IS TRUE THEN
8149 result.fail_part := 'actor.usr.barred';
8150 result.success := FALSE;
8156 SELECT INTO item_cn_object * FROM asset.call_number WHERE id = item_object.call_number;
8157 SELECT INTO item_status_object * FROM config.copy_status WHERE id = item_object.status;
8158 SELECT INTO item_location_object * FROM asset.copy_location WHERE id = item_object.location;
8160 -- Fail if we couldn't find any matchpoint (requires a default)
8161 IF matchpoint_id IS NULL THEN
8162 result.fail_part := 'no_matchpoint';
8163 result.success := FALSE;
8169 SELECT INTO hold_test * FROM config.hold_matrix_matchpoint WHERE id = matchpoint_id;
8171 IF hold_test.holdable IS FALSE THEN
8172 result.fail_part := 'config.hold_matrix_test.holdable';
8173 result.success := FALSE;
8178 IF item_object.holdable IS FALSE THEN
8179 result.fail_part := 'item.holdable';
8180 result.success := FALSE;
8185 IF item_status_object.holdable IS FALSE THEN
8186 result.fail_part := 'status.holdable';
8187 result.success := FALSE;
8192 IF item_location_object.holdable IS FALSE THEN
8193 result.fail_part := 'location.holdable';
8194 result.success := FALSE;
8199 IF hold_test.transit_range IS NOT NULL THEN
8200 SELECT INTO transit_range_ou_type * FROM actor.org_unit_type WHERE id = hold_test.transit_range;
8201 IF hold_test.distance_is_from_owner THEN
8202 SELECT INTO transit_source ou.* FROM actor.org_unit ou JOIN asset.call_number cn ON (cn.owning_lib = ou.id) WHERE cn.id = item_object.call_number;
8204 SELECT INTO transit_source * FROM actor.org_unit WHERE id = item_object.circ_lib;
8207 PERFORM * FROM actor.org_unit_descendants( transit_source.id, transit_range_ou_type.depth ) WHERE id = pickup_ou;
8210 result.fail_part := 'transit_range';
8211 result.success := FALSE;
8217 FOR standing_penalty IN
8218 SELECT DISTINCT csp.*
8219 FROM actor.usr_standing_penalty usp
8220 JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
8221 WHERE usr = match_user
8222 AND usp.org_unit IN ( SELECT * FROM unnest(context_org_list) )
8223 AND (usp.stop_date IS NULL or usp.stop_date > NOW())
8224 AND csp.block_list LIKE '%HOLD%' LOOP
8226 result.fail_part := standing_penalty.name;
8227 result.success := FALSE;
8232 IF hold_test.stop_blocked_user IS TRUE THEN
8233 FOR standing_penalty IN
8234 SELECT DISTINCT csp.*
8235 FROM actor.usr_standing_penalty usp
8236 JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
8237 WHERE usr = match_user
8238 AND usp.org_unit IN ( SELECT * FROM unnest(context_org_list) )
8239 AND (usp.stop_date IS NULL or usp.stop_date > NOW())
8240 AND csp.block_list LIKE '%CIRC%' LOOP
8242 result.fail_part := standing_penalty.name;
8243 result.success := FALSE;
8249 IF hold_test.max_holds IS NOT NULL AND NOT retargetting THEN
8250 SELECT INTO hold_count COUNT(*)
8251 FROM action.hold_request
8252 WHERE usr = match_user
8253 AND fulfillment_time IS NULL
8254 AND cancel_time IS NULL
8255 AND CASE WHEN hold_test.include_frozen_holds THEN TRUE ELSE frozen IS FALSE END;
8257 IF hold_count >= hold_test.max_holds THEN
8258 result.fail_part := 'config.hold_matrix_test.max_holds';
8259 result.success := FALSE;
8265 IF item_object.age_protect IS NOT NULL THEN
8266 SELECT INTO age_protect_object * FROM config.rule_age_hold_protect WHERE id = item_object.age_protect;
8267 IF hold_test.distance_is_from_owner THEN
8268 SELECT INTO use_active_date value FROM actor.org_unit_ancestor_setting('circ.holds.age_protect.active_date', item_cn_object.owning_lib);
8270 SELECT INTO use_active_date value FROM actor.org_unit_ancestor_setting('circ.holds.age_protect.active_date', item_object.circ_lib);
8272 IF use_active_date = 'true' THEN
8273 age_protect_date := COALESCE(item_object.active_date, NOW());
8275 age_protect_date := item_object.create_date;
8277 IF age_protect_date + age_protect_object.age > NOW() THEN
8278 IF hold_test.distance_is_from_owner THEN
8279 SELECT INTO item_cn_object * FROM asset.call_number WHERE id = item_object.call_number;
8280 SELECT INTO hold_transit_prox prox FROM actor.org_unit_proximity WHERE from_org = item_cn_object.owning_lib AND to_org = pickup_ou;
8282 SELECT INTO hold_transit_prox prox FROM actor.org_unit_proximity WHERE from_org = item_object.circ_lib AND to_org = pickup_ou;
8285 IF hold_transit_prox > age_protect_object.prox THEN
8286 result.fail_part := 'config.rule_age_hold_protect.prox';
8287 result.success := FALSE;
8300 $func$ LANGUAGE plpgsql;
8303 -- Evergreen DB patch 0669.data.recall_and_force_holds.sql
8305 -- FIXME: insert description of change, if needed
8309 -- check whether patch can be applied
8310 SELECT evergreen.upgrade_deps_block_check('0669', :eg_version);
8312 -- FIXME: add/check SQL statements to perform the upgrade
8313 INSERT INTO permission.perm_list ( id, code, description ) VALUES
8314 ( 517, 'COPY_HOLDS_FORCE', oils_i18n_gettext( 517,
8315 'Allow a user to place a force hold on a specific copy', 'ppl', 'description' )),
8316 ( 518, 'COPY_HOLDS_RECALL', oils_i18n_gettext( 518,
8317 'Allow a user to place a cataloging recall on a specific copy', 'ppl', 'description' ));
8320 -- Evergreen DB patch 0670.data.mark-email-and-phone-invalid.sql
8322 -- Add org unit settings and standing penalty types to support
8323 -- the mark email/phone invalid features.
8326 -- check whether patch can be applied
8327 SELECT evergreen.upgrade_deps_block_check('0670', :eg_version);
8330 INSERT INTO config.standing_penalty (id, name, label, staff_alert, org_depth) VALUES
8333 'INVALID_PATRON_EMAIL_ADDRESS',
8336 'Patron had an invalid email address',
8345 'INVALID_PATRON_DAY_PHONE',
8348 'Patron had an invalid daytime phone number',
8357 'INVALID_PATRON_EVENING_PHONE',
8360 'Patron had an invalid evening phone number',
8369 'INVALID_PATRON_OTHER_PHONE',
8372 'Patron had an invalid other phone number',
8382 SELECT evergreen.upgrade_deps_block_check('0671', :eg_version);
8384 ALTER TABLE asset.copy_location
8385 ADD COLUMN checkin_alert BOOL NOT NULL DEFAULT FALSE;
8387 -- Evergreen DB patch 0673.data.acq-cancel-reason-cleanup.sql
8390 -- check whether patch can be applied
8391 SELECT evergreen.upgrade_deps_block_check('0673', :eg_version);
8396 -- any entries with id >= 2000 were added locally.
8399 -- these cancel_reason's are actively used by the system
8400 AND id NOT IN (1, 2, 3, 1002, 1003, 1004, 1005, 1010, 1024, 1211, 1221, 1246, 1283)
8402 -- don't delete any cancel_reason's that may be in use locally
8403 AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.user_request WHERE cancel_reason IS NOT NULL)
8404 AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.purchase_order WHERE cancel_reason IS NOT NULL)
8405 AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.lineitem WHERE cancel_reason IS NOT NULL)
8406 AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.lineitem_detail WHERE cancel_reason IS NOT NULL)
8407 AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.acq_lineitem_history WHERE cancel_reason IS NOT NULL)
8408 AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.acq_purchase_order_history WHERE cancel_reason IS NOT NULL);
8411 SELECT evergreen.upgrade_deps_block_check('0674', :eg_version);
8413 ALTER TABLE config.copy_status
8414 ADD COLUMN restrict_copy_delete BOOL NOT NULL DEFAULT FALSE;
8416 UPDATE config.copy_status
8417 SET restrict_copy_delete = TRUE
8418 WHERE id IN (1,3,6,8);
8420 INSERT INTO permission.perm_list (id, code, description) VALUES (
8422 'COPY_DELETE_WARNING.override',
8423 'Allow a user to override warnings about deleting copies in problematic situations.'
8427 SELECT evergreen.upgrade_deps_block_check('0675', :eg_version);
8429 -- set expected row count to low value to avoid problem
8430 -- where use of this function by the circ tagging feature
8431 -- results in full scans of asset.call_number
8432 CREATE OR REPLACE FUNCTION action.usr_visible_circ_copies( INTEGER ) RETURNS SETOF BIGINT AS $$
8433 SELECT DISTINCT(target_copy) FROM action.usr_visible_circs($1)
8434 $$ LANGUAGE SQL ROWS 10;
8437 SELECT evergreen.upgrade_deps_block_check('0676', :eg_version);
8439 INSERT INTO config.global_flag (name, label, enabled, value) VALUES (
8440 'opac.use_autosuggest',
8441 'OPAC: Show auto-completing suggestions dialog under basic search box (put ''opac_visible'' into the value field to limit suggestions to OPAC-visible items, or blank the field for a possible performance improvement)',
8446 CREATE TABLE metabib.browse_entry (
8447 id BIGSERIAL PRIMARY KEY,
8449 index_vector tsvector
8451 --Skip this, will be created differently later
8452 --CREATE INDEX metabib_browse_entry_index_vector_idx ON metabib.browse_entry USING GIST (index_vector);
8453 CREATE TRIGGER metabib_browse_entry_fti_trigger
8454 BEFORE INSERT OR UPDATE ON metabib.browse_entry
8455 FOR EACH ROW EXECUTE PROCEDURE oils_tsearch2('keyword');
8458 CREATE TABLE metabib.browse_entry_def_map (
8459 id BIGSERIAL PRIMARY KEY,
8460 entry BIGINT REFERENCES metabib.browse_entry (id),
8461 def INT REFERENCES config.metabib_field (id),
8462 source BIGINT REFERENCES biblio.record_entry (id)
8465 ALTER TABLE config.metabib_field ADD COLUMN browse_field BOOLEAN DEFAULT TRUE NOT NULL;
8466 ALTER TABLE config.metabib_field ADD COLUMN browse_xpath TEXT;
8468 ALTER TABLE config.metabib_class ADD COLUMN bouyant BOOLEAN DEFAULT FALSE NOT NULL;
8469 ALTER TABLE config.metabib_class ADD COLUMN restrict BOOLEAN DEFAULT FALSE NOT NULL;
8470 ALTER TABLE config.metabib_field ADD COLUMN restrict BOOLEAN DEFAULT FALSE NOT NULL;
8472 -- one good exception to default true:
8473 UPDATE config.metabib_field
8474 SET browse_field = FALSE
8475 WHERE (field_class = 'keyword' AND name = 'keyword') OR
8476 (field_class = 'subject' AND name = 'complete');
8478 -- AFTER UPDATE OR INSERT trigger for biblio.record_entry
8479 -- We're only touching it here to add a DELETE statement to the IF NEW.deleted
8482 CREATE OR REPLACE FUNCTION biblio.indexing_ingest_or_delete () RETURNS TRIGGER AS $func$
8484 transformed_xml TEXT;
8487 xfrm config.xml_transform%ROWTYPE;
8489 new_attrs HSTORE := ''::HSTORE;
8490 attr_def config.record_attr_definition%ROWTYPE;
8493 IF NEW.deleted IS TRUE THEN -- If this bib is deleted
8494 DELETE FROM metabib.metarecord_source_map WHERE source = NEW.id; -- Rid ourselves of the search-estimate-killing linkage
8495 DELETE FROM metabib.record_attr WHERE id = NEW.id; -- Kill the attrs hash, useless on deleted records
8496 DELETE FROM authority.bib_linking WHERE bib = NEW.id; -- Avoid updating fields in bibs that are no longer visible
8497 DELETE FROM biblio.peer_bib_copy_map WHERE peer_record = NEW.id; -- Separate any multi-homed items
8498 DELETE FROM metabib.browse_entry_def_map WHERE source = NEW.id; -- Don't auto-suggest deleted bibs
8499 RETURN NEW; -- and we're done
8502 IF TG_OP = 'UPDATE' THEN -- re-ingest?
8503 PERFORM * FROM config.internal_flag WHERE name = 'ingest.reingest.force_on_same_marc' AND enabled;
8505 IF NOT FOUND AND OLD.marc = NEW.marc THEN -- don't do anything if the MARC didn't change
8510 -- Record authority linking
8511 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_linking' AND enabled;
8513 PERFORM biblio.map_authority_linking( NEW.id, NEW.marc );
8516 -- Flatten and insert the mfr data
8517 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_metabib_full_rec' AND enabled;
8519 PERFORM metabib.reingest_metabib_full_rec(NEW.id);
8521 -- Now we pull out attribute data, which is dependent on the mfr for all but XPath-based fields
8522 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_metabib_rec_descriptor' AND enabled;
8524 FOR attr_def IN SELECT * FROM config.record_attr_definition ORDER BY format LOOP
8526 IF attr_def.tag IS NOT NULL THEN -- tag (and optional subfield list) selection
8527 SELECT ARRAY_TO_STRING(ARRAY_ACCUM(value), COALESCE(attr_def.joiner,' ')) INTO attr_value
8528 FROM (SELECT * FROM metabib.full_rec ORDER BY tag, subfield) AS x
8529 WHERE record = NEW.id
8530 AND tag LIKE attr_def.tag
8532 WHEN attr_def.sf_list IS NOT NULL
8533 THEN POSITION(subfield IN attr_def.sf_list) > 0
8540 ELSIF attr_def.fixed_field IS NOT NULL THEN -- a named fixed field, see config.marc21_ff_pos_map.fixed_field
8541 attr_value := biblio.marc21_extract_fixed_field(NEW.id, attr_def.fixed_field);
8543 ELSIF attr_def.xpath IS NOT NULL THEN -- and xpath expression
8545 SELECT INTO xfrm * FROM config.xml_transform WHERE name = attr_def.format;
8547 -- See if we can skip the XSLT ... it's expensive
8548 IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
8549 -- Can't skip the transform
8550 IF xfrm.xslt <> '---' THEN
8551 transformed_xml := oils_xslt_process(NEW.marc,xfrm.xslt);
8553 transformed_xml := NEW.marc;
8556 prev_xfrm := xfrm.name;
8559 IF xfrm.name IS NULL THEN
8560 -- just grab the marcxml (empty) transform
8561 SELECT INTO xfrm * FROM config.xml_transform WHERE xslt = '---' LIMIT 1;
8562 prev_xfrm := xfrm.name;
8565 attr_value := oils_xpath_string(attr_def.xpath, transformed_xml, COALESCE(attr_def.joiner,' '), ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]);
8567 ELSIF attr_def.phys_char_sf IS NOT NULL THEN -- a named Physical Characteristic, see config.marc21_physical_characteristic_*_map
8568 SELECT m.value INTO attr_value
8569 FROM biblio.marc21_physical_characteristics(NEW.id) v
8570 JOIN config.marc21_physical_characteristic_value_map m ON (m.id = v.value)
8571 WHERE v.subfield = attr_def.phys_char_sf
8572 LIMIT 1; -- Just in case ...
8576 -- apply index normalizers to attr_value
8578 SELECT n.func AS func,
8579 n.param_count AS param_count,
8581 FROM config.index_normalizer n
8582 JOIN config.record_attr_index_norm_map m ON (m.norm = n.id)
8583 WHERE attr = attr_def.name
8585 EXECUTE 'SELECT ' || normalizer.func || '(' ||
8586 COALESCE( quote_literal( attr_value ), 'NULL' ) ||
8588 WHEN normalizer.param_count > 0
8589 THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
8592 ')' INTO attr_value;
8596 -- Add the new value to the hstore
8597 new_attrs := new_attrs || hstore( attr_def.name, attr_value );
8601 IF TG_OP = 'INSERT' OR OLD.deleted THEN -- initial insert OR revivication
8602 INSERT INTO metabib.record_attr (id, attrs) VALUES (NEW.id, new_attrs);
8604 UPDATE metabib.record_attr SET attrs = new_attrs WHERE id = NEW.id;
8610 -- Gather and insert the field entry data
8611 PERFORM metabib.reingest_metabib_field_entries(NEW.id);
8613 -- Located URI magic
8614 IF TG_OP = 'INSERT' THEN
8615 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_located_uri' AND enabled;
8617 PERFORM biblio.extract_located_uris( NEW.id, NEW.marc, NEW.editor );
8620 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_located_uri' AND enabled;
8622 PERFORM biblio.extract_located_uris( NEW.id, NEW.marc, NEW.editor );
8626 -- (re)map metarecord-bib linking
8627 IF TG_OP = 'INSERT' THEN -- if not deleted and performing an insert, check for the flag
8628 PERFORM * FROM config.internal_flag WHERE name = 'ingest.metarecord_mapping.skip_on_insert' AND enabled;
8630 PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint );
8632 ELSE -- we're doing an update, and we're not deleted, remap
8633 PERFORM * FROM config.internal_flag WHERE name = 'ingest.metarecord_mapping.skip_on_update' AND enabled;
8635 PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint );
8641 $func$ LANGUAGE PLPGSQL;
8643 CREATE OR REPLACE FUNCTION metabib.browse_normalize(facet_text TEXT, mapped_field INT) RETURNS TEXT AS $$
8649 SELECT n.func AS func,
8650 n.param_count AS param_count,
8652 FROM config.index_normalizer n
8653 JOIN config.metabib_field_index_norm_map m ON (m.norm = n.id)
8654 WHERE m.field = mapped_field AND m.pos < 0
8657 EXECUTE 'SELECT ' || normalizer.func || '(' ||
8658 quote_literal( facet_text ) ||
8660 WHEN normalizer.param_count > 0
8661 THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
8664 ')' INTO facet_text;
8671 $$ LANGUAGE PLPGSQL;
8673 DROP FUNCTION biblio.extract_metabib_field_entry(bigint, text);
8674 DROP FUNCTION biblio.extract_metabib_field_entry(bigint);
8676 DROP TYPE metabib.field_entry_template;
8677 CREATE TYPE metabib.field_entry_template AS (
8688 CREATE OR REPLACE FUNCTION biblio.extract_metabib_field_entry ( rid BIGINT, default_joiner TEXT ) RETURNS SETOF metabib.field_entry_template AS $func$
8690 bib biblio.record_entry%ROWTYPE;
8691 idx config.metabib_field%ROWTYPE;
8692 xfrm config.xml_transform%ROWTYPE;
8694 transformed_xml TEXT;
8696 xml_node_list TEXT[];
8701 joiner TEXT := default_joiner; -- XXX will index defs supply a joiner?
8702 output_row metabib.field_entry_template%ROWTYPE;
8706 SELECT INTO bib * FROM biblio.record_entry WHERE id = rid;
8708 -- Loop over the indexing entries
8709 FOR idx IN SELECT * FROM config.metabib_field ORDER BY format LOOP
8711 SELECT INTO xfrm * from config.xml_transform WHERE name = idx.format;
8713 -- See if we can skip the XSLT ... it's expensive
8714 IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
8715 -- Can't skip the transform
8716 IF xfrm.xslt <> '---' THEN
8717 transformed_xml := oils_xslt_process(bib.marc,xfrm.xslt);
8719 transformed_xml := bib.marc;
8722 prev_xfrm := xfrm.name;
8725 xml_node_list := oils_xpath( idx.xpath, transformed_xml, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
8728 FOR xml_node IN SELECT x FROM unnest(xml_node_list) AS x LOOP
8729 CONTINUE WHEN xml_node !~ E'^\\s*<';
8731 curr_text := ARRAY_TO_STRING(
8732 oils_xpath( '//text()',
8733 REGEXP_REPLACE( -- This escapes all &s not followed by "amp;". Data ise returned from oils_xpath (above) in UTF-8, not entity encoded
8734 REGEXP_REPLACE( -- This escapes embeded <s
8736 $re$(>[^<]+)(<)([^>]+<)$re$,
8748 CONTINUE WHEN curr_text IS NULL OR curr_text = '';
8750 IF raw_text IS NOT NULL THEN
8751 raw_text := raw_text || joiner;
8754 raw_text := COALESCE(raw_text,'') || curr_text;
8756 -- autosuggest/metabib.browse_entry
8757 IF idx.browse_field THEN
8759 IF idx.browse_xpath IS NOT NULL AND idx.browse_xpath <> '' THEN
8760 browse_text := oils_xpath_string( idx.browse_xpath, xml_node, joiner, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
8762 browse_text := curr_text;
8765 output_row.field_class = idx.field_class;
8766 output_row.field = idx.id;
8767 output_row.source = rid;
8768 output_row.value = BTRIM(REGEXP_REPLACE(browse_text, E'\\s+', ' ', 'g'));
8770 output_row.browse_field = TRUE;
8771 RETURN NEXT output_row;
8772 output_row.browse_field = FALSE;
8775 -- insert raw node text for faceting
8776 IF idx.facet_field THEN
8778 IF idx.facet_xpath IS NOT NULL AND idx.facet_xpath <> '' THEN
8779 facet_text := oils_xpath_string( idx.facet_xpath, xml_node, joiner, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
8781 facet_text := curr_text;
8784 output_row.field_class = idx.field_class;
8785 output_row.field = -1 * idx.id;
8786 output_row.source = rid;
8787 output_row.value = BTRIM(REGEXP_REPLACE(facet_text, E'\\s+', ' ', 'g'));
8789 output_row.facet_field = TRUE;
8790 RETURN NEXT output_row;
8791 output_row.facet_field = FALSE;
8796 CONTINUE WHEN raw_text IS NULL OR raw_text = '';
8798 -- insert combined node text for searching
8799 IF idx.search_field THEN
8800 output_row.field_class = idx.field_class;
8801 output_row.field = idx.id;
8802 output_row.source = rid;
8803 output_row.value = BTRIM(REGEXP_REPLACE(raw_text, E'\\s+', ' ', 'g'));
8805 output_row.search_field = TRUE;
8806 RETURN NEXT output_row;
8812 $func$ LANGUAGE PLPGSQL;
8814 -- default to a space joiner
8815 CREATE OR REPLACE FUNCTION biblio.extract_metabib_field_entry ( BIGINT ) RETURNS SETOF metabib.field_entry_template AS $func$
8816 SELECT * FROM biblio.extract_metabib_field_entry($1, ' ');
8817 $func$ LANGUAGE SQL;
8820 CREATE OR REPLACE FUNCTION metabib.reingest_metabib_field_entries( bib_id BIGINT ) RETURNS VOID AS $func$
8823 ind_data metabib.field_entry_template%ROWTYPE;
8824 mbe_row metabib.browse_entry%ROWTYPE;
8827 PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled;
8829 FOR fclass IN SELECT * FROM config.metabib_class LOOP
8830 -- RAISE NOTICE 'Emptying out %', fclass.name;
8831 EXECUTE $$DELETE FROM metabib.$$ || fclass.name || $$_field_entry WHERE source = $$ || bib_id;
8833 DELETE FROM metabib.facet_entry WHERE source = bib_id;
8834 DELETE FROM metabib.browse_entry_def_map WHERE source = bib_id;
8837 FOR ind_data IN SELECT * FROM biblio.extract_metabib_field_entry( bib_id ) LOOP
8838 IF ind_data.field < 0 THEN
8839 ind_data.field = -1 * ind_data.field;
8842 IF ind_data.facet_field THEN
8843 INSERT INTO metabib.facet_entry (field, source, value)
8844 VALUES (ind_data.field, ind_data.source, ind_data.value);
8847 IF ind_data.browse_field THEN
8848 SELECT INTO mbe_row * FROM metabib.browse_entry WHERE value = ind_data.value;
8850 mbe_id := mbe_row.id;
8852 INSERT INTO metabib.browse_entry (value) VALUES
8853 (metabib.browse_normalize(ind_data.value, ind_data.field));
8854 mbe_id := CURRVAL('metabib.browse_entry_id_seq'::REGCLASS);
8857 INSERT INTO metabib.browse_entry_def_map (entry, def, source)
8858 VALUES (mbe_id, ind_data.field, ind_data.source);
8861 IF ind_data.search_field THEN
8863 INSERT INTO metabib.$$ || ind_data.field_class || $$_field_entry (field, source, value)
8865 quote_literal(ind_data.field) || $$, $$ ||
8866 quote_literal(ind_data.source) || $$, $$ ||
8867 quote_literal(ind_data.value) ||
8875 $func$ LANGUAGE PLPGSQL;
8877 -- This mimics a specific part of QueryParser, turning the first part of a
8878 -- classed search (search_class) into a set of classes and possibly fields.
8879 -- search_class might look like "author" or "title|proper" or "ti|uniform"
8880 -- or "au" or "au|corporate|personal" or anything like that, where the first
8881 -- element of the list you get by separating on the "|" character is either
8882 -- a registered class (config.metabib_class) or an alias
8883 -- (config.metabib_search_alias), and the rest of any such elements are
8884 -- fields (config.metabib_field).
8886 FUNCTION metabib.search_class_to_registered_components(search_class TEXT)
8887 RETURNS SETOF RECORD AS $func$
8889 search_parts TEXT[];
8891 search_part_count INTEGER;
8893 registered_class config.metabib_class%ROWTYPE;
8894 registered_alias config.metabib_search_alias%ROWTYPE;
8895 registered_field config.metabib_field%ROWTYPE;
8897 search_parts := REGEXP_SPLIT_TO_ARRAY(search_class, E'\\|');
8899 search_part_count := ARRAY_LENGTH(search_parts, 1);
8900 IF search_part_count = 0 THEN
8903 SELECT INTO registered_class
8904 * FROM config.metabib_class WHERE name = search_parts[1];
8906 IF search_part_count < 2 THEN -- all fields
8907 rec := (registered_class.name, NULL::INTEGER);
8911 FOR field_name IN SELECT *
8912 FROM UNNEST(search_parts[2:search_part_count]) LOOP
8913 SELECT INTO registered_field
8914 * FROM config.metabib_field
8915 WHERE name = field_name AND
8916 field_class = registered_class.name;
8918 rec := (registered_class.name, registered_field.id);
8923 -- maybe we have an alias?
8924 SELECT INTO registered_alias
8925 * FROM config.metabib_search_alias WHERE alias=search_parts[1];
8929 IF search_part_count < 2 THEN -- return w/e the alias says
8931 registered_alias.field_class, registered_alias.field
8936 FOR field_name IN SELECT *
8937 FROM UNNEST(search_parts[2:search_part_count]) LOOP
8938 SELECT INTO registered_field
8939 * FROM config.metabib_field
8940 WHERE name = field_name AND
8941 field_class = registered_alias.field_class;
8944 registered_alias.field_class,
8955 $func$ LANGUAGE PLPGSQL;
8959 FUNCTION metabib.suggest_browse_entries(
8960 query_text TEXT, -- 'foo' or 'foo & ba:*',ready for to_tsquery()
8961 search_class TEXT, -- 'alias' or 'class' or 'class|field..', etc
8962 headline_opts TEXT, -- markup options for ts_headline()
8963 visibility_org INTEGER,-- null if you don't want opac visibility test
8964 query_limit INTEGER,-- use in LIMIT clause of interal query
8965 normalization INTEGER -- argument to TS_RANK_CD()
8967 value TEXT, -- plain
8969 bouyant_and_class_match BOOL,
8971 field_weight INTEGER,
8974 match TEXT -- marked up
8978 opac_visibility_join TEXT;
8979 search_class_join TEXT;
8982 query := TO_TSQUERY('keyword', query_text);
8984 IF visibility_org IS NOT NULL THEN
8985 opac_visibility_join := '
8986 JOIN asset.opac_visible_copies aovc ON (
8987 aovc.record = mbedm.source AND
8988 aovc.circ_lib IN (SELECT id FROM actor.org_unit_descendants($4))
8991 opac_visibility_join := '';
8994 -- The following determines whether we only provide suggestsons matching
8995 -- the user's selected search_class, or whether we show other suggestions
8996 -- too. The reason for MIN() is that for search_classes like
8997 -- 'title|proper|uniform' you would otherwise get multiple rows. The
8998 -- implication is that if title as a class doesn't have restrict,
8999 -- nor does the proper field, but the uniform field does, you're going
9000 -- to get 'false' for your overall evaluation of 'should we restrict?'
9001 -- To invert that, change from MIN() to MAX().
9005 MIN(cmc.restrict::INT) AS restrict_class,
9006 MIN(cmf.restrict::INT) AS restrict_field
9007 FROM metabib.search_class_to_registered_components(search_class)
9008 AS _registered (field_class TEXT, field INT)
9010 config.metabib_class cmc ON (cmc.name = _registered.field_class)
9012 config.metabib_field cmf ON (cmf.id = _registered.field);
9014 -- evaluate 'should we restrict?'
9015 IF r_fields.restrict_field::BOOL OR r_fields.restrict_class::BOOL THEN
9016 search_class_join := '
9018 metabib.search_class_to_registered_components($2)
9019 AS _registered (field_class TEXT, field INT) ON (
9020 (_registered.field IS NULL AND
9021 _registered.field_class = cmf.field_class) OR
9022 (_registered.field = cmf.id)
9026 search_class_join := '
9028 metabib.search_class_to_registered_components($2)
9029 AS _registered (field_class TEXT, field INT) ON (
9030 _registered.field_class = cmc.name
9035 RETURN QUERY EXECUTE 'SELECT *, TS_HEADLINE(value, $1, $3) FROM (SELECT DISTINCT
9038 cmc.bouyant AND _registered.field_class IS NOT NULL,
9039 _registered.field = cmf.id,
9041 TS_RANK_CD(mbe.index_vector, $1, $6),
9043 FROM metabib.browse_entry_def_map mbedm
9044 JOIN metabib.browse_entry mbe ON (mbe.id = mbedm.entry)
9045 JOIN config.metabib_field cmf ON (cmf.id = mbedm.def)
9046 JOIN config.metabib_class cmc ON (cmf.field_class = cmc.name)
9047 ' || search_class_join || opac_visibility_join ||
9048 ' WHERE $1 @@ mbe.index_vector
9049 ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
9051 ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
9052 ' -- sic, repeat the order by clause in the outer select too
9054 query, search_class, headline_opts,
9055 visibility_org, query_limit, normalization
9059 -- bouyant AND chosen class = match class
9060 -- chosen field = match field
9067 $func$ LANGUAGE PLPGSQL;
9069 -- The advantage of this over the stock regexp_split_to_array() is that it
9070 -- won't degrade unicode strings.
9071 CREATE OR REPLACE FUNCTION evergreen.regexp_split_to_array(TEXT, TEXT)
9072 RETURNS TEXT[] AS $$
9073 return encode_array_literal([split $_[1], $_[0]]);
9074 $$ LANGUAGE PLPERLU STRICT IMMUTABLE;
9077 -- Adds some logic for browse_entry to split on non-word chars for index_vector, post-normalize
9078 CREATE OR REPLACE FUNCTION oils_tsearch2 () RETURNS TRIGGER AS $$
9086 IF TG_TABLE_NAME::TEXT ~ 'field_entry$' THEN
9088 SELECT n.func AS func,
9089 n.param_count AS param_count,
9091 FROM config.index_normalizer n
9092 JOIN config.metabib_field_index_norm_map m ON (m.norm = n.id)
9093 WHERE field = NEW.field AND m.pos < 0
9095 EXECUTE 'SELECT ' || normalizer.func || '(' ||
9096 quote_literal( value ) ||
9098 WHEN normalizer.param_count > 0
9099 THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
9109 IF NEW.index_vector = ''::tsvector THEN
9113 IF TG_TABLE_NAME::TEXT ~ 'field_entry$' THEN
9115 SELECT n.func AS func,
9116 n.param_count AS param_count,
9118 FROM config.index_normalizer n
9119 JOIN config.metabib_field_index_norm_map m ON (m.norm = n.id)
9120 WHERE field = NEW.field AND m.pos >= 0
9122 EXECUTE 'SELECT ' || normalizer.func || '(' ||
9123 quote_literal( value ) ||
9125 WHEN normalizer.param_count > 0
9126 THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
9134 IF TG_TABLE_NAME::TEXT ~ 'browse_entry$' THEN
9135 value := ARRAY_TO_STRING(
9136 evergreen.regexp_split_to_array(value, E'\\W+'), ' '
9140 NEW.index_vector = to_tsvector((TG_ARGV[0])::regconfig, value);
9144 $$ LANGUAGE PLPGSQL;
9146 -- Evergreen DB patch 0677.schema.circ_limits.sql
9148 -- FIXME: insert description of change, if needed
9152 -- check whether patch can be applied
9153 SELECT evergreen.upgrade_deps_block_check('0677', :eg_version);
9155 -- FIXME: add/check SQL statements to perform the upgrade
9156 -- Limit groups for circ counting
9157 CREATE TABLE config.circ_limit_group (
9158 id SERIAL PRIMARY KEY,
9159 name TEXT UNIQUE NOT NULL,
9164 CREATE TABLE config.circ_limit_set (
9165 id SERIAL PRIMARY KEY,
9166 name TEXT UNIQUE NOT NULL,
9167 owning_lib INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
9168 items_out INT NOT NULL, -- Total current active circulations must be less than this. 0 means skip counting (always pass)
9169 depth INT NOT NULL DEFAULT 0, -- Depth count starts at
9170 global BOOL NOT NULL DEFAULT FALSE, -- If enabled, include everything below depth, otherwise ancestors/descendants only
9174 -- Linkage between matchpoints and limit sets
9175 CREATE TABLE config.circ_matrix_limit_set_map (
9176 id SERIAL PRIMARY KEY,
9177 matchpoint INT NOT NULL REFERENCES config.circ_matrix_matchpoint (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
9178 limit_set INT NOT NULL REFERENCES config.circ_limit_set (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
9179 fallthrough BOOL NOT NULL DEFAULT FALSE, -- If true fallthrough will grab this rule as it goes along
9180 active BOOL NOT NULL DEFAULT TRUE,
9181 CONSTRAINT circ_limit_set_once_per_matchpoint UNIQUE (matchpoint, limit_set)
9184 -- Linkage between limit sets and circ mods
9185 CREATE TABLE config.circ_limit_set_circ_mod_map (
9186 id SERIAL PRIMARY KEY,
9187 limit_set INT NOT NULL REFERENCES config.circ_limit_set (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
9188 circ_mod TEXT NOT NULL REFERENCES config.circ_modifier (code) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
9189 CONSTRAINT cm_once_per_set UNIQUE (limit_set, circ_mod)
9192 -- Linkage between limit sets and limit groups
9193 CREATE TABLE config.circ_limit_set_group_map (
9194 id SERIAL PRIMARY KEY,
9195 limit_set INT NOT NULL REFERENCES config.circ_limit_set (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
9196 limit_group INT NOT NULL REFERENCES config.circ_limit_group (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
9197 check_only BOOL NOT NULL DEFAULT FALSE, -- If true, don't accumulate this limit_group for storing with the circulation
9198 CONSTRAINT clg_once_per_set UNIQUE (limit_set, limit_group)
9201 -- Linkage between limit groups and circulations
9202 CREATE TABLE action.circulation_limit_group_map (
9203 circ BIGINT NOT NULL REFERENCES action.circulation (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
9204 limit_group INT NOT NULL REFERENCES config.circ_limit_group (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
9205 PRIMARY KEY (circ, limit_group)
9208 -- Function for populating the circ/limit group mappings
9209 CREATE OR REPLACE FUNCTION action.link_circ_limit_groups ( BIGINT, INT[] ) RETURNS VOID AS $func$
9210 INSERT INTO action.circulation_limit_group_map(circ, limit_group) SELECT $1, id FROM config.circ_limit_group WHERE id IN (SELECT * FROM UNNEST($2));
9211 $func$ LANGUAGE SQL;
9213 DROP TYPE IF EXISTS action.circ_matrix_test_result CASCADE;
9214 CREATE TYPE action.circ_matrix_test_result AS ( success BOOL, fail_part TEXT, buildrows INT[], matchpoint INT, circulate BOOL, duration_rule INT, recurring_fine_rule INT, max_fine_rule INT, hard_due_date INT, renewals INT, grace_period INTERVAL, limit_groups INT[] );
9216 CREATE OR REPLACE FUNCTION action.item_user_circ_test( circ_ou INT, match_item BIGINT, match_user INT, renewal BOOL ) RETURNS SETOF action.circ_matrix_test_result AS $func$
9218 user_object actor.usr%ROWTYPE;
9219 standing_penalty config.standing_penalty%ROWTYPE;
9220 item_object asset.copy%ROWTYPE;
9221 item_status_object config.copy_status%ROWTYPE;
9222 item_location_object asset.copy_location%ROWTYPE;
9223 result action.circ_matrix_test_result;
9224 circ_test action.found_circ_matrix_matchpoint;
9225 circ_matchpoint config.circ_matrix_matchpoint%ROWTYPE;
9226 circ_limit_set config.circ_limit_set%ROWTYPE;
9227 hold_ratio action.hold_stats%ROWTYPE;
9230 context_org_list INT[];
9233 -- Assume success unless we hit a failure condition
9234 result.success := TRUE;
9236 -- Need user info to look up matchpoints
9237 SELECT INTO user_object * FROM actor.usr WHERE id = match_user AND NOT deleted;
9239 -- (Insta)Fail if we couldn't find the user
9240 IF user_object.id IS NULL THEN
9241 result.fail_part := 'no_user';
9242 result.success := FALSE;
9248 -- Need item info to look up matchpoints
9249 SELECT INTO item_object * FROM asset.copy WHERE id = match_item AND NOT deleted;
9251 -- (Insta)Fail if we couldn't find the item
9252 IF item_object.id IS NULL THEN
9253 result.fail_part := 'no_item';
9254 result.success := FALSE;
9260 SELECT INTO circ_test * FROM action.find_circ_matrix_matchpoint(circ_ou, item_object, user_object, renewal);
9262 circ_matchpoint := circ_test.matchpoint;
9263 result.matchpoint := circ_matchpoint.id;
9264 result.circulate := circ_matchpoint.circulate;
9265 result.duration_rule := circ_matchpoint.duration_rule;
9266 result.recurring_fine_rule := circ_matchpoint.recurring_fine_rule;
9267 result.max_fine_rule := circ_matchpoint.max_fine_rule;
9268 result.hard_due_date := circ_matchpoint.hard_due_date;
9269 result.renewals := circ_matchpoint.renewals;
9270 result.grace_period := circ_matchpoint.grace_period;
9271 result.buildrows := circ_test.buildrows;
9273 -- (Insta)Fail if we couldn't find a matchpoint
9274 IF circ_test.success = false THEN
9275 result.fail_part := 'no_matchpoint';
9276 result.success := FALSE;
9282 -- All failures before this point are non-recoverable
9283 -- Below this point are possibly overridable failures
9285 -- Fail if the user is barred
9286 IF user_object.barred IS TRUE THEN
9287 result.fail_part := 'actor.usr.barred';
9288 result.success := FALSE;
9293 -- Fail if the item can't circulate
9294 IF item_object.circulate IS FALSE THEN
9295 result.fail_part := 'asset.copy.circulate';
9296 result.success := FALSE;
9301 -- Fail if the item isn't in a circulateable status on a non-renewal
9302 IF NOT renewal AND item_object.status NOT IN ( 0, 7, 8 ) THEN
9303 result.fail_part := 'asset.copy.status';
9304 result.success := FALSE;
9307 -- Alternately, fail if the item isn't checked out on a renewal
9308 ELSIF renewal AND item_object.status <> 1 THEN
9309 result.fail_part := 'asset.copy.status';
9310 result.success := FALSE;
9315 -- Fail if the item can't circulate because of the shelving location
9316 SELECT INTO item_location_object * FROM asset.copy_location WHERE id = item_object.location;
9317 IF item_location_object.circulate IS FALSE THEN
9318 result.fail_part := 'asset.copy_location.circulate';
9319 result.success := FALSE;
9324 -- Use Circ OU for penalties and such
9325 SELECT INTO context_org_list ARRAY_AGG(id) FROM actor.org_unit_full_path( circ_ou );
9328 penalty_type = '%RENEW%';
9330 penalty_type = '%CIRC%';
9333 FOR standing_penalty IN
9334 SELECT DISTINCT csp.*
9335 FROM actor.usr_standing_penalty usp
9336 JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
9337 WHERE usr = match_user
9338 AND usp.org_unit IN ( SELECT * FROM unnest(context_org_list) )
9339 AND (usp.stop_date IS NULL or usp.stop_date > NOW())
9340 AND csp.block_list LIKE penalty_type LOOP
9342 result.fail_part := standing_penalty.name;
9343 result.success := FALSE;
9348 -- Fail if the test is set to hard non-circulating
9349 IF circ_matchpoint.circulate IS FALSE THEN
9350 result.fail_part := 'config.circ_matrix_test.circulate';
9351 result.success := FALSE;
9356 -- Fail if the total copy-hold ratio is too low
9357 IF circ_matchpoint.total_copy_hold_ratio IS NOT NULL THEN
9358 SELECT INTO hold_ratio * FROM action.copy_related_hold_stats(match_item);
9359 IF hold_ratio.total_copy_ratio IS NOT NULL AND hold_ratio.total_copy_ratio < circ_matchpoint.total_copy_hold_ratio THEN
9360 result.fail_part := 'config.circ_matrix_test.total_copy_hold_ratio';
9361 result.success := FALSE;
9367 -- Fail if the available copy-hold ratio is too low
9368 IF circ_matchpoint.available_copy_hold_ratio IS NOT NULL THEN
9369 IF hold_ratio.hold_count IS NULL THEN
9370 SELECT INTO hold_ratio * FROM action.copy_related_hold_stats(match_item);
9372 IF hold_ratio.available_copy_ratio IS NOT NULL AND hold_ratio.available_copy_ratio < circ_matchpoint.available_copy_hold_ratio THEN
9373 result.fail_part := 'config.circ_matrix_test.available_copy_hold_ratio';
9374 result.success := FALSE;
9380 -- Fail if the user has too many items out by defined limit sets
9381 FOR circ_limit_set IN SELECT ccls.* FROM config.circ_limit_set ccls
9382 JOIN config.circ_matrix_limit_set_map ccmlsm ON ccmlsm.limit_set = ccls.id
9383 WHERE ccmlsm.active AND ( ccmlsm.matchpoint = circ_matchpoint.id OR
9384 ( ccmlsm.matchpoint IN (SELECT * FROM unnest(result.buildrows)) AND ccmlsm.fallthrough )
9386 IF circ_limit_set.items_out > 0 AND NOT renewal THEN
9387 SELECT INTO context_org_list ARRAY_AGG(aou.id)
9388 FROM actor.org_unit_full_path( circ_ou ) aou
9389 JOIN actor.org_unit_type aout ON aou.ou_type = aout.id
9390 WHERE aout.depth >= circ_limit_set.depth;
9391 IF circ_limit_set.global THEN
9392 WITH RECURSIVE descendant_depth AS (
9395 FROM actor.org_unit ou
9396 WHERE ou.id IN (SELECT * FROM unnest(context_org_list))
9400 FROM actor.org_unit ou
9401 JOIN descendant_depth ot ON (ot.id = ou.parent_ou)
9402 ) SELECT INTO context_org_list ARRAY_AGG(ou.id) FROM actor.org_unit ou JOIN descendant_depth USING (id);
9404 SELECT INTO items_out COUNT(DISTINCT circ.id)
9405 FROM action.circulation circ
9406 JOIN asset.copy copy ON (copy.id = circ.target_copy)
9407 LEFT JOIN action.circulation_limit_group_map aclgm ON (circ.id = aclgm.circ)
9408 WHERE circ.usr = match_user
9409 AND circ.circ_lib IN (SELECT * FROM unnest(context_org_list))
9410 AND circ.checkin_time IS NULL
9411 AND (circ.stop_fines IN ('MAXFINES','LONGOVERDUE') OR circ.stop_fines IS NULL)
9412 AND (copy.circ_modifier IN (SELECT circ_mod FROM config.circ_limit_set_circ_mod_map WHERE limit_set = circ_limit_set.id)
9413 OR aclgm.limit_group IN (SELECT limit_group FROM config.circ_limit_set_group_map WHERE limit_set = circ_limit_set.id)
9415 IF items_out >= circ_limit_set.items_out THEN
9416 result.fail_part := 'config.circ_matrix_circ_mod_test';
9417 result.success := FALSE;
9422 SELECT INTO result.limit_groups result.limit_groups || ARRAY_AGG(limit_group) FROM config.circ_limit_set_group_map WHERE limit_set = circ_limit_set.id AND NOT check_only;
9425 -- If we passed everything, return the successful matchpoint
9432 $func$ LANGUAGE plpgsql;
9434 -- We need to re-create these, as they got dropped with the type above.
9435 CREATE OR REPLACE FUNCTION action.item_user_circ_test( INT, BIGINT, INT ) RETURNS SETOF action.circ_matrix_test_result AS $func$
9436 SELECT * FROM action.item_user_circ_test( $1, $2, $3, FALSE );
9437 $func$ LANGUAGE SQL;
9439 CREATE OR REPLACE FUNCTION action.item_user_renew_test( INT, BIGINT, INT ) RETURNS SETOF action.circ_matrix_test_result AS $func$
9440 SELECT * FROM action.item_user_circ_test( $1, $2, $3, TRUE );
9441 $func$ LANGUAGE SQL;
9443 -- Temp function for migrating circ mod limits.
9444 CREATE OR REPLACE FUNCTION evergreen.temp_migrate_circ_mod_limits() RETURNS VOID AS $func$
9446 circ_mod_group config.circ_matrix_circ_mod_test%ROWTYPE;
9450 FOR circ_mod_group IN SELECT * FROM config.circ_matrix_circ_mod_test LOOP
9451 INSERT INTO config.circ_limit_set(name, owning_lib, items_out, depth, global, description)
9452 SELECT org_unit || ' : Matchpoint ' || circ_mod_group.matchpoint || ' : Circ Mod Test ' || circ_mod_group.id, org_unit, circ_mod_group.items_out, 0, false, 'Migrated from Circ Mod Test System'
9453 FROM config.circ_matrix_matchpoint WHERE id = circ_mod_group.matchpoint
9454 RETURNING id INTO current_set;
9455 INSERT INTO config.circ_matrix_limit_set_map(matchpoint, limit_set, fallthrough, active) VALUES (circ_mod_group.matchpoint, current_set, false, true);
9456 INSERT INTO config.circ_limit_set_circ_mod_map(limit_set, circ_mod)
9457 SELECT current_set, circ_mod FROM config.circ_matrix_circ_mod_test_map WHERE circ_mod_test = circ_mod_group.id;
9458 SELECT INTO circ_mod_count count(id) FROM config.circ_limit_set_circ_mod_map WHERE limit_set = current_set;
9459 RAISE NOTICE 'Created limit set with id % and % circ modifiers attached to matchpoint %', current_set, circ_mod_count, circ_mod_group.matchpoint;
9462 $func$ LANGUAGE plpgsql;
9464 -- Run the temp function
9465 SELECT * FROM evergreen.temp_migrate_circ_mod_limits();
9467 -- Drop the temp function
9468 DROP FUNCTION evergreen.temp_migrate_circ_mod_limits();
9470 --Drop the old tables
9471 --Not sure we want to do this. Keeping them may help "something went wrong" correction.
9472 --DROP TABLE IF EXISTS config.circ_matrix_circ_mod_test_map, config.circ_matrix_circ_mod_test;
9475 -- Evergreen DB patch 0678.data.vandelay-default-merge-profiles.sql
9477 -- check whether patch can be applied
9478 SELECT evergreen.upgrade_deps_block_check('0678', :eg_version);
9480 INSERT INTO vandelay.merge_profile (owner, name, replace_spec)
9481 VALUES (1, 'Match-Only Merge', '901c');
9483 INSERT INTO vandelay.merge_profile (owner, name, preserve_spec)
9484 VALUES (1, 'Full Overlay', '901c');
9486 -- Evergreen DB patch 0681.schema.user-activity.sql
9489 -- check whether patch can be applied
9490 SELECT evergreen.upgrade_deps_block_check('0681', :eg_version);
9494 CREATE TYPE config.usr_activity_group AS ENUM ('authen','authz','circ','hold','search');
9496 CREATE TABLE config.usr_activity_type (
9497 id SERIAL PRIMARY KEY,
9501 label TEXT NOT NULL, -- i18n
9502 egroup config.usr_activity_group NOT NULL,
9503 enabled BOOL NOT NULL DEFAULT TRUE,
9504 transient BOOL NOT NULL DEFAULT FALSE,
9505 CONSTRAINT one_of_wwh CHECK (COALESCE(ewho,ewhat,ehow) IS NOT NULL)
9508 CREATE UNIQUE INDEX unique_wwh ON config.usr_activity_type
9509 (COALESCE(ewho,''), COALESCE (ewhat,''), COALESCE(ehow,''));
9511 CREATE TABLE actor.usr_activity (
9512 id BIGSERIAL PRIMARY KEY,
9513 usr INT REFERENCES actor.usr (id) ON DELETE SET NULL,
9514 etype INT NOT NULL REFERENCES config.usr_activity_type (id),
9515 event_time TIMESTAMPTZ NOT NULL DEFAULT NOW()
9518 -- remove transient activity entries on insert of new entries
9519 CREATE OR REPLACE FUNCTION actor.usr_activity_transient_trg () RETURNS TRIGGER AS $$
9521 DELETE FROM actor.usr_activity act USING config.usr_activity_type atype
9522 WHERE atype.transient AND
9523 NEW.etype = atype.id AND
9524 act.etype = atype.id AND
9528 $$ LANGUAGE PLPGSQL;
9530 CREATE TRIGGER remove_transient_usr_activity
9531 BEFORE INSERT ON actor.usr_activity
9532 FOR EACH ROW EXECUTE PROCEDURE actor.usr_activity_transient_trg();
9534 -- given a set of activity criteria, find the most approprate activity type
9535 CREATE OR REPLACE FUNCTION actor.usr_activity_get_type (
9539 ) RETURNS SETOF config.usr_activity_type AS $$
9540 SELECT * FROM config.usr_activity_type
9543 (ewho IS NULL OR ewho = $1) AND
9544 (ewhat IS NULL OR ewhat = $2) AND
9545 (ehow IS NULL OR ehow = $3)
9547 -- BOOL comparisons sort false to true
9548 COALESCE(ewho, '') != COALESCE($1, ''),
9549 COALESCE(ewhat,'') != COALESCE($2, ''),
9550 COALESCE(ehow, '') != COALESCE($3, '')
9554 -- given a set of activity criteria, finds the best
9555 -- activity type and inserts the activity entry
9556 CREATE OR REPLACE FUNCTION actor.insert_usr_activity (
9561 ) RETURNS SETOF actor.usr_activity AS $$
9563 new_row actor.usr_activity%ROWTYPE;
9565 SELECT id INTO new_row.etype FROM actor.usr_activity_get_type(ewho, ewhat, ehow);
9568 INSERT INTO actor.usr_activity (usr, etype)
9569 VALUES (usr, new_row.etype)
9570 RETURNING * INTO new_row;
9571 RETURN NEXT new_row;
9574 $$ LANGUAGE plpgsql;
9578 INSERT INTO config.usr_activity_type (id, ewho, ewhat, ehow, egroup, label) VALUES
9580 -- authen/authz actions
9581 -- note: "opensrf" is the default ingress/ehow
9582 (1, NULL, 'login', 'opensrf', 'authen', oils_i18n_gettext(1 , 'Login via opensrf', 'cuat', 'label'))
9583 ,(2, NULL, 'login', 'srfsh', 'authen', oils_i18n_gettext(2 , 'Login via srfsh', 'cuat', 'label'))
9584 ,(3, NULL, 'login', 'gateway-v1', 'authen', oils_i18n_gettext(3 , 'Login via gateway-v1', 'cuat', 'label'))
9585 ,(4, NULL, 'login', 'translator-v1','authen', oils_i18n_gettext(4 , 'Login via translator-v1', 'cuat', 'label'))
9586 ,(5, NULL, 'login', 'xmlrpc', 'authen', oils_i18n_gettext(5 , 'Login via xmlrpc', 'cuat', 'label'))
9587 ,(6, NULL, 'login', 'remoteauth', 'authen', oils_i18n_gettext(6 , 'Login via remoteauth', 'cuat', 'label'))
9588 ,(7, NULL, 'login', 'sip2', 'authen', oils_i18n_gettext(7 , 'SIP2 Proxy Login', 'cuat', 'label'))
9589 ,(8, NULL, 'login', 'apache', 'authen', oils_i18n_gettext(8 , 'Login via Apache module', 'cuat', 'label'))
9591 ,(9, NULL, 'verify', 'opensrf', 'authz', oils_i18n_gettext(9 , 'Verification via opensrf', 'cuat', 'label'))
9592 ,(10, NULL, 'verify', 'srfsh', 'authz', oils_i18n_gettext(10, 'Verification via srfsh', 'cuat', 'label'))
9593 ,(11, NULL, 'verify', 'gateway-v1', 'authz', oils_i18n_gettext(11, 'Verification via gateway-v1', 'cuat', 'label'))
9594 ,(12, NULL, 'verify', 'translator-v1','authz', oils_i18n_gettext(12, 'Verification via translator-v1', 'cuat', 'label'))
9595 ,(13, NULL, 'verify', 'xmlrpc', 'authz', oils_i18n_gettext(13, 'Verification via xmlrpc', 'cuat', 'label'))
9596 ,(14, NULL, 'verify', 'remoteauth', 'authz', oils_i18n_gettext(14, 'Verification via remoteauth', 'cuat', 'label'))
9597 ,(15, NULL, 'verify', 'sip2', 'authz', oils_i18n_gettext(15, 'SIP2 User Verification', 'cuat', 'label'))
9599 -- authen/authz actions w/ known uses of "who"
9600 ,(16, 'opac', 'login', 'gateway-v1', 'authen', oils_i18n_gettext(16, 'OPAC Login (jspac)', 'cuat', 'label'))
9601 ,(17, 'opac', 'login', 'apache', 'authen', oils_i18n_gettext(17, 'OPAC Login (tpac)', 'cuat', 'label'))
9602 ,(18, 'staffclient', 'login', 'gateway-v1', 'authen', oils_i18n_gettext(18, 'Staff Client Login', 'cuat', 'label'))
9603 ,(19, 'selfcheck', 'login', 'translator-v1','authen', oils_i18n_gettext(19, 'Self-Check Proxy Login', 'cuat', 'label'))
9604 ,(20, 'ums', 'login', 'xmlrpc', 'authen', oils_i18n_gettext(20, 'Unique Mgt Login', 'cuat', 'label'))
9605 ,(21, 'authproxy', 'login', 'apache', 'authen', oils_i18n_gettext(21, 'Apache Auth Proxy Login', 'cuat', 'label'))
9606 ,(22, 'libraryelf', 'login', 'xmlrpc', 'authz', oils_i18n_gettext(22, 'LibraryElf Login', 'cuat', 'label'))
9608 ,(23, 'selfcheck', 'verify', 'translator-v1','authz', oils_i18n_gettext(23, 'Self-Check User Verification', 'cuat', 'label'))
9609 ,(24, 'ezproxy', 'verify', 'remoteauth', 'authz', oils_i18n_gettext(24, 'EZProxy Verification', 'cuat', 'label'))
9613 -- reserve the first 1000 slots
9614 SELECT SETVAL('config.usr_activity_type_id_seq'::TEXT, 1000);
9616 INSERT INTO config.org_unit_setting_type
9617 (name, label, description, grp, datatype)
9619 'circ.patron.usr_activity_retrieve.max',
9621 'circ.patron.usr_activity_retrieve.max',
9622 'Max user activity entries to retrieve (staff client)',
9627 'circ.patron.usr_activity_retrieve.max',
9628 'Sets the maxinum number of recent user activity entries to retrieve for display in the staff client. 0 means show none, -1 means show all. Default is 1.',
9637 SELECT evergreen.upgrade_deps_block_check('0682', :eg_version);
9639 CREATE TABLE asset.copy_location_group (
9640 id SERIAL PRIMARY KEY,
9641 name TEXT NOT NULL, -- i18n
9642 owner INT NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
9643 pos INT NOT NULL DEFAULT 0,
9644 top BOOL NOT NULL DEFAULT FALSE,
9645 opac_visible BOOL NOT NULL DEFAULT TRUE,
9646 CONSTRAINT lgroup_once_per_owner UNIQUE (owner,name)
9649 CREATE TABLE asset.copy_location_group_map (
9650 id SERIAL PRIMARY KEY,
9651 location INT NOT NULL REFERENCES asset.copy_location (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
9652 lgroup INT NOT NULL REFERENCES asset.copy_location_group (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
9653 CONSTRAINT lgroup_once_per_group UNIQUE (lgroup,location)
9656 -- check whether patch can be applied
9657 SELECT evergreen.upgrade_deps_block_check('0683', :eg_version);
9659 INSERT INTO action_trigger.event_params (event_def, param, value)
9660 VALUES (5, 'check_email_notify', 1);
9661 INSERT INTO action_trigger.event_params (event_def, param, value)
9662 VALUES (7, 'check_email_notify', 1);
9663 INSERT INTO action_trigger.event_params (event_def, param, value)
9664 VALUES (9, 'check_email_notify', 1);
9665 INSERT INTO action_trigger.validator (module,description) VALUES
9669 'Check Hold notification flag(s)',
9673 UPDATE action_trigger.event_definition SET validator = 'HoldNotifyCheck' WHERE id = 9;
9675 -- NOT COVERED: Adding check_sms_notify to the proper trigger. It doesn't have a static id.
9677 -- check whether patch can be applied
9678 SELECT evergreen.upgrade_deps_block_check('0684', :eg_version);
9682 -- Replace the constraints with more flexible ENUM's
9683 ALTER TABLE vandelay.queue DROP CONSTRAINT queue_queue_type_check;
9684 ALTER TABLE vandelay.bib_queue DROP CONSTRAINT bib_queue_queue_type_check;
9685 ALTER TABLE vandelay.authority_queue DROP CONSTRAINT authority_queue_queue_type_check;
9687 CREATE TYPE vandelay.bib_queue_queue_type AS ENUM ('bib', 'acq');
9688 CREATE TYPE vandelay.authority_queue_queue_type AS ENUM ('authority');
9690 -- dropped column is also implemented by the child tables
9691 ALTER TABLE vandelay.queue DROP COLUMN queue_type;
9693 -- to recover after using the undo sql from below
9694 -- alter table vandelay.bib_queue add column queue_type text default 'bib' not null;
9695 -- alter table vandelay.authority_queue add column queue_type text default 'authority' not null;
9697 -- modify the child tables to use the ENUMs
9698 ALTER TABLE vandelay.bib_queue
9699 ALTER COLUMN queue_type DROP DEFAULT,
9700 ALTER COLUMN queue_type TYPE vandelay.bib_queue_queue_type
9701 USING (queue_type::vandelay.bib_queue_queue_type),
9702 ALTER COLUMN queue_type SET DEFAULT 'bib';
9704 ALTER TABLE vandelay.authority_queue
9705 ALTER COLUMN queue_type DROP DEFAULT,
9706 ALTER COLUMN queue_type TYPE vandelay.authority_queue_queue_type
9707 USING (queue_type::vandelay.authority_queue_queue_type),
9708 ALTER COLUMN queue_type SET DEFAULT 'authority';
9710 -- give lineitems a pointer to their vandelay queued_record
9712 ALTER TABLE acq.lineitem ADD COLUMN queued_record BIGINT
9713 REFERENCES vandelay.queued_bib_record (id)
9714 ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
9716 ALTER TABLE acq.acq_lineitem_history ADD COLUMN queued_record BIGINT
9717 REFERENCES vandelay.queued_bib_record (id)
9718 ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
9722 INSERT INTO permission.perm_list ( id, code, description )
9725 'IMPORT_ACQ_LINEITEM_BIB_RECORD_UPLOAD',
9728 'Allows a user to create new bibs directly from an ACQ MARC file upload',
9735 INSERT INTO vandelay.import_error ( code, description )
9737 'import.record.perm_failure',
9739 'import.record.perm_failure',
9740 'Perm failure creating a record', 'vie', 'description')
9746 -- Evergreen DB patch 0685.data.bluray_vr_format.sql
9748 -- FIXME: insert description of change, if needed
9752 -- check whether patch can be applied
9753 SELECT evergreen.upgrade_deps_block_check('0685', :eg_version);
9755 -- FIXME: add/check SQL statements to perform the upgrade
9760 -- Check if it is already there
9761 PERFORM * FROM config.marc21_physical_characteristic_value_map v
9762 JOIN config.marc21_physical_characteristic_subfield_map s ON v.ptype_subfield = s.id
9763 WHERE s.ptype_key = 'v' AND s.subfield = 'e' AND s.start_pos = '4' AND s.length = '1'
9771 -- Otherwise, insert it
9772 INSERT INTO config.marc21_physical_characteristic_value_map (value,ptype_subfield,label)
9773 SELECT 's',id,'Blu-ray'
9774 FROM config.marc21_physical_characteristic_subfield_map
9775 WHERE ptype_key = 'v' AND subfield = 'e' AND start_pos = '4' AND length = '1';
9777 -- And reingest the blue-ray items so that things see the new value
9778 SELECT INTO same_marc enabled FROM config.internal_flag WHERE name = 'ingest.reingest.force_on_same_marc';
9779 UPDATE config.internal_flag SET enabled = true WHERE name = 'ingest.reingest.force_on_same_marc';
9780 UPDATE biblio.record_entry SET marc=marc WHERE id IN (SELECT record
9782 metabib.full_rec a JOIN metabib.full_rec b USING (record)
9784 a.tag = 'LDR' AND a.value LIKE '______g%'
9785 AND b.tag = '007' AND b.value LIKE 'v___s%');
9786 UPDATE config.internal_flag SET enabled = same_marc WHERE name = 'ingest.reingest.force_on_same_marc';
9791 -- Evergreen DB patch 0686.schema.auditor_boost.sql
9793 -- FIXME: insert description of change, if needed
9795 -- check whether patch can be applied
9796 SELECT evergreen.upgrade_deps_block_check('0686', :eg_version);
9798 -- FIXME: add/check SQL statements to perform the upgrade
9799 -- These three functions are for capturing, getting, and clearing user and workstation information
9801 -- Set the User AND workstation in one call. Tis faster. And less calls.
9802 -- First argument is user, second is workstation
9803 CREATE OR REPLACE FUNCTION auditor.set_audit_info(INT, INT) RETURNS VOID AS $$
9804 $_SHARED{"eg_audit_user"} = $_[0];
9805 $_SHARED{"eg_audit_ws"} = $_[1];
9808 -- Get the User AND workstation in one call. Less calls, useful for joins ;)
9809 CREATE OR REPLACE FUNCTION auditor.get_audit_info() RETURNS TABLE (eg_user INT, eg_ws INT) AS $$
9810 return [{eg_user => $_SHARED{"eg_audit_user"}, eg_ws => $_SHARED{"eg_audit_ws"}}];
9813 -- Clear the audit info, for whatever reason
9814 CREATE OR REPLACE FUNCTION auditor.clear_audit_info() RETURNS VOID AS $$
9815 delete($_SHARED{"eg_audit_user"});
9816 delete($_SHARED{"eg_audit_ws"});
9819 CREATE OR REPLACE FUNCTION auditor.create_auditor_history ( sch TEXT, tbl TEXT ) RETURNS BOOL AS $creator$
9822 CREATE TABLE auditor.$$ || sch || $$_$$ || tbl || $$_history (
9823 audit_id BIGINT PRIMARY KEY,
9824 audit_time TIMESTAMP WITH TIME ZONE NOT NULL,
9825 audit_action TEXT NOT NULL,
9828 LIKE $$ || sch || $$.$$ || tbl || $$
9833 $creator$ LANGUAGE 'plpgsql';
9835 CREATE OR REPLACE FUNCTION auditor.create_auditor_func ( sch TEXT, tbl TEXT ) RETURNS BOOL AS $creator$
9839 SELECT INTO column_list array_agg(a.attname)
9840 FROM pg_catalog.pg_attribute a
9841 JOIN pg_catalog.pg_class c ON a.attrelid = c.oid
9842 JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
9843 WHERE relkind = 'r' AND n.nspname = sch AND c.relname = tbl AND a.attnum > 0 AND NOT a.attisdropped;
9846 CREATE OR REPLACE FUNCTION auditor.audit_$$ || sch || $$_$$ || tbl || $$_func ()
9847 RETURNS TRIGGER AS $func$
9849 INSERT INTO auditor.$$ || sch || $$_$$ || tbl || $$_history ( audit_id, audit_time, audit_action, audit_user, audit_ws, $$
9850 || array_to_string(column_list, ', ') || $$ )
9851 SELECT nextval('auditor.$$ || sch || $$_$$ || tbl || $$_pkey_seq'),
9856 OLD.$$ || array_to_string(column_list, ', OLD.') || $$
9857 FROM auditor.get_audit_info();
9860 $func$ LANGUAGE 'plpgsql';
9864 $creator$ LANGUAGE 'plpgsql';
9866 CREATE OR REPLACE FUNCTION auditor.create_auditor_lifecycle ( sch TEXT, tbl TEXT ) RETURNS BOOL AS $creator$
9870 SELECT INTO column_list array_agg(a.attname)
9871 FROM pg_catalog.pg_attribute a
9872 JOIN pg_catalog.pg_class c ON a.attrelid = c.oid
9873 JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
9874 WHERE relkind = 'r' AND n.nspname = sch AND c.relname = tbl AND a.attnum > 0 AND NOT a.attisdropped;
9877 CREATE VIEW auditor.$$ || sch || $$_$$ || tbl || $$_lifecycle AS
9878 SELECT -1 AS audit_id,
9879 now() AS audit_time,
9880 '-' AS audit_action,
9883 $$ || array_to_string(column_list, ', ') || $$
9884 FROM $$ || sch || $$.$$ || tbl || $$
9886 SELECT audit_id, audit_time, audit_action, audit_user, audit_ws,
9887 $$ || array_to_string(column_list, ', ') || $$
9888 FROM auditor.$$ || sch || $$_$$ || tbl || $$_history;
9892 $creator$ LANGUAGE 'plpgsql';
9894 -- Corrects all column discrepencies between audit table and core table:
9895 -- Adds missing columns
9896 -- Removes leftover columns
9898 -- Also, ensures all core auditor columns exist.
9899 CREATE OR REPLACE FUNCTION auditor.fix_columns() RETURNS VOID AS $BODY$
9901 current_table TEXT = ''; -- Storage for post-loop main table name
9902 current_audit_table TEXT = ''; -- Storage for post-loop audit table name
9903 query TEXT = ''; -- Storage for built query
9904 cr RECORD; -- column record object
9905 alter_t BOOL = false; -- Has the alter table command been appended yet
9906 auditor_cores TEXT[] = ARRAY[]::TEXT[]; -- Core auditor function list (filled inside of loop)
9907 core_column TEXT; -- The current core column we are adding
9910 WITH audit_tables AS ( -- Basic grab of auditor tables. Anything in the auditor namespace, basically. With oids.
9911 SELECT c.oid AS audit_oid, c.relname AS audit_table
9912 FROM pg_catalog.pg_class c
9913 JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
9914 WHERE relkind='r' AND nspname = 'auditor'
9916 table_set AS ( -- Union of auditor tables with their "main" tables. With oids.
9917 SELECT a.audit_oid, a.audit_table, c.oid AS main_oid, n.nspname as main_namespace, c.relname as main_table
9918 FROM pg_catalog.pg_class c
9919 JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
9920 JOIN audit_tables a ON a.audit_table = n.nspname || '_' || c.relname || '_history'
9923 column_lists AS ( -- All columns associated with the auditor or main table, grouped by the main table's oid.
9924 SELECT DISTINCT ON (main_oid, attname) t.main_oid, a.attname
9926 JOIN pg_catalog.pg_attribute a ON a.attrelid IN (t.main_oid, t.audit_oid)
9927 WHERE attnum > 0 AND NOT attisdropped
9929 column_defs AS ( -- The motherload, every audit table and main table plus column names and defs.
9933 a.attname AS main_column, -- These two will be null for columns that have since been deleted, or for auditor core columns
9934 pg_catalog.format_type(a.atttypid, a.atttypmod) AS main_column_def,
9935 b.attname AS audit_column, -- These two will be null for columns that have since been added
9936 pg_catalog.format_type(b.atttypid, b.atttypmod) AS audit_column_def
9938 JOIN column_lists c USING (main_oid)
9939 LEFT JOIN pg_catalog.pg_attribute a ON a.attname = c.attname AND a.attrelid = t.main_oid AND a.attnum > 0 AND NOT a.attisdropped
9940 LEFT JOIN pg_catalog.pg_attribute b ON b.attname = c.attname AND b.attrelid = t.audit_oid AND b.attnum > 0 AND NOT b.attisdropped
9942 -- Nice sorted output from the above
9943 SELECT * FROM column_defs WHERE main_column_def IS DISTINCT FROM audit_column_def ORDER BY main_namespace, main_table, main_column, audit_column
9945 IF current_table <> (cr.main_namespace || '.' || cr.main_table) THEN -- New table?
9946 FOR core_column IN SELECT DISTINCT unnest(auditor_cores) LOOP -- Update missing core auditor columns
9947 IF NOT alter_t THEN -- Add ALTER TABLE if we haven't already
9948 query:=query || $$ALTER TABLE auditor.$$ || current_audit_table;
9951 query:=query || $$,$$;
9953 -- Bit of a sneaky bit here. Create audit_id as a bigserial so it gets automatic values and doesn't complain about nulls when becoming a PRIMARY KEY.
9954 query:=query || $$ ADD COLUMN $$ || CASE WHEN core_column = 'audit_id bigint' THEN $$audit_id bigserial PRIMARY KEY$$ ELSE core_column END;
9956 IF alter_t THEN -- Open alter table = needs a semicolon
9957 query:=query || $$; $$;
9959 IF 'audit_id bigint' = ANY(auditor_cores) THEN -- We added a primary key...
9960 -- Fun! Drop the default on audit_id, drop the auto-created sequence, create a new one, and set the current value
9961 -- For added fun, we have to execute in chunks due to the parser checking setval/currval arguments at parse time.
9963 EXECUTE $$ALTER TABLE auditor.$$ || current_audit_table || $$ ALTER COLUMN audit_id DROP DEFAULT; $$ ||
9964 $$CREATE SEQUENCE auditor.$$ || current_audit_table || $$_pkey_seq;$$;
9965 EXECUTE $$SELECT setval('auditor.$$ || current_audit_table || $$_pkey_seq', currval('auditor.$$ || current_audit_table || $$_audit_id_seq')); $$ ||
9966 $$DROP SEQUENCE auditor.$$ || current_audit_table || $$_audit_id_seq;$$;
9970 -- New table means we reset the list of needed auditor core columns
9971 auditor_cores = ARRAY['audit_id bigint', 'audit_time timestamp with time zone', 'audit_action text', 'audit_user integer', 'audit_ws integer'];
9972 -- And store some values for use later, because we can't rely on cr in all places.
9973 current_table:=cr.main_namespace || '.' || cr.main_table;
9974 current_audit_table:=cr.audit_table;
9976 IF cr.main_column IS NULL AND cr.audit_column LIKE 'audit_%' THEN -- Core auditor column?
9977 -- Remove core from list of cores
9978 SELECT INTO auditor_cores array_agg(core) FROM unnest(auditor_cores) AS core WHERE core != (cr.audit_column || ' ' || cr.audit_column_def);
9979 ELSIF cr.main_column IS NULL THEN -- Main column doesn't exist, and it isn't an auditor column. Needs dropping from the auditor.
9981 query:=query || $$ALTER TABLE auditor.$$ || current_audit_table;
9984 query:=query || $$,$$;
9986 query:=query || $$ DROP COLUMN $$ || cr.audit_column;
9987 ELSIF cr.audit_column IS NULL AND cr.main_column IS NOT NULL THEN -- New column auditor doesn't have. Add it.
9989 query:=query || $$ALTER TABLE auditor.$$ || current_audit_table;
9992 query:=query || $$,$$;
9994 query:=query || $$ ADD COLUMN $$ || cr.main_column || $$ $$ || cr.main_column_def;
9995 ELSIF cr.main_column IS NOT NULL AND cr.audit_column IS NOT NULL THEN -- Both sides have this column, but types differ. Fix that.
9997 query:=query || $$ALTER TABLE auditor.$$ || current_audit_table;
10000 query:=query || $$,$$;
10002 query:=query || $$ ALTER COLUMN $$ || cr.audit_column || $$ TYPE $$ || cr.main_column_def;
10005 FOR core_column IN SELECT DISTINCT unnest(auditor_cores) LOOP -- Repeat this outside of the loop to catch the last table
10006 IF NOT alter_t THEN
10007 query:=query || $$ALTER TABLE auditor.$$ || current_audit_table;
10010 query:=query || $$,$$;
10012 -- Bit of a sneaky bit here. Create audit_id as a bigserial so it gets automatic values and doesn't complain about nulls when becoming a PRIMARY KEY.
10013 query:=query || $$ ADD COLUMN $$ || CASE WHEN core_column = 'audit_id bigint' THEN $$audit_id bigserial PRIMARY KEY$$ ELSE core_column END;
10015 IF alter_t THEN -- Open alter table = needs a semicolon
10016 query:=query || $$;$$;
10017 IF 'audit_id bigint' = ANY(auditor_cores) THEN -- We added a primary key...
10018 -- Fun! Drop the default on audit_id, drop the auto-created sequence, create a new one, and set the current value
10019 -- For added fun, we have to execute in chunks due to the parser checking setval/currval arguments at parse time.
10021 EXECUTE $$ALTER TABLE auditor.$$ || current_audit_table || $$ ALTER COLUMN audit_id DROP DEFAULT; $$ ||
10022 $$CREATE SEQUENCE auditor.$$ || current_audit_table || $$_pkey_seq;$$;
10023 EXECUTE $$SELECT setval('auditor.$$ || current_audit_table || $$_pkey_seq', currval('auditor.$$ || current_audit_table || $$_audit_id_seq')); $$ ||
10024 $$DROP SEQUENCE auditor.$$ || current_audit_table || $$_audit_id_seq;$$;
10030 $BODY$ LANGUAGE plpgsql;
10032 -- Update it all routine
10033 CREATE OR REPLACE FUNCTION auditor.update_auditors() RETURNS boolean AS $BODY$
10039 -- Drop Lifecycle view(s) before potential column changes
10040 FOR auditor_name IN
10042 FROM pg_catalog.pg_class c
10043 JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
10044 WHERE relkind = 'v' AND n.nspname = 'auditor' LOOP
10045 EXECUTE $$ DROP VIEW auditor.$$ || auditor_name || $$;$$;
10047 -- Fix all column discrepencies
10048 PERFORM auditor.fix_columns();
10049 -- Re-create trigger functions and lifecycle views
10050 FOR table_schema, table_name IN
10051 WITH audit_tables AS (
10052 SELECT c.oid AS audit_oid, c.relname AS audit_table
10053 FROM pg_catalog.pg_class c
10054 JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
10055 WHERE relkind='r' AND nspname = 'auditor'
10058 SELECT a.audit_oid, a.audit_table, c.oid AS main_oid, n.nspname as main_namespace, c.relname as main_table
10059 FROM pg_catalog.pg_class c
10060 JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
10061 JOIN audit_tables a ON a.audit_table = n.nspname || '_' || c.relname || '_history'
10062 WHERE relkind = 'r'
10064 SELECT main_namespace, main_table FROM table_set LOOP
10066 PERFORM auditor.create_auditor_func(table_schema, table_name);
10067 PERFORM auditor.create_auditor_lifecycle(table_schema, table_name);
10071 $BODY$ LANGUAGE plpgsql;
10073 -- Go ahead and update them all now
10074 SELECT auditor.update_auditors();
10077 -- Evergreen DB patch 0687.schema.enhance_reingest.sql
10079 -- FIXME: insert description of change, if needed
10083 -- check whether patch can be applied
10084 SELECT evergreen.upgrade_deps_block_check('0687', :eg_version);
10085 SELECT evergreen.upgrade_deps_block_check('0711', :eg_version); -- introduces
10086 -- changes to metabib.reingest_metabib_field_entries() that must happen here
10087 -- rather than later in a separate CREATE OR REPLACE FUNCTION statement.
10089 -- FIXME: add/check SQL statements to perform the upgrade
10090 -- New function def
10091 CREATE OR REPLACE FUNCTION metabib.reingest_metabib_field_entries( bib_id BIGINT, skip_facet BOOL DEFAULT FALSE, skip_browse BOOL DEFAULT FALSE, skip_search BOOL DEFAULT FALSE ) RETURNS VOID AS $func$
10094 ind_data metabib.field_entry_template%ROWTYPE;
10095 mbe_row metabib.browse_entry%ROWTYPE;
10097 normalized_value TEXT;
10099 PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled;
10101 IF NOT skip_search THEN
10102 FOR fclass IN SELECT * FROM config.metabib_class LOOP
10103 -- RAISE NOTICE 'Emptying out %', fclass.name;
10104 EXECUTE $$DELETE FROM metabib.$$ || fclass.name || $$_field_entry WHERE source = $$ || bib_id;
10107 IF NOT skip_facet THEN
10108 DELETE FROM metabib.facet_entry WHERE source = bib_id;
10110 IF NOT skip_browse THEN
10111 DELETE FROM metabib.browse_entry_def_map WHERE source = bib_id;
10115 FOR ind_data IN SELECT * FROM biblio.extract_metabib_field_entry( bib_id ) LOOP
10116 IF ind_data.field < 0 THEN
10117 ind_data.field = -1 * ind_data.field;
10120 IF ind_data.facet_field AND NOT skip_facet THEN
10121 INSERT INTO metabib.facet_entry (field, source, value)
10122 VALUES (ind_data.field, ind_data.source, ind_data.value);
10125 IF ind_data.browse_field AND NOT skip_browse THEN
10126 -- A caveat about this SELECT: this should take care of replacing
10127 -- old mbe rows when data changes, but not if normalization (by
10128 -- which I mean specifically the output of
10129 -- evergreen.oils_tsearch2()) changes. It may or may not be
10130 -- expensive to add a comparison of index_vector to index_vector
10131 -- to the WHERE clause below.
10132 normalized_value := metabib.browse_normalize(
10133 ind_data.value, ind_data.field
10136 SELECT INTO mbe_row * FROM metabib.browse_entry WHERE value = normalized_value;
10138 mbe_id := mbe_row.id;
10140 INSERT INTO metabib.browse_entry (value) VALUES (normalized_value);
10141 mbe_id := CURRVAL('metabib.browse_entry_id_seq'::REGCLASS);
10144 INSERT INTO metabib.browse_entry_def_map (entry, def, source)
10145 VALUES (mbe_id, ind_data.field, ind_data.source);
10148 IF ind_data.search_field AND NOT skip_search THEN
10150 INSERT INTO metabib.$$ || ind_data.field_class || $$_field_entry (field, source, value)
10152 quote_literal(ind_data.field) || $$, $$ ||
10153 quote_literal(ind_data.source) || $$, $$ ||
10154 quote_literal(ind_data.value) ||
10162 $func$ LANGUAGE PLPGSQL;
10165 DROP FUNCTION IF EXISTS metabib.reingest_metabib_field_entries(BIGINT);
10167 -- Evergreen DB patch 0688.data.circ_history_export_csv.sql
10169 -- FIXME: insert description of change, if needed
10172 -- check whether patch can be applied
10173 SELECT evergreen.upgrade_deps_block_check('0688', :eg_version);
10175 INSERT INTO action_trigger.hook (key, core_type, description, passive)
10177 'circ.format.history.csv',
10180 'circ.format.history.csv',
10181 'Produce CSV of circulation history',
10188 INSERT INTO action_trigger.event_definition (
10189 active, owner, name, hook, reactor, validator, group_field, template)
10191 TRUE, 1, 'Circ History CSV', 'circ.format.history.csv', 'ProcessTemplate', 'NOOP_True', 'usr',
10193 Title,Author,Call Number,Barcode,Format
10195 FOR circ IN target;
10196 bibxml = helpers.unapi_bre(circ.target_copy.call_number.record, {flesh => '{mra}'});
10198 FOR part IN bibxml.findnodes('//*[@tag="245"]/*[@code="a" or @code="b"]');
10199 title = title _ part.textContent;
10201 author = bibxml.findnodes('//*[@tag="100"]/*[@code="a"]').textContent;
10202 item_type = bibxml.findnodes('//*[local-name()="attributes"]/*[local-name()="field"][@name="item_type"]').getAttribute('coded-value') %]
10204 [%- helpers.csv_datum(title) -%],
10205 [%- helpers.csv_datum(author) -%],
10206 [%- helpers.csv_datum(circ.target_copy.call_number.label) -%],
10207 [%- helpers.csv_datum(circ.target_copy.barcode) -%],
10208 [%- helpers.csv_datum(item_type) %]
10213 INSERT INTO action_trigger.environment (event_def, path)
10215 currval('action_trigger.event_definition_id_seq'),
10216 'target_copy.call_number'
10220 -- Evergreen DB patch 0689.data.record_print_format_update.sql
10222 -- Updates print and email templates for bib record actions
10225 -- check whether patch can be applied
10226 SELECT evergreen.upgrade_deps_block_check('0689', :eg_version);
10228 UPDATE action_trigger.event_definition SET template = $$
10230 <style> li { padding: 8px; margin 5px; }</style>
10232 [% FOR cbreb IN target %]
10233 [% FOR item IN cbreb.items;
10234 bre_id = item.target_biblio_record_entry;
10236 bibxml = helpers.unapi_bre(bre_id, {flesh => '{mra}'});
10237 FOR part IN bibxml.findnodes('//*[@tag="245"]/*[@code="a" or @code="b"]');
10238 title = title _ part.textContent;
10241 author = bibxml.findnodes('//*[@tag="100"]/*[@code="a"]').textContent;
10242 item_type = bibxml.findnodes('//*[local-name()="attributes"]/*[local-name()="field"][@name="item_type"]').getAttribute('coded-value');
10243 publisher = bibxml.findnodes('//*[@tag="260"]/*[@code="b"]').textContent;
10244 pubdate = bibxml.findnodes('//*[@tag="260"]/*[@code="c"]').textContent;
10245 isbn = bibxml.findnodes('//*[@tag="020"]/*[@code="a"]').textContent;
10246 issn = bibxml.findnodes('//*[@tag="022"]/*[@code="a"]').textContent;
10247 upc = bibxml.findnodes('//*[@tag="024"]/*[@code="a"]').textContent;
10251 Bib ID# [% bre_id %]<br/>
10252 [% IF isbn %]ISBN: [% isbn %]<br/>[% END %]
10253 [% IF issn %]ISSN: [% issn %]<br/>[% END %]
10254 [% IF upc %]UPC: [% upc %]<br/>[% END %]
10255 Title: [% title %]<br />
10256 Author: [% author %]<br />
10257 Publication Info: [% publisher %] [% pubdate %]<br/>
10258 Item Type: [% item_type %]
10265 WHERE hook = 'biblio.format.record_entry.print' AND id < 100; -- sample data
10268 UPDATE action_trigger.event_definition SET delay = '00:00:00', template = $$
10269 [%- SET user = target.0.owner -%]
10270 To: [%- params.recipient_email || user.email %]
10271 From: [%- params.sender_email || default_sender %]
10272 Subject: Bibliographic Records
10274 [% FOR cbreb IN target %]
10275 [% FOR item IN cbreb.items;
10276 bre_id = item.target_biblio_record_entry;
10278 bibxml = helpers.unapi_bre(bre_id, {flesh => '{mra}'});
10279 FOR part IN bibxml.findnodes('//*[@tag="245"]/*[@code="a" or @code="b"]');
10280 title = title _ part.textContent;
10283 author = bibxml.findnodes('//*[@tag="100"]/*[@code="a"]').textContent;
10284 item_type = bibxml.findnodes('//*[local-name()="attributes"]/*[local-name()="field"][@name="item_type"]').getAttribute('coded-value');
10285 publisher = bibxml.findnodes('//*[@tag="260"]/*[@code="b"]').textContent;
10286 pubdate = bibxml.findnodes('//*[@tag="260"]/*[@code="c"]').textContent;
10287 isbn = bibxml.findnodes('//*[@tag="020"]/*[@code="a"]').textContent;
10288 issn = bibxml.findnodes('//*[@tag="022"]/*[@code="a"]').textContent;
10289 upc = bibxml.findnodes('//*[@tag="024"]/*[@code="a"]').textContent;
10292 [% loop.count %]/[% loop.size %]. Bib ID# [% bre_id %]
10293 [% IF isbn %]ISBN: [% isbn _ "\n" %][% END -%]
10294 [% IF issn %]ISSN: [% issn _ "\n" %][% END -%]
10295 [% IF upc %]UPC: [% upc _ "\n" %] [% END -%]
10297 Author: [% author %]
10298 Publication Info: [% publisher %] [% pubdate %]
10299 Item Type: [% item_type %]
10304 WHERE hook = 'biblio.format.record_entry.email' AND id < 100; -- sample data
10306 -- remove a swath of unused environment entries
10308 DELETE FROM action_trigger.environment env
10309 USING action_trigger.event_definition def
10310 WHERE env.event_def = def.id AND
10311 env.path != 'items' AND
10312 def.hook = 'biblio.format.record_entry.print' AND
10313 def.id < 100; -- sample data
10315 DELETE FROM action_trigger.environment env
10316 USING action_trigger.event_definition def
10317 WHERE env.event_def = def.id AND
10318 env.path != 'items' AND
10319 env.path != 'owner' AND
10320 def.hook = 'biblio.format.record_entry.email' AND
10321 def.id < 100; -- sample data
10323 -- Evergreen DB patch 0690.schema.unapi_limit_rank.sql
10325 -- Rewrite the in-database unapi functions to include per-object limits and
10326 -- offsets, such as a maximum number of copies and call numbers for given
10327 -- bib record via the HSTORE syntax (for example, 'acn => 5, acp => 10' would
10328 -- limit to a maximum of 5 call numbers for the bib, with up to 10 copies per
10331 -- Add some notion of "preferred library" that will provide copy counts
10332 -- and optionally affect the sorting of returned copies.
10334 -- Sort copies by availability, preferring the most available copies.
10336 -- Return located URIs.
10340 -- check whether patch can be applied
10341 SELECT evergreen.upgrade_deps_block_check('0690', :eg_version);
10343 -- The simplest way to apply all of these changes is just to replace the unapi
10344 -- schema entirely -- the following is a copy of 990.schema.unapi.sql with
10345 -- the initial COMMIT in place in case the upgrade_deps_block_check fails;
10346 -- if it does, then the attempt to create the unapi schema in the following
10347 -- transaction will also fail. Not graceful, but safe!
10348 DROP SCHEMA IF EXISTS unapi CASCADE;
10350 CREATE SCHEMA unapi;
10352 CREATE OR REPLACE FUNCTION evergreen.org_top()
10353 RETURNS SETOF actor.org_unit AS $$
10354 SELECT * FROM actor.org_unit WHERE parent_ou IS NULL LIMIT 1;
10355 $$ LANGUAGE SQL STABLE
10358 CREATE OR REPLACE FUNCTION evergreen.array_remove_item_by_value(inp ANYARRAY, el ANYELEMENT)
10359 RETURNS anyarray AS $$
10360 SELECT ARRAY_ACCUM(x.e) FROM UNNEST( $1 ) x(e) WHERE x.e <> $2;
10361 $$ LANGUAGE SQL STABLE;
10363 CREATE OR REPLACE FUNCTION evergreen.rank_ou(lib INT, search_lib INT, pref_lib INT DEFAULT NULL)
10364 RETURNS INTEGER AS $$
10365 WITH search_libs AS (
10366 SELECT id, distance FROM actor.org_unit_descendants_distance($2)
10369 (SELECT -10000 FROM actor.org_unit
10370 WHERE $1 = $3 AND id = $3 AND $2 IN (
10371 SELECT id FROM actor.org_unit WHERE parent_ou IS NULL
10374 (SELECT distance FROM search_libs WHERE id = $1),
10377 $$ LANGUAGE SQL STABLE;
10379 CREATE OR REPLACE FUNCTION evergreen.rank_cp_status(status INT)
10380 RETURNS INTEGER AS $$
10381 WITH totally_available AS (
10382 SELECT id, 0 AS avail_rank
10383 FROM config.copy_status
10384 WHERE opac_visible IS TRUE
10385 AND copy_active IS TRUE
10386 AND id != 1 -- "Checked out"
10387 ), almost_available AS (
10388 SELECT id, 10 AS avail_rank
10389 FROM config.copy_status
10390 WHERE holdable IS TRUE
10391 AND opac_visible IS TRUE
10392 AND copy_active IS FALSE
10393 OR id = 1 -- "Checked out"
10396 (SELECT avail_rank FROM totally_available WHERE $1 IN (id)),
10397 (SELECT avail_rank FROM almost_available WHERE $1 IN (id)),
10400 $$ LANGUAGE SQL STABLE;
10402 CREATE OR REPLACE FUNCTION evergreen.ranked_volumes(
10405 depth INT DEFAULT NULL,
10406 slimit HSTORE DEFAULT NULL,
10407 soffset HSTORE DEFAULT NULL,
10408 pref_lib INT DEFAULT NULL
10409 ) RETURNS TABLE (id BIGINT, name TEXT, label_sortkey TEXT, rank BIGINT) AS $$
10410 SELECT ua.id, ua.name, ua.label_sortkey, MIN(ua.rank) AS rank FROM (
10411 SELECT acn.id, aou.name, acn.label_sortkey,
10412 evergreen.rank_ou(aou.id, $2, $6), evergreen.rank_cp_status(acp.status),
10414 FROM asset.call_number acn
10415 JOIN asset.copy acp ON (acn.id = acp.call_number)
10416 JOIN actor.org_unit_descendants( $2, COALESCE(
10419 FROM actor.org_unit_type aout
10420 INNER JOIN actor.org_unit ou ON ou_type = aout.id
10423 ) AS aou ON (acp.circ_lib = aou.id)
10424 WHERE acn.record = $1
10425 AND acn.deleted IS FALSE
10426 AND acp.deleted IS FALSE
10427 GROUP BY acn.id, acp.status, aou.name, acn.label_sortkey, aou.id
10429 ORDER BY evergreen.rank_ou(aou.id, $2, $6), evergreen.rank_cp_status(acp.status)
10432 GROUP BY ua.id, ua.name, ua.label_sortkey
10433 ORDER BY rank, ua.name, ua.label_sortkey
10434 LIMIT ($4 -> 'acn')::INT
10435 OFFSET ($5 -> 'acn')::INT;
10437 LANGUAGE SQL STABLE;
10439 CREATE OR REPLACE FUNCTION evergreen.located_uris (
10442 pref_lib INT DEFAULT NULL
10443 ) RETURNS TABLE (id BIGINT, name TEXT, label_sortkey TEXT, rank INT) AS $$
10444 SELECT acn.id, aou.name, acn.label_sortkey, evergreen.rank_ou(aou.id, $2, $3) AS pref_ou
10445 FROM asset.call_number acn
10446 INNER JOIN asset.uri_call_number_map auricnm ON acn.id = auricnm.call_number
10447 INNER JOIN asset.uri auri ON auri.id = auricnm.uri
10448 INNER JOIN actor.org_unit_ancestors( COALESCE($3, $2) ) aou ON (acn.owning_lib = aou.id)
10449 WHERE acn.record = $1
10450 AND acn.deleted IS FALSE
10451 AND auri.active IS TRUE
10453 SELECT acn.id, aou.name, acn.label_sortkey, evergreen.rank_ou(aou.id, $2, $3) AS pref_ou
10454 FROM asset.call_number acn
10455 INNER JOIN asset.uri_call_number_map auricnm ON acn.id = auricnm.call_number
10456 INNER JOIN asset.uri auri ON auri.id = auricnm.uri
10457 INNER JOIN actor.org_unit_ancestors( $2 ) aou ON (acn.owning_lib = aou.id)
10458 WHERE acn.record = $1
10459 AND acn.deleted IS FALSE
10460 AND auri.active IS TRUE;
10462 LANGUAGE SQL STABLE;
10464 CREATE TABLE unapi.bre_output_layout (
10465 name TEXT PRIMARY KEY,
10466 transform TEXT REFERENCES config.xml_transform (name) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
10467 mime_type TEXT NOT NULL,
10468 feed_top TEXT NOT NULL,
10469 holdings_element TEXT,
10470 title_element TEXT,
10471 description_element TEXT,
10472 creator_element TEXT,
10473 update_ts_element TEXT
10476 INSERT INTO unapi.bre_output_layout
10477 (name, transform, mime_type, holdings_element, feed_top, title_element, description_element, creator_element, update_ts_element)
10479 ('holdings_xml', NULL, 'application/xml', NULL, 'hxml', NULL, NULL, NULL, NULL),
10480 ('marcxml', 'marcxml', 'application/marc+xml', 'record', 'collection', NULL, NULL, NULL, NULL),
10481 ('mods32', 'mods32', 'application/mods+xml', 'mods', 'modsCollection', NULL, NULL, NULL, NULL)
10484 -- Dummy functions, so we can create the real ones out of order
10485 CREATE OR REPLACE FUNCTION unapi.aou ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10486 CREATE OR REPLACE FUNCTION unapi.acnp ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10487 CREATE OR REPLACE FUNCTION unapi.acns ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10488 CREATE OR REPLACE FUNCTION unapi.acn ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10489 CREATE OR REPLACE FUNCTION unapi.ssub ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10490 CREATE OR REPLACE FUNCTION unapi.sdist ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10491 CREATE OR REPLACE FUNCTION unapi.sstr ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10492 CREATE OR REPLACE FUNCTION unapi.sitem ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10493 CREATE OR REPLACE FUNCTION unapi.sunit ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10494 CREATE OR REPLACE FUNCTION unapi.sisum ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10495 CREATE OR REPLACE FUNCTION unapi.sbsum ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10496 CREATE OR REPLACE FUNCTION unapi.sssum ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10497 CREATE OR REPLACE FUNCTION unapi.siss ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10498 CREATE OR REPLACE FUNCTION unapi.auri ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10499 CREATE OR REPLACE FUNCTION unapi.acp ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10500 CREATE OR REPLACE FUNCTION unapi.acpn ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10501 CREATE OR REPLACE FUNCTION unapi.acl ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10502 CREATE OR REPLACE FUNCTION unapi.ccs ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10503 CREATE OR REPLACE FUNCTION unapi.ascecm ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10504 CREATE OR REPLACE FUNCTION unapi.bre (
10510 depth INT DEFAULT NULL,
10511 slimit HSTORE DEFAULT NULL,
10512 soffset HSTORE DEFAULT NULL,
10513 include_xmlns BOOL DEFAULT TRUE,
10514 pref_lib INT DEFAULT NULL
10516 RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10517 CREATE OR REPLACE FUNCTION unapi.bmp ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10518 CREATE OR REPLACE FUNCTION unapi.mra ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10519 CREATE OR REPLACE FUNCTION unapi.circ ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT DEFAULT '-', depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10521 CREATE OR REPLACE FUNCTION unapi.holdings_xml (
10525 depth INT DEFAULT NULL,
10526 includes TEXT[] DEFAULT NULL::TEXT[],
10527 slimit HSTORE DEFAULT NULL,
10528 soffset HSTORE DEFAULT NULL,
10529 include_xmlns BOOL DEFAULT TRUE,
10530 pref_lib INT DEFAULT NULL
10532 RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10534 CREATE OR REPLACE FUNCTION unapi.biblio_record_entry_feed ( id_list BIGINT[], format TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE, title TEXT DEFAULT NULL, description TEXT DEFAULT NULL, creator TEXT DEFAULT NULL, update_ts TEXT DEFAULT NULL, unapi_url TEXT DEFAULT NULL, header_xml XML DEFAULT NULL ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10536 CREATE OR REPLACE FUNCTION unapi.memoize (classname TEXT, obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
10542 'id' || COALESCE(obj_id::TEXT,'') ||
10543 'format' || COALESCE(format::TEXT,'') ||
10544 'ename' || COALESCE(ename::TEXT,'') ||
10545 'includes' || COALESCE(includes::TEXT,'{}'::TEXT[]::TEXT) ||
10546 'org' || COALESCE(org::TEXT,'') ||
10547 'depth' || COALESCE(depth::TEXT,'') ||
10548 'slimit' || COALESCE(slimit::TEXT,'') ||
10549 'soffset' || COALESCE(soffset::TEXT,'') ||
10550 'include_xmlns' || COALESCE(include_xmlns::TEXT,'');
10551 -- RAISE NOTICE 'memoize key: %', key;
10554 -- RAISE NOTICE 'memoize hash: %', key;
10556 -- XXX cache logic ... memcached? table?
10558 EXECUTE $$SELECT unapi.$$ || classname || $$( $1, $2, $3, $4, $5, $6, $7, $8, $9);$$ INTO output USING obj_id, format, ename, includes, org, depth, slimit, soffset, include_xmlns;
10561 $F$ LANGUAGE PLPGSQL STABLE;
10563 CREATE OR REPLACE FUNCTION unapi.biblio_record_entry_feed ( id_list BIGINT[], format TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE, title TEXT DEFAULT NULL, description TEXT DEFAULT NULL, creator TEXT DEFAULT NULL, update_ts TEXT DEFAULT NULL, unapi_url TEXT DEFAULT NULL, header_xml XML DEFAULT NULL ) RETURNS XML AS $F$
10565 layout unapi.bre_output_layout%ROWTYPE;
10566 transform config.xml_transform%ROWTYPE;
10569 xmlns_uri TEXT := 'http://open-ils.org/spec/feed-xml/v1';
10571 element_list TEXT[];
10574 IF org = '-' OR org IS NULL THEN
10575 SELECT shortname INTO org FROM evergreen.org_top();
10578 SELECT id INTO ouid FROM actor.org_unit WHERE shortname = org;
10579 SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format;
10581 IF layout.name IS NULL THEN
10585 SELECT * INTO transform FROM config.xml_transform WHERE name = layout.transform;
10586 xmlns_uri := COALESCE(transform.namespace_uri,xmlns_uri);
10588 -- Gather the bib xml
10589 SELECT XMLAGG( unapi.bre(i, format, '', includes, org, depth, slimit, soffset, include_xmlns)) INTO tmp_xml FROM UNNEST( id_list ) i;
10591 IF layout.title_element IS NOT NULL THEN
10592 EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.title_element ||', XMLATTRIBUTES( $1 AS xmlns), $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, title;
10595 IF layout.description_element IS NOT NULL THEN
10596 EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.description_element ||', XMLATTRIBUTES( $1 AS xmlns), $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, description;
10599 IF layout.creator_element IS NOT NULL THEN
10600 EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.creator_element ||', XMLATTRIBUTES( $1 AS xmlns), $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, creator;
10603 IF layout.update_ts_element IS NOT NULL THEN
10604 EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.update_ts_element ||', XMLATTRIBUTES( $1 AS xmlns), $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, update_ts;
10607 IF unapi_url IS NOT NULL THEN
10608 EXECUTE $$SELECT XMLCONCAT( XMLELEMENT( name link, XMLATTRIBUTES( 'http://www.w3.org/1999/xhtml' AS xmlns, 'unapi-server' AS rel, $1 AS href, 'unapi' AS title)), $2)$$ INTO tmp_xml USING unapi_url, tmp_xml::XML;
10611 IF header_xml IS NOT NULL THEN tmp_xml := XMLCONCAT(header_xml,tmp_xml::XML); END IF;
10613 element_list := regexp_split_to_array(layout.feed_top,E'\\.');
10614 FOR i IN REVERSE ARRAY_UPPER(element_list, 1) .. 1 LOOP
10615 EXECUTE 'SELECT XMLELEMENT( name '|| quote_ident(element_list[i]) ||', XMLATTRIBUTES( $1 AS xmlns), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML;
10618 RETURN tmp_xml::XML;
10620 $F$ LANGUAGE PLPGSQL STABLE;
10622 CREATE OR REPLACE FUNCTION unapi.bre (
10628 depth INT DEFAULT NULL,
10629 slimit HSTORE DEFAULT NULL,
10630 soffset HSTORE DEFAULT NULL,
10631 include_xmlns BOOL DEFAULT TRUE,
10632 pref_lib INT DEFAULT NULL
10636 me biblio.record_entry%ROWTYPE;
10637 layout unapi.bre_output_layout%ROWTYPE;
10638 xfrm config.xml_transform%ROWTYPE;
10647 IF org = '-' OR org IS NULL THEN
10648 SELECT shortname INTO org FROM evergreen.org_top();
10651 SELECT id INTO ouid FROM actor.org_unit WHERE shortname = org;
10653 IF ouid IS NULL THEN
10657 IF format = 'holdings_xml' THEN -- the special case
10658 output := unapi.holdings_xml( obj_id, ouid, org, depth, includes, slimit, soffset, include_xmlns);
10662 SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format;
10664 IF layout.name IS NULL THEN
10668 SELECT * INTO xfrm FROM config.xml_transform WHERE name = layout.transform;
10670 SELECT * INTO me FROM biblio.record_entry WHERE id = obj_id;
10672 -- grab SVF if we need them
10673 IF ('mra' = ANY (includes)) THEN
10674 axml := unapi.mra(obj_id,NULL,NULL,NULL,NULL);
10679 -- grab holdings if we need them
10680 IF ('holdings_xml' = ANY (includes)) THEN
10681 hxml := unapi.holdings_xml(obj_id, ouid, org, depth, evergreen.array_remove_item_by_value(includes,'holdings_xml'), slimit, soffset, include_xmlns, pref_lib);
10687 -- generate our item node
10690 IF format = 'marcxml' THEN
10691 tmp_xml := me.marc;
10692 IF tmp_xml !~ E'<marc:' THEN -- If we're not using the prefixed namespace in this record, then remove all declarations of it
10693 tmp_xml := REGEXP_REPLACE(tmp_xml, ' xmlns:marc="http://www.loc.gov/MARC21/slim"', '', 'g');
10696 tmp_xml := oils_xslt_process(me.marc, xfrm.xslt)::XML;
10699 top_el := REGEXP_REPLACE(tmp_xml, E'^.*?<((?:\\S+:)?' || layout.holdings_element || ').*$', E'\\1');
10701 IF axml IS NOT NULL THEN
10702 tmp_xml := REGEXP_REPLACE(tmp_xml, '</' || top_el || '>(.*?)$', axml || '</' || top_el || E'>\\1');
10705 IF hxml IS NOT NULL THEN -- XXX how do we configure the holdings position?
10706 tmp_xml := REGEXP_REPLACE(tmp_xml, '</' || top_el || '>(.*?)$', hxml || '</' || top_el || E'>\\1');
10709 IF ('bre.unapi' = ANY (includes)) THEN
10710 output := REGEXP_REPLACE(
10712 '</' || top_el || '>(.*?)',
10716 'http://www.w3.org/1999/xhtml' AS xmlns,
10717 'unapi-id' AS class,
10718 'tag:open-ils.org:U2@bre/' || obj_id || '/' || org AS title
10720 )::TEXT || '</' || top_el || E'>\\1'
10726 output := REGEXP_REPLACE(output::TEXT,E'>\\s+<','><','gs')::XML;
10729 $F$ LANGUAGE PLPGSQL STABLE;
10731 CREATE OR REPLACE FUNCTION unapi.holdings_xml (
10735 depth INT DEFAULT NULL,
10736 includes TEXT[] DEFAULT NULL::TEXT[],
10737 slimit HSTORE DEFAULT NULL,
10738 soffset HSTORE DEFAULT NULL,
10739 include_xmlns BOOL DEFAULT TRUE,
10740 pref_lib INT DEFAULT NULL
10746 CASE WHEN $8 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
10747 CASE WHEN ('bre' = ANY ($5)) THEN 'tag:open-ils.org:U2@bre/' || $1 || '/' || $3 ELSE NULL END AS id
10751 (SELECT XMLAGG(XMLELEMENT::XML) FROM (
10754 XMLATTRIBUTES('public' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
10756 FROM asset.opac_ou_record_copy_count($2, $1)
10760 XMLATTRIBUTES('staff' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
10762 FROM asset.staff_ou_record_copy_count($2, $1)
10766 XMLATTRIBUTES('pref_lib' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
10768 FROM asset.opac_ou_record_copy_count($9, $1)
10773 WHEN ('bmp' = ANY ($5)) THEN
10775 name monograph_parts,
10776 (SELECT XMLAGG(bmp) FROM (
10777 SELECT unapi.bmp( id, 'xml', 'monograph_part', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($5,'bre'), 'holdings_xml'), $3, $4, $6, $7, FALSE)
10778 FROM biblio.monograph_part
10786 (SELECT XMLAGG(acn ORDER BY rank, name, label_sortkey) FROM (
10788 SELECT unapi.acn(y.id,'xml','volume',evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($5,'holdings_xml'),'bre'), $3, $4, $6, $7, FALSE), y.rank, name, label_sortkey
10789 FROM evergreen.ranked_volumes($1, $2, $4, $6, $7, $9) AS y
10792 SELECT unapi.acn(uris.id,'xml','volume',evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($5,'holdings_xml'),'bre'), $3, $4, $6, $7, FALSE), 0, name, label_sortkey
10793 FROM evergreen.located_uris($1, $2, $9) AS uris
10796 CASE WHEN ('ssub' = ANY ($5)) THEN
10798 name subscriptions,
10799 (SELECT XMLAGG(ssub) FROM (
10800 SELECT unapi.ssub(id,'xml','subscription','{}'::TEXT[], $3, $4, $6, $7, FALSE)
10801 FROM serial.subscription
10802 WHERE record_entry = $1
10806 CASE WHEN ('acp' = ANY ($5)) THEN
10808 name foreign_copies,
10809 (SELECT XMLAGG(acp) FROM (
10810 SELECT unapi.acp(p.target_copy,'xml','copy',evergreen.array_remove_item_by_value($5,'acp'), $3, $4, $6, $7, FALSE)
10811 FROM biblio.peer_bib_copy_map p
10812 JOIN asset.copy c ON (p.target_copy = c.id)
10813 WHERE NOT c.deleted AND p.peer_record = $1
10814 LIMIT ($6 -> 'acp')::INT
10815 OFFSET ($7 -> 'acp')::INT
10820 $F$ LANGUAGE SQL STABLE;
10822 CREATE OR REPLACE FUNCTION unapi.ssub ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
10826 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
10827 'tag:open-ils.org:U2@ssub/' || id AS id,
10828 'tag:open-ils.org:U2@aou/' || owning_lib AS owning_lib,
10829 start_date AS start, end_date AS end, expected_date_offset
10832 WHEN ('sdist' = ANY ($4)) THEN
10833 XMLELEMENT( name distributions,
10834 (SELECT XMLAGG(sdist) FROM (
10835 SELECT unapi.sdist( id, 'xml', 'distribution', evergreen.array_remove_item_by_value($4,'ssub'), $5, $6, $7, $8, FALSE)
10836 FROM serial.distribution
10837 WHERE subscription = ssub.id
10843 FROM serial.subscription ssub
10845 GROUP BY id, start_date, end_date, expected_date_offset, owning_lib;
10846 $F$ LANGUAGE SQL STABLE;
10848 CREATE OR REPLACE FUNCTION unapi.sdist ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
10852 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
10853 'tag:open-ils.org:U2@sdist/' || id AS id,
10854 'tag:open-ils.org:U2@acn/' || receive_call_number AS receive_call_number,
10855 'tag:open-ils.org:U2@acn/' || bind_call_number AS bind_call_number,
10856 unit_label_prefix, label, unit_label_suffix, summary_method
10858 unapi.aou( holding_lib, $2, 'holding_lib', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8),
10859 CASE WHEN subscription IS NOT NULL AND ('ssub' = ANY ($4)) THEN unapi.ssub( subscription, 'xml', 'subscription', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE) ELSE NULL END,
10861 WHEN ('sstr' = ANY ($4)) THEN
10862 XMLELEMENT( name streams,
10863 (SELECT XMLAGG(sstr) FROM (
10864 SELECT unapi.sstr( id, 'xml', 'stream', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE)
10866 WHERE distribution = sdist.id
10871 XMLELEMENT( name summaries,
10873 WHEN ('sbsum' = ANY ($4)) THEN
10874 (SELECT XMLAGG(sbsum) FROM (
10875 SELECT unapi.sbsum( id, 'xml', 'serial_summary', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE)
10876 FROM serial.basic_summary
10877 WHERE distribution = sdist.id
10882 WHEN ('sisum' = ANY ($4)) THEN
10883 (SELECT XMLAGG(sisum) FROM (
10884 SELECT unapi.sisum( id, 'xml', 'serial_summary', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE)
10885 FROM serial.index_summary
10886 WHERE distribution = sdist.id
10891 WHEN ('sssum' = ANY ($4)) THEN
10892 (SELECT XMLAGG(sssum) FROM (
10893 SELECT unapi.sssum( id, 'xml', 'serial_summary', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE)
10894 FROM serial.supplement_summary
10895 WHERE distribution = sdist.id
10901 FROM serial.distribution sdist
10903 GROUP BY id, label, unit_label_prefix, unit_label_suffix, holding_lib, summary_method, subscription, receive_call_number, bind_call_number;
10904 $F$ LANGUAGE SQL STABLE;
10906 CREATE OR REPLACE FUNCTION unapi.sstr ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
10910 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
10911 'tag:open-ils.org:U2@sstr/' || id AS id,
10914 CASE WHEN distribution IS NOT NULL AND ('sdist' = ANY ($4)) THEN unapi.sssum( distribution, 'xml', 'distribtion', evergreen.array_remove_item_by_value($4,'sstr'), $5, $6, $7, $8, FALSE) ELSE NULL END,
10916 WHEN ('sitem' = ANY ($4)) THEN
10917 XMLELEMENT( name items,
10918 (SELECT XMLAGG(sitem) FROM (
10919 SELECT unapi.sitem( id, 'xml', 'serial_item', evergreen.array_remove_item_by_value($4,'sstr'), $5, $6, $7, $8, FALSE)
10921 WHERE stream = sstr.id
10927 FROM serial.stream sstr
10929 GROUP BY id, routing_label, distribution;
10930 $F$ LANGUAGE SQL STABLE;
10932 CREATE OR REPLACE FUNCTION unapi.siss ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
10936 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
10937 'tag:open-ils.org:U2@siss/' || id AS id,
10938 create_date, edit_date, label, date_published,
10939 holding_code, holding_type, holding_link_id
10941 CASE WHEN subscription IS NOT NULL AND ('ssub' = ANY ($4)) THEN unapi.ssub( subscription, 'xml', 'subscription', evergreen.array_remove_item_by_value($4,'siss'), $5, $6, $7, $8, FALSE) ELSE NULL END,
10943 WHEN ('sitem' = ANY ($4)) THEN
10944 XMLELEMENT( name items,
10945 (SELECT XMLAGG(sitem) FROM (
10946 SELECT unapi.sitem( id, 'xml', 'serial_item', evergreen.array_remove_item_by_value($4,'siss'), $5, $6, $7, $8, FALSE)
10948 WHERE issuance = sstr.id
10954 FROM serial.issuance sstr
10956 GROUP BY id, create_date, edit_date, label, date_published, holding_code, holding_type, holding_link_id, subscription;
10957 $F$ LANGUAGE SQL STABLE;
10959 CREATE OR REPLACE FUNCTION unapi.sitem ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
10963 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
10964 'tag:open-ils.org:U2@sitem/' || id AS id,
10965 'tag:open-ils.org:U2@siss/' || issuance AS issuance,
10966 date_expected, date_received
10968 CASE WHEN issuance IS NOT NULL AND ('siss' = ANY ($4)) THEN unapi.siss( issuance, $2, 'issuance', evergreen.array_remove_item_by_value($4,'sitem'), $5, $6, $7, $8, FALSE) ELSE NULL END,
10969 CASE WHEN stream IS NOT NULL AND ('sstr' = ANY ($4)) THEN unapi.sstr( stream, $2, 'stream', evergreen.array_remove_item_by_value($4,'sitem'), $5, $6, $7, $8, FALSE) ELSE NULL END,
10970 CASE WHEN unit IS NOT NULL AND ('sunit' = ANY ($4)) THEN unapi.sunit( unit, $2, 'serial_unit', evergreen.array_remove_item_by_value($4,'sitem'), $5, $6, $7, $8, FALSE) ELSE NULL END,
10971 CASE WHEN uri IS NOT NULL AND ('auri' = ANY ($4)) THEN unapi.auri( uri, $2, 'uri', evergreen.array_remove_item_by_value($4,'sitem'), $5, $6, $7, $8, FALSE) ELSE NULL END
10972 -- XMLELEMENT( name notes,
10974 -- WHEN ('acpn' = ANY ($4)) THEN
10975 -- (SELECT XMLAGG(acpn) FROM (
10976 -- SELECT unapi.acpn( id, 'xml', 'copy_note', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8)
10977 -- FROM asset.copy_note
10978 -- WHERE owning_copy = cp.id AND pub
10984 FROM serial.item sitem
10986 $F$ LANGUAGE SQL STABLE;
10989 CREATE OR REPLACE FUNCTION unapi.sssum ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
10991 name serial_summary,
10993 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
10994 'tag:open-ils.org:U2@sbsum/' || id AS id,
10995 'sssum' AS type, generated_coverage, textual_holdings, show_generated
10997 CASE WHEN ('sdist' = ANY ($4)) THEN unapi.sdist( distribution, 'xml', 'distribtion', evergreen.array_remove_item_by_value($4,'ssum'), $5, $6, $7, $8, FALSE) ELSE NULL END
10999 FROM serial.supplement_summary ssum
11001 GROUP BY id, generated_coverage, textual_holdings, distribution, show_generated;
11002 $F$ LANGUAGE SQL STABLE;
11004 CREATE OR REPLACE FUNCTION unapi.sbsum ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
11006 name serial_summary,
11008 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
11009 'tag:open-ils.org:U2@sbsum/' || id AS id,
11010 'sbsum' AS type, generated_coverage, textual_holdings, show_generated
11012 CASE WHEN ('sdist' = ANY ($4)) THEN unapi.sdist( distribution, 'xml', 'distribtion', evergreen.array_remove_item_by_value($4,'ssum'), $5, $6, $7, $8, FALSE) ELSE NULL END
11014 FROM serial.basic_summary ssum
11016 GROUP BY id, generated_coverage, textual_holdings, distribution, show_generated;
11017 $F$ LANGUAGE SQL STABLE;
11019 CREATE OR REPLACE FUNCTION unapi.sisum ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
11021 name serial_summary,
11023 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
11024 'tag:open-ils.org:U2@sbsum/' || id AS id,
11025 'sisum' AS type, generated_coverage, textual_holdings, show_generated
11027 CASE WHEN ('sdist' = ANY ($4)) THEN unapi.sdist( distribution, 'xml', 'distribtion', evergreen.array_remove_item_by_value($4,'ssum'), $5, $6, $7, $8, FALSE) ELSE NULL END
11029 FROM serial.index_summary ssum
11031 GROUP BY id, generated_coverage, textual_holdings, distribution, show_generated;
11032 $F$ LANGUAGE SQL STABLE;
11035 CREATE OR REPLACE FUNCTION unapi.aou ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
11039 IF ename = 'circlib' THEN
11043 'http://open-ils.org/spec/actors/v1' AS xmlns,
11048 FROM actor.org_unit aou
11051 EXECUTE $$SELECT XMLELEMENT(
11052 name $$ || ename || $$,
11054 'http://open-ils.org/spec/actors/v1' AS xmlns,
11055 'tag:open-ils.org:U2@aou/' || id AS id,
11056 shortname, name, opac_visible
11059 FROM actor.org_unit aou
11060 WHERE id = $1 $$ INTO output USING obj_id;
11066 $F$ LANGUAGE PLPGSQL STABLE;
11068 CREATE OR REPLACE FUNCTION unapi.acl ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
11072 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
11076 label_prefix AS prefix,
11077 label_suffix AS suffix
11081 FROM asset.copy_location
11083 $F$ LANGUAGE SQL STABLE;
11085 CREATE OR REPLACE FUNCTION unapi.ccs ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
11089 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
11096 FROM config.copy_status
11098 $F$ LANGUAGE SQL STABLE;
11100 CREATE OR REPLACE FUNCTION unapi.acpn ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
11104 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
11105 create_date AS date,
11110 FROM asset.copy_note
11112 $F$ LANGUAGE SQL STABLE;
11114 CREATE OR REPLACE FUNCTION unapi.ascecm ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
11118 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
11124 FROM asset.stat_cat_entry asce
11125 JOIN asset.stat_cat sc ON (sc.id = asce.stat_cat)
11126 WHERE asce.id = $1;
11127 $F$ LANGUAGE SQL STABLE;
11129 CREATE OR REPLACE FUNCTION unapi.bmp ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
11131 name monograph_part,
11133 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
11134 'tag:open-ils.org:U2@bmp/' || id AS id,
11138 'tag:open-ils.org:U2@bre/' || record AS record
11141 WHEN ('acp' = ANY ($4)) THEN
11142 XMLELEMENT( name copies,
11143 (SELECT XMLAGG(acp) FROM (
11144 SELECT unapi.acp( cp.id, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'bmp'), $5, $6, $7, $8, FALSE)
11146 JOIN asset.copy_part_map cpm ON (cpm.target_copy = cp.id)
11147 WHERE cpm.part = $1
11148 AND cp.deleted IS FALSE
11149 ORDER BY COALESCE(cp.copy_number,0), cp.barcode
11150 LIMIT ($7 -> 'acp')::INT
11151 OFFSET ($8 -> 'acp')::INT
11157 CASE WHEN ('bre' = ANY ($4)) THEN unapi.bre( record, 'marcxml', 'record', evergreen.array_remove_item_by_value($4,'bmp'), $5, $6, $7, $8, FALSE) ELSE NULL END
11159 FROM biblio.monograph_part
11161 GROUP BY id, label, label_sortkey, record;
11162 $F$ LANGUAGE SQL STABLE;
11164 CREATE OR REPLACE FUNCTION unapi.acp ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
11168 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
11169 'tag:open-ils.org:U2@acp/' || id AS id, id AS copy_id,
11170 create_date, edit_date, copy_number, circulate, deposit,
11171 ref, holdable, deleted, deposit_amount, price, barcode,
11172 circ_modifier, circ_as_type, opac_visible, age_protect
11174 unapi.ccs( status, $2, 'status', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE),
11175 unapi.acl( location, $2, 'location', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE),
11176 unapi.aou( circ_lib, $2, 'circ_lib', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8),
11177 unapi.aou( circ_lib, $2, 'circlib', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8),
11178 CASE WHEN ('acn' = ANY ($4)) THEN unapi.acn( call_number, $2, 'call_number', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE) ELSE NULL END,
11180 WHEN ('acpn' = ANY ($4)) THEN
11181 XMLELEMENT( name copy_notes,
11182 (SELECT XMLAGG(acpn) FROM (
11183 SELECT unapi.acpn( id, 'xml', 'copy_note', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
11184 FROM asset.copy_note
11185 WHERE owning_copy = cp.id AND pub
11191 WHEN ('ascecm' = ANY ($4)) THEN
11192 XMLELEMENT( name statcats,
11193 (SELECT XMLAGG(ascecm) FROM (
11194 SELECT unapi.ascecm( stat_cat_entry, 'xml', 'statcat', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
11195 FROM asset.stat_cat_entry_copy_map
11196 WHERE owning_copy = cp.id
11202 WHEN ('bre' = ANY ($4)) THEN
11203 XMLELEMENT( name foreign_records,
11204 (SELECT XMLAGG(bre) FROM (
11205 SELECT unapi.bre(peer_record,'marcxml','record','{}'::TEXT[], $5, $6, $7, $8, FALSE)
11206 FROM biblio.peer_bib_copy_map
11207 WHERE target_copy = cp.id
11214 WHEN ('bmp' = ANY ($4)) THEN
11215 XMLELEMENT( name monograph_parts,
11216 (SELECT XMLAGG(bmp) FROM (
11217 SELECT unapi.bmp( part, 'xml', 'monograph_part', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
11218 FROM asset.copy_part_map
11219 WHERE target_copy = cp.id
11225 WHEN ('circ' = ANY ($4)) THEN
11226 XMLELEMENT( name current_circulation,
11227 (SELECT XMLAGG(circ) FROM (
11228 SELECT unapi.circ( id, 'xml', 'circ', evergreen.array_remove_item_by_value($4,'circ'), $5, $6, $7, $8, FALSE)
11229 FROM action.circulation
11230 WHERE target_copy = cp.id
11231 AND checkin_time IS NULL
11239 AND cp.deleted IS FALSE
11240 GROUP BY id, status, location, circ_lib, call_number, create_date,
11241 edit_date, copy_number, circulate, deposit, ref, holdable,
11242 deleted, deposit_amount, price, barcode, circ_modifier,
11243 circ_as_type, opac_visible, age_protect;
11244 $F$ LANGUAGE SQL STABLE;
11246 CREATE OR REPLACE FUNCTION unapi.sunit ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
11250 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
11251 'tag:open-ils.org:U2@acp/' || id AS id, id AS copy_id,
11252 create_date, edit_date, copy_number, circulate, deposit,
11253 ref, holdable, deleted, deposit_amount, price, barcode,
11254 circ_modifier, circ_as_type, opac_visible, age_protect,
11255 status_changed_time, floating, mint_condition,
11256 detailed_contents, sort_key, summary_contents, cost
11258 unapi.ccs( status, $2, 'status', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($4,'acp'),'sunit'), $5, $6, $7, $8, FALSE),
11259 unapi.acl( location, $2, 'location', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($4,'acp'),'sunit'), $5, $6, $7, $8, FALSE),
11260 unapi.aou( circ_lib, $2, 'circ_lib', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($4,'acp'),'sunit'), $5, $6, $7, $8),
11261 unapi.aou( circ_lib, $2, 'circlib', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($4,'acp'),'sunit'), $5, $6, $7, $8),
11262 CASE WHEN ('acn' = ANY ($4)) THEN unapi.acn( call_number, $2, 'call_number', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE) ELSE NULL END,
11263 XMLELEMENT( name copy_notes,
11265 WHEN ('acpn' = ANY ($4)) THEN
11266 (SELECT XMLAGG(acpn) FROM (
11267 SELECT unapi.acpn( id, 'xml', 'copy_note', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($4,'acp'),'sunit'), $5, $6, $7, $8, FALSE)
11268 FROM asset.copy_note
11269 WHERE owning_copy = cp.id AND pub
11274 XMLELEMENT( name statcats,
11276 WHEN ('ascecm' = ANY ($4)) THEN
11277 (SELECT XMLAGG(ascecm) FROM (
11278 SELECT unapi.ascecm( stat_cat_entry, 'xml', 'statcat', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
11279 FROM asset.stat_cat_entry_copy_map
11280 WHERE owning_copy = cp.id
11285 XMLELEMENT( name foreign_records,
11287 WHEN ('bre' = ANY ($4)) THEN
11288 (SELECT XMLAGG(bre) FROM (
11289 SELECT unapi.bre(peer_record,'marcxml','record','{}'::TEXT[], $5, $6, $7, $8, FALSE)
11290 FROM biblio.peer_bib_copy_map
11291 WHERE target_copy = cp.id
11297 WHEN ('bmp' = ANY ($4)) THEN
11298 XMLELEMENT( name monograph_parts,
11299 (SELECT XMLAGG(bmp) FROM (
11300 SELECT unapi.bmp( part, 'xml', 'monograph_part', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
11301 FROM asset.copy_part_map
11302 WHERE target_copy = cp.id
11308 WHEN ('circ' = ANY ($4)) THEN
11309 XMLELEMENT( name current_circulation,
11310 (SELECT XMLAGG(circ) FROM (
11311 SELECT unapi.circ( id, 'xml', 'circ', evergreen.array_remove_item_by_value($4,'circ'), $5, $6, $7, $8, FALSE)
11312 FROM action.circulation
11313 WHERE target_copy = cp.id
11314 AND checkin_time IS NULL
11320 FROM serial.unit cp
11322 AND cp.deleted IS FALSE
11323 GROUP BY id, status, location, circ_lib, call_number, create_date,
11324 edit_date, copy_number, circulate, floating, mint_condition,
11325 deposit, ref, holdable, deleted, deposit_amount, price,
11326 barcode, circ_modifier, circ_as_type, opac_visible,
11327 status_changed_time, detailed_contents, sort_key,
11328 summary_contents, cost, age_protect;
11329 $F$ LANGUAGE SQL STABLE;
11331 CREATE OR REPLACE FUNCTION unapi.acn ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
11335 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
11336 'tag:open-ils.org:U2@acn/' || acn.id AS id,
11337 acn.id AS vol_id, o.shortname AS lib,
11338 o.opac_visible AS opac_visible,
11339 deleted, label, label_sortkey, label_class, record
11341 unapi.aou( owning_lib, $2, 'owning_lib', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8),
11343 WHEN ('acp' = ANY ($4)) THEN
11344 CASE WHEN $6 IS NOT NULL THEN
11345 XMLELEMENT( name copies,
11346 (SELECT XMLAGG(acp ORDER BY rank_avail) FROM (
11347 SELECT unapi.acp( cp.id, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE),
11348 evergreen.rank_cp_status(cp.status) AS rank_avail
11350 JOIN actor.org_unit_descendants( (SELECT id FROM actor.org_unit WHERE shortname = $5), $6) aoud ON (cp.circ_lib = aoud.id)
11351 WHERE cp.call_number = acn.id
11352 AND cp.deleted IS FALSE
11353 ORDER BY rank_avail, COALESCE(cp.copy_number,0), cp.barcode
11354 LIMIT ($7 -> 'acp')::INT
11355 OFFSET ($8 -> 'acp')::INT
11359 XMLELEMENT( name copies,
11360 (SELECT XMLAGG(acp ORDER BY rank_avail) FROM (
11361 SELECT unapi.acp( cp.id, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE),
11362 evergreen.rank_cp_status(cp.status) AS rank_avail
11364 JOIN actor.org_unit_descendants( (SELECT id FROM actor.org_unit WHERE shortname = $5) ) aoud ON (cp.circ_lib = aoud.id)
11365 WHERE cp.call_number = acn.id
11366 AND cp.deleted IS FALSE
11367 ORDER BY rank_avail, COALESCE(cp.copy_number,0), cp.barcode
11368 LIMIT ($7 -> 'acp')::INT
11369 OFFSET ($8 -> 'acp')::INT
11377 (SELECT XMLAGG(auri) FROM (SELECT unapi.auri(uri,'xml','uri', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE) FROM asset.uri_call_number_map WHERE call_number = acn.id)x)
11379 unapi.acnp( acn.prefix, 'marcxml', 'prefix', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE),
11380 unapi.acns( acn.suffix, 'marcxml', 'suffix', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE),
11381 CASE WHEN ('bre' = ANY ($4)) THEN unapi.bre( acn.record, 'marcxml', 'record', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE) ELSE NULL END
11383 FROM asset.call_number acn
11384 JOIN actor.org_unit o ON (o.id = acn.owning_lib)
11386 AND acn.deleted IS FALSE
11387 GROUP BY acn.id, o.shortname, o.opac_visible, deleted, label, label_sortkey, label_class, owning_lib, record, acn.prefix, acn.suffix;
11388 $F$ LANGUAGE SQL STABLE;
11390 CREATE OR REPLACE FUNCTION unapi.acnp ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
11392 name call_number_prefix,
11394 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
11397 'tag:open-ils.org:U2@aou/' || owning_lib AS owning_lib,
11401 FROM asset.call_number_prefix
11403 $F$ LANGUAGE SQL STABLE;
11405 CREATE OR REPLACE FUNCTION unapi.acns ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
11407 name call_number_suffix,
11409 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
11412 'tag:open-ils.org:U2@aou/' || owning_lib AS owning_lib,
11416 FROM asset.call_number_suffix
11418 $F$ LANGUAGE SQL STABLE;
11420 CREATE OR REPLACE FUNCTION unapi.auri ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
11424 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
11425 'tag:open-ils.org:U2@auri/' || uri.id AS id,
11431 WHEN ('acn' = ANY ($4)) THEN
11432 XMLELEMENT( name copies,
11433 (SELECT XMLAGG(acn) FROM (SELECT unapi.acn( call_number, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'auri'), $5, $6, $7, $8, FALSE) FROM asset.uri_call_number_map WHERE uri = uri.id)x)
11440 GROUP BY uri.id, use_restriction, href, label;
11441 $F$ LANGUAGE SQL STABLE;
11443 CREATE OR REPLACE FUNCTION unapi.mra ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
11447 CASE WHEN $9 THEN 'http://open-ils.org/spec/indexing/v1' ELSE NULL END AS xmlns,
11448 'tag:open-ils.org:U2@mra/' || mra.id AS id,
11449 'tag:open-ils.org:U2@bre/' || mra.id AS record
11451 (SELECT XMLAGG(foo.y)
11452 FROM (SELECT XMLELEMENT(
11456 cvm.value AS "coded-value",
11462 FROM EACH(mra.attrs) AS x
11463 JOIN config.record_attr_definition rad ON (x.key = rad.name)
11464 LEFT JOIN config.coded_value_map cvm ON (cvm.ctype = x.key AND code = x.value)
11468 FROM metabib.record_attr mra
11470 $F$ LANGUAGE SQL STABLE;
11472 CREATE OR REPLACE FUNCTION unapi.circ (obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT DEFAULT '-', depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
11476 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
11477 'tag:open-ils.org:U2@circ/' || id AS id,
11481 CASE WHEN ('aou' = ANY ($4)) THEN unapi.aou( circ_lib, $2, 'circ_lib', evergreen.array_remove_item_by_value($4,'circ'), $5, $6, $7, $8, FALSE) ELSE NULL END,
11482 CASE WHEN ('acp' = ANY ($4)) THEN unapi.acp( circ_lib, $2, 'target_copy', evergreen.array_remove_item_by_value($4,'circ'), $5, $6, $7, $8, FALSE) ELSE NULL END
11484 FROM action.circulation
11486 $F$ LANGUAGE SQL STABLE;
11490 -- Some test queries
11492 SELECT unapi.memoize( 'bre', 1,'mods32','','{holdings_xml,acp}'::TEXT[], 'SYS1');
11493 SELECT unapi.memoize( 'bre', 1,'marcxml','','{holdings_xml,acp}'::TEXT[], 'SYS1');
11494 SELECT unapi.memoize( 'bre', 1,'holdings_xml','','{holdings_xml,acp}'::TEXT[], 'SYS1');
11496 SELECT unapi.biblio_record_entry_feed('{1}'::BIGINT[],'mods32','{holdings_xml,acp}'::TEXT[],'SYS1',NULL,'acn=>1',NULL, NULL,NULL,NULL,NULL,'http://c64/opac/extras/unapi', '<totalResults xmlns="http://a9.com/-/spec/opensearch/1.1/">2</totalResults><startIndex xmlns="http://a9.com/-/spec/opensearch/1.1/">1</startIndex><itemsPerPage xmlns="http://a9.com/-/spec/opensearch/1.1/">10</itemsPerPage>');
11498 SELECT unapi.biblio_record_entry_feed('{7209,7394}'::BIGINT[],'marcxml','{}'::TEXT[],'SYS1',NULL,'acn=>1',NULL, NULL,NULL,NULL,NULL,'http://fulfillment2.esilibrary.com/opac/extras/unapi', '<totalResults xmlns="http://a9.com/-/spec/opensearch/1.1/">2</totalResults><startIndex xmlns="http://a9.com/-/spec/opensearch/1.1/">1</startIndex><itemsPerPage xmlns="http://a9.com/-/spec/opensearch/1.1/">10</itemsPerPage>');
11499 EXPLAIN ANALYZE SELECT unapi.biblio_record_entry_feed('{7209,7394}'::BIGINT[],'marcxml','{}'::TEXT[],'SYS1',NULL,'acn=>1',NULL, NULL,NULL,NULL,NULL,'http://fulfillment2.esilibrary.com/opac/extras/unapi', '<totalResults xmlns="http://a9.com/-/spec/opensearch/1.1/">2</totalResults><startIndex xmlns="http://a9.com/-/spec/opensearch/1.1/">1</startIndex><itemsPerPage xmlns="http://a9.com/-/spec/opensearch/1.1/">10</itemsPerPage>');
11500 EXPLAIN ANALYZE SELECT unapi.biblio_record_entry_feed('{7209,7394}'::BIGINT[],'marcxml','{holdings_xml}'::TEXT[],'SYS1',NULL,'acn=>1',NULL, NULL,NULL,NULL,NULL,'http://fulfillment2.esilibrary.com/opac/extras/unapi', '<totalResults xmlns="http://a9.com/-/spec/opensearch/1.1/">2</totalResults><startIndex xmlns="http://a9.com/-/spec/opensearch/1.1/">1</startIndex><itemsPerPage xmlns="http://a9.com/-/spec/opensearch/1.1/">10</itemsPerPage>');
11501 EXPLAIN ANALYZE SELECT unapi.biblio_record_entry_feed('{7209,7394}'::BIGINT[],'mods32','{holdings_xml}'::TEXT[],'SYS1',NULL,'acn=>1',NULL, NULL,NULL,NULL,NULL,'http://fulfillment2.esilibrary.com/opac/extras/unapi', '<totalResults xmlns="http://a9.com/-/spec/opensearch/1.1/">2</totalResults><startIndex xmlns="http://a9.com/-/spec/opensearch/1.1/">1</startIndex><itemsPerPage xmlns="http://a9.com/-/spec/opensearch/1.1/">10</itemsPerPage>');
11503 SELECT unapi.biblio_record_entry_feed('{216}'::BIGINT[],'marcxml','{}'::TEXT[], 'BR1');
11504 EXPLAIN ANALYZE SELECT unapi.bre(216,'marcxml','record','{holdings_xml,bre.unapi}'::TEXT[], 'BR1');
11505 EXPLAIN ANALYZE SELECT unapi.bre(216,'holdings_xml','record','{}'::TEXT[], 'BR1');
11506 EXPLAIN ANALYZE SELECT unapi.holdings_xml(216,4,'BR1',2,'{bre}'::TEXT[]);
11507 EXPLAIN ANALYZE SELECT unapi.bre(216,'mods32','record','{}'::TEXT[], 'BR1');
11509 -- Limit to 5 call numbers, 5 copies, with a preferred library of 4 (BR1), in SYS2 at a depth of 0
11510 EXPLAIN ANALYZE SELECT unapi.bre(36,'marcxml','record','{holdings_xml,mra,acp,acnp,acns,bmp}','SYS2',0,'acn=>5,acp=>5',NULL,TRUE,4);
11515 SELECT evergreen.upgrade_deps_block_check('0692', :eg_version);
11517 INSERT INTO config.org_unit_setting_type
11518 (name, label, description, grp, datatype)
11520 'circ.fines.charge_when_closed',
11522 'circ.fines.charge_when_closed',
11523 'Charge fines on overdue circulations when closed',
11528 'circ.fines.charge_when_closed',
11529 'Normally, fines are not charged when a library is closed. When set to True, fines will be charged during scheduled closings and normal weekly closed days.',
11537 SELECT evergreen.upgrade_deps_block_check('0694', :eg_version);
11539 INSERT into config.org_unit_setting_type
11540 ( name, grp, label, description, datatype, fm_class ) VALUES
11542 ( 'ui.patron.edit.au.prefix.require', 'gui',
11543 oils_i18n_gettext('ui.patron.edit.au.prefix.require',
11544 'Require prefix field on patron registration',
11546 oils_i18n_gettext('ui.patron.edit.au.prefix.require',
11547 'The prefix field will be required on the patron registration screen.',
11548 'coust', 'description'),
11551 ,( 'ui.patron.edit.au.prefix.show', 'gui',
11552 oils_i18n_gettext('ui.patron.edit.au.prefix.show',
11553 'Show prefix field on patron registration',
11555 oils_i18n_gettext('ui.patron.edit.au.prefix.show',
11556 'The prefix field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.',
11557 'coust', 'description'),
11560 ,( 'ui.patron.edit.au.prefix.suggest', 'gui',
11561 oils_i18n_gettext('ui.patron.edit.au.prefix.suggest',
11562 'Suggest prefix field on patron registration',
11564 oils_i18n_gettext('ui.patron.edit.au.prefix.suggest',
11565 'The prefix field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.',
11566 'coust', 'description'),
11571 -- Evergreen DB patch 0695.schema.custom_toolbars.sql
11573 -- FIXME: insert description of change, if needed
11576 -- check whether patch can be applied
11577 SELECT evergreen.upgrade_deps_block_check('0695', :eg_version);
11579 CREATE TABLE actor.toolbar (
11580 id BIGSERIAL PRIMARY KEY,
11581 ws INT REFERENCES actor.workstation (id) ON DELETE CASCADE,
11582 org INT REFERENCES actor.org_unit (id) ON DELETE CASCADE,
11583 usr INT REFERENCES actor.usr (id) ON DELETE CASCADE,
11584 label TEXT NOT NULL,
11585 layout TEXT NOT NULL,
11586 CONSTRAINT only_one_type CHECK (
11587 (ws IS NOT NULL AND COALESCE(org,usr) IS NULL) OR
11588 (org IS NOT NULL AND COALESCE(ws,usr) IS NULL) OR
11589 (usr IS NOT NULL AND COALESCE(org,ws) IS NULL)
11591 CONSTRAINT layout_must_be_json CHECK ( is_json(layout) )
11593 CREATE UNIQUE INDEX label_once_per_ws ON actor.toolbar (ws, label) WHERE ws IS NOT NULL;
11594 CREATE UNIQUE INDEX label_once_per_org ON actor.toolbar (org, label) WHERE org IS NOT NULL;
11595 CREATE UNIQUE INDEX label_once_per_usr ON actor.toolbar (usr, label) WHERE usr IS NOT NULL;
11597 -- this one unrelated to toolbars but is a gap in the upgrade scripts
11598 INSERT INTO permission.perm_list ( id, code, description )
11601 'IMPORT_AUTHORITY_MARC',
11604 'Allows a user to create new authority records',
11610 FROM permission.perm_list
11615 INSERT INTO permission.perm_list ( id, code, description ) VALUES (
11620 'Allows a user to create, edit, and delete custom toolbars',
11626 -- Don't want to assume stock perm groups in an upgrade script, but here for ease of testing
11627 -- INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable) SELECT pgt.id, perm.id, aout.depth, FALSE FROM permission.grp_tree pgt, permission.perm_list perm, actor.org_unit_type aout WHERE pgt.name = 'Staff' AND aout.name = 'Branch' AND perm.code = 'ADMIN_TOOLBAR';
11629 INSERT INTO actor.toolbar(org,label,layout) VALUES
11630 ( 1, 'circ', '["circ_checkout","circ_checkin","toolbarseparator.1","search_opac","copy_status","toolbarseparator.2","patron_search","patron_register","toolbarspacer.3","hotkeys_toggle"]' ),
11631 ( 1, 'cat', '["circ_checkin","toolbarseparator.1","search_opac","copy_status","toolbarseparator.2","create_marc","authority_manage","retrieve_last_record","toolbarspacer.3","hotkeys_toggle"]' );
11633 -- delete from permission.grp_perm_map where perm in (select id from permission.perm_list where code ~ 'TOOLBAR'); delete from permission.perm_list where code ~ 'TOOLBAR'; drop table actor.toolbar ;
11635 -- Evergreen DB patch 0696.no_plperl.sql
11637 -- FIXME: insert description of change, if needed
11640 -- check whether patch can be applied
11641 SELECT evergreen.upgrade_deps_block_check('0696', :eg_version);
11643 -- Re-create these as plperlu instead of plperl
11644 CREATE OR REPLACE FUNCTION auditor.set_audit_info(INT, INT) RETURNS VOID AS $$
11645 $_SHARED{"eg_audit_user"} = $_[0];
11646 $_SHARED{"eg_audit_ws"} = $_[1];
11647 $$ LANGUAGE plperlu;
11649 CREATE OR REPLACE FUNCTION auditor.get_audit_info() RETURNS TABLE (eg_user INT, eg_ws INT) AS $$
11650 return [{eg_user => $_SHARED{"eg_audit_user"}, eg_ws => $_SHARED{"eg_audit_ws"}}];
11651 $$ LANGUAGE plperlu;
11653 CREATE OR REPLACE FUNCTION auditor.clear_audit_info() RETURNS VOID AS $$
11654 delete($_SHARED{"eg_audit_user"});
11655 delete($_SHARED{"eg_audit_ws"});
11656 $$ LANGUAGE plperlu;
11658 -- Evergreen DB patch 0697.data.place_currently_unfillable_hold.sql
11660 -- FIXME: insert description of change, if needed
11663 -- check whether patch can be applied
11664 SELECT evergreen.upgrade_deps_block_check('0697', :eg_version);
11666 -- FIXME: add/check SQL statements to perform the upgrade
11667 INSERT INTO permission.perm_list ( id, code, description ) VALUES
11668 ( 524, 'PLACE_UNFILLABLE_HOLD', oils_i18n_gettext( 524,
11669 'Allows a user to place a hold that cannot currently be filled.', 'ppl', 'description' ));
11671 -- Evergreen DB patch 0698.hold_default_pickup.sql
11673 -- FIXME: insert description of change, if needed
11676 -- check whether patch can be applied
11677 SELECT evergreen.upgrade_deps_block_check('0698', :eg_version);
11679 INSERT INTO config.usr_setting_type (name,opac_visible,label,description,datatype)
11680 VALUES ('opac.default_pickup_location', TRUE, 'Default Hold Pickup Location', 'Default location for holds pickup', 'integer');
11682 SELECT evergreen.upgrade_deps_block_check('0699', :eg_version);
11684 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype, grp )
11686 'ui.hide_copy_editor_fields',
11688 'ui.hide_copy_editor_fields',
11689 'GUI: Hide these fields within the Item Attribute Editor',
11694 'ui.hide_copy_editor_fields',
11695 'This setting may be best maintained with the dedicated configuration'
11696 || ' interface within the Item Attribute Editor. However, here it'
11697 || ' shows up as comma separated list of field identifiers to hide.',
11706 SELECT evergreen.upgrade_deps_block_check('0700', :eg_version);
11707 SELECT evergreen.upgrade_deps_block_check('0706', :eg_version);
11708 SELECT evergreen.upgrade_deps_block_check('0710', :eg_version);
11711 CREATE OR REPLACE FUNCTION evergreen.could_be_serial_holding_code(TEXT) RETURNS BOOL AS $$
11716 my $holding_code = (new JSON::XS)->decode(shift);
11717 new MARC::Field('999', @$holding_code);
11720 $$ LANGUAGE PLPERLU;
11722 -- This throws away data, but only data that causes breakage anyway.
11723 UPDATE serial.issuance SET holding_code = NULL WHERE NOT could_be_serial_holding_code(holding_code);
11725 -- If we don't do this, we have unprocessed triggers and we can't alter the table
11726 SET CONSTRAINTS serial.issuance_caption_and_pattern_fkey IMMEDIATE;
11728 ALTER TABLE serial.issuance
11729 DROP CONSTRAINT IF EXISTS issuance_holding_code_check;
11731 ALTER TABLE serial.issuance ADD CHECK (holding_code IS NULL OR could_be_serial_holding_code(holding_code));
11733 INSERT INTO config.internal_flag (name, value, enabled) VALUES (
11734 'serial.rematerialize_on_same_holding_code', NULL, FALSE
11737 INSERT INTO config.org_unit_setting_type (
11738 name, label, grp, description, datatype
11740 'serial.default_display_grouping',
11741 'Default display grouping for serials distributions presented in the OPAC.',
11743 'Default display grouping for serials distributions presented in the OPAC. This can be "enum" or "chron".',
11747 ALTER TABLE serial.distribution
11748 ADD COLUMN display_grouping TEXT NOT NULL DEFAULT 'chron'
11749 CHECK (display_grouping IN ('enum', 'chron'));
11751 -- why didn't we just make one summary table in the first place?
11752 CREATE VIEW serial.any_summary AS
11754 'basic' AS summary_type, id, distribution,
11755 generated_coverage, textual_holdings, show_generated
11756 FROM serial.basic_summary
11759 'index' AS summary_type, id, distribution,
11760 generated_coverage, textual_holdings, show_generated
11761 FROM serial.index_summary
11764 'supplement' AS summary_type, id, distribution,
11765 generated_coverage, textual_holdings, show_generated
11766 FROM serial.supplement_summary ;
11769 -- Given the IDs of two rows in actor.org_unit, *the second being an ancestor
11770 -- of the first*, return in array form the path from the ancestor to the
11771 -- descendant, with each point in the path being an org_unit ID. This is
11772 -- useful for sorting org_units by their position in a depth-first (display
11773 -- order) representation of the tree.
11775 -- This breaks with the precedent set by actor.org_unit_full_path() and others,
11776 -- and gets the parameters "backwards," but otherwise this function would
11777 -- not be very usable within json_query.
11778 CREATE OR REPLACE FUNCTION actor.org_unit_simple_path(INT, INT)
11779 RETURNS INT[] AS $$
11780 WITH RECURSIVE descendant_depth(id, path) AS (
11783 FROM actor.org_unit aou
11784 JOIN actor.org_unit_type aout ON (aout.id = aou.ou_type)
11788 dd.path || ARRAY[aou.id]
11789 FROM actor.org_unit aou
11790 JOIN actor.org_unit_type aout ON (aout.id = aou.ou_type)
11791 JOIN descendant_depth dd ON (dd.id = aou.parent_ou)
11793 FROM actor.org_unit aou
11794 JOIN descendant_depth dd USING (id)
11795 WHERE aou.id = $1 ORDER BY dd.path;
11796 $$ LANGUAGE SQL STABLE;
11798 CREATE TABLE serial.materialized_holding_code (
11799 id BIGSERIAL PRIMARY KEY,
11800 issuance INTEGER NOT NULL REFERENCES serial.issuance (id) ON DELETE CASCADE,
11805 CREATE OR REPLACE FUNCTION serial.materialize_holding_code() RETURNS TRIGGER
11812 if (not defined $_TD->{new}{holding_code}) {
11813 elog(WARNING, 'NULL in "holding_code" column of serial.issuance allowed for now, but may not be useful');
11817 # Do nothing if holding_code has not changed...
11819 if ($_TD->{new}{holding_code} eq $_TD->{old}{holding_code}) {
11820 # ... unless the following internal flag is set.
11822 my $flag_rv = spi_exec_query(q{
11823 SELECT * FROM config.internal_flag
11824 WHERE name = 'serial.rematerialize_on_same_holding_code' AND enabled
11826 return unless $flag_rv->{processed};
11830 my $holding_code = (new JSON::XS)->decode($_TD->{new}{holding_code});
11832 my $field = new MARC::Field('999', @$holding_code); # tag doesnt matter
11834 my $dstmt = spi_prepare(
11835 'DELETE FROM serial.materialized_holding_code WHERE issuance = $1',
11838 spi_exec_prepared($dstmt, $_TD->{new}{id});
11840 my $istmt = spi_prepare(
11842 INSERT INTO serial.materialized_holding_code (
11843 issuance, subfield, value
11844 ) VALUES ($1, $2, $3)
11845 }, qw{INT CHAR TEXT}
11848 foreach ($field->subfields) {
11859 $func$ LANGUAGE 'plperlu';
11861 CREATE INDEX assist_holdings_display
11862 ON serial.materialized_holding_code (issuance, subfield);
11864 CREATE TRIGGER materialize_holding_code
11865 AFTER INSERT OR UPDATE ON serial.issuance
11866 FOR EACH ROW EXECUTE PROCEDURE serial.materialize_holding_code() ;
11868 -- starting here, we materialize all existing holding codes.
11870 UPDATE config.internal_flag
11872 WHERE name = 'serial.rematerialize_on_same_holding_code';
11874 UPDATE serial.issuance SET holding_code = holding_code;
11876 UPDATE config.internal_flag
11877 SET enabled = FALSE
11878 WHERE name = 'serial.rematerialize_on_same_holding_code';
11880 -- finish holding code materialization process
11882 -- fix up missing holding_code fields from serial.issuance
11883 UPDATE serial.issuance siss
11884 SET holding_type = scap.type
11885 FROM serial.caption_and_pattern scap
11886 WHERE scap.id = siss.caption_and_pattern AND siss.holding_type IS NULL;
11889 -- Evergreen DB patch 0701.schema.patron_stat_category_enhancements.sql
11891 -- Enables users to set patron statistical categories as required,
11892 -- whether or not users can input free text for the category value.
11893 -- Enables administrators to set an entry as the default for any
11894 -- given patron statistical category and org unit.
11897 -- check whether patch can be applied
11898 SELECT evergreen.upgrade_deps_block_check('0701', :eg_version);
11902 CREATE TABLE actor.stat_cat_entry_default (
11903 id SERIAL PRIMARY KEY,
11904 stat_cat_entry INT NOT NULL REFERENCES actor.stat_cat_entry (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
11905 stat_cat INT NOT NULL REFERENCES actor.stat_cat (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
11906 owner INT NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
11907 CONSTRAINT sced_once_per_owner UNIQUE (stat_cat,owner)
11910 COMMENT ON TABLE actor.stat_cat_entry_default IS $$
11911 User Statistical Category Default Entry
11913 A library may choose one of the stat_cat entries to be the
11917 -- Add columns to existing tables
11919 -- Patron stat cat required column
11920 ALTER TABLE actor.stat_cat
11921 ADD COLUMN required BOOL NOT NULL DEFAULT FALSE;
11923 -- Patron stat cat allow_freetext column
11924 ALTER TABLE actor.stat_cat
11925 ADD COLUMN allow_freetext BOOL NOT NULL DEFAULT TRUE;
11929 INSERT INTO permission.perm_list ( id, code, description ) VALUES
11930 ( 525, 'CREATE_PATRON_STAT_CAT_ENTRY_DEFAULT', oils_i18n_gettext( 525,
11931 'User may set a default entry in a patron statistical category', 'ppl', 'description' )),
11932 ( 526, 'UPDATE_PATRON_STAT_CAT_ENTRY_DEFAULT', oils_i18n_gettext( 526,
11933 'User may reset a default entry in a patron statistical category', 'ppl', 'description' )),
11934 ( 527, 'DELETE_PATRON_STAT_CAT_ENTRY_DEFAULT', oils_i18n_gettext( 527,
11935 'User may unset a default entry in a patron statistical category', 'ppl', 'description' ));
11937 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable)
11939 pgt.id, perm.id, aout.depth, TRUE
11941 permission.grp_tree pgt,
11942 permission.perm_list perm,
11943 actor.org_unit_type aout
11945 pgt.name = 'Circulation Administrator' AND
11946 aout.name = 'System' AND
11947 perm.code IN ('CREATE_PATRON_STAT_CAT_ENTRY_DEFAULT', 'DELETE_PATRON_STAT_CAT_ENTRY_DEFAULT');
11950 SELECT evergreen.upgrade_deps_block_check('0702', :eg_version);
11952 INSERT INTO config.global_flag (name, enabled, label)
11954 'opac.org_unit.non_inherited_visibility',
11957 'opac.org_unit.non_inherited_visibility',
11958 'Org Units Do Not Inherit Visibility',
11964 CREATE TYPE actor.org_unit_custom_tree_purpose AS ENUM ('opac');
11966 CREATE TABLE actor.org_unit_custom_tree (
11967 id SERIAL PRIMARY KEY,
11968 active BOOLEAN DEFAULT FALSE,
11969 purpose actor.org_unit_custom_tree_purpose NOT NULL DEFAULT 'opac' UNIQUE
11972 CREATE TABLE actor.org_unit_custom_tree_node (
11973 id SERIAL PRIMARY KEY,
11974 tree INTEGER REFERENCES actor.org_unit_custom_tree (id) DEFERRABLE INITIALLY DEFERRED,
11975 org_unit INTEGER NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
11976 parent_node INTEGER REFERENCES actor.org_unit_custom_tree_node (id) DEFERRABLE INITIALLY DEFERRED,
11977 sibling_order INTEGER NOT NULL DEFAULT 0,
11978 CONSTRAINT aouctn_once_per_org UNIQUE (tree, org_unit)
11984 DELETE FROM config.global_flag WHERE name = 'opac.org_unit.non_inheritied_visibility';
11985 DROP TABLE actor.org_unit_custom_tree_node;
11986 DROP TABLE actor.org_unit_custom_tree;
11987 DROP TYPE actor.org_unit_custom_tree_purpose;
11991 -- Evergreen DB patch 0704.schema.query_parser_fts.sql
11993 -- Add pref_ou query filter for preferred library searching
11996 -- check whether patch can be applied
11997 SELECT evergreen.upgrade_deps_block_check('0704', :eg_version);
11999 -- Create the new 11-parameter function, featuring param_pref_ou
12000 CREATE OR REPLACE FUNCTION search.query_parser_fts (
12002 param_search_ou INT,
12005 param_statuses INT[],
12006 param_locations INT[],
12012 param_pref_ou INT DEFAULT NULL
12013 ) RETURNS SETOF search.search_result AS $func$
12016 current_res search.search_result%ROWTYPE;
12017 search_org_list INT[];
12018 luri_org_list INT[];
12019 tmp_int_list INT[];
12026 core_result RECORD;
12027 core_cursor REFCURSOR;
12028 core_rel_query TEXT;
12030 total_count INT := 0;
12031 check_count INT := 0;
12032 deleted_count INT := 0;
12033 visible_count INT := 0;
12034 excluded_count INT := 0;
12038 check_limit := COALESCE( param_check, 1000 );
12039 core_limit := COALESCE( param_limit, 25000 );
12040 core_offset := COALESCE( param_offset, 0 );
12042 -- core_skip_chk := COALESCE( param_skip_chk, 1 );
12044 IF param_search_ou > 0 THEN
12045 IF param_depth IS NOT NULL THEN
12046 SELECT array_accum(distinct id) INTO search_org_list FROM actor.org_unit_descendants( param_search_ou, param_depth );
12048 SELECT array_accum(distinct id) INTO search_org_list FROM actor.org_unit_descendants( param_search_ou );
12051 SELECT array_accum(distinct id) INTO luri_org_list FROM actor.org_unit_ancestors( param_search_ou );
12053 ELSIF param_search_ou < 0 THEN
12054 SELECT array_accum(distinct org_unit) INTO search_org_list FROM actor.org_lasso_map WHERE lasso = -param_search_ou;
12056 FOR tmp_int IN SELECT * FROM UNNEST(search_org_list) LOOP
12057 SELECT array_accum(distinct id) INTO tmp_int_list FROM actor.org_unit_ancestors( tmp_int );
12058 luri_org_list := luri_org_list || tmp_int_list;
12061 SELECT array_accum(DISTINCT x.id) INTO luri_org_list FROM UNNEST(luri_org_list) x(id);
12063 ELSIF param_search_ou = 0 THEN
12064 -- reserved for user lassos (ou_buckets/type='lasso') with ID passed in depth ... hack? sure.
12067 IF param_pref_ou IS NOT NULL THEN
12068 SELECT array_accum(distinct id) INTO tmp_int_list FROM actor.org_unit_ancestors(param_pref_ou);
12069 luri_org_list := luri_org_list || tmp_int_list;
12072 OPEN core_cursor FOR EXECUTE param_query;
12076 FETCH core_cursor INTO core_result;
12077 EXIT WHEN NOT FOUND;
12078 EXIT WHEN total_count >= core_limit;
12080 total_count := total_count + 1;
12082 CONTINUE WHEN total_count NOT BETWEEN core_offset + 1 AND check_limit + core_offset;
12084 check_count := check_count + 1;
12086 PERFORM 1 FROM biblio.record_entry b WHERE NOT b.deleted AND b.id IN ( SELECT * FROM unnest( core_result.records ) );
12088 -- RAISE NOTICE ' % were all deleted ... ', core_result.records;
12089 deleted_count := deleted_count + 1;
12094 FROM biblio.record_entry b
12095 JOIN config.bib_source s ON (b.source = s.id)
12096 WHERE s.transcendant
12097 AND b.id IN ( SELECT * FROM unnest( core_result.records ) );
12100 -- RAISE NOTICE ' % were all transcendant ... ', core_result.records;
12101 visible_count := visible_count + 1;
12103 current_res.id = core_result.id;
12104 current_res.rel = core_result.rel;
12108 SELECT COUNT(DISTINCT s.source) INTO tmp_int FROM metabib.metarecord_source_map s WHERE s.metarecord = core_result.id;
12111 IF tmp_int = 1 THEN
12112 current_res.record = core_result.records[1];
12114 current_res.record = NULL;
12117 RETURN NEXT current_res;
12123 FROM asset.call_number cn
12124 JOIN asset.uri_call_number_map map ON (map.call_number = cn.id)
12125 JOIN asset.uri uri ON (map.uri = uri.id)
12126 WHERE NOT cn.deleted
12127 AND cn.label = '##URI##'
12129 AND ( param_locations IS NULL OR array_upper(param_locations, 1) IS NULL )
12130 AND cn.record IN ( SELECT * FROM unnest( core_result.records ) )
12131 AND cn.owning_lib IN ( SELECT * FROM unnest( luri_org_list ) )
12135 -- RAISE NOTICE ' % have at least one URI ... ', core_result.records;
12136 visible_count := visible_count + 1;
12138 current_res.id = core_result.id;
12139 current_res.rel = core_result.rel;
12143 SELECT COUNT(DISTINCT s.source) INTO tmp_int FROM metabib.metarecord_source_map s WHERE s.metarecord = core_result.id;
12146 IF tmp_int = 1 THEN
12147 current_res.record = core_result.records[1];
12149 current_res.record = NULL;
12152 RETURN NEXT current_res;
12157 IF param_statuses IS NOT NULL AND array_upper(param_statuses, 1) > 0 THEN
12160 FROM asset.call_number cn
12161 JOIN asset.copy cp ON (cp.call_number = cn.id)
12162 WHERE NOT cn.deleted
12164 AND cp.status IN ( SELECT * FROM unnest( param_statuses ) )
12165 AND cn.record IN ( SELECT * FROM unnest( core_result.records ) )
12166 AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
12171 FROM biblio.peer_bib_copy_map pr
12172 JOIN asset.copy cp ON (cp.id = pr.target_copy)
12173 WHERE NOT cp.deleted
12174 AND cp.status IN ( SELECT * FROM unnest( param_statuses ) )
12175 AND pr.peer_record IN ( SELECT * FROM unnest( core_result.records ) )
12176 AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
12180 -- RAISE NOTICE ' % and multi-home linked records were all status-excluded ... ', core_result.records;
12181 excluded_count := excluded_count + 1;
12188 IF param_locations IS NOT NULL AND array_upper(param_locations, 1) > 0 THEN
12191 FROM asset.call_number cn
12192 JOIN asset.copy cp ON (cp.call_number = cn.id)
12193 WHERE NOT cn.deleted
12195 AND cp.location IN ( SELECT * FROM unnest( param_locations ) )
12196 AND cn.record IN ( SELECT * FROM unnest( core_result.records ) )
12197 AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
12202 FROM biblio.peer_bib_copy_map pr
12203 JOIN asset.copy cp ON (cp.id = pr.target_copy)
12204 WHERE NOT cp.deleted
12205 AND cp.location IN ( SELECT * FROM unnest( param_locations ) )
12206 AND pr.peer_record IN ( SELECT * FROM unnest( core_result.records ) )
12207 AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
12211 -- RAISE NOTICE ' % and multi-home linked records were all copy_location-excluded ... ', core_result.records;
12212 excluded_count := excluded_count + 1;
12219 IF staff IS NULL OR NOT staff THEN
12222 FROM asset.opac_visible_copies
12223 WHERE circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
12224 AND record IN ( SELECT * FROM unnest( core_result.records ) )
12229 FROM biblio.peer_bib_copy_map pr
12230 JOIN asset.opac_visible_copies cp ON (cp.copy_id = pr.target_copy)
12231 WHERE cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
12232 AND pr.peer_record IN ( SELECT * FROM unnest( core_result.records ) )
12237 -- RAISE NOTICE ' % and multi-home linked records were all visibility-excluded ... ', core_result.records;
12238 excluded_count := excluded_count + 1;
12246 FROM asset.call_number cn
12247 JOIN asset.copy cp ON (cp.call_number = cn.id)
12248 WHERE NOT cn.deleted
12250 AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
12251 AND cn.record IN ( SELECT * FROM unnest( core_result.records ) )
12257 FROM biblio.peer_bib_copy_map pr
12258 JOIN asset.copy cp ON (cp.id = pr.target_copy)
12259 WHERE NOT cp.deleted
12260 AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
12261 AND pr.peer_record IN ( SELECT * FROM unnest( core_result.records ) )
12267 FROM asset.call_number cn
12268 JOIN asset.copy cp ON (cp.call_number = cn.id)
12269 WHERE cn.record IN ( SELECT * FROM unnest( core_result.records ) )
12274 -- RAISE NOTICE ' % and multi-home linked records were all visibility-excluded ... ', core_result.records;
12275 excluded_count := excluded_count + 1;
12284 visible_count := visible_count + 1;
12286 current_res.id = core_result.id;
12287 current_res.rel = core_result.rel;
12291 SELECT COUNT(DISTINCT s.source) INTO tmp_int FROM metabib.metarecord_source_map s WHERE s.metarecord = core_result.id;
12294 IF tmp_int = 1 THEN
12295 current_res.record = core_result.records[1];
12297 current_res.record = NULL;
12300 RETURN NEXT current_res;
12302 IF visible_count % 1000 = 0 THEN
12303 -- RAISE NOTICE ' % visible so far ... ', visible_count;
12308 current_res.id = NULL;
12309 current_res.rel = NULL;
12310 current_res.record = NULL;
12311 current_res.total = total_count;
12312 current_res.checked = check_count;
12313 current_res.deleted = deleted_count;
12314 current_res.visible = visible_count;
12315 current_res.excluded = excluded_count;
12319 RETURN NEXT current_res;
12322 $func$ LANGUAGE PLPGSQL;
12324 -- Drop the old 10-parameter function
12325 DROP FUNCTION IF EXISTS search.query_parser_fts (
12326 INT, INT, TEXT, INT[], INT[], INT, INT, INT, BOOL, BOOL
12329 -- Evergreen DB patch 0705.data.custom-org-tree-perms.sql
12331 -- check whether patch can be applied
12332 SELECT evergreen.upgrade_deps_block_check('0705', :eg_version);
12334 INSERT INTO permission.perm_list (id, code, description)
12337 'ADMIN_ORG_UNIT_CUSTOM_TREE',
12340 'User may update custom org unit trees',
12346 -- Evergreen DB patch 0707.schema.acq-vandelay-integration.sql
12348 SELECT evergreen.upgrade_deps_block_check('0707', :eg_version);
12352 INSERT INTO permission.perm_list ( id, code, description )
12355 'ADMIN_IMPORT_MATCH_SET',
12358 'Allows a user to create/retrieve/update/delete vandelay match sets',
12364 'VIEW_IMPORT_MATCH_SET',
12367 'Allows a user to view vandelay match sets',
12373 -- This upgrade script fixed a typo in a previous one. It was corrected in the proper place in this file.
12374 -- Still, record the fact it has been "applied".
12375 SELECT evergreen.upgrade_deps_block_check('0708', :eg_version);
12377 -- Evergreen DB patch 0709.data.misc_missing_perms.sql
12379 SELECT evergreen.upgrade_deps_block_check('0709', :eg_version);
12381 INSERT INTO permission.perm_list ( id, code, description )
12384 'ADMIN_ADDRESS_ALERT',
12387 'Allows a user to create/retrieve/update/delete address alerts',
12393 'VIEW_ADDRESS_ALERT',
12396 'Allows a user to view address alerts',
12402 'ADMIN_COPY_LOCATION_GROUP',
12405 'Allows a user to create/retrieve/update/delete copy location groups',
12411 'ADMIN_USER_ACTIVITY_TYPE',
12414 'Allows a user to create/retrieve/update/delete user activity types',
12422 \qecho ************************************************************************
12423 \qecho The following transaction, wrapping upgrade 0672, may take a while. If
12424 \qecho it takes an unduly long time, try it outside of a transaction.
12425 \qecho ************************************************************************
12429 -- Evergreen DB patch 0672.fix-nonfiling-titles.sql
12431 -- Titles that begin with non-filing articles using apostrophes
12432 -- (for example, "L'armée") get spaces injected between the article
12433 -- and the subsequent text, which then breaks searching for titles
12434 -- beginning with those articles.
12436 -- This patch adds a nonfiling title element to MODS32 that can then
12437 -- be used to retrieve the title proper without affecting the spaces
12438 -- in the title. It's what we want, what we really really want, for
12443 -- check whether patch can be applied
12444 SELECT evergreen.upgrade_deps_block_check('0672', :eg_version);
12446 -- Update the XPath definition before the titleNonfiling element exists;
12447 -- but are you really going to read through the whole XSL below before
12448 -- seeing this important bit?
12449 UPDATE config.metabib_field
12450 SET xpath = $$//mods32:mods/mods32:titleNonfiling[mods32:title and not (@type)]$$,
12452 WHERE field_class = 'title' AND name = 'proper';
12454 UPDATE config.xml_transform SET xslt=$$<?xml version="1.0" encoding="UTF-8"?>
12455 <xsl:stylesheet xmlns="http://www.loc.gov/mods/v3" xmlns:marc="http://www.loc.gov/MARC21/slim" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="xlink marc" version="1.0">
12456 <xsl:output encoding="UTF-8" indent="yes" method="xml"/>
12458 Revision 1.14 - Fixed template isValid and fields 010, 020, 022, 024, 028, and 037 to output additional identifier elements
12459 with corresponding @type and @invalid eq 'yes' when subfields z or y (in the case of 022) exist in the MARCXML ::: 2007/01/04 17:35:20 cred
12461 Revision 1.13 - Changed order of output under cartographics to reflect schema 2006/11/28 tmee
12463 Revision 1.12 - Updated to reflect MODS 3.2 Mapping 2006/10/11 tmee
12465 Revision 1.11 - The attribute objectPart moved from <languageTerm> to <language>
12468 Revision 1.10 MODS 3.1 revisions to language and classification elements
12469 (plus ability to find marc:collection embedded in wrapper elements such as SRU zs: wrappers)
12472 Revision 1.9 subfield $y was added to field 242 2004/09/02 10:57 jrad
12474 Revision 1.8 Subject chopPunctuation expanded and attribute fixes 2004/08/12 jrad
12476 Revision 1.7 2004/03/25 08:29 jrad
12478 Revision 1.6 various validation fixes 2004/02/20 ntra
12480 Revision 1.5 2003/10/02 16:18:58 ntra
12481 MODS2 to MODS3 updates, language unstacking and
12482 de-duping, chopPunctuation expanded
12484 Revision 1.3 2003/04/03 00:07:19 ntra
12485 Revision 1.3 Additional Changes not related to MODS Version 2.0 by ntra
12487 Revision 1.2 2003/03/24 19:37:42 ckeith
12491 <xsl:template match="/">
12493 <xsl:when test="//marc:collection">
12494 <modsCollection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.loc.gov/mods/v3 http://www.loc.gov/standards/mods/v3/mods-3-2.xsd">
12495 <xsl:for-each select="//marc:collection/marc:record">
12496 <mods version="3.2">
12497 <xsl:call-template name="marcRecord"/>
12503 <mods xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.2" xsi:schemaLocation="http://www.loc.gov/mods/v3 http://www.loc.gov/standards/mods/v3/mods-3-2.xsd">
12504 <xsl:for-each select="//marc:record">
12505 <xsl:call-template name="marcRecord"/>
12511 <xsl:template name="marcRecord">
12512 <xsl:variable name="leader" select="marc:leader"/>
12513 <xsl:variable name="leader6" select="substring($leader,7,1)"/>
12514 <xsl:variable name="leader7" select="substring($leader,8,1)"/>
12515 <xsl:variable name="controlField008" select="marc:controlfield[@tag='008']"/>
12516 <xsl:variable name="typeOf008">
12518 <xsl:when test="$leader6='a'">
12520 <xsl:when test="$leader7='a' or $leader7='c' or $leader7='d' or $leader7='m'">BK</xsl:when>
12521 <xsl:when test="$leader7='b' or $leader7='i' or $leader7='s'">SE</xsl:when>
12524 <xsl:when test="$leader6='t'">BK</xsl:when>
12525 <xsl:when test="$leader6='p'">MM</xsl:when>
12526 <xsl:when test="$leader6='m'">CF</xsl:when>
12527 <xsl:when test="$leader6='e' or $leader6='f'">MP</xsl:when>
12528 <xsl:when test="$leader6='g' or $leader6='k' or $leader6='o' or $leader6='r'">VM</xsl:when>
12529 <xsl:when test="$leader6='c' or $leader6='d' or $leader6='i' or $leader6='j'">MU</xsl:when>
12532 <xsl:for-each select="marc:datafield[@tag='245']">
12534 <xsl:variable name="title">
12536 <xsl:when test="marc:subfield[@code='b']">
12537 <xsl:call-template name="specialSubfieldSelect">
12538 <xsl:with-param name="axis">b</xsl:with-param>
12539 <xsl:with-param name="beforeCodes">afgk</xsl:with-param>
12540 </xsl:call-template>
12543 <xsl:call-template name="subfieldSelect">
12544 <xsl:with-param name="codes">abfgk</xsl:with-param>
12545 </xsl:call-template>
12549 <xsl:variable name="titleChop">
12550 <xsl:call-template name="chopPunctuation">
12551 <xsl:with-param name="chopString">
12552 <xsl:value-of select="$title"/>
12554 </xsl:call-template>
12557 <xsl:when test="@ind2>0">
12559 <xsl:value-of select="substring($titleChop,1,@ind2)"/>
12562 <xsl:value-of select="substring($titleChop,@ind2+1)"/>
12567 <xsl:value-of select="$titleChop"/>
12571 <xsl:if test="marc:subfield[@code='b']">
12573 <xsl:call-template name="chopPunctuation">
12574 <xsl:with-param name="chopString">
12575 <xsl:call-template name="specialSubfieldSelect">
12576 <xsl:with-param name="axis">b</xsl:with-param>
12577 <xsl:with-param name="anyCodes">b</xsl:with-param>
12578 <xsl:with-param name="afterCodes">afgk</xsl:with-param>
12579 </xsl:call-template>
12581 </xsl:call-template>
12584 <xsl:call-template name="part"></xsl:call-template>
12586 <!-- A form of title that ignores non-filing characters; useful
12587 for not converting "L'Oreal" into "L' Oreal" at index time -->
12589 <xsl:variable name="title">
12591 <xsl:when test="marc:subfield[@code='b']">
12592 <xsl:call-template name="specialSubfieldSelect">
12593 <xsl:with-param name="axis">b</xsl:with-param>
12594 <xsl:with-param name="beforeCodes">afgk</xsl:with-param>
12595 </xsl:call-template>
12598 <xsl:call-template name="subfieldSelect">
12599 <xsl:with-param name="codes">abfgk</xsl:with-param>
12600 </xsl:call-template>
12605 <xsl:value-of select="$title"/>
12607 <xsl:if test="marc:subfield[@code='b']">
12609 <xsl:call-template name="chopPunctuation">
12610 <xsl:with-param name="chopString">
12611 <xsl:call-template name="specialSubfieldSelect">
12612 <xsl:with-param name="axis">b</xsl:with-param>
12613 <xsl:with-param name="anyCodes">b</xsl:with-param>
12614 <xsl:with-param name="afterCodes">afgk</xsl:with-param>
12615 </xsl:call-template>
12617 </xsl:call-template>
12620 <xsl:call-template name="part"></xsl:call-template>
12623 <xsl:for-each select="marc:datafield[@tag='210']">
12624 <titleInfo type="abbreviated">
12626 <xsl:call-template name="chopPunctuation">
12627 <xsl:with-param name="chopString">
12628 <xsl:call-template name="subfieldSelect">
12629 <xsl:with-param name="codes">a</xsl:with-param>
12630 </xsl:call-template>
12632 </xsl:call-template>
12634 <xsl:call-template name="subtitle"/>
12637 <xsl:for-each select="marc:datafield[@tag='242']">
12638 <titleInfo type="translated">
12639 <!--09/01/04 Added subfield $y-->
12640 <xsl:for-each select="marc:subfield[@code='y']">
12641 <xsl:attribute name="lang">
12642 <xsl:value-of select="text()"/>
12646 <xsl:call-template name="chopPunctuation">
12647 <xsl:with-param name="chopString">
12648 <xsl:call-template name="subfieldSelect">
12649 <!-- 1/04 removed $h, b -->
12650 <xsl:with-param name="codes">a</xsl:with-param>
12651 </xsl:call-template>
12653 </xsl:call-template>
12656 <xsl:call-template name="subtitle"/>
12657 <xsl:call-template name="part"/>
12660 <xsl:for-each select="marc:datafield[@tag='246']">
12661 <titleInfo type="alternative">
12662 <xsl:for-each select="marc:subfield[@code='i']">
12663 <xsl:attribute name="displayLabel">
12664 <xsl:value-of select="text()"/>
12668 <xsl:call-template name="chopPunctuation">
12669 <xsl:with-param name="chopString">
12670 <xsl:call-template name="subfieldSelect">
12671 <!-- 1/04 removed $h, $b -->
12672 <xsl:with-param name="codes">af</xsl:with-param>
12673 </xsl:call-template>
12675 </xsl:call-template>
12677 <xsl:call-template name="subtitle"/>
12678 <xsl:call-template name="part"/>
12681 <xsl:for-each select="marc:datafield[@tag='130']|marc:datafield[@tag='240']|marc:datafield[@tag='730'][@ind2!='2']">
12682 <titleInfo type="uniform">
12684 <xsl:variable name="str">
12685 <xsl:for-each select="marc:subfield">
12686 <xsl:if test="(contains('adfklmor',@code) and (not(../marc:subfield[@code='n' or @code='p']) or (following-sibling::marc:subfield[@code='n' or @code='p'])))">
12687 <xsl:value-of select="text()"/>
12688 <xsl:text> </xsl:text>
12692 <xsl:call-template name="chopPunctuation">
12693 <xsl:with-param name="chopString">
12694 <xsl:value-of select="substring($str,1,string-length($str)-1)"/>
12696 </xsl:call-template>
12698 <xsl:call-template name="part"/>
12701 <xsl:for-each select="marc:datafield[@tag='740'][@ind2!='2']">
12702 <titleInfo type="alternative">
12704 <xsl:call-template name="chopPunctuation">
12705 <xsl:with-param name="chopString">
12706 <xsl:call-template name="subfieldSelect">
12707 <xsl:with-param name="codes">ah</xsl:with-param>
12708 </xsl:call-template>
12710 </xsl:call-template>
12712 <xsl:call-template name="part"/>
12715 <xsl:for-each select="marc:datafield[@tag='100']">
12716 <name type="personal">
12717 <xsl:call-template name="nameABCDQ"/>
12718 <xsl:call-template name="affiliation"/>
12720 <roleTerm authority="marcrelator" type="text">creator</roleTerm>
12722 <xsl:call-template name="role"/>
12725 <xsl:for-each select="marc:datafield[@tag='110']">
12726 <name type="corporate">
12727 <xsl:call-template name="nameABCDN"/>
12729 <roleTerm authority="marcrelator" type="text">creator</roleTerm>
12731 <xsl:call-template name="role"/>
12734 <xsl:for-each select="marc:datafield[@tag='111']">
12735 <name type="conference">
12736 <xsl:call-template name="nameACDEQ"/>
12738 <roleTerm authority="marcrelator" type="text">creator</roleTerm>
12740 <xsl:call-template name="role"/>
12743 <xsl:for-each select="marc:datafield[@tag='700'][not(marc:subfield[@code='t'])]">
12744 <name type="personal">
12745 <xsl:call-template name="nameABCDQ"/>
12746 <xsl:call-template name="affiliation"/>
12747 <xsl:call-template name="role"/>
12750 <xsl:for-each select="marc:datafield[@tag='710'][not(marc:subfield[@code='t'])]">
12751 <name type="corporate">
12752 <xsl:call-template name="nameABCDN"/>
12753 <xsl:call-template name="role"/>
12756 <xsl:for-each select="marc:datafield[@tag='711'][not(marc:subfield[@code='t'])]">
12757 <name type="conference">
12758 <xsl:call-template name="nameACDEQ"/>
12759 <xsl:call-template name="role"/>
12762 <xsl:for-each select="marc:datafield[@tag='720'][not(marc:subfield[@code='t'])]">
12764 <xsl:if test="@ind1=1">
12765 <xsl:attribute name="type">
12766 <xsl:text>personal</xsl:text>
12770 <xsl:value-of select="marc:subfield[@code='a']"/>
12772 <xsl:call-template name="role"/>
12776 <xsl:if test="$leader7='c'">
12777 <xsl:attribute name="collection">yes</xsl:attribute>
12779 <xsl:if test="$leader6='d' or $leader6='f' or $leader6='p' or $leader6='t'">
12780 <xsl:attribute name="manuscript">yes</xsl:attribute>
12783 <xsl:when test="$leader6='a' or $leader6='t'">text</xsl:when>
12784 <xsl:when test="$leader6='e' or $leader6='f'">cartographic</xsl:when>
12785 <xsl:when test="$leader6='c' or $leader6='d'">notated music</xsl:when>
12786 <xsl:when test="$leader6='i'">sound recording-nonmusical</xsl:when>
12787 <xsl:when test="$leader6='j'">sound recording-musical</xsl:when>
12788 <xsl:when test="$leader6='k'">still image</xsl:when>
12789 <xsl:when test="$leader6='g'">moving image</xsl:when>
12790 <xsl:when test="$leader6='r'">three dimensional object</xsl:when>
12791 <xsl:when test="$leader6='m'">software, multimedia</xsl:when>
12792 <xsl:when test="$leader6='p'">mixed material</xsl:when>
12795 <xsl:if test="substring($controlField008,26,1)='d'">
12796 <genre authority="marc">globe</genre>
12798 <xsl:if test="marc:controlfield[@tag='007'][substring(text(),1,1)='a'][substring(text(),2,1)='r']">
12799 <genre authority="marc">remote sensing image</genre>
12801 <xsl:if test="$typeOf008='MP'">
12802 <xsl:variable name="controlField008-25" select="substring($controlField008,26,1)"></xsl:variable>
12804 <xsl:when test="$controlField008-25='a' or $controlField008-25='b' or $controlField008-25='c' or marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='j']">
12805 <genre authority="marc">map</genre>
12807 <xsl:when test="$controlField008-25='e' or marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='d']">
12808 <genre authority="marc">atlas</genre>
12812 <xsl:if test="$typeOf008='SE'">
12813 <xsl:variable name="controlField008-21" select="substring($controlField008,22,1)"></xsl:variable>
12815 <xsl:when test="$controlField008-21='d'">
12816 <genre authority="marc">database</genre>
12818 <xsl:when test="$controlField008-21='l'">
12819 <genre authority="marc">loose-leaf</genre>
12821 <xsl:when test="$controlField008-21='m'">
12822 <genre authority="marc">series</genre>
12824 <xsl:when test="$controlField008-21='n'">
12825 <genre authority="marc">newspaper</genre>
12827 <xsl:when test="$controlField008-21='p'">
12828 <genre authority="marc">periodical</genre>
12830 <xsl:when test="$controlField008-21='w'">
12831 <genre authority="marc">web site</genre>
12835 <xsl:if test="$typeOf008='BK' or $typeOf008='SE'">
12836 <xsl:variable name="controlField008-24" select="substring($controlField008,25,4)"></xsl:variable>
12838 <xsl:when test="contains($controlField008-24,'a')">
12839 <genre authority="marc">abstract or summary</genre>
12841 <xsl:when test="contains($controlField008-24,'b')">
12842 <genre authority="marc">bibliography</genre>
12844 <xsl:when test="contains($controlField008-24,'c')">
12845 <genre authority="marc">catalog</genre>
12847 <xsl:when test="contains($controlField008-24,'d')">
12848 <genre authority="marc">dictionary</genre>
12850 <xsl:when test="contains($controlField008-24,'e')">
12851 <genre authority="marc">encyclopedia</genre>
12853 <xsl:when test="contains($controlField008-24,'f')">
12854 <genre authority="marc">handbook</genre>
12856 <xsl:when test="contains($controlField008-24,'g')">
12857 <genre authority="marc">legal article</genre>
12859 <xsl:when test="contains($controlField008-24,'i')">
12860 <genre authority="marc">index</genre>
12862 <xsl:when test="contains($controlField008-24,'k')">
12863 <genre authority="marc">discography</genre>
12865 <xsl:when test="contains($controlField008-24,'l')">
12866 <genre authority="marc">legislation</genre>
12868 <xsl:when test="contains($controlField008-24,'m')">
12869 <genre authority="marc">theses</genre>
12871 <xsl:when test="contains($controlField008-24,'n')">
12872 <genre authority="marc">survey of literature</genre>
12874 <xsl:when test="contains($controlField008-24,'o')">
12875 <genre authority="marc">review</genre>
12877 <xsl:when test="contains($controlField008-24,'p')">
12878 <genre authority="marc">programmed text</genre>
12880 <xsl:when test="contains($controlField008-24,'q')">
12881 <genre authority="marc">filmography</genre>
12883 <xsl:when test="contains($controlField008-24,'r')">
12884 <genre authority="marc">directory</genre>
12886 <xsl:when test="contains($controlField008-24,'s')">
12887 <genre authority="marc">statistics</genre>
12889 <xsl:when test="contains($controlField008-24,'t')">
12890 <genre authority="marc">technical report</genre>
12892 <xsl:when test="contains($controlField008-24,'v')">
12893 <genre authority="marc">legal case and case notes</genre>
12895 <xsl:when test="contains($controlField008-24,'w')">
12896 <genre authority="marc">law report or digest</genre>
12898 <xsl:when test="contains($controlField008-24,'z')">
12899 <genre authority="marc">treaty</genre>
12902 <xsl:variable name="controlField008-29" select="substring($controlField008,30,1)"></xsl:variable>
12904 <xsl:when test="$controlField008-29='1'">
12905 <genre authority="marc">conference publication</genre>
12909 <xsl:if test="$typeOf008='CF'">
12910 <xsl:variable name="controlField008-26" select="substring($controlField008,27,1)"></xsl:variable>
12912 <xsl:when test="$controlField008-26='a'">
12913 <genre authority="marc">numeric data</genre>
12915 <xsl:when test="$controlField008-26='e'">
12916 <genre authority="marc">database</genre>
12918 <xsl:when test="$controlField008-26='f'">
12919 <genre authority="marc">font</genre>
12921 <xsl:when test="$controlField008-26='g'">
12922 <genre authority="marc">game</genre>
12926 <xsl:if test="$typeOf008='BK'">
12927 <xsl:if test="substring($controlField008,25,1)='j'">
12928 <genre authority="marc">patent</genre>
12930 <xsl:if test="substring($controlField008,31,1)='1'">
12931 <genre authority="marc">festschrift</genre>
12933 <xsl:variable name="controlField008-34" select="substring($controlField008,35,1)"></xsl:variable>
12934 <xsl:if test="$controlField008-34='a' or $controlField008-34='b' or $controlField008-34='c' or $controlField008-34='d'">
12935 <genre authority="marc">biography</genre>
12937 <xsl:variable name="controlField008-33" select="substring($controlField008,34,1)"></xsl:variable>
12939 <xsl:when test="$controlField008-33='e'">
12940 <genre authority="marc">essay</genre>
12942 <xsl:when test="$controlField008-33='d'">
12943 <genre authority="marc">drama</genre>
12945 <xsl:when test="$controlField008-33='c'">
12946 <genre authority="marc">comic strip</genre>
12948 <xsl:when test="$controlField008-33='l'">
12949 <genre authority="marc">fiction</genre>
12951 <xsl:when test="$controlField008-33='h'">
12952 <genre authority="marc">humor, satire</genre>
12954 <xsl:when test="$controlField008-33='i'">
12955 <genre authority="marc">letter</genre>
12957 <xsl:when test="$controlField008-33='f'">
12958 <genre authority="marc">novel</genre>
12960 <xsl:when test="$controlField008-33='j'">
12961 <genre authority="marc">short story</genre>
12963 <xsl:when test="$controlField008-33='s'">
12964 <genre authority="marc">speech</genre>
12968 <xsl:if test="$typeOf008='MU'">
12969 <xsl:variable name="controlField008-30-31" select="substring($controlField008,31,2)"></xsl:variable>
12970 <xsl:if test="contains($controlField008-30-31,'b')">
12971 <genre authority="marc">biography</genre>
12973 <xsl:if test="contains($controlField008-30-31,'c')">
12974 <genre authority="marc">conference publication</genre>
12976 <xsl:if test="contains($controlField008-30-31,'d')">
12977 <genre authority="marc">drama</genre>
12979 <xsl:if test="contains($controlField008-30-31,'e')">
12980 <genre authority="marc">essay</genre>
12982 <xsl:if test="contains($controlField008-30-31,'f')">
12983 <genre authority="marc">fiction</genre>
12985 <xsl:if test="contains($controlField008-30-31,'o')">
12986 <genre authority="marc">folktale</genre>
12988 <xsl:if test="contains($controlField008-30-31,'h')">
12989 <genre authority="marc">history</genre>
12991 <xsl:if test="contains($controlField008-30-31,'k')">
12992 <genre authority="marc">humor, satire</genre>
12994 <xsl:if test="contains($controlField008-30-31,'m')">
12995 <genre authority="marc">memoir</genre>
12997 <xsl:if test="contains($controlField008-30-31,'p')">
12998 <genre authority="marc">poetry</genre>
13000 <xsl:if test="contains($controlField008-30-31,'r')">
13001 <genre authority="marc">rehearsal</genre>
13003 <xsl:if test="contains($controlField008-30-31,'g')">
13004 <genre authority="marc">reporting</genre>
13006 <xsl:if test="contains($controlField008-30-31,'s')">
13007 <genre authority="marc">sound</genre>
13009 <xsl:if test="contains($controlField008-30-31,'l')">
13010 <genre authority="marc">speech</genre>
13013 <xsl:if test="$typeOf008='VM'">
13014 <xsl:variable name="controlField008-33" select="substring($controlField008,34,1)"></xsl:variable>
13016 <xsl:when test="$controlField008-33='a'">
13017 <genre authority="marc">art original</genre>
13019 <xsl:when test="$controlField008-33='b'">
13020 <genre authority="marc">kit</genre>
13022 <xsl:when test="$controlField008-33='c'">
13023 <genre authority="marc">art reproduction</genre>
13025 <xsl:when test="$controlField008-33='d'">
13026 <genre authority="marc">diorama</genre>
13028 <xsl:when test="$controlField008-33='f'">
13029 <genre authority="marc">filmstrip</genre>
13031 <xsl:when test="$controlField008-33='g'">
13032 <genre authority="marc">legal article</genre>
13034 <xsl:when test="$controlField008-33='i'">
13035 <genre authority="marc">picture</genre>
13037 <xsl:when test="$controlField008-33='k'">
13038 <genre authority="marc">graphic</genre>
13040 <xsl:when test="$controlField008-33='l'">
13041 <genre authority="marc">technical drawing</genre>
13043 <xsl:when test="$controlField008-33='m'">
13044 <genre authority="marc">motion picture</genre>
13046 <xsl:when test="$controlField008-33='n'">
13047 <genre authority="marc">chart</genre>
13049 <xsl:when test="$controlField008-33='o'">
13050 <genre authority="marc">flash card</genre>
13052 <xsl:when test="$controlField008-33='p'">
13053 <genre authority="marc">microscope slide</genre>
13055 <xsl:when test="$controlField008-33='q' or marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='q']">
13056 <genre authority="marc">model</genre>
13058 <xsl:when test="$controlField008-33='r'">
13059 <genre authority="marc">realia</genre>
13061 <xsl:when test="$controlField008-33='s'">
13062 <genre authority="marc">slide</genre>
13064 <xsl:when test="$controlField008-33='t'">
13065 <genre authority="marc">transparency</genre>
13067 <xsl:when test="$controlField008-33='v'">
13068 <genre authority="marc">videorecording</genre>
13070 <xsl:when test="$controlField008-33='w'">
13071 <genre authority="marc">toy</genre>
13075 <xsl:for-each select="marc:datafield[@tag=655]">
13076 <genre authority="marc">
13077 <xsl:attribute name="authority">
13078 <xsl:value-of select="marc:subfield[@code='2']"/>
13080 <xsl:call-template name="subfieldSelect">
13081 <xsl:with-param name="codes">abvxyz</xsl:with-param>
13082 <xsl:with-param name="delimeter">-</xsl:with-param>
13083 </xsl:call-template>
13087 <xsl:variable name="MARCpublicationCode" select="normalize-space(substring($controlField008,16,3))"></xsl:variable>
13088 <xsl:if test="translate($MARCpublicationCode,'|','')">
13091 <xsl:attribute name="type">code</xsl:attribute>
13092 <xsl:attribute name="authority">marccountry</xsl:attribute>
13093 <xsl:value-of select="$MARCpublicationCode"/>
13097 <xsl:for-each select="marc:datafield[@tag=044]/marc:subfield[@code='c']">
13100 <xsl:attribute name="type">code</xsl:attribute>
13101 <xsl:attribute name="authority">iso3166</xsl:attribute>
13102 <xsl:value-of select="."/>
13106 <xsl:for-each select="marc:datafield[@tag=260]/marc:subfield[@code='a']">
13109 <xsl:attribute name="type">text</xsl:attribute>
13110 <xsl:call-template name="chopPunctuationFront">
13111 <xsl:with-param name="chopString">
13112 <xsl:call-template name="chopPunctuation">
13113 <xsl:with-param name="chopString" select="."/>
13114 </xsl:call-template>
13116 </xsl:call-template>
13120 <xsl:for-each select="marc:datafield[@tag=046]/marc:subfield[@code='m']">
13121 <dateValid point="start">
13122 <xsl:value-of select="."/>
13125 <xsl:for-each select="marc:datafield[@tag=046]/marc:subfield[@code='n']">
13126 <dateValid point="end">
13127 <xsl:value-of select="."/>
13130 <xsl:for-each select="marc:datafield[@tag=046]/marc:subfield[@code='j']">
13132 <xsl:value-of select="."/>
13135 <xsl:for-each select="marc:datafield[@tag=260]/marc:subfield[@code='b' or @code='c' or @code='g']">
13137 <xsl:when test="@code='b'">
13139 <xsl:call-template name="chopPunctuation">
13140 <xsl:with-param name="chopString" select="."/>
13141 <xsl:with-param name="punctuation">
13142 <xsl:text>:,;/ </xsl:text>
13144 </xsl:call-template>
13147 <xsl:when test="@code='c'">
13149 <xsl:call-template name="chopPunctuation">
13150 <xsl:with-param name="chopString" select="."/>
13151 </xsl:call-template>
13154 <xsl:when test="@code='g'">
13156 <xsl:value-of select="."/>
13161 <xsl:variable name="dataField260c">
13162 <xsl:call-template name="chopPunctuation">
13163 <xsl:with-param name="chopString" select="marc:datafield[@tag=260]/marc:subfield[@code='c']"></xsl:with-param>
13164 </xsl:call-template>
13166 <xsl:variable name="controlField008-7-10" select="normalize-space(substring($controlField008, 8, 4))"></xsl:variable>
13167 <xsl:variable name="controlField008-11-14" select="normalize-space(substring($controlField008, 12, 4))"></xsl:variable>
13168 <xsl:variable name="controlField008-6" select="normalize-space(substring($controlField008, 7, 1))"></xsl:variable>
13169 <xsl:if test="$controlField008-6='e' or $controlField008-6='p' or $controlField008-6='r' or $controlField008-6='t' or $controlField008-6='s'">
13170 <xsl:if test="$controlField008-7-10 and ($controlField008-7-10 != $dataField260c)">
13171 <dateIssued encoding="marc">
13172 <xsl:value-of select="$controlField008-7-10"/>
13176 <xsl:if test="$controlField008-6='c' or $controlField008-6='d' or $controlField008-6='i' or $controlField008-6='k' or $controlField008-6='m' or $controlField008-6='q' or $controlField008-6='u'">
13177 <xsl:if test="$controlField008-7-10">
13178 <dateIssued encoding="marc" point="start">
13179 <xsl:value-of select="$controlField008-7-10"/>
13183 <xsl:if test="$controlField008-6='c' or $controlField008-6='d' or $controlField008-6='i' or $controlField008-6='k' or $controlField008-6='m' or $controlField008-6='q' or $controlField008-6='u'">
13184 <xsl:if test="$controlField008-11-14">
13185 <dateIssued encoding="marc" point="end">
13186 <xsl:value-of select="$controlField008-11-14"/>
13190 <xsl:if test="$controlField008-6='q'">
13191 <xsl:if test="$controlField008-7-10">
13192 <dateIssued encoding="marc" point="start" qualifier="questionable">
13193 <xsl:value-of select="$controlField008-7-10"/>
13197 <xsl:if test="$controlField008-6='q'">
13198 <xsl:if test="$controlField008-11-14">
13199 <dateIssued encoding="marc" point="end" qualifier="questionable">
13200 <xsl:value-of select="$controlField008-11-14"/>
13204 <xsl:if test="$controlField008-6='t'">
13205 <xsl:if test="$controlField008-11-14">
13206 <copyrightDate encoding="marc">
13207 <xsl:value-of select="$controlField008-11-14"/>
13211 <xsl:for-each select="marc:datafield[@tag=033][@ind1=0 or @ind1=1]/marc:subfield[@code='a']">
13212 <dateCaptured encoding="iso8601">
13213 <xsl:value-of select="."/>
13216 <xsl:for-each select="marc:datafield[@tag=033][@ind1=2]/marc:subfield[@code='a'][1]">
13217 <dateCaptured encoding="iso8601" point="start">
13218 <xsl:value-of select="."/>
13221 <xsl:for-each select="marc:datafield[@tag=033][@ind1=2]/marc:subfield[@code='a'][2]">
13222 <dateCaptured encoding="iso8601" point="end">
13223 <xsl:value-of select="."/>
13226 <xsl:for-each select="marc:datafield[@tag=250]/marc:subfield[@code='a']">
13228 <xsl:value-of select="."/>
13231 <xsl:for-each select="marc:leader">
13234 <xsl:when test="$leader7='a' or $leader7='c' or $leader7='d' or $leader7='m'">monographic</xsl:when>
13235 <xsl:when test="$leader7='b' or $leader7='i' or $leader7='s'">continuing</xsl:when>
13239 <xsl:for-each select="marc:datafield[@tag=310]|marc:datafield[@tag=321]">
13241 <xsl:call-template name="subfieldSelect">
13242 <xsl:with-param name="codes">ab</xsl:with-param>
13243 </xsl:call-template>
13247 <xsl:variable name="controlField008-35-37" select="normalize-space(translate(substring($controlField008,36,3),'|#',''))"></xsl:variable>
13248 <xsl:if test="$controlField008-35-37">
13250 <languageTerm authority="iso639-2b" type="code">
13251 <xsl:value-of select="substring($controlField008,36,3)"/>
13255 <xsl:for-each select="marc:datafield[@tag=041]">
13256 <xsl:for-each select="marc:subfield[@code='a' or @code='b' or @code='d' or @code='e' or @code='f' or @code='g' or @code='h']">
13257 <xsl:variable name="langCodes" select="."/>
13259 <xsl:when test="../marc:subfield[@code='2']='rfc3066'">
13260 <!-- not stacked but could be repeated -->
13261 <xsl:call-template name="rfcLanguages">
13262 <xsl:with-param name="nodeNum">
13263 <xsl:value-of select="1"/>
13265 <xsl:with-param name="usedLanguages">
13266 <xsl:text></xsl:text>
13268 <xsl:with-param name="controlField008-35-37">
13269 <xsl:value-of select="$controlField008-35-37"></xsl:value-of>
13271 </xsl:call-template>
13275 <xsl:variable name="allLanguages">
13276 <xsl:copy-of select="$langCodes"></xsl:copy-of>
13278 <xsl:variable name="currentLanguage">
13279 <xsl:value-of select="substring($allLanguages,1,3)"></xsl:value-of>
13281 <xsl:call-template name="isoLanguage">
13282 <xsl:with-param name="currentLanguage">
13283 <xsl:value-of select="substring($allLanguages,1,3)"></xsl:value-of>
13285 <xsl:with-param name="remainingLanguages">
13286 <xsl:value-of select="substring($allLanguages,4,string-length($allLanguages)-3)"></xsl:value-of>
13288 <xsl:with-param name="usedLanguages">
13289 <xsl:if test="$controlField008-35-37">
13290 <xsl:value-of select="$controlField008-35-37"></xsl:value-of>
13293 </xsl:call-template>
13298 <xsl:variable name="physicalDescription">
13299 <!--3.2 change tmee 007/11 -->
13300 <xsl:if test="$typeOf008='CF' and marc:controlfield[@tag=007][substring(.,12,1)='a']">
13301 <digitalOrigin>reformatted digital</digitalOrigin>
13303 <xsl:if test="$typeOf008='CF' and marc:controlfield[@tag=007][substring(.,12,1)='b']">
13304 <digitalOrigin>digitized microfilm</digitalOrigin>
13306 <xsl:if test="$typeOf008='CF' and marc:controlfield[@tag=007][substring(.,12,1)='d']">
13307 <digitalOrigin>digitized other analog</digitalOrigin>
13309 <xsl:variable name="controlField008-23" select="substring($controlField008,24,1)"></xsl:variable>
13310 <xsl:variable name="controlField008-29" select="substring($controlField008,30,1)"></xsl:variable>
13311 <xsl:variable name="check008-23">
13312 <xsl:if test="$typeOf008='BK' or $typeOf008='MU' or $typeOf008='SE' or $typeOf008='MM'">
13313 <xsl:value-of select="true()"></xsl:value-of>
13316 <xsl:variable name="check008-29">
13317 <xsl:if test="$typeOf008='MP' or $typeOf008='VM'">
13318 <xsl:value-of select="true()"></xsl:value-of>
13322 <xsl:when test="($check008-23 and $controlField008-23='f') or ($check008-29 and $controlField008-29='f')">
13323 <form authority="marcform">braille</form>
13325 <xsl:when test="($controlField008-23=' ' and ($leader6='c' or $leader6='d')) or (($typeOf008='BK' or $typeOf008='SE') and ($controlField008-23=' ' or $controlField008='r'))">
13326 <form authority="marcform">print</form>
13328 <xsl:when test="$leader6 = 'm' or ($check008-23 and $controlField008-23='s') or ($check008-29 and $controlField008-29='s')">
13329 <form authority="marcform">electronic</form>
13331 <xsl:when test="($check008-23 and $controlField008-23='b') or ($check008-29 and $controlField008-29='b')">
13332 <form authority="marcform">microfiche</form>
13334 <xsl:when test="($check008-23 and $controlField008-23='a') or ($check008-29 and $controlField008-29='a')">
13335 <form authority="marcform">microfilm</form>
13339 <xsl:if test="marc:datafield[@tag=130]/marc:subfield[@code='h']">
13340 <form authority="gmd">
13341 <xsl:call-template name="chopBrackets">
13342 <xsl:with-param name="chopString">
13343 <xsl:value-of select="marc:datafield[@tag=130]/marc:subfield[@code='h']"></xsl:value-of>
13345 </xsl:call-template>
13348 <xsl:if test="marc:datafield[@tag=240]/marc:subfield[@code='h']">
13349 <form authority="gmd">
13350 <xsl:call-template name="chopBrackets">
13351 <xsl:with-param name="chopString">
13352 <xsl:value-of select="marc:datafield[@tag=240]/marc:subfield[@code='h']"></xsl:value-of>
13354 </xsl:call-template>
13357 <xsl:if test="marc:datafield[@tag=242]/marc:subfield[@code='h']">
13358 <form authority="gmd">
13359 <xsl:call-template name="chopBrackets">
13360 <xsl:with-param name="chopString">
13361 <xsl:value-of select="marc:datafield[@tag=242]/marc:subfield[@code='h']"></xsl:value-of>
13363 </xsl:call-template>
13366 <xsl:if test="marc:datafield[@tag=245]/marc:subfield[@code='h']">
13367 <form authority="gmd">
13368 <xsl:call-template name="chopBrackets">
13369 <xsl:with-param name="chopString">
13370 <xsl:value-of select="marc:datafield[@tag=245]/marc:subfield[@code='h']"></xsl:value-of>
13372 </xsl:call-template>
13375 <xsl:if test="marc:datafield[@tag=246]/marc:subfield[@code='h']">
13376 <form authority="gmd">
13377 <xsl:call-template name="chopBrackets">
13378 <xsl:with-param name="chopString">
13379 <xsl:value-of select="marc:datafield[@tag=246]/marc:subfield[@code='h']"></xsl:value-of>
13381 </xsl:call-template>
13384 <xsl:if test="marc:datafield[@tag=730]/marc:subfield[@code='h']">
13385 <form authority="gmd">
13386 <xsl:call-template name="chopBrackets">
13387 <xsl:with-param name="chopString">
13388 <xsl:value-of select="marc:datafield[@tag=730]/marc:subfield[@code='h']"></xsl:value-of>
13390 </xsl:call-template>
13393 <xsl:for-each select="marc:datafield[@tag=256]/marc:subfield[@code='a']">
13395 <xsl:value-of select="."></xsl:value-of>
13398 <xsl:for-each select="marc:controlfield[@tag=007][substring(text(),1,1)='c']">
13400 <xsl:when test="substring(text(),14,1)='a'">
13401 <reformattingQuality>access</reformattingQuality>
13403 <xsl:when test="substring(text(),14,1)='p'">
13404 <reformattingQuality>preservation</reformattingQuality>
13406 <xsl:when test="substring(text(),14,1)='r'">
13407 <reformattingQuality>replacement</reformattingQuality>
13411 <!--3.2 change tmee 007/01 -->
13412 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='b']">
13413 <form authority="smd">chip cartridge</form>
13415 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='c']">
13416 <form authority="smd">computer optical disc cartridge</form>
13418 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='j']">
13419 <form authority="smd">magnetic disc</form>
13421 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='m']">
13422 <form authority="smd">magneto-optical disc</form>
13424 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='o']">
13425 <form authority="smd">optical disc</form>
13427 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='r']">
13428 <form authority="smd">remote</form>
13430 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='a']">
13431 <form authority="smd">tape cartridge</form>
13433 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='f']">
13434 <form authority="smd">tape cassette</form>
13436 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='h']">
13437 <form authority="smd">tape reel</form>
13440 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='d'][substring(text(),2,1)='a']">
13441 <form authority="smd">celestial globe</form>
13443 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='d'][substring(text(),2,1)='e']">
13444 <form authority="smd">earth moon globe</form>
13446 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='d'][substring(text(),2,1)='b']">
13447 <form authority="smd">planetary or lunar globe</form>
13449 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='d'][substring(text(),2,1)='c']">
13450 <form authority="smd">terrestrial globe</form>
13453 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='o'][substring(text(),2,1)='o']">
13454 <form authority="smd">kit</form>
13457 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='d']">
13458 <form authority="smd">atlas</form>
13460 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='g']">
13461 <form authority="smd">diagram</form>
13463 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='j']">
13464 <form authority="smd">map</form>
13466 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='q']">
13467 <form authority="smd">model</form>
13469 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='k']">
13470 <form authority="smd">profile</form>
13472 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='r']">
13473 <form authority="smd">remote-sensing image</form>
13475 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='s']">
13476 <form authority="smd">section</form>
13478 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='y']">
13479 <form authority="smd">view</form>
13482 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='a']">
13483 <form authority="smd">aperture card</form>
13485 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='e']">
13486 <form authority="smd">microfiche</form>
13488 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='f']">
13489 <form authority="smd">microfiche cassette</form>
13491 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='b']">
13492 <form authority="smd">microfilm cartridge</form>
13494 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='c']">
13495 <form authority="smd">microfilm cassette</form>
13497 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='d']">
13498 <form authority="smd">microfilm reel</form>
13500 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='g']">
13501 <form authority="smd">microopaque</form>
13504 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='m'][substring(text(),2,1)='c']">
13505 <form authority="smd">film cartridge</form>
13507 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='m'][substring(text(),2,1)='f']">
13508 <form authority="smd">film cassette</form>
13510 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='m'][substring(text(),2,1)='r']">
13511 <form authority="smd">film reel</form>
13514 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='n']">
13515 <form authority="smd">chart</form>
13517 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='c']">
13518 <form authority="smd">collage</form>
13520 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='d']">
13521 <form authority="smd">drawing</form>
13523 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='o']">
13524 <form authority="smd">flash card</form>
13526 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='e']">
13527 <form authority="smd">painting</form>
13529 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='f']">
13530 <form authority="smd">photomechanical print</form>
13532 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='g']">
13533 <form authority="smd">photonegative</form>
13535 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='h']">
13536 <form authority="smd">photoprint</form>
13538 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='i']">
13539 <form authority="smd">picture</form>
13541 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='j']">
13542 <form authority="smd">print</form>
13544 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='l']">
13545 <form authority="smd">technical drawing</form>
13548 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='q'][substring(text(),2,1)='q']">
13549 <form authority="smd">notated music</form>
13552 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='d']">
13553 <form authority="smd">filmslip</form>
13555 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='c']">
13556 <form authority="smd">filmstrip cartridge</form>
13558 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='o']">
13559 <form authority="smd">filmstrip roll</form>
13561 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='f']">
13562 <form authority="smd">other filmstrip type</form>
13564 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='s']">
13565 <form authority="smd">slide</form>
13567 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='t']">
13568 <form authority="smd">transparency</form>
13570 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='r'][substring(text(),2,1)='r']">
13571 <form authority="smd">remote-sensing image</form>
13573 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='e']">
13574 <form authority="smd">cylinder</form>
13576 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='q']">
13577 <form authority="smd">roll</form>
13579 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='g']">
13580 <form authority="smd">sound cartridge</form>
13582 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='s']">
13583 <form authority="smd">sound cassette</form>
13585 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='d']">
13586 <form authority="smd">sound disc</form>
13588 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='t']">
13589 <form authority="smd">sound-tape reel</form>
13591 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='i']">
13592 <form authority="smd">sound-track film</form>
13594 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='w']">
13595 <form authority="smd">wire recording</form>
13598 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='f'][substring(text(),2,1)='c']">
13599 <form authority="smd">braille</form>
13601 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='f'][substring(text(),2,1)='b']">
13602 <form authority="smd">combination</form>
13604 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='f'][substring(text(),2,1)='a']">
13605 <form authority="smd">moon</form>
13607 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='f'][substring(text(),2,1)='d']">
13608 <form authority="smd">tactile, with no writing system</form>
13611 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='t'][substring(text(),2,1)='c']">
13612 <form authority="smd">braille</form>
13614 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='t'][substring(text(),2,1)='b']">
13615 <form authority="smd">large print</form>
13617 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='t'][substring(text(),2,1)='a']">
13618 <form authority="smd">regular print</form>
13620 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='t'][substring(text(),2,1)='d']">
13621 <form authority="smd">text in looseleaf binder</form>
13624 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='v'][substring(text(),2,1)='c']">
13625 <form authority="smd">videocartridge</form>
13627 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='v'][substring(text(),2,1)='f']">
13628 <form authority="smd">videocassette</form>
13630 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='v'][substring(text(),2,1)='d']">
13631 <form authority="smd">videodisc</form>
13633 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='v'][substring(text(),2,1)='r']">
13634 <form authority="smd">videoreel</form>
13637 <xsl:for-each select="marc:datafield[@tag=856]/marc:subfield[@code='q'][string-length(.)>1]">
13638 <internetMediaType>
13639 <xsl:value-of select="."></xsl:value-of>
13640 </internetMediaType>
13642 <xsl:for-each select="marc:datafield[@tag=300]">
13644 <xsl:call-template name="subfieldSelect">
13645 <xsl:with-param name="codes">abce</xsl:with-param>
13646 </xsl:call-template>
13650 <xsl:if test="string-length(normalize-space($physicalDescription))">
13651 <physicalDescription>
13652 <xsl:copy-of select="$physicalDescription"></xsl:copy-of>
13653 </physicalDescription>
13655 <xsl:for-each select="marc:datafield[@tag=520]">
13657 <xsl:call-template name="uri"></xsl:call-template>
13658 <xsl:call-template name="subfieldSelect">
13659 <xsl:with-param name="codes">ab</xsl:with-param>
13660 </xsl:call-template>
13663 <xsl:for-each select="marc:datafield[@tag=505]">
13665 <xsl:call-template name="uri"></xsl:call-template>
13666 <xsl:call-template name="subfieldSelect">
13667 <xsl:with-param name="codes">agrt</xsl:with-param>
13668 </xsl:call-template>
13671 <xsl:for-each select="marc:datafield[@tag=521]">
13673 <xsl:call-template name="subfieldSelect">
13674 <xsl:with-param name="codes">ab</xsl:with-param>
13675 </xsl:call-template>
13678 <xsl:if test="$typeOf008='BK' or $typeOf008='CF' or $typeOf008='MU' or $typeOf008='VM'">
13679 <xsl:variable name="controlField008-22" select="substring($controlField008,23,1)"></xsl:variable>
13682 <xsl:when test="$controlField008-22='d'">
13683 <targetAudience authority="marctarget">adolescent</targetAudience>
13685 <xsl:when test="$controlField008-22='e'">
13686 <targetAudience authority="marctarget">adult</targetAudience>
13688 <xsl:when test="$controlField008-22='g'">
13689 <targetAudience authority="marctarget">general</targetAudience>
13691 <xsl:when test="$controlField008-22='b' or $controlField008-22='c' or $controlField008-22='j'">
13692 <targetAudience authority="marctarget">juvenile</targetAudience>
13694 <xsl:when test="$controlField008-22='a'">
13695 <targetAudience authority="marctarget">preschool</targetAudience>
13697 <xsl:when test="$controlField008-22='f'">
13698 <targetAudience authority="marctarget">specialized</targetAudience>
13702 <xsl:for-each select="marc:datafield[@tag=245]/marc:subfield[@code='c']">
13703 <note type="statement of responsibility">
13704 <xsl:value-of select="."></xsl:value-of>
13707 <xsl:for-each select="marc:datafield[@tag=500]">
13709 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
13710 <xsl:call-template name="uri"></xsl:call-template>
13714 <!--3.2 change tmee additional note fields-->
13716 <xsl:for-each select="marc:datafield[@tag=506]">
13717 <note type="restrictions">
13718 <xsl:call-template name="uri"></xsl:call-template>
13719 <xsl:variable name="str">
13720 <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
13721 <xsl:value-of select="."></xsl:value-of>
13722 <xsl:text> </xsl:text>
13725 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
13729 <xsl:for-each select="marc:datafield[@tag=510]">
13730 <note type="citation/reference">
13731 <xsl:call-template name="uri"></xsl:call-template>
13732 <xsl:variable name="str">
13733 <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
13734 <xsl:value-of select="."></xsl:value-of>
13735 <xsl:text> </xsl:text>
13738 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
13743 <xsl:for-each select="marc:datafield[@tag=511]">
13744 <note type="performers">
13745 <xsl:call-template name="uri"></xsl:call-template>
13746 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
13749 <xsl:for-each select="marc:datafield[@tag=518]">
13750 <note type="venue">
13751 <xsl:call-template name="uri"></xsl:call-template>
13752 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
13756 <xsl:for-each select="marc:datafield[@tag=530]">
13757 <note type="additional physical form">
13758 <xsl:call-template name="uri"></xsl:call-template>
13759 <xsl:variable name="str">
13760 <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
13761 <xsl:value-of select="."></xsl:value-of>
13762 <xsl:text> </xsl:text>
13765 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
13769 <xsl:for-each select="marc:datafield[@tag=533]">
13770 <note type="reproduction">
13771 <xsl:call-template name="uri"></xsl:call-template>
13772 <xsl:variable name="str">
13773 <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
13774 <xsl:value-of select="."></xsl:value-of>
13775 <xsl:text> </xsl:text>
13778 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
13782 <xsl:for-each select="marc:datafield[@tag=534]">
13783 <note type="original version">
13784 <xsl:call-template name="uri"></xsl:call-template>
13785 <xsl:variable name="str">
13786 <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
13787 <xsl:value-of select="."></xsl:value-of>
13788 <xsl:text> </xsl:text>
13791 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
13795 <xsl:for-each select="marc:datafield[@tag=538]">
13796 <note type="system details">
13797 <xsl:call-template name="uri"></xsl:call-template>
13798 <xsl:variable name="str">
13799 <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
13800 <xsl:value-of select="."></xsl:value-of>
13801 <xsl:text> </xsl:text>
13804 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
13808 <xsl:for-each select="marc:datafield[@tag=583]">
13809 <note type="action">
13810 <xsl:call-template name="uri"></xsl:call-template>
13811 <xsl:variable name="str">
13812 <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
13813 <xsl:value-of select="."></xsl:value-of>
13814 <xsl:text> </xsl:text>
13817 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
13825 <xsl:for-each select="marc:datafield[@tag=501 or @tag=502 or @tag=504 or @tag=507 or @tag=508 or @tag=513 or @tag=514 or @tag=515 or @tag=516 or @tag=522 or @tag=524 or @tag=525 or @tag=526 or @tag=535 or @tag=536 or @tag=540 or @tag=541 or @tag=544 or @tag=545 or @tag=546 or @tag=547 or @tag=550 or @tag=552 or @tag=555 or @tag=556 or @tag=561 or @tag=562 or @tag=565 or @tag=567 or @tag=580 or @tag=581 or @tag=584 or @tag=585 or @tag=586]">
13827 <xsl:call-template name="uri"></xsl:call-template>
13828 <xsl:variable name="str">
13829 <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
13830 <xsl:value-of select="."></xsl:value-of>
13831 <xsl:text> </xsl:text>
13834 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
13837 <xsl:for-each select="marc:datafield[@tag=034][marc:subfield[@code='d' or @code='e' or @code='f' or @code='g']]">
13841 <xsl:call-template name="subfieldSelect">
13842 <xsl:with-param name="codes">defg</xsl:with-param>
13843 </xsl:call-template>
13848 <xsl:for-each select="marc:datafield[@tag=043]">
13850 <xsl:for-each select="marc:subfield[@code='a' or @code='b' or @code='c']">
13852 <xsl:attribute name="authority">
13853 <xsl:if test="@code='a'">
13854 <xsl:text>marcgac</xsl:text>
13856 <xsl:if test="@code='b'">
13857 <xsl:value-of select="following-sibling::marc:subfield[@code=2]"></xsl:value-of>
13859 <xsl:if test="@code='c'">
13860 <xsl:text>iso3166</xsl:text>
13863 <xsl:value-of select="self::marc:subfield"></xsl:value-of>
13868 <!-- tmee 2006/11/27 -->
13869 <xsl:for-each select="marc:datafield[@tag=255]">
13871 <xsl:for-each select="marc:subfield[@code='a' or @code='b' or @code='c']">
13873 <xsl:if test="@code='a'">
13875 <xsl:value-of select="."></xsl:value-of>
13878 <xsl:if test="@code='b'">
13880 <xsl:value-of select="."></xsl:value-of>
13883 <xsl:if test="@code='c'">
13885 <xsl:value-of select="."></xsl:value-of>
13893 <xsl:apply-templates select="marc:datafield[653 >= @tag and @tag >= 600]"></xsl:apply-templates>
13894 <xsl:apply-templates select="marc:datafield[@tag=656]"></xsl:apply-templates>
13895 <xsl:for-each select="marc:datafield[@tag=752]">
13897 <hierarchicalGeographic>
13898 <xsl:for-each select="marc:subfield[@code='a']">
13900 <xsl:call-template name="chopPunctuation">
13901 <xsl:with-param name="chopString" select="."></xsl:with-param>
13902 </xsl:call-template>
13905 <xsl:for-each select="marc:subfield[@code='b']">
13907 <xsl:call-template name="chopPunctuation">
13908 <xsl:with-param name="chopString" select="."></xsl:with-param>
13909 </xsl:call-template>
13912 <xsl:for-each select="marc:subfield[@code='c']">
13914 <xsl:call-template name="chopPunctuation">
13915 <xsl:with-param name="chopString" select="."></xsl:with-param>
13916 </xsl:call-template>
13919 <xsl:for-each select="marc:subfield[@code='d']">
13921 <xsl:call-template name="chopPunctuation">
13922 <xsl:with-param name="chopString" select="."></xsl:with-param>
13923 </xsl:call-template>
13926 </hierarchicalGeographic>
13929 <xsl:for-each select="marc:datafield[@tag=045][marc:subfield[@code='b']]">
13932 <xsl:when test="@ind1=2">
13933 <temporal encoding="iso8601" point="start">
13934 <xsl:call-template name="chopPunctuation">
13935 <xsl:with-param name="chopString">
13936 <xsl:value-of select="marc:subfield[@code='b'][1]"></xsl:value-of>
13938 </xsl:call-template>
13940 <temporal encoding="iso8601" point="end">
13941 <xsl:call-template name="chopPunctuation">
13942 <xsl:with-param name="chopString">
13943 <xsl:value-of select="marc:subfield[@code='b'][2]"></xsl:value-of>
13945 </xsl:call-template>
13949 <xsl:for-each select="marc:subfield[@code='b']">
13950 <temporal encoding="iso8601">
13951 <xsl:call-template name="chopPunctuation">
13952 <xsl:with-param name="chopString" select="."></xsl:with-param>
13953 </xsl:call-template>
13960 <xsl:for-each select="marc:datafield[@tag=050]">
13961 <xsl:for-each select="marc:subfield[@code='b']">
13962 <classification authority="lcc">
13963 <xsl:if test="../marc:subfield[@code='3']">
13964 <xsl:attribute name="displayLabel">
13965 <xsl:value-of select="../marc:subfield[@code='3']"></xsl:value-of>
13968 <xsl:value-of select="preceding-sibling::marc:subfield[@code='a'][1]"></xsl:value-of>
13969 <xsl:text> </xsl:text>
13970 <xsl:value-of select="text()"></xsl:value-of>
13973 <xsl:for-each select="marc:subfield[@code='a'][not(following-sibling::marc:subfield[@code='b'])]">
13974 <classification authority="lcc">
13975 <xsl:if test="../marc:subfield[@code='3']">
13976 <xsl:attribute name="displayLabel">
13977 <xsl:value-of select="../marc:subfield[@code='3']"></xsl:value-of>
13980 <xsl:value-of select="text()"></xsl:value-of>
13984 <xsl:for-each select="marc:datafield[@tag=082]">
13985 <classification authority="ddc">
13986 <xsl:if test="marc:subfield[@code='2']">
13987 <xsl:attribute name="edition">
13988 <xsl:value-of select="marc:subfield[@code='2']"></xsl:value-of>
13991 <xsl:call-template name="subfieldSelect">
13992 <xsl:with-param name="codes">ab</xsl:with-param>
13993 </xsl:call-template>
13996 <xsl:for-each select="marc:datafield[@tag=080]">
13997 <classification authority="udc">
13998 <xsl:call-template name="subfieldSelect">
13999 <xsl:with-param name="codes">abx</xsl:with-param>
14000 </xsl:call-template>
14003 <xsl:for-each select="marc:datafield[@tag=060]">
14004 <classification authority="nlm">
14005 <xsl:call-template name="subfieldSelect">
14006 <xsl:with-param name="codes">ab</xsl:with-param>
14007 </xsl:call-template>
14010 <xsl:for-each select="marc:datafield[@tag=086][@ind1=0]">
14011 <classification authority="sudocs">
14012 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
14015 <xsl:for-each select="marc:datafield[@tag=086][@ind1=1]">
14016 <classification authority="candoc">
14017 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
14020 <xsl:for-each select="marc:datafield[@tag=086]">
14022 <xsl:attribute name="authority">
14023 <xsl:value-of select="marc:subfield[@code='2']"></xsl:value-of>
14025 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
14028 <xsl:for-each select="marc:datafield[@tag=084]">
14030 <xsl:attribute name="authority">
14031 <xsl:value-of select="marc:subfield[@code='2']"></xsl:value-of>
14033 <xsl:call-template name="subfieldSelect">
14034 <xsl:with-param name="codes">ab</xsl:with-param>
14035 </xsl:call-template>
14038 <xsl:for-each select="marc:datafield[@tag=440]">
14039 <relatedItem type="series">
14042 <xsl:call-template name="chopPunctuation">
14043 <xsl:with-param name="chopString">
14044 <xsl:call-template name="subfieldSelect">
14045 <xsl:with-param name="codes">av</xsl:with-param>
14046 </xsl:call-template>
14048 </xsl:call-template>
14050 <xsl:call-template name="part"></xsl:call-template>
14054 <xsl:for-each select="marc:datafield[@tag=490][@ind1=0]">
14055 <relatedItem type="series">
14058 <xsl:call-template name="chopPunctuation">
14059 <xsl:with-param name="chopString">
14060 <xsl:call-template name="subfieldSelect">
14061 <xsl:with-param name="codes">av</xsl:with-param>
14062 </xsl:call-template>
14064 </xsl:call-template>
14066 <xsl:call-template name="part"></xsl:call-template>
14070 <xsl:for-each select="marc:datafield[@tag=510]">
14071 <relatedItem type="isReferencedBy">
14073 <xsl:call-template name="subfieldSelect">
14074 <xsl:with-param name="codes">abcx3</xsl:with-param>
14075 </xsl:call-template>
14079 <xsl:for-each select="marc:datafield[@tag=534]">
14080 <relatedItem type="original">
14081 <xsl:call-template name="relatedTitle"></xsl:call-template>
14082 <xsl:call-template name="relatedName"></xsl:call-template>
14083 <xsl:if test="marc:subfield[@code='b' or @code='c']">
14085 <xsl:for-each select="marc:subfield[@code='c']">
14087 <xsl:value-of select="."></xsl:value-of>
14090 <xsl:for-each select="marc:subfield[@code='b']">
14092 <xsl:value-of select="."></xsl:value-of>
14097 <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
14098 <xsl:for-each select="marc:subfield[@code='z']">
14099 <identifier type="isbn">
14100 <xsl:value-of select="."></xsl:value-of>
14103 <xsl:call-template name="relatedNote"></xsl:call-template>
14106 <xsl:for-each select="marc:datafield[@tag=700][marc:subfield[@code='t']]">
14108 <xsl:call-template name="constituentOrRelatedType"></xsl:call-template>
14111 <xsl:call-template name="chopPunctuation">
14112 <xsl:with-param name="chopString">
14113 <xsl:call-template name="specialSubfieldSelect">
14114 <xsl:with-param name="anyCodes">tfklmorsv</xsl:with-param>
14115 <xsl:with-param name="axis">t</xsl:with-param>
14116 <xsl:with-param name="afterCodes">g</xsl:with-param>
14117 </xsl:call-template>
14119 </xsl:call-template>
14121 <xsl:call-template name="part"></xsl:call-template>
14123 <name type="personal">
14125 <xsl:call-template name="specialSubfieldSelect">
14126 <xsl:with-param name="anyCodes">aq</xsl:with-param>
14127 <xsl:with-param name="axis">t</xsl:with-param>
14128 <xsl:with-param name="beforeCodes">g</xsl:with-param>
14129 </xsl:call-template>
14131 <xsl:call-template name="termsOfAddress"></xsl:call-template>
14132 <xsl:call-template name="nameDate"></xsl:call-template>
14133 <xsl:call-template name="role"></xsl:call-template>
14135 <xsl:call-template name="relatedForm"></xsl:call-template>
14136 <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
14139 <xsl:for-each select="marc:datafield[@tag=710][marc:subfield[@code='t']]">
14141 <xsl:call-template name="constituentOrRelatedType"></xsl:call-template>
14144 <xsl:call-template name="chopPunctuation">
14145 <xsl:with-param name="chopString">
14146 <xsl:call-template name="specialSubfieldSelect">
14147 <xsl:with-param name="anyCodes">tfklmorsv</xsl:with-param>
14148 <xsl:with-param name="axis">t</xsl:with-param>
14149 <xsl:with-param name="afterCodes">dg</xsl:with-param>
14150 </xsl:call-template>
14152 </xsl:call-template>
14154 <xsl:call-template name="relatedPartNumName"></xsl:call-template>
14156 <name type="corporate">
14157 <xsl:for-each select="marc:subfield[@code='a']">
14159 <xsl:value-of select="."></xsl:value-of>
14162 <xsl:for-each select="marc:subfield[@code='b']">
14164 <xsl:value-of select="."></xsl:value-of>
14167 <xsl:variable name="tempNamePart">
14168 <xsl:call-template name="specialSubfieldSelect">
14169 <xsl:with-param name="anyCodes">c</xsl:with-param>
14170 <xsl:with-param name="axis">t</xsl:with-param>
14171 <xsl:with-param name="beforeCodes">dgn</xsl:with-param>
14172 </xsl:call-template>
14174 <xsl:if test="normalize-space($tempNamePart)">
14176 <xsl:value-of select="$tempNamePart"></xsl:value-of>
14179 <xsl:call-template name="role"></xsl:call-template>
14181 <xsl:call-template name="relatedForm"></xsl:call-template>
14182 <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
14185 <xsl:for-each select="marc:datafield[@tag=711][marc:subfield[@code='t']]">
14187 <xsl:call-template name="constituentOrRelatedType"></xsl:call-template>
14190 <xsl:call-template name="chopPunctuation">
14191 <xsl:with-param name="chopString">
14192 <xsl:call-template name="specialSubfieldSelect">
14193 <xsl:with-param name="anyCodes">tfklsv</xsl:with-param>
14194 <xsl:with-param name="axis">t</xsl:with-param>
14195 <xsl:with-param name="afterCodes">g</xsl:with-param>
14196 </xsl:call-template>
14198 </xsl:call-template>
14200 <xsl:call-template name="relatedPartNumName"></xsl:call-template>
14202 <name type="conference">
14204 <xsl:call-template name="specialSubfieldSelect">
14205 <xsl:with-param name="anyCodes">aqdc</xsl:with-param>
14206 <xsl:with-param name="axis">t</xsl:with-param>
14207 <xsl:with-param name="beforeCodes">gn</xsl:with-param>
14208 </xsl:call-template>
14211 <xsl:call-template name="relatedForm"></xsl:call-template>
14212 <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
14215 <xsl:for-each select="marc:datafield[@tag=730][@ind2=2]">
14217 <xsl:call-template name="constituentOrRelatedType"></xsl:call-template>
14220 <xsl:call-template name="chopPunctuation">
14221 <xsl:with-param name="chopString">
14222 <xsl:call-template name="subfieldSelect">
14223 <xsl:with-param name="codes">adfgklmorsv</xsl:with-param>
14224 </xsl:call-template>
14226 </xsl:call-template>
14228 <xsl:call-template name="part"></xsl:call-template>
14230 <xsl:call-template name="relatedForm"></xsl:call-template>
14231 <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
14234 <xsl:for-each select="marc:datafield[@tag=740][@ind2=2]">
14236 <xsl:call-template name="constituentOrRelatedType"></xsl:call-template>
14239 <xsl:call-template name="chopPunctuation">
14240 <xsl:with-param name="chopString">
14241 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
14243 </xsl:call-template>
14245 <xsl:call-template name="part"></xsl:call-template>
14247 <xsl:call-template name="relatedForm"></xsl:call-template>
14250 <xsl:for-each select="marc:datafield[@tag=760]|marc:datafield[@tag=762]">
14251 <relatedItem type="series">
14252 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
14255 <xsl:for-each select="marc:datafield[@tag=765]|marc:datafield[@tag=767]|marc:datafield[@tag=777]|marc:datafield[@tag=787]">
14257 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
14260 <xsl:for-each select="marc:datafield[@tag=775]">
14261 <relatedItem type="otherVersion">
14262 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
14265 <xsl:for-each select="marc:datafield[@tag=770]|marc:datafield[@tag=774]">
14266 <relatedItem type="constituent">
14267 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
14270 <xsl:for-each select="marc:datafield[@tag=772]|marc:datafield[@tag=773]">
14271 <relatedItem type="host">
14272 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
14275 <xsl:for-each select="marc:datafield[@tag=776]">
14276 <relatedItem type="otherFormat">
14277 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
14280 <xsl:for-each select="marc:datafield[@tag=780]">
14281 <relatedItem type="preceding">
14282 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
14285 <xsl:for-each select="marc:datafield[@tag=785]">
14286 <relatedItem type="succeeding">
14287 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
14290 <xsl:for-each select="marc:datafield[@tag=786]">
14291 <relatedItem type="original">
14292 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
14295 <xsl:for-each select="marc:datafield[@tag=800]">
14296 <relatedItem type="series">
14299 <xsl:call-template name="chopPunctuation">
14300 <xsl:with-param name="chopString">
14301 <xsl:call-template name="specialSubfieldSelect">
14302 <xsl:with-param name="anyCodes">tfklmorsv</xsl:with-param>
14303 <xsl:with-param name="axis">t</xsl:with-param>
14304 <xsl:with-param name="afterCodes">g</xsl:with-param>
14305 </xsl:call-template>
14307 </xsl:call-template>
14309 <xsl:call-template name="part"></xsl:call-template>
14311 <name type="personal">
14313 <xsl:call-template name="chopPunctuation">
14314 <xsl:with-param name="chopString">
14315 <xsl:call-template name="specialSubfieldSelect">
14316 <xsl:with-param name="anyCodes">aq</xsl:with-param>
14317 <xsl:with-param name="axis">t</xsl:with-param>
14318 <xsl:with-param name="beforeCodes">g</xsl:with-param>
14319 </xsl:call-template>
14321 </xsl:call-template>
14323 <xsl:call-template name="termsOfAddress"></xsl:call-template>
14324 <xsl:call-template name="nameDate"></xsl:call-template>
14325 <xsl:call-template name="role"></xsl:call-template>
14327 <xsl:call-template name="relatedForm"></xsl:call-template>
14330 <xsl:for-each select="marc:datafield[@tag=810]">
14331 <relatedItem type="series">
14334 <xsl:call-template name="chopPunctuation">
14335 <xsl:with-param name="chopString">
14336 <xsl:call-template name="specialSubfieldSelect">
14337 <xsl:with-param name="anyCodes">tfklmorsv</xsl:with-param>
14338 <xsl:with-param name="axis">t</xsl:with-param>
14339 <xsl:with-param name="afterCodes">dg</xsl:with-param>
14340 </xsl:call-template>
14342 </xsl:call-template>
14344 <xsl:call-template name="relatedPartNumName"></xsl:call-template>
14346 <name type="corporate">
14347 <xsl:for-each select="marc:subfield[@code='a']">
14349 <xsl:value-of select="."></xsl:value-of>
14352 <xsl:for-each select="marc:subfield[@code='b']">
14354 <xsl:value-of select="."></xsl:value-of>
14358 <xsl:call-template name="specialSubfieldSelect">
14359 <xsl:with-param name="anyCodes">c</xsl:with-param>
14360 <xsl:with-param name="axis">t</xsl:with-param>
14361 <xsl:with-param name="beforeCodes">dgn</xsl:with-param>
14362 </xsl:call-template>
14364 <xsl:call-template name="role"></xsl:call-template>
14366 <xsl:call-template name="relatedForm"></xsl:call-template>
14369 <xsl:for-each select="marc:datafield[@tag=811]">
14370 <relatedItem type="series">
14373 <xsl:call-template name="chopPunctuation">
14374 <xsl:with-param name="chopString">
14375 <xsl:call-template name="specialSubfieldSelect">
14376 <xsl:with-param name="anyCodes">tfklsv</xsl:with-param>
14377 <xsl:with-param name="axis">t</xsl:with-param>
14378 <xsl:with-param name="afterCodes">g</xsl:with-param>
14379 </xsl:call-template>
14381 </xsl:call-template>
14383 <xsl:call-template name="relatedPartNumName"/>
14385 <name type="conference">
14387 <xsl:call-template name="specialSubfieldSelect">
14388 <xsl:with-param name="anyCodes">aqdc</xsl:with-param>
14389 <xsl:with-param name="axis">t</xsl:with-param>
14390 <xsl:with-param name="beforeCodes">gn</xsl:with-param>
14391 </xsl:call-template>
14393 <xsl:call-template name="role"/>
14395 <xsl:call-template name="relatedForm"/>
14398 <xsl:for-each select="marc:datafield[@tag='830']">
14399 <relatedItem type="series">
14402 <xsl:call-template name="chopPunctuation">
14403 <xsl:with-param name="chopString">
14404 <xsl:call-template name="subfieldSelect">
14405 <xsl:with-param name="codes">adfgklmorsv</xsl:with-param>
14406 </xsl:call-template>
14408 </xsl:call-template>
14410 <xsl:call-template name="part"/>
14412 <xsl:call-template name="relatedForm"/>
14415 <xsl:for-each select="marc:datafield[@tag='856'][@ind2='2']/marc:subfield[@code='q']">
14417 <internetMediaType>
14418 <xsl:value-of select="."/>
14419 </internetMediaType>
14422 <xsl:for-each select="marc:datafield[@tag='020']">
14423 <xsl:call-template name="isInvalid">
14424 <xsl:with-param name="type">isbn</xsl:with-param>
14425 </xsl:call-template>
14426 <xsl:if test="marc:subfield[@code='a']">
14427 <identifier type="isbn">
14428 <xsl:value-of select="marc:subfield[@code='a']"/>
14432 <xsl:for-each select="marc:datafield[@tag='024'][@ind1='0']">
14433 <xsl:call-template name="isInvalid">
14434 <xsl:with-param name="type">isrc</xsl:with-param>
14435 </xsl:call-template>
14436 <xsl:if test="marc:subfield[@code='a']">
14437 <identifier type="isrc">
14438 <xsl:value-of select="marc:subfield[@code='a']"/>
14442 <xsl:for-each select="marc:datafield[@tag='024'][@ind1='2']">
14443 <xsl:call-template name="isInvalid">
14444 <xsl:with-param name="type">ismn</xsl:with-param>
14445 </xsl:call-template>
14446 <xsl:if test="marc:subfield[@code='a']">
14447 <identifier type="ismn">
14448 <xsl:value-of select="marc:subfield[@code='a']"/>
14452 <xsl:for-each select="marc:datafield[@tag='024'][@ind1='4']">
14453 <xsl:call-template name="isInvalid">
14454 <xsl:with-param name="type">sici</xsl:with-param>
14455 </xsl:call-template>
14456 <identifier type="sici">
14457 <xsl:call-template name="subfieldSelect">
14458 <xsl:with-param name="codes">ab</xsl:with-param>
14459 </xsl:call-template>
14462 <xsl:for-each select="marc:datafield[@tag='022']">
14463 <xsl:call-template name="isInvalid">
14464 <xsl:with-param name="type">issn</xsl:with-param>
14465 </xsl:call-template>
14466 <identifier type="issn">
14467 <xsl:value-of select="marc:subfield[@code='a']"/>
14470 <xsl:for-each select="marc:datafield[@tag='010']">
14471 <xsl:call-template name="isInvalid">
14472 <xsl:with-param name="type">lccn</xsl:with-param>
14473 </xsl:call-template>
14474 <identifier type="lccn">
14475 <xsl:value-of select="normalize-space(marc:subfield[@code='a'])"/>
14478 <xsl:for-each select="marc:datafield[@tag='028']">
14480 <xsl:attribute name="type">
14482 <xsl:when test="@ind1='0'">issue number</xsl:when>
14483 <xsl:when test="@ind1='1'">matrix number</xsl:when>
14484 <xsl:when test="@ind1='2'">music plate</xsl:when>
14485 <xsl:when test="@ind1='3'">music publisher</xsl:when>
14486 <xsl:when test="@ind1='4'">videorecording identifier</xsl:when>
14489 <!--<xsl:call-template name="isInvalid"/>--> <!-- no $z in 028 -->
14490 <xsl:call-template name="subfieldSelect">
14491 <xsl:with-param name="codes">
14493 <xsl:when test="@ind1='0'">ba</xsl:when>
14494 <xsl:otherwise>ab</xsl:otherwise>
14497 </xsl:call-template>
14500 <xsl:for-each select="marc:datafield[@tag='037']">
14501 <identifier type="stock number">
14502 <!--<xsl:call-template name="isInvalid"/>--> <!-- no $z in 037 -->
14503 <xsl:call-template name="subfieldSelect">
14504 <xsl:with-param name="codes">ab</xsl:with-param>
14505 </xsl:call-template>
14508 <xsl:for-each select="marc:datafield[@tag='856'][marc:subfield[@code='u']]">
14510 <xsl:attribute name="type">
14512 <xsl:when test="starts-with(marc:subfield[@code='u'],'urn:doi') or starts-with(marc:subfield[@code='u'],'doi')">doi</xsl:when>
14513 <xsl:when test="starts-with(marc:subfield[@code='u'],'urn:hdl') or starts-with(marc:subfield[@code='u'],'hdl') or starts-with(marc:subfield[@code='u'],'http://hdl.loc.gov')">hdl</xsl:when>
14514 <xsl:otherwise>uri</xsl:otherwise>
14518 <xsl:when test="starts-with(marc:subfield[@code='u'],'urn:hdl') or starts-with(marc:subfield[@code='u'],'hdl') or starts-with(marc:subfield[@code='u'],'http://hdl.loc.gov') ">
14519 <xsl:value-of select="concat('hdl:',substring-after(marc:subfield[@code='u'],'http://hdl.loc.gov/'))"></xsl:value-of>
14522 <xsl:value-of select="marc:subfield[@code='u']"></xsl:value-of>
14526 <xsl:if test="starts-with(marc:subfield[@code='u'],'urn:hdl') or starts-with(marc:subfield[@code='u'],'hdl')">
14527 <identifier type="hdl">
14528 <xsl:if test="marc:subfield[@code='y' or @code='3' or @code='z']">
14529 <xsl:attribute name="displayLabel">
14530 <xsl:call-template name="subfieldSelect">
14531 <xsl:with-param name="codes">y3z</xsl:with-param>
14532 </xsl:call-template>
14535 <xsl:value-of select="concat('hdl:',substring-after(marc:subfield[@code='u'],'http://hdl.loc.gov/'))"></xsl:value-of>
14539 <xsl:for-each select="marc:datafield[@tag=024][@ind1=1]">
14540 <identifier type="upc">
14541 <xsl:call-template name="isInvalid"/>
14542 <xsl:value-of select="marc:subfield[@code='a']"/>
14545 <!-- 1/04 fix added $y -->
14546 <xsl:for-each select="marc:datafield[@tag=856][marc:subfield[@code='u']]">
14549 <xsl:if test="marc:subfield[@code='y' or @code='3']">
14550 <xsl:attribute name="displayLabel">
14551 <xsl:call-template name="subfieldSelect">
14552 <xsl:with-param name="codes">y3</xsl:with-param>
14553 </xsl:call-template>
14556 <xsl:if test="marc:subfield[@code='z' ]">
14557 <xsl:attribute name="note">
14558 <xsl:call-template name="subfieldSelect">
14559 <xsl:with-param name="codes">z</xsl:with-param>
14560 </xsl:call-template>
14563 <xsl:value-of select="marc:subfield[@code='u']"></xsl:value-of>
14569 <!-- 3.2 change tmee 856z -->
14572 <xsl:for-each select="marc:datafield[@tag=852]">
14575 <xsl:call-template name="displayLabel"></xsl:call-template>
14576 <xsl:call-template name="subfieldSelect">
14577 <xsl:with-param name="codes">abje</xsl:with-param>
14578 </xsl:call-template>
14579 </physicalLocation>
14582 <xsl:for-each select="marc:datafield[@tag=506]">
14583 <accessCondition type="restrictionOnAccess">
14584 <xsl:call-template name="subfieldSelect">
14585 <xsl:with-param name="codes">abcd35</xsl:with-param>
14586 </xsl:call-template>
14589 <xsl:for-each select="marc:datafield[@tag=540]">
14590 <accessCondition type="useAndReproduction">
14591 <xsl:call-template name="subfieldSelect">
14592 <xsl:with-param name="codes">abcde35</xsl:with-param>
14593 </xsl:call-template>
14597 <xsl:for-each select="marc:datafield[@tag=040]">
14598 <recordContentSource authority="marcorg">
14599 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
14600 </recordContentSource>
14602 <xsl:for-each select="marc:controlfield[@tag=008]">
14603 <recordCreationDate encoding="marc">
14604 <xsl:value-of select="substring(.,1,6)"></xsl:value-of>
14605 </recordCreationDate>
14607 <xsl:for-each select="marc:controlfield[@tag=005]">
14608 <recordChangeDate encoding="iso8601">
14609 <xsl:value-of select="."></xsl:value-of>
14610 </recordChangeDate>
14612 <xsl:for-each select="marc:controlfield[@tag=001]">
14614 <xsl:if test="../marc:controlfield[@tag=003]">
14615 <xsl:attribute name="source">
14616 <xsl:value-of select="../marc:controlfield[@tag=003]"></xsl:value-of>
14619 <xsl:value-of select="."></xsl:value-of>
14620 </recordIdentifier>
14622 <xsl:for-each select="marc:datafield[@tag=040]/marc:subfield[@code='b']">
14623 <languageOfCataloging>
14624 <languageTerm authority="iso639-2b" type="code">
14625 <xsl:value-of select="."></xsl:value-of>
14627 </languageOfCataloging>
14631 <xsl:template name="displayForm">
14632 <xsl:for-each select="marc:subfield[@code='c']">
14634 <xsl:value-of select="."></xsl:value-of>
14638 <xsl:template name="affiliation">
14639 <xsl:for-each select="marc:subfield[@code='u']">
14641 <xsl:value-of select="."></xsl:value-of>
14645 <xsl:template name="uri">
14646 <xsl:for-each select="marc:subfield[@code='u']">
14647 <xsl:attribute name="xlink:href">
14648 <xsl:value-of select="."></xsl:value-of>
14652 <xsl:template name="role">
14653 <xsl:for-each select="marc:subfield[@code='e']">
14655 <roleTerm type="text">
14656 <xsl:value-of select="."></xsl:value-of>
14660 <xsl:for-each select="marc:subfield[@code='4']">
14662 <roleTerm authority="marcrelator" type="code">
14663 <xsl:value-of select="."></xsl:value-of>
14668 <xsl:template name="part">
14669 <xsl:variable name="partNumber">
14670 <xsl:call-template name="specialSubfieldSelect">
14671 <xsl:with-param name="axis">n</xsl:with-param>
14672 <xsl:with-param name="anyCodes">n</xsl:with-param>
14673 <xsl:with-param name="afterCodes">fgkdlmor</xsl:with-param>
14674 </xsl:call-template>
14676 <xsl:variable name="partName">
14677 <xsl:call-template name="specialSubfieldSelect">
14678 <xsl:with-param name="axis">p</xsl:with-param>
14679 <xsl:with-param name="anyCodes">p</xsl:with-param>
14680 <xsl:with-param name="afterCodes">fgkdlmor</xsl:with-param>
14681 </xsl:call-template>
14683 <xsl:if test="string-length(normalize-space($partNumber))">
14685 <xsl:call-template name="chopPunctuation">
14686 <xsl:with-param name="chopString" select="$partNumber"></xsl:with-param>
14687 </xsl:call-template>
14690 <xsl:if test="string-length(normalize-space($partName))">
14692 <xsl:call-template name="chopPunctuation">
14693 <xsl:with-param name="chopString" select="$partName"></xsl:with-param>
14694 </xsl:call-template>
14698 <xsl:template name="relatedPart">
14699 <xsl:if test="@tag=773">
14700 <xsl:for-each select="marc:subfield[@code='g']">
14703 <xsl:value-of select="."></xsl:value-of>
14707 <xsl:for-each select="marc:subfield[@code='q']">
14709 <xsl:call-template name="parsePart"></xsl:call-template>
14714 <xsl:template name="relatedPartNumName">
14715 <xsl:variable name="partNumber">
14716 <xsl:call-template name="specialSubfieldSelect">
14717 <xsl:with-param name="axis">g</xsl:with-param>
14718 <xsl:with-param name="anyCodes">g</xsl:with-param>
14719 <xsl:with-param name="afterCodes">pst</xsl:with-param>
14720 </xsl:call-template>
14722 <xsl:variable name="partName">
14723 <xsl:call-template name="specialSubfieldSelect">
14724 <xsl:with-param name="axis">p</xsl:with-param>
14725 <xsl:with-param name="anyCodes">p</xsl:with-param>
14726 <xsl:with-param name="afterCodes">fgkdlmor</xsl:with-param>
14727 </xsl:call-template>
14729 <xsl:if test="string-length(normalize-space($partNumber))">
14731 <xsl:value-of select="$partNumber"></xsl:value-of>
14734 <xsl:if test="string-length(normalize-space($partName))">
14736 <xsl:value-of select="$partName"></xsl:value-of>
14740 <xsl:template name="relatedName">
14741 <xsl:for-each select="marc:subfield[@code='a']">
14744 <xsl:value-of select="."></xsl:value-of>
14749 <xsl:template name="relatedForm">
14750 <xsl:for-each select="marc:subfield[@code='h']">
14751 <physicalDescription>
14753 <xsl:value-of select="."></xsl:value-of>
14755 </physicalDescription>
14758 <xsl:template name="relatedExtent">
14759 <xsl:for-each select="marc:subfield[@code='h']">
14760 <physicalDescription>
14762 <xsl:value-of select="."></xsl:value-of>
14764 </physicalDescription>
14767 <xsl:template name="relatedNote">
14768 <xsl:for-each select="marc:subfield[@code='n']">
14770 <xsl:value-of select="."></xsl:value-of>
14774 <xsl:template name="relatedSubject">
14775 <xsl:for-each select="marc:subfield[@code='j']">
14777 <temporal encoding="iso8601">
14778 <xsl:call-template name="chopPunctuation">
14779 <xsl:with-param name="chopString" select="."></xsl:with-param>
14780 </xsl:call-template>
14785 <xsl:template name="relatedIdentifierISSN">
14786 <xsl:for-each select="marc:subfield[@code='x']">
14787 <identifier type="issn">
14788 <xsl:value-of select="."></xsl:value-of>
14792 <xsl:template name="relatedIdentifierLocal">
14793 <xsl:for-each select="marc:subfield[@code='w']">
14794 <identifier type="local">
14795 <xsl:value-of select="."></xsl:value-of>
14799 <xsl:template name="relatedIdentifier">
14800 <xsl:for-each select="marc:subfield[@code='o']">
14802 <xsl:value-of select="."></xsl:value-of>
14806 <xsl:template name="relatedItem76X-78X">
14807 <xsl:call-template name="displayLabel"></xsl:call-template>
14808 <xsl:call-template name="relatedTitle76X-78X"></xsl:call-template>
14809 <xsl:call-template name="relatedName"></xsl:call-template>
14810 <xsl:call-template name="relatedOriginInfo"></xsl:call-template>
14811 <xsl:call-template name="relatedLanguage"></xsl:call-template>
14812 <xsl:call-template name="relatedExtent"></xsl:call-template>
14813 <xsl:call-template name="relatedNote"></xsl:call-template>
14814 <xsl:call-template name="relatedSubject"></xsl:call-template>
14815 <xsl:call-template name="relatedIdentifier"></xsl:call-template>
14816 <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
14817 <xsl:call-template name="relatedIdentifierLocal"></xsl:call-template>
14818 <xsl:call-template name="relatedPart"></xsl:call-template>
14820 <xsl:template name="subjectGeographicZ">
14822 <xsl:call-template name="chopPunctuation">
14823 <xsl:with-param name="chopString" select="."></xsl:with-param>
14824 </xsl:call-template>
14827 <xsl:template name="subjectTemporalY">
14829 <xsl:call-template name="chopPunctuation">
14830 <xsl:with-param name="chopString" select="."></xsl:with-param>
14831 </xsl:call-template>
14834 <xsl:template name="subjectTopic">
14836 <xsl:call-template name="chopPunctuation">
14837 <xsl:with-param name="chopString" select="."></xsl:with-param>
14838 </xsl:call-template>
14841 <!-- 3.2 change tmee 6xx $v genre -->
14842 <xsl:template name="subjectGenre">
14844 <xsl:call-template name="chopPunctuation">
14845 <xsl:with-param name="chopString" select="."></xsl:with-param>
14846 </xsl:call-template>
14850 <xsl:template name="nameABCDN">
14851 <xsl:for-each select="marc:subfield[@code='a']">
14853 <xsl:call-template name="chopPunctuation">
14854 <xsl:with-param name="chopString" select="."></xsl:with-param>
14855 </xsl:call-template>
14858 <xsl:for-each select="marc:subfield[@code='b']">
14860 <xsl:value-of select="."></xsl:value-of>
14863 <xsl:if test="marc:subfield[@code='c'] or marc:subfield[@code='d'] or marc:subfield[@code='n']">
14865 <xsl:call-template name="subfieldSelect">
14866 <xsl:with-param name="codes">cdn</xsl:with-param>
14867 </xsl:call-template>
14871 <xsl:template name="nameABCDQ">
14873 <xsl:call-template name="chopPunctuation">
14874 <xsl:with-param name="chopString">
14875 <xsl:call-template name="subfieldSelect">
14876 <xsl:with-param name="codes">aq</xsl:with-param>
14877 </xsl:call-template>
14879 <xsl:with-param name="punctuation">
14880 <xsl:text>:,;/ </xsl:text>
14882 </xsl:call-template>
14884 <xsl:call-template name="termsOfAddress"></xsl:call-template>
14885 <xsl:call-template name="nameDate"></xsl:call-template>
14887 <xsl:template name="nameACDEQ">
14889 <xsl:call-template name="subfieldSelect">
14890 <xsl:with-param name="codes">acdeq</xsl:with-param>
14891 </xsl:call-template>
14894 <xsl:template name="constituentOrRelatedType">
14895 <xsl:if test="@ind2=2">
14896 <xsl:attribute name="type">constituent</xsl:attribute>
14899 <xsl:template name="relatedTitle">
14900 <xsl:for-each select="marc:subfield[@code='t']">
14903 <xsl:call-template name="chopPunctuation">
14904 <xsl:with-param name="chopString">
14905 <xsl:value-of select="."></xsl:value-of>
14907 </xsl:call-template>
14912 <xsl:template name="relatedTitle76X-78X">
14913 <xsl:for-each select="marc:subfield[@code='t']">
14916 <xsl:call-template name="chopPunctuation">
14917 <xsl:with-param name="chopString">
14918 <xsl:value-of select="."></xsl:value-of>
14920 </xsl:call-template>
14922 <xsl:if test="marc:datafield[@tag!=773]and marc:subfield[@code='g']">
14923 <xsl:call-template name="relatedPartNumName"></xsl:call-template>
14927 <xsl:for-each select="marc:subfield[@code='p']">
14928 <titleInfo type="abbreviated">
14930 <xsl:call-template name="chopPunctuation">
14931 <xsl:with-param name="chopString">
14932 <xsl:value-of select="."></xsl:value-of>
14934 </xsl:call-template>
14936 <xsl:if test="marc:datafield[@tag!=773]and marc:subfield[@code='g']">
14937 <xsl:call-template name="relatedPartNumName"></xsl:call-template>
14941 <xsl:for-each select="marc:subfield[@code='s']">
14942 <titleInfo type="uniform">
14944 <xsl:call-template name="chopPunctuation">
14945 <xsl:with-param name="chopString">
14946 <xsl:value-of select="."></xsl:value-of>
14948 </xsl:call-template>
14950 <xsl:if test="marc:datafield[@tag!=773]and marc:subfield[@code='g']">
14951 <xsl:call-template name="relatedPartNumName"></xsl:call-template>
14956 <xsl:template name="relatedOriginInfo">
14957 <xsl:if test="marc:subfield[@code='b' or @code='d'] or marc:subfield[@code='f']">
14959 <xsl:if test="@tag=775">
14960 <xsl:for-each select="marc:subfield[@code='f']">
14963 <xsl:attribute name="type">code</xsl:attribute>
14964 <xsl:attribute name="authority">marcgac</xsl:attribute>
14965 <xsl:value-of select="."></xsl:value-of>
14970 <xsl:for-each select="marc:subfield[@code='d']">
14972 <xsl:value-of select="."></xsl:value-of>
14975 <xsl:for-each select="marc:subfield[@code='b']">
14977 <xsl:value-of select="."></xsl:value-of>
14983 <xsl:template name="relatedLanguage">
14984 <xsl:for-each select="marc:subfield[@code='e']">
14985 <xsl:call-template name="getLanguage">
14986 <xsl:with-param name="langString">
14987 <xsl:value-of select="."></xsl:value-of>
14989 </xsl:call-template>
14992 <xsl:template name="nameDate">
14993 <xsl:for-each select="marc:subfield[@code='d']">
14994 <namePart type="date">
14995 <xsl:call-template name="chopPunctuation">
14996 <xsl:with-param name="chopString" select="."></xsl:with-param>
14997 </xsl:call-template>
15001 <xsl:template name="subjectAuthority">
15002 <xsl:if test="@ind2!=4">
15003 <xsl:if test="@ind2!=' '">
15004 <xsl:if test="@ind2!=8">
15005 <xsl:if test="@ind2!=9">
15006 <xsl:attribute name="authority">
15008 <xsl:when test="@ind2=0">lcsh</xsl:when>
15009 <xsl:when test="@ind2=1">lcshac</xsl:when>
15010 <xsl:when test="@ind2=2">mesh</xsl:when>
15012 <xsl:when test="@ind2=3">nal</xsl:when>
15013 <xsl:when test="@ind2=5">csh</xsl:when>
15014 <xsl:when test="@ind2=6">rvm</xsl:when>
15015 <xsl:when test="@ind2=7">
15016 <xsl:value-of select="marc:subfield[@code='2']"></xsl:value-of>
15025 <xsl:template name="subjectAnyOrder">
15026 <xsl:for-each select="marc:subfield[@code='v' or @code='x' or @code='y' or @code='z']">
15028 <xsl:when test="@code='v'">
15029 <xsl:call-template name="subjectGenre"></xsl:call-template>
15031 <xsl:when test="@code='x'">
15032 <xsl:call-template name="subjectTopic"></xsl:call-template>
15034 <xsl:when test="@code='y'">
15035 <xsl:call-template name="subjectTemporalY"></xsl:call-template>
15037 <xsl:when test="@code='z'">
15038 <xsl:call-template name="subjectGeographicZ"></xsl:call-template>
15043 <xsl:template name="specialSubfieldSelect">
15044 <xsl:param name="anyCodes"></xsl:param>
15045 <xsl:param name="axis"></xsl:param>
15046 <xsl:param name="beforeCodes"></xsl:param>
15047 <xsl:param name="afterCodes"></xsl:param>
15048 <xsl:variable name="str">
15049 <xsl:for-each select="marc:subfield">
15050 <xsl:if test="contains($anyCodes, @code) or (contains($beforeCodes,@code) and following-sibling::marc:subfield[@code=$axis]) or (contains($afterCodes,@code) and preceding-sibling::marc:subfield[@code=$axis])">
15051 <xsl:value-of select="text()"></xsl:value-of>
15052 <xsl:text> </xsl:text>
15056 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
15059 <!-- 3.2 change tmee 6xx $v genre -->
15060 <xsl:template match="marc:datafield[@tag=600]">
15062 <xsl:call-template name="subjectAuthority"></xsl:call-template>
15063 <name type="personal">
15064 <xsl:call-template name="termsOfAddress"></xsl:call-template>
15066 <xsl:call-template name="chopPunctuation">
15067 <xsl:with-param name="chopString">
15068 <xsl:call-template name="subfieldSelect">
15069 <xsl:with-param name="codes">aq</xsl:with-param>
15070 </xsl:call-template>
15072 </xsl:call-template>
15074 <xsl:call-template name="nameDate"></xsl:call-template>
15075 <xsl:call-template name="affiliation"></xsl:call-template>
15076 <xsl:call-template name="role"></xsl:call-template>
15078 <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
15081 <xsl:template match="marc:datafield[@tag=610]">
15083 <xsl:call-template name="subjectAuthority"></xsl:call-template>
15084 <name type="corporate">
15085 <xsl:for-each select="marc:subfield[@code='a']">
15087 <xsl:value-of select="."></xsl:value-of>
15090 <xsl:for-each select="marc:subfield[@code='b']">
15092 <xsl:value-of select="."></xsl:value-of>
15095 <xsl:if test="marc:subfield[@code='c' or @code='d' or @code='n' or @code='p']">
15097 <xsl:call-template name="subfieldSelect">
15098 <xsl:with-param name="codes">cdnp</xsl:with-param>
15099 </xsl:call-template>
15102 <xsl:call-template name="role"></xsl:call-template>
15104 <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
15107 <xsl:template match="marc:datafield[@tag=611]">
15109 <xsl:call-template name="subjectAuthority"></xsl:call-template>
15110 <name type="conference">
15112 <xsl:call-template name="subfieldSelect">
15113 <xsl:with-param name="codes">abcdeqnp</xsl:with-param>
15114 </xsl:call-template>
15116 <xsl:for-each select="marc:subfield[@code='4']">
15118 <roleTerm authority="marcrelator" type="code">
15119 <xsl:value-of select="."></xsl:value-of>
15124 <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
15127 <xsl:template match="marc:datafield[@tag=630]">
15129 <xsl:call-template name="subjectAuthority"></xsl:call-template>
15132 <xsl:call-template name="chopPunctuation">
15133 <xsl:with-param name="chopString">
15134 <xsl:call-template name="subfieldSelect">
15135 <xsl:with-param name="codes">adfhklor</xsl:with-param>
15136 </xsl:call-template>
15138 </xsl:call-template>
15139 <xsl:call-template name="part"></xsl:call-template>
15142 <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
15145 <xsl:template match="marc:datafield[@tag=650]">
15147 <xsl:call-template name="subjectAuthority"></xsl:call-template>
15149 <xsl:call-template name="chopPunctuation">
15150 <xsl:with-param name="chopString">
15151 <xsl:call-template name="subfieldSelect">
15152 <xsl:with-param name="codes">abcd</xsl:with-param>
15153 </xsl:call-template>
15155 </xsl:call-template>
15157 <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
15160 <xsl:template match="marc:datafield[@tag=651]">
15162 <xsl:call-template name="subjectAuthority"></xsl:call-template>
15163 <xsl:for-each select="marc:subfield[@code='a']">
15165 <xsl:call-template name="chopPunctuation">
15166 <xsl:with-param name="chopString" select="."></xsl:with-param>
15167 </xsl:call-template>
15170 <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
15173 <xsl:template match="marc:datafield[@tag=653]">
15175 <xsl:for-each select="marc:subfield[@code='a']">
15177 <xsl:value-of select="."></xsl:value-of>
15182 <xsl:template match="marc:datafield[@tag=656]">
15184 <xsl:if test="marc:subfield[@code=2]">
15185 <xsl:attribute name="authority">
15186 <xsl:value-of select="marc:subfield[@code=2]"></xsl:value-of>
15190 <xsl:call-template name="chopPunctuation">
15191 <xsl:with-param name="chopString">
15192 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
15194 </xsl:call-template>
15198 <xsl:template name="termsOfAddress">
15199 <xsl:if test="marc:subfield[@code='b' or @code='c']">
15200 <namePart type="termsOfAddress">
15201 <xsl:call-template name="chopPunctuation">
15202 <xsl:with-param name="chopString">
15203 <xsl:call-template name="subfieldSelect">
15204 <xsl:with-param name="codes">bc</xsl:with-param>
15205 </xsl:call-template>
15207 </xsl:call-template>
15211 <xsl:template name="displayLabel">
15212 <xsl:if test="marc:subfield[@code='i']">
15213 <xsl:attribute name="displayLabel">
15214 <xsl:value-of select="marc:subfield[@code='i']"></xsl:value-of>
15217 <xsl:if test="marc:subfield[@code='3']">
15218 <xsl:attribute name="displayLabel">
15219 <xsl:value-of select="marc:subfield[@code='3']"></xsl:value-of>
15223 <xsl:template name="isInvalid">
15224 <xsl:param name="type"/>
15225 <xsl:if test="marc:subfield[@code='z'] or marc:subfield[@code='y']">
15227 <xsl:attribute name="type">
15228 <xsl:value-of select="$type"/>
15230 <xsl:attribute name="invalid">
15231 <xsl:text>yes</xsl:text>
15233 <xsl:if test="marc:subfield[@code='z']">
15234 <xsl:value-of select="marc:subfield[@code='z']"/>
15236 <xsl:if test="marc:subfield[@code='y']">
15237 <xsl:value-of select="marc:subfield[@code='y']"/>
15242 <xsl:template name="subtitle">
15243 <xsl:if test="marc:subfield[@code='b']">
15245 <xsl:call-template name="chopPunctuation">
15246 <xsl:with-param name="chopString">
15247 <xsl:value-of select="marc:subfield[@code='b']"/>
15248 <!--<xsl:call-template name="subfieldSelect">
15249 <xsl:with-param name="codes">b</xsl:with-param>
15250 </xsl:call-template>-->
15252 </xsl:call-template>
15256 <xsl:template name="script">
15257 <xsl:param name="scriptCode"></xsl:param>
15258 <xsl:attribute name="script">
15260 <xsl:when test="$scriptCode='(3'">Arabic</xsl:when>
15261 <xsl:when test="$scriptCode='(B'">Latin</xsl:when>
15262 <xsl:when test="$scriptCode='$1'">Chinese, Japanese, Korean</xsl:when>
15263 <xsl:when test="$scriptCode='(N'">Cyrillic</xsl:when>
15264 <xsl:when test="$scriptCode='(2'">Hebrew</xsl:when>
15265 <xsl:when test="$scriptCode='(S'">Greek</xsl:when>
15269 <xsl:template name="parsePart">
15270 <!-- assumes 773$q= 1:2:3<4
15271 with up to 3 levels and one optional start page
15273 <xsl:variable name="level1">
15275 <xsl:when test="contains(text(),':')">
15277 <xsl:value-of select="substring-before(text(),':')"></xsl:value-of>
15279 <xsl:when test="not(contains(text(),':'))">
15281 <xsl:if test="contains(text(),'<')">
15283 <xsl:value-of select="substring-before(text(),'<')"></xsl:value-of>
15285 <xsl:if test="not(contains(text(),'<'))">
15287 <xsl:value-of select="text()"></xsl:value-of>
15292 <xsl:variable name="sici2">
15294 <xsl:when test="starts-with(substring-after(text(),$level1),':')">
15295 <xsl:value-of select="substring(substring-after(text(),$level1),2)"></xsl:value-of>
15298 <xsl:value-of select="substring-after(text(),$level1)"></xsl:value-of>
15302 <xsl:variable name="level2">
15304 <xsl:when test="contains($sici2,':')">
15306 <xsl:value-of select="substring-before($sici2,':')"></xsl:value-of>
15308 <xsl:when test="contains($sici2,'<')">
15310 <xsl:value-of select="substring-before($sici2,'<')"></xsl:value-of>
15313 <xsl:value-of select="$sici2"></xsl:value-of>
15318 <xsl:variable name="sici3">
15320 <xsl:when test="starts-with(substring-after($sici2,$level2),':')">
15321 <xsl:value-of select="substring(substring-after($sici2,$level2),2)"></xsl:value-of>
15324 <xsl:value-of select="substring-after($sici2,$level2)"></xsl:value-of>
15328 <xsl:variable name="level3">
15330 <xsl:when test="contains($sici3,'<')">
15332 <xsl:value-of select="substring-before($sici3,'<')"></xsl:value-of>
15335 <xsl:value-of select="$sici3"></xsl:value-of>
15340 <xsl:variable name="page">
15341 <xsl:if test="contains(text(),'<')">
15342 <xsl:value-of select="substring-after(text(),'<')"></xsl:value-of>
15345 <xsl:if test="$level1">
15348 <xsl:value-of select="$level1"></xsl:value-of>
15352 <xsl:if test="$level2">
15355 <xsl:value-of select="$level2"></xsl:value-of>
15359 <xsl:if test="$level3">
15362 <xsl:value-of select="$level3"></xsl:value-of>
15366 <xsl:if test="$page">
15367 <extent unit="page">
15369 <xsl:value-of select="$page"></xsl:value-of>
15374 <xsl:template name="getLanguage">
15375 <xsl:param name="langString"></xsl:param>
15376 <xsl:param name="controlField008-35-37"></xsl:param>
15377 <xsl:variable name="length" select="string-length($langString)"></xsl:variable>
15379 <xsl:when test="$length=0"></xsl:when>
15380 <xsl:when test="$controlField008-35-37=substring($langString,1,3)">
15381 <xsl:call-template name="getLanguage">
15382 <xsl:with-param name="langString" select="substring($langString,4,$length)"></xsl:with-param>
15383 <xsl:with-param name="controlField008-35-37" select="$controlField008-35-37"></xsl:with-param>
15384 </xsl:call-template>
15388 <languageTerm authority="iso639-2b" type="code">
15389 <xsl:value-of select="substring($langString,1,3)"></xsl:value-of>
15392 <xsl:call-template name="getLanguage">
15393 <xsl:with-param name="langString" select="substring($langString,4,$length)"></xsl:with-param>
15394 <xsl:with-param name="controlField008-35-37" select="$controlField008-35-37"></xsl:with-param>
15395 </xsl:call-template>
15399 <xsl:template name="isoLanguage">
15400 <xsl:param name="currentLanguage"></xsl:param>
15401 <xsl:param name="usedLanguages"></xsl:param>
15402 <xsl:param name="remainingLanguages"></xsl:param>
15404 <xsl:when test="string-length($currentLanguage)=0"></xsl:when>
15405 <xsl:when test="not(contains($usedLanguages, $currentLanguage))">
15407 <xsl:if test="@code!='a'">
15408 <xsl:attribute name="objectPart">
15410 <xsl:when test="@code='b'">summary or subtitle</xsl:when>
15411 <xsl:when test="@code='d'">sung or spoken text</xsl:when>
15412 <xsl:when test="@code='e'">libretto</xsl:when>
15413 <xsl:when test="@code='f'">table of contents</xsl:when>
15414 <xsl:when test="@code='g'">accompanying material</xsl:when>
15415 <xsl:when test="@code='h'">translation</xsl:when>
15419 <languageTerm authority="iso639-2b" type="code">
15420 <xsl:value-of select="$currentLanguage"></xsl:value-of>
15423 <xsl:call-template name="isoLanguage">
15424 <xsl:with-param name="currentLanguage">
15425 <xsl:value-of select="substring($remainingLanguages,1,3)"></xsl:value-of>
15427 <xsl:with-param name="usedLanguages">
15428 <xsl:value-of select="concat($usedLanguages,$currentLanguage)"></xsl:value-of>
15430 <xsl:with-param name="remainingLanguages">
15431 <xsl:value-of select="substring($remainingLanguages,4,string-length($remainingLanguages))"></xsl:value-of>
15433 </xsl:call-template>
15436 <xsl:call-template name="isoLanguage">
15437 <xsl:with-param name="currentLanguage">
15438 <xsl:value-of select="substring($remainingLanguages,1,3)"></xsl:value-of>
15440 <xsl:with-param name="usedLanguages">
15441 <xsl:value-of select="concat($usedLanguages,$currentLanguage)"></xsl:value-of>
15443 <xsl:with-param name="remainingLanguages">
15444 <xsl:value-of select="substring($remainingLanguages,4,string-length($remainingLanguages))"></xsl:value-of>
15446 </xsl:call-template>
15450 <xsl:template name="chopBrackets">
15451 <xsl:param name="chopString"></xsl:param>
15452 <xsl:variable name="string">
15453 <xsl:call-template name="chopPunctuation">
15454 <xsl:with-param name="chopString" select="$chopString"></xsl:with-param>
15455 </xsl:call-template>
15457 <xsl:if test="substring($string, 1,1)='['">
15458 <xsl:value-of select="substring($string,2, string-length($string)-2)"></xsl:value-of>
15460 <xsl:if test="substring($string, 1,1)!='['">
15461 <xsl:value-of select="$string"></xsl:value-of>
15464 <xsl:template name="rfcLanguages">
15465 <xsl:param name="nodeNum"></xsl:param>
15466 <xsl:param name="usedLanguages"></xsl:param>
15467 <xsl:param name="controlField008-35-37"></xsl:param>
15468 <xsl:variable name="currentLanguage" select="."></xsl:variable>
15470 <xsl:when test="not($currentLanguage)"></xsl:when>
15471 <xsl:when test="$currentLanguage!=$controlField008-35-37 and $currentLanguage!='rfc3066'">
15472 <xsl:if test="not(contains($usedLanguages,$currentLanguage))">
15474 <xsl:if test="@code!='a'">
15475 <xsl:attribute name="objectPart">
15477 <xsl:when test="@code='b'">summary or subtitle</xsl:when>
15478 <xsl:when test="@code='d'">sung or spoken text</xsl:when>
15479 <xsl:when test="@code='e'">libretto</xsl:when>
15480 <xsl:when test="@code='f'">table of contents</xsl:when>
15481 <xsl:when test="@code='g'">accompanying material</xsl:when>
15482 <xsl:when test="@code='h'">translation</xsl:when>
15486 <languageTerm authority="rfc3066" type="code">
15487 <xsl:value-of select="$currentLanguage"/>
15496 <xsl:template name="datafield">
15497 <xsl:param name="tag"/>
15498 <xsl:param name="ind1"><xsl:text> </xsl:text></xsl:param>
15499 <xsl:param name="ind2"><xsl:text> </xsl:text></xsl:param>
15500 <xsl:param name="subfields"/>
15501 <xsl:element name="marc:datafield">
15502 <xsl:attribute name="tag">
15503 <xsl:value-of select="$tag"/>
15505 <xsl:attribute name="ind1">
15506 <xsl:value-of select="$ind1"/>
15508 <xsl:attribute name="ind2">
15509 <xsl:value-of select="$ind2"/>
15511 <xsl:copy-of select="$subfields"/>
15515 <xsl:template name="subfieldSelect">
15516 <xsl:param name="codes"/>
15517 <xsl:param name="delimeter"><xsl:text> </xsl:text></xsl:param>
15518 <xsl:variable name="str">
15519 <xsl:for-each select="marc:subfield">
15520 <xsl:if test="contains($codes, @code)">
15521 <xsl:value-of select="text()"/><xsl:value-of select="$delimeter"/>
15525 <xsl:value-of select="substring($str,1,string-length($str)-string-length($delimeter))"/>
15528 <xsl:template name="buildSpaces">
15529 <xsl:param name="spaces"/>
15530 <xsl:param name="char"><xsl:text> </xsl:text></xsl:param>
15531 <xsl:if test="$spaces>0">
15532 <xsl:value-of select="$char"/>
15533 <xsl:call-template name="buildSpaces">
15534 <xsl:with-param name="spaces" select="$spaces - 1"/>
15535 <xsl:with-param name="char" select="$char"/>
15536 </xsl:call-template>
15540 <xsl:template name="chopPunctuation">
15541 <xsl:param name="chopString"/>
15542 <xsl:param name="punctuation"><xsl:text>.:,;/ </xsl:text></xsl:param>
15543 <xsl:variable name="length" select="string-length($chopString)"/>
15545 <xsl:when test="$length=0"/>
15546 <xsl:when test="contains($punctuation, substring($chopString,$length,1))">
15547 <xsl:call-template name="chopPunctuation">
15548 <xsl:with-param name="chopString" select="substring($chopString,1,$length - 1)"/>
15549 <xsl:with-param name="punctuation" select="$punctuation"/>
15550 </xsl:call-template>
15552 <xsl:when test="not($chopString)"/>
15553 <xsl:otherwise><xsl:value-of select="$chopString"/></xsl:otherwise>
15557 <xsl:template name="chopPunctuationFront">
15558 <xsl:param name="chopString"/>
15559 <xsl:variable name="length" select="string-length($chopString)"/>
15561 <xsl:when test="$length=0"/>
15562 <xsl:when test="contains('.:,;/[ ', substring($chopString,1,1))">
15563 <xsl:call-template name="chopPunctuationFront">
15564 <xsl:with-param name="chopString" select="substring($chopString,2,$length - 1)"/>
15565 </xsl:call-template>
15567 <xsl:when test="not($chopString)"/>
15568 <xsl:otherwise><xsl:value-of select="$chopString"/></xsl:otherwise>
15571 </xsl:stylesheet>$$ WHERE name = 'mods32';
15573 -- Currently, the only difference from naco_normalize is that search_normalize
15574 -- turns apostrophes into spaces, while naco_normalize collapses them.
15575 CREATE OR REPLACE FUNCTION public.search_normalize( TEXT, TEXT ) RETURNS TEXT AS $func$
15578 use Unicode::Normalize;
15581 my $str = decode_utf8(shift);
15584 # Apply NACO normalization to input string; based on
15585 # http://www.loc.gov/catdir/pcc/naco/SCA_PccNormalization_Final_revised.pdf
15587 # Note that unlike a strict reading of the NACO normalization rules,
15588 # output is returned as lowercase instead of uppercase for compatibility
15589 # with previous versions of the Evergreen naco_normalize routine.
15591 # Convert to upper-case first; even though final output will be lowercase, doing this will
15592 # ensure that the German eszett (ß) and certain ligatures (ff, fi, ffl, etc.) will be handled correctly.
15593 # If there are any bugs in Perl's implementation of upcasing, they will be passed through here.
15596 # remove non-filing strings
15597 $str =~ s/\x{0098}.*?\x{009C}//g;
15601 # additional substitutions - 3.6.
15602 $str =~ s/\x{00C6}/AE/g;
15603 $str =~ s/\x{00DE}/TH/g;
15604 $str =~ s/\x{0152}/OE/g;
15605 $str =~ tr/\x{0110}\x{00D0}\x{00D8}\x{0141}\x{2113}\x{02BB}\x{02BC}][/DDOLl/d;
15607 # transformations based on Unicode category codes
15608 $str =~ s/[\p{Cc}\p{Cf}\p{Co}\p{Cs}\p{Lm}\p{Mc}\p{Me}\p{Mn}]//g;
15610 if ($sf && $sf =~ /^a/o) {
15611 my $commapos = index($str, ',');
15612 if ($commapos > -1) {
15613 if ($commapos != length($str) - 1) {
15614 $str =~ s/,/\x07/; # preserve first comma
15619 # since we've stripped out the control characters, we can now
15620 # use a few as placeholders temporarily
15621 $str =~ tr/+&@\x{266D}\x{266F}#/\x01\x02\x03\x04\x05\x06/;
15622 $str =~ s/[\p{Pc}\p{Pd}\p{Pe}\p{Pf}\p{Pi}\p{Po}\p{Ps}\p{Sk}\p{Sm}\p{So}\p{Zl}\p{Zp}\p{Zs}]/ /g;
15623 $str =~ tr/\x01\x02\x03\x04\x05\x06\x07/+&@\x{266D}\x{266F}#,/;
15626 $str =~ tr/\x{0660}-\x{0669}\x{06F0}-\x{06F9}\x{07C0}-\x{07C9}\x{0966}-\x{096F}\x{09E6}-\x{09EF}\x{0A66}-\x{0A6F}\x{0AE6}-\x{0AEF}\x{0B66}-\x{0B6F}\x{0BE6}-\x{0BEF}\x{0C66}-\x{0C6F}\x{0CE6}-\x{0CEF}\x{0D66}-\x{0D6F}\x{0E50}-\x{0E59}\x{0ED0}-\x{0ED9}\x{0F20}-\x{0F29}\x{1040}-\x{1049}\x{1090}-\x{1099}\x{17E0}-\x{17E9}\x{1810}-\x{1819}\x{1946}-\x{194F}\x{19D0}-\x{19D9}\x{1A80}-\x{1A89}\x{1A90}-\x{1A99}\x{1B50}-\x{1B59}\x{1BB0}-\x{1BB9}\x{1C40}-\x{1C49}\x{1C50}-\x{1C59}\x{A620}-\x{A629}\x{A8D0}-\x{A8D9}\x{A900}-\x{A909}\x{A9D0}-\x{A9D9}\x{AA50}-\x{AA59}\x{ABF0}-\x{ABF9}\x{FF10}-\x{FF19}/0-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-9/;
15628 # intentionally skipping step 8 of the NACO algorithm; if the string
15629 # gets normalized away, that's fine.
15631 # leading and trailing spaces
15637 $func$ LANGUAGE 'plperlu' STRICT IMMUTABLE;
15639 CREATE OR REPLACE FUNCTION public.search_normalize_keep_comma( TEXT ) RETURNS TEXT AS $func$
15640 SELECT public.search_normalize($1,'a');
15641 $func$ LANGUAGE SQL STRICT IMMUTABLE;
15643 CREATE OR REPLACE FUNCTION public.search_normalize( TEXT ) RETURNS TEXT AS $func$
15644 SELECT public.search_normalize($1,'');
15645 $func$ LANGUAGE 'sql' STRICT IMMUTABLE;
15647 INSERT INTO config.index_normalizer (name, description, func, param_count) VALUES (
15648 'Search Normalize',
15649 'Apply search normalization rules to the extracted text. A less extreme version of NACO normalization.',
15650 'search_normalize',
15654 UPDATE config.metabib_field_index_norm_map
15656 SELECT id FROM config.index_normalizer WHERE func = 'search_normalize'
15659 SELECT id FROM config.index_normalizer WHERE func = 'naco_normalize'
15664 -- This could take a long time if you have a very non-English bib database
15665 -- Run it outside of a transaction to avoid lock escalation
15666 SELECT metabib.reingest_metabib_field_entries(record)
15667 FROM metabib.full_rec
15670 AND value LIKE '%''%'
15675 -- This is split out because it takes forever to run on large bib collections.
15676 \qecho ************************************************************************
15677 \qecho The following transaction, wrapping upgrades 0679 and 0680, may take a
15678 \qecho *really* long time, and you might be able to run it by itself in
15679 \qecho parallel with other operations using a separate session.
15680 \qecho ************************************************************************
15683 SELECT evergreen.upgrade_deps_block_check('0679', :eg_version);
15685 -- Address typo in column name
15686 ALTER TABLE config.metabib_class ADD COLUMN buoyant BOOL DEFAULT FALSE NOT NULL;
15687 UPDATE config.metabib_class SET buoyant = bouyant;
15688 ALTER TABLE config.metabib_class DROP COLUMN bouyant;
15690 CREATE OR REPLACE FUNCTION oils_tsearch2 () RETURNS TRIGGER AS $$
15696 value := NEW.value;
15698 IF TG_TABLE_NAME::TEXT ~ 'field_entry$' THEN
15700 SELECT n.func AS func,
15701 n.param_count AS param_count,
15703 FROM config.index_normalizer n
15704 JOIN config.metabib_field_index_norm_map m ON (m.norm = n.id)
15705 WHERE field = NEW.field AND m.pos < 0
15706 ORDER BY m.pos LOOP
15707 EXECUTE 'SELECT ' || normalizer.func || '(' ||
15708 quote_literal( value ) ||
15710 WHEN normalizer.param_count > 0
15711 THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
15718 NEW.value := value;
15721 IF NEW.index_vector = ''::tsvector THEN
15725 IF TG_TABLE_NAME::TEXT ~ 'field_entry$' THEN
15727 SELECT n.func AS func,
15728 n.param_count AS param_count,
15730 FROM config.index_normalizer n
15731 JOIN config.metabib_field_index_norm_map m ON (m.norm = n.id)
15732 WHERE field = NEW.field AND m.pos >= 0
15733 ORDER BY m.pos LOOP
15734 EXECUTE 'SELECT ' || normalizer.func || '(' ||
15735 quote_literal( value ) ||
15737 WHEN normalizer.param_count > 0
15738 THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
15746 IF TG_TABLE_NAME::TEXT ~ 'browse_entry$' THEN
15747 value := ARRAY_TO_STRING(
15748 evergreen.regexp_split_to_array(value, E'\\W+'), ' '
15750 value := public.search_normalize(value);
15753 NEW.index_vector = to_tsvector((TG_ARGV[0])::regconfig, value);
15757 $$ LANGUAGE PLPGSQL;
15759 -- Given a string such as a user might type into a search box, prepare
15760 -- two changed variants for TO_TSQUERY(). See
15761 -- http://www.postgresql.org/docs/9.0/static/textsearch-controls.html
15762 -- The first variant is normalized to match indexed documents regardless
15763 -- of diacritics. The second variant keeps its diacritics for proper
15764 -- highlighting via TS_HEADLINE().
15766 FUNCTION metabib.autosuggest_prepare_tsquery(orig TEXT) RETURNS TEXT[] AS
15769 orig_ended_in_space BOOLEAN;
15774 orig_ended_in_space := orig ~ E'\\s$';
15776 orig := ARRAY_TO_STRING(
15777 evergreen.regexp_split_to_array(orig, E'\\W+'), ' '
15780 normalized := public.search_normalize(orig); -- also trim()s
15781 plain := trim(orig);
15783 IF NOT orig_ended_in_space THEN
15784 plain := plain || ':*';
15785 normalized := normalized || ':*';
15788 plain := ARRAY_TO_STRING(
15789 evergreen.regexp_split_to_array(plain, E'\\s+'), ' & '
15791 normalized := ARRAY_TO_STRING(
15792 evergreen.regexp_split_to_array(normalized, E'\\s+'), ' & '
15795 RETURN ARRAY[normalized, plain];
15797 $$ LANGUAGE PLPGSQL;
15800 -- Definition of OUT parameters changes, so must drop first
15801 DROP FUNCTION IF EXISTS metabib.suggest_browse_entries (TEXT, TEXT, TEXT, INTEGER, INTEGER, INTEGER);
15804 FUNCTION metabib.suggest_browse_entries(
15805 raw_query_text TEXT, -- actually typed by humans at the UI level
15806 search_class TEXT, -- 'alias' or 'class' or 'class|field..', etc
15807 headline_opts TEXT, -- markup options for ts_headline()
15808 visibility_org INTEGER,-- null if you don't want opac visibility test
15809 query_limit INTEGER,-- use in LIMIT clause of interal query
15810 normalization INTEGER -- argument to TS_RANK_CD()
15812 value TEXT, -- plain
15814 buoyant_and_class_match BOOL,
15816 field_weight INTEGER,
15819 match TEXT -- marked up
15822 prepared_query_texts TEXT[];
15824 plain_query TSQUERY;
15825 opac_visibility_join TEXT;
15826 search_class_join TEXT;
15829 prepared_query_texts := metabib.autosuggest_prepare_tsquery(raw_query_text);
15831 query := TO_TSQUERY('keyword', prepared_query_texts[1]);
15832 plain_query := TO_TSQUERY('keyword', prepared_query_texts[2]);
15834 IF visibility_org IS NOT NULL THEN
15835 opac_visibility_join := '
15836 JOIN asset.opac_visible_copies aovc ON (
15837 aovc.record = mbedm.source AND
15838 aovc.circ_lib IN (SELECT id FROM actor.org_unit_descendants($4))
15841 opac_visibility_join := '';
15844 -- The following determines whether we only provide suggestsons matching
15845 -- the user's selected search_class, or whether we show other suggestions
15846 -- too. The reason for MIN() is that for search_classes like
15847 -- 'title|proper|uniform' you would otherwise get multiple rows. The
15848 -- implication is that if title as a class doesn't have restrict,
15849 -- nor does the proper field, but the uniform field does, you're going
15850 -- to get 'false' for your overall evaluation of 'should we restrict?'
15851 -- To invert that, change from MIN() to MAX().
15855 MIN(cmc.restrict::INT) AS restrict_class,
15856 MIN(cmf.restrict::INT) AS restrict_field
15857 FROM metabib.search_class_to_registered_components(search_class)
15858 AS _registered (field_class TEXT, field INT)
15860 config.metabib_class cmc ON (cmc.name = _registered.field_class)
15862 config.metabib_field cmf ON (cmf.id = _registered.field);
15864 -- evaluate 'should we restrict?'
15865 IF r_fields.restrict_field::BOOL OR r_fields.restrict_class::BOOL THEN
15866 search_class_join := '
15868 metabib.search_class_to_registered_components($2)
15869 AS _registered (field_class TEXT, field INT) ON (
15870 (_registered.field IS NULL AND
15871 _registered.field_class = cmf.field_class) OR
15872 (_registered.field = cmf.id)
15876 search_class_join := '
15878 metabib.search_class_to_registered_components($2)
15879 AS _registered (field_class TEXT, field INT) ON (
15880 _registered.field_class = cmc.name
15885 RETURN QUERY EXECUTE 'SELECT *, TS_HEADLINE(value, $7, $3) FROM (SELECT DISTINCT
15888 cmc.buoyant AND _registered.field_class IS NOT NULL,
15889 _registered.field = cmf.id,
15891 TS_RANK_CD(mbe.index_vector, $1, $6),
15893 FROM metabib.browse_entry_def_map mbedm
15894 JOIN metabib.browse_entry mbe ON (mbe.id = mbedm.entry)
15895 JOIN config.metabib_field cmf ON (cmf.id = mbedm.def)
15896 JOIN config.metabib_class cmc ON (cmf.field_class = cmc.name)
15897 ' || search_class_join || opac_visibility_join ||
15898 ' WHERE $1 @@ mbe.index_vector
15899 ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
15901 ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
15902 ' -- sic, repeat the order by clause in the outer select too
15904 query, search_class, headline_opts,
15905 visibility_org, query_limit, normalization, plain_query
15909 -- buoyant AND chosen class = match class
15910 -- chosen field = match field
15917 $func$ LANGUAGE PLPGSQL;
15921 \qecho The following takes about a minute per 100,000 rows in
15922 \qecho metabib.browse_entry on my development system, which is only a VM with
15923 \qecho 4 GB of memory and 2 cores.
15925 \qecho The following is a very loose estimate of how long the next UPDATE
15926 \qecho statement would take to finish on MY machine, based on YOUR number
15927 \qecho of rows in metabib.browse_entry:
15930 SELECT (COUNT(id) / 100000.0) * INTERVAL '1 minute'
15931 AS "approximate duration of following UPDATE statement"
15932 FROM metabib.browse_entry;
15934 UPDATE metabib.browse_entry SET index_vector = TO_TSVECTOR(
15936 public.search_normalize(
15938 evergreen.regexp_split_to_array(value, E'\\W+'), ' '
15944 SELECT evergreen.upgrade_deps_block_check('0680', :eg_version);
15946 -- Not much use in having identifier-class fields be suggestions. Credit for the idea goes to Ben Shum.
15947 UPDATE config.metabib_field SET browse_field = FALSE WHERE id < 100 AND field_class = 'identifier';
15950 ---------------------------------------------------------------------------
15951 -- The rest of this was tested on Evergreen Indiana's dev server, which has
15952 -- a large data set of 2.6M bibs, and was instrumental in sussing out the
15953 -- needed adjustments. Thanks, EG-IN!
15954 ---------------------------------------------------------------------------
15956 -- GIN indexes are /much/ better for prefix matching, which is important for browse and autosuggest
15957 --Commented out the creation earlier, so we don't need to drop it here.
15958 --DROP INDEX metabib.metabib_browse_entry_index_vector_idx;
15959 CREATE INDEX metabib_browse_entry_index_vector_idx ON metabib.browse_entry USING GIN (index_vector);
15962 -- We need thes to make the autosuggest limiting joins fast
15963 CREATE INDEX browse_entry_def_map_def_idx ON metabib.browse_entry_def_map (def);
15964 CREATE INDEX browse_entry_def_map_entry_idx ON metabib.browse_entry_def_map (entry);
15965 CREATE INDEX browse_entry_def_map_source_idx ON metabib.browse_entry_def_map (source);
15967 -- In practice this will always be ~1 row, and the default of 1000 causes terrible plans
15968 ALTER FUNCTION metabib.search_class_to_registered_components(text) ROWS 1;
15970 -- Reworking of the generated query to act in a sane manner in the face of large datasets
15972 FUNCTION metabib.suggest_browse_entries(
15973 raw_query_text TEXT, -- actually typed by humans at the UI level
15974 search_class TEXT, -- 'alias' or 'class' or 'class|field..', etc
15975 headline_opts TEXT, -- markup options for ts_headline()
15976 visibility_org INTEGER,-- null if you don't want opac visibility test
15977 query_limit INTEGER,-- use in LIMIT clause of interal query
15978 normalization INTEGER -- argument to TS_RANK_CD()
15980 value TEXT, -- plain
15982 buoyant_and_class_match BOOL,
15984 field_weight INTEGER,
15987 match TEXT -- marked up
15990 prepared_query_texts TEXT[];
15992 plain_query TSQUERY;
15993 opac_visibility_join TEXT;
15994 search_class_join TEXT;
15997 prepared_query_texts := metabib.autosuggest_prepare_tsquery(raw_query_text);
15999 query := TO_TSQUERY('keyword', prepared_query_texts[1]);
16000 plain_query := TO_TSQUERY('keyword', prepared_query_texts[2]);
16002 IF visibility_org IS NOT NULL THEN
16003 opac_visibility_join := '
16004 JOIN asset.opac_visible_copies aovc ON (
16005 aovc.record = x.source AND
16006 aovc.circ_lib IN (SELECT id FROM actor.org_unit_descendants($4))
16009 opac_visibility_join := '';
16012 -- The following determines whether we only provide suggestsons matching
16013 -- the user's selected search_class, or whether we show other suggestions
16014 -- too. The reason for MIN() is that for search_classes like
16015 -- 'title|proper|uniform' you would otherwise get multiple rows. The
16016 -- implication is that if title as a class doesn't have restrict,
16017 -- nor does the proper field, but the uniform field does, you're going
16018 -- to get 'false' for your overall evaluation of 'should we restrict?'
16019 -- To invert that, change from MIN() to MAX().
16023 MIN(cmc.restrict::INT) AS restrict_class,
16024 MIN(cmf.restrict::INT) AS restrict_field
16025 FROM metabib.search_class_to_registered_components(search_class)
16026 AS _registered (field_class TEXT, field INT)
16028 config.metabib_class cmc ON (cmc.name = _registered.field_class)
16030 config.metabib_field cmf ON (cmf.id = _registered.field);
16032 -- evaluate 'should we restrict?'
16033 IF r_fields.restrict_field::BOOL OR r_fields.restrict_class::BOOL THEN
16034 search_class_join := '
16036 metabib.search_class_to_registered_components($2)
16037 AS _registered (field_class TEXT, field INT) ON (
16038 (_registered.field IS NULL AND
16039 _registered.field_class = cmf.field_class) OR
16040 (_registered.field = cmf.id)
16044 search_class_join := '
16046 metabib.search_class_to_registered_components($2)
16047 AS _registered (field_class TEXT, field INT) ON (
16048 _registered.field_class = cmc.name
16053 RETURN QUERY EXECUTE '
16062 TS_HEADLINE(value, $7, $3)
16063 FROM (SELECT DISTINCT
16066 cmc.buoyant AND _registered.field_class IS NOT NULL AS push,
16067 _registered.field = cmf.id AS restrict,
16069 TS_RANK_CD(mbe.index_vector, $1, $6),
16072 FROM metabib.browse_entry_def_map mbedm
16074 -- Start with a pre-limited set of 10k possible suggestions. More than that is not going to be useful anyway
16075 JOIN (SELECT * FROM metabib.browse_entry WHERE index_vector @@ $1 LIMIT 10000) mbe ON (mbe.id = mbedm.entry)
16077 JOIN config.metabib_field cmf ON (cmf.id = mbedm.def)
16078 JOIN config.metabib_class cmc ON (cmf.field_class = cmc.name)
16079 ' || search_class_join || '
16080 ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
16081 LIMIT 1000) AS x -- This outer limit makes testing for opac visibility usably fast
16082 ' || opac_visibility_join || '
16083 ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
16085 ' -- sic, repeat the order by clause in the outer select too
16087 query, search_class, headline_opts,
16088 visibility_org, query_limit, normalization, plain_query
16092 -- buoyant AND chosen class = match class
16093 -- chosen field = match field
16100 $func$ LANGUAGE PLPGSQL;
16104 -- This is split out because it was backported to 2.1, but may not exist before upgrades
16105 -- It can safely fail
16106 -- Also, lets say that. <_<
16108 \qecho *************************************************************************
16109 \qecho !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
16110 \qecho We are about to apply a patch that may not be needed. It can fail safely.
16111 \qecho !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
16112 \qecho *************************************************************************
16115 -- Evergreen DB patch 0693.schema.do_not_despace_issns.sql
16117 -- FIXME: insert description of change, if needed
16122 -- check whether patch can be applied
16123 SELECT evergreen.upgrade_deps_block_check('0693', :eg_version);
16125 -- FIXME: add/check SQL statements to perform the upgrade
16126 -- Delete the index normalizer that was meant to remove spaces from ISSNs
16127 -- but ended up breaking records with multiple ISSNs
16128 DELETE FROM config.metabib_field_index_norm_map WHERE id IN (
16129 SELECT map.id FROM config.metabib_field_index_norm_map map
16130 INNER JOIN config.metabib_field cmf ON cmf.id = map.field
16131 INNER JOIN config.index_normalizer cin ON cin.id = map.norm
16132 WHERE cin.func = 'replace'
16133 AND cmf.field_class = 'identifier'
16134 AND cmf.name = 'issn'
16135 AND map.params = $$[" ",""]$$
16138 -- Reindex records that have more than just a single ISSN
16139 -- to ensure that spaces are maintained
16140 SELECT metabib.reingest_metabib_field_entries(source)
16141 FROM metabib.identifier_field_entry mife
16142 INNER JOIN config.metabib_field cmf ON cmf.id = mife.field
16143 WHERE cmf.field_class = 'identifier'
16144 AND cmf.name = 'issn'
16145 AND char_length(value) > 9
16151 -- outside of any transaction
16153 \qecho ************************************************************************
16154 \qecho Failures from here down are okay!
16155 \qecho ************************************************************************
16157 SELECT evergreen.upgrade_deps_block_check('0691', :eg_version);
16159 CREATE INDEX poi_po_idx ON acq.po_item (purchase_order);
16161 CREATE INDEX ie_inv_idx on acq.invoice_entry (invoice);
16162 CREATE INDEX ie_po_idx on acq.invoice_entry (purchase_order);
16163 CREATE INDEX ie_li_idx on acq.invoice_entry (lineitem);
16165 CREATE INDEX ii_inv_idx on acq.invoice_item (invoice);
16166 CREATE INDEX ii_po_idx on acq.invoice_item (purchase_order);
16167 CREATE INDEX ii_poi_idx on acq.invoice_item (po_item);
16169 \qecho All Evergreen core database functions have been converted to
16170 \qecho use PLPERLU instead of PLPERL, so we are attempting to remove
16171 \qecho the PLPERL language here; but it is entirely possible that
16172 \qecho existing sites will have custom PLPERL functions that they
16173 \qecho will want to retain, so the following DROP LANGUAGE statement
16174 \qecho may fail, and that is okay.
16176 DROP LANGUAGE plperl;