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 applied_to;
10 DROP FUNCTION evergreen.upgrade_list_applied_deprecates(TEXT);
11 DROP FUNCTION 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 0672.fix-nonfiling-titles.sql
8389 -- Titles that begin with non-filing articles using apostrophes
8390 -- (for example, "L'armée") get spaces injected between the article
8391 -- and the subsequent text, which then breaks searching for titles
8392 -- beginning with those articles.
8394 -- This patch adds a nonfiling title element to MODS32 that can then
8395 -- be used to retrieve the title proper without affecting the spaces
8396 -- in the title. It's what we want, what we really really want, for
8401 -- check whether patch can be applied
8402 SELECT evergreen.upgrade_deps_block_check('0672', :eg_version);
8404 -- Update the XPath definition before the titleNonfiling element exists;
8405 -- but are you really going to read through the whole XSL below before
8406 -- seeing this important bit?
8407 UPDATE config.metabib_field
8408 SET xpath = $$//mods32:mods/mods32:titleNonfiling[mods32:title and not (@type)]$$,
8410 WHERE field_class = 'title' AND name = 'proper';
8412 UPDATE config.xml_transform SET xslt=$$<?xml version="1.0" encoding="UTF-8"?>
8413 <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">
8414 <xsl:output encoding="UTF-8" indent="yes" method="xml"/>
8416 Revision 1.14 - Fixed template isValid and fields 010, 020, 022, 024, 028, and 037 to output additional identifier elements
8417 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
8419 Revision 1.13 - Changed order of output under cartographics to reflect schema 2006/11/28 tmee
8421 Revision 1.12 - Updated to reflect MODS 3.2 Mapping 2006/10/11 tmee
8423 Revision 1.11 - The attribute objectPart moved from <languageTerm> to <language>
8426 Revision 1.10 MODS 3.1 revisions to language and classification elements
8427 (plus ability to find marc:collection embedded in wrapper elements such as SRU zs: wrappers)
8430 Revision 1.9 subfield $y was added to field 242 2004/09/02 10:57 jrad
8432 Revision 1.8 Subject chopPunctuation expanded and attribute fixes 2004/08/12 jrad
8434 Revision 1.7 2004/03/25 08:29 jrad
8436 Revision 1.6 various validation fixes 2004/02/20 ntra
8438 Revision 1.5 2003/10/02 16:18:58 ntra
8439 MODS2 to MODS3 updates, language unstacking and
8440 de-duping, chopPunctuation expanded
8442 Revision 1.3 2003/04/03 00:07:19 ntra
8443 Revision 1.3 Additional Changes not related to MODS Version 2.0 by ntra
8445 Revision 1.2 2003/03/24 19:37:42 ckeith
8449 <xsl:template match="/">
8451 <xsl:when test="//marc:collection">
8452 <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">
8453 <xsl:for-each select="//marc:collection/marc:record">
8454 <mods version="3.2">
8455 <xsl:call-template name="marcRecord"/>
8461 <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">
8462 <xsl:for-each select="//marc:record">
8463 <xsl:call-template name="marcRecord"/>
8469 <xsl:template name="marcRecord">
8470 <xsl:variable name="leader" select="marc:leader"/>
8471 <xsl:variable name="leader6" select="substring($leader,7,1)"/>
8472 <xsl:variable name="leader7" select="substring($leader,8,1)"/>
8473 <xsl:variable name="controlField008" select="marc:controlfield[@tag='008']"/>
8474 <xsl:variable name="typeOf008">
8476 <xsl:when test="$leader6='a'">
8478 <xsl:when test="$leader7='a' or $leader7='c' or $leader7='d' or $leader7='m'">BK</xsl:when>
8479 <xsl:when test="$leader7='b' or $leader7='i' or $leader7='s'">SE</xsl:when>
8482 <xsl:when test="$leader6='t'">BK</xsl:when>
8483 <xsl:when test="$leader6='p'">MM</xsl:when>
8484 <xsl:when test="$leader6='m'">CF</xsl:when>
8485 <xsl:when test="$leader6='e' or $leader6='f'">MP</xsl:when>
8486 <xsl:when test="$leader6='g' or $leader6='k' or $leader6='o' or $leader6='r'">VM</xsl:when>
8487 <xsl:when test="$leader6='c' or $leader6='d' or $leader6='i' or $leader6='j'">MU</xsl:when>
8490 <xsl:for-each select="marc:datafield[@tag='245']">
8492 <xsl:variable name="title">
8494 <xsl:when test="marc:subfield[@code='b']">
8495 <xsl:call-template name="specialSubfieldSelect">
8496 <xsl:with-param name="axis">b</xsl:with-param>
8497 <xsl:with-param name="beforeCodes">afgk</xsl:with-param>
8498 </xsl:call-template>
8501 <xsl:call-template name="subfieldSelect">
8502 <xsl:with-param name="codes">abfgk</xsl:with-param>
8503 </xsl:call-template>
8507 <xsl:variable name="titleChop">
8508 <xsl:call-template name="chopPunctuation">
8509 <xsl:with-param name="chopString">
8510 <xsl:value-of select="$title"/>
8512 </xsl:call-template>
8515 <xsl:when test="@ind2>0">
8517 <xsl:value-of select="substring($titleChop,1,@ind2)"/>
8520 <xsl:value-of select="substring($titleChop,@ind2+1)"/>
8525 <xsl:value-of select="$titleChop"/>
8529 <xsl:if test="marc:subfield[@code='b']">
8531 <xsl:call-template name="chopPunctuation">
8532 <xsl:with-param name="chopString">
8533 <xsl:call-template name="specialSubfieldSelect">
8534 <xsl:with-param name="axis">b</xsl:with-param>
8535 <xsl:with-param name="anyCodes">b</xsl:with-param>
8536 <xsl:with-param name="afterCodes">afgk</xsl:with-param>
8537 </xsl:call-template>
8539 </xsl:call-template>
8542 <xsl:call-template name="part"></xsl:call-template>
8544 <!-- A form of title that ignores non-filing characters; useful
8545 for not converting "L'Oreal" into "L' Oreal" at index time -->
8547 <xsl:variable name="title">
8549 <xsl:when test="marc:subfield[@code='b']">
8550 <xsl:call-template name="specialSubfieldSelect">
8551 <xsl:with-param name="axis">b</xsl:with-param>
8552 <xsl:with-param name="beforeCodes">afgk</xsl:with-param>
8553 </xsl:call-template>
8556 <xsl:call-template name="subfieldSelect">
8557 <xsl:with-param name="codes">abfgk</xsl:with-param>
8558 </xsl:call-template>
8563 <xsl:value-of select="$title"/>
8565 <xsl:if test="marc:subfield[@code='b']">
8567 <xsl:call-template name="chopPunctuation">
8568 <xsl:with-param name="chopString">
8569 <xsl:call-template name="specialSubfieldSelect">
8570 <xsl:with-param name="axis">b</xsl:with-param>
8571 <xsl:with-param name="anyCodes">b</xsl:with-param>
8572 <xsl:with-param name="afterCodes">afgk</xsl:with-param>
8573 </xsl:call-template>
8575 </xsl:call-template>
8578 <xsl:call-template name="part"></xsl:call-template>
8581 <xsl:for-each select="marc:datafield[@tag='210']">
8582 <titleInfo type="abbreviated">
8584 <xsl:call-template name="chopPunctuation">
8585 <xsl:with-param name="chopString">
8586 <xsl:call-template name="subfieldSelect">
8587 <xsl:with-param name="codes">a</xsl:with-param>
8588 </xsl:call-template>
8590 </xsl:call-template>
8592 <xsl:call-template name="subtitle"/>
8595 <xsl:for-each select="marc:datafield[@tag='242']">
8596 <titleInfo type="translated">
8597 <!--09/01/04 Added subfield $y-->
8598 <xsl:for-each select="marc:subfield[@code='y']">
8599 <xsl:attribute name="lang">
8600 <xsl:value-of select="text()"/>
8604 <xsl:call-template name="chopPunctuation">
8605 <xsl:with-param name="chopString">
8606 <xsl:call-template name="subfieldSelect">
8607 <!-- 1/04 removed $h, b -->
8608 <xsl:with-param name="codes">a</xsl:with-param>
8609 </xsl:call-template>
8611 </xsl:call-template>
8614 <xsl:call-template name="subtitle"/>
8615 <xsl:call-template name="part"/>
8618 <xsl:for-each select="marc:datafield[@tag='246']">
8619 <titleInfo type="alternative">
8620 <xsl:for-each select="marc:subfield[@code='i']">
8621 <xsl:attribute name="displayLabel">
8622 <xsl:value-of select="text()"/>
8626 <xsl:call-template name="chopPunctuation">
8627 <xsl:with-param name="chopString">
8628 <xsl:call-template name="subfieldSelect">
8629 <!-- 1/04 removed $h, $b -->
8630 <xsl:with-param name="codes">af</xsl:with-param>
8631 </xsl:call-template>
8633 </xsl:call-template>
8635 <xsl:call-template name="subtitle"/>
8636 <xsl:call-template name="part"/>
8639 <xsl:for-each select="marc:datafield[@tag='130']|marc:datafield[@tag='240']|marc:datafield[@tag='730'][@ind2!='2']">
8640 <titleInfo type="uniform">
8642 <xsl:variable name="str">
8643 <xsl:for-each select="marc:subfield">
8644 <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'])))">
8645 <xsl:value-of select="text()"/>
8646 <xsl:text> </xsl:text>
8650 <xsl:call-template name="chopPunctuation">
8651 <xsl:with-param name="chopString">
8652 <xsl:value-of select="substring($str,1,string-length($str)-1)"/>
8654 </xsl:call-template>
8656 <xsl:call-template name="part"/>
8659 <xsl:for-each select="marc:datafield[@tag='740'][@ind2!='2']">
8660 <titleInfo type="alternative">
8662 <xsl:call-template name="chopPunctuation">
8663 <xsl:with-param name="chopString">
8664 <xsl:call-template name="subfieldSelect">
8665 <xsl:with-param name="codes">ah</xsl:with-param>
8666 </xsl:call-template>
8668 </xsl:call-template>
8670 <xsl:call-template name="part"/>
8673 <xsl:for-each select="marc:datafield[@tag='100']">
8674 <name type="personal">
8675 <xsl:call-template name="nameABCDQ"/>
8676 <xsl:call-template name="affiliation"/>
8678 <roleTerm authority="marcrelator" type="text">creator</roleTerm>
8680 <xsl:call-template name="role"/>
8683 <xsl:for-each select="marc:datafield[@tag='110']">
8684 <name type="corporate">
8685 <xsl:call-template name="nameABCDN"/>
8687 <roleTerm authority="marcrelator" type="text">creator</roleTerm>
8689 <xsl:call-template name="role"/>
8692 <xsl:for-each select="marc:datafield[@tag='111']">
8693 <name type="conference">
8694 <xsl:call-template name="nameACDEQ"/>
8696 <roleTerm authority="marcrelator" type="text">creator</roleTerm>
8698 <xsl:call-template name="role"/>
8701 <xsl:for-each select="marc:datafield[@tag='700'][not(marc:subfield[@code='t'])]">
8702 <name type="personal">
8703 <xsl:call-template name="nameABCDQ"/>
8704 <xsl:call-template name="affiliation"/>
8705 <xsl:call-template name="role"/>
8708 <xsl:for-each select="marc:datafield[@tag='710'][not(marc:subfield[@code='t'])]">
8709 <name type="corporate">
8710 <xsl:call-template name="nameABCDN"/>
8711 <xsl:call-template name="role"/>
8714 <xsl:for-each select="marc:datafield[@tag='711'][not(marc:subfield[@code='t'])]">
8715 <name type="conference">
8716 <xsl:call-template name="nameACDEQ"/>
8717 <xsl:call-template name="role"/>
8720 <xsl:for-each select="marc:datafield[@tag='720'][not(marc:subfield[@code='t'])]">
8722 <xsl:if test="@ind1=1">
8723 <xsl:attribute name="type">
8724 <xsl:text>personal</xsl:text>
8728 <xsl:value-of select="marc:subfield[@code='a']"/>
8730 <xsl:call-template name="role"/>
8734 <xsl:if test="$leader7='c'">
8735 <xsl:attribute name="collection">yes</xsl:attribute>
8737 <xsl:if test="$leader6='d' or $leader6='f' or $leader6='p' or $leader6='t'">
8738 <xsl:attribute name="manuscript">yes</xsl:attribute>
8741 <xsl:when test="$leader6='a' or $leader6='t'">text</xsl:when>
8742 <xsl:when test="$leader6='e' or $leader6='f'">cartographic</xsl:when>
8743 <xsl:when test="$leader6='c' or $leader6='d'">notated music</xsl:when>
8744 <xsl:when test="$leader6='i'">sound recording-nonmusical</xsl:when>
8745 <xsl:when test="$leader6='j'">sound recording-musical</xsl:when>
8746 <xsl:when test="$leader6='k'">still image</xsl:when>
8747 <xsl:when test="$leader6='g'">moving image</xsl:when>
8748 <xsl:when test="$leader6='r'">three dimensional object</xsl:when>
8749 <xsl:when test="$leader6='m'">software, multimedia</xsl:when>
8750 <xsl:when test="$leader6='p'">mixed material</xsl:when>
8753 <xsl:if test="substring($controlField008,26,1)='d'">
8754 <genre authority="marc">globe</genre>
8756 <xsl:if test="marc:controlfield[@tag='007'][substring(text(),1,1)='a'][substring(text(),2,1)='r']">
8757 <genre authority="marc">remote sensing image</genre>
8759 <xsl:if test="$typeOf008='MP'">
8760 <xsl:variable name="controlField008-25" select="substring($controlField008,26,1)"></xsl:variable>
8762 <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']">
8763 <genre authority="marc">map</genre>
8765 <xsl:when test="$controlField008-25='e' or marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='d']">
8766 <genre authority="marc">atlas</genre>
8770 <xsl:if test="$typeOf008='SE'">
8771 <xsl:variable name="controlField008-21" select="substring($controlField008,22,1)"></xsl:variable>
8773 <xsl:when test="$controlField008-21='d'">
8774 <genre authority="marc">database</genre>
8776 <xsl:when test="$controlField008-21='l'">
8777 <genre authority="marc">loose-leaf</genre>
8779 <xsl:when test="$controlField008-21='m'">
8780 <genre authority="marc">series</genre>
8782 <xsl:when test="$controlField008-21='n'">
8783 <genre authority="marc">newspaper</genre>
8785 <xsl:when test="$controlField008-21='p'">
8786 <genre authority="marc">periodical</genre>
8788 <xsl:when test="$controlField008-21='w'">
8789 <genre authority="marc">web site</genre>
8793 <xsl:if test="$typeOf008='BK' or $typeOf008='SE'">
8794 <xsl:variable name="controlField008-24" select="substring($controlField008,25,4)"></xsl:variable>
8796 <xsl:when test="contains($controlField008-24,'a')">
8797 <genre authority="marc">abstract or summary</genre>
8799 <xsl:when test="contains($controlField008-24,'b')">
8800 <genre authority="marc">bibliography</genre>
8802 <xsl:when test="contains($controlField008-24,'c')">
8803 <genre authority="marc">catalog</genre>
8805 <xsl:when test="contains($controlField008-24,'d')">
8806 <genre authority="marc">dictionary</genre>
8808 <xsl:when test="contains($controlField008-24,'e')">
8809 <genre authority="marc">encyclopedia</genre>
8811 <xsl:when test="contains($controlField008-24,'f')">
8812 <genre authority="marc">handbook</genre>
8814 <xsl:when test="contains($controlField008-24,'g')">
8815 <genre authority="marc">legal article</genre>
8817 <xsl:when test="contains($controlField008-24,'i')">
8818 <genre authority="marc">index</genre>
8820 <xsl:when test="contains($controlField008-24,'k')">
8821 <genre authority="marc">discography</genre>
8823 <xsl:when test="contains($controlField008-24,'l')">
8824 <genre authority="marc">legislation</genre>
8826 <xsl:when test="contains($controlField008-24,'m')">
8827 <genre authority="marc">theses</genre>
8829 <xsl:when test="contains($controlField008-24,'n')">
8830 <genre authority="marc">survey of literature</genre>
8832 <xsl:when test="contains($controlField008-24,'o')">
8833 <genre authority="marc">review</genre>
8835 <xsl:when test="contains($controlField008-24,'p')">
8836 <genre authority="marc">programmed text</genre>
8838 <xsl:when test="contains($controlField008-24,'q')">
8839 <genre authority="marc">filmography</genre>
8841 <xsl:when test="contains($controlField008-24,'r')">
8842 <genre authority="marc">directory</genre>
8844 <xsl:when test="contains($controlField008-24,'s')">
8845 <genre authority="marc">statistics</genre>
8847 <xsl:when test="contains($controlField008-24,'t')">
8848 <genre authority="marc">technical report</genre>
8850 <xsl:when test="contains($controlField008-24,'v')">
8851 <genre authority="marc">legal case and case notes</genre>
8853 <xsl:when test="contains($controlField008-24,'w')">
8854 <genre authority="marc">law report or digest</genre>
8856 <xsl:when test="contains($controlField008-24,'z')">
8857 <genre authority="marc">treaty</genre>
8860 <xsl:variable name="controlField008-29" select="substring($controlField008,30,1)"></xsl:variable>
8862 <xsl:when test="$controlField008-29='1'">
8863 <genre authority="marc">conference publication</genre>
8867 <xsl:if test="$typeOf008='CF'">
8868 <xsl:variable name="controlField008-26" select="substring($controlField008,27,1)"></xsl:variable>
8870 <xsl:when test="$controlField008-26='a'">
8871 <genre authority="marc">numeric data</genre>
8873 <xsl:when test="$controlField008-26='e'">
8874 <genre authority="marc">database</genre>
8876 <xsl:when test="$controlField008-26='f'">
8877 <genre authority="marc">font</genre>
8879 <xsl:when test="$controlField008-26='g'">
8880 <genre authority="marc">game</genre>
8884 <xsl:if test="$typeOf008='BK'">
8885 <xsl:if test="substring($controlField008,25,1)='j'">
8886 <genre authority="marc">patent</genre>
8888 <xsl:if test="substring($controlField008,31,1)='1'">
8889 <genre authority="marc">festschrift</genre>
8891 <xsl:variable name="controlField008-34" select="substring($controlField008,35,1)"></xsl:variable>
8892 <xsl:if test="$controlField008-34='a' or $controlField008-34='b' or $controlField008-34='c' or $controlField008-34='d'">
8893 <genre authority="marc">biography</genre>
8895 <xsl:variable name="controlField008-33" select="substring($controlField008,34,1)"></xsl:variable>
8897 <xsl:when test="$controlField008-33='e'">
8898 <genre authority="marc">essay</genre>
8900 <xsl:when test="$controlField008-33='d'">
8901 <genre authority="marc">drama</genre>
8903 <xsl:when test="$controlField008-33='c'">
8904 <genre authority="marc">comic strip</genre>
8906 <xsl:when test="$controlField008-33='l'">
8907 <genre authority="marc">fiction</genre>
8909 <xsl:when test="$controlField008-33='h'">
8910 <genre authority="marc">humor, satire</genre>
8912 <xsl:when test="$controlField008-33='i'">
8913 <genre authority="marc">letter</genre>
8915 <xsl:when test="$controlField008-33='f'">
8916 <genre authority="marc">novel</genre>
8918 <xsl:when test="$controlField008-33='j'">
8919 <genre authority="marc">short story</genre>
8921 <xsl:when test="$controlField008-33='s'">
8922 <genre authority="marc">speech</genre>
8926 <xsl:if test="$typeOf008='MU'">
8927 <xsl:variable name="controlField008-30-31" select="substring($controlField008,31,2)"></xsl:variable>
8928 <xsl:if test="contains($controlField008-30-31,'b')">
8929 <genre authority="marc">biography</genre>
8931 <xsl:if test="contains($controlField008-30-31,'c')">
8932 <genre authority="marc">conference publication</genre>
8934 <xsl:if test="contains($controlField008-30-31,'d')">
8935 <genre authority="marc">drama</genre>
8937 <xsl:if test="contains($controlField008-30-31,'e')">
8938 <genre authority="marc">essay</genre>
8940 <xsl:if test="contains($controlField008-30-31,'f')">
8941 <genre authority="marc">fiction</genre>
8943 <xsl:if test="contains($controlField008-30-31,'o')">
8944 <genre authority="marc">folktale</genre>
8946 <xsl:if test="contains($controlField008-30-31,'h')">
8947 <genre authority="marc">history</genre>
8949 <xsl:if test="contains($controlField008-30-31,'k')">
8950 <genre authority="marc">humor, satire</genre>
8952 <xsl:if test="contains($controlField008-30-31,'m')">
8953 <genre authority="marc">memoir</genre>
8955 <xsl:if test="contains($controlField008-30-31,'p')">
8956 <genre authority="marc">poetry</genre>
8958 <xsl:if test="contains($controlField008-30-31,'r')">
8959 <genre authority="marc">rehearsal</genre>
8961 <xsl:if test="contains($controlField008-30-31,'g')">
8962 <genre authority="marc">reporting</genre>
8964 <xsl:if test="contains($controlField008-30-31,'s')">
8965 <genre authority="marc">sound</genre>
8967 <xsl:if test="contains($controlField008-30-31,'l')">
8968 <genre authority="marc">speech</genre>
8971 <xsl:if test="$typeOf008='VM'">
8972 <xsl:variable name="controlField008-33" select="substring($controlField008,34,1)"></xsl:variable>
8974 <xsl:when test="$controlField008-33='a'">
8975 <genre authority="marc">art original</genre>
8977 <xsl:when test="$controlField008-33='b'">
8978 <genre authority="marc">kit</genre>
8980 <xsl:when test="$controlField008-33='c'">
8981 <genre authority="marc">art reproduction</genre>
8983 <xsl:when test="$controlField008-33='d'">
8984 <genre authority="marc">diorama</genre>
8986 <xsl:when test="$controlField008-33='f'">
8987 <genre authority="marc">filmstrip</genre>
8989 <xsl:when test="$controlField008-33='g'">
8990 <genre authority="marc">legal article</genre>
8992 <xsl:when test="$controlField008-33='i'">
8993 <genre authority="marc">picture</genre>
8995 <xsl:when test="$controlField008-33='k'">
8996 <genre authority="marc">graphic</genre>
8998 <xsl:when test="$controlField008-33='l'">
8999 <genre authority="marc">technical drawing</genre>
9001 <xsl:when test="$controlField008-33='m'">
9002 <genre authority="marc">motion picture</genre>
9004 <xsl:when test="$controlField008-33='n'">
9005 <genre authority="marc">chart</genre>
9007 <xsl:when test="$controlField008-33='o'">
9008 <genre authority="marc">flash card</genre>
9010 <xsl:when test="$controlField008-33='p'">
9011 <genre authority="marc">microscope slide</genre>
9013 <xsl:when test="$controlField008-33='q' or marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='q']">
9014 <genre authority="marc">model</genre>
9016 <xsl:when test="$controlField008-33='r'">
9017 <genre authority="marc">realia</genre>
9019 <xsl:when test="$controlField008-33='s'">
9020 <genre authority="marc">slide</genre>
9022 <xsl:when test="$controlField008-33='t'">
9023 <genre authority="marc">transparency</genre>
9025 <xsl:when test="$controlField008-33='v'">
9026 <genre authority="marc">videorecording</genre>
9028 <xsl:when test="$controlField008-33='w'">
9029 <genre authority="marc">toy</genre>
9033 <xsl:for-each select="marc:datafield[@tag=655]">
9034 <genre authority="marc">
9035 <xsl:attribute name="authority">
9036 <xsl:value-of select="marc:subfield[@code='2']"/>
9038 <xsl:call-template name="subfieldSelect">
9039 <xsl:with-param name="codes">abvxyz</xsl:with-param>
9040 <xsl:with-param name="delimeter">-</xsl:with-param>
9041 </xsl:call-template>
9045 <xsl:variable name="MARCpublicationCode" select="normalize-space(substring($controlField008,16,3))"></xsl:variable>
9046 <xsl:if test="translate($MARCpublicationCode,'|','')">
9049 <xsl:attribute name="type">code</xsl:attribute>
9050 <xsl:attribute name="authority">marccountry</xsl:attribute>
9051 <xsl:value-of select="$MARCpublicationCode"/>
9055 <xsl:for-each select="marc:datafield[@tag=044]/marc:subfield[@code='c']">
9058 <xsl:attribute name="type">code</xsl:attribute>
9059 <xsl:attribute name="authority">iso3166</xsl:attribute>
9060 <xsl:value-of select="."/>
9064 <xsl:for-each select="marc:datafield[@tag=260]/marc:subfield[@code='a']">
9067 <xsl:attribute name="type">text</xsl:attribute>
9068 <xsl:call-template name="chopPunctuationFront">
9069 <xsl:with-param name="chopString">
9070 <xsl:call-template name="chopPunctuation">
9071 <xsl:with-param name="chopString" select="."/>
9072 </xsl:call-template>
9074 </xsl:call-template>
9078 <xsl:for-each select="marc:datafield[@tag=046]/marc:subfield[@code='m']">
9079 <dateValid point="start">
9080 <xsl:value-of select="."/>
9083 <xsl:for-each select="marc:datafield[@tag=046]/marc:subfield[@code='n']">
9084 <dateValid point="end">
9085 <xsl:value-of select="."/>
9088 <xsl:for-each select="marc:datafield[@tag=046]/marc:subfield[@code='j']">
9090 <xsl:value-of select="."/>
9093 <xsl:for-each select="marc:datafield[@tag=260]/marc:subfield[@code='b' or @code='c' or @code='g']">
9095 <xsl:when test="@code='b'">
9097 <xsl:call-template name="chopPunctuation">
9098 <xsl:with-param name="chopString" select="."/>
9099 <xsl:with-param name="punctuation">
9100 <xsl:text>:,;/ </xsl:text>
9102 </xsl:call-template>
9105 <xsl:when test="@code='c'">
9107 <xsl:call-template name="chopPunctuation">
9108 <xsl:with-param name="chopString" select="."/>
9109 </xsl:call-template>
9112 <xsl:when test="@code='g'">
9114 <xsl:value-of select="."/>
9119 <xsl:variable name="dataField260c">
9120 <xsl:call-template name="chopPunctuation">
9121 <xsl:with-param name="chopString" select="marc:datafield[@tag=260]/marc:subfield[@code='c']"></xsl:with-param>
9122 </xsl:call-template>
9124 <xsl:variable name="controlField008-7-10" select="normalize-space(substring($controlField008, 8, 4))"></xsl:variable>
9125 <xsl:variable name="controlField008-11-14" select="normalize-space(substring($controlField008, 12, 4))"></xsl:variable>
9126 <xsl:variable name="controlField008-6" select="normalize-space(substring($controlField008, 7, 1))"></xsl:variable>
9127 <xsl:if test="$controlField008-6='e' or $controlField008-6='p' or $controlField008-6='r' or $controlField008-6='t' or $controlField008-6='s'">
9128 <xsl:if test="$controlField008-7-10 and ($controlField008-7-10 != $dataField260c)">
9129 <dateIssued encoding="marc">
9130 <xsl:value-of select="$controlField008-7-10"/>
9134 <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'">
9135 <xsl:if test="$controlField008-7-10">
9136 <dateIssued encoding="marc" point="start">
9137 <xsl:value-of select="$controlField008-7-10"/>
9141 <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'">
9142 <xsl:if test="$controlField008-11-14">
9143 <dateIssued encoding="marc" point="end">
9144 <xsl:value-of select="$controlField008-11-14"/>
9148 <xsl:if test="$controlField008-6='q'">
9149 <xsl:if test="$controlField008-7-10">
9150 <dateIssued encoding="marc" point="start" qualifier="questionable">
9151 <xsl:value-of select="$controlField008-7-10"/>
9155 <xsl:if test="$controlField008-6='q'">
9156 <xsl:if test="$controlField008-11-14">
9157 <dateIssued encoding="marc" point="end" qualifier="questionable">
9158 <xsl:value-of select="$controlField008-11-14"/>
9162 <xsl:if test="$controlField008-6='t'">
9163 <xsl:if test="$controlField008-11-14">
9164 <copyrightDate encoding="marc">
9165 <xsl:value-of select="$controlField008-11-14"/>
9169 <xsl:for-each select="marc:datafield[@tag=033][@ind1=0 or @ind1=1]/marc:subfield[@code='a']">
9170 <dateCaptured encoding="iso8601">
9171 <xsl:value-of select="."/>
9174 <xsl:for-each select="marc:datafield[@tag=033][@ind1=2]/marc:subfield[@code='a'][1]">
9175 <dateCaptured encoding="iso8601" point="start">
9176 <xsl:value-of select="."/>
9179 <xsl:for-each select="marc:datafield[@tag=033][@ind1=2]/marc:subfield[@code='a'][2]">
9180 <dateCaptured encoding="iso8601" point="end">
9181 <xsl:value-of select="."/>
9184 <xsl:for-each select="marc:datafield[@tag=250]/marc:subfield[@code='a']">
9186 <xsl:value-of select="."/>
9189 <xsl:for-each select="marc:leader">
9192 <xsl:when test="$leader7='a' or $leader7='c' or $leader7='d' or $leader7='m'">monographic</xsl:when>
9193 <xsl:when test="$leader7='b' or $leader7='i' or $leader7='s'">continuing</xsl:when>
9197 <xsl:for-each select="marc:datafield[@tag=310]|marc:datafield[@tag=321]">
9199 <xsl:call-template name="subfieldSelect">
9200 <xsl:with-param name="codes">ab</xsl:with-param>
9201 </xsl:call-template>
9205 <xsl:variable name="controlField008-35-37" select="normalize-space(translate(substring($controlField008,36,3),'|#',''))"></xsl:variable>
9206 <xsl:if test="$controlField008-35-37">
9208 <languageTerm authority="iso639-2b" type="code">
9209 <xsl:value-of select="substring($controlField008,36,3)"/>
9213 <xsl:for-each select="marc:datafield[@tag=041]">
9214 <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']">
9215 <xsl:variable name="langCodes" select="."/>
9217 <xsl:when test="../marc:subfield[@code='2']='rfc3066'">
9218 <!-- not stacked but could be repeated -->
9219 <xsl:call-template name="rfcLanguages">
9220 <xsl:with-param name="nodeNum">
9221 <xsl:value-of select="1"/>
9223 <xsl:with-param name="usedLanguages">
9224 <xsl:text></xsl:text>
9226 <xsl:with-param name="controlField008-35-37">
9227 <xsl:value-of select="$controlField008-35-37"></xsl:value-of>
9229 </xsl:call-template>
9233 <xsl:variable name="allLanguages">
9234 <xsl:copy-of select="$langCodes"></xsl:copy-of>
9236 <xsl:variable name="currentLanguage">
9237 <xsl:value-of select="substring($allLanguages,1,3)"></xsl:value-of>
9239 <xsl:call-template name="isoLanguage">
9240 <xsl:with-param name="currentLanguage">
9241 <xsl:value-of select="substring($allLanguages,1,3)"></xsl:value-of>
9243 <xsl:with-param name="remainingLanguages">
9244 <xsl:value-of select="substring($allLanguages,4,string-length($allLanguages)-3)"></xsl:value-of>
9246 <xsl:with-param name="usedLanguages">
9247 <xsl:if test="$controlField008-35-37">
9248 <xsl:value-of select="$controlField008-35-37"></xsl:value-of>
9251 </xsl:call-template>
9256 <xsl:variable name="physicalDescription">
9257 <!--3.2 change tmee 007/11 -->
9258 <xsl:if test="$typeOf008='CF' and marc:controlfield[@tag=007][substring(.,12,1)='a']">
9259 <digitalOrigin>reformatted digital</digitalOrigin>
9261 <xsl:if test="$typeOf008='CF' and marc:controlfield[@tag=007][substring(.,12,1)='b']">
9262 <digitalOrigin>digitized microfilm</digitalOrigin>
9264 <xsl:if test="$typeOf008='CF' and marc:controlfield[@tag=007][substring(.,12,1)='d']">
9265 <digitalOrigin>digitized other analog</digitalOrigin>
9267 <xsl:variable name="controlField008-23" select="substring($controlField008,24,1)"></xsl:variable>
9268 <xsl:variable name="controlField008-29" select="substring($controlField008,30,1)"></xsl:variable>
9269 <xsl:variable name="check008-23">
9270 <xsl:if test="$typeOf008='BK' or $typeOf008='MU' or $typeOf008='SE' or $typeOf008='MM'">
9271 <xsl:value-of select="true()"></xsl:value-of>
9274 <xsl:variable name="check008-29">
9275 <xsl:if test="$typeOf008='MP' or $typeOf008='VM'">
9276 <xsl:value-of select="true()"></xsl:value-of>
9280 <xsl:when test="($check008-23 and $controlField008-23='f') or ($check008-29 and $controlField008-29='f')">
9281 <form authority="marcform">braille</form>
9283 <xsl:when test="($controlField008-23=' ' and ($leader6='c' or $leader6='d')) or (($typeOf008='BK' or $typeOf008='SE') and ($controlField008-23=' ' or $controlField008='r'))">
9284 <form authority="marcform">print</form>
9286 <xsl:when test="$leader6 = 'm' or ($check008-23 and $controlField008-23='s') or ($check008-29 and $controlField008-29='s')">
9287 <form authority="marcform">electronic</form>
9289 <xsl:when test="($check008-23 and $controlField008-23='b') or ($check008-29 and $controlField008-29='b')">
9290 <form authority="marcform">microfiche</form>
9292 <xsl:when test="($check008-23 and $controlField008-23='a') or ($check008-29 and $controlField008-29='a')">
9293 <form authority="marcform">microfilm</form>
9297 <xsl:if test="marc:datafield[@tag=130]/marc:subfield[@code='h']">
9298 <form authority="gmd">
9299 <xsl:call-template name="chopBrackets">
9300 <xsl:with-param name="chopString">
9301 <xsl:value-of select="marc:datafield[@tag=130]/marc:subfield[@code='h']"></xsl:value-of>
9303 </xsl:call-template>
9306 <xsl:if test="marc:datafield[@tag=240]/marc:subfield[@code='h']">
9307 <form authority="gmd">
9308 <xsl:call-template name="chopBrackets">
9309 <xsl:with-param name="chopString">
9310 <xsl:value-of select="marc:datafield[@tag=240]/marc:subfield[@code='h']"></xsl:value-of>
9312 </xsl:call-template>
9315 <xsl:if test="marc:datafield[@tag=242]/marc:subfield[@code='h']">
9316 <form authority="gmd">
9317 <xsl:call-template name="chopBrackets">
9318 <xsl:with-param name="chopString">
9319 <xsl:value-of select="marc:datafield[@tag=242]/marc:subfield[@code='h']"></xsl:value-of>
9321 </xsl:call-template>
9324 <xsl:if test="marc:datafield[@tag=245]/marc:subfield[@code='h']">
9325 <form authority="gmd">
9326 <xsl:call-template name="chopBrackets">
9327 <xsl:with-param name="chopString">
9328 <xsl:value-of select="marc:datafield[@tag=245]/marc:subfield[@code='h']"></xsl:value-of>
9330 </xsl:call-template>
9333 <xsl:if test="marc:datafield[@tag=246]/marc:subfield[@code='h']">
9334 <form authority="gmd">
9335 <xsl:call-template name="chopBrackets">
9336 <xsl:with-param name="chopString">
9337 <xsl:value-of select="marc:datafield[@tag=246]/marc:subfield[@code='h']"></xsl:value-of>
9339 </xsl:call-template>
9342 <xsl:if test="marc:datafield[@tag=730]/marc:subfield[@code='h']">
9343 <form authority="gmd">
9344 <xsl:call-template name="chopBrackets">
9345 <xsl:with-param name="chopString">
9346 <xsl:value-of select="marc:datafield[@tag=730]/marc:subfield[@code='h']"></xsl:value-of>
9348 </xsl:call-template>
9351 <xsl:for-each select="marc:datafield[@tag=256]/marc:subfield[@code='a']">
9353 <xsl:value-of select="."></xsl:value-of>
9356 <xsl:for-each select="marc:controlfield[@tag=007][substring(text(),1,1)='c']">
9358 <xsl:when test="substring(text(),14,1)='a'">
9359 <reformattingQuality>access</reformattingQuality>
9361 <xsl:when test="substring(text(),14,1)='p'">
9362 <reformattingQuality>preservation</reformattingQuality>
9364 <xsl:when test="substring(text(),14,1)='r'">
9365 <reformattingQuality>replacement</reformattingQuality>
9369 <!--3.2 change tmee 007/01 -->
9370 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='b']">
9371 <form authority="smd">chip cartridge</form>
9373 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='c']">
9374 <form authority="smd">computer optical disc cartridge</form>
9376 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='j']">
9377 <form authority="smd">magnetic disc</form>
9379 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='m']">
9380 <form authority="smd">magneto-optical disc</form>
9382 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='o']">
9383 <form authority="smd">optical disc</form>
9385 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='r']">
9386 <form authority="smd">remote</form>
9388 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='a']">
9389 <form authority="smd">tape cartridge</form>
9391 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='f']">
9392 <form authority="smd">tape cassette</form>
9394 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='h']">
9395 <form authority="smd">tape reel</form>
9398 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='d'][substring(text(),2,1)='a']">
9399 <form authority="smd">celestial globe</form>
9401 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='d'][substring(text(),2,1)='e']">
9402 <form authority="smd">earth moon globe</form>
9404 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='d'][substring(text(),2,1)='b']">
9405 <form authority="smd">planetary or lunar globe</form>
9407 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='d'][substring(text(),2,1)='c']">
9408 <form authority="smd">terrestrial globe</form>
9411 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='o'][substring(text(),2,1)='o']">
9412 <form authority="smd">kit</form>
9415 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='d']">
9416 <form authority="smd">atlas</form>
9418 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='g']">
9419 <form authority="smd">diagram</form>
9421 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='j']">
9422 <form authority="smd">map</form>
9424 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='q']">
9425 <form authority="smd">model</form>
9427 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='k']">
9428 <form authority="smd">profile</form>
9430 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='r']">
9431 <form authority="smd">remote-sensing image</form>
9433 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='s']">
9434 <form authority="smd">section</form>
9436 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='y']">
9437 <form authority="smd">view</form>
9440 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='a']">
9441 <form authority="smd">aperture card</form>
9443 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='e']">
9444 <form authority="smd">microfiche</form>
9446 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='f']">
9447 <form authority="smd">microfiche cassette</form>
9449 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='b']">
9450 <form authority="smd">microfilm cartridge</form>
9452 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='c']">
9453 <form authority="smd">microfilm cassette</form>
9455 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='d']">
9456 <form authority="smd">microfilm reel</form>
9458 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='g']">
9459 <form authority="smd">microopaque</form>
9462 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='m'][substring(text(),2,1)='c']">
9463 <form authority="smd">film cartridge</form>
9465 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='m'][substring(text(),2,1)='f']">
9466 <form authority="smd">film cassette</form>
9468 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='m'][substring(text(),2,1)='r']">
9469 <form authority="smd">film reel</form>
9472 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='n']">
9473 <form authority="smd">chart</form>
9475 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='c']">
9476 <form authority="smd">collage</form>
9478 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='d']">
9479 <form authority="smd">drawing</form>
9481 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='o']">
9482 <form authority="smd">flash card</form>
9484 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='e']">
9485 <form authority="smd">painting</form>
9487 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='f']">
9488 <form authority="smd">photomechanical print</form>
9490 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='g']">
9491 <form authority="smd">photonegative</form>
9493 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='h']">
9494 <form authority="smd">photoprint</form>
9496 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='i']">
9497 <form authority="smd">picture</form>
9499 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='j']">
9500 <form authority="smd">print</form>
9502 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='l']">
9503 <form authority="smd">technical drawing</form>
9506 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='q'][substring(text(),2,1)='q']">
9507 <form authority="smd">notated music</form>
9510 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='d']">
9511 <form authority="smd">filmslip</form>
9513 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='c']">
9514 <form authority="smd">filmstrip cartridge</form>
9516 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='o']">
9517 <form authority="smd">filmstrip roll</form>
9519 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='f']">
9520 <form authority="smd">other filmstrip type</form>
9522 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='s']">
9523 <form authority="smd">slide</form>
9525 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='t']">
9526 <form authority="smd">transparency</form>
9528 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='r'][substring(text(),2,1)='r']">
9529 <form authority="smd">remote-sensing image</form>
9531 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='e']">
9532 <form authority="smd">cylinder</form>
9534 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='q']">
9535 <form authority="smd">roll</form>
9537 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='g']">
9538 <form authority="smd">sound cartridge</form>
9540 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='s']">
9541 <form authority="smd">sound cassette</form>
9543 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='d']">
9544 <form authority="smd">sound disc</form>
9546 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='t']">
9547 <form authority="smd">sound-tape reel</form>
9549 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='i']">
9550 <form authority="smd">sound-track film</form>
9552 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='w']">
9553 <form authority="smd">wire recording</form>
9556 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='f'][substring(text(),2,1)='c']">
9557 <form authority="smd">braille</form>
9559 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='f'][substring(text(),2,1)='b']">
9560 <form authority="smd">combination</form>
9562 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='f'][substring(text(),2,1)='a']">
9563 <form authority="smd">moon</form>
9565 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='f'][substring(text(),2,1)='d']">
9566 <form authority="smd">tactile, with no writing system</form>
9569 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='t'][substring(text(),2,1)='c']">
9570 <form authority="smd">braille</form>
9572 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='t'][substring(text(),2,1)='b']">
9573 <form authority="smd">large print</form>
9575 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='t'][substring(text(),2,1)='a']">
9576 <form authority="smd">regular print</form>
9578 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='t'][substring(text(),2,1)='d']">
9579 <form authority="smd">text in looseleaf binder</form>
9582 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='v'][substring(text(),2,1)='c']">
9583 <form authority="smd">videocartridge</form>
9585 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='v'][substring(text(),2,1)='f']">
9586 <form authority="smd">videocassette</form>
9588 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='v'][substring(text(),2,1)='d']">
9589 <form authority="smd">videodisc</form>
9591 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='v'][substring(text(),2,1)='r']">
9592 <form authority="smd">videoreel</form>
9595 <xsl:for-each select="marc:datafield[@tag=856]/marc:subfield[@code='q'][string-length(.)>1]">
9597 <xsl:value-of select="."></xsl:value-of>
9598 </internetMediaType>
9600 <xsl:for-each select="marc:datafield[@tag=300]">
9602 <xsl:call-template name="subfieldSelect">
9603 <xsl:with-param name="codes">abce</xsl:with-param>
9604 </xsl:call-template>
9608 <xsl:if test="string-length(normalize-space($physicalDescription))">
9609 <physicalDescription>
9610 <xsl:copy-of select="$physicalDescription"></xsl:copy-of>
9611 </physicalDescription>
9613 <xsl:for-each select="marc:datafield[@tag=520]">
9615 <xsl:call-template name="uri"></xsl:call-template>
9616 <xsl:call-template name="subfieldSelect">
9617 <xsl:with-param name="codes">ab</xsl:with-param>
9618 </xsl:call-template>
9621 <xsl:for-each select="marc:datafield[@tag=505]">
9623 <xsl:call-template name="uri"></xsl:call-template>
9624 <xsl:call-template name="subfieldSelect">
9625 <xsl:with-param name="codes">agrt</xsl:with-param>
9626 </xsl:call-template>
9629 <xsl:for-each select="marc:datafield[@tag=521]">
9631 <xsl:call-template name="subfieldSelect">
9632 <xsl:with-param name="codes">ab</xsl:with-param>
9633 </xsl:call-template>
9636 <xsl:if test="$typeOf008='BK' or $typeOf008='CF' or $typeOf008='MU' or $typeOf008='VM'">
9637 <xsl:variable name="controlField008-22" select="substring($controlField008,23,1)"></xsl:variable>
9640 <xsl:when test="$controlField008-22='d'">
9641 <targetAudience authority="marctarget">adolescent</targetAudience>
9643 <xsl:when test="$controlField008-22='e'">
9644 <targetAudience authority="marctarget">adult</targetAudience>
9646 <xsl:when test="$controlField008-22='g'">
9647 <targetAudience authority="marctarget">general</targetAudience>
9649 <xsl:when test="$controlField008-22='b' or $controlField008-22='c' or $controlField008-22='j'">
9650 <targetAudience authority="marctarget">juvenile</targetAudience>
9652 <xsl:when test="$controlField008-22='a'">
9653 <targetAudience authority="marctarget">preschool</targetAudience>
9655 <xsl:when test="$controlField008-22='f'">
9656 <targetAudience authority="marctarget">specialized</targetAudience>
9660 <xsl:for-each select="marc:datafield[@tag=245]/marc:subfield[@code='c']">
9661 <note type="statement of responsibility">
9662 <xsl:value-of select="."></xsl:value-of>
9665 <xsl:for-each select="marc:datafield[@tag=500]">
9667 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
9668 <xsl:call-template name="uri"></xsl:call-template>
9672 <!--3.2 change tmee additional note fields-->
9674 <xsl:for-each select="marc:datafield[@tag=506]">
9675 <note type="restrictions">
9676 <xsl:call-template name="uri"></xsl:call-template>
9677 <xsl:variable name="str">
9678 <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
9679 <xsl:value-of select="."></xsl:value-of>
9680 <xsl:text> </xsl:text>
9683 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
9687 <xsl:for-each select="marc:datafield[@tag=510]">
9688 <note type="citation/reference">
9689 <xsl:call-template name="uri"></xsl:call-template>
9690 <xsl:variable name="str">
9691 <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
9692 <xsl:value-of select="."></xsl:value-of>
9693 <xsl:text> </xsl:text>
9696 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
9701 <xsl:for-each select="marc:datafield[@tag=511]">
9702 <note type="performers">
9703 <xsl:call-template name="uri"></xsl:call-template>
9704 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
9707 <xsl:for-each select="marc:datafield[@tag=518]">
9709 <xsl:call-template name="uri"></xsl:call-template>
9710 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
9714 <xsl:for-each select="marc:datafield[@tag=530]">
9715 <note type="additional physical form">
9716 <xsl:call-template name="uri"></xsl:call-template>
9717 <xsl:variable name="str">
9718 <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
9719 <xsl:value-of select="."></xsl:value-of>
9720 <xsl:text> </xsl:text>
9723 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
9727 <xsl:for-each select="marc:datafield[@tag=533]">
9728 <note type="reproduction">
9729 <xsl:call-template name="uri"></xsl:call-template>
9730 <xsl:variable name="str">
9731 <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
9732 <xsl:value-of select="."></xsl:value-of>
9733 <xsl:text> </xsl:text>
9736 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
9740 <xsl:for-each select="marc:datafield[@tag=534]">
9741 <note type="original version">
9742 <xsl:call-template name="uri"></xsl:call-template>
9743 <xsl:variable name="str">
9744 <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
9745 <xsl:value-of select="."></xsl:value-of>
9746 <xsl:text> </xsl:text>
9749 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
9753 <xsl:for-each select="marc:datafield[@tag=538]">
9754 <note type="system details">
9755 <xsl:call-template name="uri"></xsl:call-template>
9756 <xsl:variable name="str">
9757 <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
9758 <xsl:value-of select="."></xsl:value-of>
9759 <xsl:text> </xsl:text>
9762 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
9766 <xsl:for-each select="marc:datafield[@tag=583]">
9767 <note type="action">
9768 <xsl:call-template name="uri"></xsl:call-template>
9769 <xsl:variable name="str">
9770 <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
9771 <xsl:value-of select="."></xsl:value-of>
9772 <xsl:text> </xsl:text>
9775 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
9783 <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]">
9785 <xsl:call-template name="uri"></xsl:call-template>
9786 <xsl:variable name="str">
9787 <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
9788 <xsl:value-of select="."></xsl:value-of>
9789 <xsl:text> </xsl:text>
9792 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
9795 <xsl:for-each select="marc:datafield[@tag=034][marc:subfield[@code='d' or @code='e' or @code='f' or @code='g']]">
9799 <xsl:call-template name="subfieldSelect">
9800 <xsl:with-param name="codes">defg</xsl:with-param>
9801 </xsl:call-template>
9806 <xsl:for-each select="marc:datafield[@tag=043]">
9808 <xsl:for-each select="marc:subfield[@code='a' or @code='b' or @code='c']">
9810 <xsl:attribute name="authority">
9811 <xsl:if test="@code='a'">
9812 <xsl:text>marcgac</xsl:text>
9814 <xsl:if test="@code='b'">
9815 <xsl:value-of select="following-sibling::marc:subfield[@code=2]"></xsl:value-of>
9817 <xsl:if test="@code='c'">
9818 <xsl:text>iso3166</xsl:text>
9821 <xsl:value-of select="self::marc:subfield"></xsl:value-of>
9826 <!-- tmee 2006/11/27 -->
9827 <xsl:for-each select="marc:datafield[@tag=255]">
9829 <xsl:for-each select="marc:subfield[@code='a' or @code='b' or @code='c']">
9831 <xsl:if test="@code='a'">
9833 <xsl:value-of select="."></xsl:value-of>
9836 <xsl:if test="@code='b'">
9838 <xsl:value-of select="."></xsl:value-of>
9841 <xsl:if test="@code='c'">
9843 <xsl:value-of select="."></xsl:value-of>
9851 <xsl:apply-templates select="marc:datafield[653 >= @tag and @tag >= 600]"></xsl:apply-templates>
9852 <xsl:apply-templates select="marc:datafield[@tag=656]"></xsl:apply-templates>
9853 <xsl:for-each select="marc:datafield[@tag=752]">
9855 <hierarchicalGeographic>
9856 <xsl:for-each select="marc:subfield[@code='a']">
9858 <xsl:call-template name="chopPunctuation">
9859 <xsl:with-param name="chopString" select="."></xsl:with-param>
9860 </xsl:call-template>
9863 <xsl:for-each select="marc:subfield[@code='b']">
9865 <xsl:call-template name="chopPunctuation">
9866 <xsl:with-param name="chopString" select="."></xsl:with-param>
9867 </xsl:call-template>
9870 <xsl:for-each select="marc:subfield[@code='c']">
9872 <xsl:call-template name="chopPunctuation">
9873 <xsl:with-param name="chopString" select="."></xsl:with-param>
9874 </xsl:call-template>
9877 <xsl:for-each select="marc:subfield[@code='d']">
9879 <xsl:call-template name="chopPunctuation">
9880 <xsl:with-param name="chopString" select="."></xsl:with-param>
9881 </xsl:call-template>
9884 </hierarchicalGeographic>
9887 <xsl:for-each select="marc:datafield[@tag=045][marc:subfield[@code='b']]">
9890 <xsl:when test="@ind1=2">
9891 <temporal encoding="iso8601" point="start">
9892 <xsl:call-template name="chopPunctuation">
9893 <xsl:with-param name="chopString">
9894 <xsl:value-of select="marc:subfield[@code='b'][1]"></xsl:value-of>
9896 </xsl:call-template>
9898 <temporal encoding="iso8601" point="end">
9899 <xsl:call-template name="chopPunctuation">
9900 <xsl:with-param name="chopString">
9901 <xsl:value-of select="marc:subfield[@code='b'][2]"></xsl:value-of>
9903 </xsl:call-template>
9907 <xsl:for-each select="marc:subfield[@code='b']">
9908 <temporal encoding="iso8601">
9909 <xsl:call-template name="chopPunctuation">
9910 <xsl:with-param name="chopString" select="."></xsl:with-param>
9911 </xsl:call-template>
9918 <xsl:for-each select="marc:datafield[@tag=050]">
9919 <xsl:for-each select="marc:subfield[@code='b']">
9920 <classification authority="lcc">
9921 <xsl:if test="../marc:subfield[@code='3']">
9922 <xsl:attribute name="displayLabel">
9923 <xsl:value-of select="../marc:subfield[@code='3']"></xsl:value-of>
9926 <xsl:value-of select="preceding-sibling::marc:subfield[@code='a'][1]"></xsl:value-of>
9927 <xsl:text> </xsl:text>
9928 <xsl:value-of select="text()"></xsl:value-of>
9931 <xsl:for-each select="marc:subfield[@code='a'][not(following-sibling::marc:subfield[@code='b'])]">
9932 <classification authority="lcc">
9933 <xsl:if test="../marc:subfield[@code='3']">
9934 <xsl:attribute name="displayLabel">
9935 <xsl:value-of select="../marc:subfield[@code='3']"></xsl:value-of>
9938 <xsl:value-of select="text()"></xsl:value-of>
9942 <xsl:for-each select="marc:datafield[@tag=082]">
9943 <classification authority="ddc">
9944 <xsl:if test="marc:subfield[@code='2']">
9945 <xsl:attribute name="edition">
9946 <xsl:value-of select="marc:subfield[@code='2']"></xsl:value-of>
9949 <xsl:call-template name="subfieldSelect">
9950 <xsl:with-param name="codes">ab</xsl:with-param>
9951 </xsl:call-template>
9954 <xsl:for-each select="marc:datafield[@tag=080]">
9955 <classification authority="udc">
9956 <xsl:call-template name="subfieldSelect">
9957 <xsl:with-param name="codes">abx</xsl:with-param>
9958 </xsl:call-template>
9961 <xsl:for-each select="marc:datafield[@tag=060]">
9962 <classification authority="nlm">
9963 <xsl:call-template name="subfieldSelect">
9964 <xsl:with-param name="codes">ab</xsl:with-param>
9965 </xsl:call-template>
9968 <xsl:for-each select="marc:datafield[@tag=086][@ind1=0]">
9969 <classification authority="sudocs">
9970 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
9973 <xsl:for-each select="marc:datafield[@tag=086][@ind1=1]">
9974 <classification authority="candoc">
9975 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
9978 <xsl:for-each select="marc:datafield[@tag=086]">
9980 <xsl:attribute name="authority">
9981 <xsl:value-of select="marc:subfield[@code='2']"></xsl:value-of>
9983 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
9986 <xsl:for-each select="marc:datafield[@tag=084]">
9988 <xsl:attribute name="authority">
9989 <xsl:value-of select="marc:subfield[@code='2']"></xsl:value-of>
9991 <xsl:call-template name="subfieldSelect">
9992 <xsl:with-param name="codes">ab</xsl:with-param>
9993 </xsl:call-template>
9996 <xsl:for-each select="marc:datafield[@tag=440]">
9997 <relatedItem type="series">
10000 <xsl:call-template name="chopPunctuation">
10001 <xsl:with-param name="chopString">
10002 <xsl:call-template name="subfieldSelect">
10003 <xsl:with-param name="codes">av</xsl:with-param>
10004 </xsl:call-template>
10006 </xsl:call-template>
10008 <xsl:call-template name="part"></xsl:call-template>
10012 <xsl:for-each select="marc:datafield[@tag=490][@ind1=0]">
10013 <relatedItem type="series">
10016 <xsl:call-template name="chopPunctuation">
10017 <xsl:with-param name="chopString">
10018 <xsl:call-template name="subfieldSelect">
10019 <xsl:with-param name="codes">av</xsl:with-param>
10020 </xsl:call-template>
10022 </xsl:call-template>
10024 <xsl:call-template name="part"></xsl:call-template>
10028 <xsl:for-each select="marc:datafield[@tag=510]">
10029 <relatedItem type="isReferencedBy">
10031 <xsl:call-template name="subfieldSelect">
10032 <xsl:with-param name="codes">abcx3</xsl:with-param>
10033 </xsl:call-template>
10037 <xsl:for-each select="marc:datafield[@tag=534]">
10038 <relatedItem type="original">
10039 <xsl:call-template name="relatedTitle"></xsl:call-template>
10040 <xsl:call-template name="relatedName"></xsl:call-template>
10041 <xsl:if test="marc:subfield[@code='b' or @code='c']">
10043 <xsl:for-each select="marc:subfield[@code='c']">
10045 <xsl:value-of select="."></xsl:value-of>
10048 <xsl:for-each select="marc:subfield[@code='b']">
10050 <xsl:value-of select="."></xsl:value-of>
10055 <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
10056 <xsl:for-each select="marc:subfield[@code='z']">
10057 <identifier type="isbn">
10058 <xsl:value-of select="."></xsl:value-of>
10061 <xsl:call-template name="relatedNote"></xsl:call-template>
10064 <xsl:for-each select="marc:datafield[@tag=700][marc:subfield[@code='t']]">
10066 <xsl:call-template name="constituentOrRelatedType"></xsl:call-template>
10069 <xsl:call-template name="chopPunctuation">
10070 <xsl:with-param name="chopString">
10071 <xsl:call-template name="specialSubfieldSelect">
10072 <xsl:with-param name="anyCodes">tfklmorsv</xsl:with-param>
10073 <xsl:with-param name="axis">t</xsl:with-param>
10074 <xsl:with-param name="afterCodes">g</xsl:with-param>
10075 </xsl:call-template>
10077 </xsl:call-template>
10079 <xsl:call-template name="part"></xsl:call-template>
10081 <name type="personal">
10083 <xsl:call-template name="specialSubfieldSelect">
10084 <xsl:with-param name="anyCodes">aq</xsl:with-param>
10085 <xsl:with-param name="axis">t</xsl:with-param>
10086 <xsl:with-param name="beforeCodes">g</xsl:with-param>
10087 </xsl:call-template>
10089 <xsl:call-template name="termsOfAddress"></xsl:call-template>
10090 <xsl:call-template name="nameDate"></xsl:call-template>
10091 <xsl:call-template name="role"></xsl:call-template>
10093 <xsl:call-template name="relatedForm"></xsl:call-template>
10094 <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
10097 <xsl:for-each select="marc:datafield[@tag=710][marc:subfield[@code='t']]">
10099 <xsl:call-template name="constituentOrRelatedType"></xsl:call-template>
10102 <xsl:call-template name="chopPunctuation">
10103 <xsl:with-param name="chopString">
10104 <xsl:call-template name="specialSubfieldSelect">
10105 <xsl:with-param name="anyCodes">tfklmorsv</xsl:with-param>
10106 <xsl:with-param name="axis">t</xsl:with-param>
10107 <xsl:with-param name="afterCodes">dg</xsl:with-param>
10108 </xsl:call-template>
10110 </xsl:call-template>
10112 <xsl:call-template name="relatedPartNumName"></xsl:call-template>
10114 <name type="corporate">
10115 <xsl:for-each select="marc:subfield[@code='a']">
10117 <xsl:value-of select="."></xsl:value-of>
10120 <xsl:for-each select="marc:subfield[@code='b']">
10122 <xsl:value-of select="."></xsl:value-of>
10125 <xsl:variable name="tempNamePart">
10126 <xsl:call-template name="specialSubfieldSelect">
10127 <xsl:with-param name="anyCodes">c</xsl:with-param>
10128 <xsl:with-param name="axis">t</xsl:with-param>
10129 <xsl:with-param name="beforeCodes">dgn</xsl:with-param>
10130 </xsl:call-template>
10132 <xsl:if test="normalize-space($tempNamePart)">
10134 <xsl:value-of select="$tempNamePart"></xsl:value-of>
10137 <xsl:call-template name="role"></xsl:call-template>
10139 <xsl:call-template name="relatedForm"></xsl:call-template>
10140 <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
10143 <xsl:for-each select="marc:datafield[@tag=711][marc:subfield[@code='t']]">
10145 <xsl:call-template name="constituentOrRelatedType"></xsl:call-template>
10148 <xsl:call-template name="chopPunctuation">
10149 <xsl:with-param name="chopString">
10150 <xsl:call-template name="specialSubfieldSelect">
10151 <xsl:with-param name="anyCodes">tfklsv</xsl:with-param>
10152 <xsl:with-param name="axis">t</xsl:with-param>
10153 <xsl:with-param name="afterCodes">g</xsl:with-param>
10154 </xsl:call-template>
10156 </xsl:call-template>
10158 <xsl:call-template name="relatedPartNumName"></xsl:call-template>
10160 <name type="conference">
10162 <xsl:call-template name="specialSubfieldSelect">
10163 <xsl:with-param name="anyCodes">aqdc</xsl:with-param>
10164 <xsl:with-param name="axis">t</xsl:with-param>
10165 <xsl:with-param name="beforeCodes">gn</xsl:with-param>
10166 </xsl:call-template>
10169 <xsl:call-template name="relatedForm"></xsl:call-template>
10170 <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
10173 <xsl:for-each select="marc:datafield[@tag=730][@ind2=2]">
10175 <xsl:call-template name="constituentOrRelatedType"></xsl:call-template>
10178 <xsl:call-template name="chopPunctuation">
10179 <xsl:with-param name="chopString">
10180 <xsl:call-template name="subfieldSelect">
10181 <xsl:with-param name="codes">adfgklmorsv</xsl:with-param>
10182 </xsl:call-template>
10184 </xsl:call-template>
10186 <xsl:call-template name="part"></xsl:call-template>
10188 <xsl:call-template name="relatedForm"></xsl:call-template>
10189 <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
10192 <xsl:for-each select="marc:datafield[@tag=740][@ind2=2]">
10194 <xsl:call-template name="constituentOrRelatedType"></xsl:call-template>
10197 <xsl:call-template name="chopPunctuation">
10198 <xsl:with-param name="chopString">
10199 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
10201 </xsl:call-template>
10203 <xsl:call-template name="part"></xsl:call-template>
10205 <xsl:call-template name="relatedForm"></xsl:call-template>
10208 <xsl:for-each select="marc:datafield[@tag=760]|marc:datafield[@tag=762]">
10209 <relatedItem type="series">
10210 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
10213 <xsl:for-each select="marc:datafield[@tag=765]|marc:datafield[@tag=767]|marc:datafield[@tag=777]|marc:datafield[@tag=787]">
10215 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
10218 <xsl:for-each select="marc:datafield[@tag=775]">
10219 <relatedItem type="otherVersion">
10220 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
10223 <xsl:for-each select="marc:datafield[@tag=770]|marc:datafield[@tag=774]">
10224 <relatedItem type="constituent">
10225 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
10228 <xsl:for-each select="marc:datafield[@tag=772]|marc:datafield[@tag=773]">
10229 <relatedItem type="host">
10230 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
10233 <xsl:for-each select="marc:datafield[@tag=776]">
10234 <relatedItem type="otherFormat">
10235 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
10238 <xsl:for-each select="marc:datafield[@tag=780]">
10239 <relatedItem type="preceding">
10240 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
10243 <xsl:for-each select="marc:datafield[@tag=785]">
10244 <relatedItem type="succeeding">
10245 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
10248 <xsl:for-each select="marc:datafield[@tag=786]">
10249 <relatedItem type="original">
10250 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
10253 <xsl:for-each select="marc:datafield[@tag=800]">
10254 <relatedItem type="series">
10257 <xsl:call-template name="chopPunctuation">
10258 <xsl:with-param name="chopString">
10259 <xsl:call-template name="specialSubfieldSelect">
10260 <xsl:with-param name="anyCodes">tfklmorsv</xsl:with-param>
10261 <xsl:with-param name="axis">t</xsl:with-param>
10262 <xsl:with-param name="afterCodes">g</xsl:with-param>
10263 </xsl:call-template>
10265 </xsl:call-template>
10267 <xsl:call-template name="part"></xsl:call-template>
10269 <name type="personal">
10271 <xsl:call-template name="chopPunctuation">
10272 <xsl:with-param name="chopString">
10273 <xsl:call-template name="specialSubfieldSelect">
10274 <xsl:with-param name="anyCodes">aq</xsl:with-param>
10275 <xsl:with-param name="axis">t</xsl:with-param>
10276 <xsl:with-param name="beforeCodes">g</xsl:with-param>
10277 </xsl:call-template>
10279 </xsl:call-template>
10281 <xsl:call-template name="termsOfAddress"></xsl:call-template>
10282 <xsl:call-template name="nameDate"></xsl:call-template>
10283 <xsl:call-template name="role"></xsl:call-template>
10285 <xsl:call-template name="relatedForm"></xsl:call-template>
10288 <xsl:for-each select="marc:datafield[@tag=810]">
10289 <relatedItem type="series">
10292 <xsl:call-template name="chopPunctuation">
10293 <xsl:with-param name="chopString">
10294 <xsl:call-template name="specialSubfieldSelect">
10295 <xsl:with-param name="anyCodes">tfklmorsv</xsl:with-param>
10296 <xsl:with-param name="axis">t</xsl:with-param>
10297 <xsl:with-param name="afterCodes">dg</xsl:with-param>
10298 </xsl:call-template>
10300 </xsl:call-template>
10302 <xsl:call-template name="relatedPartNumName"></xsl:call-template>
10304 <name type="corporate">
10305 <xsl:for-each select="marc:subfield[@code='a']">
10307 <xsl:value-of select="."></xsl:value-of>
10310 <xsl:for-each select="marc:subfield[@code='b']">
10312 <xsl:value-of select="."></xsl:value-of>
10316 <xsl:call-template name="specialSubfieldSelect">
10317 <xsl:with-param name="anyCodes">c</xsl:with-param>
10318 <xsl:with-param name="axis">t</xsl:with-param>
10319 <xsl:with-param name="beforeCodes">dgn</xsl:with-param>
10320 </xsl:call-template>
10322 <xsl:call-template name="role"></xsl:call-template>
10324 <xsl:call-template name="relatedForm"></xsl:call-template>
10327 <xsl:for-each select="marc:datafield[@tag=811]">
10328 <relatedItem type="series">
10331 <xsl:call-template name="chopPunctuation">
10332 <xsl:with-param name="chopString">
10333 <xsl:call-template name="specialSubfieldSelect">
10334 <xsl:with-param name="anyCodes">tfklsv</xsl:with-param>
10335 <xsl:with-param name="axis">t</xsl:with-param>
10336 <xsl:with-param name="afterCodes">g</xsl:with-param>
10337 </xsl:call-template>
10339 </xsl:call-template>
10341 <xsl:call-template name="relatedPartNumName"/>
10343 <name type="conference">
10345 <xsl:call-template name="specialSubfieldSelect">
10346 <xsl:with-param name="anyCodes">aqdc</xsl:with-param>
10347 <xsl:with-param name="axis">t</xsl:with-param>
10348 <xsl:with-param name="beforeCodes">gn</xsl:with-param>
10349 </xsl:call-template>
10351 <xsl:call-template name="role"/>
10353 <xsl:call-template name="relatedForm"/>
10356 <xsl:for-each select="marc:datafield[@tag='830']">
10357 <relatedItem type="series">
10360 <xsl:call-template name="chopPunctuation">
10361 <xsl:with-param name="chopString">
10362 <xsl:call-template name="subfieldSelect">
10363 <xsl:with-param name="codes">adfgklmorsv</xsl:with-param>
10364 </xsl:call-template>
10366 </xsl:call-template>
10368 <xsl:call-template name="part"/>
10370 <xsl:call-template name="relatedForm"/>
10373 <xsl:for-each select="marc:datafield[@tag='856'][@ind2='2']/marc:subfield[@code='q']">
10375 <internetMediaType>
10376 <xsl:value-of select="."/>
10377 </internetMediaType>
10380 <xsl:for-each select="marc:datafield[@tag='020']">
10381 <xsl:call-template name="isInvalid">
10382 <xsl:with-param name="type">isbn</xsl:with-param>
10383 </xsl:call-template>
10384 <xsl:if test="marc:subfield[@code='a']">
10385 <identifier type="isbn">
10386 <xsl:value-of select="marc:subfield[@code='a']"/>
10390 <xsl:for-each select="marc:datafield[@tag='024'][@ind1='0']">
10391 <xsl:call-template name="isInvalid">
10392 <xsl:with-param name="type">isrc</xsl:with-param>
10393 </xsl:call-template>
10394 <xsl:if test="marc:subfield[@code='a']">
10395 <identifier type="isrc">
10396 <xsl:value-of select="marc:subfield[@code='a']"/>
10400 <xsl:for-each select="marc:datafield[@tag='024'][@ind1='2']">
10401 <xsl:call-template name="isInvalid">
10402 <xsl:with-param name="type">ismn</xsl:with-param>
10403 </xsl:call-template>
10404 <xsl:if test="marc:subfield[@code='a']">
10405 <identifier type="ismn">
10406 <xsl:value-of select="marc:subfield[@code='a']"/>
10410 <xsl:for-each select="marc:datafield[@tag='024'][@ind1='4']">
10411 <xsl:call-template name="isInvalid">
10412 <xsl:with-param name="type">sici</xsl:with-param>
10413 </xsl:call-template>
10414 <identifier type="sici">
10415 <xsl:call-template name="subfieldSelect">
10416 <xsl:with-param name="codes">ab</xsl:with-param>
10417 </xsl:call-template>
10420 <xsl:for-each select="marc:datafield[@tag='022']">
10421 <xsl:call-template name="isInvalid">
10422 <xsl:with-param name="type">issn</xsl:with-param>
10423 </xsl:call-template>
10424 <identifier type="issn">
10425 <xsl:value-of select="marc:subfield[@code='a']"/>
10428 <xsl:for-each select="marc:datafield[@tag='010']">
10429 <xsl:call-template name="isInvalid">
10430 <xsl:with-param name="type">lccn</xsl:with-param>
10431 </xsl:call-template>
10432 <identifier type="lccn">
10433 <xsl:value-of select="normalize-space(marc:subfield[@code='a'])"/>
10436 <xsl:for-each select="marc:datafield[@tag='028']">
10438 <xsl:attribute name="type">
10440 <xsl:when test="@ind1='0'">issue number</xsl:when>
10441 <xsl:when test="@ind1='1'">matrix number</xsl:when>
10442 <xsl:when test="@ind1='2'">music plate</xsl:when>
10443 <xsl:when test="@ind1='3'">music publisher</xsl:when>
10444 <xsl:when test="@ind1='4'">videorecording identifier</xsl:when>
10447 <!--<xsl:call-template name="isInvalid"/>--> <!-- no $z in 028 -->
10448 <xsl:call-template name="subfieldSelect">
10449 <xsl:with-param name="codes">
10451 <xsl:when test="@ind1='0'">ba</xsl:when>
10452 <xsl:otherwise>ab</xsl:otherwise>
10455 </xsl:call-template>
10458 <xsl:for-each select="marc:datafield[@tag='037']">
10459 <identifier type="stock number">
10460 <!--<xsl:call-template name="isInvalid"/>--> <!-- no $z in 037 -->
10461 <xsl:call-template name="subfieldSelect">
10462 <xsl:with-param name="codes">ab</xsl:with-param>
10463 </xsl:call-template>
10466 <xsl:for-each select="marc:datafield[@tag='856'][marc:subfield[@code='u']]">
10468 <xsl:attribute name="type">
10470 <xsl:when test="starts-with(marc:subfield[@code='u'],'urn:doi') or starts-with(marc:subfield[@code='u'],'doi')">doi</xsl:when>
10471 <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>
10472 <xsl:otherwise>uri</xsl:otherwise>
10476 <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') ">
10477 <xsl:value-of select="concat('hdl:',substring-after(marc:subfield[@code='u'],'http://hdl.loc.gov/'))"></xsl:value-of>
10480 <xsl:value-of select="marc:subfield[@code='u']"></xsl:value-of>
10484 <xsl:if test="starts-with(marc:subfield[@code='u'],'urn:hdl') or starts-with(marc:subfield[@code='u'],'hdl')">
10485 <identifier type="hdl">
10486 <xsl:if test="marc:subfield[@code='y' or @code='3' or @code='z']">
10487 <xsl:attribute name="displayLabel">
10488 <xsl:call-template name="subfieldSelect">
10489 <xsl:with-param name="codes">y3z</xsl:with-param>
10490 </xsl:call-template>
10493 <xsl:value-of select="concat('hdl:',substring-after(marc:subfield[@code='u'],'http://hdl.loc.gov/'))"></xsl:value-of>
10497 <xsl:for-each select="marc:datafield[@tag=024][@ind1=1]">
10498 <identifier type="upc">
10499 <xsl:call-template name="isInvalid"/>
10500 <xsl:value-of select="marc:subfield[@code='a']"/>
10503 <!-- 1/04 fix added $y -->
10504 <xsl:for-each select="marc:datafield[@tag=856][marc:subfield[@code='u']]">
10507 <xsl:if test="marc:subfield[@code='y' or @code='3']">
10508 <xsl:attribute name="displayLabel">
10509 <xsl:call-template name="subfieldSelect">
10510 <xsl:with-param name="codes">y3</xsl:with-param>
10511 </xsl:call-template>
10514 <xsl:if test="marc:subfield[@code='z' ]">
10515 <xsl:attribute name="note">
10516 <xsl:call-template name="subfieldSelect">
10517 <xsl:with-param name="codes">z</xsl:with-param>
10518 </xsl:call-template>
10521 <xsl:value-of select="marc:subfield[@code='u']"></xsl:value-of>
10527 <!-- 3.2 change tmee 856z -->
10530 <xsl:for-each select="marc:datafield[@tag=852]">
10533 <xsl:call-template name="displayLabel"></xsl:call-template>
10534 <xsl:call-template name="subfieldSelect">
10535 <xsl:with-param name="codes">abje</xsl:with-param>
10536 </xsl:call-template>
10537 </physicalLocation>
10540 <xsl:for-each select="marc:datafield[@tag=506]">
10541 <accessCondition type="restrictionOnAccess">
10542 <xsl:call-template name="subfieldSelect">
10543 <xsl:with-param name="codes">abcd35</xsl:with-param>
10544 </xsl:call-template>
10547 <xsl:for-each select="marc:datafield[@tag=540]">
10548 <accessCondition type="useAndReproduction">
10549 <xsl:call-template name="subfieldSelect">
10550 <xsl:with-param name="codes">abcde35</xsl:with-param>
10551 </xsl:call-template>
10555 <xsl:for-each select="marc:datafield[@tag=040]">
10556 <recordContentSource authority="marcorg">
10557 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
10558 </recordContentSource>
10560 <xsl:for-each select="marc:controlfield[@tag=008]">
10561 <recordCreationDate encoding="marc">
10562 <xsl:value-of select="substring(.,1,6)"></xsl:value-of>
10563 </recordCreationDate>
10565 <xsl:for-each select="marc:controlfield[@tag=005]">
10566 <recordChangeDate encoding="iso8601">
10567 <xsl:value-of select="."></xsl:value-of>
10568 </recordChangeDate>
10570 <xsl:for-each select="marc:controlfield[@tag=001]">
10572 <xsl:if test="../marc:controlfield[@tag=003]">
10573 <xsl:attribute name="source">
10574 <xsl:value-of select="../marc:controlfield[@tag=003]"></xsl:value-of>
10577 <xsl:value-of select="."></xsl:value-of>
10578 </recordIdentifier>
10580 <xsl:for-each select="marc:datafield[@tag=040]/marc:subfield[@code='b']">
10581 <languageOfCataloging>
10582 <languageTerm authority="iso639-2b" type="code">
10583 <xsl:value-of select="."></xsl:value-of>
10585 </languageOfCataloging>
10589 <xsl:template name="displayForm">
10590 <xsl:for-each select="marc:subfield[@code='c']">
10592 <xsl:value-of select="."></xsl:value-of>
10596 <xsl:template name="affiliation">
10597 <xsl:for-each select="marc:subfield[@code='u']">
10599 <xsl:value-of select="."></xsl:value-of>
10603 <xsl:template name="uri">
10604 <xsl:for-each select="marc:subfield[@code='u']">
10605 <xsl:attribute name="xlink:href">
10606 <xsl:value-of select="."></xsl:value-of>
10610 <xsl:template name="role">
10611 <xsl:for-each select="marc:subfield[@code='e']">
10613 <roleTerm type="text">
10614 <xsl:value-of select="."></xsl:value-of>
10618 <xsl:for-each select="marc:subfield[@code='4']">
10620 <roleTerm authority="marcrelator" type="code">
10621 <xsl:value-of select="."></xsl:value-of>
10626 <xsl:template name="part">
10627 <xsl:variable name="partNumber">
10628 <xsl:call-template name="specialSubfieldSelect">
10629 <xsl:with-param name="axis">n</xsl:with-param>
10630 <xsl:with-param name="anyCodes">n</xsl:with-param>
10631 <xsl:with-param name="afterCodes">fgkdlmor</xsl:with-param>
10632 </xsl:call-template>
10634 <xsl:variable name="partName">
10635 <xsl:call-template name="specialSubfieldSelect">
10636 <xsl:with-param name="axis">p</xsl:with-param>
10637 <xsl:with-param name="anyCodes">p</xsl:with-param>
10638 <xsl:with-param name="afterCodes">fgkdlmor</xsl:with-param>
10639 </xsl:call-template>
10641 <xsl:if test="string-length(normalize-space($partNumber))">
10643 <xsl:call-template name="chopPunctuation">
10644 <xsl:with-param name="chopString" select="$partNumber"></xsl:with-param>
10645 </xsl:call-template>
10648 <xsl:if test="string-length(normalize-space($partName))">
10650 <xsl:call-template name="chopPunctuation">
10651 <xsl:with-param name="chopString" select="$partName"></xsl:with-param>
10652 </xsl:call-template>
10656 <xsl:template name="relatedPart">
10657 <xsl:if test="@tag=773">
10658 <xsl:for-each select="marc:subfield[@code='g']">
10661 <xsl:value-of select="."></xsl:value-of>
10665 <xsl:for-each select="marc:subfield[@code='q']">
10667 <xsl:call-template name="parsePart"></xsl:call-template>
10672 <xsl:template name="relatedPartNumName">
10673 <xsl:variable name="partNumber">
10674 <xsl:call-template name="specialSubfieldSelect">
10675 <xsl:with-param name="axis">g</xsl:with-param>
10676 <xsl:with-param name="anyCodes">g</xsl:with-param>
10677 <xsl:with-param name="afterCodes">pst</xsl:with-param>
10678 </xsl:call-template>
10680 <xsl:variable name="partName">
10681 <xsl:call-template name="specialSubfieldSelect">
10682 <xsl:with-param name="axis">p</xsl:with-param>
10683 <xsl:with-param name="anyCodes">p</xsl:with-param>
10684 <xsl:with-param name="afterCodes">fgkdlmor</xsl:with-param>
10685 </xsl:call-template>
10687 <xsl:if test="string-length(normalize-space($partNumber))">
10689 <xsl:value-of select="$partNumber"></xsl:value-of>
10692 <xsl:if test="string-length(normalize-space($partName))">
10694 <xsl:value-of select="$partName"></xsl:value-of>
10698 <xsl:template name="relatedName">
10699 <xsl:for-each select="marc:subfield[@code='a']">
10702 <xsl:value-of select="."></xsl:value-of>
10707 <xsl:template name="relatedForm">
10708 <xsl:for-each select="marc:subfield[@code='h']">
10709 <physicalDescription>
10711 <xsl:value-of select="."></xsl:value-of>
10713 </physicalDescription>
10716 <xsl:template name="relatedExtent">
10717 <xsl:for-each select="marc:subfield[@code='h']">
10718 <physicalDescription>
10720 <xsl:value-of select="."></xsl:value-of>
10722 </physicalDescription>
10725 <xsl:template name="relatedNote">
10726 <xsl:for-each select="marc:subfield[@code='n']">
10728 <xsl:value-of select="."></xsl:value-of>
10732 <xsl:template name="relatedSubject">
10733 <xsl:for-each select="marc:subfield[@code='j']">
10735 <temporal encoding="iso8601">
10736 <xsl:call-template name="chopPunctuation">
10737 <xsl:with-param name="chopString" select="."></xsl:with-param>
10738 </xsl:call-template>
10743 <xsl:template name="relatedIdentifierISSN">
10744 <xsl:for-each select="marc:subfield[@code='x']">
10745 <identifier type="issn">
10746 <xsl:value-of select="."></xsl:value-of>
10750 <xsl:template name="relatedIdentifierLocal">
10751 <xsl:for-each select="marc:subfield[@code='w']">
10752 <identifier type="local">
10753 <xsl:value-of select="."></xsl:value-of>
10757 <xsl:template name="relatedIdentifier">
10758 <xsl:for-each select="marc:subfield[@code='o']">
10760 <xsl:value-of select="."></xsl:value-of>
10764 <xsl:template name="relatedItem76X-78X">
10765 <xsl:call-template name="displayLabel"></xsl:call-template>
10766 <xsl:call-template name="relatedTitle76X-78X"></xsl:call-template>
10767 <xsl:call-template name="relatedName"></xsl:call-template>
10768 <xsl:call-template name="relatedOriginInfo"></xsl:call-template>
10769 <xsl:call-template name="relatedLanguage"></xsl:call-template>
10770 <xsl:call-template name="relatedExtent"></xsl:call-template>
10771 <xsl:call-template name="relatedNote"></xsl:call-template>
10772 <xsl:call-template name="relatedSubject"></xsl:call-template>
10773 <xsl:call-template name="relatedIdentifier"></xsl:call-template>
10774 <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
10775 <xsl:call-template name="relatedIdentifierLocal"></xsl:call-template>
10776 <xsl:call-template name="relatedPart"></xsl:call-template>
10778 <xsl:template name="subjectGeographicZ">
10780 <xsl:call-template name="chopPunctuation">
10781 <xsl:with-param name="chopString" select="."></xsl:with-param>
10782 </xsl:call-template>
10785 <xsl:template name="subjectTemporalY">
10787 <xsl:call-template name="chopPunctuation">
10788 <xsl:with-param name="chopString" select="."></xsl:with-param>
10789 </xsl:call-template>
10792 <xsl:template name="subjectTopic">
10794 <xsl:call-template name="chopPunctuation">
10795 <xsl:with-param name="chopString" select="."></xsl:with-param>
10796 </xsl:call-template>
10799 <!-- 3.2 change tmee 6xx $v genre -->
10800 <xsl:template name="subjectGenre">
10802 <xsl:call-template name="chopPunctuation">
10803 <xsl:with-param name="chopString" select="."></xsl:with-param>
10804 </xsl:call-template>
10808 <xsl:template name="nameABCDN">
10809 <xsl:for-each select="marc:subfield[@code='a']">
10811 <xsl:call-template name="chopPunctuation">
10812 <xsl:with-param name="chopString" select="."></xsl:with-param>
10813 </xsl:call-template>
10816 <xsl:for-each select="marc:subfield[@code='b']">
10818 <xsl:value-of select="."></xsl:value-of>
10821 <xsl:if test="marc:subfield[@code='c'] or marc:subfield[@code='d'] or marc:subfield[@code='n']">
10823 <xsl:call-template name="subfieldSelect">
10824 <xsl:with-param name="codes">cdn</xsl:with-param>
10825 </xsl:call-template>
10829 <xsl:template name="nameABCDQ">
10831 <xsl:call-template name="chopPunctuation">
10832 <xsl:with-param name="chopString">
10833 <xsl:call-template name="subfieldSelect">
10834 <xsl:with-param name="codes">aq</xsl:with-param>
10835 </xsl:call-template>
10837 <xsl:with-param name="punctuation">
10838 <xsl:text>:,;/ </xsl:text>
10840 </xsl:call-template>
10842 <xsl:call-template name="termsOfAddress"></xsl:call-template>
10843 <xsl:call-template name="nameDate"></xsl:call-template>
10845 <xsl:template name="nameACDEQ">
10847 <xsl:call-template name="subfieldSelect">
10848 <xsl:with-param name="codes">acdeq</xsl:with-param>
10849 </xsl:call-template>
10852 <xsl:template name="constituentOrRelatedType">
10853 <xsl:if test="@ind2=2">
10854 <xsl:attribute name="type">constituent</xsl:attribute>
10857 <xsl:template name="relatedTitle">
10858 <xsl:for-each select="marc:subfield[@code='t']">
10861 <xsl:call-template name="chopPunctuation">
10862 <xsl:with-param name="chopString">
10863 <xsl:value-of select="."></xsl:value-of>
10865 </xsl:call-template>
10870 <xsl:template name="relatedTitle76X-78X">
10871 <xsl:for-each select="marc:subfield[@code='t']">
10874 <xsl:call-template name="chopPunctuation">
10875 <xsl:with-param name="chopString">
10876 <xsl:value-of select="."></xsl:value-of>
10878 </xsl:call-template>
10880 <xsl:if test="marc:datafield[@tag!=773]and marc:subfield[@code='g']">
10881 <xsl:call-template name="relatedPartNumName"></xsl:call-template>
10885 <xsl:for-each select="marc:subfield[@code='p']">
10886 <titleInfo type="abbreviated">
10888 <xsl:call-template name="chopPunctuation">
10889 <xsl:with-param name="chopString">
10890 <xsl:value-of select="."></xsl:value-of>
10892 </xsl:call-template>
10894 <xsl:if test="marc:datafield[@tag!=773]and marc:subfield[@code='g']">
10895 <xsl:call-template name="relatedPartNumName"></xsl:call-template>
10899 <xsl:for-each select="marc:subfield[@code='s']">
10900 <titleInfo type="uniform">
10902 <xsl:call-template name="chopPunctuation">
10903 <xsl:with-param name="chopString">
10904 <xsl:value-of select="."></xsl:value-of>
10906 </xsl:call-template>
10908 <xsl:if test="marc:datafield[@tag!=773]and marc:subfield[@code='g']">
10909 <xsl:call-template name="relatedPartNumName"></xsl:call-template>
10914 <xsl:template name="relatedOriginInfo">
10915 <xsl:if test="marc:subfield[@code='b' or @code='d'] or marc:subfield[@code='f']">
10917 <xsl:if test="@tag=775">
10918 <xsl:for-each select="marc:subfield[@code='f']">
10921 <xsl:attribute name="type">code</xsl:attribute>
10922 <xsl:attribute name="authority">marcgac</xsl:attribute>
10923 <xsl:value-of select="."></xsl:value-of>
10928 <xsl:for-each select="marc:subfield[@code='d']">
10930 <xsl:value-of select="."></xsl:value-of>
10933 <xsl:for-each select="marc:subfield[@code='b']">
10935 <xsl:value-of select="."></xsl:value-of>
10941 <xsl:template name="relatedLanguage">
10942 <xsl:for-each select="marc:subfield[@code='e']">
10943 <xsl:call-template name="getLanguage">
10944 <xsl:with-param name="langString">
10945 <xsl:value-of select="."></xsl:value-of>
10947 </xsl:call-template>
10950 <xsl:template name="nameDate">
10951 <xsl:for-each select="marc:subfield[@code='d']">
10952 <namePart type="date">
10953 <xsl:call-template name="chopPunctuation">
10954 <xsl:with-param name="chopString" select="."></xsl:with-param>
10955 </xsl:call-template>
10959 <xsl:template name="subjectAuthority">
10960 <xsl:if test="@ind2!=4">
10961 <xsl:if test="@ind2!=' '">
10962 <xsl:if test="@ind2!=8">
10963 <xsl:if test="@ind2!=9">
10964 <xsl:attribute name="authority">
10966 <xsl:when test="@ind2=0">lcsh</xsl:when>
10967 <xsl:when test="@ind2=1">lcshac</xsl:when>
10968 <xsl:when test="@ind2=2">mesh</xsl:when>
10970 <xsl:when test="@ind2=3">nal</xsl:when>
10971 <xsl:when test="@ind2=5">csh</xsl:when>
10972 <xsl:when test="@ind2=6">rvm</xsl:when>
10973 <xsl:when test="@ind2=7">
10974 <xsl:value-of select="marc:subfield[@code='2']"></xsl:value-of>
10983 <xsl:template name="subjectAnyOrder">
10984 <xsl:for-each select="marc:subfield[@code='v' or @code='x' or @code='y' or @code='z']">
10986 <xsl:when test="@code='v'">
10987 <xsl:call-template name="subjectGenre"></xsl:call-template>
10989 <xsl:when test="@code='x'">
10990 <xsl:call-template name="subjectTopic"></xsl:call-template>
10992 <xsl:when test="@code='y'">
10993 <xsl:call-template name="subjectTemporalY"></xsl:call-template>
10995 <xsl:when test="@code='z'">
10996 <xsl:call-template name="subjectGeographicZ"></xsl:call-template>
11001 <xsl:template name="specialSubfieldSelect">
11002 <xsl:param name="anyCodes"></xsl:param>
11003 <xsl:param name="axis"></xsl:param>
11004 <xsl:param name="beforeCodes"></xsl:param>
11005 <xsl:param name="afterCodes"></xsl:param>
11006 <xsl:variable name="str">
11007 <xsl:for-each select="marc:subfield">
11008 <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])">
11009 <xsl:value-of select="text()"></xsl:value-of>
11010 <xsl:text> </xsl:text>
11014 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
11017 <!-- 3.2 change tmee 6xx $v genre -->
11018 <xsl:template match="marc:datafield[@tag=600]">
11020 <xsl:call-template name="subjectAuthority"></xsl:call-template>
11021 <name type="personal">
11022 <xsl:call-template name="termsOfAddress"></xsl:call-template>
11024 <xsl:call-template name="chopPunctuation">
11025 <xsl:with-param name="chopString">
11026 <xsl:call-template name="subfieldSelect">
11027 <xsl:with-param name="codes">aq</xsl:with-param>
11028 </xsl:call-template>
11030 </xsl:call-template>
11032 <xsl:call-template name="nameDate"></xsl:call-template>
11033 <xsl:call-template name="affiliation"></xsl:call-template>
11034 <xsl:call-template name="role"></xsl:call-template>
11036 <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
11039 <xsl:template match="marc:datafield[@tag=610]">
11041 <xsl:call-template name="subjectAuthority"></xsl:call-template>
11042 <name type="corporate">
11043 <xsl:for-each select="marc:subfield[@code='a']">
11045 <xsl:value-of select="."></xsl:value-of>
11048 <xsl:for-each select="marc:subfield[@code='b']">
11050 <xsl:value-of select="."></xsl:value-of>
11053 <xsl:if test="marc:subfield[@code='c' or @code='d' or @code='n' or @code='p']">
11055 <xsl:call-template name="subfieldSelect">
11056 <xsl:with-param name="codes">cdnp</xsl:with-param>
11057 </xsl:call-template>
11060 <xsl:call-template name="role"></xsl:call-template>
11062 <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
11065 <xsl:template match="marc:datafield[@tag=611]">
11067 <xsl:call-template name="subjectAuthority"></xsl:call-template>
11068 <name type="conference">
11070 <xsl:call-template name="subfieldSelect">
11071 <xsl:with-param name="codes">abcdeqnp</xsl:with-param>
11072 </xsl:call-template>
11074 <xsl:for-each select="marc:subfield[@code='4']">
11076 <roleTerm authority="marcrelator" type="code">
11077 <xsl:value-of select="."></xsl:value-of>
11082 <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
11085 <xsl:template match="marc:datafield[@tag=630]">
11087 <xsl:call-template name="subjectAuthority"></xsl:call-template>
11090 <xsl:call-template name="chopPunctuation">
11091 <xsl:with-param name="chopString">
11092 <xsl:call-template name="subfieldSelect">
11093 <xsl:with-param name="codes">adfhklor</xsl:with-param>
11094 </xsl:call-template>
11096 </xsl:call-template>
11097 <xsl:call-template name="part"></xsl:call-template>
11100 <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
11103 <xsl:template match="marc:datafield[@tag=650]">
11105 <xsl:call-template name="subjectAuthority"></xsl:call-template>
11107 <xsl:call-template name="chopPunctuation">
11108 <xsl:with-param name="chopString">
11109 <xsl:call-template name="subfieldSelect">
11110 <xsl:with-param name="codes">abcd</xsl:with-param>
11111 </xsl:call-template>
11113 </xsl:call-template>
11115 <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
11118 <xsl:template match="marc:datafield[@tag=651]">
11120 <xsl:call-template name="subjectAuthority"></xsl:call-template>
11121 <xsl:for-each select="marc:subfield[@code='a']">
11123 <xsl:call-template name="chopPunctuation">
11124 <xsl:with-param name="chopString" select="."></xsl:with-param>
11125 </xsl:call-template>
11128 <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
11131 <xsl:template match="marc:datafield[@tag=653]">
11133 <xsl:for-each select="marc:subfield[@code='a']">
11135 <xsl:value-of select="."></xsl:value-of>
11140 <xsl:template match="marc:datafield[@tag=656]">
11142 <xsl:if test="marc:subfield[@code=2]">
11143 <xsl:attribute name="authority">
11144 <xsl:value-of select="marc:subfield[@code=2]"></xsl:value-of>
11148 <xsl:call-template name="chopPunctuation">
11149 <xsl:with-param name="chopString">
11150 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
11152 </xsl:call-template>
11156 <xsl:template name="termsOfAddress">
11157 <xsl:if test="marc:subfield[@code='b' or @code='c']">
11158 <namePart type="termsOfAddress">
11159 <xsl:call-template name="chopPunctuation">
11160 <xsl:with-param name="chopString">
11161 <xsl:call-template name="subfieldSelect">
11162 <xsl:with-param name="codes">bc</xsl:with-param>
11163 </xsl:call-template>
11165 </xsl:call-template>
11169 <xsl:template name="displayLabel">
11170 <xsl:if test="marc:subfield[@code='i']">
11171 <xsl:attribute name="displayLabel">
11172 <xsl:value-of select="marc:subfield[@code='i']"></xsl:value-of>
11175 <xsl:if test="marc:subfield[@code='3']">
11176 <xsl:attribute name="displayLabel">
11177 <xsl:value-of select="marc:subfield[@code='3']"></xsl:value-of>
11181 <xsl:template name="isInvalid">
11182 <xsl:param name="type"/>
11183 <xsl:if test="marc:subfield[@code='z'] or marc:subfield[@code='y']">
11185 <xsl:attribute name="type">
11186 <xsl:value-of select="$type"/>
11188 <xsl:attribute name="invalid">
11189 <xsl:text>yes</xsl:text>
11191 <xsl:if test="marc:subfield[@code='z']">
11192 <xsl:value-of select="marc:subfield[@code='z']"/>
11194 <xsl:if test="marc:subfield[@code='y']">
11195 <xsl:value-of select="marc:subfield[@code='y']"/>
11200 <xsl:template name="subtitle">
11201 <xsl:if test="marc:subfield[@code='b']">
11203 <xsl:call-template name="chopPunctuation">
11204 <xsl:with-param name="chopString">
11205 <xsl:value-of select="marc:subfield[@code='b']"/>
11206 <!--<xsl:call-template name="subfieldSelect">
11207 <xsl:with-param name="codes">b</xsl:with-param>
11208 </xsl:call-template>-->
11210 </xsl:call-template>
11214 <xsl:template name="script">
11215 <xsl:param name="scriptCode"></xsl:param>
11216 <xsl:attribute name="script">
11218 <xsl:when test="$scriptCode='(3'">Arabic</xsl:when>
11219 <xsl:when test="$scriptCode='(B'">Latin</xsl:when>
11220 <xsl:when test="$scriptCode='$1'">Chinese, Japanese, Korean</xsl:when>
11221 <xsl:when test="$scriptCode='(N'">Cyrillic</xsl:when>
11222 <xsl:when test="$scriptCode='(2'">Hebrew</xsl:when>
11223 <xsl:when test="$scriptCode='(S'">Greek</xsl:when>
11227 <xsl:template name="parsePart">
11228 <!-- assumes 773$q= 1:2:3<4
11229 with up to 3 levels and one optional start page
11231 <xsl:variable name="level1">
11233 <xsl:when test="contains(text(),':')">
11235 <xsl:value-of select="substring-before(text(),':')"></xsl:value-of>
11237 <xsl:when test="not(contains(text(),':'))">
11239 <xsl:if test="contains(text(),'<')">
11241 <xsl:value-of select="substring-before(text(),'<')"></xsl:value-of>
11243 <xsl:if test="not(contains(text(),'<'))">
11245 <xsl:value-of select="text()"></xsl:value-of>
11250 <xsl:variable name="sici2">
11252 <xsl:when test="starts-with(substring-after(text(),$level1),':')">
11253 <xsl:value-of select="substring(substring-after(text(),$level1),2)"></xsl:value-of>
11256 <xsl:value-of select="substring-after(text(),$level1)"></xsl:value-of>
11260 <xsl:variable name="level2">
11262 <xsl:when test="contains($sici2,':')">
11264 <xsl:value-of select="substring-before($sici2,':')"></xsl:value-of>
11266 <xsl:when test="contains($sici2,'<')">
11268 <xsl:value-of select="substring-before($sici2,'<')"></xsl:value-of>
11271 <xsl:value-of select="$sici2"></xsl:value-of>
11276 <xsl:variable name="sici3">
11278 <xsl:when test="starts-with(substring-after($sici2,$level2),':')">
11279 <xsl:value-of select="substring(substring-after($sici2,$level2),2)"></xsl:value-of>
11282 <xsl:value-of select="substring-after($sici2,$level2)"></xsl:value-of>
11286 <xsl:variable name="level3">
11288 <xsl:when test="contains($sici3,'<')">
11290 <xsl:value-of select="substring-before($sici3,'<')"></xsl:value-of>
11293 <xsl:value-of select="$sici3"></xsl:value-of>
11298 <xsl:variable name="page">
11299 <xsl:if test="contains(text(),'<')">
11300 <xsl:value-of select="substring-after(text(),'<')"></xsl:value-of>
11303 <xsl:if test="$level1">
11306 <xsl:value-of select="$level1"></xsl:value-of>
11310 <xsl:if test="$level2">
11313 <xsl:value-of select="$level2"></xsl:value-of>
11317 <xsl:if test="$level3">
11320 <xsl:value-of select="$level3"></xsl:value-of>
11324 <xsl:if test="$page">
11325 <extent unit="page">
11327 <xsl:value-of select="$page"></xsl:value-of>
11332 <xsl:template name="getLanguage">
11333 <xsl:param name="langString"></xsl:param>
11334 <xsl:param name="controlField008-35-37"></xsl:param>
11335 <xsl:variable name="length" select="string-length($langString)"></xsl:variable>
11337 <xsl:when test="$length=0"></xsl:when>
11338 <xsl:when test="$controlField008-35-37=substring($langString,1,3)">
11339 <xsl:call-template name="getLanguage">
11340 <xsl:with-param name="langString" select="substring($langString,4,$length)"></xsl:with-param>
11341 <xsl:with-param name="controlField008-35-37" select="$controlField008-35-37"></xsl:with-param>
11342 </xsl:call-template>
11346 <languageTerm authority="iso639-2b" type="code">
11347 <xsl:value-of select="substring($langString,1,3)"></xsl:value-of>
11350 <xsl:call-template name="getLanguage">
11351 <xsl:with-param name="langString" select="substring($langString,4,$length)"></xsl:with-param>
11352 <xsl:with-param name="controlField008-35-37" select="$controlField008-35-37"></xsl:with-param>
11353 </xsl:call-template>
11357 <xsl:template name="isoLanguage">
11358 <xsl:param name="currentLanguage"></xsl:param>
11359 <xsl:param name="usedLanguages"></xsl:param>
11360 <xsl:param name="remainingLanguages"></xsl:param>
11362 <xsl:when test="string-length($currentLanguage)=0"></xsl:when>
11363 <xsl:when test="not(contains($usedLanguages, $currentLanguage))">
11365 <xsl:if test="@code!='a'">
11366 <xsl:attribute name="objectPart">
11368 <xsl:when test="@code='b'">summary or subtitle</xsl:when>
11369 <xsl:when test="@code='d'">sung or spoken text</xsl:when>
11370 <xsl:when test="@code='e'">libretto</xsl:when>
11371 <xsl:when test="@code='f'">table of contents</xsl:when>
11372 <xsl:when test="@code='g'">accompanying material</xsl:when>
11373 <xsl:when test="@code='h'">translation</xsl:when>
11377 <languageTerm authority="iso639-2b" type="code">
11378 <xsl:value-of select="$currentLanguage"></xsl:value-of>
11381 <xsl:call-template name="isoLanguage">
11382 <xsl:with-param name="currentLanguage">
11383 <xsl:value-of select="substring($remainingLanguages,1,3)"></xsl:value-of>
11385 <xsl:with-param name="usedLanguages">
11386 <xsl:value-of select="concat($usedLanguages,$currentLanguage)"></xsl:value-of>
11388 <xsl:with-param name="remainingLanguages">
11389 <xsl:value-of select="substring($remainingLanguages,4,string-length($remainingLanguages))"></xsl:value-of>
11391 </xsl:call-template>
11394 <xsl:call-template name="isoLanguage">
11395 <xsl:with-param name="currentLanguage">
11396 <xsl:value-of select="substring($remainingLanguages,1,3)"></xsl:value-of>
11398 <xsl:with-param name="usedLanguages">
11399 <xsl:value-of select="concat($usedLanguages,$currentLanguage)"></xsl:value-of>
11401 <xsl:with-param name="remainingLanguages">
11402 <xsl:value-of select="substring($remainingLanguages,4,string-length($remainingLanguages))"></xsl:value-of>
11404 </xsl:call-template>
11408 <xsl:template name="chopBrackets">
11409 <xsl:param name="chopString"></xsl:param>
11410 <xsl:variable name="string">
11411 <xsl:call-template name="chopPunctuation">
11412 <xsl:with-param name="chopString" select="$chopString"></xsl:with-param>
11413 </xsl:call-template>
11415 <xsl:if test="substring($string, 1,1)='['">
11416 <xsl:value-of select="substring($string,2, string-length($string)-2)"></xsl:value-of>
11418 <xsl:if test="substring($string, 1,1)!='['">
11419 <xsl:value-of select="$string"></xsl:value-of>
11422 <xsl:template name="rfcLanguages">
11423 <xsl:param name="nodeNum"></xsl:param>
11424 <xsl:param name="usedLanguages"></xsl:param>
11425 <xsl:param name="controlField008-35-37"></xsl:param>
11426 <xsl:variable name="currentLanguage" select="."></xsl:variable>
11428 <xsl:when test="not($currentLanguage)"></xsl:when>
11429 <xsl:when test="$currentLanguage!=$controlField008-35-37 and $currentLanguage!='rfc3066'">
11430 <xsl:if test="not(contains($usedLanguages,$currentLanguage))">
11432 <xsl:if test="@code!='a'">
11433 <xsl:attribute name="objectPart">
11435 <xsl:when test="@code='b'">summary or subtitle</xsl:when>
11436 <xsl:when test="@code='d'">sung or spoken text</xsl:when>
11437 <xsl:when test="@code='e'">libretto</xsl:when>
11438 <xsl:when test="@code='f'">table of contents</xsl:when>
11439 <xsl:when test="@code='g'">accompanying material</xsl:when>
11440 <xsl:when test="@code='h'">translation</xsl:when>
11444 <languageTerm authority="rfc3066" type="code">
11445 <xsl:value-of select="$currentLanguage"/>
11454 <xsl:template name="datafield">
11455 <xsl:param name="tag"/>
11456 <xsl:param name="ind1"><xsl:text> </xsl:text></xsl:param>
11457 <xsl:param name="ind2"><xsl:text> </xsl:text></xsl:param>
11458 <xsl:param name="subfields"/>
11459 <xsl:element name="marc:datafield">
11460 <xsl:attribute name="tag">
11461 <xsl:value-of select="$tag"/>
11463 <xsl:attribute name="ind1">
11464 <xsl:value-of select="$ind1"/>
11466 <xsl:attribute name="ind2">
11467 <xsl:value-of select="$ind2"/>
11469 <xsl:copy-of select="$subfields"/>
11473 <xsl:template name="subfieldSelect">
11474 <xsl:param name="codes"/>
11475 <xsl:param name="delimeter"><xsl:text> </xsl:text></xsl:param>
11476 <xsl:variable name="str">
11477 <xsl:for-each select="marc:subfield">
11478 <xsl:if test="contains($codes, @code)">
11479 <xsl:value-of select="text()"/><xsl:value-of select="$delimeter"/>
11483 <xsl:value-of select="substring($str,1,string-length($str)-string-length($delimeter))"/>
11486 <xsl:template name="buildSpaces">
11487 <xsl:param name="spaces"/>
11488 <xsl:param name="char"><xsl:text> </xsl:text></xsl:param>
11489 <xsl:if test="$spaces>0">
11490 <xsl:value-of select="$char"/>
11491 <xsl:call-template name="buildSpaces">
11492 <xsl:with-param name="spaces" select="$spaces - 1"/>
11493 <xsl:with-param name="char" select="$char"/>
11494 </xsl:call-template>
11498 <xsl:template name="chopPunctuation">
11499 <xsl:param name="chopString"/>
11500 <xsl:param name="punctuation"><xsl:text>.:,;/ </xsl:text></xsl:param>
11501 <xsl:variable name="length" select="string-length($chopString)"/>
11503 <xsl:when test="$length=0"/>
11504 <xsl:when test="contains($punctuation, substring($chopString,$length,1))">
11505 <xsl:call-template name="chopPunctuation">
11506 <xsl:with-param name="chopString" select="substring($chopString,1,$length - 1)"/>
11507 <xsl:with-param name="punctuation" select="$punctuation"/>
11508 </xsl:call-template>
11510 <xsl:when test="not($chopString)"/>
11511 <xsl:otherwise><xsl:value-of select="$chopString"/></xsl:otherwise>
11515 <xsl:template name="chopPunctuationFront">
11516 <xsl:param name="chopString"/>
11517 <xsl:variable name="length" select="string-length($chopString)"/>
11519 <xsl:when test="$length=0"/>
11520 <xsl:when test="contains('.:,;/[ ', substring($chopString,1,1))">
11521 <xsl:call-template name="chopPunctuationFront">
11522 <xsl:with-param name="chopString" select="substring($chopString,2,$length - 1)"/>
11523 </xsl:call-template>
11525 <xsl:when test="not($chopString)"/>
11526 <xsl:otherwise><xsl:value-of select="$chopString"/></xsl:otherwise>
11529 </xsl:stylesheet>$$ WHERE name = 'mods32';
11531 -- Currently, the only difference from naco_normalize is that search_normalize
11532 -- turns apostrophes into spaces, while naco_normalize collapses them.
11533 CREATE OR REPLACE FUNCTION public.search_normalize( TEXT, TEXT ) RETURNS TEXT AS $func$
11536 use Unicode::Normalize;
11539 my $str = decode_utf8(shift);
11542 # Apply NACO normalization to input string; based on
11543 # http://www.loc.gov/catdir/pcc/naco/SCA_PccNormalization_Final_revised.pdf
11545 # Note that unlike a strict reading of the NACO normalization rules,
11546 # output is returned as lowercase instead of uppercase for compatibility
11547 # with previous versions of the Evergreen naco_normalize routine.
11549 # Convert to upper-case first; even though final output will be lowercase, doing this will
11550 # ensure that the German eszett (ß) and certain ligatures (ff, fi, ffl, etc.) will be handled correctly.
11551 # If there are any bugs in Perl's implementation of upcasing, they will be passed through here.
11554 # remove non-filing strings
11555 $str =~ s/\x{0098}.*?\x{009C}//g;
11559 # additional substitutions - 3.6.
11560 $str =~ s/\x{00C6}/AE/g;
11561 $str =~ s/\x{00DE}/TH/g;
11562 $str =~ s/\x{0152}/OE/g;
11563 $str =~ tr/\x{0110}\x{00D0}\x{00D8}\x{0141}\x{2113}\x{02BB}\x{02BC}][/DDOLl/d;
11565 # transformations based on Unicode category codes
11566 $str =~ s/[\p{Cc}\p{Cf}\p{Co}\p{Cs}\p{Lm}\p{Mc}\p{Me}\p{Mn}]//g;
11568 if ($sf && $sf =~ /^a/o) {
11569 my $commapos = index($str, ',');
11570 if ($commapos > -1) {
11571 if ($commapos != length($str) - 1) {
11572 $str =~ s/,/\x07/; # preserve first comma
11577 # since we've stripped out the control characters, we can now
11578 # use a few as placeholders temporarily
11579 $str =~ tr/+&@\x{266D}\x{266F}#/\x01\x02\x03\x04\x05\x06/;
11580 $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;
11581 $str =~ tr/\x01\x02\x03\x04\x05\x06\x07/+&@\x{266D}\x{266F}#,/;
11584 $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/;
11586 # intentionally skipping step 8 of the NACO algorithm; if the string
11587 # gets normalized away, that's fine.
11589 # leading and trailing spaces
11595 $func$ LANGUAGE 'plperlu' STRICT IMMUTABLE;
11597 CREATE OR REPLACE FUNCTION public.search_normalize_keep_comma( TEXT ) RETURNS TEXT AS $func$
11598 SELECT public.search_normalize($1,'a');
11599 $func$ LANGUAGE SQL STRICT IMMUTABLE;
11601 CREATE OR REPLACE FUNCTION public.search_normalize( TEXT ) RETURNS TEXT AS $func$
11602 SELECT public.search_normalize($1,'');
11603 $func$ LANGUAGE 'sql' STRICT IMMUTABLE;
11605 INSERT INTO config.index_normalizer (name, description, func, param_count) VALUES (
11606 'Search Normalize',
11607 'Apply search normalization rules to the extracted text. A less extreme version of NACO normalization.',
11608 'search_normalize',
11612 UPDATE config.metabib_field_index_norm_map
11614 SELECT id FROM config.index_normalizer WHERE func = 'search_normalize'
11617 SELECT id FROM config.index_normalizer WHERE func = 'naco_normalize'
11622 -- This could take a long time if you have a very non-English bib database
11623 -- Run it outside of a transaction to avoid lock escalation
11624 SELECT metabib.reingest_metabib_field_entries(record)
11625 FROM metabib.full_rec
11628 AND value LIKE '%''%'
11630 -- Evergreen DB patch 0673.data.acq-cancel-reason-cleanup.sql
11633 -- check whether patch can be applied
11634 SELECT evergreen.upgrade_deps_block_check('0673', :eg_version);
11639 -- any entries with id >= 2000 were added locally.
11642 -- these cancel_reason's are actively used by the system
11643 AND id NOT IN (1, 2, 3, 1002, 1003, 1004, 1005, 1010, 1024, 1211, 1221, 1246, 1283)
11645 -- don't delete any cancel_reason's that may be in use locally
11646 AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.user_request WHERE cancel_reason IS NOT NULL)
11647 AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.purchase_order WHERE cancel_reason IS NOT NULL)
11648 AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.lineitem WHERE cancel_reason IS NOT NULL)
11649 AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.lineitem_detail WHERE cancel_reason IS NOT NULL)
11650 AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.acq_lineitem_history WHERE cancel_reason IS NOT NULL)
11651 AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.acq_purchase_order_history WHERE cancel_reason IS NOT NULL);
11654 SELECT evergreen.upgrade_deps_block_check('0674', :eg_version);
11656 ALTER TABLE config.copy_status
11657 ADD COLUMN restrict_copy_delete BOOL NOT NULL DEFAULT FALSE;
11659 UPDATE config.copy_status
11660 SET restrict_copy_delete = TRUE
11661 WHERE id IN (1,3,6,8);
11663 INSERT INTO permission.perm_list (id, code, description) VALUES (
11665 'COPY_DELETE_WARNING.override',
11666 'Allow a user to override warnings about deleting copies in problematic situations.'
11670 SELECT evergreen.upgrade_deps_block_check('0675', :eg_version);
11672 -- set expected row count to low value to avoid problem
11673 -- where use of this function by the circ tagging feature
11674 -- results in full scans of asset.call_number
11675 CREATE OR REPLACE FUNCTION action.usr_visible_circ_copies( INTEGER ) RETURNS SETOF BIGINT AS $$
11676 SELECT DISTINCT(target_copy) FROM action.usr_visible_circs($1)
11677 $$ LANGUAGE SQL ROWS 10;
11680 SELECT evergreen.upgrade_deps_block_check('0676', :eg_version);
11682 INSERT INTO config.global_flag (name, label, enabled, value) VALUES (
11683 'opac.use_autosuggest',
11684 '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)',
11689 CREATE TABLE metabib.browse_entry (
11690 id BIGSERIAL PRIMARY KEY,
11692 index_vector tsvector
11694 --Skip this, will be created differently later
11695 --CREATE INDEX metabib_browse_entry_index_vector_idx ON metabib.browse_entry USING GIST (index_vector);
11696 CREATE TRIGGER metabib_browse_entry_fti_trigger
11697 BEFORE INSERT OR UPDATE ON metabib.browse_entry
11698 FOR EACH ROW EXECUTE PROCEDURE oils_tsearch2('keyword');
11701 CREATE TABLE metabib.browse_entry_def_map (
11702 id BIGSERIAL PRIMARY KEY,
11703 entry BIGINT REFERENCES metabib.browse_entry (id),
11704 def INT REFERENCES config.metabib_field (id),
11705 source BIGINT REFERENCES biblio.record_entry (id)
11708 ALTER TABLE config.metabib_field ADD COLUMN browse_field BOOLEAN DEFAULT TRUE NOT NULL;
11709 ALTER TABLE config.metabib_field ADD COLUMN browse_xpath TEXT;
11711 ALTER TABLE config.metabib_class ADD COLUMN bouyant BOOLEAN DEFAULT FALSE NOT NULL;
11712 ALTER TABLE config.metabib_class ADD COLUMN restrict BOOLEAN DEFAULT FALSE NOT NULL;
11713 ALTER TABLE config.metabib_field ADD COLUMN restrict BOOLEAN DEFAULT FALSE NOT NULL;
11715 -- one good exception to default true:
11716 UPDATE config.metabib_field
11717 SET browse_field = FALSE
11718 WHERE (field_class = 'keyword' AND name = 'keyword') OR
11719 (field_class = 'subject' AND name = 'complete');
11721 -- AFTER UPDATE OR INSERT trigger for biblio.record_entry
11722 -- We're only touching it here to add a DELETE statement to the IF NEW.deleted
11725 CREATE OR REPLACE FUNCTION biblio.indexing_ingest_or_delete () RETURNS TRIGGER AS $func$
11727 transformed_xml TEXT;
11730 xfrm config.xml_transform%ROWTYPE;
11732 new_attrs HSTORE := ''::HSTORE;
11733 attr_def config.record_attr_definition%ROWTYPE;
11736 IF NEW.deleted IS TRUE THEN -- If this bib is deleted
11737 DELETE FROM metabib.metarecord_source_map WHERE source = NEW.id; -- Rid ourselves of the search-estimate-killing linkage
11738 DELETE FROM metabib.record_attr WHERE id = NEW.id; -- Kill the attrs hash, useless on deleted records
11739 DELETE FROM authority.bib_linking WHERE bib = NEW.id; -- Avoid updating fields in bibs that are no longer visible
11740 DELETE FROM biblio.peer_bib_copy_map WHERE peer_record = NEW.id; -- Separate any multi-homed items
11741 DELETE FROM metabib.browse_entry_def_map WHERE source = NEW.id; -- Don't auto-suggest deleted bibs
11742 RETURN NEW; -- and we're done
11745 IF TG_OP = 'UPDATE' THEN -- re-ingest?
11746 PERFORM * FROM config.internal_flag WHERE name = 'ingest.reingest.force_on_same_marc' AND enabled;
11748 IF NOT FOUND AND OLD.marc = NEW.marc THEN -- don't do anything if the MARC didn't change
11753 -- Record authority linking
11754 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_linking' AND enabled;
11756 PERFORM biblio.map_authority_linking( NEW.id, NEW.marc );
11759 -- Flatten and insert the mfr data
11760 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_metabib_full_rec' AND enabled;
11762 PERFORM metabib.reingest_metabib_full_rec(NEW.id);
11764 -- Now we pull out attribute data, which is dependent on the mfr for all but XPath-based fields
11765 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_metabib_rec_descriptor' AND enabled;
11767 FOR attr_def IN SELECT * FROM config.record_attr_definition ORDER BY format LOOP
11769 IF attr_def.tag IS NOT NULL THEN -- tag (and optional subfield list) selection
11770 SELECT ARRAY_TO_STRING(ARRAY_ACCUM(value), COALESCE(attr_def.joiner,' ')) INTO attr_value
11771 FROM (SELECT * FROM metabib.full_rec ORDER BY tag, subfield) AS x
11772 WHERE record = NEW.id
11773 AND tag LIKE attr_def.tag
11775 WHEN attr_def.sf_list IS NOT NULL
11776 THEN POSITION(subfield IN attr_def.sf_list) > 0
11783 ELSIF attr_def.fixed_field IS NOT NULL THEN -- a named fixed field, see config.marc21_ff_pos_map.fixed_field
11784 attr_value := biblio.marc21_extract_fixed_field(NEW.id, attr_def.fixed_field);
11786 ELSIF attr_def.xpath IS NOT NULL THEN -- and xpath expression
11788 SELECT INTO xfrm * FROM config.xml_transform WHERE name = attr_def.format;
11790 -- See if we can skip the XSLT ... it's expensive
11791 IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
11792 -- Can't skip the transform
11793 IF xfrm.xslt <> '---' THEN
11794 transformed_xml := oils_xslt_process(NEW.marc,xfrm.xslt);
11796 transformed_xml := NEW.marc;
11799 prev_xfrm := xfrm.name;
11802 IF xfrm.name IS NULL THEN
11803 -- just grab the marcxml (empty) transform
11804 SELECT INTO xfrm * FROM config.xml_transform WHERE xslt = '---' LIMIT 1;
11805 prev_xfrm := xfrm.name;
11808 attr_value := oils_xpath_string(attr_def.xpath, transformed_xml, COALESCE(attr_def.joiner,' '), ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]);
11810 ELSIF attr_def.phys_char_sf IS NOT NULL THEN -- a named Physical Characteristic, see config.marc21_physical_characteristic_*_map
11811 SELECT m.value INTO attr_value
11812 FROM biblio.marc21_physical_characteristics(NEW.id) v
11813 JOIN config.marc21_physical_characteristic_value_map m ON (m.id = v.value)
11814 WHERE v.subfield = attr_def.phys_char_sf
11815 LIMIT 1; -- Just in case ...
11819 -- apply index normalizers to attr_value
11821 SELECT n.func AS func,
11822 n.param_count AS param_count,
11824 FROM config.index_normalizer n
11825 JOIN config.record_attr_index_norm_map m ON (m.norm = n.id)
11826 WHERE attr = attr_def.name
11827 ORDER BY m.pos LOOP
11828 EXECUTE 'SELECT ' || normalizer.func || '(' ||
11829 COALESCE( quote_literal( attr_value ), 'NULL' ) ||
11831 WHEN normalizer.param_count > 0
11832 THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
11835 ')' INTO attr_value;
11839 -- Add the new value to the hstore
11840 new_attrs := new_attrs || hstore( attr_def.name, attr_value );
11844 IF TG_OP = 'INSERT' OR OLD.deleted THEN -- initial insert OR revivication
11845 INSERT INTO metabib.record_attr (id, attrs) VALUES (NEW.id, new_attrs);
11847 UPDATE metabib.record_attr SET attrs = new_attrs WHERE id = NEW.id;
11853 -- Gather and insert the field entry data
11854 PERFORM metabib.reingest_metabib_field_entries(NEW.id);
11856 -- Located URI magic
11857 IF TG_OP = 'INSERT' THEN
11858 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_located_uri' AND enabled;
11860 PERFORM biblio.extract_located_uris( NEW.id, NEW.marc, NEW.editor );
11863 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_located_uri' AND enabled;
11865 PERFORM biblio.extract_located_uris( NEW.id, NEW.marc, NEW.editor );
11869 -- (re)map metarecord-bib linking
11870 IF TG_OP = 'INSERT' THEN -- if not deleted and performing an insert, check for the flag
11871 PERFORM * FROM config.internal_flag WHERE name = 'ingest.metarecord_mapping.skip_on_insert' AND enabled;
11873 PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint );
11875 ELSE -- we're doing an update, and we're not deleted, remap
11876 PERFORM * FROM config.internal_flag WHERE name = 'ingest.metarecord_mapping.skip_on_update' AND enabled;
11878 PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint );
11884 $func$ LANGUAGE PLPGSQL;
11886 CREATE OR REPLACE FUNCTION metabib.browse_normalize(facet_text TEXT, mapped_field INT) RETURNS TEXT AS $$
11892 SELECT n.func AS func,
11893 n.param_count AS param_count,
11895 FROM config.index_normalizer n
11896 JOIN config.metabib_field_index_norm_map m ON (m.norm = n.id)
11897 WHERE m.field = mapped_field AND m.pos < 0
11898 ORDER BY m.pos LOOP
11900 EXECUTE 'SELECT ' || normalizer.func || '(' ||
11901 quote_literal( facet_text ) ||
11903 WHEN normalizer.param_count > 0
11904 THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
11907 ')' INTO facet_text;
11914 $$ LANGUAGE PLPGSQL;
11916 DROP FUNCTION biblio.extract_metabib_field_entry(bigint, text);
11917 DROP FUNCTION biblio.extract_metabib_field_entry(bigint);
11919 DROP TYPE metabib.field_entry_template;
11920 CREATE TYPE metabib.field_entry_template AS (
11931 CREATE OR REPLACE FUNCTION biblio.extract_metabib_field_entry ( rid BIGINT, default_joiner TEXT ) RETURNS SETOF metabib.field_entry_template AS $func$
11933 bib biblio.record_entry%ROWTYPE;
11934 idx config.metabib_field%ROWTYPE;
11935 xfrm config.xml_transform%ROWTYPE;
11937 transformed_xml TEXT;
11939 xml_node_list TEXT[];
11944 joiner TEXT := default_joiner; -- XXX will index defs supply a joiner?
11945 output_row metabib.field_entry_template%ROWTYPE;
11949 SELECT INTO bib * FROM biblio.record_entry WHERE id = rid;
11951 -- Loop over the indexing entries
11952 FOR idx IN SELECT * FROM config.metabib_field ORDER BY format LOOP
11954 SELECT INTO xfrm * from config.xml_transform WHERE name = idx.format;
11956 -- See if we can skip the XSLT ... it's expensive
11957 IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
11958 -- Can't skip the transform
11959 IF xfrm.xslt <> '---' THEN
11960 transformed_xml := oils_xslt_process(bib.marc,xfrm.xslt);
11962 transformed_xml := bib.marc;
11965 prev_xfrm := xfrm.name;
11968 xml_node_list := oils_xpath( idx.xpath, transformed_xml, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
11971 FOR xml_node IN SELECT x FROM unnest(xml_node_list) AS x LOOP
11972 CONTINUE WHEN xml_node !~ E'^\\s*<';
11974 curr_text := ARRAY_TO_STRING(
11975 oils_xpath( '//text()',
11976 REGEXP_REPLACE( -- This escapes all &s not followed by "amp;". Data ise returned from oils_xpath (above) in UTF-8, not entity encoded
11977 REGEXP_REPLACE( -- This escapes embeded <s
11979 $re$(>[^<]+)(<)([^>]+<)$re$,
11991 CONTINUE WHEN curr_text IS NULL OR curr_text = '';
11993 IF raw_text IS NOT NULL THEN
11994 raw_text := raw_text || joiner;
11997 raw_text := COALESCE(raw_text,'') || curr_text;
11999 -- autosuggest/metabib.browse_entry
12000 IF idx.browse_field THEN
12002 IF idx.browse_xpath IS NOT NULL AND idx.browse_xpath <> '' THEN
12003 browse_text := oils_xpath_string( idx.browse_xpath, xml_node, joiner, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
12005 browse_text := curr_text;
12008 output_row.field_class = idx.field_class;
12009 output_row.field = idx.id;
12010 output_row.source = rid;
12011 output_row.value = BTRIM(REGEXP_REPLACE(browse_text, E'\\s+', ' ', 'g'));
12013 output_row.browse_field = TRUE;
12014 RETURN NEXT output_row;
12015 output_row.browse_field = FALSE;
12018 -- insert raw node text for faceting
12019 IF idx.facet_field THEN
12021 IF idx.facet_xpath IS NOT NULL AND idx.facet_xpath <> '' THEN
12022 facet_text := oils_xpath_string( idx.facet_xpath, xml_node, joiner, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
12024 facet_text := curr_text;
12027 output_row.field_class = idx.field_class;
12028 output_row.field = -1 * idx.id;
12029 output_row.source = rid;
12030 output_row.value = BTRIM(REGEXP_REPLACE(facet_text, E'\\s+', ' ', 'g'));
12032 output_row.facet_field = TRUE;
12033 RETURN NEXT output_row;
12034 output_row.facet_field = FALSE;
12039 CONTINUE WHEN raw_text IS NULL OR raw_text = '';
12041 -- insert combined node text for searching
12042 IF idx.search_field THEN
12043 output_row.field_class = idx.field_class;
12044 output_row.field = idx.id;
12045 output_row.source = rid;
12046 output_row.value = BTRIM(REGEXP_REPLACE(raw_text, E'\\s+', ' ', 'g'));
12048 output_row.search_field = TRUE;
12049 RETURN NEXT output_row;
12055 $func$ LANGUAGE PLPGSQL;
12057 -- default to a space joiner
12058 CREATE OR REPLACE FUNCTION biblio.extract_metabib_field_entry ( BIGINT ) RETURNS SETOF metabib.field_entry_template AS $func$
12059 SELECT * FROM biblio.extract_metabib_field_entry($1, ' ');
12060 $func$ LANGUAGE SQL;
12063 CREATE OR REPLACE FUNCTION metabib.reingest_metabib_field_entries( bib_id BIGINT ) RETURNS VOID AS $func$
12066 ind_data metabib.field_entry_template%ROWTYPE;
12067 mbe_row metabib.browse_entry%ROWTYPE;
12070 PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled;
12072 FOR fclass IN SELECT * FROM config.metabib_class LOOP
12073 -- RAISE NOTICE 'Emptying out %', fclass.name;
12074 EXECUTE $$DELETE FROM metabib.$$ || fclass.name || $$_field_entry WHERE source = $$ || bib_id;
12076 DELETE FROM metabib.facet_entry WHERE source = bib_id;
12077 DELETE FROM metabib.browse_entry_def_map WHERE source = bib_id;
12080 FOR ind_data IN SELECT * FROM biblio.extract_metabib_field_entry( bib_id ) LOOP
12081 IF ind_data.field < 0 THEN
12082 ind_data.field = -1 * ind_data.field;
12085 IF ind_data.facet_field THEN
12086 INSERT INTO metabib.facet_entry (field, source, value)
12087 VALUES (ind_data.field, ind_data.source, ind_data.value);
12090 IF ind_data.browse_field THEN
12091 SELECT INTO mbe_row * FROM metabib.browse_entry WHERE value = ind_data.value;
12093 mbe_id := mbe_row.id;
12095 INSERT INTO metabib.browse_entry (value) VALUES
12096 (metabib.browse_normalize(ind_data.value, ind_data.field));
12097 mbe_id := CURRVAL('metabib.browse_entry_id_seq'::REGCLASS);
12100 INSERT INTO metabib.browse_entry_def_map (entry, def, source)
12101 VALUES (mbe_id, ind_data.field, ind_data.source);
12104 IF ind_data.search_field THEN
12106 INSERT INTO metabib.$$ || ind_data.field_class || $$_field_entry (field, source, value)
12108 quote_literal(ind_data.field) || $$, $$ ||
12109 quote_literal(ind_data.source) || $$, $$ ||
12110 quote_literal(ind_data.value) ||
12118 $func$ LANGUAGE PLPGSQL;
12120 -- This mimics a specific part of QueryParser, turning the first part of a
12121 -- classed search (search_class) into a set of classes and possibly fields.
12122 -- search_class might look like "author" or "title|proper" or "ti|uniform"
12123 -- or "au" or "au|corporate|personal" or anything like that, where the first
12124 -- element of the list you get by separating on the "|" character is either
12125 -- a registered class (config.metabib_class) or an alias
12126 -- (config.metabib_search_alias), and the rest of any such elements are
12127 -- fields (config.metabib_field).
12129 FUNCTION metabib.search_class_to_registered_components(search_class TEXT)
12130 RETURNS SETOF RECORD AS $func$
12132 search_parts TEXT[];
12134 search_part_count INTEGER;
12136 registered_class config.metabib_class%ROWTYPE;
12137 registered_alias config.metabib_search_alias%ROWTYPE;
12138 registered_field config.metabib_field%ROWTYPE;
12140 search_parts := REGEXP_SPLIT_TO_ARRAY(search_class, E'\\|');
12142 search_part_count := ARRAY_LENGTH(search_parts, 1);
12143 IF search_part_count = 0 THEN
12146 SELECT INTO registered_class
12147 * FROM config.metabib_class WHERE name = search_parts[1];
12149 IF search_part_count < 2 THEN -- all fields
12150 rec := (registered_class.name, NULL::INTEGER);
12154 FOR field_name IN SELECT *
12155 FROM UNNEST(search_parts[2:search_part_count]) LOOP
12156 SELECT INTO registered_field
12157 * FROM config.metabib_field
12158 WHERE name = field_name AND
12159 field_class = registered_class.name;
12161 rec := (registered_class.name, registered_field.id);
12166 -- maybe we have an alias?
12167 SELECT INTO registered_alias
12168 * FROM config.metabib_search_alias WHERE alias=search_parts[1];
12172 IF search_part_count < 2 THEN -- return w/e the alias says
12174 registered_alias.field_class, registered_alias.field
12179 FOR field_name IN SELECT *
12180 FROM UNNEST(search_parts[2:search_part_count]) LOOP
12181 SELECT INTO registered_field
12182 * FROM config.metabib_field
12183 WHERE name = field_name AND
12184 field_class = registered_alias.field_class;
12187 registered_alias.field_class,
12188 registered_field.id
12198 $func$ LANGUAGE PLPGSQL;
12202 FUNCTION metabib.suggest_browse_entries(
12203 query_text TEXT, -- 'foo' or 'foo & ba:*',ready for to_tsquery()
12204 search_class TEXT, -- 'alias' or 'class' or 'class|field..', etc
12205 headline_opts TEXT, -- markup options for ts_headline()
12206 visibility_org INTEGER,-- null if you don't want opac visibility test
12207 query_limit INTEGER,-- use in LIMIT clause of interal query
12208 normalization INTEGER -- argument to TS_RANK_CD()
12210 value TEXT, -- plain
12212 bouyant_and_class_match BOOL,
12214 field_weight INTEGER,
12217 match TEXT -- marked up
12221 opac_visibility_join TEXT;
12222 search_class_join TEXT;
12225 query := TO_TSQUERY('keyword', query_text);
12227 IF visibility_org IS NOT NULL THEN
12228 opac_visibility_join := '
12229 JOIN asset.opac_visible_copies aovc ON (
12230 aovc.record = mbedm.source AND
12231 aovc.circ_lib IN (SELECT id FROM actor.org_unit_descendants($4))
12234 opac_visibility_join := '';
12237 -- The following determines whether we only provide suggestsons matching
12238 -- the user's selected search_class, or whether we show other suggestions
12239 -- too. The reason for MIN() is that for search_classes like
12240 -- 'title|proper|uniform' you would otherwise get multiple rows. The
12241 -- implication is that if title as a class doesn't have restrict,
12242 -- nor does the proper field, but the uniform field does, you're going
12243 -- to get 'false' for your overall evaluation of 'should we restrict?'
12244 -- To invert that, change from MIN() to MAX().
12248 MIN(cmc.restrict::INT) AS restrict_class,
12249 MIN(cmf.restrict::INT) AS restrict_field
12250 FROM metabib.search_class_to_registered_components(search_class)
12251 AS _registered (field_class TEXT, field INT)
12253 config.metabib_class cmc ON (cmc.name = _registered.field_class)
12255 config.metabib_field cmf ON (cmf.id = _registered.field);
12257 -- evaluate 'should we restrict?'
12258 IF r_fields.restrict_field::BOOL OR r_fields.restrict_class::BOOL THEN
12259 search_class_join := '
12261 metabib.search_class_to_registered_components($2)
12262 AS _registered (field_class TEXT, field INT) ON (
12263 (_registered.field IS NULL AND
12264 _registered.field_class = cmf.field_class) OR
12265 (_registered.field = cmf.id)
12269 search_class_join := '
12271 metabib.search_class_to_registered_components($2)
12272 AS _registered (field_class TEXT, field INT) ON (
12273 _registered.field_class = cmc.name
12278 RETURN QUERY EXECUTE 'SELECT *, TS_HEADLINE(value, $1, $3) FROM (SELECT DISTINCT
12281 cmc.bouyant AND _registered.field_class IS NOT NULL,
12282 _registered.field = cmf.id,
12284 TS_RANK_CD(mbe.index_vector, $1, $6),
12286 FROM metabib.browse_entry_def_map mbedm
12287 JOIN metabib.browse_entry mbe ON (mbe.id = mbedm.entry)
12288 JOIN config.metabib_field cmf ON (cmf.id = mbedm.def)
12289 JOIN config.metabib_class cmc ON (cmf.field_class = cmc.name)
12290 ' || search_class_join || opac_visibility_join ||
12291 ' WHERE $1 @@ mbe.index_vector
12292 ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
12294 ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
12295 ' -- sic, repeat the order by clause in the outer select too
12297 query, search_class, headline_opts,
12298 visibility_org, query_limit, normalization
12302 -- bouyant AND chosen class = match class
12303 -- chosen field = match field
12310 $func$ LANGUAGE PLPGSQL;
12312 -- The advantage of this over the stock regexp_split_to_array() is that it
12313 -- won't degrade unicode strings.
12314 CREATE OR REPLACE FUNCTION evergreen.regexp_split_to_array(TEXT, TEXT)
12315 RETURNS TEXT[] AS $$
12316 return encode_array_literal([split $_[1], $_[0]]);
12317 $$ LANGUAGE PLPERLU STRICT IMMUTABLE;
12320 -- Adds some logic for browse_entry to split on non-word chars for index_vector, post-normalize
12321 CREATE OR REPLACE FUNCTION oils_tsearch2 () RETURNS TRIGGER AS $$
12327 value := NEW.value;
12329 IF TG_TABLE_NAME::TEXT ~ 'field_entry$' THEN
12331 SELECT n.func AS func,
12332 n.param_count AS param_count,
12334 FROM config.index_normalizer n
12335 JOIN config.metabib_field_index_norm_map m ON (m.norm = n.id)
12336 WHERE field = NEW.field AND m.pos < 0
12337 ORDER BY m.pos LOOP
12338 EXECUTE 'SELECT ' || normalizer.func || '(' ||
12339 quote_literal( value ) ||
12341 WHEN normalizer.param_count > 0
12342 THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
12349 NEW.value := value;
12352 IF NEW.index_vector = ''::tsvector THEN
12356 IF TG_TABLE_NAME::TEXT ~ 'field_entry$' THEN
12358 SELECT n.func AS func,
12359 n.param_count AS param_count,
12361 FROM config.index_normalizer n
12362 JOIN config.metabib_field_index_norm_map m ON (m.norm = n.id)
12363 WHERE field = NEW.field AND m.pos >= 0
12364 ORDER BY m.pos LOOP
12365 EXECUTE 'SELECT ' || normalizer.func || '(' ||
12366 quote_literal( value ) ||
12368 WHEN normalizer.param_count > 0
12369 THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
12377 IF TG_TABLE_NAME::TEXT ~ 'browse_entry$' THEN
12378 value := ARRAY_TO_STRING(
12379 evergreen.regexp_split_to_array(value, E'\\W+'), ' '
12383 NEW.index_vector = to_tsvector((TG_ARGV[0])::regconfig, value);
12387 $$ LANGUAGE PLPGSQL;
12389 -- Evergreen DB patch 0677.schema.circ_limits.sql
12391 -- FIXME: insert description of change, if needed
12395 -- check whether patch can be applied
12396 SELECT evergreen.upgrade_deps_block_check('0677', :eg_version);
12398 -- FIXME: add/check SQL statements to perform the upgrade
12399 -- Limit groups for circ counting
12400 CREATE TABLE config.circ_limit_group (
12401 id SERIAL PRIMARY KEY,
12402 name TEXT UNIQUE NOT NULL,
12407 CREATE TABLE config.circ_limit_set (
12408 id SERIAL PRIMARY KEY,
12409 name TEXT UNIQUE NOT NULL,
12410 owning_lib INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
12411 items_out INT NOT NULL, -- Total current active circulations must be less than this. 0 means skip counting (always pass)
12412 depth INT NOT NULL DEFAULT 0, -- Depth count starts at
12413 global BOOL NOT NULL DEFAULT FALSE, -- If enabled, include everything below depth, otherwise ancestors/descendants only
12417 -- Linkage between matchpoints and limit sets
12418 CREATE TABLE config.circ_matrix_limit_set_map (
12419 id SERIAL PRIMARY KEY,
12420 matchpoint INT NOT NULL REFERENCES config.circ_matrix_matchpoint (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
12421 limit_set INT NOT NULL REFERENCES config.circ_limit_set (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
12422 fallthrough BOOL NOT NULL DEFAULT FALSE, -- If true fallthrough will grab this rule as it goes along
12423 active BOOL NOT NULL DEFAULT TRUE,
12424 CONSTRAINT circ_limit_set_once_per_matchpoint UNIQUE (matchpoint, limit_set)
12427 -- Linkage between limit sets and circ mods
12428 CREATE TABLE config.circ_limit_set_circ_mod_map (
12429 id SERIAL PRIMARY KEY,
12430 limit_set INT NOT NULL REFERENCES config.circ_limit_set (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
12431 circ_mod TEXT NOT NULL REFERENCES config.circ_modifier (code) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
12432 CONSTRAINT cm_once_per_set UNIQUE (limit_set, circ_mod)
12435 -- Linkage between limit sets and limit groups
12436 CREATE TABLE config.circ_limit_set_group_map (
12437 id SERIAL PRIMARY KEY,
12438 limit_set INT NOT NULL REFERENCES config.circ_limit_set (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
12439 limit_group INT NOT NULL REFERENCES config.circ_limit_group (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
12440 check_only BOOL NOT NULL DEFAULT FALSE, -- If true, don't accumulate this limit_group for storing with the circulation
12441 CONSTRAINT clg_once_per_set UNIQUE (limit_set, limit_group)
12444 -- Linkage between limit groups and circulations
12445 CREATE TABLE action.circulation_limit_group_map (
12446 circ BIGINT NOT NULL REFERENCES action.circulation (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
12447 limit_group INT NOT NULL REFERENCES config.circ_limit_group (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
12448 PRIMARY KEY (circ, limit_group)
12451 -- Function for populating the circ/limit group mappings
12452 CREATE OR REPLACE FUNCTION action.link_circ_limit_groups ( BIGINT, INT[] ) RETURNS VOID AS $func$
12453 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));
12454 $func$ LANGUAGE SQL;
12456 DROP TYPE IF EXISTS action.circ_matrix_test_result CASCADE;
12457 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[] );
12459 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$
12461 user_object actor.usr%ROWTYPE;
12462 standing_penalty config.standing_penalty%ROWTYPE;
12463 item_object asset.copy%ROWTYPE;
12464 item_status_object config.copy_status%ROWTYPE;
12465 item_location_object asset.copy_location%ROWTYPE;
12466 result action.circ_matrix_test_result;
12467 circ_test action.found_circ_matrix_matchpoint;
12468 circ_matchpoint config.circ_matrix_matchpoint%ROWTYPE;
12469 circ_limit_set config.circ_limit_set%ROWTYPE;
12470 hold_ratio action.hold_stats%ROWTYPE;
12473 context_org_list INT[];
12474 done BOOL := FALSE;
12476 -- Assume success unless we hit a failure condition
12477 result.success := TRUE;
12479 -- Need user info to look up matchpoints
12480 SELECT INTO user_object * FROM actor.usr WHERE id = match_user AND NOT deleted;
12482 -- (Insta)Fail if we couldn't find the user
12483 IF user_object.id IS NULL THEN
12484 result.fail_part := 'no_user';
12485 result.success := FALSE;
12487 RETURN NEXT result;
12491 -- Need item info to look up matchpoints
12492 SELECT INTO item_object * FROM asset.copy WHERE id = match_item AND NOT deleted;
12494 -- (Insta)Fail if we couldn't find the item
12495 IF item_object.id IS NULL THEN
12496 result.fail_part := 'no_item';
12497 result.success := FALSE;
12499 RETURN NEXT result;
12503 SELECT INTO circ_test * FROM action.find_circ_matrix_matchpoint(circ_ou, item_object, user_object, renewal);
12505 circ_matchpoint := circ_test.matchpoint;
12506 result.matchpoint := circ_matchpoint.id;
12507 result.circulate := circ_matchpoint.circulate;
12508 result.duration_rule := circ_matchpoint.duration_rule;
12509 result.recurring_fine_rule := circ_matchpoint.recurring_fine_rule;
12510 result.max_fine_rule := circ_matchpoint.max_fine_rule;
12511 result.hard_due_date := circ_matchpoint.hard_due_date;
12512 result.renewals := circ_matchpoint.renewals;
12513 result.grace_period := circ_matchpoint.grace_period;
12514 result.buildrows := circ_test.buildrows;
12516 -- (Insta)Fail if we couldn't find a matchpoint
12517 IF circ_test.success = false THEN
12518 result.fail_part := 'no_matchpoint';
12519 result.success := FALSE;
12521 RETURN NEXT result;
12525 -- All failures before this point are non-recoverable
12526 -- Below this point are possibly overridable failures
12528 -- Fail if the user is barred
12529 IF user_object.barred IS TRUE THEN
12530 result.fail_part := 'actor.usr.barred';
12531 result.success := FALSE;
12533 RETURN NEXT result;
12536 -- Fail if the item can't circulate
12537 IF item_object.circulate IS FALSE THEN
12538 result.fail_part := 'asset.copy.circulate';
12539 result.success := FALSE;
12541 RETURN NEXT result;
12544 -- Fail if the item isn't in a circulateable status on a non-renewal
12545 IF NOT renewal AND item_object.status NOT IN ( 0, 7, 8 ) THEN
12546 result.fail_part := 'asset.copy.status';
12547 result.success := FALSE;
12549 RETURN NEXT result;
12550 -- Alternately, fail if the item isn't checked out on a renewal
12551 ELSIF renewal AND item_object.status <> 1 THEN
12552 result.fail_part := 'asset.copy.status';
12553 result.success := FALSE;
12555 RETURN NEXT result;
12558 -- Fail if the item can't circulate because of the shelving location
12559 SELECT INTO item_location_object * FROM asset.copy_location WHERE id = item_object.location;
12560 IF item_location_object.circulate IS FALSE THEN
12561 result.fail_part := 'asset.copy_location.circulate';
12562 result.success := FALSE;
12564 RETURN NEXT result;
12567 -- Use Circ OU for penalties and such
12568 SELECT INTO context_org_list ARRAY_AGG(id) FROM actor.org_unit_full_path( circ_ou );
12571 penalty_type = '%RENEW%';
12573 penalty_type = '%CIRC%';
12576 FOR standing_penalty IN
12577 SELECT DISTINCT csp.*
12578 FROM actor.usr_standing_penalty usp
12579 JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
12580 WHERE usr = match_user
12581 AND usp.org_unit IN ( SELECT * FROM unnest(context_org_list) )
12582 AND (usp.stop_date IS NULL or usp.stop_date > NOW())
12583 AND csp.block_list LIKE penalty_type LOOP
12585 result.fail_part := standing_penalty.name;
12586 result.success := FALSE;
12588 RETURN NEXT result;
12591 -- Fail if the test is set to hard non-circulating
12592 IF circ_matchpoint.circulate IS FALSE THEN
12593 result.fail_part := 'config.circ_matrix_test.circulate';
12594 result.success := FALSE;
12596 RETURN NEXT result;
12599 -- Fail if the total copy-hold ratio is too low
12600 IF circ_matchpoint.total_copy_hold_ratio IS NOT NULL THEN
12601 SELECT INTO hold_ratio * FROM action.copy_related_hold_stats(match_item);
12602 IF hold_ratio.total_copy_ratio IS NOT NULL AND hold_ratio.total_copy_ratio < circ_matchpoint.total_copy_hold_ratio THEN
12603 result.fail_part := 'config.circ_matrix_test.total_copy_hold_ratio';
12604 result.success := FALSE;
12606 RETURN NEXT result;
12610 -- Fail if the available copy-hold ratio is too low
12611 IF circ_matchpoint.available_copy_hold_ratio IS NOT NULL THEN
12612 IF hold_ratio.hold_count IS NULL THEN
12613 SELECT INTO hold_ratio * FROM action.copy_related_hold_stats(match_item);
12615 IF hold_ratio.available_copy_ratio IS NOT NULL AND hold_ratio.available_copy_ratio < circ_matchpoint.available_copy_hold_ratio THEN
12616 result.fail_part := 'config.circ_matrix_test.available_copy_hold_ratio';
12617 result.success := FALSE;
12619 RETURN NEXT result;
12623 -- Fail if the user has too many items out by defined limit sets
12624 FOR circ_limit_set IN SELECT ccls.* FROM config.circ_limit_set ccls
12625 JOIN config.circ_matrix_limit_set_map ccmlsm ON ccmlsm.limit_set = ccls.id
12626 WHERE ccmlsm.active AND ( ccmlsm.matchpoint = circ_matchpoint.id OR
12627 ( ccmlsm.matchpoint IN (SELECT * FROM unnest(result.buildrows)) AND ccmlsm.fallthrough )
12629 IF circ_limit_set.items_out > 0 AND NOT renewal THEN
12630 SELECT INTO context_org_list ARRAY_AGG(aou.id)
12631 FROM actor.org_unit_full_path( circ_ou ) aou
12632 JOIN actor.org_unit_type aout ON aou.ou_type = aout.id
12633 WHERE aout.depth >= circ_limit_set.depth;
12634 IF circ_limit_set.global THEN
12635 WITH RECURSIVE descendant_depth AS (
12638 FROM actor.org_unit ou
12639 WHERE ou.id IN (SELECT * FROM unnest(context_org_list))
12643 FROM actor.org_unit ou
12644 JOIN descendant_depth ot ON (ot.id = ou.parent_ou)
12645 ) SELECT INTO context_org_list ARRAY_AGG(ou.id) FROM actor.org_unit ou JOIN descendant_depth USING (id);
12647 SELECT INTO items_out COUNT(DISTINCT circ.id)
12648 FROM action.circulation circ
12649 JOIN asset.copy copy ON (copy.id = circ.target_copy)
12650 LEFT JOIN action.circulation_limit_group_map aclgm ON (circ.id = aclgm.circ)
12651 WHERE circ.usr = match_user
12652 AND circ.circ_lib IN (SELECT * FROM unnest(context_org_list))
12653 AND circ.checkin_time IS NULL
12654 AND (circ.stop_fines IN ('MAXFINES','LONGOVERDUE') OR circ.stop_fines IS NULL)
12655 AND (copy.circ_modifier IN (SELECT circ_mod FROM config.circ_limit_set_circ_mod_map WHERE limit_set = circ_limit_set.id)
12656 OR aclgm.limit_group IN (SELECT limit_group FROM config.circ_limit_set_group_map WHERE limit_set = circ_limit_set.id)
12658 IF items_out >= circ_limit_set.items_out THEN
12659 result.fail_part := 'config.circ_matrix_circ_mod_test';
12660 result.success := FALSE;
12662 RETURN NEXT result;
12665 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;
12668 -- If we passed everything, return the successful matchpoint
12670 RETURN NEXT result;
12675 $func$ LANGUAGE plpgsql;
12677 -- We need to re-create these, as they got dropped with the type above.
12678 CREATE OR REPLACE FUNCTION action.item_user_circ_test( INT, BIGINT, INT ) RETURNS SETOF action.circ_matrix_test_result AS $func$
12679 SELECT * FROM action.item_user_circ_test( $1, $2, $3, FALSE );
12680 $func$ LANGUAGE SQL;
12682 CREATE OR REPLACE FUNCTION action.item_user_renew_test( INT, BIGINT, INT ) RETURNS SETOF action.circ_matrix_test_result AS $func$
12683 SELECT * FROM action.item_user_circ_test( $1, $2, $3, TRUE );
12684 $func$ LANGUAGE SQL;
12686 -- Temp function for migrating circ mod limits.
12687 CREATE OR REPLACE FUNCTION evergreen.temp_migrate_circ_mod_limits() RETURNS VOID AS $func$
12689 circ_mod_group config.circ_matrix_circ_mod_test%ROWTYPE;
12691 circ_mod_count INT;
12693 FOR circ_mod_group IN SELECT * FROM config.circ_matrix_circ_mod_test LOOP
12694 INSERT INTO config.circ_limit_set(name, owning_lib, items_out, depth, global, description)
12695 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'
12696 FROM config.circ_matrix_matchpoint WHERE id = circ_mod_group.matchpoint
12697 RETURNING id INTO current_set;
12698 INSERT INTO config.circ_matrix_limit_set_map(matchpoint, limit_set, fallthrough, active) VALUES (circ_mod_group.matchpoint, current_set, false, true);
12699 INSERT INTO config.circ_limit_set_circ_mod_map(limit_set, circ_mod)
12700 SELECT current_set, circ_mod FROM config.circ_matrix_circ_mod_test_map WHERE circ_mod_test = circ_mod_group.id;
12701 SELECT INTO circ_mod_count count(id) FROM config.circ_limit_set_circ_mod_map WHERE limit_set = current_set;
12702 RAISE NOTICE 'Created limit set with id % and % circ modifiers attached to matchpoint %', current_set, circ_mod_count, circ_mod_group.matchpoint;
12705 $func$ LANGUAGE plpgsql;
12707 -- Run the temp function
12708 SELECT * FROM evergreen.temp_migrate_circ_mod_limits();
12710 -- Drop the temp function
12711 DROP FUNCTION evergreen.temp_migrate_circ_mod_limits();
12713 --Drop the old tables
12714 --Not sure we want to do this. Keeping them may help "something went wrong" correction.
12715 --DROP TABLE IF EXISTS config.circ_matrix_circ_mod_test_map, config.circ_matrix_circ_mod_test;
12718 -- Evergreen DB patch 0678.data.vandelay-default-merge-profiles.sql
12720 -- check whether patch can be applied
12721 SELECT evergreen.upgrade_deps_block_check('0678', :eg_version);
12723 INSERT INTO vandelay.merge_profile (owner, name, replace_spec)
12724 VALUES (1, 'Match-Only Merge', '901c');
12726 INSERT INTO vandelay.merge_profile (owner, name, preserve_spec)
12727 VALUES (1, 'Full Overlay', '901c');
12729 SELECT evergreen.upgrade_deps_block_check('0679', :eg_version);
12731 -- Address typo in column name
12732 ALTER TABLE config.metabib_class ADD COLUMN buoyant BOOL DEFAULT FALSE NOT NULL;
12733 UPDATE config.metabib_class SET buoyant = bouyant;
12734 ALTER TABLE config.metabib_class DROP COLUMN bouyant;
12736 CREATE OR REPLACE FUNCTION oils_tsearch2 () RETURNS TRIGGER AS $$
12742 value := NEW.value;
12744 IF TG_TABLE_NAME::TEXT ~ 'field_entry$' THEN
12746 SELECT n.func AS func,
12747 n.param_count AS param_count,
12749 FROM config.index_normalizer n
12750 JOIN config.metabib_field_index_norm_map m ON (m.norm = n.id)
12751 WHERE field = NEW.field AND m.pos < 0
12752 ORDER BY m.pos LOOP
12753 EXECUTE 'SELECT ' || normalizer.func || '(' ||
12754 quote_literal( value ) ||
12756 WHEN normalizer.param_count > 0
12757 THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
12764 NEW.value := value;
12767 IF NEW.index_vector = ''::tsvector THEN
12771 IF TG_TABLE_NAME::TEXT ~ 'field_entry$' THEN
12773 SELECT n.func AS func,
12774 n.param_count AS param_count,
12776 FROM config.index_normalizer n
12777 JOIN config.metabib_field_index_norm_map m ON (m.norm = n.id)
12778 WHERE field = NEW.field AND m.pos >= 0
12779 ORDER BY m.pos LOOP
12780 EXECUTE 'SELECT ' || normalizer.func || '(' ||
12781 quote_literal( value ) ||
12783 WHEN normalizer.param_count > 0
12784 THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
12792 IF TG_TABLE_NAME::TEXT ~ 'browse_entry$' THEN
12793 value := ARRAY_TO_STRING(
12794 evergreen.regexp_split_to_array(value, E'\\W+'), ' '
12796 value := public.search_normalize(value);
12799 NEW.index_vector = to_tsvector((TG_ARGV[0])::regconfig, value);
12803 $$ LANGUAGE PLPGSQL;
12805 -- Given a string such as a user might type into a search box, prepare
12806 -- two changed variants for TO_TSQUERY(). See
12807 -- http://www.postgresql.org/docs/9.0/static/textsearch-controls.html
12808 -- The first variant is normalized to match indexed documents regardless
12809 -- of diacritics. The second variant keeps its diacritics for proper
12810 -- highlighting via TS_HEADLINE().
12812 FUNCTION metabib.autosuggest_prepare_tsquery(orig TEXT) RETURNS TEXT[] AS
12815 orig_ended_in_space BOOLEAN;
12820 orig_ended_in_space := orig ~ E'\\s$';
12822 orig := ARRAY_TO_STRING(
12823 evergreen.regexp_split_to_array(orig, E'\\W+'), ' '
12826 normalized := public.search_normalize(orig); -- also trim()s
12827 plain := trim(orig);
12829 IF NOT orig_ended_in_space THEN
12830 plain := plain || ':*';
12831 normalized := normalized || ':*';
12834 plain := ARRAY_TO_STRING(
12835 evergreen.regexp_split_to_array(plain, E'\\s+'), ' & '
12837 normalized := ARRAY_TO_STRING(
12838 evergreen.regexp_split_to_array(normalized, E'\\s+'), ' & '
12841 RETURN ARRAY[normalized, plain];
12843 $$ LANGUAGE PLPGSQL;
12846 -- Definition of OUT parameters changes, so must drop first
12847 DROP FUNCTION IF EXISTS metabib.suggest_browse_entries (TEXT, TEXT, TEXT, INTEGER, INTEGER, INTEGER);
12850 FUNCTION metabib.suggest_browse_entries(
12851 raw_query_text TEXT, -- actually typed by humans at the UI level
12852 search_class TEXT, -- 'alias' or 'class' or 'class|field..', etc
12853 headline_opts TEXT, -- markup options for ts_headline()
12854 visibility_org INTEGER,-- null if you don't want opac visibility test
12855 query_limit INTEGER,-- use in LIMIT clause of interal query
12856 normalization INTEGER -- argument to TS_RANK_CD()
12858 value TEXT, -- plain
12860 buoyant_and_class_match BOOL,
12862 field_weight INTEGER,
12865 match TEXT -- marked up
12868 prepared_query_texts TEXT[];
12870 plain_query TSQUERY;
12871 opac_visibility_join TEXT;
12872 search_class_join TEXT;
12875 prepared_query_texts := metabib.autosuggest_prepare_tsquery(raw_query_text);
12877 query := TO_TSQUERY('keyword', prepared_query_texts[1]);
12878 plain_query := TO_TSQUERY('keyword', prepared_query_texts[2]);
12880 IF visibility_org IS NOT NULL THEN
12881 opac_visibility_join := '
12882 JOIN asset.opac_visible_copies aovc ON (
12883 aovc.record = mbedm.source AND
12884 aovc.circ_lib IN (SELECT id FROM actor.org_unit_descendants($4))
12887 opac_visibility_join := '';
12890 -- The following determines whether we only provide suggestsons matching
12891 -- the user's selected search_class, or whether we show other suggestions
12892 -- too. The reason for MIN() is that for search_classes like
12893 -- 'title|proper|uniform' you would otherwise get multiple rows. The
12894 -- implication is that if title as a class doesn't have restrict,
12895 -- nor does the proper field, but the uniform field does, you're going
12896 -- to get 'false' for your overall evaluation of 'should we restrict?'
12897 -- To invert that, change from MIN() to MAX().
12901 MIN(cmc.restrict::INT) AS restrict_class,
12902 MIN(cmf.restrict::INT) AS restrict_field
12903 FROM metabib.search_class_to_registered_components(search_class)
12904 AS _registered (field_class TEXT, field INT)
12906 config.metabib_class cmc ON (cmc.name = _registered.field_class)
12908 config.metabib_field cmf ON (cmf.id = _registered.field);
12910 -- evaluate 'should we restrict?'
12911 IF r_fields.restrict_field::BOOL OR r_fields.restrict_class::BOOL THEN
12912 search_class_join := '
12914 metabib.search_class_to_registered_components($2)
12915 AS _registered (field_class TEXT, field INT) ON (
12916 (_registered.field IS NULL AND
12917 _registered.field_class = cmf.field_class) OR
12918 (_registered.field = cmf.id)
12922 search_class_join := '
12924 metabib.search_class_to_registered_components($2)
12925 AS _registered (field_class TEXT, field INT) ON (
12926 _registered.field_class = cmc.name
12931 RETURN QUERY EXECUTE 'SELECT *, TS_HEADLINE(value, $7, $3) FROM (SELECT DISTINCT
12934 cmc.buoyant AND _registered.field_class IS NOT NULL,
12935 _registered.field = cmf.id,
12937 TS_RANK_CD(mbe.index_vector, $1, $6),
12939 FROM metabib.browse_entry_def_map mbedm
12940 JOIN metabib.browse_entry mbe ON (mbe.id = mbedm.entry)
12941 JOIN config.metabib_field cmf ON (cmf.id = mbedm.def)
12942 JOIN config.metabib_class cmc ON (cmf.field_class = cmc.name)
12943 ' || search_class_join || opac_visibility_join ||
12944 ' WHERE $1 @@ mbe.index_vector
12945 ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
12947 ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
12948 ' -- sic, repeat the order by clause in the outer select too
12950 query, search_class, headline_opts,
12951 visibility_org, query_limit, normalization, plain_query
12955 -- buoyant AND chosen class = match class
12956 -- chosen field = match field
12963 $func$ LANGUAGE PLPGSQL;
12967 \qecho The following takes about a minute per 100,000 rows in
12968 \qecho metabib.browse_entry on my development system, which is only a VM with
12969 \qecho 4 GB of memory and 2 cores.
12971 \qecho The following is a very loose estimate of how long the next UPDATE
12972 \qecho statement would take to finish on MY machine, based on YOUR number
12973 \qecho of rows in metabib.browse_entry:
12976 SELECT (COUNT(id) / 100000.0) * INTERVAL '1 minute'
12977 AS "approximate duration of following UPDATE statement"
12978 FROM metabib.browse_entry;
12980 UPDATE metabib.browse_entry SET index_vector = TO_TSVECTOR(
12982 public.search_normalize(
12984 evergreen.regexp_split_to_array(value, E'\\W+'), ' '
12990 SELECT evergreen.upgrade_deps_block_check('0680', :eg_version);
12992 -- Not much use in having identifier-class fields be suggestions. Credit for the idea goes to Ben Shum.
12993 UPDATE config.metabib_field SET browse_field = FALSE WHERE id < 100 AND field_class = 'identifier';
12996 ---------------------------------------------------------------------------
12997 -- The rest of this was tested on Evergreen Indiana's dev server, which has
12998 -- a large data set of 2.6M bibs, and was instrumental in sussing out the
12999 -- needed adjustments. Thanks, EG-IN!
13000 ---------------------------------------------------------------------------
13002 -- GIN indexes are /much/ better for prefix matching, which is important for browse and autosuggest
13003 --Commented out the creation earlier, so we don't need to drop it here.
13004 --DROP INDEX metabib.metabib_browse_entry_index_vector_idx;
13005 CREATE INDEX metabib_browse_entry_index_vector_idx ON metabib.browse_entry USING GIN (index_vector);
13008 -- We need thes to make the autosuggest limiting joins fast
13009 CREATE INDEX browse_entry_def_map_def_idx ON metabib.browse_entry_def_map (def);
13010 CREATE INDEX browse_entry_def_map_entry_idx ON metabib.browse_entry_def_map (entry);
13011 CREATE INDEX browse_entry_def_map_source_idx ON metabib.browse_entry_def_map (source);
13013 -- In practice this will always be ~1 row, and the default of 1000 causes terrible plans
13014 ALTER FUNCTION metabib.search_class_to_registered_components(text) ROWS 1;
13016 -- Reworking of the generated query to act in a sane manner in the face of large datasets
13018 FUNCTION metabib.suggest_browse_entries(
13019 raw_query_text TEXT, -- actually typed by humans at the UI level
13020 search_class TEXT, -- 'alias' or 'class' or 'class|field..', etc
13021 headline_opts TEXT, -- markup options for ts_headline()
13022 visibility_org INTEGER,-- null if you don't want opac visibility test
13023 query_limit INTEGER,-- use in LIMIT clause of interal query
13024 normalization INTEGER -- argument to TS_RANK_CD()
13026 value TEXT, -- plain
13028 buoyant_and_class_match BOOL,
13030 field_weight INTEGER,
13033 match TEXT -- marked up
13036 prepared_query_texts TEXT[];
13038 plain_query TSQUERY;
13039 opac_visibility_join TEXT;
13040 search_class_join TEXT;
13043 prepared_query_texts := metabib.autosuggest_prepare_tsquery(raw_query_text);
13045 query := TO_TSQUERY('keyword', prepared_query_texts[1]);
13046 plain_query := TO_TSQUERY('keyword', prepared_query_texts[2]);
13048 IF visibility_org IS NOT NULL THEN
13049 opac_visibility_join := '
13050 JOIN asset.opac_visible_copies aovc ON (
13051 aovc.record = x.source AND
13052 aovc.circ_lib IN (SELECT id FROM actor.org_unit_descendants($4))
13055 opac_visibility_join := '';
13058 -- The following determines whether we only provide suggestsons matching
13059 -- the user's selected search_class, or whether we show other suggestions
13060 -- too. The reason for MIN() is that for search_classes like
13061 -- 'title|proper|uniform' you would otherwise get multiple rows. The
13062 -- implication is that if title as a class doesn't have restrict,
13063 -- nor does the proper field, but the uniform field does, you're going
13064 -- to get 'false' for your overall evaluation of 'should we restrict?'
13065 -- To invert that, change from MIN() to MAX().
13069 MIN(cmc.restrict::INT) AS restrict_class,
13070 MIN(cmf.restrict::INT) AS restrict_field
13071 FROM metabib.search_class_to_registered_components(search_class)
13072 AS _registered (field_class TEXT, field INT)
13074 config.metabib_class cmc ON (cmc.name = _registered.field_class)
13076 config.metabib_field cmf ON (cmf.id = _registered.field);
13078 -- evaluate 'should we restrict?'
13079 IF r_fields.restrict_field::BOOL OR r_fields.restrict_class::BOOL THEN
13080 search_class_join := '
13082 metabib.search_class_to_registered_components($2)
13083 AS _registered (field_class TEXT, field INT) ON (
13084 (_registered.field IS NULL AND
13085 _registered.field_class = cmf.field_class) OR
13086 (_registered.field = cmf.id)
13090 search_class_join := '
13092 metabib.search_class_to_registered_components($2)
13093 AS _registered (field_class TEXT, field INT) ON (
13094 _registered.field_class = cmc.name
13099 RETURN QUERY EXECUTE '
13108 TS_HEADLINE(value, $7, $3)
13109 FROM (SELECT DISTINCT
13112 cmc.buoyant AND _registered.field_class IS NOT NULL AS push,
13113 _registered.field = cmf.id AS restrict,
13115 TS_RANK_CD(mbe.index_vector, $1, $6),
13118 FROM metabib.browse_entry_def_map mbedm
13120 -- Start with a pre-limited set of 10k possible suggestions. More than that is not going to be useful anyway
13121 JOIN (SELECT * FROM metabib.browse_entry WHERE index_vector @@ $1 LIMIT 10000) mbe ON (mbe.id = mbedm.entry)
13123 JOIN config.metabib_field cmf ON (cmf.id = mbedm.def)
13124 JOIN config.metabib_class cmc ON (cmf.field_class = cmc.name)
13125 ' || search_class_join || '
13126 ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
13127 LIMIT 1000) AS x -- This outer limit makes testing for opac visibility usably fast
13128 ' || opac_visibility_join || '
13129 ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
13131 ' -- sic, repeat the order by clause in the outer select too
13133 query, search_class, headline_opts,
13134 visibility_org, query_limit, normalization, plain_query
13138 -- buoyant AND chosen class = match class
13139 -- chosen field = match field
13146 $func$ LANGUAGE PLPGSQL;
13149 -- Evergreen DB patch 0681.schema.user-activity.sql
13152 -- check whether patch can be applied
13153 SELECT evergreen.upgrade_deps_block_check('0681', :eg_version);
13157 CREATE TYPE config.usr_activity_group AS ENUM ('authen','authz','circ','hold','search');
13159 CREATE TABLE config.usr_activity_type (
13160 id SERIAL PRIMARY KEY,
13164 label TEXT NOT NULL, -- i18n
13165 egroup config.usr_activity_group NOT NULL,
13166 enabled BOOL NOT NULL DEFAULT TRUE,
13167 transient BOOL NOT NULL DEFAULT FALSE,
13168 CONSTRAINT one_of_wwh CHECK (COALESCE(ewho,ewhat,ehow) IS NOT NULL)
13171 CREATE UNIQUE INDEX unique_wwh ON config.usr_activity_type
13172 (COALESCE(ewho,''), COALESCE (ewhat,''), COALESCE(ehow,''));
13174 CREATE TABLE actor.usr_activity (
13175 id BIGSERIAL PRIMARY KEY,
13176 usr INT REFERENCES actor.usr (id) ON DELETE SET NULL,
13177 etype INT NOT NULL REFERENCES config.usr_activity_type (id),
13178 event_time TIMESTAMPTZ NOT NULL DEFAULT NOW()
13181 -- remove transient activity entries on insert of new entries
13182 CREATE OR REPLACE FUNCTION actor.usr_activity_transient_trg () RETURNS TRIGGER AS $$
13184 DELETE FROM actor.usr_activity act USING config.usr_activity_type atype
13185 WHERE atype.transient AND
13186 NEW.etype = atype.id AND
13187 act.etype = atype.id AND
13191 $$ LANGUAGE PLPGSQL;
13193 CREATE TRIGGER remove_transient_usr_activity
13194 BEFORE INSERT ON actor.usr_activity
13195 FOR EACH ROW EXECUTE PROCEDURE actor.usr_activity_transient_trg();
13197 -- given a set of activity criteria, find the most approprate activity type
13198 CREATE OR REPLACE FUNCTION actor.usr_activity_get_type (
13202 ) RETURNS SETOF config.usr_activity_type AS $$
13203 SELECT * FROM config.usr_activity_type
13206 (ewho IS NULL OR ewho = $1) AND
13207 (ewhat IS NULL OR ewhat = $2) AND
13208 (ehow IS NULL OR ehow = $3)
13210 -- BOOL comparisons sort false to true
13211 COALESCE(ewho, '') != COALESCE($1, ''),
13212 COALESCE(ewhat,'') != COALESCE($2, ''),
13213 COALESCE(ehow, '') != COALESCE($3, '')
13217 -- given a set of activity criteria, finds the best
13218 -- activity type and inserts the activity entry
13219 CREATE OR REPLACE FUNCTION actor.insert_usr_activity (
13224 ) RETURNS SETOF actor.usr_activity AS $$
13226 new_row actor.usr_activity%ROWTYPE;
13228 SELECT id INTO new_row.etype FROM actor.usr_activity_get_type(ewho, ewhat, ehow);
13230 new_row.usr := usr;
13231 INSERT INTO actor.usr_activity (usr, etype)
13232 VALUES (usr, new_row.etype)
13233 RETURNING * INTO new_row;
13234 RETURN NEXT new_row;
13237 $$ LANGUAGE plpgsql;
13241 INSERT INTO config.usr_activity_type (id, ewho, ewhat, ehow, egroup, label) VALUES
13243 -- authen/authz actions
13244 -- note: "opensrf" is the default ingress/ehow
13245 (1, NULL, 'login', 'opensrf', 'authen', oils_i18n_gettext(1 , 'Login via opensrf', 'cuat', 'label'))
13246 ,(2, NULL, 'login', 'srfsh', 'authen', oils_i18n_gettext(2 , 'Login via srfsh', 'cuat', 'label'))
13247 ,(3, NULL, 'login', 'gateway-v1', 'authen', oils_i18n_gettext(3 , 'Login via gateway-v1', 'cuat', 'label'))
13248 ,(4, NULL, 'login', 'translator-v1','authen', oils_i18n_gettext(4 , 'Login via translator-v1', 'cuat', 'label'))
13249 ,(5, NULL, 'login', 'xmlrpc', 'authen', oils_i18n_gettext(5 , 'Login via xmlrpc', 'cuat', 'label'))
13250 ,(6, NULL, 'login', 'remoteauth', 'authen', oils_i18n_gettext(6 , 'Login via remoteauth', 'cuat', 'label'))
13251 ,(7, NULL, 'login', 'sip2', 'authen', oils_i18n_gettext(7 , 'SIP2 Proxy Login', 'cuat', 'label'))
13252 ,(8, NULL, 'login', 'apache', 'authen', oils_i18n_gettext(8 , 'Login via Apache module', 'cuat', 'label'))
13254 ,(9, NULL, 'verify', 'opensrf', 'authz', oils_i18n_gettext(9 , 'Verification via opensrf', 'cuat', 'label'))
13255 ,(10, NULL, 'verify', 'srfsh', 'authz', oils_i18n_gettext(10, 'Verification via srfsh', 'cuat', 'label'))
13256 ,(11, NULL, 'verify', 'gateway-v1', 'authz', oils_i18n_gettext(11, 'Verification via gateway-v1', 'cuat', 'label'))
13257 ,(12, NULL, 'verify', 'translator-v1','authz', oils_i18n_gettext(12, 'Verification via translator-v1', 'cuat', 'label'))
13258 ,(13, NULL, 'verify', 'xmlrpc', 'authz', oils_i18n_gettext(13, 'Verification via xmlrpc', 'cuat', 'label'))
13259 ,(14, NULL, 'verify', 'remoteauth', 'authz', oils_i18n_gettext(14, 'Verification via remoteauth', 'cuat', 'label'))
13260 ,(15, NULL, 'verify', 'sip2', 'authz', oils_i18n_gettext(15, 'SIP2 User Verification', 'cuat', 'label'))
13262 -- authen/authz actions w/ known uses of "who"
13263 ,(16, 'opac', 'login', 'gateway-v1', 'authen', oils_i18n_gettext(16, 'OPAC Login (jspac)', 'cuat', 'label'))
13264 ,(17, 'opac', 'login', 'apache', 'authen', oils_i18n_gettext(17, 'OPAC Login (tpac)', 'cuat', 'label'))
13265 ,(18, 'staffclient', 'login', 'gateway-v1', 'authen', oils_i18n_gettext(18, 'Staff Client Login', 'cuat', 'label'))
13266 ,(19, 'selfcheck', 'login', 'translator-v1','authen', oils_i18n_gettext(19, 'Self-Check Proxy Login', 'cuat', 'label'))
13267 ,(20, 'ums', 'login', 'xmlrpc', 'authen', oils_i18n_gettext(20, 'Unique Mgt Login', 'cuat', 'label'))
13268 ,(21, 'authproxy', 'login', 'apache', 'authen', oils_i18n_gettext(21, 'Apache Auth Proxy Login', 'cuat', 'label'))
13269 ,(22, 'libraryelf', 'login', 'xmlrpc', 'authz', oils_i18n_gettext(22, 'LibraryElf Login', 'cuat', 'label'))
13271 ,(23, 'selfcheck', 'verify', 'translator-v1','authz', oils_i18n_gettext(23, 'Self-Check User Verification', 'cuat', 'label'))
13272 ,(24, 'ezproxy', 'verify', 'remoteauth', 'authz', oils_i18n_gettext(24, 'EZProxy Verification', 'cuat', 'label'))
13276 -- reserve the first 1000 slots
13277 SELECT SETVAL('config.usr_activity_type_id_seq'::TEXT, 1000);
13279 INSERT INTO config.org_unit_setting_type
13280 (name, label, description, grp, datatype)
13282 'circ.patron.usr_activity_retrieve.max',
13284 'circ.patron.usr_activity_retrieve.max',
13285 'Max user activity entries to retrieve (staff client)',
13290 'circ.patron.usr_activity_retrieve.max',
13291 '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.',
13300 SELECT evergreen.upgrade_deps_block_check('0682', :eg_version);
13302 CREATE TABLE asset.copy_location_group (
13303 id SERIAL PRIMARY KEY,
13304 name TEXT NOT NULL, -- i18n
13305 owner INT NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
13306 pos INT NOT NULL DEFAULT 0,
13307 top BOOL NOT NULL DEFAULT FALSE,
13308 opac_visible BOOL NOT NULL DEFAULT TRUE,
13309 CONSTRAINT lgroup_once_per_owner UNIQUE (owner,name)
13312 CREATE TABLE asset.copy_location_group_map (
13313 id SERIAL PRIMARY KEY,
13314 location INT NOT NULL REFERENCES asset.copy_location (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
13315 lgroup INT NOT NULL REFERENCES asset.copy_location_group (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
13316 CONSTRAINT lgroup_once_per_group UNIQUE (lgroup,location)
13319 -- check whether patch can be applied
13320 SELECT evergreen.upgrade_deps_block_check('0683', :eg_version);
13322 INSERT INTO action_trigger.event_params (event_def, param, value)
13323 VALUES (5, 'check_email_notify', 1);
13324 INSERT INTO action_trigger.event_params (event_def, param, value)
13325 VALUES (7, 'check_email_notify', 1);
13326 INSERT INTO action_trigger.event_params (event_def, param, value)
13327 VALUES (9, 'check_email_notify', 1);
13328 INSERT INTO action_trigger.validator (module,description) VALUES
13329 ('HoldNotifyCheck',
13332 'Check Hold notification flag(s)',
13336 UPDATE action_trigger.event_definition SET validator = 'HoldNotifyCheck' WHERE id = 9;
13338 -- NOT COVERED: Adding check_sms_notify to the proper trigger. It doesn't have a static id.
13340 -- check whether patch can be applied
13341 SELECT evergreen.upgrade_deps_block_check('0684', :eg_version);
13345 -- Replace the constraints with more flexible ENUM's
13346 ALTER TABLE vandelay.queue DROP CONSTRAINT queue_queue_type_check;
13347 ALTER TABLE vandelay.bib_queue DROP CONSTRAINT bib_queue_queue_type_check;
13348 ALTER TABLE vandelay.authority_queue DROP CONSTRAINT authority_queue_queue_type_check;
13350 CREATE TYPE vandelay.bib_queue_queue_type AS ENUM ('bib', 'acq');
13351 CREATE TYPE vandelay.authority_queue_queue_type AS ENUM ('authority');
13353 -- dropped column is also implemented by the child tables
13354 ALTER TABLE vandelay.queue DROP COLUMN queue_type;
13356 -- to recover after using the undo sql from below
13357 -- alter table vandelay.bib_queue add column queue_type text default 'bib' not null;
13358 -- alter table vandelay.authority_queue add column queue_type text default 'authority' not null;
13360 -- modify the child tables to use the ENUMs
13361 ALTER TABLE vandelay.bib_queue
13362 ALTER COLUMN queue_type DROP DEFAULT,
13363 ALTER COLUMN queue_type TYPE vandelay.bib_queue_queue_type
13364 USING (queue_type::vandelay.bib_queue_queue_type),
13365 ALTER COLUMN queue_type SET DEFAULT 'bib';
13367 ALTER TABLE vandelay.authority_queue
13368 ALTER COLUMN queue_type DROP DEFAULT,
13369 ALTER COLUMN queue_type TYPE vandelay.authority_queue_queue_type
13370 USING (queue_type::vandelay.authority_queue_queue_type),
13371 ALTER COLUMN queue_type SET DEFAULT 'authority';
13373 -- give lineitems a pointer to their vandelay queued_record
13375 ALTER TABLE acq.lineitem ADD COLUMN queued_record BIGINT
13376 REFERENCES vandelay.queued_bib_record (id)
13377 ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
13379 ALTER TABLE acq.acq_lineitem_history ADD COLUMN queued_record BIGINT
13380 REFERENCES vandelay.queued_bib_record (id)
13381 ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
13385 INSERT INTO permission.perm_list ( id, code, description )
13388 'IMPORT_ACQ_LINEITEM_BIB_RECORD_UPLOAD',
13391 'Allows a user to create new bibs directly from an ACQ MARC file upload',
13398 INSERT INTO vandelay.import_error ( code, description )
13400 'import.record.perm_failure',
13402 'import.record.perm_failure',
13403 'Perm failure creating a record', 'vie', 'description')
13409 -- Evergreen DB patch 0685.data.bluray_vr_format.sql
13411 -- FIXME: insert description of change, if needed
13415 -- check whether patch can be applied
13416 SELECT evergreen.upgrade_deps_block_check('0685', :eg_version);
13418 -- FIXME: add/check SQL statements to perform the upgrade
13423 -- Check if it is already there
13424 PERFORM * FROM config.marc21_physical_characteristic_value_map v
13425 JOIN config.marc21_physical_characteristic_subfield_map s ON v.ptype_subfield = s.id
13426 WHERE s.ptype_key = 'v' AND s.subfield = 'e' AND s.start_pos = '4' AND s.length = '1'
13434 -- Otherwise, insert it
13435 INSERT INTO config.marc21_physical_characteristic_value_map (value,ptype_subfield,label)
13436 SELECT 's',id,'Blu-ray'
13437 FROM config.marc21_physical_characteristic_subfield_map
13438 WHERE ptype_key = 'v' AND subfield = 'e' AND start_pos = '4' AND length = '1';
13440 -- And reingest the blue-ray items so that things see the new value
13441 SELECT INTO same_marc enabled FROM config.internal_flag WHERE name = 'ingest.reingest.force_on_same_marc';
13442 UPDATE config.internal_flag SET enabled = true WHERE name = 'ingest.reingest.force_on_same_marc';
13443 UPDATE biblio.record_entry SET marc=marc WHERE id IN (SELECT record
13445 metabib.full_rec a JOIN metabib.full_rec b USING (record)
13447 a.tag = 'LDR' AND a.value LIKE '______g%'
13448 AND b.tag = '007' AND b.value LIKE 'v___s%');
13449 UPDATE config.internal_flag SET enabled = same_marc WHERE name = 'ingest.reingest.force_on_same_marc';
13454 -- Evergreen DB patch 0686.schema.auditor_boost.sql
13456 -- FIXME: insert description of change, if needed
13458 -- check whether patch can be applied
13459 SELECT evergreen.upgrade_deps_block_check('0686', :eg_version);
13461 -- FIXME: add/check SQL statements to perform the upgrade
13462 -- These three functions are for capturing, getting, and clearing user and workstation information
13464 -- Set the User AND workstation in one call. Tis faster. And less calls.
13465 -- First argument is user, second is workstation
13466 CREATE OR REPLACE FUNCTION auditor.set_audit_info(INT, INT) RETURNS VOID AS $$
13467 $_SHARED{"eg_audit_user"} = $_[0];
13468 $_SHARED{"eg_audit_ws"} = $_[1];
13469 $$ LANGUAGE plperl;
13471 -- Get the User AND workstation in one call. Less calls, useful for joins ;)
13472 CREATE OR REPLACE FUNCTION auditor.get_audit_info() RETURNS TABLE (eg_user INT, eg_ws INT) AS $$
13473 return [{eg_user => $_SHARED{"eg_audit_user"}, eg_ws => $_SHARED{"eg_audit_ws"}}];
13474 $$ LANGUAGE plperl;
13476 -- Clear the audit info, for whatever reason
13477 CREATE OR REPLACE FUNCTION auditor.clear_audit_info() RETURNS VOID AS $$
13478 delete($_SHARED{"eg_audit_user"});
13479 delete($_SHARED{"eg_audit_ws"});
13480 $$ LANGUAGE plperl;
13482 CREATE OR REPLACE FUNCTION auditor.create_auditor_history ( sch TEXT, tbl TEXT ) RETURNS BOOL AS $creator$
13485 CREATE TABLE auditor.$$ || sch || $$_$$ || tbl || $$_history (
13486 audit_id BIGINT PRIMARY KEY,
13487 audit_time TIMESTAMP WITH TIME ZONE NOT NULL,
13488 audit_action TEXT NOT NULL,
13491 LIKE $$ || sch || $$.$$ || tbl || $$
13496 $creator$ LANGUAGE 'plpgsql';
13498 CREATE OR REPLACE FUNCTION auditor.create_auditor_func ( sch TEXT, tbl TEXT ) RETURNS BOOL AS $creator$
13500 column_list TEXT[];
13502 SELECT INTO column_list array_agg(a.attname)
13503 FROM pg_catalog.pg_attribute a
13504 JOIN pg_catalog.pg_class c ON a.attrelid = c.oid
13505 JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
13506 WHERE relkind = 'r' AND n.nspname = sch AND c.relname = tbl AND a.attnum > 0 AND NOT a.attisdropped;
13509 CREATE OR REPLACE FUNCTION auditor.audit_$$ || sch || $$_$$ || tbl || $$_func ()
13510 RETURNS TRIGGER AS $func$
13512 INSERT INTO auditor.$$ || sch || $$_$$ || tbl || $$_history ( audit_id, audit_time, audit_action, audit_user, audit_ws, $$
13513 || array_to_string(column_list, ', ') || $$ )
13514 SELECT nextval('auditor.$$ || sch || $$_$$ || tbl || $$_pkey_seq'),
13519 OLD.$$ || array_to_string(column_list, ', OLD.') || $$
13520 FROM auditor.get_audit_info();
13523 $func$ LANGUAGE 'plpgsql';
13527 $creator$ LANGUAGE 'plpgsql';
13529 CREATE OR REPLACE FUNCTION auditor.create_auditor_lifecycle ( sch TEXT, tbl TEXT ) RETURNS BOOL AS $creator$
13531 column_list TEXT[];
13533 SELECT INTO column_list array_agg(a.attname)
13534 FROM pg_catalog.pg_attribute a
13535 JOIN pg_catalog.pg_class c ON a.attrelid = c.oid
13536 JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
13537 WHERE relkind = 'r' AND n.nspname = sch AND c.relname = tbl AND a.attnum > 0 AND NOT a.attisdropped;
13540 CREATE VIEW auditor.$$ || sch || $$_$$ || tbl || $$_lifecycle AS
13541 SELECT -1 AS audit_id,
13542 now() AS audit_time,
13543 '-' AS audit_action,
13546 $$ || array_to_string(column_list, ', ') || $$
13547 FROM $$ || sch || $$.$$ || tbl || $$
13549 SELECT audit_id, audit_time, audit_action, audit_user, audit_ws,
13550 $$ || array_to_string(column_list, ', ') || $$
13551 FROM auditor.$$ || sch || $$_$$ || tbl || $$_history;
13555 $creator$ LANGUAGE 'plpgsql';
13557 -- Corrects all column discrepencies between audit table and core table:
13558 -- Adds missing columns
13559 -- Removes leftover columns
13561 -- Also, ensures all core auditor columns exist.
13562 CREATE OR REPLACE FUNCTION auditor.fix_columns() RETURNS VOID AS $BODY$
13564 current_table TEXT = ''; -- Storage for post-loop main table name
13565 current_audit_table TEXT = ''; -- Storage for post-loop audit table name
13566 query TEXT = ''; -- Storage for built query
13567 cr RECORD; -- column record object
13568 alter_t BOOL = false; -- Has the alter table command been appended yet
13569 auditor_cores TEXT[] = ARRAY[]::TEXT[]; -- Core auditor function list (filled inside of loop)
13570 core_column TEXT; -- The current core column we are adding
13573 WITH audit_tables AS ( -- Basic grab of auditor tables. Anything in the auditor namespace, basically. With oids.
13574 SELECT c.oid AS audit_oid, c.relname AS audit_table
13575 FROM pg_catalog.pg_class c
13576 JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
13577 WHERE relkind='r' AND nspname = 'auditor'
13579 table_set AS ( -- Union of auditor tables with their "main" tables. With oids.
13580 SELECT a.audit_oid, a.audit_table, c.oid AS main_oid, n.nspname as main_namespace, c.relname as main_table
13581 FROM pg_catalog.pg_class c
13582 JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
13583 JOIN audit_tables a ON a.audit_table = n.nspname || '_' || c.relname || '_history'
13584 WHERE relkind = 'r'
13586 column_lists AS ( -- All columns associated with the auditor or main table, grouped by the main table's oid.
13587 SELECT DISTINCT ON (main_oid, attname) t.main_oid, a.attname
13589 JOIN pg_catalog.pg_attribute a ON a.attrelid IN (t.main_oid, t.audit_oid)
13590 WHERE attnum > 0 AND NOT attisdropped
13592 column_defs AS ( -- The motherload, every audit table and main table plus column names and defs.
13593 SELECT audit_table,
13596 a.attname AS main_column, -- These two will be null for columns that have since been deleted, or for auditor core columns
13597 pg_catalog.format_type(a.atttypid, a.atttypmod) AS main_column_def,
13598 b.attname AS audit_column, -- These two will be null for columns that have since been added
13599 pg_catalog.format_type(b.atttypid, b.atttypmod) AS audit_column_def
13601 JOIN column_lists c USING (main_oid)
13602 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
13603 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
13605 -- Nice sorted output from the above
13606 SELECT * FROM column_defs WHERE main_column_def IS DISTINCT FROM audit_column_def ORDER BY main_namespace, main_table, main_column, audit_column
13608 IF current_table <> (cr.main_namespace || '.' || cr.main_table) THEN -- New table?
13609 FOR core_column IN SELECT DISTINCT unnest(auditor_cores) LOOP -- Update missing core auditor columns
13610 IF NOT alter_t THEN -- Add ALTER TABLE if we haven't already
13611 query:=query || $$ALTER TABLE auditor.$$ || current_audit_table;
13614 query:=query || $$,$$;
13616 -- 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.
13617 query:=query || $$ ADD COLUMN $$ || CASE WHEN core_column = 'audit_id bigint' THEN $$audit_id bigserial PRIMARY KEY$$ ELSE core_column END;
13619 IF alter_t THEN -- Open alter table = needs a semicolon
13620 query:=query || $$; $$;
13622 IF 'audit_id bigint' = ANY(auditor_cores) THEN -- We added a primary key...
13623 -- Fun! Drop the default on audit_id, drop the auto-created sequence, create a new one, and set the current value
13624 -- For added fun, we have to execute in chunks due to the parser checking setval/currval arguments at parse time.
13626 EXECUTE $$ALTER TABLE auditor.$$ || current_audit_table || $$ ALTER COLUMN audit_id DROP DEFAULT; $$ ||
13627 $$CREATE SEQUENCE auditor.$$ || current_audit_table || $$_pkey_seq;$$;
13628 EXECUTE $$SELECT setval('auditor.$$ || current_audit_table || $$_pkey_seq', currval('auditor.$$ || current_audit_table || $$_audit_id_seq')); $$ ||
13629 $$DROP SEQUENCE auditor.$$ || current_audit_table || $$_audit_id_seq;$$;
13633 -- New table means we reset the list of needed auditor core columns
13634 auditor_cores = ARRAY['audit_id bigint', 'audit_time timestamp with time zone', 'audit_action text', 'audit_user integer', 'audit_ws integer'];
13635 -- And store some values for use later, because we can't rely on cr in all places.
13636 current_table:=cr.main_namespace || '.' || cr.main_table;
13637 current_audit_table:=cr.audit_table;
13639 IF cr.main_column IS NULL AND cr.audit_column LIKE 'audit_%' THEN -- Core auditor column?
13640 -- Remove core from list of cores
13641 SELECT INTO auditor_cores array_agg(core) FROM unnest(auditor_cores) AS core WHERE core != (cr.audit_column || ' ' || cr.audit_column_def);
13642 ELSIF cr.main_column IS NULL THEN -- Main column doesn't exist, and it isn't an auditor column. Needs dropping from the auditor.
13643 IF NOT alter_t THEN
13644 query:=query || $$ALTER TABLE auditor.$$ || current_audit_table;
13647 query:=query || $$,$$;
13649 query:=query || $$ DROP COLUMN $$ || cr.audit_column;
13650 ELSIF cr.audit_column IS NULL AND cr.main_column IS NOT NULL THEN -- New column auditor doesn't have. Add it.
13651 IF NOT alter_t THEN
13652 query:=query || $$ALTER TABLE auditor.$$ || current_audit_table;
13655 query:=query || $$,$$;
13657 query:=query || $$ ADD COLUMN $$ || cr.main_column || $$ $$ || cr.main_column_def;
13658 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.
13659 IF NOT alter_t THEN
13660 query:=query || $$ALTER TABLE auditor.$$ || current_audit_table;
13663 query:=query || $$,$$;
13665 query:=query || $$ ALTER COLUMN $$ || cr.audit_column || $$ TYPE $$ || cr.main_column_def;
13668 FOR core_column IN SELECT DISTINCT unnest(auditor_cores) LOOP -- Repeat this outside of the loop to catch the last table
13669 IF NOT alter_t THEN
13670 query:=query || $$ALTER TABLE auditor.$$ || current_audit_table;
13673 query:=query || $$,$$;
13675 -- 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.
13676 query:=query || $$ ADD COLUMN $$ || CASE WHEN core_column = 'audit_id bigint' THEN $$audit_id bigserial PRIMARY KEY$$ ELSE core_column END;
13678 IF alter_t THEN -- Open alter table = needs a semicolon
13679 query:=query || $$;$$;
13680 IF 'audit_id bigint' = ANY(auditor_cores) THEN -- We added a primary key...
13681 -- Fun! Drop the default on audit_id, drop the auto-created sequence, create a new one, and set the current value
13682 -- For added fun, we have to execute in chunks due to the parser checking setval/currval arguments at parse time.
13684 EXECUTE $$ALTER TABLE auditor.$$ || current_audit_table || $$ ALTER COLUMN audit_id DROP DEFAULT; $$ ||
13685 $$CREATE SEQUENCE auditor.$$ || current_audit_table || $$_pkey_seq;$$;
13686 EXECUTE $$SELECT setval('auditor.$$ || current_audit_table || $$_pkey_seq', currval('auditor.$$ || current_audit_table || $$_audit_id_seq')); $$ ||
13687 $$DROP SEQUENCE auditor.$$ || current_audit_table || $$_audit_id_seq;$$;
13693 $BODY$ LANGUAGE plpgsql;
13695 -- Update it all routine
13696 CREATE OR REPLACE FUNCTION auditor.update_auditors() RETURNS boolean AS $BODY$
13702 -- Drop Lifecycle view(s) before potential column changes
13703 FOR auditor_name IN
13705 FROM pg_catalog.pg_class c
13706 JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
13707 WHERE relkind = 'v' AND n.nspname = 'auditor' LOOP
13708 EXECUTE $$ DROP VIEW auditor.$$ || auditor_name || $$;$$;
13710 -- Fix all column discrepencies
13711 PERFORM auditor.fix_columns();
13712 -- Re-create trigger functions and lifecycle views
13713 FOR table_schema, table_name IN
13714 WITH audit_tables AS (
13715 SELECT c.oid AS audit_oid, c.relname AS audit_table
13716 FROM pg_catalog.pg_class c
13717 JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
13718 WHERE relkind='r' AND nspname = 'auditor'
13721 SELECT a.audit_oid, a.audit_table, c.oid AS main_oid, n.nspname as main_namespace, c.relname as main_table
13722 FROM pg_catalog.pg_class c
13723 JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
13724 JOIN audit_tables a ON a.audit_table = n.nspname || '_' || c.relname || '_history'
13725 WHERE relkind = 'r'
13727 SELECT main_namespace, main_table FROM table_set LOOP
13729 PERFORM auditor.create_auditor_func(table_schema, table_name);
13730 PERFORM auditor.create_auditor_lifecycle(table_schema, table_name);
13734 $BODY$ LANGUAGE plpgsql;
13736 -- Go ahead and update them all now
13737 SELECT auditor.update_auditors();
13740 -- Evergreen DB patch 0687.schema.enhance_reingest.sql
13742 -- FIXME: insert description of change, if needed
13746 -- check whether patch can be applied
13747 SELECT evergreen.upgrade_deps_block_check('0687', :eg_version);
13749 -- FIXME: add/check SQL statements to perform the upgrade
13750 -- New function def
13751 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$
13754 ind_data metabib.field_entry_template%ROWTYPE;
13755 mbe_row metabib.browse_entry%ROWTYPE;
13758 PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled;
13760 IF NOT skip_search THEN
13761 FOR fclass IN SELECT * FROM config.metabib_class LOOP
13762 -- RAISE NOTICE 'Emptying out %', fclass.name;
13763 EXECUTE $$DELETE FROM metabib.$$ || fclass.name || $$_field_entry WHERE source = $$ || bib_id;
13766 IF NOT skip_facet THEN
13767 DELETE FROM metabib.facet_entry WHERE source = bib_id;
13769 IF NOT skip_browse THEN
13770 DELETE FROM metabib.browse_entry_def_map WHERE source = bib_id;
13774 FOR ind_data IN SELECT * FROM biblio.extract_metabib_field_entry( bib_id ) LOOP
13775 IF ind_data.field < 0 THEN
13776 ind_data.field = -1 * ind_data.field;
13779 IF ind_data.facet_field AND NOT skip_facet THEN
13780 INSERT INTO metabib.facet_entry (field, source, value)
13781 VALUES (ind_data.field, ind_data.source, ind_data.value);
13784 IF ind_data.browse_field AND NOT skip_browse THEN
13785 -- A caveat about this SELECT: this should take care of replacing
13786 -- old mbe rows when data changes, but not if normalization (by
13787 -- which I mean specifically the output of
13788 -- evergreen.oils_tsearch2()) changes. It may or may not be
13789 -- expensive to add a comparison of index_vector to index_vector
13790 -- to the WHERE clause below.
13791 SELECT INTO mbe_row * FROM metabib.browse_entry WHERE value = ind_data.value;
13793 mbe_id := mbe_row.id;
13795 INSERT INTO metabib.browse_entry (value) VALUES
13796 (metabib.browse_normalize(ind_data.value, ind_data.field));
13797 mbe_id := CURRVAL('metabib.browse_entry_id_seq'::REGCLASS);
13800 INSERT INTO metabib.browse_entry_def_map (entry, def, source)
13801 VALUES (mbe_id, ind_data.field, ind_data.source);
13804 IF ind_data.search_field AND NOT skip_search THEN
13806 INSERT INTO metabib.$$ || ind_data.field_class || $$_field_entry (field, source, value)
13808 quote_literal(ind_data.field) || $$, $$ ||
13809 quote_literal(ind_data.source) || $$, $$ ||
13810 quote_literal(ind_data.value) ||
13818 $func$ LANGUAGE PLPGSQL;
13821 DROP FUNCTION IF EXISTS metabib.reingest_metabib_field_entries(BIGINT);
13823 -- Evergreen DB patch 0688.data.circ_history_export_csv.sql
13825 -- FIXME: insert description of change, if needed
13828 -- check whether patch can be applied
13829 SELECT evergreen.upgrade_deps_block_check('0688', :eg_version);
13831 INSERT INTO action_trigger.hook (key, core_type, description, passive)
13833 'circ.format.history.csv',
13836 'circ.format.history.csv',
13837 'Produce CSV of circulation history',
13844 INSERT INTO action_trigger.event_definition (
13845 active, owner, name, hook, reactor, validator, group_field, template)
13847 TRUE, 1, 'Circ History CSV', 'circ.format.history.csv', 'ProcessTemplate', 'NOOP_True', 'usr',
13849 Title,Author,Call Number,Barcode,Format
13851 FOR circ IN target;
13852 bibxml = helpers.unapi_bre(circ.target_copy.call_number.record, {flesh => '{mra}'});
13854 FOR part IN bibxml.findnodes('//*[@tag="245"]/*[@code="a" or @code="b"]');
13855 title = title _ part.textContent;
13857 author = bibxml.findnodes('//*[@tag="100"]/*[@code="a"]').textContent;
13858 item_type = bibxml.findnodes('//*[local-name()="attributes"]/*[local-name()="field"][@name="item_type"]').getAttribute('coded-value') %]
13860 [%- helpers.csv_datum(title) -%],
13861 [%- helpers.csv_datum(author) -%],
13862 [%- helpers.csv_datum(circ.target_copy.call_number.label) -%],
13863 [%- helpers.csv_datum(circ.target_copy.barcode) -%],
13864 [%- helpers.csv_datum(item_type) %]
13869 INSERT INTO action_trigger.environment (event_def, path)
13871 currval('action_trigger.event_definition_id_seq'),
13872 'target_copy.call_number'
13876 -- Evergreen DB patch 0689.data.record_print_format_update.sql
13878 -- Updates print and email templates for bib record actions
13881 -- check whether patch can be applied
13882 SELECT evergreen.upgrade_deps_block_check('0689', :eg_version);
13884 UPDATE action_trigger.event_definition SET template = $$
13886 <style> li { padding: 8px; margin 5px; }</style>
13888 [% FOR cbreb IN target %]
13889 [% FOR item IN cbreb.items;
13890 bre_id = item.target_biblio_record_entry;
13892 bibxml = helpers.unapi_bre(bre_id, {flesh => '{mra}'});
13893 FOR part IN bibxml.findnodes('//*[@tag="245"]/*[@code="a" or @code="b"]');
13894 title = title _ part.textContent;
13897 author = bibxml.findnodes('//*[@tag="100"]/*[@code="a"]').textContent;
13898 item_type = bibxml.findnodes('//*[local-name()="attributes"]/*[local-name()="field"][@name="item_type"]').getAttribute('coded-value');
13899 publisher = bibxml.findnodes('//*[@tag="260"]/*[@code="b"]').textContent;
13900 pubdate = bibxml.findnodes('//*[@tag="260"]/*[@code="c"]').textContent;
13901 isbn = bibxml.findnodes('//*[@tag="020"]/*[@code="a"]').textContent;
13902 issn = bibxml.findnodes('//*[@tag="022"]/*[@code="a"]').textContent;
13903 upc = bibxml.findnodes('//*[@tag="024"]/*[@code="a"]').textContent;
13907 Bib ID# [% bre_id %]<br/>
13908 [% IF isbn %]ISBN: [% isbn %]<br/>[% END %]
13909 [% IF issn %]ISSN: [% issn %]<br/>[% END %]
13910 [% IF upc %]UPC: [% upc %]<br/>[% END %]
13911 Title: [% title %]<br />
13912 Author: [% author %]<br />
13913 Publication Info: [% publisher %] [% pubdate %]<br/>
13914 Item Type: [% item_type %]
13921 WHERE hook = 'biblio.format.record_entry.print' AND id < 100; -- sample data
13924 UPDATE action_trigger.event_definition SET delay = '00:00:00', template = $$
13925 [%- SET user = target.0.owner -%]
13926 To: [%- params.recipient_email || user.email %]
13927 From: [%- params.sender_email || default_sender %]
13928 Subject: Bibliographic Records
13930 [% FOR cbreb IN target %]
13931 [% FOR item IN cbreb.items;
13932 bre_id = item.target_biblio_record_entry;
13934 bibxml = helpers.unapi_bre(bre_id, {flesh => '{mra}'});
13935 FOR part IN bibxml.findnodes('//*[@tag="245"]/*[@code="a" or @code="b"]');
13936 title = title _ part.textContent;
13939 author = bibxml.findnodes('//*[@tag="100"]/*[@code="a"]').textContent;
13940 item_type = bibxml.findnodes('//*[local-name()="attributes"]/*[local-name()="field"][@name="item_type"]').getAttribute('coded-value');
13941 publisher = bibxml.findnodes('//*[@tag="260"]/*[@code="b"]').textContent;
13942 pubdate = bibxml.findnodes('//*[@tag="260"]/*[@code="c"]').textContent;
13943 isbn = bibxml.findnodes('//*[@tag="020"]/*[@code="a"]').textContent;
13944 issn = bibxml.findnodes('//*[@tag="022"]/*[@code="a"]').textContent;
13945 upc = bibxml.findnodes('//*[@tag="024"]/*[@code="a"]').textContent;
13948 [% loop.count %]/[% loop.size %]. Bib ID# [% bre_id %]
13949 [% IF isbn %]ISBN: [% isbn _ "\n" %][% END -%]
13950 [% IF issn %]ISSN: [% issn _ "\n" %][% END -%]
13951 [% IF upc %]UPC: [% upc _ "\n" %] [% END -%]
13953 Author: [% author %]
13954 Publication Info: [% publisher %] [% pubdate %]
13955 Item Type: [% item_type %]
13960 WHERE hook = 'biblio.format.record_entry.email' AND id < 100; -- sample data
13962 -- remove a swath of unused environment entries
13964 DELETE FROM action_trigger.environment env
13965 USING action_trigger.event_definition def
13966 WHERE env.event_def = def.id AND
13967 env.path != 'items' AND
13968 def.hook = 'biblio.format.record_entry.print' AND
13969 def.id < 100; -- sample data
13971 DELETE FROM action_trigger.environment env
13972 USING action_trigger.event_definition def
13973 WHERE env.event_def = def.id AND
13974 env.path != 'items' AND
13975 env.path != 'owner' AND
13976 def.hook = 'biblio.format.record_entry.email' AND
13977 def.id < 100; -- sample data
13979 -- Evergreen DB patch 0690.schema.unapi_limit_rank.sql
13981 -- Rewrite the in-database unapi functions to include per-object limits and
13982 -- offsets, such as a maximum number of copies and call numbers for given
13983 -- bib record via the HSTORE syntax (for example, 'acn => 5, acp => 10' would
13984 -- limit to a maximum of 5 call numbers for the bib, with up to 10 copies per
13987 -- Add some notion of "preferred library" that will provide copy counts
13988 -- and optionally affect the sorting of returned copies.
13990 -- Sort copies by availability, preferring the most available copies.
13992 -- Return located URIs.
13996 -- check whether patch can be applied
13997 SELECT evergreen.upgrade_deps_block_check('0690', :eg_version);
13999 -- The simplest way to apply all of these changes is just to replace the unapi
14000 -- schema entirely -- the following is a copy of 990.schema.unapi.sql with
14001 -- the initial COMMIT in place in case the upgrade_deps_block_check fails;
14002 -- if it does, then the attempt to create the unapi schema in the following
14003 -- transaction will also fail. Not graceful, but safe!
14004 DROP SCHEMA IF EXISTS unapi CASCADE;
14006 CREATE SCHEMA unapi;
14008 CREATE OR REPLACE FUNCTION evergreen.org_top()
14009 RETURNS SETOF actor.org_unit AS $$
14010 SELECT * FROM actor.org_unit WHERE parent_ou IS NULL LIMIT 1;
14011 $$ LANGUAGE SQL STABLE
14014 CREATE OR REPLACE FUNCTION evergreen.array_remove_item_by_value(inp ANYARRAY, el ANYELEMENT)
14015 RETURNS anyarray AS $$
14016 SELECT ARRAY_ACCUM(x.e) FROM UNNEST( $1 ) x(e) WHERE x.e <> $2;
14017 $$ LANGUAGE SQL STABLE;
14019 CREATE OR REPLACE FUNCTION evergreen.rank_ou(lib INT, search_lib INT, pref_lib INT DEFAULT NULL)
14020 RETURNS INTEGER AS $$
14021 WITH search_libs AS (
14022 SELECT id, distance FROM actor.org_unit_descendants_distance($2)
14025 (SELECT -10000 FROM actor.org_unit
14026 WHERE $1 = $3 AND id = $3 AND $2 IN (
14027 SELECT id FROM actor.org_unit WHERE parent_ou IS NULL
14030 (SELECT distance FROM search_libs WHERE id = $1),
14033 $$ LANGUAGE SQL STABLE;
14035 CREATE OR REPLACE FUNCTION evergreen.rank_cp_status(status INT)
14036 RETURNS INTEGER AS $$
14037 WITH totally_available AS (
14038 SELECT id, 0 AS avail_rank
14039 FROM config.copy_status
14040 WHERE opac_visible IS TRUE
14041 AND copy_active IS TRUE
14042 AND id != 1 -- "Checked out"
14043 ), almost_available AS (
14044 SELECT id, 10 AS avail_rank
14045 FROM config.copy_status
14046 WHERE holdable IS TRUE
14047 AND opac_visible IS TRUE
14048 AND copy_active IS FALSE
14049 OR id = 1 -- "Checked out"
14052 (SELECT avail_rank FROM totally_available WHERE $1 IN (id)),
14053 (SELECT avail_rank FROM almost_available WHERE $1 IN (id)),
14056 $$ LANGUAGE SQL STABLE;
14058 CREATE OR REPLACE FUNCTION evergreen.ranked_volumes(
14061 depth INT DEFAULT NULL,
14062 slimit HSTORE DEFAULT NULL,
14063 soffset HSTORE DEFAULT NULL,
14064 pref_lib INT DEFAULT NULL
14065 ) RETURNS TABLE (id BIGINT, name TEXT, label_sortkey TEXT, rank BIGINT) AS $$
14066 SELECT ua.id, ua.name, ua.label_sortkey, MIN(ua.rank) AS rank FROM (
14067 SELECT acn.id, aou.name, acn.label_sortkey,
14068 evergreen.rank_ou(aou.id, $2, $6), evergreen.rank_cp_status(acp.status),
14070 FROM asset.call_number acn
14071 JOIN asset.copy acp ON (acn.id = acp.call_number)
14072 JOIN actor.org_unit_descendants( $2, COALESCE(
14075 FROM actor.org_unit_type aout
14076 INNER JOIN actor.org_unit ou ON ou_type = aout.id
14079 ) AS aou ON (acp.circ_lib = aou.id)
14080 WHERE acn.record = $1
14081 AND acn.deleted IS FALSE
14082 AND acp.deleted IS FALSE
14083 GROUP BY acn.id, acp.status, aou.name, acn.label_sortkey, aou.id
14085 ORDER BY evergreen.rank_ou(aou.id, $2, $6), evergreen.rank_cp_status(acp.status)
14088 GROUP BY ua.id, ua.name, ua.label_sortkey
14089 ORDER BY rank, ua.name, ua.label_sortkey
14090 LIMIT ($4 -> 'acn')::INT
14091 OFFSET ($5 -> 'acn')::INT;
14093 LANGUAGE SQL STABLE;
14095 CREATE OR REPLACE FUNCTION evergreen.located_uris (
14098 pref_lib INT DEFAULT NULL
14099 ) RETURNS TABLE (id BIGINT, name TEXT, label_sortkey TEXT, rank INT) AS $$
14100 SELECT acn.id, aou.name, acn.label_sortkey, evergreen.rank_ou(aou.id, $2, $3) AS pref_ou
14101 FROM asset.call_number acn
14102 INNER JOIN asset.uri_call_number_map auricnm ON acn.id = auricnm.call_number
14103 INNER JOIN asset.uri auri ON auri.id = auricnm.uri
14104 INNER JOIN actor.org_unit_ancestors( COALESCE($3, $2) ) aou ON (acn.owning_lib = aou.id)
14105 WHERE acn.record = $1
14106 AND acn.deleted IS FALSE
14107 AND auri.active IS TRUE
14109 SELECT acn.id, aou.name, acn.label_sortkey, evergreen.rank_ou(aou.id, $2, $3) AS pref_ou
14110 FROM asset.call_number acn
14111 INNER JOIN asset.uri_call_number_map auricnm ON acn.id = auricnm.call_number
14112 INNER JOIN asset.uri auri ON auri.id = auricnm.uri
14113 INNER JOIN actor.org_unit_ancestors( $2 ) aou ON (acn.owning_lib = aou.id)
14114 WHERE acn.record = $1
14115 AND acn.deleted IS FALSE
14116 AND auri.active IS TRUE;
14118 LANGUAGE SQL STABLE;
14120 CREATE TABLE unapi.bre_output_layout (
14121 name TEXT PRIMARY KEY,
14122 transform TEXT REFERENCES config.xml_transform (name) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
14123 mime_type TEXT NOT NULL,
14124 feed_top TEXT NOT NULL,
14125 holdings_element TEXT,
14126 title_element TEXT,
14127 description_element TEXT,
14128 creator_element TEXT,
14129 update_ts_element TEXT
14132 INSERT INTO unapi.bre_output_layout
14133 (name, transform, mime_type, holdings_element, feed_top, title_element, description_element, creator_element, update_ts_element)
14135 ('holdings_xml', NULL, 'application/xml', NULL, 'hxml', NULL, NULL, NULL, NULL),
14136 ('marcxml', 'marcxml', 'application/marc+xml', 'record', 'collection', NULL, NULL, NULL, NULL),
14137 ('mods32', 'mods32', 'application/mods+xml', 'mods', 'modsCollection', NULL, NULL, NULL, NULL)
14140 -- Dummy functions, so we can create the real ones out of order
14141 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;
14142 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;
14143 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;
14144 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;
14145 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;
14146 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;
14147 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;
14148 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;
14149 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;
14150 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;
14151 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;
14152 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;
14153 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;
14154 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;
14155 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;
14156 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;
14157 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;
14158 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;
14159 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;
14160 CREATE OR REPLACE FUNCTION unapi.bre (
14166 depth INT DEFAULT NULL,
14167 slimit HSTORE DEFAULT NULL,
14168 soffset HSTORE DEFAULT NULL,
14169 include_xmlns BOOL DEFAULT TRUE,
14170 pref_lib INT DEFAULT NULL
14172 RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
14173 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;
14174 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;
14175 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;
14177 CREATE OR REPLACE FUNCTION unapi.holdings_xml (
14181 depth INT DEFAULT NULL,
14182 includes TEXT[] DEFAULT NULL::TEXT[],
14183 slimit HSTORE DEFAULT NULL,
14184 soffset HSTORE DEFAULT NULL,
14185 include_xmlns BOOL DEFAULT TRUE,
14186 pref_lib INT DEFAULT NULL
14188 RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
14190 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;
14192 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$
14198 'id' || COALESCE(obj_id::TEXT,'') ||
14199 'format' || COALESCE(format::TEXT,'') ||
14200 'ename' || COALESCE(ename::TEXT,'') ||
14201 'includes' || COALESCE(includes::TEXT,'{}'::TEXT[]::TEXT) ||
14202 'org' || COALESCE(org::TEXT,'') ||
14203 'depth' || COALESCE(depth::TEXT,'') ||
14204 'slimit' || COALESCE(slimit::TEXT,'') ||
14205 'soffset' || COALESCE(soffset::TEXT,'') ||
14206 'include_xmlns' || COALESCE(include_xmlns::TEXT,'');
14207 -- RAISE NOTICE 'memoize key: %', key;
14210 -- RAISE NOTICE 'memoize hash: %', key;
14212 -- XXX cache logic ... memcached? table?
14214 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;
14217 $F$ LANGUAGE PLPGSQL STABLE;
14219 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$
14221 layout unapi.bre_output_layout%ROWTYPE;
14222 transform config.xml_transform%ROWTYPE;
14225 xmlns_uri TEXT := 'http://open-ils.org/spec/feed-xml/v1';
14227 element_list TEXT[];
14230 IF org = '-' OR org IS NULL THEN
14231 SELECT shortname INTO org FROM evergreen.org_top();
14234 SELECT id INTO ouid FROM actor.org_unit WHERE shortname = org;
14235 SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format;
14237 IF layout.name IS NULL THEN
14241 SELECT * INTO transform FROM config.xml_transform WHERE name = layout.transform;
14242 xmlns_uri := COALESCE(transform.namespace_uri,xmlns_uri);
14244 -- Gather the bib xml
14245 SELECT XMLAGG( unapi.bre(i, format, '', includes, org, depth, slimit, soffset, include_xmlns)) INTO tmp_xml FROM UNNEST( id_list ) i;
14247 IF layout.title_element IS NOT NULL THEN
14248 EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.title_element ||', XMLATTRIBUTES( $1 AS xmlns), $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, title;
14251 IF layout.description_element IS NOT NULL THEN
14252 EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.description_element ||', XMLATTRIBUTES( $1 AS xmlns), $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, description;
14255 IF layout.creator_element IS NOT NULL THEN
14256 EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.creator_element ||', XMLATTRIBUTES( $1 AS xmlns), $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, creator;
14259 IF layout.update_ts_element IS NOT NULL THEN
14260 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;
14263 IF unapi_url IS NOT NULL THEN
14264 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;
14267 IF header_xml IS NOT NULL THEN tmp_xml := XMLCONCAT(header_xml,tmp_xml::XML); END IF;
14269 element_list := regexp_split_to_array(layout.feed_top,E'\\.');
14270 FOR i IN REVERSE ARRAY_UPPER(element_list, 1) .. 1 LOOP
14271 EXECUTE 'SELECT XMLELEMENT( name '|| quote_ident(element_list[i]) ||', XMLATTRIBUTES( $1 AS xmlns), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML;
14274 RETURN tmp_xml::XML;
14276 $F$ LANGUAGE PLPGSQL STABLE;
14278 CREATE OR REPLACE FUNCTION unapi.bre (
14284 depth INT DEFAULT NULL,
14285 slimit HSTORE DEFAULT NULL,
14286 soffset HSTORE DEFAULT NULL,
14287 include_xmlns BOOL DEFAULT TRUE,
14288 pref_lib INT DEFAULT NULL
14292 me biblio.record_entry%ROWTYPE;
14293 layout unapi.bre_output_layout%ROWTYPE;
14294 xfrm config.xml_transform%ROWTYPE;
14303 IF org = '-' OR org IS NULL THEN
14304 SELECT shortname INTO org FROM evergreen.org_top();
14307 SELECT id INTO ouid FROM actor.org_unit WHERE shortname = org;
14309 IF ouid IS NULL THEN
14313 IF format = 'holdings_xml' THEN -- the special case
14314 output := unapi.holdings_xml( obj_id, ouid, org, depth, includes, slimit, soffset, include_xmlns);
14318 SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format;
14320 IF layout.name IS NULL THEN
14324 SELECT * INTO xfrm FROM config.xml_transform WHERE name = layout.transform;
14326 SELECT * INTO me FROM biblio.record_entry WHERE id = obj_id;
14328 -- grab SVF if we need them
14329 IF ('mra' = ANY (includes)) THEN
14330 axml := unapi.mra(obj_id,NULL,NULL,NULL,NULL);
14335 -- grab holdings if we need them
14336 IF ('holdings_xml' = ANY (includes)) THEN
14337 hxml := unapi.holdings_xml(obj_id, ouid, org, depth, evergreen.array_remove_item_by_value(includes,'holdings_xml'), slimit, soffset, include_xmlns, pref_lib);
14343 -- generate our item node
14346 IF format = 'marcxml' THEN
14347 tmp_xml := me.marc;
14348 IF tmp_xml !~ E'<marc:' THEN -- If we're not using the prefixed namespace in this record, then remove all declarations of it
14349 tmp_xml := REGEXP_REPLACE(tmp_xml, ' xmlns:marc="http://www.loc.gov/MARC21/slim"', '', 'g');
14352 tmp_xml := oils_xslt_process(me.marc, xfrm.xslt)::XML;
14355 top_el := REGEXP_REPLACE(tmp_xml, E'^.*?<((?:\\S+:)?' || layout.holdings_element || ').*$', E'\\1');
14357 IF axml IS NOT NULL THEN
14358 tmp_xml := REGEXP_REPLACE(tmp_xml, '</' || top_el || '>(.*?)$', axml || '</' || top_el || E'>\\1');
14361 IF hxml IS NOT NULL THEN -- XXX how do we configure the holdings position?
14362 tmp_xml := REGEXP_REPLACE(tmp_xml, '</' || top_el || '>(.*?)$', hxml || '</' || top_el || E'>\\1');
14365 IF ('bre.unapi' = ANY (includes)) THEN
14366 output := REGEXP_REPLACE(
14368 '</' || top_el || '>(.*?)',
14372 'http://www.w3.org/1999/xhtml' AS xmlns,
14373 'unapi-id' AS class,
14374 'tag:open-ils.org:U2@bre/' || obj_id || '/' || org AS title
14376 )::TEXT || '</' || top_el || E'>\\1'
14382 output := REGEXP_REPLACE(output::TEXT,E'>\\s+<','><','gs')::XML;
14385 $F$ LANGUAGE PLPGSQL STABLE;
14387 CREATE OR REPLACE FUNCTION unapi.holdings_xml (
14391 depth INT DEFAULT NULL,
14392 includes TEXT[] DEFAULT NULL::TEXT[],
14393 slimit HSTORE DEFAULT NULL,
14394 soffset HSTORE DEFAULT NULL,
14395 include_xmlns BOOL DEFAULT TRUE,
14396 pref_lib INT DEFAULT NULL
14402 CASE WHEN $8 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14403 CASE WHEN ('bre' = ANY ($5)) THEN 'tag:open-ils.org:U2@bre/' || $1 || '/' || $3 ELSE NULL END AS id
14407 (SELECT XMLAGG(XMLELEMENT::XML) FROM (
14410 XMLATTRIBUTES('public' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
14412 FROM asset.opac_ou_record_copy_count($2, $1)
14416 XMLATTRIBUTES('staff' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
14418 FROM asset.staff_ou_record_copy_count($2, $1)
14422 XMLATTRIBUTES('pref_lib' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
14424 FROM asset.opac_ou_record_copy_count($9, $1)
14429 WHEN ('bmp' = ANY ($5)) THEN
14431 name monograph_parts,
14432 (SELECT XMLAGG(bmp) FROM (
14433 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)
14434 FROM biblio.monograph_part
14442 (SELECT XMLAGG(acn ORDER BY rank, name, label_sortkey) FROM (
14444 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
14445 FROM evergreen.ranked_volumes($1, $2, $4, $6, $7, $9) AS y
14448 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
14449 FROM evergreen.located_uris($1, $2, $9) AS uris
14452 CASE WHEN ('ssub' = ANY ($5)) THEN
14454 name subscriptions,
14455 (SELECT XMLAGG(ssub) FROM (
14456 SELECT unapi.ssub(id,'xml','subscription','{}'::TEXT[], $3, $4, $6, $7, FALSE)
14457 FROM serial.subscription
14458 WHERE record_entry = $1
14462 CASE WHEN ('acp' = ANY ($5)) THEN
14464 name foreign_copies,
14465 (SELECT XMLAGG(acp) FROM (
14466 SELECT unapi.acp(p.target_copy,'xml','copy',evergreen.array_remove_item_by_value($5,'acp'), $3, $4, $6, $7, FALSE)
14467 FROM biblio.peer_bib_copy_map p
14468 JOIN asset.copy c ON (p.target_copy = c.id)
14469 WHERE NOT c.deleted AND p.peer_record = $1
14470 LIMIT ($6 -> 'acp')::INT
14471 OFFSET ($7 -> 'acp')::INT
14476 $F$ LANGUAGE SQL STABLE;
14478 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$
14482 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14483 'tag:open-ils.org:U2@ssub/' || id AS id,
14484 'tag:open-ils.org:U2@aou/' || owning_lib AS owning_lib,
14485 start_date AS start, end_date AS end, expected_date_offset
14488 WHEN ('sdist' = ANY ($4)) THEN
14489 XMLELEMENT( name distributions,
14490 (SELECT XMLAGG(sdist) FROM (
14491 SELECT unapi.sdist( id, 'xml', 'distribution', evergreen.array_remove_item_by_value($4,'ssub'), $5, $6, $7, $8, FALSE)
14492 FROM serial.distribution
14493 WHERE subscription = ssub.id
14499 FROM serial.subscription ssub
14501 GROUP BY id, start_date, end_date, expected_date_offset, owning_lib;
14502 $F$ LANGUAGE SQL STABLE;
14504 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$
14508 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14509 'tag:open-ils.org:U2@sdist/' || id AS id,
14510 'tag:open-ils.org:U2@acn/' || receive_call_number AS receive_call_number,
14511 'tag:open-ils.org:U2@acn/' || bind_call_number AS bind_call_number,
14512 unit_label_prefix, label, unit_label_suffix, summary_method
14514 unapi.aou( holding_lib, $2, 'holding_lib', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8),
14515 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,
14517 WHEN ('sstr' = ANY ($4)) THEN
14518 XMLELEMENT( name streams,
14519 (SELECT XMLAGG(sstr) FROM (
14520 SELECT unapi.sstr( id, 'xml', 'stream', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE)
14522 WHERE distribution = sdist.id
14527 XMLELEMENT( name summaries,
14529 WHEN ('sbsum' = ANY ($4)) THEN
14530 (SELECT XMLAGG(sbsum) FROM (
14531 SELECT unapi.sbsum( id, 'xml', 'serial_summary', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE)
14532 FROM serial.basic_summary
14533 WHERE distribution = sdist.id
14538 WHEN ('sisum' = ANY ($4)) THEN
14539 (SELECT XMLAGG(sisum) FROM (
14540 SELECT unapi.sisum( id, 'xml', 'serial_summary', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE)
14541 FROM serial.index_summary
14542 WHERE distribution = sdist.id
14547 WHEN ('sssum' = ANY ($4)) THEN
14548 (SELECT XMLAGG(sssum) FROM (
14549 SELECT unapi.sssum( id, 'xml', 'serial_summary', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE)
14550 FROM serial.supplement_summary
14551 WHERE distribution = sdist.id
14557 FROM serial.distribution sdist
14559 GROUP BY id, label, unit_label_prefix, unit_label_suffix, holding_lib, summary_method, subscription, receive_call_number, bind_call_number;
14560 $F$ LANGUAGE SQL STABLE;
14562 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$
14566 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14567 'tag:open-ils.org:U2@sstr/' || id AS id,
14570 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,
14572 WHEN ('sitem' = ANY ($4)) THEN
14573 XMLELEMENT( name items,
14574 (SELECT XMLAGG(sitem) FROM (
14575 SELECT unapi.sitem( id, 'xml', 'serial_item', evergreen.array_remove_item_by_value($4,'sstr'), $5, $6, $7, $8, FALSE)
14577 WHERE stream = sstr.id
14583 FROM serial.stream sstr
14585 GROUP BY id, routing_label, distribution;
14586 $F$ LANGUAGE SQL STABLE;
14588 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$
14592 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14593 'tag:open-ils.org:U2@siss/' || id AS id,
14594 create_date, edit_date, label, date_published,
14595 holding_code, holding_type, holding_link_id
14597 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,
14599 WHEN ('sitem' = ANY ($4)) THEN
14600 XMLELEMENT( name items,
14601 (SELECT XMLAGG(sitem) FROM (
14602 SELECT unapi.sitem( id, 'xml', 'serial_item', evergreen.array_remove_item_by_value($4,'siss'), $5, $6, $7, $8, FALSE)
14604 WHERE issuance = sstr.id
14610 FROM serial.issuance sstr
14612 GROUP BY id, create_date, edit_date, label, date_published, holding_code, holding_type, holding_link_id, subscription;
14613 $F$ LANGUAGE SQL STABLE;
14615 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$
14619 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14620 'tag:open-ils.org:U2@sitem/' || id AS id,
14621 'tag:open-ils.org:U2@siss/' || issuance AS issuance,
14622 date_expected, date_received
14624 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,
14625 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,
14626 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,
14627 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
14628 -- XMLELEMENT( name notes,
14630 -- WHEN ('acpn' = ANY ($4)) THEN
14631 -- (SELECT XMLAGG(acpn) FROM (
14632 -- SELECT unapi.acpn( id, 'xml', 'copy_note', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8)
14633 -- FROM asset.copy_note
14634 -- WHERE owning_copy = cp.id AND pub
14640 FROM serial.item sitem
14642 $F$ LANGUAGE SQL STABLE;
14645 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$
14647 name serial_summary,
14649 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14650 'tag:open-ils.org:U2@sbsum/' || id AS id,
14651 'sssum' AS type, generated_coverage, textual_holdings, show_generated
14653 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
14655 FROM serial.supplement_summary ssum
14657 GROUP BY id, generated_coverage, textual_holdings, distribution, show_generated;
14658 $F$ LANGUAGE SQL STABLE;
14660 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$
14662 name serial_summary,
14664 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14665 'tag:open-ils.org:U2@sbsum/' || id AS id,
14666 'sbsum' AS type, generated_coverage, textual_holdings, show_generated
14668 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
14670 FROM serial.basic_summary ssum
14672 GROUP BY id, generated_coverage, textual_holdings, distribution, show_generated;
14673 $F$ LANGUAGE SQL STABLE;
14675 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$
14677 name serial_summary,
14679 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14680 'tag:open-ils.org:U2@sbsum/' || id AS id,
14681 'sisum' AS type, generated_coverage, textual_holdings, show_generated
14683 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
14685 FROM serial.index_summary ssum
14687 GROUP BY id, generated_coverage, textual_holdings, distribution, show_generated;
14688 $F$ LANGUAGE SQL STABLE;
14691 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$
14695 IF ename = 'circlib' THEN
14699 'http://open-ils.org/spec/actors/v1' AS xmlns,
14704 FROM actor.org_unit aou
14707 EXECUTE $$SELECT XMLELEMENT(
14708 name $$ || ename || $$,
14710 'http://open-ils.org/spec/actors/v1' AS xmlns,
14711 'tag:open-ils.org:U2@aou/' || id AS id,
14712 shortname, name, opac_visible
14715 FROM actor.org_unit aou
14716 WHERE id = $1 $$ INTO output USING obj_id;
14722 $F$ LANGUAGE PLPGSQL STABLE;
14724 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$
14728 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14732 label_prefix AS prefix,
14733 label_suffix AS suffix
14737 FROM asset.copy_location
14739 $F$ LANGUAGE SQL STABLE;
14741 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$
14745 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14752 FROM config.copy_status
14754 $F$ LANGUAGE SQL STABLE;
14756 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$
14760 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14761 create_date AS date,
14766 FROM asset.copy_note
14768 $F$ LANGUAGE SQL STABLE;
14770 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$
14774 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14780 FROM asset.stat_cat_entry asce
14781 JOIN asset.stat_cat sc ON (sc.id = asce.stat_cat)
14782 WHERE asce.id = $1;
14783 $F$ LANGUAGE SQL STABLE;
14785 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$
14787 name monograph_part,
14789 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14790 'tag:open-ils.org:U2@bmp/' || id AS id,
14794 'tag:open-ils.org:U2@bre/' || record AS record
14797 WHEN ('acp' = ANY ($4)) THEN
14798 XMLELEMENT( name copies,
14799 (SELECT XMLAGG(acp) FROM (
14800 SELECT unapi.acp( cp.id, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'bmp'), $5, $6, $7, $8, FALSE)
14802 JOIN asset.copy_part_map cpm ON (cpm.target_copy = cp.id)
14803 WHERE cpm.part = $1
14804 AND cp.deleted IS FALSE
14805 ORDER BY COALESCE(cp.copy_number,0), cp.barcode
14806 LIMIT ($7 -> 'acp')::INT
14807 OFFSET ($8 -> 'acp')::INT
14813 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
14815 FROM biblio.monograph_part
14817 GROUP BY id, label, label_sortkey, record;
14818 $F$ LANGUAGE SQL STABLE;
14820 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$
14824 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14825 'tag:open-ils.org:U2@acp/' || id AS id, id AS copy_id,
14826 create_date, edit_date, copy_number, circulate, deposit,
14827 ref, holdable, deleted, deposit_amount, price, barcode,
14828 circ_modifier, circ_as_type, opac_visible, age_protect
14830 unapi.ccs( status, $2, 'status', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE),
14831 unapi.acl( location, $2, 'location', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE),
14832 unapi.aou( circ_lib, $2, 'circ_lib', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8),
14833 unapi.aou( circ_lib, $2, 'circlib', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8),
14834 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,
14836 WHEN ('acpn' = ANY ($4)) THEN
14837 XMLELEMENT( name copy_notes,
14838 (SELECT XMLAGG(acpn) FROM (
14839 SELECT unapi.acpn( id, 'xml', 'copy_note', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
14840 FROM asset.copy_note
14841 WHERE owning_copy = cp.id AND pub
14847 WHEN ('ascecm' = ANY ($4)) THEN
14848 XMLELEMENT( name statcats,
14849 (SELECT XMLAGG(ascecm) FROM (
14850 SELECT unapi.ascecm( stat_cat_entry, 'xml', 'statcat', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
14851 FROM asset.stat_cat_entry_copy_map
14852 WHERE owning_copy = cp.id
14858 WHEN ('bre' = ANY ($4)) THEN
14859 XMLELEMENT( name foreign_records,
14860 (SELECT XMLAGG(bre) FROM (
14861 SELECT unapi.bre(peer_record,'marcxml','record','{}'::TEXT[], $5, $6, $7, $8, FALSE)
14862 FROM biblio.peer_bib_copy_map
14863 WHERE target_copy = cp.id
14870 WHEN ('bmp' = ANY ($4)) THEN
14871 XMLELEMENT( name monograph_parts,
14872 (SELECT XMLAGG(bmp) FROM (
14873 SELECT unapi.bmp( part, 'xml', 'monograph_part', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
14874 FROM asset.copy_part_map
14875 WHERE target_copy = cp.id
14881 WHEN ('circ' = ANY ($4)) THEN
14882 XMLELEMENT( name current_circulation,
14883 (SELECT XMLAGG(circ) FROM (
14884 SELECT unapi.circ( id, 'xml', 'circ', evergreen.array_remove_item_by_value($4,'circ'), $5, $6, $7, $8, FALSE)
14885 FROM action.circulation
14886 WHERE target_copy = cp.id
14887 AND checkin_time IS NULL
14895 AND cp.deleted IS FALSE
14896 GROUP BY id, status, location, circ_lib, call_number, create_date,
14897 edit_date, copy_number, circulate, deposit, ref, holdable,
14898 deleted, deposit_amount, price, barcode, circ_modifier,
14899 circ_as_type, opac_visible, age_protect;
14900 $F$ LANGUAGE SQL STABLE;
14902 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$
14906 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14907 'tag:open-ils.org:U2@acp/' || id AS id, id AS copy_id,
14908 create_date, edit_date, copy_number, circulate, deposit,
14909 ref, holdable, deleted, deposit_amount, price, barcode,
14910 circ_modifier, circ_as_type, opac_visible, age_protect,
14911 status_changed_time, floating, mint_condition,
14912 detailed_contents, sort_key, summary_contents, cost
14914 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),
14915 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),
14916 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),
14917 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),
14918 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,
14919 XMLELEMENT( name copy_notes,
14921 WHEN ('acpn' = ANY ($4)) THEN
14922 (SELECT XMLAGG(acpn) FROM (
14923 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)
14924 FROM asset.copy_note
14925 WHERE owning_copy = cp.id AND pub
14930 XMLELEMENT( name statcats,
14932 WHEN ('ascecm' = ANY ($4)) THEN
14933 (SELECT XMLAGG(ascecm) FROM (
14934 SELECT unapi.ascecm( stat_cat_entry, 'xml', 'statcat', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
14935 FROM asset.stat_cat_entry_copy_map
14936 WHERE owning_copy = cp.id
14941 XMLELEMENT( name foreign_records,
14943 WHEN ('bre' = ANY ($4)) THEN
14944 (SELECT XMLAGG(bre) FROM (
14945 SELECT unapi.bre(peer_record,'marcxml','record','{}'::TEXT[], $5, $6, $7, $8, FALSE)
14946 FROM biblio.peer_bib_copy_map
14947 WHERE target_copy = cp.id
14953 WHEN ('bmp' = ANY ($4)) THEN
14954 XMLELEMENT( name monograph_parts,
14955 (SELECT XMLAGG(bmp) FROM (
14956 SELECT unapi.bmp( part, 'xml', 'monograph_part', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
14957 FROM asset.copy_part_map
14958 WHERE target_copy = cp.id
14964 WHEN ('circ' = ANY ($4)) THEN
14965 XMLELEMENT( name current_circulation,
14966 (SELECT XMLAGG(circ) FROM (
14967 SELECT unapi.circ( id, 'xml', 'circ', evergreen.array_remove_item_by_value($4,'circ'), $5, $6, $7, $8, FALSE)
14968 FROM action.circulation
14969 WHERE target_copy = cp.id
14970 AND checkin_time IS NULL
14976 FROM serial.unit cp
14978 AND cp.deleted IS FALSE
14979 GROUP BY id, status, location, circ_lib, call_number, create_date,
14980 edit_date, copy_number, circulate, floating, mint_condition,
14981 deposit, ref, holdable, deleted, deposit_amount, price,
14982 barcode, circ_modifier, circ_as_type, opac_visible,
14983 status_changed_time, detailed_contents, sort_key,
14984 summary_contents, cost, age_protect;
14985 $F$ LANGUAGE SQL STABLE;
14987 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$
14991 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14992 'tag:open-ils.org:U2@acn/' || acn.id AS id,
14993 acn.id AS vol_id, o.shortname AS lib,
14994 o.opac_visible AS opac_visible,
14995 deleted, label, label_sortkey, label_class, record
14997 unapi.aou( owning_lib, $2, 'owning_lib', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8),
14999 WHEN ('acp' = ANY ($4)) THEN
15000 CASE WHEN $6 IS NOT NULL THEN
15001 XMLELEMENT( name copies,
15002 (SELECT XMLAGG(acp ORDER BY rank_avail) FROM (
15003 SELECT unapi.acp( cp.id, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE),
15004 evergreen.rank_cp_status(cp.status) AS rank_avail
15006 JOIN actor.org_unit_descendants( (SELECT id FROM actor.org_unit WHERE shortname = $5), $6) aoud ON (cp.circ_lib = aoud.id)
15007 WHERE cp.call_number = acn.id
15008 AND cp.deleted IS FALSE
15009 ORDER BY rank_avail, COALESCE(cp.copy_number,0), cp.barcode
15010 LIMIT ($7 -> 'acp')::INT
15011 OFFSET ($8 -> 'acp')::INT
15015 XMLELEMENT( name copies,
15016 (SELECT XMLAGG(acp ORDER BY rank_avail) FROM (
15017 SELECT unapi.acp( cp.id, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE),
15018 evergreen.rank_cp_status(cp.status) AS rank_avail
15020 JOIN actor.org_unit_descendants( (SELECT id FROM actor.org_unit WHERE shortname = $5) ) aoud ON (cp.circ_lib = aoud.id)
15021 WHERE cp.call_number = acn.id
15022 AND cp.deleted IS FALSE
15023 ORDER BY rank_avail, COALESCE(cp.copy_number,0), cp.barcode
15024 LIMIT ($7 -> 'acp')::INT
15025 OFFSET ($8 -> 'acp')::INT
15033 (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)
15035 unapi.acnp( acn.prefix, 'marcxml', 'prefix', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE),
15036 unapi.acns( acn.suffix, 'marcxml', 'suffix', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE),
15037 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
15039 FROM asset.call_number acn
15040 JOIN actor.org_unit o ON (o.id = acn.owning_lib)
15042 AND acn.deleted IS FALSE
15043 GROUP BY acn.id, o.shortname, o.opac_visible, deleted, label, label_sortkey, label_class, owning_lib, record, acn.prefix, acn.suffix;
15044 $F$ LANGUAGE SQL STABLE;
15046 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$
15048 name call_number_prefix,
15050 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
15053 'tag:open-ils.org:U2@aou/' || owning_lib AS owning_lib,
15057 FROM asset.call_number_prefix
15059 $F$ LANGUAGE SQL STABLE;
15061 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$
15063 name call_number_suffix,
15065 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
15068 'tag:open-ils.org:U2@aou/' || owning_lib AS owning_lib,
15072 FROM asset.call_number_suffix
15074 $F$ LANGUAGE SQL STABLE;
15076 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$
15080 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
15081 'tag:open-ils.org:U2@auri/' || uri.id AS id,
15087 WHEN ('acn' = ANY ($4)) THEN
15088 XMLELEMENT( name copies,
15089 (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)
15096 GROUP BY uri.id, use_restriction, href, label;
15097 $F$ LANGUAGE SQL STABLE;
15099 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$
15103 CASE WHEN $9 THEN 'http://open-ils.org/spec/indexing/v1' ELSE NULL END AS xmlns,
15104 'tag:open-ils.org:U2@mra/' || mra.id AS id,
15105 'tag:open-ils.org:U2@bre/' || mra.id AS record
15107 (SELECT XMLAGG(foo.y)
15108 FROM (SELECT XMLELEMENT(
15112 cvm.value AS "coded-value",
15118 FROM EACH(mra.attrs) AS x
15119 JOIN config.record_attr_definition rad ON (x.key = rad.name)
15120 LEFT JOIN config.coded_value_map cvm ON (cvm.ctype = x.key AND code = x.value)
15124 FROM metabib.record_attr mra
15126 $F$ LANGUAGE SQL STABLE;
15128 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$
15132 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
15133 'tag:open-ils.org:U2@circ/' || id AS id,
15137 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,
15138 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
15140 FROM action.circulation
15142 $F$ LANGUAGE SQL STABLE;
15146 -- Some test queries
15148 SELECT unapi.memoize( 'bre', 1,'mods32','','{holdings_xml,acp}'::TEXT[], 'SYS1');
15149 SELECT unapi.memoize( 'bre', 1,'marcxml','','{holdings_xml,acp}'::TEXT[], 'SYS1');
15150 SELECT unapi.memoize( 'bre', 1,'holdings_xml','','{holdings_xml,acp}'::TEXT[], 'SYS1');
15152 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>');
15154 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>');
15155 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>');
15156 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>');
15157 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>');
15159 SELECT unapi.biblio_record_entry_feed('{216}'::BIGINT[],'marcxml','{}'::TEXT[], 'BR1');
15160 EXPLAIN ANALYZE SELECT unapi.bre(216,'marcxml','record','{holdings_xml,bre.unapi}'::TEXT[], 'BR1');
15161 EXPLAIN ANALYZE SELECT unapi.bre(216,'holdings_xml','record','{}'::TEXT[], 'BR1');
15162 EXPLAIN ANALYZE SELECT unapi.holdings_xml(216,4,'BR1',2,'{bre}'::TEXT[]);
15163 EXPLAIN ANALYZE SELECT unapi.bre(216,'mods32','record','{}'::TEXT[], 'BR1');
15165 -- Limit to 5 call numbers, 5 copies, with a preferred library of 4 (BR1), in SYS2 at a depth of 0
15166 EXPLAIN ANALYZE SELECT unapi.bre(36,'marcxml','record','{holdings_xml,mra,acp,acnp,acns,bmp}','SYS2',0,'acn=>5,acp=>5',NULL,TRUE,4);
15171 SELECT evergreen.upgrade_deps_block_check('0691', :eg_version);
15173 CREATE INDEX poi_po_idx ON acq.po_item (purchase_order);
15175 CREATE INDEX ie_inv_idx on acq.invoice_entry (invoice);
15176 CREATE INDEX ie_po_idx on acq.invoice_entry (purchase_order);
15177 CREATE INDEX ie_li_idx on acq.invoice_entry (lineitem);
15179 CREATE INDEX ii_inv_idx on acq.invoice_item (invoice);
15180 CREATE INDEX ii_po_idx on acq.invoice_item (purchase_order);
15181 CREATE INDEX ii_poi_idx on acq.invoice_item (po_item);
15184 SELECT evergreen.upgrade_deps_block_check('0692', :eg_version);
15186 INSERT INTO config.org_unit_setting_type
15187 (name, label, description, grp, datatype)
15189 'circ.fines.charge_when_closed',
15191 'circ.fines.charge_when_closed',
15192 'Charge fines on overdue circulations when closed',
15197 'circ.fines.charge_when_closed',
15198 '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.',
15206 SELECT evergreen.upgrade_deps_block_check('0694', :eg_version);
15208 INSERT into config.org_unit_setting_type
15209 ( name, grp, label, description, datatype, fm_class ) VALUES
15211 ( 'ui.patron.edit.au.prefix.require', 'gui',
15212 oils_i18n_gettext('ui.patron.edit.au.prefix.require',
15213 'Require prefix field on patron registration',
15215 oils_i18n_gettext('ui.patron.edit.au.prefix.require',
15216 'The prefix field will be required on the patron registration screen.',
15217 'coust', 'description'),
15220 ,( 'ui.patron.edit.au.prefix.show', 'gui',
15221 oils_i18n_gettext('ui.patron.edit.au.prefix.show',
15222 'Show prefix field on patron registration',
15224 oils_i18n_gettext('ui.patron.edit.au.prefix.show',
15225 '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.',
15226 'coust', 'description'),
15229 ,( 'ui.patron.edit.au.prefix.suggest', 'gui',
15230 oils_i18n_gettext('ui.patron.edit.au.prefix.suggest',
15231 'Suggest prefix field on patron registration',
15233 oils_i18n_gettext('ui.patron.edit.au.prefix.suggest',
15234 '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.',
15235 'coust', 'description'),
15240 -- Evergreen DB patch 0695.schema.custom_toolbars.sql
15242 -- FIXME: insert description of change, if needed
15245 -- check whether patch can be applied
15246 SELECT evergreen.upgrade_deps_block_check('0695', :eg_version);
15248 CREATE TABLE actor.toolbar (
15249 id BIGSERIAL PRIMARY KEY,
15250 ws INT REFERENCES actor.workstation (id) ON DELETE CASCADE,
15251 org INT REFERENCES actor.org_unit (id) ON DELETE CASCADE,
15252 usr INT REFERENCES actor.usr (id) ON DELETE CASCADE,
15253 label TEXT NOT NULL,
15254 layout TEXT NOT NULL,
15255 CONSTRAINT only_one_type CHECK (
15256 (ws IS NOT NULL AND COALESCE(org,usr) IS NULL) OR
15257 (org IS NOT NULL AND COALESCE(ws,usr) IS NULL) OR
15258 (usr IS NOT NULL AND COALESCE(org,ws) IS NULL)
15260 CONSTRAINT layout_must_be_json CHECK ( is_json(layout) )
15262 CREATE UNIQUE INDEX label_once_per_ws ON actor.toolbar (ws, label) WHERE ws IS NOT NULL;
15263 CREATE UNIQUE INDEX label_once_per_org ON actor.toolbar (org, label) WHERE org IS NOT NULL;
15264 CREATE UNIQUE INDEX label_once_per_usr ON actor.toolbar (usr, label) WHERE usr IS NOT NULL;
15266 -- this one unrelated to toolbars but is a gap in the upgrade scripts
15267 INSERT INTO permission.perm_list ( id, code, description )
15270 'IMPORT_AUTHORITY_MARC',
15273 'Allows a user to create new authority records',
15279 FROM permission.perm_list
15284 INSERT INTO permission.perm_list ( id, code, description ) VALUES (
15289 'Allows a user to create, edit, and delete custom toolbars',
15295 -- Don't want to assume stock perm groups in an upgrade script, but here for ease of testing
15296 -- 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';
15298 INSERT INTO actor.toolbar(org,label,layout) VALUES
15299 ( 1, 'circ', '["circ_checkout","circ_checkin","toolbarseparator.1","search_opac","copy_status","toolbarseparator.2","patron_search","patron_register","toolbarspacer.3","hotkeys_toggle"]' ),
15300 ( 1, 'cat', '["circ_checkin","toolbarseparator.1","search_opac","copy_status","toolbarseparator.2","create_marc","authority_manage","retrieve_last_record","toolbarspacer.3","hotkeys_toggle"]' );
15302 -- 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 ;
15304 -- Evergreen DB patch 0696.no_plperl.sql
15306 -- FIXME: insert description of change, if needed
15309 -- check whether patch can be applied
15310 SELECT evergreen.upgrade_deps_block_check('0696', :eg_version);
15312 -- Re-create these as plperlu instead of plperl
15313 CREATE OR REPLACE FUNCTION auditor.set_audit_info(INT, INT) RETURNS VOID AS $$
15314 $_SHARED{"eg_audit_user"} = $_[0];
15315 $_SHARED{"eg_audit_ws"} = $_[1];
15316 $$ LANGUAGE plperlu;
15318 CREATE OR REPLACE FUNCTION auditor.get_audit_info() RETURNS TABLE (eg_user INT, eg_ws INT) AS $$
15319 return [{eg_user => $_SHARED{"eg_audit_user"}, eg_ws => $_SHARED{"eg_audit_ws"}}];
15320 $$ LANGUAGE plperlu;
15322 CREATE OR REPLACE FUNCTION auditor.clear_audit_info() RETURNS VOID AS $$
15323 delete($_SHARED{"eg_audit_user"});
15324 delete($_SHARED{"eg_audit_ws"});
15325 $$ LANGUAGE plperlu;
15327 -- And remove the language so that we don't use it later.
15328 DROP LANGUAGE plperl;
15330 -- Evergreen DB patch 0697.data.place_currently_unfillable_hold.sql
15332 -- FIXME: insert description of change, if needed
15335 -- check whether patch can be applied
15336 SELECT evergreen.upgrade_deps_block_check('0697', :eg_version);
15338 -- FIXME: add/check SQL statements to perform the upgrade
15339 INSERT INTO permission.perm_list ( id, code, description ) VALUES
15340 ( 524, 'PLACE_UNFILLABLE_HOLD', oils_i18n_gettext( 524,
15341 'Allows a user to place a hold that cannot currently be filled.', 'ppl', 'description' ));
15343 -- Evergreen DB patch 0698.hold_default_pickup.sql
15345 -- FIXME: insert description of change, if needed
15348 -- check whether patch can be applied
15349 SELECT evergreen.upgrade_deps_block_check('0698', :eg_version);
15351 INSERT INTO config.usr_setting_type (name,opac_visible,label,description,datatype)
15352 VALUES ('opac.default_pickup_location', TRUE, 'Default Hold Pickup Location', 'Default location for holds pickup', 'integer');
15354 SELECT evergreen.upgrade_deps_block_check('0699', :eg_version);
15356 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype, grp )
15358 'ui.hide_copy_editor_fields',
15360 'ui.hide_copy_editor_fields',
15361 'GUI: Hide these fields within the Item Attribute Editor',
15366 'ui.hide_copy_editor_fields',
15367 'This setting may be best maintained with the dedicated configuration'
15368 || ' interface within the Item Attribute Editor. However, here it'
15369 || ' shows up as comma separated list of field identifiers to hide.',
15378 SELECT evergreen.upgrade_deps_block_check('0700', :eg_version);
15379 SELECT evergreen.upgrade_deps_block_check('0706', :eg_version);
15381 -- This throws away data, but only data that causes breakage anyway.
15382 UPDATE serial.issuance SET holding_code = NULL WHERE NOT is_json(holding_code);
15384 -- If we don't do this, we have unprocessed triggers and we can't alter the table
15385 SET CONSTRAINTS serial.issuance_caption_and_pattern_fkey IMMEDIATE;
15387 ALTER TABLE serial.issuance ADD CHECK (holding_code IS NULL OR is_json(holding_code));
15389 INSERT INTO config.internal_flag (name, value, enabled) VALUES (
15390 'serial.rematerialize_on_same_holding_code', NULL, FALSE
15393 INSERT INTO config.org_unit_setting_type (
15394 name, label, grp, description, datatype
15396 'serial.default_display_grouping',
15397 'Default display grouping for serials distributions presented in the OPAC.',
15399 'Default display grouping for serials distributions presented in the OPAC. This can be "enum" or "chron".',
15403 ALTER TABLE serial.distribution
15404 ADD COLUMN display_grouping TEXT NOT NULL DEFAULT 'chron'
15405 CHECK (display_grouping IN ('enum', 'chron'));
15407 -- why didn't we just make one summary table in the first place?
15408 CREATE VIEW serial.any_summary AS
15410 'basic' AS summary_type, id, distribution,
15411 generated_coverage, textual_holdings, show_generated
15412 FROM serial.basic_summary
15415 'index' AS summary_type, id, distribution,
15416 generated_coverage, textual_holdings, show_generated
15417 FROM serial.index_summary
15420 'supplement' AS summary_type, id, distribution,
15421 generated_coverage, textual_holdings, show_generated
15422 FROM serial.supplement_summary ;
15425 -- Given the IDs of two rows in actor.org_unit, *the second being an ancestor
15426 -- of the first*, return in array form the path from the ancestor to the
15427 -- descendant, with each point in the path being an org_unit ID. This is
15428 -- useful for sorting org_units by their position in a depth-first (display
15429 -- order) representation of the tree.
15431 -- This breaks with the precedent set by actor.org_unit_full_path() and others,
15432 -- and gets the parameters "backwards," but otherwise this function would
15433 -- not be very usable within json_query.
15434 CREATE OR REPLACE FUNCTION actor.org_unit_simple_path(INT, INT)
15435 RETURNS INT[] AS $$
15436 WITH RECURSIVE descendant_depth(id, path) AS (
15439 FROM actor.org_unit aou
15440 JOIN actor.org_unit_type aout ON (aout.id = aou.ou_type)
15444 dd.path || ARRAY[aou.id]
15445 FROM actor.org_unit aou
15446 JOIN actor.org_unit_type aout ON (aout.id = aou.ou_type)
15447 JOIN descendant_depth dd ON (dd.id = aou.parent_ou)
15449 FROM actor.org_unit aou
15450 JOIN descendant_depth dd USING (id)
15451 WHERE aou.id = $1 ORDER BY dd.path;
15452 $$ LANGUAGE SQL STABLE;
15454 CREATE TABLE serial.materialized_holding_code (
15455 id BIGSERIAL PRIMARY KEY,
15456 issuance INTEGER NOT NULL REFERENCES serial.issuance (id) ON DELETE CASCADE,
15461 CREATE OR REPLACE FUNCTION serial.materialize_holding_code() RETURNS TRIGGER
15468 if (not defined $_TD->{new}{holding_code}) {
15469 elog(WARNING, 'NULL in "holding_code" column of serial.issuance allowed for now, but may not be useful');
15473 # Do nothing if holding_code has not changed...
15475 if ($_TD->{new}{holding_code} eq $_TD->{old}{holding_code}) {
15476 # ... unless the following internal flag is set.
15478 my $flag_rv = spi_exec_query(q{
15479 SELECT * FROM config.internal_flag
15480 WHERE name = 'serial.rematerialize_on_same_holding_code' AND enabled
15482 return unless $flag_rv->{processed};
15486 my $holding_code = (new JSON::XS)->decode($_TD->{new}{holding_code});
15488 my $field = new MARC::Field('999', @$holding_code); # tag doesnt matter
15490 my $dstmt = spi_prepare(
15491 'DELETE FROM serial.materialized_holding_code WHERE issuance = $1',
15494 spi_exec_prepared($dstmt, $_TD->{new}{id});
15496 my $istmt = spi_prepare(
15498 INSERT INTO serial.materialized_holding_code (
15499 issuance, subfield, value
15500 ) VALUES ($1, $2, $3)
15501 }, qw{INT CHAR TEXT}
15504 foreach ($field->subfields) {
15515 $func$ LANGUAGE 'plperlu';
15517 CREATE INDEX assist_holdings_display
15518 ON serial.materialized_holding_code (issuance, subfield);
15520 CREATE TRIGGER materialize_holding_code
15521 AFTER INSERT OR UPDATE ON serial.issuance
15522 FOR EACH ROW EXECUTE PROCEDURE serial.materialize_holding_code() ;
15524 -- starting here, we materialize all existing holding codes.
15526 UPDATE config.internal_flag
15528 WHERE name = 'serial.rematerialize_on_same_holding_code';
15530 UPDATE serial.issuance SET holding_code = holding_code;
15532 UPDATE config.internal_flag
15533 SET enabled = FALSE
15534 WHERE name = 'serial.rematerialize_on_same_holding_code';
15536 -- finish holding code materialization process
15538 -- fix up missing holding_code fields from serial.issuance
15539 UPDATE serial.issuance siss
15540 SET holding_type = scap.type
15541 FROM serial.caption_and_pattern scap
15542 WHERE scap.id = siss.caption_and_pattern AND siss.holding_type IS NULL;
15545 -- Evergreen DB patch 0701.schema.patron_stat_category_enhancements.sql
15547 -- Enables users to set patron statistical categories as required,
15548 -- whether or not users can input free text for the category value.
15549 -- Enables administrators to set an entry as the default for any
15550 -- given patron statistical category and org unit.
15553 -- check whether patch can be applied
15554 SELECT evergreen.upgrade_deps_block_check('0701', :eg_version);
15558 CREATE TABLE actor.stat_cat_entry_default (
15559 id SERIAL PRIMARY KEY,
15560 stat_cat_entry INT NOT NULL REFERENCES actor.stat_cat_entry (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
15561 stat_cat INT NOT NULL REFERENCES actor.stat_cat (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
15562 owner INT NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
15563 CONSTRAINT sced_once_per_owner UNIQUE (stat_cat,owner)
15566 COMMENT ON TABLE actor.stat_cat_entry_default IS $$
15567 User Statistical Category Default Entry
15569 A library may choose one of the stat_cat entries to be the
15573 -- Add columns to existing tables
15575 -- Patron stat cat required column
15576 ALTER TABLE actor.stat_cat
15577 ADD COLUMN required BOOL NOT NULL DEFAULT FALSE;
15579 -- Patron stat cat allow_freetext column
15580 ALTER TABLE actor.stat_cat
15581 ADD COLUMN allow_freetext BOOL NOT NULL DEFAULT TRUE;
15585 INSERT INTO permission.perm_list ( id, code, description ) VALUES
15586 ( 525, 'CREATE_PATRON_STAT_CAT_ENTRY_DEFAULT', oils_i18n_gettext( 525,
15587 'User may set a default entry in a patron statistical category', 'ppl', 'description' )),
15588 ( 526, 'UPDATE_PATRON_STAT_CAT_ENTRY_DEFAULT', oils_i18n_gettext( 526,
15589 'User may reset a default entry in a patron statistical category', 'ppl', 'description' )),
15590 ( 527, 'DELETE_PATRON_STAT_CAT_ENTRY_DEFAULT', oils_i18n_gettext( 527,
15591 'User may unset a default entry in a patron statistical category', 'ppl', 'description' ));
15593 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable)
15595 pgt.id, perm.id, aout.depth, TRUE
15597 permission.grp_tree pgt,
15598 permission.perm_list perm,
15599 actor.org_unit_type aout
15601 pgt.name = 'Circulation Administrator' AND
15602 aout.name = 'System' AND
15603 perm.code IN ('CREATE_PATRON_STAT_CAT_ENTRY_DEFAULT', 'DELETE_PATRON_STAT_CAT_ENTRY_DEFAULT');
15606 SELECT evergreen.upgrade_deps_block_check('0702', :eg_version);
15608 INSERT INTO config.global_flag (name, enabled, label)
15610 'opac.org_unit.non_inheritied_visibility',
15613 'opac.org_unit.non_inheritied_visibility',
15614 'Org Units Do Not Inherit Visibility',
15620 CREATE TYPE actor.org_unit_custom_tree_purpose AS ENUM ('opac');
15622 CREATE TABLE actor.org_unit_custom_tree (
15623 id SERIAL PRIMARY KEY,
15624 active BOOLEAN DEFAULT FALSE,
15625 purpose actor.org_unit_custom_tree_purpose NOT NULL DEFAULT 'opac' UNIQUE
15628 CREATE TABLE actor.org_unit_custom_tree_node (
15629 id SERIAL PRIMARY KEY,
15630 tree INTEGER REFERENCES actor.org_unit_custom_tree (id) DEFERRABLE INITIALLY DEFERRED,
15631 org_unit INTEGER NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
15632 parent_node INTEGER REFERENCES actor.org_unit_custom_tree_node (id) DEFERRABLE INITIALLY DEFERRED,
15633 sibling_order INTEGER NOT NULL DEFAULT 0,
15634 CONSTRAINT aouctn_once_per_org UNIQUE (tree, org_unit)
15640 DELETE FROM config.global_flag WHERE name = 'opac.org_unit.non_inheritied_visibility';
15641 DROP TABLE actor.org_unit_custom_tree_node;
15642 DROP TABLE actor.org_unit_custom_tree;
15643 DROP TYPE actor.org_unit_custom_tree_purpose;
15649 -- This is split out because it was backported to 2.1, but may not exist before upgrades
15650 -- It can safely fail
15651 -- Also, lets say that. <_<
15653 \qecho *************************************************************************
15654 \qecho !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
15655 \qecho We are about to apply a patch that may not be needed. It can fail safely.
15656 \qecho !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
15657 \qecho *************************************************************************
15660 -- Evergreen DB patch 0693.schema.do_not_despace_issns.sql
15662 -- FIXME: insert description of change, if needed
15667 -- check whether patch can be applied
15668 SELECT evergreen.upgrade_deps_block_check('0693', :eg_version);
15670 -- FIXME: add/check SQL statements to perform the upgrade
15671 -- Delete the index normalizer that was meant to remove spaces from ISSNs
15672 -- but ended up breaking records with multiple ISSNs
15673 DELETE FROM config.metabib_field_index_norm_map WHERE id IN (
15674 SELECT map.id FROM config.metabib_field_index_norm_map map
15675 INNER JOIN config.metabib_field cmf ON cmf.id = map.field
15676 INNER JOIN config.index_normalizer cin ON cin.id = map.norm
15677 WHERE cin.func = 'replace'
15678 AND cmf.field_class = 'identifier'
15679 AND cmf.name = 'issn'
15680 AND map.params = $$[" ",""]$$
15683 -- Reindex records that have more than just a single ISSN
15684 -- to ensure that spaces are maintained
15685 SELECT metabib.reingest_metabib_field_entries(source)
15686 FROM metabib.identifier_field_entry mife
15687 INNER JOIN config.metabib_field cmf ON cmf.id = mife.field
15688 WHERE cmf.field_class = 'identifier'
15689 AND cmf.name = 'issn'
15690 AND char_length(value) > 9