1 --Upgrade Script for 2.1 to 2.2-alpha2
2 -- DROP objects that might have existed from a prior run of 0526
4 DROP TABLE IF EXISTS config.db_patch_dependencies;
5 ALTER TABLE config.upgrade_log DROP COLUMN applied_to;
6 DROP FUNCTION evergreen.upgrade_list_applied_deprecates(TEXT);
7 DROP FUNCTION evergreen.upgrade_list_applied_supersedes(TEXT);
10 INSERT INTO config.upgrade_log (version) VALUES ('2.2-alpha3');
12 INSERT INTO config.upgrade_log (version) VALUES ('0526'); --miker
14 CREATE TABLE config.db_patch_dependencies (
15 db_patch TEXT PRIMARY KEY,
20 CREATE OR REPLACE FUNCTION evergreen.array_overlap_check (/* field */) RETURNS TRIGGER AS $$
26 EXECUTE 'SELECT COUNT(*) FROM '|| TG_TABLE_SCHEMA ||'.'|| TG_TABLE_NAME ||' WHERE '|| fld ||' && ($1).'|| fld INTO cnt USING NEW;
28 RAISE EXCEPTION 'Cannot insert duplicate array into field % of table %', fld, TG_TABLE_SCHEMA ||'.'|| TG_TABLE_NAME;
34 CREATE TRIGGER no_overlapping_sups
35 BEFORE INSERT OR UPDATE ON config.db_patch_dependencies
36 FOR EACH ROW EXECUTE PROCEDURE evergreen.array_overlap_check ('supersedes');
38 CREATE TRIGGER no_overlapping_deps
39 BEFORE INSERT OR UPDATE ON config.db_patch_dependencies
40 FOR EACH ROW EXECUTE PROCEDURE evergreen.array_overlap_check ('deprecates');
42 ALTER TABLE config.upgrade_log
43 ADD COLUMN applied_to TEXT;
45 -- Provide a named type for patching functions
46 CREATE TYPE evergreen.patch AS (patch TEXT);
48 -- List applied db patches that are deprecated by (and block the application of) my_db_patch
49 CREATE OR REPLACE FUNCTION evergreen.upgrade_list_applied_deprecates ( my_db_patch TEXT ) RETURNS SETOF evergreen.patch AS $$
50 SELECT DISTINCT l.version
51 FROM config.upgrade_log l
52 JOIN config.db_patch_dependencies d ON (l.version::TEXT[] && d.deprecates)
56 -- List applied db patches that are superseded by (and block the application of) my_db_patch
57 CREATE OR REPLACE FUNCTION evergreen.upgrade_list_applied_supersedes ( my_db_patch TEXT ) RETURNS SETOF evergreen.patch AS $$
58 SELECT DISTINCT l.version
59 FROM config.upgrade_log l
60 JOIN config.db_patch_dependencies d ON (l.version::TEXT[] && d.supersedes)
64 -- List applied db patches that deprecates (and block the application of) my_db_patch
65 CREATE OR REPLACE FUNCTION evergreen.upgrade_list_applied_deprecated ( my_db_patch TEXT ) RETURNS TEXT AS $$
67 FROM config.db_patch_dependencies
68 WHERE ARRAY[$1]::TEXT[] && deprecates
71 -- List applied db patches that supersedes (and block the application of) my_db_patch
72 CREATE OR REPLACE FUNCTION evergreen.upgrade_list_applied_superseded ( my_db_patch TEXT ) RETURNS TEXT AS $$
74 FROM config.db_patch_dependencies
75 WHERE ARRAY[$1]::TEXT[] && supersedes
78 -- Make sure that no deprecated or superseded db patches are currently applied
79 CREATE OR REPLACE FUNCTION evergreen.upgrade_verify_no_dep_conflicts ( my_db_patch TEXT ) RETURNS BOOL AS $$
81 FROM (SELECT * FROM evergreen.upgrade_list_applied_deprecates( $1 )
83 SELECT * FROM evergreen.upgrade_list_applied_supersedes( $1 )
85 SELECT * FROM evergreen.upgrade_list_applied_deprecated( $1 )
87 SELECT * FROM evergreen.upgrade_list_applied_superseded( $1 ))x
90 -- Raise an exception if there are, in fact, dep/sup confilct
91 CREATE OR REPLACE FUNCTION evergreen.upgrade_deps_block_check ( my_db_patch TEXT, my_applied_to TEXT ) RETURNS BOOL AS $$
96 IF NOT evergreen.upgrade_verify_no_dep_conflicts( my_db_patch ) THEN
97 SELECT STRING_AGG(patch, ', ') INTO deprecates FROM evergreen.upgrade_list_applied_deprecates(my_db_patch);
98 SELECT STRING_AGG(patch, ', ') INTO supersedes FROM evergreen.upgrade_list_applied_supersedes(my_db_patch);
100 Upgrade script % can not be applied:
101 applied deprecated scripts %
102 applied superseded scripts %
106 ARRAY_AGG(evergreen.upgrade_list_applied_deprecates(my_db_patch)),
107 ARRAY_AGG(evergreen.upgrade_list_applied_supersedes(my_db_patch)),
108 evergreen.upgrade_list_applied_deprecated(my_db_patch),
109 evergreen.upgrade_list_applied_superseded(my_db_patch);
112 INSERT INTO config.upgrade_log (version, applied_to) VALUES (my_db_patch, my_applied_to);
117 -- Evergreen DB patch 0536.schema.lazy_circ-barcode_lookup.sql
119 -- FIXME: insert description of change, if needed
122 -- check whether patch can be applied
123 INSERT INTO config.upgrade_log (version) VALUES ('0536');
125 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');
127 CREATE TABLE config.barcode_completion (
128 id SERIAL PRIMARY KEY,
129 active BOOL NOT NULL DEFAULT true,
130 org_unit INT NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
133 length INT NOT NULL DEFAULT 0,
135 padding_end BOOL NOT NULL DEFAULT false,
136 asset BOOL NOT NULL DEFAULT true,
137 actor BOOL NOT NULL DEFAULT true
140 CREATE TYPE evergreen.barcode_set AS (type TEXT, id BIGINT, barcode TEXT);
142 CREATE OR REPLACE FUNCTION evergreen.get_barcodes(select_ou INT, type TEXT, in_barcode TEXT) RETURNS SETOF evergreen.barcode_set AS $$
147 asset_barcodes TEXT[];
148 actor_barcodes TEXT[];
149 do_asset BOOL = false;
150 do_serial BOOL = false;
151 do_booking BOOL = false;
152 do_actor BOOL = false;
153 completion_set config.barcode_completion%ROWTYPE;
156 IF position('asset' in type) > 0 THEN
159 IF position('serial' in type) > 0 THEN
162 IF position('booking' in type) > 0 THEN
165 IF do_asset OR do_serial OR do_booking THEN
166 asset_barcodes = asset_barcodes || in_barcode;
168 IF position('actor' in type) > 0 THEN
170 actor_barcodes = actor_barcodes || in_barcode;
173 barcode_len := length(in_barcode);
175 FOR completion_set IN
176 SELECT * FROM config.barcode_completion
178 AND org_unit IN (SELECT aou.id FROM actor.org_unit_ancestors(select_ou) aou)
180 IF completion_set.prefix IS NULL THEN
181 completion_set.prefix := '';
183 IF completion_set.suffix IS NULL THEN
184 completion_set.suffix := '';
186 IF completion_set.length = 0 OR completion_set.padding IS NULL OR length(completion_set.padding) = 0 THEN
187 cur_barcode = completion_set.prefix || in_barcode || completion_set.suffix;
189 completion_len = completion_set.length - length(completion_set.prefix) - length(completion_set.suffix);
190 IF completion_len >= barcode_len THEN
191 IF completion_set.padding_end THEN
192 cur_barcode = rpad(in_barcode, completion_len, completion_set.padding);
194 cur_barcode = lpad(in_barcode, completion_len, completion_set.padding);
196 cur_barcode = completion_set.prefix || cur_barcode || completion_set.suffix;
199 IF completion_set.actor THEN
200 actor_barcodes = actor_barcodes || cur_barcode;
202 IF completion_set.asset THEN
203 asset_barcodes = asset_barcodes || cur_barcode;
207 IF do_asset AND do_serial THEN
208 RETURN QUERY SELECT 'asset'::TEXT, id, barcode FROM ONLY asset.copy WHERE barcode = ANY(asset_barcodes) AND deleted = false;
209 RETURN QUERY SELECT 'serial'::TEXT, id, barcode FROM serial.unit WHERE barcode = ANY(asset_barcodes) AND deleted = false;
211 RETURN QUERY SELECT 'asset'::TEXT, id, barcode FROM 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;
216 RETURN QUERY SELECT 'booking'::TEXT, id::BIGINT, barcode FROM booking.resource WHERE barcode = ANY(asset_barcodes);
219 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;
225 COMMENT ON FUNCTION evergreen.get_barcodes(INT, TEXT, TEXT) IS $$
226 Given user input, find an appropriate barcode in the proper class.
228 Will add prefix/suffix information to do so, and return all results.
233 INSERT INTO config.upgrade_log (version) VALUES ('0537'); --miker
235 DROP FUNCTION evergreen.upgrade_deps_block_check(text,text);
236 DROP FUNCTION evergreen.upgrade_verify_no_dep_conflicts(text);
237 DROP FUNCTION evergreen.upgrade_list_applied_deprecated(text);
238 DROP FUNCTION evergreen.upgrade_list_applied_superseded(text);
240 -- List applied db patches that deprecates (and block the application of) my_db_patch
241 CREATE FUNCTION evergreen.upgrade_list_applied_deprecated ( my_db_patch TEXT ) RETURNS SETOF TEXT AS $$
243 FROM config.db_patch_dependencies
244 WHERE ARRAY[$1]::TEXT[] && deprecates
247 -- List applied db patches that supersedes (and block the application of) my_db_patch
248 CREATE FUNCTION evergreen.upgrade_list_applied_superseded ( my_db_patch TEXT ) RETURNS SETOF TEXT AS $$
250 FROM config.db_patch_dependencies
251 WHERE ARRAY[$1]::TEXT[] && supersedes
254 -- Make sure that no deprecated or superseded db patches are currently applied
255 CREATE FUNCTION evergreen.upgrade_verify_no_dep_conflicts ( my_db_patch TEXT ) RETURNS BOOL AS $$
257 FROM (SELECT * FROM evergreen.upgrade_list_applied_deprecates( $1 )
259 SELECT * FROM evergreen.upgrade_list_applied_supersedes( $1 )
261 SELECT * FROM evergreen.upgrade_list_applied_deprecated( $1 )
263 SELECT * FROM evergreen.upgrade_list_applied_superseded( $1 ))x
266 -- Raise an exception if there are, in fact, dep/sup confilct
267 CREATE FUNCTION evergreen.upgrade_deps_block_check ( my_db_patch TEXT, my_applied_to TEXT ) RETURNS BOOL AS $$
269 IF NOT evergreen.upgrade_verify_no_dep_conflicts( my_db_patch ) THEN
271 Upgrade script % can not be applied:
272 applied deprecated scripts %
273 applied superseded scripts %
277 ARRAY_ACCUM(evergreen.upgrade_list_applied_deprecates(my_db_patch)),
278 ARRAY_ACCUM(evergreen.upgrade_list_applied_supersedes(my_db_patch)),
279 evergreen.upgrade_list_applied_deprecated(my_db_patch),
280 evergreen.upgrade_list_applied_superseded(my_db_patch);
283 INSERT INTO config.upgrade_log (version, applied_to) VALUES (my_db_patch, my_applied_to);
289 INSERT INTO config.upgrade_log (version) VALUES ('0544');
291 INSERT INTO config.usr_setting_type
292 ( name, opac_visible, label, description, datatype) VALUES
293 ( 'circ.collections.exempt',
295 oils_i18n_gettext('circ.collections.exempt', 'Collections: Exempt', 'cust', 'description'),
296 oils_i18n_gettext('circ.collections.exempt', 'User is exempt from collections tracking/processing', 'cust', 'description'),
302 SELECT evergreen.upgrade_deps_block_check('0545', :eg_version);
304 INSERT INTO permission.perm_list VALUES
305 (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')),
306 (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'));
308 --- stock Circulation Administrator group
310 INSERT INTO permission.grp_perm_map ( grp, perm, depth, grantable )
316 FROM permission.perm_list
317 WHERE code in ('ABORT_TRANSIT_ON_LOST', 'ABORT_TRANSIT_ON_MISSING');
319 -- Evergreen DB patch 0546.schema.sip_statcats.sql
322 -- check whether patch can be applied
323 SELECT evergreen.upgrade_deps_block_check('0546', :eg_version);
325 CREATE TABLE actor.stat_cat_sip_fields (
326 field CHAR(2) PRIMARY KEY,
328 one_only BOOL NOT NULL DEFAULT FALSE
330 COMMENT ON TABLE actor.stat_cat_sip_fields IS $$
331 Actor Statistical Category SIP Fields
333 Contains the list of valid SIP Field identifiers for
334 Statistical Categories.
336 ALTER TABLE actor.stat_cat
337 ADD COLUMN sip_field CHAR(2) REFERENCES actor.stat_cat_sip_fields(field) ON UPDATE CASCADE ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
338 ADD COLUMN sip_format TEXT;
340 CREATE FUNCTION actor.stat_cat_check() RETURNS trigger AS $func$
342 sipfield actor.stat_cat_sip_fields%ROWTYPE;
345 IF NEW.sip_field IS NOT NULL THEN
346 SELECT INTO sipfield * FROM actor.stat_cat_sip_fields WHERE field = NEW.sip_field;
347 IF sipfield.one_only THEN
348 SELECT INTO use_count count(id) FROM actor.stat_cat WHERE sip_field = NEW.sip_field AND id != NEW.id;
349 IF use_count > 0 THEN
350 RAISE EXCEPTION 'Sip field cannot be used twice';
356 $func$ LANGUAGE PLPGSQL;
358 CREATE TRIGGER actor_stat_cat_sip_update_trigger
359 BEFORE INSERT OR UPDATE ON actor.stat_cat FOR EACH ROW
360 EXECUTE PROCEDURE actor.stat_cat_check();
362 CREATE TABLE asset.stat_cat_sip_fields (
363 field CHAR(2) PRIMARY KEY,
365 one_only BOOL NOT NULL DEFAULT FALSE
367 COMMENT ON TABLE asset.stat_cat_sip_fields IS $$
368 Asset Statistical Category SIP Fields
370 Contains the list of valid SIP Field identifiers for
371 Statistical Categories.
374 ALTER TABLE asset.stat_cat
375 ADD COLUMN sip_field CHAR(2) REFERENCES asset.stat_cat_sip_fields(field) ON UPDATE CASCADE ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
376 ADD COLUMN sip_format TEXT;
378 CREATE FUNCTION asset.stat_cat_check() RETURNS trigger AS $func$
380 sipfield asset.stat_cat_sip_fields%ROWTYPE;
383 IF NEW.sip_field IS NOT NULL THEN
384 SELECT INTO sipfield * FROM asset.stat_cat_sip_fields WHERE field = NEW.sip_field;
385 IF sipfield.one_only THEN
386 SELECT INTO use_count count(id) FROM asset.stat_cat WHERE sip_field = NEW.sip_field AND id != NEW.id;
387 IF use_count > 0 THEN
388 RAISE EXCEPTION 'Sip field cannot be used twice';
394 $func$ LANGUAGE PLPGSQL;
396 CREATE TRIGGER asset_stat_cat_sip_update_trigger
397 BEFORE INSERT OR UPDATE ON asset.stat_cat FOR EACH ROW
398 EXECUTE PROCEDURE asset.stat_cat_check();
402 SELECT evergreen.upgrade_deps_block_check('0548', :eg_version); -- dbwells
404 \qecho This redoes the original part 1 of 0547 which did not apply to rel_2_1,
405 \qecho and is being added for the sake of clarity
407 -- delete errant inserts from 0545 (group 4 is NOT the circulation admin group)
408 DELETE FROM permission.grp_perm_map WHERE grp = 4 AND perm IN (
409 SELECT id FROM permission.perm_list
410 WHERE code in ('ABORT_TRANSIT_ON_LOST', 'ABORT_TRANSIT_ON_MISSING')
413 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable)
415 pgt.id, perm.id, aout.depth, TRUE
417 permission.grp_tree pgt,
418 permission.perm_list perm,
419 actor.org_unit_type aout
421 pgt.name = 'Circulation Administrator' AND
422 aout.name = 'Consortium' AND
424 'ABORT_TRANSIT_ON_LOST',
425 'ABORT_TRANSIT_ON_MISSING'
428 FROM permission.grp_perm_map AS map
431 AND map.perm = perm.id
434 -- Evergreen DB patch XXXX.data.transit-checkin-interval.sql
436 -- New org unit setting "circ.transit.min_checkin_interval"
437 -- New TRANSIT_CHECKIN_INTERVAL_BLOCK.override permission
441 -- check whether patch can be applied
442 SELECT evergreen.upgrade_deps_block_check('0549', :eg_version);
444 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype ) VALUES (
445 'circ.transit.min_checkin_interval',
447 'circ.transit.min_checkin_interval',
448 'Circ: Minimum Transit Checkin Interval',
453 'circ.transit.min_checkin_interval',
454 'In-Transit items checked in this close to the transit start time will be prevented from checking in',
461 INSERT INTO permission.perm_list ( id, code, description ) VALUES (
463 'TRANSIT_CHECKIN_INTERVAL_BLOCK.override',
466 'Allows a user to override the TRANSIT_CHECKIN_INTERVAL_BLOCK event',
472 -- add the perm to the default circ admin group
473 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable)
475 pgt.id, perm.id, aout.depth, TRUE
477 permission.grp_tree pgt,
478 permission.perm_list perm,
479 actor.org_unit_type aout
481 pgt.name = 'Circulation Administrator' AND
482 aout.name = 'System' AND
483 perm.code IN ( 'TRANSIT_CHECKIN_INTERVAL_BLOCK.override' );
486 -- check whether patch can be applied
487 SELECT evergreen.upgrade_deps_block_check('0550', :eg_version);
489 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype ) VALUES (
490 'org.patron_opt_boundary',
492 'org.patron_opt_boundary',
493 'Circ: Patron Opt-In Boundary',
498 'org.patron_opt_boundary',
499 'This determines at which depth above which patrons must be opted in, and below which patrons will be assumed to be opted in.',
506 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype ) VALUES (
507 'org.patron_opt_default',
509 'org.patron_opt_default',
510 'Circ: Patron Opt-In Default',
515 'org.patron_opt_default',
516 'This is the default depth at which a patron is opted in; it is calculated as an org unit relative to the current workstation.',
523 -- Evergreen DB patch 0562.schema.copy_active_date.sql
528 -- check whether patch can be applied
529 SELECT evergreen.upgrade_deps_block_check('0562', :eg_version);
531 ALTER TABLE asset.copy
532 ADD COLUMN active_date TIMESTAMP WITH TIME ZONE;
534 ALTER TABLE auditor.asset_copy_history
535 ADD COLUMN active_date TIMESTAMP WITH TIME ZONE;
537 ALTER TABLE auditor.serial_unit_history
538 ADD COLUMN active_date TIMESTAMP WITH TIME ZONE;
540 ALTER TABLE config.copy_status
541 ADD COLUMN copy_active BOOL NOT NULL DEFAULT FALSE;
543 ALTER TABLE config.circ_matrix_weights
544 ADD COLUMN item_age NUMERIC(6,2) NOT NULL DEFAULT 0.0;
546 ALTER TABLE config.hold_matrix_weights
547 ADD COLUMN item_age NUMERIC(6,2) NOT NULL DEFAULT 0.0;
549 -- The two defaults above were to stop erroring on NOT NULL
551 ALTER TABLE config.circ_matrix_weights
552 ALTER COLUMN item_age DROP DEFAULT;
554 ALTER TABLE config.hold_matrix_weights
555 ALTER COLUMN item_age DROP DEFAULT;
557 ALTER TABLE config.circ_matrix_matchpoint
558 ADD COLUMN item_age INTERVAL;
560 ALTER TABLE config.hold_matrix_matchpoint
561 ADD COLUMN item_age INTERVAL;
563 --Removed dupe asset.acp_status_changed
565 CREATE OR REPLACE FUNCTION asset.acp_created()
566 RETURNS TRIGGER AS $$
568 IF NEW.active_date IS NULL AND NEW.status IN (SELECT id FROM config.copy_status WHERE copy_active = true) THEN
569 NEW.active_date := now();
571 IF NEW.status_changed_time IS NULL THEN
572 NEW.status_changed_time := now();
578 CREATE TRIGGER acp_created_trig
579 BEFORE INSERT ON asset.copy
580 FOR EACH ROW EXECUTE PROCEDURE asset.acp_created();
582 CREATE TRIGGER sunit_created_trig
583 BEFORE INSERT ON serial.unit
584 FOR EACH ROW EXECUTE PROCEDURE asset.acp_created();
586 --Removed dupe action.hold_request_permit_test
588 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$
590 cn_object asset.call_number%ROWTYPE;
591 rec_descriptor metabib.rec_descriptor%ROWTYPE;
592 cur_matchpoint config.circ_matrix_matchpoint%ROWTYPE;
593 matchpoint config.circ_matrix_matchpoint%ROWTYPE;
594 weights config.circ_matrix_weights%ROWTYPE;
596 my_item_age INTERVAL;
597 denominator NUMERIC(6,2);
599 result action.found_circ_matrix_matchpoint;
602 result.success = false;
605 SELECT INTO cn_object * FROM asset.call_number WHERE id = item_object.call_number;
606 SELECT INTO rec_descriptor * FROM metabib.rec_descriptor WHERE record = cn_object.record;
608 -- Pre-generate this so we only calc it once
609 IF user_object.dob IS NOT NULL THEN
610 SELECT INTO user_age age(user_object.dob);
614 SELECT INTO my_item_age age(coalesce(item_object.active_date, now()));
616 -- Grab the closest set circ weight setting.
617 SELECT INTO weights cw.*
618 FROM config.weight_assoc wa
619 JOIN config.circ_matrix_weights cw ON (cw.id = wa.circ_weights)
620 JOIN actor.org_unit_ancestors_distance( context_ou ) d ON (wa.org_unit = d.id)
625 -- No weights? Bad admin! Defaults to handle that anyway.
626 IF weights.id IS NULL THEN
628 weights.org_unit := 10.0;
629 weights.circ_modifier := 5.0;
630 weights.marc_type := 4.0;
631 weights.marc_form := 3.0;
632 weights.marc_bib_level := 2.0;
633 weights.marc_vr_format := 2.0;
634 weights.copy_circ_lib := 8.0;
635 weights.copy_owning_lib := 8.0;
636 weights.user_home_ou := 8.0;
637 weights.ref_flag := 1.0;
638 weights.juvenile_flag := 6.0;
639 weights.is_renewal := 7.0;
640 weights.usr_age_lower_bound := 0.0;
641 weights.usr_age_upper_bound := 0.0;
642 weights.item_age := 0.0;
645 -- Determine the max (expected) depth (+1) of the org tree and max depth of the permisson tree
646 -- If you break your org tree with funky parenting this may be wrong
647 -- 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
648 -- We use one denominator for all tree-based checks for when permission groups and org units have the same weighting
649 WITH all_distance(distance) AS (
650 SELECT depth AS distance FROM actor.org_unit_type
652 SELECT distance AS distance FROM permission.grp_ancestors_distance((SELECT id FROM permission.grp_tree WHERE parent IS NULL))
654 SELECT INTO denominator MAX(distance) + 1 FROM all_distance;
656 -- Loop over all the potential matchpoints
657 FOR cur_matchpoint IN
659 FROM config.circ_matrix_matchpoint m
660 /*LEFT*/ JOIN permission.grp_ancestors_distance( user_object.profile ) upgad ON m.grp = upgad.id
661 /*LEFT*/ JOIN actor.org_unit_ancestors_distance( context_ou ) ctoua ON m.org_unit = ctoua.id
662 LEFT JOIN actor.org_unit_ancestors_distance( cn_object.owning_lib ) cnoua ON m.copy_owning_lib = cnoua.id
663 LEFT JOIN actor.org_unit_ancestors_distance( item_object.circ_lib ) iooua ON m.copy_circ_lib = iooua.id
664 LEFT JOIN actor.org_unit_ancestors_distance( user_object.home_ou ) uhoua ON m.user_home_ou = uhoua.id
667 -- AND (m.grp IS NULL OR upgad.id IS NOT NULL) -- Optional Permission Group?
669 -- AND (m.org_unit IS NULL OR ctoua.id IS NOT NULL) -- Optional Org Unit?
670 AND (m.copy_owning_lib IS NULL OR cnoua.id IS NOT NULL)
671 AND (m.copy_circ_lib IS NULL OR iooua.id IS NOT NULL)
672 AND (m.user_home_ou IS NULL OR uhoua.id IS NOT NULL)
674 AND (m.is_renewal IS NULL OR m.is_renewal = renewal)
675 -- Static User Checks
676 AND (m.juvenile_flag IS NULL OR m.juvenile_flag = user_object.juvenile)
677 AND (m.usr_age_lower_bound IS NULL OR (user_age IS NOT NULL AND m.usr_age_lower_bound < user_age))
678 AND (m.usr_age_upper_bound IS NULL OR (user_age IS NOT NULL AND m.usr_age_upper_bound > user_age))
679 -- Static Item Checks
680 AND (m.circ_modifier IS NULL OR m.circ_modifier = item_object.circ_modifier)
681 AND (m.marc_type IS NULL OR m.marc_type = COALESCE(item_object.circ_as_type, rec_descriptor.item_type))
682 AND (m.marc_form IS NULL OR m.marc_form = rec_descriptor.item_form)
683 AND (m.marc_bib_level IS NULL OR m.marc_bib_level = rec_descriptor.bib_level)
684 AND (m.marc_vr_format IS NULL OR m.marc_vr_format = rec_descriptor.vr_format)
685 AND (m.ref_flag IS NULL OR m.ref_flag = item_object.ref)
686 AND (m.item_age IS NULL OR (my_item_age IS NOT NULL AND m.item_age > my_item_age))
689 CASE WHEN upgad.distance IS NOT NULL THEN 2^(2*weights.grp - (upgad.distance/denominator)) ELSE 0.0 END +
691 CASE WHEN ctoua.distance IS NOT NULL THEN 2^(2*weights.org_unit - (ctoua.distance/denominator)) ELSE 0.0 END +
692 CASE WHEN cnoua.distance IS NOT NULL THEN 2^(2*weights.copy_owning_lib - (cnoua.distance/denominator)) ELSE 0.0 END +
693 CASE WHEN iooua.distance IS NOT NULL THEN 2^(2*weights.copy_circ_lib - (iooua.distance/denominator)) ELSE 0.0 END +
694 CASE WHEN uhoua.distance IS NOT NULL THEN 2^(2*weights.user_home_ou - (uhoua.distance/denominator)) ELSE 0.0 END +
695 -- Circ Type -- Note: 4^x is equiv to 2^(2*x)
696 CASE WHEN m.is_renewal IS NOT NULL THEN 4^weights.is_renewal ELSE 0.0 END +
697 -- Static User Checks
698 CASE WHEN m.juvenile_flag IS NOT NULL THEN 4^weights.juvenile_flag ELSE 0.0 END +
699 CASE WHEN m.usr_age_lower_bound IS NOT NULL THEN 4^weights.usr_age_lower_bound ELSE 0.0 END +
700 CASE WHEN m.usr_age_upper_bound IS NOT NULL THEN 4^weights.usr_age_upper_bound ELSE 0.0 END +
701 -- Static Item Checks
702 CASE WHEN m.circ_modifier IS NOT NULL THEN 4^weights.circ_modifier ELSE 0.0 END +
703 CASE WHEN m.marc_type IS NOT NULL THEN 4^weights.marc_type ELSE 0.0 END +
704 CASE WHEN m.marc_form IS NOT NULL THEN 4^weights.marc_form ELSE 0.0 END +
705 CASE WHEN m.marc_vr_format IS NOT NULL THEN 4^weights.marc_vr_format ELSE 0.0 END +
706 CASE WHEN m.ref_flag IS NOT NULL THEN 4^weights.ref_flag ELSE 0.0 END +
707 -- Item age has a slight adjustment to weight based on value.
708 -- This should ensure that a shorter age limit comes first when all else is equal.
709 -- NOTE: This assumes that intervals will normally be in days.
710 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,
711 -- Final sort on id, so that if two rules have the same sorting in the previous sort they have a defined order
712 -- This prevents "we changed the table order by updating a rule, and we started getting different results"
715 -- Record the full matching row list
716 row_list := row_list || cur_matchpoint.id;
718 -- No matchpoint yet?
719 IF matchpoint.id IS NULL THEN
720 -- Take the entire matchpoint as a starting point
721 matchpoint := cur_matchpoint;
722 CONTINUE; -- No need to look at this row any more.
725 -- Incomplete matchpoint?
726 IF matchpoint.circulate IS NULL THEN
727 matchpoint.circulate := cur_matchpoint.circulate;
729 IF matchpoint.duration_rule IS NULL THEN
730 matchpoint.duration_rule := cur_matchpoint.duration_rule;
732 IF matchpoint.recurring_fine_rule IS NULL THEN
733 matchpoint.recurring_fine_rule := cur_matchpoint.recurring_fine_rule;
735 IF matchpoint.max_fine_rule IS NULL THEN
736 matchpoint.max_fine_rule := cur_matchpoint.max_fine_rule;
738 IF matchpoint.hard_due_date IS NULL THEN
739 matchpoint.hard_due_date := cur_matchpoint.hard_due_date;
741 IF matchpoint.total_copy_hold_ratio IS NULL THEN
742 matchpoint.total_copy_hold_ratio := cur_matchpoint.total_copy_hold_ratio;
744 IF matchpoint.available_copy_hold_ratio IS NULL THEN
745 matchpoint.available_copy_hold_ratio := cur_matchpoint.available_copy_hold_ratio;
747 IF matchpoint.renewals IS NULL THEN
748 matchpoint.renewals := cur_matchpoint.renewals;
750 IF matchpoint.grace_period IS NULL THEN
751 matchpoint.grace_period := cur_matchpoint.grace_period;
755 -- Check required fields
756 IF matchpoint.circulate IS NOT NULL AND
757 matchpoint.duration_rule IS NOT NULL AND
758 matchpoint.recurring_fine_rule IS NOT NULL AND
759 matchpoint.max_fine_rule IS NOT NULL THEN
760 -- All there? We have a completed match.
761 result.success := true;
764 -- Include the assembled matchpoint, even if it isn't complete
765 result.matchpoint := matchpoint;
767 -- Include (for debugging) the full list of matching rows
768 result.buildrows := row_list;
770 -- Hand the result back to caller
773 $func$ LANGUAGE plpgsql;
775 CREATE OR REPLACE FUNCTION action.find_hold_matrix_matchpoint(pickup_ou integer, request_ou integer, match_item bigint, match_user integer, match_requestor integer)
779 requestor_object actor.usr%ROWTYPE;
780 user_object actor.usr%ROWTYPE;
781 item_object asset.copy%ROWTYPE;
782 item_cn_object asset.call_number%ROWTYPE;
783 my_item_age INTERVAL;
784 rec_descriptor metabib.rec_descriptor%ROWTYPE;
785 matchpoint config.hold_matrix_matchpoint%ROWTYPE;
786 weights config.hold_matrix_weights%ROWTYPE;
787 denominator NUMERIC(6,2);
789 SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
790 SELECT INTO requestor_object * FROM actor.usr WHERE id = match_requestor;
791 SELECT INTO item_object * FROM asset.copy WHERE id = match_item;
792 SELECT INTO item_cn_object * FROM asset.call_number WHERE id = item_object.call_number;
793 SELECT INTO rec_descriptor * FROM metabib.rec_descriptor WHERE record = item_cn_object.record;
795 SELECT INTO my_item_age age(coalesce(item_object.active_date, now()));
797 -- The item's owner should probably be the one determining if the item is holdable
798 -- How to decide that is debatable. Decided to default to the circ library (where the item lives)
799 -- This flag will allow for setting it to the owning library (where the call number "lives")
800 PERFORM * FROM config.internal_flag WHERE name = 'circ.holds.weight_owner_not_circ' AND enabled;
802 -- Grab the closest set circ weight setting.
804 -- Default to circ library
805 SELECT INTO weights hw.*
806 FROM config.weight_assoc wa
807 JOIN config.hold_matrix_weights hw ON (hw.id = wa.hold_weights)
808 JOIN actor.org_unit_ancestors_distance( item_object.circ_lib ) d ON (wa.org_unit = d.id)
813 -- Flag is set, use owning library
814 SELECT INTO weights hw.*
815 FROM config.weight_assoc wa
816 JOIN config.hold_matrix_weights hw ON (hw.id = wa.hold_weights)
817 JOIN actor.org_unit_ancestors_distance( item_cn_object.owning_lib ) d ON (wa.org_unit = d.id)
823 -- No weights? Bad admin! Defaults to handle that anyway.
824 IF weights.id IS NULL THEN
825 weights.user_home_ou := 5.0;
826 weights.request_ou := 5.0;
827 weights.pickup_ou := 5.0;
828 weights.item_owning_ou := 5.0;
829 weights.item_circ_ou := 5.0;
830 weights.usr_grp := 7.0;
831 weights.requestor_grp := 8.0;
832 weights.circ_modifier := 4.0;
833 weights.marc_type := 3.0;
834 weights.marc_form := 2.0;
835 weights.marc_bib_level := 1.0;
836 weights.marc_vr_format := 1.0;
837 weights.juvenile_flag := 4.0;
838 weights.ref_flag := 0.0;
839 weights.item_age := 0.0;
842 -- Determine the max (expected) depth (+1) of the org tree and max depth of the permisson tree
843 -- If you break your org tree with funky parenting this may be wrong
844 -- 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
845 -- We use one denominator for all tree-based checks for when permission groups and org units have the same weighting
846 WITH all_distance(distance) AS (
847 SELECT depth AS distance FROM actor.org_unit_type
849 SELECT distance AS distance FROM permission.grp_ancestors_distance((SELECT id FROM permission.grp_tree WHERE parent IS NULL))
851 SELECT INTO denominator MAX(distance) + 1 FROM all_distance;
853 -- To ATTEMPT to make this work like it used to, make it reverse the user/requestor profile ids.
854 -- This may be better implemented as part of the upgrade script?
855 -- Set usr_grp = requestor_grp, requestor_grp = 1 or something when this flag is already set
856 -- Then remove this flag, of course.
857 PERFORM * FROM config.internal_flag WHERE name = 'circ.holds.usr_not_requestor' AND enabled;
860 -- Note: This, to me, is REALLY hacky. I put it in anyway.
861 -- If you can't tell, this is a single call swap on two variables.
862 SELECT INTO user_object.profile, requestor_object.profile
863 requestor_object.profile, user_object.profile;
866 -- Select the winning matchpoint into the matchpoint variable for returning
867 SELECT INTO matchpoint m.*
868 FROM config.hold_matrix_matchpoint m
869 /*LEFT*/ JOIN permission.grp_ancestors_distance( requestor_object.profile ) rpgad ON m.requestor_grp = rpgad.id
870 LEFT JOIN permission.grp_ancestors_distance( user_object.profile ) upgad ON m.usr_grp = upgad.id
871 LEFT JOIN actor.org_unit_ancestors_distance( pickup_ou ) puoua ON m.pickup_ou = puoua.id
872 LEFT JOIN actor.org_unit_ancestors_distance( request_ou ) rqoua ON m.request_ou = rqoua.id
873 LEFT JOIN actor.org_unit_ancestors_distance( item_cn_object.owning_lib ) cnoua ON m.item_owning_ou = cnoua.id
874 LEFT JOIN actor.org_unit_ancestors_distance( item_object.circ_lib ) iooua ON m.item_circ_ou = iooua.id
875 LEFT JOIN actor.org_unit_ancestors_distance( user_object.home_ou ) uhoua ON m.user_home_ou = uhoua.id
878 -- AND (m.requestor_grp IS NULL OR upgad.id IS NOT NULL) -- Optional Requestor Group?
879 AND (m.usr_grp IS NULL OR upgad.id IS NOT NULL)
881 AND (m.pickup_ou IS NULL OR (puoua.id IS NOT NULL AND (puoua.distance = 0 OR NOT m.strict_ou_match)))
882 AND (m.request_ou IS NULL OR (rqoua.id IS NOT NULL AND (rqoua.distance = 0 OR NOT m.strict_ou_match)))
883 AND (m.item_owning_ou IS NULL OR (cnoua.id IS NOT NULL AND (cnoua.distance = 0 OR NOT m.strict_ou_match)))
884 AND (m.item_circ_ou IS NULL OR (iooua.id IS NOT NULL AND (iooua.distance = 0 OR NOT m.strict_ou_match)))
885 AND (m.user_home_ou IS NULL OR (uhoua.id IS NOT NULL AND (uhoua.distance = 0 OR NOT m.strict_ou_match)))
886 -- Static User Checks
887 AND (m.juvenile_flag IS NULL OR m.juvenile_flag = user_object.juvenile)
888 -- Static Item Checks
889 AND (m.circ_modifier IS NULL OR m.circ_modifier = item_object.circ_modifier)
890 AND (m.marc_type IS NULL OR m.marc_type = COALESCE(item_object.circ_as_type, rec_descriptor.item_type))
891 AND (m.marc_form IS NULL OR m.marc_form = rec_descriptor.item_form)
892 AND (m.marc_bib_level IS NULL OR m.marc_bib_level = rec_descriptor.bib_level)
893 AND (m.marc_vr_format IS NULL OR m.marc_vr_format = rec_descriptor.vr_format)
894 AND (m.ref_flag IS NULL OR m.ref_flag = item_object.ref)
895 AND (m.item_age IS NULL OR (my_item_age IS NOT NULL AND m.item_age > my_item_age))
898 CASE WHEN rpgad.distance IS NOT NULL THEN 2^(2*weights.requestor_grp - (rpgad.distance/denominator)) ELSE 0.0 END +
899 CASE WHEN upgad.distance IS NOT NULL THEN 2^(2*weights.usr_grp - (upgad.distance/denominator)) ELSE 0.0 END +
901 CASE WHEN puoua.distance IS NOT NULL THEN 2^(2*weights.pickup_ou - (puoua.distance/denominator)) ELSE 0.0 END +
902 CASE WHEN rqoua.distance IS NOT NULL THEN 2^(2*weights.request_ou - (rqoua.distance/denominator)) ELSE 0.0 END +
903 CASE WHEN cnoua.distance IS NOT NULL THEN 2^(2*weights.item_owning_ou - (cnoua.distance/denominator)) ELSE 0.0 END +
904 CASE WHEN iooua.distance IS NOT NULL THEN 2^(2*weights.item_circ_ou - (iooua.distance/denominator)) ELSE 0.0 END +
905 CASE WHEN uhoua.distance IS NOT NULL THEN 2^(2*weights.user_home_ou - (uhoua.distance/denominator)) ELSE 0.0 END +
906 -- Static User Checks -- Note: 4^x is equiv to 2^(2*x)
907 CASE WHEN m.juvenile_flag IS NOT NULL THEN 4^weights.juvenile_flag ELSE 0.0 END +
908 -- Static Item Checks
909 CASE WHEN m.circ_modifier IS NOT NULL THEN 4^weights.circ_modifier ELSE 0.0 END +
910 CASE WHEN m.marc_type IS NOT NULL THEN 4^weights.marc_type ELSE 0.0 END +
911 CASE WHEN m.marc_form IS NOT NULL THEN 4^weights.marc_form ELSE 0.0 END +
912 CASE WHEN m.marc_vr_format IS NOT NULL THEN 4^weights.marc_vr_format ELSE 0.0 END +
913 CASE WHEN m.ref_flag IS NOT NULL THEN 4^weights.ref_flag ELSE 0.0 END +
914 -- Item age has a slight adjustment to weight based on value.
915 -- This should ensure that a shorter age limit comes first when all else is equal.
916 -- NOTE: This assumes that intervals will normally be in days.
917 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,
918 -- Final sort on id, so that if two rules have the same sorting in the previous sort they have a defined order
919 -- This prevents "we changed the table order by updating a rule, and we started getting different results"
922 -- Return just the ID for now
923 RETURN matchpoint.id;
925 $func$ LANGUAGE 'plpgsql';
927 DROP INDEX IF EXISTS config.ccmm_once_per_paramset;
929 DROP INDEX IF EXISTS config.chmm_once_per_paramset;
931 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;
933 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;
935 UPDATE config.copy_status SET copy_active = true WHERE id IN (0, 1, 7, 8, 10, 12, 15);
937 INSERT into config.org_unit_setting_type
938 ( name, label, description, datatype ) VALUES
939 ( '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');
941 -- Assume create date when item is in status we would update active date for anyway
942 UPDATE asset.copy SET active_date = create_date WHERE status IN (SELECT id FROM config.copy_status WHERE copy_active = true);
944 -- Assume create date for any item with circs
945 UPDATE asset.copy SET active_date = create_date WHERE id IN (SELECT id FROM extend_reporter.full_circ_count WHERE circ_count > 0);
947 -- Assume create date for status change time while we are at it. Because being created WAS a change in status.
948 UPDATE asset.copy SET status_changed_time = create_date WHERE status_changed_time IS NULL;
950 -- Evergreen DB patch 0564.data.delete_empty_volume.sql
952 -- New org setting cat.volume.delete_on_empty
955 -- check whether patch can be applied
956 SELECT evergreen.upgrade_deps_block_check('0564', :eg_version);
958 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype )
960 'cat.volume.delete_on_empty',
961 oils_i18n_gettext('cat.volume.delete_on_empty', 'Cat: Delete volume with last copy', 'coust', 'label'),
962 oils_i18n_gettext('cat.volume.delete_on_empty', 'Automatically delete a volume when the last linked copy is deleted', 'coust', 'description'),
967 -- Evergreen DB patch 0565.schema.action-trigger.event_definition.hold-cancel-no-target-notification.sql
969 -- New action trigger event definition: Hold Cancelled (No Target) Email Notification
972 -- check whether patch can be applied
973 SELECT evergreen.upgrade_deps_block_check('0565', :eg_version);
975 INSERT INTO action_trigger.event_definition (id, active, owner, name, hook, validator, reactor, delay, delay_field, group_field, template)
976 VALUES (38, FALSE, 1,
977 'Hold Cancelled (No Target) Email Notification',
978 'hold_request.cancel.expire_no_target',
979 'HoldIsCancelled', 'SendEmail', '30 minutes', 'cancel_time', 'usr',
982 [%- user = target.0.usr -%]
983 To: [%- params.recipient_email || user.email %]
984 From: [%- params.sender_email || default_sender %]
985 Subject: Hold Request Cancelled
987 Dear [% user.family_name %], [% user.first_given_name %]
988 The following holds were cancelled because no items were found to fullfil the hold.
990 [% FOR hold IN target %]
991 Title: [% hold.bib_rec.bib_record.simple_record.title %]
992 Author: [% hold.bib_rec.bib_record.simple_record.author %]
993 Library: [% hold.pickup_lib.name %]
994 Request Date: [% date.format(helpers.format_date(hold.rrequest_time), '%Y-%m-%d') %]
999 INSERT INTO action_trigger.environment (event_def, path) VALUES
1002 (38, 'bib_rec.bib_record.simple_record');
1004 -- Evergreen DB patch XXXX.data.ou_setting_generate_overdue_on_lost.sql.sql
1006 -- check whether patch can be applied
1007 SELECT evergreen.upgrade_deps_block_check('0567', :eg_version);
1009 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype ) VALUES (
1010 'circ.lost.generate_overdue_on_checkin',
1012 'circ.lost.generate_overdue_on_checkin',
1013 'Circ: Lost Checkin Generates New Overdues',
1018 'circ.lost.generate_overdue_on_checkin',
1019 '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',
1026 -- Evergreen DB patch 0572.vandelay-record-matching-and-quality.sql
1030 -- check whether patch can be applied
1031 SELECT evergreen.upgrade_deps_block_check('0572', :eg_version);
1033 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;
1035 CREATE TABLE vandelay.match_set (
1036 id SERIAL PRIMARY KEY,
1038 owner INT NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE,
1039 mtype TEXT NOT NULL DEFAULT 'biblio', -- 'biblio','authority','mfhd'?, others?
1040 CONSTRAINT name_once_per_owner_mtype UNIQUE (name, owner, mtype)
1043 -- Table to define match points, either FF via SVF or tag+subfield
1044 CREATE TABLE vandelay.match_set_point (
1045 id SERIAL PRIMARY KEY,
1046 match_set INT REFERENCES vandelay.match_set (id) ON DELETE CASCADE,
1047 parent INT REFERENCES vandelay.match_set_point (id),
1048 bool_op TEXT CHECK (bool_op IS NULL OR (bool_op IN ('AND','OR','NOT'))),
1049 svf TEXT REFERENCES config.record_attr_definition (name),
1052 negate BOOL DEFAULT FALSE,
1053 quality INT NOT NULL DEFAULT 1, -- higher is better
1054 CONSTRAINT vmsp_need_a_subfield_with_a_tag CHECK ((tag IS NOT NULL AND subfield IS NOT NULL) OR tag IS NULL),
1055 CONSTRAINT vmsp_need_a_tag_or_a_ff_or_a_bo CHECK (
1056 (tag IS NOT NULL AND svf IS NULL AND bool_op IS NULL) OR
1057 (tag IS NULL AND svf IS NOT NULL AND bool_op IS NULL) OR
1058 (tag IS NULL AND svf IS NULL AND bool_op IS NOT NULL)
1062 CREATE TABLE vandelay.match_set_quality (
1063 id SERIAL PRIMARY KEY,
1064 match_set INT NOT NULL REFERENCES vandelay.match_set (id) ON DELETE CASCADE,
1065 svf TEXT REFERENCES config.record_attr_definition,
1068 value TEXT NOT NULL,
1069 quality INT NOT NULL DEFAULT 1, -- higher is better
1070 CONSTRAINT vmsq_need_a_subfield_with_a_tag CHECK ((tag IS NOT NULL AND subfield IS NOT NULL) OR tag IS NULL),
1071 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))
1073 CREATE UNIQUE INDEX vmsq_def_once_per_set ON vandelay.match_set_quality (match_set, COALESCE(tag,''), COALESCE(subfield,''), COALESCE(svf,''), value);
1077 ALTER TABLE vandelay.queue ADD COLUMN match_set INT REFERENCES vandelay.match_set (id) ON UPDATE CASCADE ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
1078 ALTER TABLE vandelay.queued_record ADD COLUMN quality INT NOT NULL DEFAULT 0;
1079 ALTER TABLE vandelay.bib_attr_definition DROP COLUMN ident;
1081 CREATE TABLE vandelay.import_error (
1082 code TEXT PRIMARY KEY,
1083 description TEXT NOT NULL -- i18n
1086 ALTER TABLE vandelay.queued_bib_record
1087 ADD COLUMN import_error TEXT REFERENCES vandelay.import_error (code) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
1088 ADD COLUMN error_detail TEXT;
1090 ALTER TABLE vandelay.bib_match
1091 DROP COLUMN field_type,
1092 DROP COLUMN matched_attr,
1093 ADD COLUMN quality INT NOT NULL DEFAULT 1,
1094 ADD COLUMN match_score INT NOT NULL DEFAULT 0;
1096 ALTER TABLE vandelay.import_item
1097 ADD COLUMN import_error TEXT REFERENCES vandelay.import_error (code) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
1098 ADD COLUMN error_detail TEXT,
1099 ADD COLUMN imported_as BIGINT REFERENCES asset.copy (id) DEFERRABLE INITIALLY DEFERRED,
1100 ADD COLUMN import_time TIMESTAMP WITH TIME ZONE;
1102 ALTER TABLE vandelay.merge_profile ADD COLUMN lwm_ratio NUMERIC;
1104 CREATE OR REPLACE FUNCTION vandelay.marc21_record_type( marc TEXT ) RETURNS config.marc21_rec_type_map AS $func$
1111 retval config.marc21_rec_type_map%ROWTYPE;
1113 ldr := oils_xpath_string( '//*[local-name()="leader"]', marc );
1115 IF ldr IS NULL OR ldr = '' THEN
1116 SELECT * INTO retval FROM config.marc21_rec_type_map WHERE code = 'BKS';
1120 SELECT * INTO tval_rec FROM config.marc21_ff_pos_map WHERE fixed_field = 'Type' LIMIT 1; -- They're all the same
1121 SELECT * INTO bval_rec FROM config.marc21_ff_pos_map WHERE fixed_field = 'BLvl' LIMIT 1; -- They're all the same
1124 tval := SUBSTRING( ldr, tval_rec.start_pos + 1, tval_rec.length );
1125 bval := SUBSTRING( ldr, bval_rec.start_pos + 1, bval_rec.length );
1127 -- RAISE NOTICE 'type %, blvl %, ldr %', tval, bval, ldr;
1129 SELECT * INTO retval FROM config.marc21_rec_type_map WHERE type_val LIKE '%' || tval || '%' AND blvl_val LIKE '%' || bval || '%';
1132 IF retval.code IS NULL THEN
1133 SELECT * INTO retval FROM config.marc21_rec_type_map WHERE code = 'BKS';
1138 $func$ LANGUAGE PLPGSQL;
1140 CREATE OR REPLACE FUNCTION vandelay.marc21_extract_fixed_field( marc TEXT, ff TEXT ) RETURNS TEXT AS $func$
1147 rtype := (vandelay.marc21_record_type( marc )).code;
1148 FOR ff_pos IN SELECT * FROM config.marc21_ff_pos_map WHERE fixed_field = ff AND rec_type = rtype ORDER BY tag DESC LOOP
1149 IF ff_pos.tag = 'ldr' THEN
1150 val := oils_xpath_string('//*[local-name()="leader"]', marc);
1151 IF val IS NOT NULL THEN
1152 val := SUBSTRING( val, ff_pos.start_pos + 1, ff_pos.length );
1156 FOR tag_data IN SELECT value FROM UNNEST( oils_xpath( '//*[@tag="' || UPPER(ff_pos.tag) || '"]/text()', marc ) ) x(value) LOOP
1157 val := SUBSTRING( tag_data.value, ff_pos.start_pos + 1, ff_pos.length );
1161 val := REPEAT( ff_pos.default_val, ff_pos.length );
1167 $func$ LANGUAGE PLPGSQL;
1169 CREATE OR REPLACE FUNCTION vandelay.marc21_extract_all_fixed_fields( marc TEXT ) RETURNS SETOF biblio.record_ff_map AS $func$
1174 output biblio.record_ff_map%ROWTYPE;
1176 rtype := (vandelay.marc21_record_type( marc )).code;
1178 FOR ff_pos IN SELECT * FROM config.marc21_ff_pos_map WHERE rec_type = rtype ORDER BY tag DESC LOOP
1179 output.ff_name := ff_pos.fixed_field;
1180 output.ff_value := NULL;
1182 IF ff_pos.tag = 'ldr' THEN
1183 output.ff_value := oils_xpath_string('//*[local-name()="leader"]', marc);
1184 IF output.ff_value IS NOT NULL THEN
1185 output.ff_value := SUBSTRING( output.ff_value, ff_pos.start_pos + 1, ff_pos.length );
1187 output.ff_value := NULL;
1190 FOR tag_data IN SELECT value FROM UNNEST( oils_xpath( '//*[@tag="' || UPPER(ff_pos.tag) || '"]/text()', marc ) ) x(value) LOOP
1191 output.ff_value := SUBSTRING( tag_data, ff_pos.start_pos + 1, ff_pos.length );
1192 IF output.ff_value IS NULL THEN output.ff_value := REPEAT( ff_pos.default_val, ff_pos.length ); END IF;
1194 output.ff_value := NULL;
1202 $func$ LANGUAGE PLPGSQL;
1204 CREATE OR REPLACE FUNCTION vandelay.marc21_physical_characteristics( marc TEXT) RETURNS SETOF biblio.marc21_physical_characteristics AS $func$
1208 ptype config.marc21_physical_characteristic_type_map%ROWTYPE;
1209 psf config.marc21_physical_characteristic_subfield_map%ROWTYPE;
1210 pval config.marc21_physical_characteristic_value_map%ROWTYPE;
1211 retval biblio.marc21_physical_characteristics%ROWTYPE;
1214 _007 := oils_xpath_string( '//*[@tag="007"]', marc );
1216 IF _007 IS NOT NULL AND _007 <> '' THEN
1217 SELECT * INTO ptype FROM config.marc21_physical_characteristic_type_map WHERE ptype_key = SUBSTRING( _007, 1, 1 );
1219 IF ptype.ptype_key IS NOT NULL THEN
1220 FOR psf IN SELECT * FROM config.marc21_physical_characteristic_subfield_map WHERE ptype_key = ptype.ptype_key LOOP
1221 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 );
1223 IF pval.id IS NOT NULL THEN
1226 retval.ptype := ptype.ptype_key;
1227 retval.subfield := psf.id;
1228 retval.value := pval.id;
1238 $func$ LANGUAGE PLPGSQL;
1240 CREATE TYPE vandelay.flat_marc AS ( tag CHAR(3), ind1 TEXT, ind2 TEXT, subfield TEXT, value TEXT );
1241 CREATE OR REPLACE FUNCTION vandelay.flay_marc ( TEXT ) RETURNS SETOF vandelay.flat_marc AS $func$
1244 use MARC::File::XML (BinaryEncoding => 'UTF-8');
1248 MARC::Charset->assume_unicode(1);
1251 my $r = MARC::Record->new_from_xml( $xml );
1253 return_next( { tag => 'LDR', value => $r->leader } );
1255 for my $f ( $r->fields ) {
1256 if ($f->is_control_field) {
1257 return_next({ tag => $f->tag, value => $f->data });
1259 for my $s ($f->subfields) {
1262 ind1 => $f->indicator(1),
1263 ind2 => $f->indicator(2),
1264 subfield => $s->[0],
1268 if ( $f->tag eq '245' and $s->[0] eq 'a' ) {
1269 my $trim = $f->indicator(2) || 0;
1272 ind1 => $f->indicator(1),
1273 ind2 => $f->indicator(2),
1275 value => substr( $s->[1], $trim )
1284 $func$ LANGUAGE PLPERLU;
1286 CREATE OR REPLACE FUNCTION vandelay.flatten_marc ( marc TEXT ) RETURNS SETOF vandelay.flat_marc AS $func$
1288 output vandelay.flat_marc%ROWTYPE;
1291 FOR field IN SELECT * FROM vandelay.flay_marc( marc ) LOOP
1292 output.ind1 := field.ind1;
1293 output.ind2 := field.ind2;
1294 output.tag := field.tag;
1295 output.subfield := field.subfield;
1296 IF field.subfield IS NOT NULL AND field.tag NOT IN ('020','022','024') THEN -- exclude standard numbers and control fields
1297 output.value := naco_normalize(field.value, field.subfield);
1299 output.value := field.value;
1302 CONTINUE WHEN output.value IS NULL;
1307 $func$ LANGUAGE PLPGSQL;
1309 CREATE OR REPLACE FUNCTION vandelay.extract_rec_attrs ( xml TEXT, attr_defs TEXT[]) RETURNS hstore AS $_$
1311 transformed_xml TEXT;
1314 xfrm config.xml_transform%ROWTYPE;
1316 new_attrs HSTORE := ''::HSTORE;
1317 attr_def config.record_attr_definition%ROWTYPE;
1320 FOR attr_def IN SELECT * FROM config.record_attr_definition WHERE name IN (SELECT * FROM UNNEST(attr_defs)) ORDER BY format LOOP
1322 IF attr_def.tag IS NOT NULL THEN -- tag (and optional subfield list) selection
1323 SELECT ARRAY_TO_STRING(ARRAY_ACCUM(x.value), COALESCE(attr_def.joiner,' ')) INTO attr_value
1324 FROM vandelay.flatten_marc(xml) AS x
1325 WHERE x.tag LIKE attr_def.tag
1327 WHEN attr_def.sf_list IS NOT NULL
1328 THEN POSITION(x.subfield IN attr_def.sf_list) > 0
1335 ELSIF attr_def.fixed_field IS NOT NULL THEN -- a named fixed field, see config.marc21_ff_pos_map.fixed_field
1336 attr_value := vandelay.marc21_extract_fixed_field(xml, attr_def.fixed_field);
1338 ELSIF attr_def.xpath IS NOT NULL THEN -- and xpath expression
1340 SELECT INTO xfrm * FROM config.xml_transform WHERE name = attr_def.format;
1342 -- See if we can skip the XSLT ... it's expensive
1343 IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
1344 -- Can't skip the transform
1345 IF xfrm.xslt <> '---' THEN
1346 transformed_xml := oils_xslt_process(xml,xfrm.xslt);
1348 transformed_xml := xml;
1351 prev_xfrm := xfrm.name;
1354 IF xfrm.name IS NULL THEN
1355 -- just grab the marcxml (empty) transform
1356 SELECT INTO xfrm * FROM config.xml_transform WHERE xslt = '---' LIMIT 1;
1357 prev_xfrm := xfrm.name;
1360 attr_value := oils_xpath_string(attr_def.xpath, transformed_xml, COALESCE(attr_def.joiner,' '), ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]);
1362 ELSIF attr_def.phys_char_sf IS NOT NULL THEN -- a named Physical Characteristic, see config.marc21_physical_characteristic_*_map
1363 SELECT m.value::TEXT INTO attr_value
1364 FROM vandelay.marc21_physical_characteristics(xml) v
1365 JOIN config.marc21_physical_characteristic_value_map m ON (m.id = v.value)
1366 WHERE v.subfield = attr_def.phys_char_sf
1367 LIMIT 1; -- Just in case ...
1371 -- apply index normalizers to attr_value
1373 SELECT n.func AS func,
1374 n.param_count AS param_count,
1376 FROM config.index_normalizer n
1377 JOIN config.record_attr_index_norm_map m ON (m.norm = n.id)
1378 WHERE attr = attr_def.name
1380 EXECUTE 'SELECT ' || normalizer.func || '(' ||
1381 quote_literal( attr_value ) ||
1383 WHEN normalizer.param_count > 0
1384 THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
1387 ')' INTO attr_value;
1391 -- Add the new value to the hstore
1392 new_attrs := new_attrs || hstore( attr_def.name, attr_value );
1398 $_$ LANGUAGE PLPGSQL;
1400 CREATE OR REPLACE FUNCTION vandelay.extract_rec_attrs ( xml TEXT ) RETURNS hstore AS $_$
1401 SELECT vandelay.extract_rec_attrs( $1, (SELECT ARRAY_ACCUM(name) FROM config.record_attr_definition));
1404 -- Everything between this comment and the beginning of the definition of
1405 -- vandelay.match_bib_record() is strictly in service of that function.
1406 CREATE TYPE vandelay.match_set_test_result AS (record BIGINT, quality INTEGER);
1408 CREATE OR REPLACE FUNCTION vandelay.match_set_test_marcxml(
1409 match_set_id INTEGER, record_xml TEXT
1410 ) RETURNS SETOF vandelay.match_set_test_result AS $$
1421 tags_rstore := vandelay.flatten_marc_hstore(record_xml);
1422 svf_rstore := vandelay.extract_rec_attrs(record_xml);
1424 CREATE TEMPORARY TABLE _vandelay_tmp_qrows (q INTEGER);
1425 CREATE TEMPORARY TABLE _vandelay_tmp_jrows (j TEXT);
1427 -- generate the where clause and return that directly (into wq), and as
1428 -- a side-effect, populate the _vandelay_tmp_[qj]rows tables.
1429 wq := vandelay.get_expr_from_match_set(match_set_id);
1431 query_ := 'SELECT bre.id AS record, ';
1433 -- qrows table is for the quality bits we add to the SELECT clause
1434 SELECT ARRAY_TO_STRING(
1435 ARRAY_ACCUM('COALESCE(n' || q::TEXT || '.quality, 0)'), ' + '
1436 ) INTO coal FROM _vandelay_tmp_qrows;
1438 -- our query string so far is the SELECT clause and the inital FROM.
1439 -- no JOINs yet nor the WHERE clause
1440 query_ := query_ || coal || ' AS quality ' || E'\n' ||
1441 'FROM biblio.record_entry bre ';
1443 -- jrows table is for the joins we must make (and the real text conditions)
1444 SELECT ARRAY_TO_STRING(ARRAY_ACCUM(j), E'\n') INTO joins
1445 FROM _vandelay_tmp_jrows;
1447 -- add those joins and the where clause to our query.
1448 query_ := query_ || joins || E'\n' || 'WHERE ' || wq || ' AND not bre.deleted';
1450 -- this will return rows of record,quality
1451 FOR rec IN EXECUTE query_ USING tags_rstore, svf_rstore LOOP
1455 DROP TABLE _vandelay_tmp_qrows;
1456 DROP TABLE _vandelay_tmp_jrows;
1460 $$ LANGUAGE PLPGSQL;
1462 CREATE OR REPLACE FUNCTION vandelay.flatten_marc_hstore(
1464 ) RETURNS HSTORE AS $$
1468 ARRAY_ACCUM(tag || (COALESCE(subfield, ''))),
1472 SELECT tag, subfield, ARRAY_ACCUM(value)::TEXT AS value
1473 FROM vandelay.flatten_marc(record_xml)
1474 GROUP BY tag, subfield ORDER BY tag, subfield
1478 $$ LANGUAGE PLPGSQL;
1480 CREATE OR REPLACE FUNCTION vandelay.get_expr_from_match_set(
1481 match_set_id INTEGER
1482 ) RETURNS TEXT AS $$
1484 root vandelay.match_set_point;
1486 SELECT * INTO root FROM vandelay.match_set_point
1487 WHERE parent IS NULL AND match_set = match_set_id;
1489 RETURN vandelay.get_expr_from_match_set_point(root);
1491 $$ LANGUAGE PLPGSQL;
1493 CREATE OR REPLACE FUNCTION vandelay.get_expr_from_match_set_point(
1494 node vandelay.match_set_point
1495 ) RETURNS TEXT AS $$
1501 child vandelay.match_set_point;
1503 SELECT ARRAY_ACCUM(id) INTO children FROM vandelay.match_set_point
1504 WHERE parent = node.id;
1506 IF ARRAY_LENGTH(children, 1) > 0 THEN
1507 this_op := vandelay._get_expr_render_one(node);
1510 WHILE children[i] IS NOT NULL LOOP
1511 SELECT * INTO child FROM vandelay.match_set_point
1512 WHERE id = children[i];
1514 q := q || ' ' || this_op || ' ';
1517 q := q || vandelay.get_expr_from_match_set_point(child);
1521 ELSIF node.bool_op IS NULL THEN
1522 PERFORM vandelay._get_expr_push_qrow(node);
1523 PERFORM vandelay._get_expr_push_jrow(node);
1524 RETURN vandelay._get_expr_render_one(node);
1529 $$ LANGUAGE PLPGSQL;
1531 CREATE OR REPLACE FUNCTION vandelay._get_expr_push_qrow(
1532 node vandelay.match_set_point
1533 ) RETURNS VOID AS $$
1536 INSERT INTO _vandelay_tmp_qrows (q) VALUES (node.id);
1538 $$ LANGUAGE PLPGSQL;
1540 CREATE OR REPLACE FUNCTION vandelay._get_expr_push_jrow(
1541 node vandelay.match_set_point
1542 ) RETURNS VOID AS $$
1555 IF node.tag IS NOT NULL THEN
1557 IF node.subfield IS NOT NULL THEN
1558 tagkey := tagkey || node.subfield;
1562 my_alias := 'n' || node.id::TEXT;
1564 jrow := 'LEFT JOIN (SELECT *, ' || node.quality ||
1565 ' AS quality FROM metabib.';
1566 IF node.tag IS NOT NULL THEN
1567 jrow := jrow || 'full_rec) ' || my_alias || ' ON (' ||
1568 my_alias || '.record = bre.id AND ' || my_alias || '.tag = ''' ||
1570 IF node.subfield IS NOT NULL THEN
1571 jrow := jrow || ' AND ' || my_alias || '.subfield = ''' ||
1572 node.subfield || '''';
1574 jrow := jrow || ' AND (' || my_alias || '.value ' || op ||
1575 ' ANY(($1->''' || tagkey || ''')::TEXT[])))';
1577 jrow := jrow || 'record_attr) ' || my_alias || ' ON (' ||
1578 my_alias || '.id = bre.id AND (' ||
1579 my_alias || '.attrs->''' || node.svf ||
1580 ''' ' || op || ' $2->''' || node.svf || '''))';
1582 INSERT INTO _vandelay_tmp_jrows (j) VALUES (jrow);
1584 $$ LANGUAGE PLPGSQL;
1586 CREATE OR REPLACE FUNCTION vandelay._get_expr_render_one(
1587 node vandelay.match_set_point
1588 ) RETURNS TEXT AS $$
1592 IF node.bool_op IS NOT NULL THEN
1593 RETURN node.bool_op;
1595 RETURN '(n' || node.id::TEXT || '.id IS NOT NULL)';
1598 $$ LANGUAGE PLPGSQL;
1600 CREATE OR REPLACE FUNCTION vandelay.match_bib_record() RETURNS TRIGGER AS $func$
1602 incoming_existing_id TEXT;
1603 test_result vandelay.match_set_test_result%ROWTYPE;
1607 IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
1611 DELETE FROM vandelay.bib_match WHERE queued_record = NEW.id;
1613 SELECT q.match_set INTO match_set FROM vandelay.bib_queue q WHERE q.id = NEW.queue;
1615 IF match_set IS NOT NULL THEN
1616 NEW.quality := vandelay.measure_record_quality( NEW.marc, match_set );
1619 -- Perfect matches on 901$c exit early with a match with high quality.
1620 incoming_existing_id :=
1621 oils_xpath_string('//*[@tag="901"]/*[@code="c"][1]', NEW.marc);
1623 IF incoming_existing_id IS NOT NULL AND incoming_existing_id != '' THEN
1624 SELECT id INTO tmp_rec FROM biblio.record_entry WHERE id = incoming_existing_id::bigint;
1625 IF tmp_rec IS NOT NULL THEN
1626 INSERT INTO vandelay.bib_match (queued_record, eg_record, match_score, quality)
1631 -- note: no match_set means quality==0
1632 vandelay.measure_record_quality( b.marc, match_set )
1633 FROM biblio.record_entry b
1634 WHERE id = incoming_existing_id::bigint;
1638 IF match_set IS NULL THEN
1642 FOR test_result IN SELECT * FROM
1643 vandelay.match_set_test_marcxml(match_set, NEW.marc) LOOP
1645 INSERT INTO vandelay.bib_match ( queued_record, eg_record, match_score, quality )
1649 test_result.quality,
1650 vandelay.measure_record_quality( b.marc, match_set )
1651 FROM biblio.record_entry b
1652 WHERE id = test_result.record;
1658 $func$ LANGUAGE PLPGSQL;
1660 CREATE OR REPLACE FUNCTION vandelay.measure_record_quality ( xml TEXT, match_set_id INT ) RETURNS INT AS $_$
1664 test vandelay.match_set_quality%ROWTYPE;
1667 FOR test IN SELECT * FROM vandelay.match_set_quality WHERE match_set = match_set_id LOOP
1668 IF test.tag IS NOT NULL THEN
1669 FOR rvalue IN SELECT value FROM vandelay.flatten_marc( xml ) WHERE tag = test.tag AND subfield = test.subfield LOOP
1670 IF test.value = rvalue THEN
1671 out_q := out_q + test.quality;
1675 IF test.value = vandelay.extract_rec_attrs(xml, ARRAY[test.svf]) -> test.svf THEN
1676 out_q := out_q + test.quality;
1683 $_$ LANGUAGE PLPGSQL;
1686 CREATE OR REPLACE FUNCTION vandelay.overlay_bib_record ( import_id BIGINT, eg_id BIGINT, merge_profile_id INT ) RETURNS BOOL AS $$
1688 merge_profile vandelay.merge_profile%ROWTYPE;
1689 dyn_profile vandelay.compile_profile%ROWTYPE;
1699 SELECT q.marc INTO v_marc
1700 FROM vandelay.queued_record q
1701 JOIN vandelay.bib_match m ON (m.queued_record = q.id AND q.id = import_id)
1704 IF v_marc IS NULL THEN
1705 -- RAISE NOTICE 'no marc for vandelay or bib record';
1709 IF vandelay.template_overlay_bib_record( v_marc, eg_id, merge_profile_id) THEN
1710 UPDATE vandelay.queued_bib_record
1711 SET imported_as = eg_id,
1713 WHERE id = import_id;
1715 editor_string := (oils_xpath('//*[@tag="905"]/*[@code="u"]/text()',v_marc))[1];
1717 IF editor_string IS NOT NULL AND editor_string <> '' THEN
1718 SELECT usr INTO editor_id FROM actor.card WHERE barcode = editor_string;
1720 IF editor_id IS NULL THEN
1721 SELECT id INTO editor_id FROM actor.usr WHERE usrname = editor_string;
1724 IF editor_id IS NOT NULL THEN
1725 UPDATE biblio.record_entry SET editor = editor_id WHERE id = eg_id;
1732 -- RAISE NOTICE 'update of biblio.record_entry failed';
1737 $$ LANGUAGE PLPGSQL;
1740 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 $$
1743 lwm_ratio_value NUMERIC;
1746 lwm_ratio_value := COALESCE(lwm_ratio_value_p, 0.0);
1748 PERFORM * FROM vandelay.queued_bib_record WHERE import_time IS NOT NULL AND id = import_id;
1751 -- RAISE NOTICE 'already imported, cannot auto-overlay'
1755 SELECT m.eg_record INTO eg_id
1756 FROM vandelay.bib_match m
1757 JOIN vandelay.queued_bib_record qr ON (m.queued_record = qr.id)
1758 JOIN vandelay.bib_queue q ON (qr.queue = q.id)
1759 JOIN biblio.record_entry r ON (r.id = m.eg_record)
1760 WHERE m.queued_record = import_id
1761 AND qr.quality::NUMERIC / COALESCE(NULLIF(m.quality,0),1)::NUMERIC >= lwm_ratio_value
1762 ORDER BY m.match_score DESC, -- required match score
1763 qr.quality::NUMERIC / COALESCE(NULLIF(m.quality,0),1)::NUMERIC DESC, -- quality tie breaker
1764 m.id -- when in doubt, use the first match
1767 IF eg_id IS NULL THEN
1768 -- RAISE NOTICE 'incoming record is not of high enough quality';
1772 RETURN vandelay.overlay_bib_record( import_id, eg_id, merge_profile_id );
1774 $$ LANGUAGE PLPGSQL;
1776 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 $$
1779 lwm_ratio_value NUMERIC;
1782 lwm_ratio_value := COALESCE(lwm_ratio_value_p, 0.0);
1784 PERFORM * FROM vandelay.queued_bib_record WHERE import_time IS NOT NULL AND id = import_id;
1787 -- RAISE NOTICE 'already imported, cannot auto-overlay'
1791 SELECT m.eg_record INTO eg_id
1792 FROM vandelay.bib_match m
1793 JOIN vandelay.queued_bib_record qr ON (m.queued_record = qr.id)
1794 JOIN vandelay.bib_queue q ON (qr.queue = q.id)
1795 JOIN biblio.record_entry r ON (r.id = m.eg_record)
1796 WHERE m.queued_record = import_id
1797 AND qr.quality::NUMERIC / COALESCE(NULLIF(m.quality,0),1)::NUMERIC >= lwm_ratio_value
1798 ORDER BY m.match_score DESC, -- required match score
1799 qr.quality::NUMERIC / COALESCE(NULLIF(m.quality,0),1)::NUMERIC DESC, -- quality tie breaker
1800 m.id -- when in doubt, use the first match
1803 IF eg_id IS NULL THEN
1804 -- RAISE NOTICE 'incoming record is not of high enough quality';
1808 RETURN vandelay.overlay_bib_record( import_id, eg_id, merge_profile_id );
1810 $$ LANGUAGE PLPGSQL;
1813 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 $$
1815 queued_record vandelay.queued_bib_record%ROWTYPE;
1818 FOR queued_record IN SELECT * FROM vandelay.queued_bib_record WHERE queue = queue_id AND import_time IS NULL LOOP
1820 IF vandelay.auto_overlay_bib_record_with_best( queued_record.id, merge_profile_id, lwm_ratio_value ) THEN
1821 RETURN NEXT queued_record.id;
1829 $$ LANGUAGE PLPGSQL;
1831 CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_queue_with_best ( import_id BIGINT, merge_profile_id INT ) RETURNS SETOF BIGINT AS $$
1832 SELECT vandelay.auto_overlay_bib_queue_with_best( $1, $2, p.lwm_ratio ) FROM vandelay.merge_profile p WHERE id = $2;
1835 CREATE OR REPLACE FUNCTION vandelay.ingest_bib_marc ( ) RETURNS TRIGGER AS $$
1841 IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
1845 FOR adef IN SELECT * FROM vandelay.bib_attr_definition LOOP
1847 SELECT extract_marc_field('vandelay.queued_bib_record', id, adef.xpath, adef.remove) INTO value FROM vandelay.queued_bib_record WHERE id = NEW.id;
1848 IF (value IS NOT NULL AND value <> '') THEN
1849 INSERT INTO vandelay.queued_bib_record_attr (record, field, attr_value) VALUES (NEW.id, adef.id, value);
1856 $$ LANGUAGE PLPGSQL;
1858 CREATE OR REPLACE FUNCTION vandelay.ingest_bib_items ( ) RETURNS TRIGGER AS $func$
1861 item_data vandelay.import_item%ROWTYPE;
1864 IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
1868 SELECT item_attr_def INTO attr_def FROM vandelay.bib_queue WHERE id = NEW.queue;
1870 FOR item_data IN SELECT * FROM vandelay.ingest_items( NEW.id::BIGINT, attr_def ) LOOP
1871 INSERT INTO vandelay.import_item (
1895 item_data.definition,
1896 item_data.owning_lib,
1898 item_data.call_number,
1899 item_data.copy_number,
1902 item_data.circulate,
1904 item_data.deposit_amount,
1909 item_data.circ_modifier,
1910 item_data.circ_as_type,
1911 item_data.alert_message,
1913 item_data.priv_note,
1914 item_data.opac_visible
1920 $func$ LANGUAGE PLPGSQL;
1922 CREATE OR REPLACE FUNCTION vandelay.cleanup_bib_marc ( ) RETURNS TRIGGER AS $$
1924 IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
1928 DELETE FROM vandelay.queued_bib_record_attr WHERE record = OLD.id;
1929 DELETE FROM vandelay.import_item WHERE record = OLD.id;
1931 IF TG_OP = 'UPDATE' THEN
1936 $$ LANGUAGE PLPGSQL;
1940 DROP TRIGGER zz_match_bibs_trigger ON vandelay.queued_bib_record;
1941 CREATE TRIGGER zz_match_bibs_trigger
1942 BEFORE INSERT OR UPDATE ON vandelay.queued_bib_record
1943 FOR EACH ROW EXECUTE PROCEDURE vandelay.match_bib_record();
1945 CREATE OR REPLACE FUNCTION vandelay.ingest_authority_marc ( ) RETURNS TRIGGER AS $$
1951 IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
1955 FOR adef IN SELECT * FROM vandelay.authority_attr_definition LOOP
1957 SELECT extract_marc_field('vandelay.queued_authority_record', id, adef.xpath, adef.remove) INTO value FROM vandelay.queued_authority_record WHERE id = NEW.id;
1958 IF (value IS NOT NULL AND value <> '') THEN
1959 INSERT INTO vandelay.queued_authority_record_attr (record, field, attr_value) VALUES (NEW.id, adef.id, value);
1966 $$ LANGUAGE PLPGSQL;
1968 ALTER TABLE vandelay.authority_attr_definition DROP COLUMN ident;
1969 ALTER TABLE vandelay.queued_authority_record
1970 ADD COLUMN import_error TEXT REFERENCES vandelay.import_error (code) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
1971 ADD COLUMN error_detail TEXT;
1973 ALTER TABLE vandelay.authority_match DROP COLUMN matched_attr;
1975 CREATE OR REPLACE FUNCTION vandelay.cleanup_authority_marc ( ) RETURNS TRIGGER AS $$
1977 IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
1981 DELETE FROM vandelay.queued_authority_record_attr WHERE record = OLD.id;
1982 IF TG_OP = 'UPDATE' THEN
1987 $$ LANGUAGE PLPGSQL;
1989 CREATE OR REPLACE FUNCTION authority.flatten_marc ( rid BIGINT ) RETURNS SETOF authority.full_rec AS $func$
1991 auth authority.record_entry%ROWTYPE;
1992 output authority.full_rec%ROWTYPE;
1995 SELECT INTO auth * FROM authority.record_entry WHERE id = rid;
1997 FOR field IN SELECT * FROM vandelay.flatten_marc( auth.marc ) LOOP
1998 output.record := rid;
1999 output.ind1 := field.ind1;
2000 output.ind2 := field.ind2;
2001 output.tag := field.tag;
2002 output.subfield := field.subfield;
2003 output.value := field.value;
2008 $func$ LANGUAGE PLPGSQL;
2010 CREATE OR REPLACE FUNCTION biblio.flatten_marc ( rid BIGINT ) RETURNS SETOF metabib.full_rec AS $func$
2012 bib biblio.record_entry%ROWTYPE;
2013 output metabib.full_rec%ROWTYPE;
2016 SELECT INTO bib * FROM biblio.record_entry WHERE id = rid;
2018 FOR field IN SELECT * FROM vandelay.flatten_marc( bib.marc ) LOOP
2019 output.record := rid;
2020 output.ind1 := field.ind1;
2021 output.ind2 := field.ind2;
2022 output.tag := field.tag;
2023 output.subfield := field.subfield;
2024 output.value := field.value;
2029 $func$ LANGUAGE PLPGSQL;
2031 -----------------------------------------------
2032 -- Seed data for import errors
2033 -----------------------------------------------
2035 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'general.unknown', oils_i18n_gettext('general.unknown', 'Import or Overlay failed', 'vie', 'description') );
2036 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') );
2037 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') );
2038 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') );
2039 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') );
2040 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') );
2041 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') );
2042 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') );
2043 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.xml.malformed', oils_i18n_gettext('import.xml.malformed', 'Malformed record cause Import failure', 'vie', 'description') );
2044 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'overlay.xml.malformed', oils_i18n_gettext('overlay.xml.malformed', 'Malformed record cause Overlay failure', 'vie', 'description') );
2045 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'overlay.record.quality', oils_i18n_gettext('overlay.record.quality', 'New record had insufficient quality', 'vie', 'description') );
2048 ----------------------------------------------------------------
2049 -- Seed data for queued record/item exports
2050 ----------------------------------------------------------------
2052 INSERT INTO action_trigger.hook (key,core_type,description,passive) VALUES (
2053 'vandelay.queued_bib_record.print',
2056 'vandelay.queued_bib_record.print',
2057 'Print output has been requested for records in an Importer Bib Queue.',
2064 'vandelay.queued_bib_record.csv',
2067 'vandelay.queued_bib_record.csv',
2068 'CSV output has been requested for records in an Importer Bib Queue.',
2075 'vandelay.queued_bib_record.email',
2078 'vandelay.queued_bib_record.email',
2079 'An email has been requested for records in an Importer Bib Queue.',
2086 'vandelay.queued_auth_record.print',
2089 'vandelay.queued_auth_record.print',
2090 'Print output has been requested for records in an Importer Authority Queue.',
2097 'vandelay.queued_auth_record.csv',
2100 'vandelay.queued_auth_record.csv',
2101 'CSV output has been requested for records in an Importer Authority Queue.',
2108 'vandelay.queued_auth_record.email',
2111 'vandelay.queued_auth_record.email',
2112 'An email has been requested for records in an Importer Authority Queue.',
2119 'vandelay.import_items.print',
2122 'vandelay.import_items.print',
2123 'Print output has been requested for Import Items from records in an Importer Bib Queue.',
2130 'vandelay.import_items.csv',
2133 'vandelay.import_items.csv',
2134 'CSV output has been requested for Import Items from records in an Importer Bib Queue.',
2141 'vandelay.import_items.email',
2144 'vandelay.import_items.email',
2145 'An email has been requested for Import Items from records in an Importer Bib Queue.',
2153 INSERT INTO action_trigger.event_definition (
2168 'Print Output for Queued Bib Records',
2169 'vandelay.queued_bib_record.print',
2177 Queue ID: [% target.0.queue.id %]
2178 Queue Name: [% target.0.queue.name %]
2179 Queue Type: [% target.0.queue.queue_type %]
2180 Complete? [% target.0.queue.complete %]
2182 [% FOR vqbr IN target %]
2184 Title of work | [% helpers.get_queued_bib_attr('title',vqbr.attributes) %]
2185 Author of work | [% helpers.get_queued_bib_attr('author',vqbr.attributes) %]
2186 Language of work | [% helpers.get_queued_bib_attr('language',vqbr.attributes) %]
2187 Pagination | [% helpers.get_queued_bib_attr('pagination',vqbr.attributes) %]
2188 ISBN | [% helpers.get_queued_bib_attr('isbn',vqbr.attributes) %]
2189 ISSN | [% helpers.get_queued_bib_attr('issn',vqbr.attributes) %]
2190 Price | [% helpers.get_queued_bib_attr('price',vqbr.attributes) %]
2191 Accession Number | [% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) %]
2192 TCN Value | [% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) %]
2193 TCN Source | [% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) %]
2194 Internal ID | [% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) %]
2195 Publisher | [% helpers.get_queued_bib_attr('publisher',vqbr.attributes) %]
2196 Publication Date | [% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) %]
2197 Edition | [% helpers.get_queued_bib_attr('edition',vqbr.attributes) %]
2198 Item Barcode | [% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) %]
2206 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2211 INSERT INTO action_trigger.event_definition (
2226 'CSV Output for Queued Bib Records',
2227 'vandelay.queued_bib_record.csv',
2234 "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"
2235 [% 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('"', '""') %]"
2241 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2246 INSERT INTO action_trigger.event_definition (
2261 'Email Output for Queued Bib Records',
2262 'vandelay.queued_bib_record.email',
2269 [%- SET user = target.0.queue.owner -%]
2270 To: [%- params.recipient_email || user.email || 'root@localhost' %]
2271 From: [%- params.sender_email || default_sender %]
2272 Subject: Bibs from Import Queue
2274 Queue ID: [% target.0.queue.id %]
2275 Queue Name: [% target.0.queue.name %]
2276 Queue Type: [% target.0.queue.queue_type %]
2277 Complete? [% target.0.queue.complete %]
2279 [% FOR vqbr IN target %]
2281 Title of work | [% helpers.get_queued_bib_attr('title',vqbr.attributes) %]
2282 Author of work | [% helpers.get_queued_bib_attr('author',vqbr.attributes) %]
2283 Language of work | [% helpers.get_queued_bib_attr('language',vqbr.attributes) %]
2284 Pagination | [% helpers.get_queued_bib_attr('pagination',vqbr.attributes) %]
2285 ISBN | [% helpers.get_queued_bib_attr('isbn',vqbr.attributes) %]
2286 ISSN | [% helpers.get_queued_bib_attr('issn',vqbr.attributes) %]
2287 Price | [% helpers.get_queued_bib_attr('price',vqbr.attributes) %]
2288 Accession Number | [% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) %]
2289 TCN Value | [% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) %]
2290 TCN Source | [% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) %]
2291 Internal ID | [% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) %]
2292 Publisher | [% helpers.get_queued_bib_attr('publisher',vqbr.attributes) %]
2293 Publication Date | [% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) %]
2294 Edition | [% helpers.get_queued_bib_attr('edition',vqbr.attributes) %]
2295 Item Barcode | [% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) %]
2303 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2306 ,( 41, 'queue.owner')
2309 INSERT INTO action_trigger.event_definition (
2324 'Print Output for Queued Authority Records',
2325 'vandelay.queued_auth_record.print',
2333 Queue ID: [% target.0.queue.id %]
2334 Queue Name: [% target.0.queue.name %]
2335 Queue Type: [% target.0.queue.queue_type %]
2336 Complete? [% target.0.queue.complete %]
2338 [% FOR vqar IN target %]
2340 Record Identifier | [% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) %]
2348 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2353 INSERT INTO action_trigger.event_definition (
2368 'CSV Output for Queued Authority Records',
2369 'vandelay.queued_auth_record.csv',
2377 [% FOR vqar IN target %]"[% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) | replace('"', '""') %]"
2383 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2388 INSERT INTO action_trigger.event_definition (
2403 'Email Output for Queued Authority Records',
2404 'vandelay.queued_auth_record.email',
2411 [%- SET user = target.0.queue.owner -%]
2412 To: [%- params.recipient_email || user.email || 'root@localhost' %]
2413 From: [%- params.sender_email || default_sender %]
2414 Subject: Authorities from Import Queue
2416 Queue ID: [% target.0.queue.id %]
2417 Queue Name: [% target.0.queue.name %]
2418 Queue Type: [% target.0.queue.queue_type %]
2419 Complete? [% target.0.queue.complete %]
2421 [% FOR vqar IN target %]
2423 Record Identifier | [% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) %]
2431 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2434 ,( 44, 'queue.owner')
2437 INSERT INTO action_trigger.event_definition (
2452 'Print Output for Import Items from Queued Bib Records',
2453 'vandelay.import_items.print',
2456 'record.queue.owner',
2461 Queue ID: [% target.0.record.queue.id %]
2462 Queue Name: [% target.0.record.queue.name %]
2463 Queue Type: [% target.0.record.queue.queue_type %]
2464 Complete? [% target.0.record.queue.complete %]
2466 [% FOR vii IN target %]
2468 Import Item ID | [% vii.id %]
2469 Title of work | [% helpers.get_queued_bib_attr('title',vii.record.attributes) %]
2470 ISBN | [% helpers.get_queued_bib_attr('isbn',vii.record.attributes) %]
2471 Attribute Definition | [% vii.definition %]
2472 Import Error | [% vii.import_error %]
2473 Import Error Detail | [% vii.error_detail %]
2474 Owning Library | [% vii.owning_lib %]
2475 Circulating Library | [% vii.circ_lib %]
2476 Call Number | [% vii.call_number %]
2477 Copy Number | [% vii.copy_number %]
2478 Status | [% vii.status.name %]
2479 Shelving Location | [% vii.location.name %]
2480 Circulate | [% vii.circulate %]
2481 Deposit | [% vii.deposit %]
2482 Deposit Amount | [% vii.deposit_amount %]
2483 Reference | [% vii.ref %]
2484 Holdable | [% vii.holdable %]
2485 Price | [% vii.price %]
2486 Barcode | [% vii.barcode %]
2487 Circulation Modifier | [% vii.circ_modifier %]
2488 Circulate As MARC Type | [% vii.circ_as_type %]
2489 Alert Message | [% vii.alert_message %]
2490 Public Note | [% vii.pub_note %]
2491 Private Note | [% vii.priv_note %]
2492 OPAC Visible | [% vii.opac_visible %]
2500 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2502 ,( 45, 'record.attributes')
2503 ,( 45, 'record.queue')
2504 ,( 45, 'record.queue.owner')
2507 INSERT INTO action_trigger.event_definition (
2522 'CSV Output for Import Items from Queued Bib Records',
2523 'vandelay.import_items.csv',
2526 'record.queue.owner',
2530 "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"
2531 [% 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('"', '""') %]"
2537 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2539 ,( 46, 'record.attributes')
2540 ,( 46, 'record.queue')
2541 ,( 46, 'record.queue.owner')
2544 INSERT INTO action_trigger.event_definition (
2559 'Email Output for Import Items from Queued Bib Records',
2560 'vandelay.import_items.email',
2563 'record.queue.owner',
2567 [%- SET user = target.0.record.queue.owner -%]
2568 To: [%- params.recipient_email || user.email || 'root@localhost' %]
2569 From: [%- params.sender_email || default_sender %]
2570 Subject: Import Items from Import Queue
2572 Queue ID: [% target.0.record.queue.id %]
2573 Queue Name: [% target.0.record.queue.name %]
2574 Queue Type: [% target.0.record.queue.queue_type %]
2575 Complete? [% target.0.record.queue.complete %]
2577 [% FOR vii IN target %]
2579 Import Item ID | [% vii.id %]
2580 Title of work | [% helpers.get_queued_bib_attr('title',vii.record.attributes) %]
2581 ISBN | [% helpers.get_queued_bib_attr('isbn',vii.record.attributes) %]
2582 Attribute Definition | [% vii.definition %]
2583 Import Error | [% vii.import_error %]
2584 Import Error Detail | [% vii.error_detail %]
2585 Owning Library | [% vii.owning_lib %]
2586 Circulating Library | [% vii.circ_lib %]
2587 Call Number | [% vii.call_number %]
2588 Copy Number | [% vii.copy_number %]
2589 Status | [% vii.status.name %]
2590 Shelving Location | [% vii.location.name %]
2591 Circulate | [% vii.circulate %]
2592 Deposit | [% vii.deposit %]
2593 Deposit Amount | [% vii.deposit_amount %]
2594 Reference | [% vii.ref %]
2595 Holdable | [% vii.holdable %]
2596 Price | [% vii.price %]
2597 Barcode | [% vii.barcode %]
2598 Circulation Modifier | [% vii.circ_modifier %]
2599 Circulate As MARC Type | [% vii.circ_as_type %]
2600 Alert Message | [% vii.alert_message %]
2601 Public Note | [% vii.pub_note %]
2602 Private Note | [% vii.priv_note %]
2603 OPAC Visible | [% vii.opac_visible %]
2610 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2612 ,( 47, 'record.attributes')
2613 ,( 47, 'record.queue')
2614 ,( 47, 'record.queue.owner')
2619 SELECT evergreen.upgrade_deps_block_check('0574', :eg_version);
2621 UPDATE action_trigger.event_definition SET template =
2625 table { border-collapse: collapse; }
2626 td { padding: 5px; border-bottom: 1px solid #888; }
2627 th { font-weight: bold; }
2630 # Sort the holds into copy-location buckets
2631 # In the main print loop, sort each bucket by callnumber before printing
2632 SET holds_list = [];
2634 SET current_location = target.0.current_copy.location.id;
2636 IF current_location != hold.current_copy.location.id;
2637 SET current_location = hold.current_copy.location.id;
2638 holds_list.push(loc_data);
2643 'callnumber' => hold.current_copy.call_number.label
2645 loc_data.push(hold_data);
2647 holds_list.push(loc_data)
2654 <th>Shelving Location</th>
2655 <th>Call Number</th>
2656 <th>Barcode/Part</th>
2661 [% FOR loc_data IN holds_list %]
2662 [% FOR hold_data IN loc_data.sort('callnumber') %]
2664 SET hold = hold_data.hold;
2665 SET copy_data = helpers.get_copy_bib_basics(hold.current_copy.id);
2668 <td>[% copy_data.title | truncate %]</td>
2669 <td>[% copy_data.author | truncate %]</td>
2670 <td>[% hold.current_copy.location.name %]</td>
2671 <td>[% hold.current_copy.call_number.label %]</td>
2672 <td>[% hold.current_copy.barcode %]
2673 [% FOR part IN hold.current_copy.parts %]
2674 [% part.part.label %]
2677 <td>[% hold.usr.card.barcode %]</td>
2686 INSERT INTO action_trigger.environment (
2690 (35, 'current_copy.parts'),
2691 (35, 'current_copy.parts.part')
2695 -- Evergreen DB patch XXXX.schema.authority-control-sets.sql
2697 -- Schema upgrade to add Authority Control Set functionality
2701 -- check whether patch can be applied
2702 SELECT evergreen.upgrade_deps_block_check('0575', :eg_version);
2704 CREATE TABLE authority.control_set (
2705 id SERIAL PRIMARY KEY,
2706 name TEXT NOT NULL UNIQUE, -- i18n
2707 description TEXT -- i18n
2710 CREATE TABLE authority.control_set_authority_field (
2711 id SERIAL PRIMARY KEY,
2712 main_entry INT REFERENCES authority.control_set_authority_field (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2713 control_set INT NOT NULL REFERENCES authority.control_set (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2714 tag CHAR(3) NOT NULL,
2716 sf_list TEXT NOT NULL,
2717 name TEXT NOT NULL, -- i18n
2718 description TEXT -- i18n
2721 CREATE TABLE authority.control_set_bib_field (
2722 id SERIAL PRIMARY KEY,
2723 authority_field INT NOT NULL REFERENCES authority.control_set_authority_field (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2724 tag CHAR(3) NOT NULL
2727 CREATE TABLE authority.thesaurus (
2728 code TEXT PRIMARY KEY, -- MARC21 thesaurus code
2729 control_set INT NOT NULL REFERENCES authority.control_set (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2730 name TEXT NOT NULL UNIQUE, -- i18n
2731 description TEXT -- i18n
2734 CREATE TABLE authority.browse_axis (
2735 code TEXT PRIMARY KEY,
2736 name TEXT UNIQUE NOT NULL, -- i18n
2737 sorter TEXT REFERENCES config.record_attr_definition (name) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2741 CREATE TABLE authority.browse_axis_authority_field_map (
2742 id SERIAL PRIMARY KEY,
2743 axis TEXT NOT NULL REFERENCES authority.browse_axis (code) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2744 field INT NOT NULL REFERENCES authority.control_set_authority_field (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED
2747 ALTER TABLE authority.record_entry ADD COLUMN control_set INT REFERENCES authority.control_set (id) ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED;
2748 ALTER TABLE authority.rec_descriptor DROP COLUMN char_encoding, ADD COLUMN encoding_level TEXT, ADD COLUMN thesaurus TEXT;
2750 CREATE INDEX authority_full_rec_value_index ON authority.full_rec (value);
2751 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);
2753 CREATE OR REPLACE FUNCTION authority.normalize_heading( marcxml TEXT, no_thesaurus BOOL ) RETURNS TEXT AS $func$
2755 acsaf authority.control_set_authority_field%ROWTYPE;
2763 thes_code := vandelay.marc21_extract_fixed_field(marcxml,'Subj');
2764 IF thes_code IS NULL THEN
2768 SELECT control_set INTO cset FROM authority.thesaurus WHERE code = thes_code;
2774 FOR acsaf IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset AND main_entry IS NULL LOOP
2775 tag_used := acsaf.tag;
2776 FOR sf IN SELECT * FROM regexp_split_to_table(acsaf.sf_list,'') LOOP
2777 tmp_text := oils_xpath_string('//*[@tag="'||tag_used||'"]/*[@code="'||sf||'"]', marcxml);
2778 IF tmp_text IS NOT NULL AND tmp_text <> '' THEN
2779 heading_text := heading_text || E'\u2021' || sf || ' ' || tmp_text;
2782 EXIT WHEN heading_text <> '';
2785 IF thes_code = 'z' THEN
2786 thes_code := oils_xpath_string('//*[@tag="040"]/*[@code="f"][1]', marcxml);
2789 IF heading_text <> '' THEN
2790 IF no_thesaurus IS TRUE THEN
2791 heading_text := tag_used || ' ' || public.naco_normalize(heading_text);
2793 heading_text := tag_used || '_' || thes_code || ' ' || public.naco_normalize(heading_text);
2796 heading_text := 'NOHEADING_' || thes_code || ' ' || MD5(marcxml);
2799 RETURN heading_text;
2801 $func$ LANGUAGE PLPGSQL IMMUTABLE;
2803 CREATE OR REPLACE FUNCTION authority.simple_normalize_heading( marcxml TEXT ) RETURNS TEXT AS $func$
2804 SELECT authority.normalize_heading($1, TRUE);
2805 $func$ LANGUAGE SQL IMMUTABLE;
2807 CREATE OR REPLACE FUNCTION authority.normalize_heading( marcxml TEXT ) RETURNS TEXT AS $func$
2808 SELECT authority.normalize_heading($1, FALSE);
2809 $func$ LANGUAGE SQL IMMUTABLE;
2811 CREATE OR REPLACE VIEW authority.tracing_links AS
2812 SELECT main.record AS record,
2814 main.tag AS main_tag,
2815 oils_xpath_string('//*[@tag="'||main.tag||'"]/*[local-name()="subfield"]', are.marc) AS main_value,
2816 substr(link.value,1,1) AS relationship,
2817 substr(link.value,2,1) AS use_restriction,
2818 substr(link.value,3,1) AS deprecation,
2819 substr(link.value,4,1) AS display_restriction,
2821 link.tag AS link_tag,
2822 oils_xpath_string('//*[@tag="'||link.tag||'"]/*[local-name()="subfield"]', are.marc) AS link_value,
2823 authority.normalize_heading(are.marc) AS normalized_main_value
2824 FROM authority.full_rec main
2825 JOIN authority.record_entry are ON (main.record = are.id)
2826 JOIN authority.control_set_authority_field main_entry
2827 ON (main_entry.tag = main.tag
2828 AND main_entry.main_entry IS NULL
2829 AND main.subfield = 'a' )
2830 JOIN authority.control_set_authority_field sub_entry
2831 ON (main_entry.id = sub_entry.main_entry)
2832 JOIN authority.full_rec link
2833 ON (link.record = main.record
2834 AND link.tag = sub_entry.tag
2835 AND link.subfield = 'w' );
2837 CREATE OR REPLACE FUNCTION authority.generate_overlay_template (source_xml TEXT) RETURNS TEXT AS $f$
2840 main_entry authority.control_set_authority_field%ROWTYPE;
2841 bib_field authority.control_set_bib_field%ROWTYPE;
2842 auth_id INT DEFAULT oils_xpath_string('//*[@tag="901"]/*[local-name()="subfield" and @code="c"]', source_xml)::INT;
2843 replace_data XML[] DEFAULT '{}'::XML[];
2844 replace_rules TEXT[] DEFAULT '{}'::TEXT[];
2847 IF auth_id IS NULL THEN
2851 -- Default to the LoC controll set
2852 SELECT COALESCE(control_set,1) INTO cset FROM authority.record_entry WHERE id = auth_id;
2854 FOR main_entry IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset LOOP
2855 auth_field := XPATH('//*[@tag="'||main_entry.tag||'"][1]',source_xml::XML);
2856 IF ARRAY_LENGTH(auth_field,1) > 0 THEN
2857 FOR bib_field IN SELECT * FROM authority.control_set_bib_field WHERE authority_field = main_entry.id LOOP
2858 replace_data := replace_data || XMLELEMENT( name datafield, XMLATTRIBUTES(bib_field.tag AS tag), XPATH('//*[local-name()="subfield"]',auth_field[1])::XML[]);
2859 replace_rules := replace_rules || ( bib_field.tag || main_entry.sf_list || E'[0~\\)' || auth_id || '$]' );
2867 XMLATTRIBUTES('http://www.loc.gov/MARC21/slim' AS xmlns),
2868 XMLELEMENT( name leader, '00881nam a2200193 4500'),
2872 XMLATTRIBUTES( '905' AS tag, ' ' AS ind1, ' ' AS ind2),
2875 XMLATTRIBUTES('r' AS code),
2876 ARRAY_TO_STRING(replace_rules,',')
2881 $f$ STABLE LANGUAGE PLPGSQL;
2883 CREATE OR REPLACE FUNCTION authority.generate_overlay_template ( BIGINT ) RETURNS TEXT AS $func$
2884 SELECT authority.generate_overlay_template( marc ) FROM authority.record_entry WHERE id = $1;
2885 $func$ LANGUAGE SQL;
2887 CREATE OR REPLACE FUNCTION vandelay.add_field ( target_xml TEXT, source_xml TEXT, field TEXT, force_add INT ) RETURNS TEXT AS $_$
2890 use MARC::File::XML (BinaryEncoding => 'UTF-8');
2894 MARC::Charset->assume_unicode(1);
2896 my $target_xml = shift;
2897 my $source_xml = shift;
2898 my $field_spec = shift;
2899 my $force_add = shift || 0;
2901 my $target_r = MARC::Record->new_from_xml( $target_xml );
2902 my $source_r = MARC::Record->new_from_xml( $source_xml );
2904 return $target_xml unless ($target_r && $source_r);
2906 my @field_list = split(',', $field_spec);
2909 for my $f (@field_list) {
2910 $f =~ s/^\s*//; $f =~ s/\s*$//;
2911 if ($f =~ /^(.{3})(\w*)(?:\[([^]]*)\])?$/) {
2917 $match =~ s/^\s*//; $match =~ s/\s*$//;
2918 $fields{$field} = { sf => [ split('', $sf) ] };
2920 my ($msf,$mre) = split('~', $match);
2921 if (length($msf) > 0 and length($mre) > 0) {
2922 $msf =~ s/^\s*//; $msf =~ s/\s*$//;
2923 $mre =~ s/^\s*//; $mre =~ s/\s*$//;
2924 $fields{$field}{match} = { sf => $msf, re => qr/$mre/ };
2930 for my $f ( keys %fields) {
2931 if ( @{$fields{$f}{sf}} ) {
2932 for my $from_field ($source_r->field( $f )) {
2933 my @tos = $target_r->field( $f );
2935 next if (exists($fields{$f}{match}) and !$force_add);
2936 my @new_fields = map { $_->clone } $source_r->field( $f );
2937 $target_r->insert_fields_ordered( @new_fields );
2939 for my $to_field (@tos) {
2940 if (exists($fields{$f}{match})) {
2941 next unless (grep { $_ =~ $fields{$f}{match}{re} } $to_field->subfield($fields{$f}{match}{sf}));
2943 my @new_sf = map { ($_ => $from_field->subfield($_)) } grep { defined($from_field->subfield($_)) } @{$fields{$f}{sf}};
2944 $to_field->add_subfields( @new_sf );
2949 my @new_fields = map { $_->clone } $source_r->field( $f );
2950 $target_r->insert_fields_ordered( @new_fields );
2954 $target_xml = $target_r->as_xml_record;
2955 $target_xml =~ s/^<\?.+?\?>$//mo;
2956 $target_xml =~ s/\n//sgo;
2957 $target_xml =~ s/>\s+</></sgo;
2961 $_$ LANGUAGE PLPERLU;
2964 CREATE INDEX by_heading ON authority.record_entry (authority.simple_normalize_heading(marc)) WHERE deleted IS FALSE or deleted = FALSE;
2966 INSERT INTO config.metabib_field ( id, field_class, name, label, format, xpath, search_field, facet_field) VALUES
2967 (28, 'identifier', 'authority_id', oils_i18n_gettext(28, 'Authority Record ID', 'cmf', 'label'), 'marcxml', '//marc:datafield/marc:subfield[@code="0"]', FALSE, TRUE);
2969 INSERT INTO config.marc21_rec_type_map (code, type_val, blvl_val) VALUES ('AUT','z',' ');
2970 INSERT INTO config.marc21_rec_type_map (code, type_val, blvl_val) VALUES ('MFHD','uvxy',' ');
2972 INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('ELvl', 'ldr', 'AUT', 17, 1, ' ');
2973 INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('Subj', '008', 'AUT', 11, 1, '|');
2974 INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('RecStat', 'ldr', 'AUT', 5, 1, 'n');
2976 INSERT INTO config.metabib_field_index_norm_map (field,norm,pos)
2980 FROM config.metabib_field m,
2981 config.index_normalizer i
2982 WHERE i.func = 'remove_paren_substring'
2985 SELECT SETVAL('authority.control_set_id_seq'::TEXT, 100);
2986 SELECT SETVAL('authority.control_set_authority_field_id_seq'::TEXT, 1000);
2987 SELECT SETVAL('authority.control_set_bib_field_id_seq'::TEXT, 1000);
2989 INSERT INTO authority.control_set (id, name, description) VALUES (
2991 oils_i18n_gettext('1','LoC','acs','name'),
2992 oils_i18n_gettext('1','Library of Congress standard authority record control semantics','acs','description')
2995 INSERT INTO authority.control_set_authority_field (id, control_set, main_entry, tag, sf_list, name) VALUES
2998 (1, 1, NULL, '100', 'abcdefklmnopqrstvxyz', oils_i18n_gettext('1','Heading -- Personal Name','acsaf','name')),
2999 (2, 1, NULL, '110', 'abcdefgklmnoprstvxyz', oils_i18n_gettext('2','Heading -- Corporate Name','acsaf','name')),
3000 (3, 1, NULL, '111', 'acdefgklnpqstvxyz', oils_i18n_gettext('3','Heading -- Meeting Name','acsaf','name')),
3001 (4, 1, NULL, '130', 'adfgklmnoprstvxyz', oils_i18n_gettext('4','Heading -- Uniform Title','acsaf','name')),
3002 (5, 1, NULL, '150', 'abvxyz', oils_i18n_gettext('5','Heading -- Topical Term','acsaf','name')),
3003 (6, 1, NULL, '151', 'avxyz', oils_i18n_gettext('6','Heading -- Geographic Name','acsaf','name')),
3004 (7, 1, NULL, '155', 'avxyz', oils_i18n_gettext('7','Heading -- Genre/Form Term','acsaf','name')),
3005 (8, 1, NULL, '180', 'vxyz', oils_i18n_gettext('8','Heading -- General Subdivision','acsaf','name')),
3006 (9, 1, NULL, '181', 'vxyz', oils_i18n_gettext('9','Heading -- Geographic Subdivision','acsaf','name')),
3007 (10, 1, NULL, '182', 'vxyz', oils_i18n_gettext('10','Heading -- Chronological Subdivision','acsaf','name')),
3008 (11, 1, NULL, '185', 'vxyz', oils_i18n_gettext('11','Heading -- Form Subdivision','acsaf','name')),
3009 (12, 1, NULL, '148', 'avxyz', oils_i18n_gettext('12','Heading -- Chronological Term','acsaf','name')),
3011 -- See Also From tracings
3012 (21, 1, 1, '500', 'abcdefiklmnopqrstvwxyz4', oils_i18n_gettext('21','See Also From Tracing -- Personal Name','acsaf','name')),
3013 (22, 1, 2, '510', 'abcdefgiklmnoprstvwxyz4', oils_i18n_gettext('22','See Also From Tracing -- Corporate Name','acsaf','name')),
3014 (23, 1, 3, '511', 'acdefgiklnpqstvwxyz4', oils_i18n_gettext('23','See Also From Tracing -- Meeting Name','acsaf','name')),
3015 (24, 1, 4, '530', 'adfgiklmnoprstvwxyz4', oils_i18n_gettext('24','See Also From Tracing -- Uniform Title','acsaf','name')),
3016 (25, 1, 5, '550', 'abivwxyz4', oils_i18n_gettext('25','See Also From Tracing -- Topical Term','acsaf','name')),
3017 (26, 1, 6, '551', 'aivwxyz4', oils_i18n_gettext('26','See Also From Tracing -- Geographic Name','acsaf','name')),
3018 (27, 1, 7, '555', 'aivwxyz4', oils_i18n_gettext('27','See Also From Tracing -- Genre/Form Term','acsaf','name')),
3019 (28, 1, 8, '580', 'ivwxyz4', oils_i18n_gettext('28','See Also From Tracing -- General Subdivision','acsaf','name')),
3020 (29, 1, 9, '581', 'ivwxyz4', oils_i18n_gettext('29','See Also From Tracing -- Geographic Subdivision','acsaf','name')),
3021 (30, 1, 10, '582', 'ivwxyz4', oils_i18n_gettext('30','See Also From Tracing -- Chronological Subdivision','acsaf','name')),
3022 (31, 1, 11, '585', 'ivwxyz4', oils_i18n_gettext('31','See Also From Tracing -- Form Subdivision','acsaf','name')),
3023 (32, 1, 12, '548', 'aivwxyz4', oils_i18n_gettext('32','See Also From Tracing -- Chronological Term','acsaf','name')),
3026 (41, 1, 1, '700', 'abcdefghjklmnopqrstvwxyz25', oils_i18n_gettext('41','Established Heading Linking Entry -- Personal Name','acsaf','name')),
3027 (42, 1, 2, '710', 'abcdefghklmnoprstvwxyz25', oils_i18n_gettext('42','Established Heading Linking Entry -- Corporate Name','acsaf','name')),
3028 (43, 1, 3, '711', 'acdefghklnpqstvwxyz25', oils_i18n_gettext('43','Established Heading Linking Entry -- Meeting Name','acsaf','name')),
3029 (44, 1, 4, '730', 'adfghklmnoprstvwxyz25', oils_i18n_gettext('44','Established Heading Linking Entry -- Uniform Title','acsaf','name')),
3030 (45, 1, 5, '750', 'abvwxyz25', oils_i18n_gettext('45','Established Heading Linking Entry -- Topical Term','acsaf','name')),
3031 (46, 1, 6, '751', 'avwxyz25', oils_i18n_gettext('46','Established Heading Linking Entry -- Geographic Name','acsaf','name')),
3032 (47, 1, 7, '755', 'avwxyz25', oils_i18n_gettext('47','Established Heading Linking Entry -- Genre/Form Term','acsaf','name')),
3033 (48, 1, 8, '780', 'vwxyz25', oils_i18n_gettext('48','Subdivision Linking Entry -- General Subdivision','acsaf','name')),
3034 (49, 1, 9, '781', 'vwxyz25', oils_i18n_gettext('49','Subdivision Linking Entry -- Geographic Subdivision','acsaf','name')),
3035 (50, 1, 10, '782', 'vwxyz25', oils_i18n_gettext('50','Subdivision Linking Entry -- Chronological Subdivision','acsaf','name')),
3036 (51, 1, 11, '785', 'vwxyz25', oils_i18n_gettext('51','Subdivision Linking Entry -- Form Subdivision','acsaf','name')),
3037 (52, 1, 12, '748', 'avwxyz25', oils_i18n_gettext('52','Established Heading Linking Entry -- Chronological Term','acsaf','name')),
3039 -- See From tracings
3040 (61, 1, 1, '400', 'abcdefiklmnopqrstvwxyz4', oils_i18n_gettext('61','See Also Tracing -- Personal Name','acsaf','name')),
3041 (62, 1, 2, '410', 'abcdefgiklmnoprstvwxyz4', oils_i18n_gettext('62','See Also Tracing -- Corporate Name','acsaf','name')),
3042 (63, 1, 3, '411', 'acdefgiklnpqstvwxyz4', oils_i18n_gettext('63','See Also Tracing -- Meeting Name','acsaf','name')),
3043 (64, 1, 4, '430', 'adfgiklmnoprstvwxyz4', oils_i18n_gettext('64','See Also Tracing -- Uniform Title','acsaf','name')),
3044 (65, 1, 5, '450', 'abivwxyz4', oils_i18n_gettext('65','See Also Tracing -- Topical Term','acsaf','name')),
3045 (66, 1, 6, '451', 'aivwxyz4', oils_i18n_gettext('66','See Also Tracing -- Geographic Name','acsaf','name')),
3046 (67, 1, 7, '455', 'aivwxyz4', oils_i18n_gettext('67','See Also Tracing -- Genre/Form Term','acsaf','name')),
3047 (68, 1, 8, '480', 'ivwxyz4', oils_i18n_gettext('68','See Also Tracing -- General Subdivision','acsaf','name')),
3048 (69, 1, 9, '481', 'ivwxyz4', oils_i18n_gettext('69','See Also Tracing -- Geographic Subdivision','acsaf','name')),
3049 (70, 1, 10, '482', 'ivwxyz4', oils_i18n_gettext('70','See Also Tracing -- Chronological Subdivision','acsaf','name')),
3050 (71, 1, 11, '485', 'ivwxyz4', oils_i18n_gettext('71','See Also Tracing -- Form Subdivision','acsaf','name')),
3051 (72, 1, 12, '448', 'aivwxyz4', oils_i18n_gettext('72','See Also Tracing -- Chronological Term','acsaf','name'));
3053 INSERT INTO authority.browse_axis (code,name,description,sorter) VALUES
3054 ('title','Title','Title axis','titlesort'),
3055 ('author','Author','Author axis','titlesort'),
3056 ('subject','Subject','Subject axis','titlesort'),
3057 ('topic','Topic','Topic Subject axis','titlesort');
3059 INSERT INTO authority.browse_axis_authority_field_map (axis,field) VALUES
3070 INSERT INTO authority.control_set_bib_field (tag, authority_field)
3071 SELECT '100', id FROM authority.control_set_authority_field WHERE tag IN ('100')
3073 SELECT '600', id FROM authority.control_set_authority_field WHERE tag IN ('100','180','181','182','185')
3075 SELECT '700', id FROM authority.control_set_authority_field WHERE tag IN ('100')
3077 SELECT '800', id FROM authority.control_set_authority_field WHERE tag IN ('100')
3080 SELECT '110', id FROM authority.control_set_authority_field WHERE tag IN ('110')
3082 SELECT '610', id FROM authority.control_set_authority_field WHERE tag IN ('110')
3084 SELECT '710', id FROM authority.control_set_authority_field WHERE tag IN ('110')
3086 SELECT '810', id FROM authority.control_set_authority_field WHERE tag IN ('110')
3089 SELECT '111', id FROM authority.control_set_authority_field WHERE tag IN ('111')
3091 SELECT '611', id FROM authority.control_set_authority_field WHERE tag IN ('111')
3093 SELECT '711', id FROM authority.control_set_authority_field WHERE tag IN ('111')
3095 SELECT '811', id FROM authority.control_set_authority_field WHERE tag IN ('111')
3098 SELECT '130', id FROM authority.control_set_authority_field WHERE tag IN ('130')
3100 SELECT '240', id FROM authority.control_set_authority_field WHERE tag IN ('130')
3102 SELECT '630', id FROM authority.control_set_authority_field WHERE tag IN ('130')
3104 SELECT '730', id FROM authority.control_set_authority_field WHERE tag IN ('130')
3106 SELECT '830', id FROM authority.control_set_authority_field WHERE tag IN ('130')
3109 SELECT '648', id FROM authority.control_set_authority_field WHERE tag IN ('148')
3112 SELECT '650', id FROM authority.control_set_authority_field WHERE tag IN ('150','180','181','182','185')
3114 SELECT '651', id FROM authority.control_set_authority_field WHERE tag IN ('151','180','181','182','185')
3116 SELECT '655', id FROM authority.control_set_authority_field WHERE tag IN ('155','180','181','182','185')
3119 INSERT INTO authority.thesaurus (code, name, control_set) VALUES
3120 ('a', oils_i18n_gettext('a','Library of Congress Subject Headings','at','name'), 1),
3121 ('b', oils_i18n_gettext('b',$$LC subject headings for children's literature$$,'at','name'), 1), -- silly vim '
3122 ('c', oils_i18n_gettext('c','Medical Subject Headings','at','name'), 1),
3123 ('d', oils_i18n_gettext('d','National Agricultural Library subject authority file','at','name'), 1),
3124 ('k', oils_i18n_gettext('k','Canadian Subject Headings','at','name'), 1),
3125 ('n', oils_i18n_gettext('n','Not applicable','at','name'), 1),
3126 ('r', oils_i18n_gettext('r','Art and Architecture Thesaurus','at','name'), 1),
3127 ('s', oils_i18n_gettext('s','Sears List of Subject Headings','at','name'), 1),
3128 ('v', oils_i18n_gettext('v','Repertoire de vedettes-matiere','at','name'), 1),
3129 ('z', oils_i18n_gettext('z','Other','at','name'), 1),
3130 ('|', oils_i18n_gettext('|','No attempt to code','at','name'), 1);
3132 CREATE OR REPLACE FUNCTION authority.map_thesaurus_to_control_set () RETURNS TRIGGER AS $func$
3134 IF NEW.control_set IS NULL THEN
3135 SELECT control_set INTO NEW.control_set
3136 FROM authority.thesaurus
3137 WHERE vandelay.marc21_extract_fixed_field(NEW.marc,'Subj') = code;
3142 $func$ LANGUAGE PLPGSQL;
3144 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 ();
3146 CREATE OR REPLACE FUNCTION authority.reingest_authority_rec_descriptor( auth_id BIGINT ) RETURNS VOID AS $func$
3148 DELETE FROM authority.rec_descriptor WHERE record = auth_id;
3149 INSERT INTO authority.rec_descriptor (record, record_status, encoding_level, thesaurus)
3151 vandelay.marc21_extract_fixed_field(marc,'RecStat'),
3152 vandelay.marc21_extract_fixed_field(marc,'ELvl'),
3153 vandelay.marc21_extract_fixed_field(marc,'Subj')
3154 FROM authority.record_entry
3158 $func$ LANGUAGE PLPGSQL;
3160 --Removed dupe authority.indexing_ingest_or_delete
3162 -- Evergreen DB patch 0577.schema.vandelay-item-import-copy-loc-ancestors.sql
3164 -- Ingest items copy location inheritance
3167 -- check whether patch can be applied
3168 SELECT evergreen.upgrade_deps_block_check('0577', :eg_version); -- berick
3170 CREATE OR REPLACE FUNCTION vandelay.ingest_items ( import_id BIGINT, attr_def_id BIGINT ) RETURNS SETOF vandelay.import_item AS $$
3181 deposit_amount TEXT;
3194 tmp_attr_set RECORD;
3195 attr_set vandelay.import_item%ROWTYPE;
3201 SELECT * INTO attr_def FROM vandelay.import_item_attr_definition WHERE id = attr_def_id;
3205 attr_set.definition := attr_def.id;
3207 -- Build the combined XPath
3211 WHEN attr_def.owning_lib IS NULL THEN 'null()'
3212 WHEN LENGTH( attr_def.owning_lib ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.owning_lib || '"]'
3213 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.owning_lib
3218 WHEN attr_def.circ_lib IS NULL THEN 'null()'
3219 WHEN LENGTH( attr_def.circ_lib ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_lib || '"]'
3220 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_lib
3225 WHEN attr_def.call_number IS NULL THEN 'null()'
3226 WHEN LENGTH( attr_def.call_number ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.call_number || '"]'
3227 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.call_number
3232 WHEN attr_def.copy_number IS NULL THEN 'null()'
3233 WHEN LENGTH( attr_def.copy_number ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.copy_number || '"]'
3234 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.copy_number
3239 WHEN attr_def.status IS NULL THEN 'null()'
3240 WHEN LENGTH( attr_def.status ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.status || '"]'
3241 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.status
3246 WHEN attr_def.location IS NULL THEN 'null()'
3247 WHEN LENGTH( attr_def.location ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.location || '"]'
3248 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.location
3253 WHEN attr_def.circulate IS NULL THEN 'null()'
3254 WHEN LENGTH( attr_def.circulate ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circulate || '"]'
3255 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circulate
3260 WHEN attr_def.deposit IS NULL THEN 'null()'
3261 WHEN LENGTH( attr_def.deposit ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.deposit || '"]'
3262 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.deposit
3267 WHEN attr_def.deposit_amount IS NULL THEN 'null()'
3268 WHEN LENGTH( attr_def.deposit_amount ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.deposit_amount || '"]'
3269 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.deposit_amount
3274 WHEN attr_def.ref IS NULL THEN 'null()'
3275 WHEN LENGTH( attr_def.ref ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.ref || '"]'
3276 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.ref
3281 WHEN attr_def.holdable IS NULL THEN 'null()'
3282 WHEN LENGTH( attr_def.holdable ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.holdable || '"]'
3283 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.holdable
3288 WHEN attr_def.price IS NULL THEN 'null()'
3289 WHEN LENGTH( attr_def.price ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.price || '"]'
3290 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.price
3295 WHEN attr_def.barcode IS NULL THEN 'null()'
3296 WHEN LENGTH( attr_def.barcode ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.barcode || '"]'
3297 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.barcode
3302 WHEN attr_def.circ_modifier IS NULL THEN 'null()'
3303 WHEN LENGTH( attr_def.circ_modifier ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_modifier || '"]'
3304 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_modifier
3309 WHEN attr_def.circ_as_type IS NULL THEN 'null()'
3310 WHEN LENGTH( attr_def.circ_as_type ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_as_type || '"]'
3311 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_as_type
3316 WHEN attr_def.alert_message IS NULL THEN 'null()'
3317 WHEN LENGTH( attr_def.alert_message ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.alert_message || '"]'
3318 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.alert_message
3323 WHEN attr_def.opac_visible IS NULL THEN 'null()'
3324 WHEN LENGTH( attr_def.opac_visible ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.opac_visible || '"]'
3325 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.opac_visible
3330 WHEN attr_def.pub_note IS NULL THEN 'null()'
3331 WHEN LENGTH( attr_def.pub_note ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.pub_note || '"]'
3332 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.pub_note
3336 WHEN attr_def.priv_note IS NULL THEN 'null()'
3337 WHEN LENGTH( attr_def.priv_note ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.priv_note || '"]'
3338 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.priv_note
3343 owning_lib || '|' ||
3345 call_number || '|' ||
3346 copy_number || '|' ||
3351 deposit_amount || '|' ||
3356 circ_modifier || '|' ||
3357 circ_as_type || '|' ||
3358 alert_message || '|' ||
3363 -- RAISE NOTICE 'XPath: %', xpath;
3367 FROM oils_xpath_table( 'id', 'marc', 'vandelay.queued_bib_record', xpath, 'id = ' || import_id )
3368 AS t( id INT, ol TEXT, clib TEXT, cn TEXT, cnum TEXT, cs TEXT, cl TEXT, circ TEXT,
3369 dep TEXT, dep_amount TEXT, r TEXT, hold TEXT, pr TEXT, bc TEXT, circ_mod TEXT,
3370 circ_as TEXT, amessage TEXT, note TEXT, pnote TEXT, opac_vis TEXT )
3373 tmp_attr_set.pr = REGEXP_REPLACE(tmp_attr_set.pr, E'[^0-9\\.]', '', 'g');
3374 tmp_attr_set.dep_amount = REGEXP_REPLACE(tmp_attr_set.dep_amount, E'[^0-9\\.]', '', 'g');
3376 tmp_attr_set.pr := NULLIF( tmp_attr_set.pr, '' );
3377 tmp_attr_set.dep_amount := NULLIF( tmp_attr_set.dep_amount, '' );
3379 SELECT id INTO attr_set.owning_lib FROM actor.org_unit WHERE shortname = UPPER(tmp_attr_set.ol); -- INT
3380 SELECT id INTO attr_set.circ_lib FROM actor.org_unit WHERE shortname = UPPER(tmp_attr_set.clib); -- INT
3381 SELECT id INTO attr_set.status FROM config.copy_status WHERE LOWER(name) = LOWER(tmp_attr_set.cs); -- INT
3384 -- search up the org unit tree for a matching copy location
3386 WITH RECURSIVE anscestor_depth AS (
3390 FROM actor.org_unit ou
3391 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
3392 WHERE ou.id = COALESCE(attr_set.owning_lib, attr_set.circ_lib)
3397 FROM actor.org_unit ou
3398 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
3399 JOIN anscestor_depth ot ON (ot.parent_ou = ou.id)
3400 ) SELECT cpl.id INTO attr_set.location
3401 FROM anscestor_depth a
3402 JOIN asset.copy_location cpl ON (cpl.owning_lib = a.id)
3403 WHERE LOWER(cpl.name) = LOWER(tmp_attr_set.cl)
3404 ORDER BY a.depth DESC
3407 attr_set.circulate :=
3408 LOWER( SUBSTRING( tmp_attr_set.circ, 1, 1)) IN ('t','y','1')
3409 OR LOWER(tmp_attr_set.circ) = 'circulating'; -- BOOL
3412 LOWER( SUBSTRING( tmp_attr_set.dep, 1, 1 ) ) IN ('t','y','1')
3413 OR LOWER(tmp_attr_set.dep) = 'deposit'; -- BOOL
3415 attr_set.holdable :=
3416 LOWER( SUBSTRING( tmp_attr_set.hold, 1, 1 ) ) IN ('t','y','1')
3417 OR LOWER(tmp_attr_set.hold) = 'holdable'; -- BOOL
3419 attr_set.opac_visible :=
3420 LOWER( SUBSTRING( tmp_attr_set.opac_vis, 1, 1 ) ) IN ('t','y','1')
3421 OR LOWER(tmp_attr_set.opac_vis) = 'visible'; -- BOOL
3424 LOWER( SUBSTRING( tmp_attr_set.r, 1, 1 ) ) IN ('t','y','1')
3425 OR LOWER(tmp_attr_set.r) = 'reference'; -- BOOL
3427 attr_set.copy_number := tmp_attr_set.cnum::INT; -- INT,
3428 attr_set.deposit_amount := tmp_attr_set.dep_amount::NUMERIC(6,2); -- NUMERIC(6,2),
3429 attr_set.price := tmp_attr_set.pr::NUMERIC(8,2); -- NUMERIC(8,2),
3431 attr_set.call_number := tmp_attr_set.cn; -- TEXT
3432 attr_set.barcode := tmp_attr_set.bc; -- TEXT,
3433 attr_set.circ_modifier := tmp_attr_set.circ_mod; -- TEXT,
3434 attr_set.circ_as_type := tmp_attr_set.circ_as; -- TEXT,
3435 attr_set.alert_message := tmp_attr_set.amessage; -- TEXT,
3436 attr_set.pub_note := tmp_attr_set.note; -- TEXT,
3437 attr_set.priv_note := tmp_attr_set.pnote; -- TEXT,
3438 attr_set.alert_message := tmp_attr_set.amessage; -- TEXT,
3440 RETURN NEXT attr_set;
3449 $$ LANGUAGE PLPGSQL;
3452 -- Evergreen DB patch XXXX.data.org-setting-ui.circ.billing.uncheck_bills_and_unfocus_payment_box.sql
3454 -- New org setting ui.circ.billing.uncheck_bills_and_unfocus_payment_box
3457 -- check whether patch can be applied
3458 SELECT evergreen.upgrade_deps_block_check('0584', :eg_version);
3460 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype )
3462 'ui.circ.billing.uncheck_bills_and_unfocus_payment_box',
3464 'ui.circ.billing.uncheck_bills_and_unfocus_payment_box',
3465 'GUI: Uncheck bills by default in the patron billing interface',
3470 'ui.circ.billing.uncheck_bills_and_unfocus_payment_box',
3471 'Uncheck bills by default in the patron billing interface,'
3472 || ' and focus on the Uncheck All button instead of the'
3473 || ' Payment Received field.',
3481 -- check whether patch can be applied
3482 SELECT evergreen.upgrade_deps_block_check('0585', :eg_version);
3484 INSERT into config.org_unit_setting_type
3485 ( name, label, description, datatype ) VALUES
3486 ( 'circ.checkout_fills_related_hold_exact_match_only',
3487 'Checkout Fills Related Hold On Valid Copy Only',
3488 '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.',
3492 -- check whether patch can be applied
3493 SELECT evergreen.upgrade_deps_block_check('0586', :eg_version);
3495 INSERT INTO permission.perm_list (id, code, description) VALUES (
3500 'Allows a user to authenticate and get a long-lived session (length configured in opensrf.xml)',
3506 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable)
3508 pgt.id, perm.id, aout.depth, FALSE
3510 permission.grp_tree pgt,
3511 permission.perm_list perm,
3512 actor.org_unit_type aout
3514 pgt.name = 'Users' AND
3515 aout.name = 'Consortium' AND
3516 perm.code = 'PERSISTENT_LOGIN';
3519 \qecho If this transaction succeeded, your users (staff and patrons) now have
3520 \qecho the PERSISTENT_LOGIN permission by default.
3524 -- Evergreen DB patch XXXX.data.org-setting-circ.offline.skip_foo_if_newer_status_changed_time.sql
3526 -- New org setting circ.offline.skip_checkout_if_newer_status_changed_time
3527 -- New org setting circ.offline.skip_renew_if_newer_status_changed_time
3528 -- New org setting circ.offline.skip_checkin_if_newer_status_changed_time
3531 -- check whether patch can be applied
3532 SELECT evergreen.upgrade_deps_block_check('0593', :eg_version);
3534 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype )
3536 'circ.offline.skip_checkout_if_newer_status_changed_time',
3538 'circ.offline.skip_checkout_if_newer_status_changed_time',
3539 'Offline: Skip offline checkout if newer item Status Changed Time.',
3544 'circ.offline.skip_checkout_if_newer_status_changed_time',
3545 'Skip offline checkout transaction (raise exception when'
3546 || ' processing) if item Status Changed Time is newer than the'
3547 || ' recorded transaction time. WARNING: The Reshelving to'
3548 || ' Available status rollover will trigger this.',
3554 'circ.offline.skip_renew_if_newer_status_changed_time',
3556 'circ.offline.skip_renew_if_newer_status_changed_time',
3557 'Offline: Skip offline renewal if newer item Status Changed Time.',
3562 'circ.offline.skip_renew_if_newer_status_changed_time',
3563 'Skip offline renewal transaction (raise exception when'
3564 || ' processing) if item Status Changed Time is newer than the'
3565 || ' recorded transaction time. WARNING: The Reshelving to'
3566 || ' Available status rollover will trigger this.',
3572 'circ.offline.skip_checkin_if_newer_status_changed_time',
3574 'circ.offline.skip_checkin_if_newer_status_changed_time',
3575 'Offline: Skip offline checkin if newer item Status Changed Time.',
3580 'circ.offline.skip_checkin_if_newer_status_changed_time',
3581 'Skip offline checkin transaction (raise exception when'
3582 || ' processing) if item Status Changed Time is newer than the'
3583 || ' recorded transaction time. WARNING: The Reshelving to'
3584 || ' Available status rollover will trigger this.',
3591 -- Evergreen DB patch YYYY.schema.acp_status_date_changed.sql
3593 -- Change trigger which updates copy status_changed_time to ignore the
3594 -- Reshelving->Available status rollover
3596 -- FIXME: 0039.schema.acp_status_date_changed.sql defines this the first time
3597 -- around, but along with the column itself, etc. And it gets modified with
3598 -- 0562.schema.copy_active_date.sql. Not sure how to use the supercedes /
3599 -- deprecate stuff for upgrade scripts, if it's even applicable when a given
3600 -- upgrade script is doing so much.
3602 -- check whether patch can be applied
3603 SELECT evergreen.upgrade_deps_block_check('0594', :eg_version);
3605 CREATE OR REPLACE FUNCTION asset.acp_status_changed()
3606 RETURNS TRIGGER AS $$
3608 IF NEW.status <> OLD.status AND NOT (NEW.status = 0 AND OLD.status = 7) THEN
3609 NEW.status_changed_time := now();
3610 IF NEW.active_date IS NULL AND NEW.status IN (SELECT id FROM config.copy_status WHERE copy_active = true) THEN
3611 NEW.active_date := now();
3616 $$ LANGUAGE plpgsql;
3618 -- Evergreen DB patch 0595.data.org-setting-ui.patron_search.result_cap.sql
3620 -- New org setting ui.patron_search.result_cap
3623 -- check whether patch can be applied
3624 SELECT evergreen.upgrade_deps_block_check('0595', :eg_version);
3626 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype )
3628 'ui.patron_search.result_cap',
3630 'ui.patron_search.result_cap',
3631 'GUI: Cap results in Patron Search at this number.',
3636 'ui.patron_search.result_cap',
3637 'So for example, if you search for John Doe, normally you would get'
3638 || ' at most 50 results. This setting allows you to raise or lower'
3646 -- Evergreen DB patch 0596.schema.vandelay-item-import-error-detail.sql
3648 -- check whether patch can be applied
3649 SELECT evergreen.upgrade_deps_block_check('0596', :eg_version);
3651 INSERT INTO vandelay.import_error ( code, description ) VALUES (
3652 'import.item.invalid.status', oils_i18n_gettext('import.item.invalid.status', 'Invalid value for "status"', 'vie', 'description') );
3653 INSERT INTO vandelay.import_error ( code, description ) VALUES (
3654 'import.item.invalid.price', oils_i18n_gettext('import.item.invalid.price', 'Invalid value for "price"', 'vie', 'description') );
3655 INSERT INTO vandelay.import_error ( code, description ) VALUES (
3656 'import.item.invalid.deposit_amount', oils_i18n_gettext('import.item.invalid.deposit_amount', 'Invalid value for "deposit_amount"', 'vie', 'description') );
3657 INSERT INTO vandelay.import_error ( code, description ) VALUES (
3658 'import.item.invalid.owning_lib', oils_i18n_gettext('import.item.invalid.owning_lib', 'Invalid value for "owning_lib"', 'vie', 'description') );
3659 INSERT INTO vandelay.import_error ( code, description ) VALUES (
3660 'import.item.invalid.circ_lib', oils_i18n_gettext('import.item.invalid.circ_lib', 'Invalid value for "circ_lib"', 'vie', 'description') );
3661 INSERT INTO vandelay.import_error ( code, description ) VALUES (
3662 'import.item.invalid.copy_number', oils_i18n_gettext('import.item.invalid.copy_number', 'Invalid value for "copy_number"', 'vie', 'description') );
3663 INSERT INTO vandelay.import_error ( code, description ) VALUES (
3664 'import.item.invalid.circ_as_type', oils_i18n_gettext('import.item.invalid.circ_as_type', 'Invalid value for "circ_as_type"', 'vie', 'description') );
3666 CREATE OR REPLACE FUNCTION vandelay.ingest_items ( import_id BIGINT, attr_def_id BIGINT ) RETURNS SETOF vandelay.import_item AS $$
3677 deposit_amount TEXT;
3690 tmp_attr_set RECORD;
3691 attr_set vandelay.import_item%ROWTYPE;
3698 SELECT * INTO attr_def FROM vandelay.import_item_attr_definition WHERE id = attr_def_id;
3702 attr_set.definition := attr_def.id;
3704 -- Build the combined XPath
3708 WHEN attr_def.owning_lib IS NULL THEN 'null()'
3709 WHEN LENGTH( attr_def.owning_lib ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.owning_lib || '"]'
3710 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.owning_lib
3715 WHEN attr_def.circ_lib IS NULL THEN 'null()'
3716 WHEN LENGTH( attr_def.circ_lib ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_lib || '"]'
3717 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_lib
3722 WHEN attr_def.call_number IS NULL THEN 'null()'
3723 WHEN LENGTH( attr_def.call_number ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.call_number || '"]'
3724 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.call_number
3729 WHEN attr_def.copy_number IS NULL THEN 'null()'
3730 WHEN LENGTH( attr_def.copy_number ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.copy_number || '"]'
3731 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.copy_number
3736 WHEN attr_def.status IS NULL THEN 'null()'
3737 WHEN LENGTH( attr_def.status ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.status || '"]'
3738 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.status
3743 WHEN attr_def.location IS NULL THEN 'null()'
3744 WHEN LENGTH( attr_def.location ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.location || '"]'
3745 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.location
3750 WHEN attr_def.circulate IS NULL THEN 'null()'
3751 WHEN LENGTH( attr_def.circulate ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circulate || '"]'
3752 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circulate
3757 WHEN attr_def.deposit IS NULL THEN 'null()'
3758 WHEN LENGTH( attr_def.deposit ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.deposit || '"]'
3759 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.deposit
3764 WHEN attr_def.deposit_amount IS NULL THEN 'null()'
3765 WHEN LENGTH( attr_def.deposit_amount ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.deposit_amount || '"]'
3766 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.deposit_amount
3771 WHEN attr_def.ref IS NULL THEN 'null()'
3772 WHEN LENGTH( attr_def.ref ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.ref || '"]'
3773 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.ref
3778 WHEN attr_def.holdable IS NULL THEN 'null()'
3779 WHEN LENGTH( attr_def.holdable ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.holdable || '"]'
3780 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.holdable
3785 WHEN attr_def.price IS NULL THEN 'null()'
3786 WHEN LENGTH( attr_def.price ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.price || '"]'
3787 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.price
3792 WHEN attr_def.barcode IS NULL THEN 'null()'
3793 WHEN LENGTH( attr_def.barcode ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.barcode || '"]'
3794 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.barcode
3799 WHEN attr_def.circ_modifier IS NULL THEN 'null()'
3800 WHEN LENGTH( attr_def.circ_modifier ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_modifier || '"]'
3801 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_modifier
3806 WHEN attr_def.circ_as_type IS NULL THEN 'null()'
3807 WHEN LENGTH( attr_def.circ_as_type ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_as_type || '"]'
3808 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_as_type
3813 WHEN attr_def.alert_message IS NULL THEN 'null()'
3814 WHEN LENGTH( attr_def.alert_message ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.alert_message || '"]'
3815 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.alert_message
3820 WHEN attr_def.opac_visible IS NULL THEN 'null()'
3821 WHEN LENGTH( attr_def.opac_visible ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.opac_visible || '"]'
3822 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.opac_visible
3827 WHEN attr_def.pub_note IS NULL THEN 'null()'
3828 WHEN LENGTH( attr_def.pub_note ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.pub_note || '"]'
3829 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.pub_note
3833 WHEN attr_def.priv_note IS NULL THEN 'null()'
3834 WHEN LENGTH( attr_def.priv_note ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.priv_note || '"]'
3835 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.priv_note
3840 owning_lib || '|' ||
3842 call_number || '|' ||
3843 copy_number || '|' ||
3848 deposit_amount || '|' ||
3853 circ_modifier || '|' ||
3854 circ_as_type || '|' ||
3855 alert_message || '|' ||
3862 FROM oils_xpath_table( 'id', 'marc', 'vandelay.queued_bib_record', xpath, 'id = ' || import_id )
3863 AS t( id INT, ol TEXT, clib TEXT, cn TEXT, cnum TEXT, cs TEXT, cl TEXT, circ TEXT,
3864 dep TEXT, dep_amount TEXT, r TEXT, hold TEXT, pr TEXT, bc TEXT, circ_mod TEXT,
3865 circ_as TEXT, amessage TEXT, note TEXT, pnote TEXT, opac_vis TEXT )
3868 attr_set.import_error := NULL;
3869 attr_set.error_detail := NULL;
3870 attr_set.deposit_amount := NULL;
3871 attr_set.copy_number := NULL;
3872 attr_set.price := NULL;
3874 IF tmp_attr_set.pr != '' THEN
3875 tmp_str = REGEXP_REPLACE(tmp_attr_set.pr, E'[^0-9\\.]', '', 'g');
3876 IF tmp_str = '' THEN
3877 attr_set.import_error := 'import.item.invalid.price';
3878 attr_set.error_detail := tmp_attr_set.pr; -- original value
3879 RETURN NEXT attr_set; CONTINUE;
3881 attr_set.price := tmp_str::NUMERIC(8,2);
3884 IF tmp_attr_set.dep_amount != '' THEN
3885 tmp_str = REGEXP_REPLACE(tmp_attr_set.dep_amount, E'[^0-9\\.]', '', 'g');
3886 IF tmp_str = '' THEN
3887 attr_set.import_error := 'import.item.invalid.deposit_amount';
3888 attr_set.error_detail := tmp_attr_set.dep_amount;
3889 RETURN NEXT attr_set; CONTINUE;
3891 attr_set.deposit_amount := tmp_str::NUMERIC(8,2);
3894 IF tmp_attr_set.cnum != '' THEN
3895 tmp_str = REGEXP_REPLACE(tmp_attr_set.cnum, E'[^0-9]', '', 'g');
3896 IF tmp_str = '' THEN
3897 attr_set.import_error := 'import.item.invalid.copy_number';
3898 attr_set.error_detail := tmp_attr_set.cnum;
3899 RETURN NEXT attr_set; CONTINUE;
3901 attr_set.copy_number := tmp_str::INT;
3904 IF tmp_attr_set.ol != '' THEN
3905 SELECT id INTO attr_set.owning_lib FROM actor.org_unit WHERE shortname = UPPER(tmp_attr_set.ol); -- INT
3907 attr_set.import_error := 'import.item.invalid.owning_lib';
3908 attr_set.error_detail := tmp_attr_set.ol;
3909 RETURN NEXT attr_set; CONTINUE;
3913 IF tmp_attr_set.clib != '' THEN
3914 SELECT id INTO attr_set.circ_lib FROM actor.org_unit WHERE shortname = UPPER(tmp_attr_set.clib); -- INT
3916 attr_set.import_error := 'import.item.invalid.circ_lib';
3917 attr_set.error_detail := tmp_attr_set.clib;
3918 RETURN NEXT attr_set; CONTINUE;
3922 IF tmp_attr_set.cs != '' THEN
3923 SELECT id INTO attr_set.status FROM config.copy_status WHERE LOWER(name) = LOWER(tmp_attr_set.cs); -- INT
3925 attr_set.import_error := 'import.item.invalid.status';
3926 attr_set.error_detail := tmp_attr_set.cs;
3927 RETURN NEXT attr_set; CONTINUE;
3931 IF tmp_attr_set.circ_mod != '' THEN
3932 SELECT code INTO attr_set.circ_modifier FROM config.circ_modifier WHERE code = tmp_attr_set.circ_mod;
3934 attr_set.import_error := 'import.item.invalid.circ_modifier';
3935 attr_set.error_detail := tmp_attr_set.circ_mod;
3936 RETURN NEXT attr_set; CONTINUE;
3940 IF tmp_attr_set.circ_as != '' THEN
3941 SELECT code INTO attr_set.circ_as_type FROM config.coded_value_map WHERE ctype = 'item_type' AND code = tmp_attr_set.circ_as;
3943 attr_set.import_error := 'import.item.invalid.circ_as_type';
3944 attr_set.error_detail := tmp_attr_set.circ_as;
3945 RETURN NEXT attr_set; CONTINUE;
3949 IF tmp_attr_set.cl != '' THEN
3951 -- search up the org unit tree for a matching copy location
3952 WITH RECURSIVE anscestor_depth AS (
3956 FROM actor.org_unit ou
3957 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
3958 WHERE ou.id = COALESCE(attr_set.owning_lib, attr_set.circ_lib)
3963 FROM actor.org_unit ou
3964 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
3965 JOIN anscestor_depth ot ON (ot.parent_ou = ou.id)
3966 ) SELECT cpl.id INTO attr_set.location
3967 FROM anscestor_depth a
3968 JOIN asset.copy_location cpl ON (cpl.owning_lib = a.id)
3969 WHERE LOWER(cpl.name) = LOWER(tmp_attr_set.cl)
3970 ORDER BY a.depth DESC
3974 attr_set.import_error := 'import.item.invalid.location';
3975 attr_set.error_detail := tmp_attr_set.cs;
3976 RETURN NEXT attr_set; CONTINUE;
3980 attr_set.circulate :=
3981 LOWER( SUBSTRING( tmp_attr_set.circ, 1, 1)) IN ('t','y','1')
3982 OR LOWER(tmp_attr_set.circ) = 'circulating'; -- BOOL
3985 LOWER( SUBSTRING( tmp_attr_set.dep, 1, 1 ) ) IN ('t','y','1')
3986 OR LOWER(tmp_attr_set.dep) = 'deposit'; -- BOOL
3988 attr_set.holdable :=
3989 LOWER( SUBSTRING( tmp_attr_set.hold, 1, 1 ) ) IN ('t','y','1')
3990 OR LOWER(tmp_attr_set.hold) = 'holdable'; -- BOOL
3992 attr_set.opac_visible :=
3993 LOWER( SUBSTRING( tmp_attr_set.opac_vis, 1, 1 ) ) IN ('t','y','1')
3994 OR LOWER(tmp_attr_set.opac_vis) = 'visible'; -- BOOL
3997 LOWER( SUBSTRING( tmp_attr_set.r, 1, 1 ) ) IN ('t','y','1')
3998 OR LOWER(tmp_attr_set.r) = 'reference'; -- BOOL
4000 attr_set.call_number := tmp_attr_set.cn; -- TEXT
4001 attr_set.barcode := tmp_attr_set.bc; -- TEXT,
4002 attr_set.alert_message := tmp_attr_set.amessage; -- TEXT,
4003 attr_set.pub_note := tmp_attr_set.note; -- TEXT,
4004 attr_set.priv_note := tmp_attr_set.pnote; -- TEXT,
4005 attr_set.alert_message := tmp_attr_set.amessage; -- TEXT,
4007 RETURN NEXT attr_set;
4016 $$ LANGUAGE PLPGSQL;
4018 CREATE OR REPLACE FUNCTION vandelay.ingest_bib_items ( ) RETURNS TRIGGER AS $func$
4021 item_data vandelay.import_item%ROWTYPE;
4024 IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
4028 SELECT item_attr_def INTO attr_def FROM vandelay.bib_queue WHERE id = NEW.queue;
4030 FOR item_data IN SELECT * FROM vandelay.ingest_items( NEW.id::BIGINT, attr_def ) LOOP
4031 INSERT INTO vandelay.import_item (
4057 item_data.definition,
4058 item_data.owning_lib,
4060 item_data.call_number,
4061 item_data.copy_number,
4064 item_data.circulate,
4066 item_data.deposit_amount,
4071 item_data.circ_modifier,
4072 item_data.circ_as_type,
4073 item_data.alert_message,
4075 item_data.priv_note,
4076 item_data.opac_visible,
4077 item_data.import_error,
4078 item_data.error_detail
4084 $func$ LANGUAGE PLPGSQL;
4086 -- Evergreen DB patch XXXX.schema.vandelay.bib_match_isxn_caseless.sql
4089 -- check whether patch can be applied
4090 SELECT evergreen.upgrade_deps_block_check('0597', :eg_version);
4092 CREATE INDEX metabib_full_rec_isxn_caseless_idx
4093 ON metabib.real_full_rec (LOWER(value))
4094 WHERE tag IN ('020', '022', '024');
4097 CREATE OR REPLACE FUNCTION vandelay.flatten_marc_hstore(
4099 ) RETURNS HSTORE AS $$
4103 ARRAY_ACCUM(tag || (COALESCE(subfield, ''))),
4109 CASE WHEN tag IN ('020', '022', '024') THEN -- caseless
4110 ARRAY_ACCUM(LOWER(value))::TEXT
4112 ARRAY_ACCUM(value)::TEXT
4114 FROM vandelay.flatten_marc(record_xml)
4115 GROUP BY tag, subfield ORDER BY tag, subfield
4119 $$ LANGUAGE PLPGSQL;
4121 CREATE OR REPLACE FUNCTION vandelay._get_expr_push_jrow(
4122 node vandelay.match_set_point
4123 ) RETURNS VOID AS $$
4131 -- remember $1 is tags_rstore, and $2 is svf_rstore
4141 IF node.tag IS NOT NULL THEN
4142 caseless := (node.tag IN ('020', '022', '024'));
4144 IF node.subfield IS NOT NULL THEN
4145 tagkey := tagkey || node.subfield;
4149 my_alias := 'n' || node.id::TEXT;
4151 jrow := 'LEFT JOIN (SELECT *, ' || node.quality ||
4152 ' AS quality FROM metabib.';
4153 IF node.tag IS NOT NULL THEN
4154 jrow := jrow || 'full_rec) ' || my_alias || ' ON (' ||
4155 my_alias || '.record = bre.id AND ' || my_alias || '.tag = ''' ||
4157 IF node.subfield IS NOT NULL THEN
4158 jrow := jrow || ' AND ' || my_alias || '.subfield = ''' ||
4159 node.subfield || '''';
4161 jrow := jrow || ' AND (';
4164 jrow := jrow || 'LOWER(' || my_alias || '.value) ' || op;
4166 jrow := jrow || my_alias || '.value ' || op;
4169 jrow := jrow || ' ANY(($1->''' || tagkey || ''')::TEXT[])))';
4171 jrow := jrow || 'record_attr) ' || my_alias || ' ON (' ||
4172 my_alias || '.id = bre.id AND (' ||
4173 my_alias || '.attrs->''' || node.svf ||
4174 ''' ' || op || ' $2->''' || node.svf || '''))';
4176 INSERT INTO _vandelay_tmp_jrows (j) VALUES (jrow);
4178 $$ LANGUAGE PLPGSQL;
4180 -- Evergreen DB patch 0598.schema.vandelay_one_match_per.sql
4184 -- check whether patch can be applied
4185 SELECT evergreen.upgrade_deps_block_check('0598', :eg_version);
4187 CREATE OR REPLACE FUNCTION vandelay.match_set_test_marcxml(
4188 match_set_id INTEGER, record_xml TEXT
4189 ) RETURNS SETOF vandelay.match_set_test_result AS $$
4200 tags_rstore := vandelay.flatten_marc_hstore(record_xml);
4201 svf_rstore := vandelay.extract_rec_attrs(record_xml);
4203 CREATE TEMPORARY TABLE _vandelay_tmp_qrows (q INTEGER);
4204 CREATE TEMPORARY TABLE _vandelay_tmp_jrows (j TEXT);
4206 -- generate the where clause and return that directly (into wq), and as
4207 -- a side-effect, populate the _vandelay_tmp_[qj]rows tables.
4208 wq := vandelay.get_expr_from_match_set(match_set_id);
4210 query_ := 'SELECT DISTINCT(bre.id) AS record, ';
4212 -- qrows table is for the quality bits we add to the SELECT clause
4213 SELECT ARRAY_TO_STRING(
4214 ARRAY_ACCUM('COALESCE(n' || q::TEXT || '.quality, 0)'), ' + '
4215 ) INTO coal FROM _vandelay_tmp_qrows;
4217 -- our query string so far is the SELECT clause and the inital FROM.
4218 -- no JOINs yet nor the WHERE clause
4219 query_ := query_ || coal || ' AS quality ' || E'\n' ||
4220 'FROM biblio.record_entry bre ';
4222 -- jrows table is for the joins we must make (and the real text conditions)
4223 SELECT ARRAY_TO_STRING(ARRAY_ACCUM(j), E'\n') INTO joins
4224 FROM _vandelay_tmp_jrows;
4226 -- add those joins and the where clause to our query.
4227 query_ := query_ || joins || E'\n' || 'WHERE ' || wq || ' AND not bre.deleted';
4229 -- this will return rows of record,quality
4230 FOR rec IN EXECUTE query_ USING tags_rstore, svf_rstore LOOP
4234 DROP TABLE _vandelay_tmp_qrows;
4235 DROP TABLE _vandelay_tmp_jrows;
4239 $$ LANGUAGE PLPGSQL;
4241 -- Evergreen DB patch 0606.schema.czs_use_perm_column.sql
4243 -- This adds a column to config.z3950_source called use_perm.
4244 -- The idea is that if a permission is set for a given source,
4245 -- then staff will need the referenced permission to use that
4249 -- check whether patch can be applied
4250 SELECT evergreen.upgrade_deps_block_check('0606', :eg_version);
4252 ALTER TABLE config.z3950_source
4253 ADD COLUMN use_perm INT REFERENCES permission.perm_list (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
4255 COMMENT ON COLUMN config.z3950_source.use_perm IS $$
4256 If set, this permission is required for the source to be listed in the staff
4257 client Z39.50 interface. Similar to permission.grp_tree.application_perm.
4260 -- Evergreen DB patch 0608.data.vandelay-export-error-match-info.sql
4265 -- check whether patch can be applied
4266 SELECT evergreen.upgrade_deps_block_check('0608', :eg_version);
4268 -- Add vqbr.import_error, vqbr.error_detail, and vqbr.matches.size to queue print output
4270 UPDATE action_trigger.event_definition SET template = $$
4273 Queue ID: [% target.0.queue.id %]
4274 Queue Name: [% target.0.queue.name %]
4275 Queue Type: [% target.0.queue.queue_type %]
4276 Complete? [% target.0.queue.complete %]
4278 [% FOR vqbr IN target %]
4280 Title of work | [% helpers.get_queued_bib_attr('title',vqbr.attributes) %]
4281 Author of work | [% helpers.get_queued_bib_attr('author',vqbr.attributes) %]
4282 Language of work | [% helpers.get_queued_bib_attr('language',vqbr.attributes) %]
4283 Pagination | [% helpers.get_queued_bib_attr('pagination',vqbr.attributes) %]
4284 ISBN | [% helpers.get_queued_bib_attr('isbn',vqbr.attributes) %]
4285 ISSN | [% helpers.get_queued_bib_attr('issn',vqbr.attributes) %]
4286 Price | [% helpers.get_queued_bib_attr('price',vqbr.attributes) %]
4287 Accession Number | [% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) %]
4288 TCN Value | [% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) %]
4289 TCN Source | [% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) %]
4290 Internal ID | [% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) %]
4291 Publisher | [% helpers.get_queued_bib_attr('publisher',vqbr.attributes) %]
4292 Publication Date | [% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) %]
4293 Edition | [% helpers.get_queued_bib_attr('edition',vqbr.attributes) %]
4294 Item Barcode | [% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) %]
4295 Import Error | [% vqbr.import_error %]
4296 Error Detail | [% vqbr.error_detail %]
4297 Match Count | [% vqbr.matches.size %]
4305 -- Do the same for the CVS version
4307 UPDATE action_trigger.event_definition SET template = $$
4309 "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"
4310 [% 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 %]"
4315 -- Add matches to the env for both
4316 INSERT INTO action_trigger.environment (event_def, path) VALUES (39, 'matches');
4317 INSERT INTO action_trigger.environment (event_def, path) VALUES (40, 'matches');
4320 -- Evergreen DB patch XXXX.data.acq-copy-creator-from-receiver.sql
4322 -- check whether patch can be applied
4323 SELECT evergreen.upgrade_deps_block_check('0609', :eg_version);
4325 ALTER TABLE acq.lineitem_detail
4326 ADD COLUMN receiver INT REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED;
4329 -- Evergreen DB patch XXXX.data.acq-copy-creator-from-receiver.sql
4331 -- check whether patch can be applied
4332 SELECT evergreen.upgrade_deps_block_check('0610', :eg_version);
4334 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype ) VALUES (
4335 'acq.copy_creator_uses_receiver',
4337 'acq.copy_creator_uses_receiver',
4338 'Acq: Set copy creator as receiver',
4343 'acq.copy_creator_uses_receiver',
4344 'When receiving a copy in acquisitions, set the copy "creator" to be the staff that received the copy',
4351 -- Evergreen DB patch 0611.data.magic_macros.sql
4353 -- check whether patch can be applied
4354 SELECT evergreen.upgrade_deps_block_check('0611', :eg_version);
4356 INSERT into config.org_unit_setting_type
4357 ( name, label, description, datatype ) VALUES
4359 'circ.staff_client.receipt.header_text',
4361 'circ.staff_client.receipt.header_text',
4362 'Receipt Template: Content of header_text include',
4367 'circ.staff_client.receipt.header_text',
4368 'Text/HTML/Macros to be inserted into receipt templates in place of %INCLUDE(header_text)%',
4375 'circ.staff_client.receipt.footer_text',
4377 'circ.staff_client.receipt.footer_text',
4378 'Receipt Template: Content of footer_text include',
4383 'circ.staff_client.receipt.footer_text',
4384 'Text/HTML/Macros to be inserted into receipt templates in place of %INCLUDE(footer_text)%',
4391 'circ.staff_client.receipt.notice_text',
4393 'circ.staff_client.receipt.notice_text',
4394 'Receipt Template: Content of notice_text include',
4399 'circ.staff_client.receipt.notice_text',
4400 'Text/HTML/Macros to be inserted into receipt templates in place of %INCLUDE(notice_text)%',
4407 'circ.staff_client.receipt.alert_text',
4409 'circ.staff_client.receipt.alert_text',
4410 'Receipt Template: Content of alert_text include',
4415 'circ.staff_client.receipt.alert_text',
4416 'Text/HTML/Macros to be inserted into receipt templates in place of %INCLUDE(alert_text)%',
4423 'circ.staff_client.receipt.event_text',
4425 'circ.staff_client.receipt.event_text',
4426 'Receipt Template: Content of event_text include',
4431 'circ.staff_client.receipt.event_text',
4432 'Text/HTML/Macros to be inserted into receipt templates in place of %INCLUDE(event_text)%',
4439 -- Evergreen DB patch 0612.schema.authority_overlay_protection.sql
4443 -- check whether patch can be applied
4444 SELECT evergreen.upgrade_deps_block_check('0612', :eg_version);
4446 -- FIXME: add/check SQL statements to perform the upgrade
4448 -- Function to generate an ephemeral overlay template from an authority record
4449 CREATE OR REPLACE FUNCTION authority.generate_overlay_template (source_xml TEXT) RETURNS TEXT AS $f$
4452 main_entry authority.control_set_authority_field%ROWTYPE;
4453 bib_field authority.control_set_bib_field%ROWTYPE;
4454 auth_id INT DEFAULT oils_xpath_string('//*[@tag="901"]/*[local-name()="subfield" and @code="c"]', source_xml)::INT;
4455 replace_data XML[] DEFAULT '{}'::XML[];
4456 replace_rules TEXT[] DEFAULT '{}'::TEXT[];
4459 IF auth_id IS NULL THEN
4463 -- Default to the LoC controll set
4464 SELECT control_set INTO cset FROM authority.record_entry WHERE id = auth_id;
4466 -- if none, make a best guess
4467 IF cset IS NULL THEN
4468 SELECT control_set INTO cset
4469 FROM authority.control_set_authority_field
4471 SELECT UNNEST(XPATH('//*[starts-with(@tag,"1")]/@tag',marc::XML)::TEXT[])
4472 FROM authority.record_entry
4478 -- if STILL none, no-op change
4479 IF cset IS NULL THEN
4482 XMLATTRIBUTES('http://www.loc.gov/MARC21/slim' AS xmlns),
4483 XMLELEMENT( name leader, '00881nam a2200193 4500'),
4486 XMLATTRIBUTES( '905' AS tag, ' ' AS ind1, ' ' AS ind2),
4489 XMLATTRIBUTES('d' AS code),
4496 FOR main_entry IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset LOOP
4497 auth_field := XPATH('//*[@tag="'||main_entry.tag||'"][1]',source_xml::XML);
4498 IF ARRAY_LENGTH(auth_field,1) > 0 THEN
4499 FOR bib_field IN SELECT * FROM authority.control_set_bib_field WHERE authority_field = main_entry.id LOOP
4500 replace_data := replace_data || XMLELEMENT( name datafield, XMLATTRIBUTES(bib_field.tag AS tag), XPATH('//*[local-name()="subfield"]',auth_field[1])::XML[]);
4501 replace_rules := replace_rules || ( bib_field.tag || main_entry.sf_list || E'[0~\\)' || auth_id || '$]' );
4509 XMLATTRIBUTES('http://www.loc.gov/MARC21/slim' AS xmlns),
4510 XMLELEMENT( name leader, '00881nam a2200193 4500'),
4514 XMLATTRIBUTES( '905' AS tag, ' ' AS ind1, ' ' AS ind2),
4517 XMLATTRIBUTES('r' AS code),
4518 ARRAY_TO_STRING(replace_rules,',')
4523 $f$ STABLE LANGUAGE PLPGSQL;
4527 -- Evergreen DB patch 0613.schema.vandelay_isxn_normalization.sql
4531 -- check whether patch can be applied
4532 SELECT evergreen.upgrade_deps_block_check('0613', :eg_version);
4534 CREATE OR REPLACE FUNCTION vandelay.flatten_marc_hstore(
4536 ) RETURNS HSTORE AS $func$
4540 ARRAY_ACCUM(tag || (COALESCE(subfield, ''))),
4544 SELECT tag, subfield, ARRAY_ACCUM(value)::TEXT AS value
4547 CASE WHEN tag = '020' THEN -- caseless -- isbn
4548 LOWER((REGEXP_MATCHES(value,$$^(\S{10,17})$$))[1] || '%')
4549 WHEN tag = '022' THEN -- caseless -- issn
4550 LOWER((REGEXP_MATCHES(value,$$^(\S{4}[- ]?\S{4})$$))[1] || '%')
4551 WHEN tag = '024' THEN -- caseless -- upc (other)
4556 FROM vandelay.flatten_marc(record_xml)) x
4557 GROUP BY tag, subfield ORDER BY tag, subfield
4561 $func$ LANGUAGE PLPGSQL;
4563 CREATE OR REPLACE FUNCTION vandelay._get_expr_push_jrow(
4564 node vandelay.match_set_point
4565 ) RETURNS VOID AS $$
4573 -- remember $1 is tags_rstore, and $2 is svf_rstore
4577 IF node.tag IS NOT NULL THEN
4578 caseless := (node.tag IN ('020', '022', '024'));
4580 IF node.subfield IS NOT NULL THEN
4581 tagkey := tagkey || node.subfield;
4599 my_alias := 'n' || node.id::TEXT;
4601 jrow := 'LEFT JOIN (SELECT *, ' || node.quality ||
4602 ' AS quality FROM metabib.';
4603 IF node.tag IS NOT NULL THEN
4604 jrow := jrow || 'full_rec) ' || my_alias || ' ON (' ||
4605 my_alias || '.record = bre.id AND ' || my_alias || '.tag = ''' ||
4607 IF node.subfield IS NOT NULL THEN
4608 jrow := jrow || ' AND ' || my_alias || '.subfield = ''' ||
4609 node.subfield || '''';
4611 jrow := jrow || ' AND (';
4614 jrow := jrow || 'LOWER(' || my_alias || '.value) ' || op;
4616 jrow := jrow || my_alias || '.value ' || op;
4619 jrow := jrow || ' ANY(($1->''' || tagkey || ''')::TEXT[])))';
4621 jrow := jrow || 'record_attr) ' || my_alias || ' ON (' ||
4622 my_alias || '.id = bre.id AND (' ||
4623 my_alias || '.attrs->''' || node.svf ||
4624 ''' ' || op || ' $2->''' || node.svf || '''))';
4626 INSERT INTO _vandelay_tmp_jrows (j) VALUES (jrow);
4628 $$ LANGUAGE PLPGSQL;
4632 -- Evergreen DB patch XXXX.schema.generic-mapping-index-normalizer.sql
4635 -- check whether patch can be applied
4636 SELECT evergreen.upgrade_deps_block_check('0615', :eg_version);
4638 -- evergreen.generic_map_normalizer
4640 CREATE OR REPLACE FUNCTION evergreen.generic_map_normalizer ( TEXT, TEXT ) RETURNS TEXT AS $f$
4644 my $default = $string;
4647 while (/^\s*?(.*?)\s*?=>\s*?(\S+)\s*/) {
4651 $map{$2} = [split(/\s*,\s*/, $1)];
4656 for my $key ( keys %map ) {
4657 return $key if (grep { $_ eq $string } @{ $map{$key} });
4662 $f$ LANGUAGE PLPERLU;
4664 -- evergreen.generic_map_normalizer
4666 INSERT INTO config.index_normalizer (name, description, func, param_count) VALUES (
4667 'Generic Mapping Normalizer',
4668 'Map values or sets of values to new values',
4669 'generic_map_normalizer',
4674 SELECT evergreen.upgrade_deps_block_check('0616', :eg_version);
4676 CREATE OR REPLACE FUNCTION actor.org_unit_prox_update () RETURNS TRIGGER as $$
4680 IF TG_OP = 'DELETE' THEN
4682 DELETE FROM actor.org_unit_proximity WHERE (from_org = OLD.id or to_org= OLD.id);
4686 IF TG_OP = 'UPDATE' THEN
4688 IF NEW.parent_ou <> OLD.parent_ou THEN
4690 DELETE FROM actor.org_unit_proximity WHERE (from_org = OLD.id or to_org= OLD.id);
4691 INSERT INTO actor.org_unit_proximity (from_org, to_org, prox)
4692 SELECT l.id, r.id, actor.org_unit_proximity(l.id,r.id)
4693 FROM actor.org_unit l, actor.org_unit r
4694 WHERE (l.id = NEW.id or r.id = NEW.id);
4700 IF TG_OP = 'INSERT' THEN
4702 INSERT INTO actor.org_unit_proximity (from_org, to_org, prox)
4703 SELECT l.id, r.id, actor.org_unit_proximity(l.id,r.id)
4704 FROM actor.org_unit l, actor.org_unit r
4705 WHERE (l.id = NEW.id or r.id = NEW.id);
4712 $$ LANGUAGE plpgsql;
4715 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 ();
4718 SELECT evergreen.upgrade_deps_block_check('0617', :eg_version);
4720 -- add notify columns to booking.reservation
4721 ALTER TABLE booking.reservation
4722 ADD COLUMN email_notify BOOLEAN NOT NULL DEFAULT FALSE;
4724 -- create the hook and validator
4725 INSERT INTO action_trigger.hook (key, core_type, description, passive)
4726 VALUES ('reservation.available', 'bresv', 'A reservation is available for pickup', false);
4727 INSERT INTO action_trigger.validator (module, description)
4728 VALUES ('ReservationIsAvailable','Checked that a reserved resource is available for checkout');
4730 -- create org unit setting to toggle checkbox display
4731 INSERT INTO config.org_unit_setting_type (name, label, description, datatype)
4732 VALUES ('booking.allow_email_notify', 'booking.allow_email_notify', 'Permit email notification when a reservation is ready for pickup.', 'bool');
4735 SELECT evergreen.upgrade_deps_block_check('0618', :eg_version);
4737 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';
4739 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';
4741 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';
4743 -- Evergreen DB patch 0619.schema.au_last_update_time.sql
4745 -- check whether patch can be applied
4746 SELECT evergreen.upgrade_deps_block_check('0619', :eg_version);
4748 -- Add new column last_update_time to actor.usr, with trigger to maintain it
4749 -- Add corresponding new column to auditor.actor_usr_history
4751 ALTER TABLE actor.usr
4752 ADD COLUMN last_update_time TIMESTAMPTZ;
4754 ALTER TABLE auditor.actor_usr_history
4755 ADD COLUMN last_update_time TIMESTAMPTZ;
4757 CREATE OR REPLACE FUNCTION actor.au_updated()
4758 RETURNS TRIGGER AS $$
4760 NEW.last_update_time := now();
4763 $$ LANGUAGE plpgsql;
4765 CREATE TRIGGER au_update_trig
4766 BEFORE INSERT OR UPDATE ON actor.usr
4767 FOR EACH ROW EXECUTE PROCEDURE actor.au_updated();
4769 -- Evergreen DB patch XXXX.data.opac_payment_history_age_limit.sql
4772 SELECT evergreen.upgrade_deps_block_check('0621', :eg_version);
4774 INSERT into config.org_unit_setting_type (name, label, description, datatype)
4776 'opac.payment_history_age_limit',
4777 oils_i18n_gettext('opac.payment_history_age_limit',
4778 'OPAC: Payment History Age Limit', 'coust', 'label'),
4779 oils_i18n_gettext('opac.payment_history_age_limit',
4780 'The OPAC should not display payments by patrons that are older than any interval defined here.', 'coust', 'label'),
4784 -- Updates config.org_unit_setting_type to remove the old tag prefixes for once
4785 -- groups have been added.
4788 SELECT evergreen.upgrade_deps_block_check('0622', :eg_version);
4790 INSERT INTO config.settings_group (name, label) VALUES
4791 ('sys', oils_i18n_gettext('config.settings_group.system', 'System', 'coust', 'label')),
4792 ('gui', oils_i18n_gettext('config.settings_group.gui', 'GUI', 'coust', 'label')),
4793 ('lib', oils_i18n_gettext('config.settings_group.lib', 'Library', 'coust', 'label')),
4794 ('sec', oils_i18n_gettext('config.settings_group.sec', 'Security', 'coust', 'label')),
4795 ('cat', oils_i18n_gettext('config.settings_group.cat', 'Cataloging', 'coust', 'label')),
4796 ('holds', oils_i18n_gettext('config.settings_group.holds', 'Holds', 'coust', 'label')),
4797 ('circ', oils_i18n_gettext('config.settings_group.circulation', 'Circulation', 'coust', 'label')),
4798 ('self', oils_i18n_gettext('config.settings_group.self', 'Self Check', 'coust', 'label')),
4799 ('opac', oils_i18n_gettext('config.settings_group.opac', 'OPAC', 'coust', 'label')),
4800 ('prog', oils_i18n_gettext('config.settings_group.program', 'Program', 'coust', 'label')),
4801 ('glob', oils_i18n_gettext('config.settings_group.global', 'Global', 'coust', 'label')),
4802 ('finance', oils_i18n_gettext('config.settings_group.finances', 'Finanaces', 'coust', 'label')),
4803 ('credit', oils_i18n_gettext('config.settings_group.ccp', 'Credit Card Processing', 'coust', 'label')),
4804 ('serial', oils_i18n_gettext('config.settings_group.serial', 'Serials', 'coust', 'label')),
4805 ('recall', oils_i18n_gettext('config.settings_group.recall', 'Recalls', 'coust', 'label')),
4806 ('booking', oils_i18n_gettext('config.settings_group.booking', 'Booking', 'coust', 'label')),
4807 ('offline', oils_i18n_gettext('config.settings_group.offline', 'Offline', 'coust', 'label')),
4808 ('receipt_template', oils_i18n_gettext('config.settings_group.receipt_template', 'Receipt Template', 'coust', 'label'));
4810 UPDATE config.org_unit_setting_type SET grp = 'lib', label='Set copy creator as receiver' WHERE name = 'acq.copy_creator_uses_receiver';
4811 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'acq.default_circ_modifier';
4812 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'acq.default_copy_location';
4813 UPDATE config.org_unit_setting_type SET grp = 'finance' WHERE name = 'acq.fund.balance_limit.block';
4814 UPDATE config.org_unit_setting_type SET grp = 'finance' WHERE name = 'acq.fund.balance_limit.warn';
4815 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'acq.holds.allow_holds_from_purchase_request';
4816 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'acq.tmp_barcode_prefix';
4817 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'acq.tmp_callnumber_prefix';
4818 UPDATE config.org_unit_setting_type SET grp = 'sec' WHERE name = 'auth.opac_timeout';
4819 UPDATE config.org_unit_setting_type SET grp = 'sec' WHERE name = 'auth.persistent_login_interval';
4820 UPDATE config.org_unit_setting_type SET grp = 'sec' WHERE name = 'auth.staff_timeout';
4821 UPDATE config.org_unit_setting_type SET grp = 'booking' WHERE name = 'booking.allow_email_notify';
4822 UPDATE config.org_unit_setting_type SET grp = 'gui' WHERE name = 'cat.bib.alert_on_empty';
4823 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';
4824 UPDATE config.org_unit_setting_type SET grp = 'prog' WHERE name = 'cat.bib.keep_on_empty';
4825 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Default Classification Scheme' WHERE name = 'cat.default_classification_scheme';
4826 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Default copy status (fast add)' WHERE name = 'cat.default_copy_status_fast';
4827 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Default copy status (normal)' WHERE name = 'cat.default_copy_status_normal';
4828 UPDATE config.org_unit_setting_type SET grp = 'finance' WHERE name = 'cat.default_item_price';
4829 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Spine and pocket label font family' WHERE name = 'cat.label.font.family';
4830 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Spine and pocket label font size' WHERE name = 'cat.label.font.size';
4831 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Spine and pocket label font weight' WHERE name = 'cat.label.font.weight';
4832 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';
4833 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Spine label maximum lines' WHERE name = 'cat.spine.line.height';
4834 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Spine label left margin' WHERE name = 'cat.spine.line.margin';
4835 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Spine label line width' WHERE name = 'cat.spine.line.width';
4836 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Delete volume with last copy' WHERE name = 'cat.volume.delete_on_empty';
4837 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';
4838 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Block Renewal of Items Needed for Holds' WHERE name = 'circ.block_renews_for_holds';
4839 UPDATE config.org_unit_setting_type SET grp = 'booking', label='Elbow room' WHERE name = 'circ.booking_reservation.default_elbow_room';
4840 UPDATE config.org_unit_setting_type SET grp = 'finance' WHERE name = 'circ.charge_lost_on_zero';
4841 UPDATE config.org_unit_setting_type SET grp = 'finance' WHERE name = 'circ.charge_on_damaged';
4842 UPDATE config.org_unit_setting_type SET grp = 'circ' WHERE name = 'circ.checkout_auto_renew_age';
4843 UPDATE config.org_unit_setting_type SET grp = 'circ' WHERE name = 'circ.checkout_fills_related_hold';
4844 UPDATE config.org_unit_setting_type SET grp = 'circ' WHERE name = 'circ.checkout_fills_related_hold_exact_match_only';
4845 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'circ.claim_never_checked_out.mark_missing';
4846 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'circ.claim_return.copy_status';
4847 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'circ.damaged.void_ovedue';
4848 UPDATE config.org_unit_setting_type SET grp = 'finance' WHERE name = 'circ.damaged_item_processing_fee';
4849 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';
4850 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Hard boundary' WHERE name = 'circ.hold_boundary.hard';
4851 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Soft boundary' WHERE name = 'circ.hold_boundary.soft';
4852 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Expire Alert Interval' WHERE name = 'circ.hold_expire_alert_interval';
4853 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Expire Interval' WHERE name = 'circ.hold_expire_interval';
4854 UPDATE config.org_unit_setting_type SET grp = 'circ' WHERE name = 'circ.hold_shelf_status_delay';
4855 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Soft stalling interval' WHERE name = 'circ.hold_stalling.soft';
4856 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Hard stalling interval' WHERE name = 'circ.hold_stalling_hard';
4857 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Use Active Date for Age Protection' WHERE name = 'circ.holds.age_protect.active_date';
4858 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Behind Desk Pickup Supported' WHERE name = 'circ.holds.behind_desk_pickup_supported';
4859 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Canceled holds display age' WHERE name = 'circ.holds.canceled.display_age';
4860 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Canceled holds display count' WHERE name = 'circ.holds.canceled.display_count';
4861 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Clear shelf copy status' WHERE name = 'circ.holds.clear_shelf.copy_status';
4862 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';
4863 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Default Estimated Wait' WHERE name = 'circ.holds.default_estimated_wait_interval';
4864 UPDATE config.org_unit_setting_type SET grp = 'holds' WHERE name = 'circ.holds.default_shelf_expire_interval';
4865 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';
4866 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Has Local Copy Alert' WHERE name = 'circ.holds.hold_has_copy_at.alert';
4867 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Has Local Copy Block' WHERE name = 'circ.holds.hold_has_copy_at.block';
4868 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Maximum library target attempts' WHERE name = 'circ.holds.max_org_unit_target_loops';
4869 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Minimum Estimated Wait' WHERE name = 'circ.holds.min_estimated_wait_interval';
4870 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Org Unit Target Weight' WHERE name = 'circ.holds.org_unit_target_weight';
4871 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';
4872 UPDATE config.org_unit_setting_type SET grp = 'recall', label='Truncated loan period.' WHERE name = 'circ.holds.recall_return_interval';
4873 UPDATE config.org_unit_setting_type SET grp = 'recall', label='Circulation duration that triggers a recall.' WHERE name = 'circ.holds.recall_threshold';
4874 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';
4875 UPDATE config.org_unit_setting_type SET grp = 'holds' WHERE name = 'circ.holds.target_skip_me';
4876 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Reset request time on un-cancel' WHERE name = 'circ.holds.uncancel.reset_request_time';
4877 UPDATE config.org_unit_setting_type SET grp = 'holds', label='FIFO' WHERE name = 'circ.holds_fifo';
4878 UPDATE config.org_unit_setting_type SET grp = 'gui' WHERE name = 'circ.item_checkout_history.max';
4879 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Lost Checkin Generates New Overdues' WHERE name = 'circ.lost.generate_overdue_on_checkin';
4880 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Lost items usable on checkin' WHERE name = 'circ.lost_immediately_available';
4881 UPDATE config.org_unit_setting_type SET grp = 'finance' WHERE name = 'circ.lost_materials_processing_fee';
4882 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Void lost max interval' WHERE name = 'circ.max_accept_return_of_lost';
4883 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Cap Max Fine at Item Price' WHERE name = 'circ.max_fine.cap_at_price';
4884 UPDATE config.org_unit_setting_type SET grp = 'circ' WHERE name = 'circ.max_patron_claim_return_count';
4885 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Item Status for Missing Pieces' WHERE name = 'circ.missing_pieces.copy_status';
4886 UPDATE config.org_unit_setting_type SET grp = 'sec' WHERE name = 'circ.obscure_dob';
4887 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';
4888 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';
4889 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';
4890 UPDATE config.org_unit_setting_type SET grp = 'sec', label='Offline: Patron Usernames Allowed' WHERE name = 'circ.offline.username_allowed';
4891 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';
4892 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';
4893 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';
4894 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';
4895 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';
4896 UPDATE config.org_unit_setting_type SET grp = 'circ' WHERE name = 'circ.patron_invalid_address_apply_penalty';
4897 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'circ.pre_cat_copy_circ_lib';
4898 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'circ.reshelving_complete.interval';
4899 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Restore overdues on lost item return' WHERE name = 'circ.restore_overdue_on_lost_return';
4900 UPDATE config.org_unit_setting_type SET grp = 'self', label='Pop-up alert for errors' WHERE name = 'circ.selfcheck.alert.popup';
4901 UPDATE config.org_unit_setting_type SET grp = 'self', label='Audio Alerts' WHERE name = 'circ.selfcheck.alert.sound';
4902 UPDATE config.org_unit_setting_type SET grp = 'self' WHERE name = 'circ.selfcheck.auto_override_checkout_events';
4903 UPDATE config.org_unit_setting_type SET grp = 'self', label='Block copy checkout status' WHERE name = 'circ.selfcheck.block_checkout_on_copy_status';
4904 UPDATE config.org_unit_setting_type SET grp = 'self', label='Patron Login Timeout (in seconds)' WHERE name = 'circ.selfcheck.patron_login_timeout';
4905 UPDATE config.org_unit_setting_type SET grp = 'self', label='Require Patron Password' WHERE name = 'circ.selfcheck.patron_password_required';
4906 UPDATE config.org_unit_setting_type SET grp = 'self', label='Require patron password' WHERE name = 'circ.selfcheck.require_patron_password';
4907 UPDATE config.org_unit_setting_type SET grp = 'self', label='Workstation Required' WHERE name = 'circ.selfcheck.workstation_required';
4908 UPDATE config.org_unit_setting_type SET grp = 'circ' WHERE name = 'circ.staff_client.actor_on_checkout';
4909 UPDATE config.org_unit_setting_type SET grp = 'prog' WHERE name = 'circ.staff_client.do_not_auto_attempt_print';
4910 UPDATE config.org_unit_setting_type SET grp = 'receipt_template', label='Content of alert_text include' WHERE name = 'circ.staff_client.receipt.alert_text';
4911 UPDATE config.org_unit_setting_type SET grp = 'receipt_template', label='Content of event_text include' WHERE name = 'circ.staff_client.receipt.event_text';
4912 UPDATE config.org_unit_setting_type SET grp = 'receipt_template', label='Content of footer_text include' WHERE name = 'circ.staff_client.receipt.footer_text';
4913 UPDATE config.org_unit_setting_type SET grp = 'receipt_template', label='Content of header_text include' WHERE name = 'circ.staff_client.receipt.header_text';
4914 UPDATE config.org_unit_setting_type SET grp = 'receipt_template', label='Content of notice_text include' WHERE name = 'circ.staff_client.receipt.notice_text';
4915 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Minimum Transit Checkin Interval' WHERE name = 'circ.transit.min_checkin_interval';
4916 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Patron Merge Deactivate Card' WHERE name = 'circ.user_merge.deactivate_cards';
4917 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Patron Merge Address Delete' WHERE name = 'circ.user_merge.delete_addresses';
4918 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Patron Merge Barcode Delete' WHERE name = 'circ.user_merge.delete_cards';
4919 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Void lost item billing when returned' WHERE name = 'circ.void_lost_on_checkin';
4920 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';
4921 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';
4922 UPDATE config.org_unit_setting_type SET grp = 'finance' WHERE name = 'credit.payments.allow';
4923 UPDATE config.org_unit_setting_type SET grp = 'credit', label='Enable AuthorizeNet payments' WHERE name = 'credit.processor.authorizenet.enabled';
4924 UPDATE config.org_unit_setting_type SET grp = 'credit', label='AuthorizeNet login' WHERE name = 'credit.processor.authorizenet.login';
4925 UPDATE config.org_unit_setting_type SET grp = 'credit', label='AuthorizeNet password' WHERE name = 'credit.processor.authorizenet.password';
4926 UPDATE config.org_unit_setting_type SET grp = 'credit', label='AuthorizeNet server' WHERE name = 'credit.processor.authorizenet.server';
4927 UPDATE config.org_unit_setting_type SET grp = 'credit', label='AuthorizeNet test mode' WHERE name = 'credit.processor.authorizenet.testmode';
4928 UPDATE config.org_unit_setting_type SET grp = 'credit', label='Name default credit processor' WHERE name = 'credit.processor.default';
4929 UPDATE config.org_unit_setting_type SET grp = 'credit', label='Enable PayflowPro payments' WHERE name = 'credit.processor.payflowpro.enabled';
4930 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayflowPro login/merchant ID' WHERE name = 'credit.processor.payflowpro.login';
4931 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayflowPro partner' WHERE name = 'credit.processor.payflowpro.partner';
4932 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayflowPro password' WHERE name = 'credit.processor.payflowpro.password';
4933 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayflowPro test mode' WHERE name = 'credit.processor.payflowpro.testmode';
4934 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayflowPro vendor' WHERE name = 'credit.processor.payflowpro.vendor';
4935 UPDATE config.org_unit_setting_type SET grp = 'credit', label='Enable PayPal payments' WHERE name = 'credit.processor.paypal.enabled';
4936 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayPal login' WHERE name = 'credit.processor.paypal.login';
4937 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayPal password' WHERE name = 'credit.processor.paypal.password';
4938 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayPal signature' WHERE name = 'credit.processor.paypal.signature';
4939 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayPal test mode' WHERE name = 'credit.processor.paypal.testmode';
4940 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Format Dates with this pattern.' WHERE name = 'format.date';
4941 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Format Times with this pattern.' WHERE name = 'format.time';
4942 UPDATE config.org_unit_setting_type SET grp = 'glob' WHERE name = 'global.default_locale';
4943 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'global.juvenile_age_threshold';
4944 UPDATE config.org_unit_setting_type SET grp = 'glob' WHERE name = 'global.password_regex';
4945 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';
4946 UPDATE config.org_unit_setting_type SET grp = 'lib', label='Courier Code' WHERE name = 'lib.courier_code';
4947 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'notice.telephony.callfile_lines';
4948 UPDATE config.org_unit_setting_type SET grp = 'opac', label='Allow pending addresses' WHERE name = 'opac.allow_pending_address';
4949 UPDATE config.org_unit_setting_type SET grp = 'glob' WHERE name = 'opac.barcode_regex';
4950 UPDATE config.org_unit_setting_type SET grp = 'opac', label='Use fully compressed serial holdings' WHERE name = 'opac.fully_compressed_serial_holdings';
4951 UPDATE config.org_unit_setting_type SET grp = 'opac', label='Org Unit Hiding Depth' WHERE name = 'opac.org_unit_hiding.depth';
4952 UPDATE config.org_unit_setting_type SET grp = 'opac', label='Payment History Age Limit' WHERE name = 'opac.payment_history_age_limit';
4953 UPDATE config.org_unit_setting_type SET grp = 'prog' WHERE name = 'org.bounced_emails';
4954 UPDATE config.org_unit_setting_type SET grp = 'sec', label='Patron Opt-In Boundary' WHERE name = 'org.patron_opt_boundary';
4955 UPDATE config.org_unit_setting_type SET grp = 'sec', label='Patron Opt-In Default' WHERE name = 'org.patron_opt_default';
4956 UPDATE config.org_unit_setting_type SET grp = 'sec' WHERE name = 'patron.password.use_phone';
4957 UPDATE config.org_unit_setting_type SET grp = 'serial', label='Previous Issuance Copy Location' WHERE name = 'serial.prev_issuance_copy_location';
4958 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Work Log: Maximum Patrons Logged' WHERE name = 'ui.admin.patron_log.max_entries';
4959 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Work Log: Maximum Actions Logged' WHERE name = 'ui.admin.work_log.max_entries';
4960 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';
4961 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';
4962 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';
4963 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';
4964 UPDATE config.org_unit_setting_type SET grp = 'gui' WHERE name = 'ui.circ.patron_summary.horizontal';
4965 UPDATE config.org_unit_setting_type SET grp = 'gui' WHERE name = 'ui.circ.show_billing_tab_on_bills';
4966 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Suppress popup-dialogs during check-in.' WHERE name = 'ui.circ.suppress_checkin_popups';
4967 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Button bar' WHERE name = 'ui.general.button_bar';
4968 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Default Hotkeyset' WHERE name = 'ui.general.hotkeyset';
4969 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Idle timeout' WHERE name = 'ui.general.idle_timeout';
4970 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Default Country for New Addresses in Patron Editor' WHERE name = 'ui.patron.default_country';
4971 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Default Ident Type for Patron Registration' WHERE name = 'ui.patron.default_ident_type';
4972 UPDATE config.org_unit_setting_type SET grp = 'sec', label='Default level of patrons'' internet access' WHERE name = 'ui.patron.default_inet_access_level';
4973 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show active field on patron registration' WHERE name = 'ui.patron.edit.au.active.show';
4974 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest active field on patron registration' WHERE name = 'ui.patron.edit.au.active.suggest';
4975 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';
4976 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';
4977 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show alias field on patron registration' WHERE name = 'ui.patron.edit.au.alias.show';
4978 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest alias field on patron registration' WHERE name = 'ui.patron.edit.au.alias.suggest';
4979 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show barred field on patron registration' WHERE name = 'ui.patron.edit.au.barred.show';
4980 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest barred field on patron registration' WHERE name = 'ui.patron.edit.au.barred.suggest';
4981 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';
4982 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';
4983 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';
4984 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';
4985 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';
4986 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';
4987 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';
4988 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';
4989 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';
4990 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';
4991 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Require dob field on patron registration' WHERE name = 'ui.patron.edit.au.dob.require';
4992 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show dob field on patron registration' WHERE name = 'ui.patron.edit.au.dob.show';
4993 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest dob field on patron registration' WHERE name = 'ui.patron.edit.au.dob.suggest';
4994 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';
4995 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';
4996 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Require email field on patron registration' WHERE name = 'ui.patron.edit.au.email.require';
4997 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show email field on patron registration' WHERE name = 'ui.patron.edit.au.email.show';
4998 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest email field on patron registration' WHERE name = 'ui.patron.edit.au.email.suggest';
4999 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';
5000 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';
5001 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';
5002 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';
5003 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';
5004 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';
5005 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';
5006 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';
5007 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';
5008 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show juvenile field on patron registration' WHERE name = 'ui.patron.edit.au.juvenile.show';
5009 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest juvenile field on patron registration' WHERE name = 'ui.patron.edit.au.juvenile.suggest';
5010 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';
5011 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';
5012 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';
5013 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';
5014 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';
5015 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';
5016 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';
5017 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';
5018 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';
5019 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show suffix field on patron registration' WHERE name = 'ui.patron.edit.au.suffix.show';
5020 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest suffix field on patron registration' WHERE name = 'ui.patron.edit.au.suffix.suggest';
5021 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Require county field on patron registration' WHERE name = 'ui.patron.edit.aua.county.require';
5022 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';
5023 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';
5024 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Default showing suggested patron registration fields' WHERE name = 'ui.patron.edit.default_suggested';
5025 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Example for phone fields on patron registration' WHERE name = 'ui.patron.edit.phone.example';
5026 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Regex for phone fields on patron registration' WHERE name = 'ui.patron.edit.phone.regex';
5027 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';
5028 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';
5029 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';
5030 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Unified Volume/Item Creator/Editor' WHERE name = 'ui.unified_volume_copy_editor';
5031 UPDATE config.org_unit_setting_type SET grp = 'gui', label='URL for remote directory containing list column settings.' WHERE name = 'url.remote_column_settings';
5036 SELECT evergreen.upgrade_deps_block_check('0623', :eg_version);
5039 CREATE TABLE config.org_unit_setting_type_log (
5040 id BIGSERIAL PRIMARY KEY,
5041 date_applied TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
5042 org INT REFERENCES actor.org_unit (id),
5043 original_value TEXT,
5045 field_name TEXT REFERENCES config.org_unit_setting_type (name)
5048 -- Log each change in oust to oustl, so admins can see what they messed up if someting stops working.
5049 CREATE OR REPLACE FUNCTION ous_change_log() RETURNS TRIGGER AS $ous_change_log$
5053 -- Check for which setting is being updated, and log it.
5054 SELECT INTO original value FROM actor.org_unit_setting WHERE name = NEW.name AND org_unit = NEW.org_unit;
5056 INSERT INTO config.org_unit_setting_type_log (org,original_value,new_value,field_name) VALUES (NEW.org_unit, original, NEW.value, NEW.name);
5060 $ous_change_log$ LANGUAGE plpgsql;
5062 CREATE TRIGGER log_ous_change
5063 BEFORE INSERT OR UPDATE ON actor.org_unit_setting
5064 FOR EACH ROW EXECUTE PROCEDURE ous_change_log();
5066 CREATE OR REPLACE FUNCTION ous_delete_log() RETURNS TRIGGER AS $ous_delete_log$
5070 -- Check for which setting is being updated, and log it.
5071 SELECT INTO original value FROM actor.org_unit_setting WHERE name = OLD.name AND org_unit = OLD.org_unit;
5073 INSERT INTO config.org_unit_setting_type_log (org,original_value,new_value,field_name) VALUES (OLD.org_unit, original, 'null', OLD.name);
5077 $ous_delete_log$ LANGUAGE plpgsql;
5079 CREATE TRIGGER log_ous_del
5080 BEFORE DELETE ON actor.org_unit_setting
5081 FOR EACH ROW EXECUTE PROCEDURE ous_delete_log();
5083 -- Evergreen DB patch 0625.data.opac_staff_saved_search_size.sql
5086 SELECT evergreen.upgrade_deps_block_check('0625', :eg_version);
5088 INSERT into config.org_unit_setting_type (name, grp, label, description, datatype)
5090 'opac.staff_saved_search.size', 'opac',
5091 oils_i18n_gettext('opac.staff_saved_search.size',
5092 'OPAC: Number of staff client saved searches to display on left side of results and record details pages', 'coust', 'label'),
5093 oils_i18n_gettext('opac.staff_saved_search.size',
5094 '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'),
5098 -- Evergreen DB patch 0626.schema.bookbag-goodies.sql
5101 SELECT evergreen.upgrade_deps_block_check('0626', :eg_version);
5103 ALTER TABLE container.biblio_record_entry_bucket
5104 ADD COLUMN description TEXT;
5106 ALTER TABLE container.call_number_bucket
5107 ADD COLUMN description TEXT;
5109 ALTER TABLE container.copy_bucket
5110 ADD COLUMN description TEXT;
5112 ALTER TABLE container.user_bucket
5113 ADD COLUMN description TEXT;
5115 INSERT INTO action_trigger.hook (key, core_type, description, passive)
5117 'container.biblio_record_entry_bucket.csv',
5120 'container.biblio_record_entry_bucket.csv',
5121 'Produce a CSV file representing a bookbag',
5128 INSERT INTO action_trigger.reactor (module, description)
5133 'Facilitates produce a CSV file representing a bookbag by introducing an "items" variable into the TT environment, sorted as dictated according to user params',
5139 INSERT INTO action_trigger.event_definition (
5141 name, hook, reactor,
5145 'Bookbag CSV', 'container.biblio_record_entry_bucket.csv', 'ContainerCSV',
5149 # target is the bookbag itself. The 'items' variable does not need to be in
5150 # the environment because a special reactor will take care of filling it in.
5153 bibxml = helpers.xml_doc(item.target_biblio_record_entry.marc);
5155 FOR part IN bibxml.findnodes('//*[@tag="245"]/*[@code="a" or @code="b"]');
5156 title = title _ part.textContent;
5158 author = bibxml.findnodes('//*[@tag="100"]/*[@code="a"]').textContent;
5160 helpers.csv_datum(title) %],[% helpers.csv_datum(author) %],[% FOR note IN item.notes; helpers.csv_datum(note.note); ","; END; "\n";
5165 -- Evergreen DB patch 0627.data.patron-password-reset-msg.sql
5167 -- Updates password reset template to match TPAC reset form
5170 -- check whether patch can be applied
5171 SELECT evergreen.upgrade_deps_block_check('0627', :eg_version);
5173 UPDATE action_trigger.event_definition SET template =
5176 [%- user = target.usr -%]
5177 To: [%- params.recipient_email || user.email %]
5178 From: [%- params.sender_email || user.home_ou.email || default_sender %]
5179 Subject: [% user.home_ou.name %]: library account password reset request
5181 You have received this message because you, or somebody else, requested a reset
5182 of your library system password. If you did not request a reset of your library
5183 system password, just ignore this message and your current password will
5186 If you did request a reset of your library system password, please perform
5187 the following steps to continue the process of resetting your password:
5189 1. Open the following link in a web browser: https://[% params.hostname %]/eg/opac/password_reset/[% target.uuid %]
5190 The browser displays a password reset form.
5192 2. Enter your new password in the password reset form in the browser. You must
5193 enter the password twice to ensure that you do not make a mistake. If the
5194 passwords match, you will then be able to log in to your library system account
5195 with the new password.
5198 WHERE id = 20; -- Password reset request notification
5201 SELECT evergreen.upgrade_deps_block_check('0630', :eg_version);
5203 INSERT into config.org_unit_setting_type (name, grp, label, description, datatype) VALUES
5204 ( 'circ.transit.suppress_hold', 'circ',
5205 oils_i18n_gettext('circ.transit.suppress_hold',
5206 'Suppress Hold Transits Group',
5208 oils_i18n_gettext('circ.transit.suppress_hold',
5209 '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.',
5210 'coust', 'description'),
5212 ,( 'circ.transit.suppress_non_hold', 'circ',
5213 oils_i18n_gettext('circ.transit.suppress_non_hold',
5214 'Suppress Non-Hold Transits Group',
5216 oils_i18n_gettext('circ.transit.suppress_non_hold',
5217 '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.',
5218 'coust', 'description'),
5222 -- check whether patch can be applied
5223 SELECT evergreen.upgrade_deps_block_check('0632', :eg_version);
5225 INSERT INTO config.org_unit_setting_type (name, grp, label, description, datatype) VALUES
5226 ( 'opac.username_regex', 'glob',
5227 oils_i18n_gettext('opac.username_regex',
5228 'Patron username format',
5230 oils_i18n_gettext('opac.username_regex',
5231 'Regular expression defining the patron username format, used for patron registration and self-service username changing only',
5232 'coust', 'description'),
5234 ,( 'opac.lock_usernames', 'glob',
5235 oils_i18n_gettext('opac.lock_usernames',
5238 oils_i18n_gettext('opac.lock_usernames',
5239 'If enabled username changing via the OPAC will be disabled',
5240 'coust', 'description'),
5242 ,( 'opac.unlimit_usernames', 'glob',
5243 oils_i18n_gettext('opac.unlimit_usernames',
5244 'Allow multiple username changes',
5246 oils_i18n_gettext('opac.unlimit_usernames',
5247 '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.',
5248 'coust', 'description'),
5252 -- Evergreen DB patch 0635.data.opac.jump-to-details-setting.sql
5256 -- check whether patch can be applied
5257 SELECT evergreen.upgrade_deps_block_check('0635', :eg_version);
5259 INSERT INTO config.org_unit_setting_type ( name, grp, label, description, datatype )
5261 'opac.staff.jump_to_details_on_single_hit',
5264 'opac.staff.jump_to_details_on_single_hit',
5265 'Jump to details on 1 hit (staff client)',
5270 'opac.staff.jump_to_details_on_single_hit',
5271 'When a search yields only 1 result, jump directly to the record details page. This setting only affects the OPAC within the staff client',
5277 'opac.patron.jump_to_details_on_single_hit',
5280 'opac.patron.jump_to_details_on_single_hit',
5281 'Jump to details on 1 hit (public)',
5286 'opac.patron.jump_to_details_on_single_hit',
5287 'When a search yields only 1 result, jump directly to the record details page. This setting only affects the public OPAC',
5294 -- Evergreen DB patch 0636.data.grace_period_extend.sql
5296 -- OU setting turns on grace period auto extension. By default they only do so
5297 -- when the grace period ends on a closed date, but there are two modifiers to
5300 -- The first modifier causes grace periods to extend for all closed dates that
5301 -- they intersect. This is "grace periods are only consumed by open days."
5303 -- The second modifier causes a grace period that ends just before a closed
5304 -- day, with or without extension having happened, to include the closed day
5305 -- (and any following it) as well. This is mainly so that a backdate into the
5306 -- closed period following the grace period will assume the "best case" of the
5307 -- item having been returned after hours on the last day of the closed date.
5311 -- check whether patch can be applied
5312 SELECT evergreen.upgrade_deps_block_check('0636', :eg_version);
5314 INSERT INTO config.org_unit_setting_type(name, grp, label, description, datatype) VALUES
5316 ( 'circ.grace.extend', 'circ',
5317 oils_i18n_gettext('circ.grace.extend',
5318 'Auto-Extend Grace Periods',
5320 oils_i18n_gettext('circ.grace.extend',
5321 '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.',
5322 'coust', 'description'),
5325 ,( 'circ.grace.extend.all', 'circ',
5326 oils_i18n_gettext('circ.grace.extend.all',
5327 'Auto-Extending Grace Periods extend for all closed dates',
5329 oils_i18n_gettext('circ.grace.extend.all',
5330 '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".',
5331 'coust', 'description'),
5334 ,( 'circ.grace.extend.into_closed', 'circ',
5335 oils_i18n_gettext('circ.grace.extend.into_closed',
5336 'Auto-Extending Grace Periods include trailing closed dates',
5338 oils_i18n_gettext('circ.grace.extend.into_closed',
5339 '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.',
5340 'coust', 'description'),
5344 -- XXXX.schema-acs-nfi.sql
5346 SELECT evergreen.upgrade_deps_block_check('0640', :eg_version);
5348 -- AFTER UPDATE OR INSERT trigger for authority.record_entry
5349 CREATE OR REPLACE FUNCTION authority.indexing_ingest_or_delete () RETURNS TRIGGER AS $func$
5352 IF NEW.deleted IS TRUE THEN -- If this authority is deleted
5353 DELETE FROM authority.bib_linking WHERE authority = NEW.id; -- Avoid updating fields in bibs that are no longer visible
5354 DELETE FROM authority.full_rec WHERE record = NEW.id; -- Avoid validating fields against deleted authority records
5355 DELETE FROM authority.simple_heading WHERE record = NEW.id;
5356 -- Should remove matching $0 from controlled fields at the same time?
5357 RETURN NEW; -- and we're done
5360 IF TG_OP = 'UPDATE' THEN -- re-ingest?
5361 PERFORM * FROM config.internal_flag WHERE name = 'ingest.reingest.force_on_same_marc' AND enabled;
5363 IF NOT FOUND AND OLD.marc = NEW.marc THEN -- don't do anything if the MARC didn't change
5367 -- Propagate these updates to any linked bib records
5368 PERFORM authority.propagate_changes(NEW.id) FROM authority.record_entry WHERE id = NEW.id;
5370 DELETE FROM authority.simple_heading WHERE record = NEW.id;
5373 INSERT INTO authority.simple_heading (record,atag,value,sort_value)
5374 SELECT record, atag, value, sort_value FROM authority.simple_heading_set(NEW.marc);
5376 -- Flatten and insert the afr data
5377 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_full_rec' AND enabled;
5379 PERFORM authority.reingest_authority_full_rec(NEW.id);
5380 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_rec_descriptor' AND enabled;
5382 PERFORM authority.reingest_authority_rec_descriptor(NEW.id);
5388 $func$ LANGUAGE PLPGSQL;
5390 -- Entries that need to respect an NFI
5391 UPDATE authority.control_set_authority_field SET nfi = '2'
5392 WHERE id IN (4,24,44,64);
5394 DROP TRIGGER authority_full_rec_fti_trigger ON authority.full_rec;
5395 CREATE TRIGGER authority_full_rec_fti_trigger
5396 BEFORE UPDATE OR INSERT ON authority.full_rec
5397 FOR EACH ROW EXECUTE PROCEDURE oils_tsearch2('keyword');
5399 CREATE OR REPLACE FUNCTION authority.normalize_heading( marcxml TEXT, no_thesaurus BOOL ) RETURNS TEXT AS $func$
5401 acsaf authority.control_set_authority_field%ROWTYPE;
5410 auth_id INT DEFAULT oils_xpath_string('//*[@tag="901"]/*[local-name()="subfield" and @code="c"]', marcxml)::INT;
5412 SELECT control_set INTO cset FROM authority.record_entry WHERE id = auth_id;
5414 IF cset IS NULL THEN
5415 SELECT control_set INTO cset
5416 FROM authority.control_set_authority_field
5417 WHERE tag IN ( SELECT UNNEST(XPATH('//*[starts-with(@tag,"1")]/@tag',marcxml::XML)::TEXT[]))
5421 thes_code := vandelay.marc21_extract_fixed_field(marcxml,'Subj');
5422 IF thes_code IS NULL THEN
5424 ELSIF thes_code = 'z' THEN
5425 thes_code := COALESCE( oils_xpath_string('//*[@tag="040"]/*[@code="f"][1]', marcxml), '' );
5429 FOR acsaf IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset AND main_entry IS NULL LOOP
5430 tag_used := acsaf.tag;
5431 nfi_used := acsaf.nfi;
5433 FOR sf IN SELECT * FROM regexp_split_to_table(acsaf.sf_list,'') LOOP
5434 tmp_text := oils_xpath_string('//*[@tag="'||tag_used||'"]/*[@code="'||sf||'"]', marcxml);
5436 IF first_sf AND tmp_text IS NOT NULL AND nfi_used IS NOT NULL THEN
5438 tmp_text := SUBSTRING(
5443 oils_xpath_string('//*[@tag="'||tag_used||'"]/@ind'||nfi_used, marcxml),
5458 IF tmp_text IS NOT NULL AND tmp_text <> '' THEN
5459 heading_text := heading_text || E'\u2021' || sf || ' ' || tmp_text;
5462 EXIT WHEN heading_text <> '';
5465 IF heading_text <> '' THEN
5466 IF no_thesaurus IS TRUE THEN
5467 heading_text := tag_used || ' ' || public.naco_normalize(heading_text);
5469 heading_text := tag_used || '_' || COALESCE(nfi_used,'-') || '_' || thes_code || ' ' || public.naco_normalize(heading_text);
5472 heading_text := 'NOHEADING_' || thes_code || ' ' || MD5(marcxml);
5475 RETURN heading_text;
5477 $func$ LANGUAGE PLPGSQL IMMUTABLE;
5479 CREATE OR REPLACE FUNCTION authority.simple_normalize_heading( marcxml TEXT ) RETURNS TEXT AS $func$
5480 SELECT authority.normalize_heading($1, TRUE);
5481 $func$ LANGUAGE SQL IMMUTABLE;
5483 CREATE OR REPLACE FUNCTION authority.normalize_heading( marcxml TEXT ) RETURNS TEXT AS $func$
5484 SELECT authority.normalize_heading($1, FALSE);
5485 $func$ LANGUAGE SQL IMMUTABLE;
5488 CREATE TABLE authority.simple_heading (
5489 id BIGSERIAL PRIMARY KEY,
5490 record BIGINT NOT NULL REFERENCES authority.record_entry (id),
5491 atag INT NOT NULL REFERENCES authority.control_set_authority_field (id),
5492 value TEXT NOT NULL,
5493 sort_value TEXT NOT NULL,
5494 index_vector tsvector NOT NULL
5496 CREATE TRIGGER authority_simple_heading_fti_trigger
5497 BEFORE UPDATE OR INSERT ON authority.simple_heading
5498 FOR EACH ROW EXECUTE PROCEDURE oils_tsearch2('keyword');
5500 CREATE INDEX authority_simple_heading_index_vector_idx ON authority.simple_heading USING GIST (index_vector);
5501 CREATE INDEX authority_simple_heading_value_idx ON authority.simple_heading (value);
5502 CREATE INDEX authority_simple_heading_sort_value_idx ON authority.simple_heading (sort_value);
5504 CREATE OR REPLACE FUNCTION authority.simple_heading_set( marcxml TEXT ) RETURNS SETOF authority.simple_heading AS $func$
5506 res authority.simple_heading%ROWTYPE;
5507 acsaf authority.control_set_authority_field%ROWTYPE;
5517 auth_id INT DEFAULT oils_xpath_string('//*[@tag="901"]/*[local-name()="subfield" and @code="c"]', marcxml)::INT;
5520 res.record := auth_id;
5522 SELECT control_set INTO cset
5523 FROM authority.control_set_authority_field
5524 WHERE tag IN ( SELECT UNNEST(XPATH('//*[starts-with(@tag,"1")]/@tag',marcxml::XML)::TEXT[]) )
5527 FOR acsaf IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset LOOP
5529 res.atag := acsaf.id;
5530 tag_used := acsaf.tag;
5531 nfi_used := acsaf.nfi;
5533 FOR tmp_xml IN SELECT UNNEST(XPATH('//*[@tag="'||tag_used||'"]', marcxml::XML)) LOOP
5536 FOR sf IN SELECT * FROM regexp_split_to_table(acsaf.sf_list,'') LOOP
5537 heading_text := heading_text || COALESCE( ' ' || oils_xpath_string('//*[@code="'||sf||'"]',tmp_xml::TEXT), '');
5540 heading_text := public.naco_normalize(heading_text);
5542 IF nfi_used IS NOT NULL THEN
5544 sort_text := SUBSTRING(
5549 oils_xpath_string('//*[@tag="'||tag_used||'"]/@ind'||nfi_used, marcxml),
5561 sort_text := heading_text;
5564 IF heading_text IS NOT NULL AND heading_text <> '' THEN
5565 res.value := heading_text;
5566 res.sort_value := sort_text;
5576 $func$ LANGUAGE PLPGSQL IMMUTABLE;
5578 -- Support function used to find the pivot for alpha-heading-browse style searching
5579 CREATE OR REPLACE FUNCTION authority.simple_heading_find_pivot( a INT[], q TEXT ) RETURNS TEXT AS $$
5581 sort_value_row RECORD;
5586 t_term := public.naco_normalize(q);
5588 SELECT CASE WHEN ash.sort_value LIKE t_term || '%' THEN 1 ELSE 0 END
5589 + CASE WHEN ash.value LIKE t_term || '%' THEN 1 ELSE 0 END AS rank,
5592 FROM authority.simple_heading ash
5593 WHERE ash.atag = ANY (a)
5594 AND ash.sort_value >= t_term
5595 ORDER BY rank DESC, ash.sort_value
5598 SELECT CASE WHEN ash.sort_value LIKE t_term || '%' THEN 1 ELSE 0 END
5599 + CASE WHEN ash.value LIKE t_term || '%' THEN 1 ELSE 0 END AS rank,
5602 FROM authority.simple_heading ash
5603 WHERE ash.atag = ANY (a)
5604 AND ash.value >= t_term
5605 ORDER BY rank DESC, ash.sort_value
5608 IF value_row.rank > sort_value_row.rank THEN
5609 RETURN value_row.sort_value;
5611 RETURN sort_value_row.sort_value;
5614 $$ LANGUAGE PLPGSQL;
5617 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 $$
5619 pivot_sort_value TEXT;
5620 boffset INT DEFAULT 0;
5621 aoffset INT DEFAULT 0;
5622 blimit INT DEFAULT 0;
5623 alimit INT DEFAULT 0;
5626 pivot_sort_value := authority.simple_heading_find_pivot(atag_list,q);
5629 blimit := pagesize / 2;
5632 IF pagesize % 2 <> 0 THEN
5633 alimit := alimit + 1;
5639 boffset := pagesize / 2;
5642 IF pagesize % 2 <> 0 THEN
5643 boffset := boffset + 1;
5649 -- "bottom" half of the browse results
5652 row_number() over ()
5653 FROM authority.simple_heading ash
5654 WHERE ash.atag = ANY (atag_list)
5655 AND ash.sort_value < pivot_sort_value
5656 ORDER BY ash.sort_value DESC
5658 OFFSET ABS(page) * pagesize - boffset
5659 ) x ORDER BY row_number DESC;
5664 -- "bottom" half of the browse results
5666 FROM authority.simple_heading ash
5667 WHERE ash.atag = ANY (atag_list)
5668 AND ash.sort_value >= pivot_sort_value
5669 ORDER BY ash.sort_value
5671 OFFSET ABS(page) * pagesize - aoffset;
5674 $$ LANGUAGE PLPGSQL ROWS 10;
5676 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 $$
5678 pivot_sort_value TEXT;
5681 pivot_sort_value := authority.simple_heading_find_pivot(atag_list,q);
5685 -- "bottom" half of the browse results
5688 row_number() over ()
5689 FROM authority.simple_heading ash
5690 WHERE ash.atag = ANY (atag_list)
5691 AND ash.sort_value < pivot_sort_value
5692 ORDER BY ash.sort_value DESC
5694 OFFSET (ABS(page) - 1) * pagesize
5695 ) x ORDER BY row_number DESC;
5700 -- "bottom" half of the browse results
5702 FROM authority.simple_heading ash
5703 WHERE ash.atag = ANY (atag_list)
5704 AND ash.sort_value >= pivot_sort_value
5705 ORDER BY ash.sort_value
5707 OFFSET ABS(page) * pagesize ;
5710 $$ LANGUAGE PLPGSQL ROWS 10;
5712 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 $$
5714 FROM authority.simple_heading ash,
5715 public.naco_normalize($2) t(term),
5716 plainto_tsquery('keyword'::regconfig,$2) ptsq(term)
5717 WHERE ash.atag = ANY ($1)
5718 AND ash.index_vector @@ ptsq.term
5719 ORDER BY ts_rank_cd(ash.index_vector,ptsq.term,14)::numeric
5720 + CASE WHEN ash.sort_value LIKE t.term || '%' THEN 2 ELSE 0 END
5721 + CASE WHEN ash.value LIKE t.term || '%' THEN 1 ELSE 0 END DESC
5724 $$ LANGUAGE SQL ROWS 10;
5726 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 $$
5728 FROM authority.simple_heading ash,
5729 public.naco_normalize($2) t(term),
5730 plainto_tsquery('keyword'::regconfig,$2) ptsq(term)
5731 WHERE ash.atag = ANY ($1)
5732 AND ash.index_vector @@ ptsq.term
5733 ORDER BY ash.sort_value
5736 $$ LANGUAGE SQL ROWS 10;
5739 CREATE OR REPLACE FUNCTION authority.axis_authority_tags(a TEXT) RETURNS INT[] AS $$
5740 SELECT ARRAY_ACCUM(field) FROM authority.browse_axis_authority_field_map WHERE axis = $1;
5743 CREATE OR REPLACE FUNCTION authority.axis_authority_tags_refs(a TEXT) RETURNS INT[] AS $$
5746 (SELECT ARRAY_ACCUM(x.id) FROM authority.control_set_authority_field x WHERE x.main_entry = a.field)
5748 FROM authority.browse_axis_authority_field_map a
5754 CREATE OR REPLACE FUNCTION authority.btag_authority_tags(btag TEXT) RETURNS INT[] AS $$
5755 SELECT ARRAY_ACCUM(authority_field) FROM authority.control_set_bib_field WHERE tag = $1
5758 CREATE OR REPLACE FUNCTION authority.btag_authority_tags_refs(btag TEXT) RETURNS INT[] AS $$
5760 ARRAY[a.authority_field],
5761 (SELECT ARRAY_ACCUM(x.id) FROM authority.control_set_authority_field x WHERE x.main_entry = a.authority_field)
5763 FROM authority.control_set_bib_field a
5769 CREATE OR REPLACE FUNCTION authority.atag_authority_tags(atag TEXT) RETURNS INT[] AS $$
5770 SELECT ARRAY_ACCUM(id) FROM authority.control_set_authority_field WHERE tag = $1
5773 CREATE OR REPLACE FUNCTION authority.atag_authority_tags_refs(atag TEXT) RETURNS INT[] AS $$
5776 (SELECT ARRAY_ACCUM(x.id) FROM authority.control_set_authority_field x WHERE x.main_entry = a.id)
5778 FROM authority.control_set_authority_field a
5783 CREATE OR REPLACE FUNCTION authority.axis_browse_center( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 9 ) RETURNS SETOF BIGINT AS $$
5784 SELECT * FROM authority.simple_heading_browse_center(authority.axis_authority_tags($1), $2, $3, $4)
5785 $$ LANGUAGE SQL ROWS 10;
5787 CREATE OR REPLACE FUNCTION authority.btag_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.btag_authority_tags($1), $2, $3, $4)
5789 $$ LANGUAGE SQL ROWS 10;
5791 CREATE OR REPLACE FUNCTION authority.atag_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.atag_authority_tags($1), $2, $3, $4)
5793 $$ LANGUAGE SQL ROWS 10;
5795 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 $$
5796 SELECT * FROM authority.simple_heading_browse_center(authority.axis_authority_tags_refs($1), $2, $3, $4)
5797 $$ LANGUAGE SQL ROWS 10;
5799 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 $$
5800 SELECT * FROM authority.simple_heading_browse_center(authority.btag_authority_tags_refs($1), $2, $3, $4)
5801 $$ LANGUAGE SQL ROWS 10;
5803 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 $$
5804 SELECT * FROM authority.simple_heading_browse_center(authority.atag_authority_tags_refs($1), $2, $3, $4)
5805 $$ LANGUAGE SQL ROWS 10;
5808 CREATE OR REPLACE FUNCTION authority.axis_browse_top( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5809 SELECT * FROM authority.simple_heading_browse_top(authority.axis_authority_tags($1), $2, $3, $4)
5810 $$ LANGUAGE SQL ROWS 10;
5812 CREATE OR REPLACE FUNCTION authority.btag_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.btag_authority_tags($1), $2, $3, $4)
5814 $$ LANGUAGE SQL ROWS 10;
5816 CREATE OR REPLACE FUNCTION authority.atag_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.atag_authority_tags($1), $2, $3, $4)
5818 $$ LANGUAGE SQL ROWS 10;
5820 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 $$
5821 SELECT * FROM authority.simple_heading_browse_top(authority.axis_authority_tags_refs($1), $2, $3, $4)
5822 $$ LANGUAGE SQL ROWS 10;
5824 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 $$
5825 SELECT * FROM authority.simple_heading_browse_top(authority.btag_authority_tags_refs($1), $2, $3, $4)
5826 $$ LANGUAGE SQL ROWS 10;
5828 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 $$
5829 SELECT * FROM authority.simple_heading_browse_top(authority.atag_authority_tags_refs($1), $2, $3, $4)
5830 $$ LANGUAGE SQL ROWS 10;
5833 CREATE OR REPLACE FUNCTION authority.axis_search_rank( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5834 SELECT * FROM authority.simple_heading_search_rank(authority.axis_authority_tags($1), $2, $3, $4)
5835 $$ LANGUAGE SQL ROWS 10;
5837 CREATE OR REPLACE FUNCTION authority.btag_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.btag_authority_tags($1), $2, $3, $4)
5839 $$ LANGUAGE SQL ROWS 10;
5841 CREATE OR REPLACE FUNCTION authority.atag_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.atag_authority_tags($1), $2, $3, $4)
5843 $$ LANGUAGE SQL ROWS 10;
5845 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 $$
5846 SELECT * FROM authority.simple_heading_search_rank(authority.axis_authority_tags_refs($1), $2, $3, $4)
5847 $$ LANGUAGE SQL ROWS 10;
5849 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 $$
5850 SELECT * FROM authority.simple_heading_search_rank(authority.btag_authority_tags_refs($1), $2, $3, $4)
5851 $$ LANGUAGE SQL ROWS 10;
5853 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 $$
5854 SELECT * FROM authority.simple_heading_search_rank(authority.atag_authority_tags_refs($1), $2, $3, $4)
5855 $$ LANGUAGE SQL ROWS 10;
5858 CREATE OR REPLACE FUNCTION authority.axis_search_heading( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5859 SELECT * FROM authority.simple_heading_search_heading(authority.axis_authority_tags($1), $2, $3, $4)
5860 $$ LANGUAGE SQL ROWS 10;
5862 CREATE OR REPLACE FUNCTION authority.btag_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.btag_authority_tags($1), $2, $3, $4)
5864 $$ LANGUAGE SQL ROWS 10;
5866 CREATE OR REPLACE FUNCTION authority.atag_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.atag_authority_tags($1), $2, $3, $4)
5868 $$ LANGUAGE SQL ROWS 10;
5870 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 $$
5871 SELECT * FROM authority.simple_heading_search_heading(authority.axis_authority_tags_refs($1), $2, $3, $4)
5872 $$ LANGUAGE SQL ROWS 10;
5874 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 $$
5875 SELECT * FROM authority.simple_heading_search_heading(authority.btag_authority_tags_refs($1), $2, $3, $4)
5876 $$ LANGUAGE SQL ROWS 10;
5878 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 $$
5879 SELECT * FROM authority.simple_heading_search_heading(authority.atag_authority_tags_refs($1), $2, $3, $4)
5880 $$ LANGUAGE SQL ROWS 10;
5884 -- Evergreen DB patch 0641.schema.org_unit_setting_json_check.sql
5888 -- check whether patch can be applied
5889 SELECT evergreen.upgrade_deps_block_check('0641', :eg_version);
5891 ALTER TABLE actor.org_unit_setting ADD CONSTRAINT aous_must_be_json CHECK ( is_json(value) );
5893 -- Evergreen DB patch 0642.data.acq-worksheet-hold-count.sql
5895 -- check whether patch can be applied
5896 SELECT evergreen.upgrade_deps_block_check('0642', :eg_version);
5898 UPDATE action_trigger.event_definition SET template =
5901 [%- SET li = target; -%]
5902 <div class="wrapper">
5903 <div class="summary" style='font-size:110%; font-weight:bold;'>
5905 <div>Title: [% helpers.get_li_attr("title", "", li.attributes) %]</div>
5906 <div>Author: [% helpers.get_li_attr("author", "", li.attributes) %]</div>
5907 <div class="count">Item Count: [% li.lineitem_details.size %]</div>
5908 <div class="lineid">Lineitem ID: [% li.id %]</div>
5909 <div>Open Holds: [% helpers.bre_open_hold_count(li.eg_bib_id) %]</div>
5911 [% IF li.distribution_formulas.size > 0 %]
5912 [% SET forms = [] %]
5913 [% FOREACH form IN li.distribution_formulas; forms.push(form.formula.name); END %]
5914 <div>Distribution Formulas: [% forms.join(',') %]</div>
5917 [% IF li.lineitem_notes.size > 0 %]
5920 [%- FOR note IN li.lineitem_notes -%]
5922 [% IF note.alert_text %]
5923 [% note.alert_text.code -%]
5924 [% IF note.value -%]
5941 <th>Call Number</th>
5943 <th>Shelving Location</th>
5949 [% FOREACH detail IN li.lineitem_details.sort('owning_lib') %]
5951 IF detail.eg_copy_id;
5952 SET copy = detail.eg_copy_id;
5953 SET cn_label = copy.call_number.label;
5956 SET cn_label = detail.cn_label;
5960 <!-- acq.lineitem_detail.id = [%- detail.id -%] -->
5961 <td style='padding:5px;'>[% detail.owning_lib.shortname %]</td>
5962 <td style='padding:5px;'>[% IF copy.barcode %]<span class="barcode" >[% detail.barcode %]</span>[% END %]</td>
5963 <td style='padding:5px;'>[% IF cn_label %]<span class="cn_label" >[% cn_label %]</span>[% END %]</td>
5964 <td style='padding:5px;'>[% IF detail.fund %]<span class="fund">[% detail.fund.code %] ([% detail.fund.year %])</span>[% END %]</td>
5965 <td style='padding:5px;'>[% copy.location.name %]</td>
5966 <td style='padding:5px;'>[% IF detail.recv_time %]<span class="recv_time">[% detail.recv_time %]</span>[% END %]</td>
5967 <td style='padding:5px;'>[% detail.note %]</td>
5977 SELECT evergreen.upgrade_deps_block_check('0643', :eg_version);
5985 FROM authority.record_entry
5988 AND id NOT IN (SELECT DISTINCT record FROM authority.simple_heading)
5990 INSERT INTO authority.simple_heading (record,atag,value,sort_value)
5991 SELECT record, atag, value, sort_value FROM authority.simple_heading_set(x);
5998 SELECT evergreen.upgrade_deps_block_check('0644', :eg_version);
6000 INSERT into config.org_unit_setting_type (name, grp, label, description, datatype) VALUES
6001 ( 'circ.holds.target_when_closed', 'circ',
6002 oils_i18n_gettext('circ.holds.target_when_closed',
6003 'Target copies for a hold even if copy''s circ lib is closed',
6005 oils_i18n_gettext('circ.holds.target_when_closed',
6006 '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).',
6007 'coust', 'description'),
6009 ( 'circ.holds.target_when_closed_if_at_pickup_lib', 'circ',
6010 oils_i18n_gettext('circ.holds.target_when_closed_if_at_pickup_lib',
6011 'Target copies for a hold even if copy''s circ lib is closed IF the circ lib is the hold''s pickup lib',
6013 oils_i18n_gettext('circ.holds.target_when_closed_if_at_pickup_lib',
6014 '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.',
6015 'coust', 'description'),
6019 -- Evergreen DB patch XXXX.data.hold-notification-cleanup-mod.sql
6021 -- check whether patch can be applied
6022 SELECT evergreen.upgrade_deps_block_check('0647', :eg_version);
6024 INSERT INTO action_trigger.cleanup ( module, description ) VALUES (
6025 'CreateHoldNotification',
6027 'CreateHoldNotification',
6028 'Creates a hold_notification record for each notified hold',
6034 UPDATE action_trigger.event_definition
6036 cleanup_success = 'CreateHoldNotification'
6038 id = 5 -- stock hold-ready email event_def
6039 AND cleanup_success IS NULL; -- don't clobber any existing cleanup mod
6041 -- Evergreen DB patch XXXX.schema.unnest-hold-permit-upgrade-script-repair.sql
6043 -- This patch makes no changes to the baseline schema and is
6044 -- only meant to repair a previous upgrade script.
6047 -- check whether patch can be applied
6048 SELECT evergreen.upgrade_deps_block_check('0651', :eg_version);
6050 --Removed dupe action.hold_request_permit_test
6052 -- Evergreen DB patch XXXX.data.vandelay-queue-bib-bucket-type.sql
6055 -- check whether patch can be applied
6056 SELECT evergreen.upgrade_deps_block_check('0652', :eg_version);
6058 INSERT INTO container.biblio_record_entry_bucket_type (code, label) VALUES (
6060 oils_i18n_gettext('vandelay_queue', 'Vandelay Queue', 'cbrebt', 'label')
6063 -- Evergreen DB patch XXXX.schema.unapi-indb-optional-org.sql
6065 -- check whether patch can be applied
6066 SELECT evergreen.upgrade_deps_block_check('0653', :eg_version);
6068 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;
6070 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$
6072 layout unapi.bre_output_layout%ROWTYPE;
6073 transform config.xml_transform%ROWTYPE;
6076 xmlns_uri TEXT := 'http://open-ils.org/spec/feed-xml/v1';
6078 element_list TEXT[];
6081 IF org = '-' OR org IS NULL THEN
6082 SELECT shortname INTO org FROM evergreen.org_top();
6085 SELECT id INTO ouid FROM actor.org_unit WHERE shortname = org;
6086 SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format;
6088 IF layout.name IS NULL THEN
6092 SELECT * INTO transform FROM config.xml_transform WHERE name = layout.transform;
6093 xmlns_uri := COALESCE(transform.namespace_uri,xmlns_uri);
6095 -- Gather the bib xml
6096 SELECT XMLAGG( unapi.bre(i, format, '', includes, org, depth, slimit, soffset, include_xmlns)) INTO tmp_xml FROM UNNEST( id_list ) i;
6098 IF layout.title_element IS NOT NULL THEN
6099 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;
6102 IF layout.description_element IS NOT NULL THEN
6103 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;
6106 IF layout.creator_element IS NOT NULL THEN
6107 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;
6110 IF layout.update_ts_element IS NOT NULL THEN
6111 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;
6114 IF unapi_url IS NOT NULL THEN
6115 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;
6118 IF header_xml IS NOT NULL THEN tmp_xml := XMLCONCAT(header_xml,tmp_xml::XML); END IF;
6120 element_list := regexp_split_to_array(layout.feed_top,E'\\.');
6121 FOR i IN REVERSE ARRAY_UPPER(element_list, 1) .. 1 LOOP
6122 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;
6125 RETURN tmp_xml::XML;
6127 $F$ LANGUAGE PLPGSQL;
6129 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$
6131 me biblio.record_entry%ROWTYPE;
6132 layout unapi.bre_output_layout%ROWTYPE;
6133 xfrm config.xml_transform%ROWTYPE;
6142 IF org = '-' OR org IS NULL THEN
6143 SELECT shortname INTO org FROM evergreen.org_top();
6146 SELECT id INTO ouid FROM actor.org_unit WHERE shortname = org;
6148 IF ouid IS NULL THEN
6152 IF format = 'holdings_xml' THEN -- the special case
6153 output := unapi.holdings_xml( obj_id, ouid, org, depth, includes, slimit, soffset, include_xmlns);
6157 SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format;
6159 IF layout.name IS NULL THEN
6163 SELECT * INTO xfrm FROM config.xml_transform WHERE name = layout.transform;
6165 SELECT * INTO me FROM biblio.record_entry WHERE id = obj_id;
6167 -- grab SVF if we need them
6168 IF ('mra' = ANY (includes)) THEN
6169 axml := unapi.mra(obj_id,NULL,NULL,NULL,NULL);
6174 -- grab hodlings if we need them
6175 IF ('holdings_xml' = ANY (includes)) THEN
6176 hxml := unapi.holdings_xml(obj_id, ouid, org, depth, evergreen.array_remove_item_by_value(includes,'holdings_xml'), slimit, soffset, include_xmlns);
6182 -- generate our item node
6185 IF format = 'marcxml' THEN
6187 IF tmp_xml !~ E'<marc:' THEN -- If we're not using the prefixed namespace in this record, then remove all declarations of it
6188 tmp_xml := REGEXP_REPLACE(tmp_xml, ' xmlns:marc="http://www.loc.gov/MARC21/slim"', '', 'g');
6191 tmp_xml := oils_xslt_process(me.marc, xfrm.xslt)::XML;
6194 top_el := REGEXP_REPLACE(tmp_xml, E'^.*?<((?:\\S+:)?' || layout.holdings_element || ').*$', E'\\1');
6196 IF axml IS NOT NULL THEN
6197 tmp_xml := REGEXP_REPLACE(tmp_xml, '</' || top_el || '>(.*?)$', axml || '</' || top_el || E'>\\1');
6200 IF hxml IS NOT NULL THEN -- XXX how do we configure the holdings position?
6201 tmp_xml := REGEXP_REPLACE(tmp_xml, '</' || top_el || '>(.*?)$', hxml || '</' || top_el || E'>\\1');
6204 IF ('bre.unapi' = ANY (includes)) THEN
6205 output := REGEXP_REPLACE(
6207 '</' || top_el || '>(.*?)',
6211 'http://www.w3.org/1999/xhtml' AS xmlns,
6212 'unapi-id' AS class,
6213 'tag:open-ils.org:U2@bre/' || obj_id || '/' || org AS title
6215 )::TEXT || '</' || top_el || E'>\\1'
6221 output := REGEXP_REPLACE(output::TEXT,E'>\\s+<','><','gs')::XML;
6224 $F$ LANGUAGE PLPGSQL;
6229 SELECT evergreen.upgrade_deps_block_check('0654', :eg_version);
6231 INSERT INTO permission.perm_list ( id, code, description ) VALUES
6232 ( 514, 'UPDATE_PATRON_ACTIVE_CARD', oils_i18n_gettext( 514,
6233 'Allows a user to manually adjust a patron''s active cards', 'ppl', 'description')),
6234 ( 515, 'UPDATE_PATRON_PRIMARY_CARD', oils_i18n_gettext( 515,
6235 'Allows a user to manually adjust a patron''s primary card', 'ppl', 'description'));
6237 -- Evergreen DB patch 0655.config.bib_source.can_have_copies.sql
6239 -- This column introduces the ability to prevent bib records associated
6240 -- with specific bib sources from being able to have volumes or MFHD
6241 -- records attached to them.
6244 -- check whether patch can be applied
6245 SELECT evergreen.upgrade_deps_block_check('0655', :eg_version);
6247 ALTER TABLE config.bib_source
6248 ADD COLUMN can_have_copies BOOL NOT NULL DEFAULT TRUE;
6250 -- Evergreen DB patch XXXX.LP893315_schema.function.filter_deleted_acns_from_unapi.holdings_xml.sql
6252 -- Prevent deleted call numbers from hiding active call numbers / copies / URIs
6255 -- check whether patch can be applied
6256 SELECT evergreen.upgrade_deps_block_check('0656', :eg_version);
6258 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$
6262 CASE WHEN $8 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
6263 CASE WHEN ('bre' = ANY ($5)) THEN 'tag:open-ils.org:U2@bre/' || $1 || '/' || $3 ELSE NULL END AS id
6267 (SELECT XMLAGG(XMLELEMENT::XML) FROM (
6270 XMLATTRIBUTES('public' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
6272 FROM asset.opac_ou_record_copy_count($2, $1)
6276 XMLATTRIBUTES('staff' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
6278 FROM asset.staff_ou_record_copy_count($2, $1)
6283 WHEN ('bmp' = ANY ($5)) THEN
6285 name monograph_parts,
6286 (SELECT XMLAGG(bmp) FROM (
6287 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)
6288 FROM biblio.monograph_part
6296 (SELECT XMLAGG(acn) FROM (
6297 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)
6298 FROM asset.call_number acn
6299 WHERE acn.record = $1
6300 AND acn.deleted IS FALSE
6304 JOIN actor.org_unit_descendants(
6309 FROM actor.org_unit_type aout
6310 JOIN actor.org_unit aou ON (aou.ou_type = aout.id AND aou.id = $2)
6313 ) aoud ON (acp.circ_lib = aoud.id)
6316 ORDER BY label_sortkey
6321 CASE WHEN ('ssub' = ANY ($5)) THEN
6324 (SELECT XMLAGG(ssub) FROM (
6325 SELECT unapi.ssub(id,'xml','subscription','{}'::TEXT[], $3, $4, $6, $7, FALSE)
6326 FROM serial.subscription
6327 WHERE record_entry = $1
6331 CASE WHEN ('acp' = ANY ($5)) THEN
6333 name foreign_copies,
6334 (SELECT XMLAGG(acp) FROM (
6335 SELECT unapi.acp(p.target_copy,'xml','copy','{}'::TEXT[], $3, $4, $6, $7, FALSE)
6336 FROM biblio.peer_bib_copy_map p
6337 JOIN asset.copy c ON (p.target_copy = c.id)
6338 WHERE NOT c.deleted AND peer_record = $1
6345 -- Evergreen DB patch 0657.schema.address-alert.sql
6348 -- check whether patch can be applied
6349 SELECT evergreen.upgrade_deps_block_check('0657', :eg_version);
6351 CREATE TABLE actor.address_alert (
6352 id SERIAL PRIMARY KEY,
6353 owner INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
6354 active BOOL NOT NULL DEFAULT TRUE,
6355 match_all BOOL NOT NULL DEFAULT TRUE,
6356 alert_message TEXT NOT NULL,
6364 mailing_address BOOL NOT NULL DEFAULT FALSE,
6365 billing_address BOOL NOT NULL DEFAULT FALSE
6368 CREATE OR REPLACE FUNCTION actor.address_alert_matches (
6377 mailing_address BOOL DEFAULT FALSE,
6378 billing_address BOOL DEFAULT FALSE
6379 ) RETURNS SETOF actor.address_alert AS $$
6382 FROM actor.address_alert
6385 AND owner IN (SELECT id FROM actor.org_unit_ancestors($1))
6387 (NOT mailing_address AND NOT billing_address)
6388 OR (mailing_address AND $9)
6389 OR (billing_address AND $10)
6394 AND COALESCE($2, '') ~* COALESCE(street1, '.*')
6395 AND COALESCE($3, '') ~* COALESCE(street2, '.*')
6396 AND COALESCE($4, '') ~* COALESCE(city, '.*')
6397 AND COALESCE($5, '') ~* COALESCE(county, '.*')
6398 AND COALESCE($6, '') ~* COALESCE(state, '.*')
6399 AND COALESCE($7, '') ~* COALESCE(country, '.*')
6400 AND COALESCE($8, '') ~* COALESCE(post_code, '.*')
6414 ORDER BY actor.org_unit_proximity(owner, $1)
6419 DROP FUNCTION actor.address_alert_matches(INT, TEXT, TEXT, TEXT, TEXT, TEXT, TEXT, TEXT, BOOL, BOOL);
6420 DROP TABLE actor.address_alert;
6422 -- Evergreen DB patch 0659.add_create_report_perms.sql
6424 -- Add a permission to control the ability to create report templates
6427 -- check whether patch can be applied
6428 SELECT evergreen.upgrade_deps_block_check('0659', :eg_version);
6430 -- FIXME: add/check SQL statements to perform the upgrade
6431 INSERT INTO permission.perm_list ( id, code, description ) VALUES
6432 ( 516, 'CREATE_REPORT_TEMPLATE', oils_i18n_gettext( 516,
6433 'Allows a user to create report templates', 'ppl', 'description' ));
6435 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable)
6436 SELECT grp, 516, depth, grantable
6437 FROM permission.grp_perm_map
6440 FROM permission.perm_list
6441 WHERE code = 'RUN_REPORTS'
6446 SELECT evergreen.upgrade_deps_block_check('0660', :eg_version);
6448 UPDATE action_trigger.event_definition SET template = $$
6450 # target is the bookbag itself. The 'items' variable does not need to be in
6451 # the environment because a special reactor will take care of filling it in.
6454 bibxml = helpers.unapi_bre(item.target_biblio_record_entry, {flesh => '{mra}'});
6456 FOR part IN bibxml.findnodes('//*[@tag="245"]/*[@code="a" or @code="b"]');
6457 title = title _ part.textContent;
6459 author = bibxml.findnodes('//*[@tag="100"]/*[@code="a"]').textContent;
6460 item_type = bibxml.findnodes('//*[local-name()="attributes"]/*[local-name()="field"][@name="item_type"]').getAttribute('coded-value');
6462 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";
6465 WHERE reactor = 'ContainerCSV';
6467 -- Evergreen DB patch 0661.data.yaous-opac-tag-circed-items.sql
6469 -- Add org unit setting that enables users who have opted in to
6470 -- tracking their circulation history to see which items they
6471 -- have previously checked out in search results.
6474 -- check whether patch can be applied
6475 SELECT evergreen.upgrade_deps_block_check('0661', :eg_version);
6477 INSERT into config.org_unit_setting_type
6478 (name, grp, label, description, datatype)
6480 'opac.search.tag_circulated_items',
6483 'opac.search.tag_circulated_items',
6484 'Tag Circulated Items in Results',
6489 'opac.search.tag_circulated_items',
6490 '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',
6498 -- Evergreen DB patch 0662.schema.coded-value-map-index-normalizer.sql
6501 -- check whether patch can be applied
6502 SELECT evergreen.upgrade_deps_block_check('0662', :eg_version);
6504 -- create the normalizer
6505 CREATE OR REPLACE FUNCTION evergreen.coded_value_map_normalizer( input TEXT, ctype TEXT )
6507 SELECT COALESCE(value,$1)
6508 FROM config.coded_value_map
6509 WHERE ctype = $2 AND code = $1;
6512 -- register the normalizer
6513 INSERT INTO config.index_normalizer (name, description, func, param_count) VALUES (
6514 'Coded Value Map Normalizer',
6515 'Applies coded_value_map mapping of values',
6516 'coded_value_map_normalizer',
6520 -- Evergreen DB patch 0663.schema.archive_circ_stat_cats.sql
6522 -- Enables users to set copy and patron stat cats to be archivable
6523 -- for the purposes of statistics even after the circs are aged.
6526 -- check whether patch can be applied
6527 SELECT evergreen.upgrade_deps_block_check('0663', :eg_version);
6531 CREATE TABLE action.archive_actor_stat_cat (
6532 id BIGSERIAL PRIMARY KEY,
6533 xact BIGINT NOT NULL,
6534 stat_cat INT NOT NULL,
6538 CREATE TABLE action.archive_asset_stat_cat (
6539 id BIGSERIAL PRIMARY KEY,
6540 xact BIGINT NOT NULL,
6541 stat_cat INT NOT NULL,
6545 -- Add columns to existing tables
6547 -- Archive Flag Columns
6548 ALTER TABLE actor.stat_cat
6549 ADD COLUMN checkout_archive BOOL NOT NULL DEFAULT FALSE;
6550 ALTER TABLE asset.stat_cat
6551 ADD COLUMN checkout_archive BOOL NOT NULL DEFAULT FALSE;
6553 -- Circulation copy column
6554 ALTER TABLE action.circulation
6555 ADD COLUMN copy_location INT NULL REFERENCES asset.copy_location(id) DEFERRABLE INITIALLY DEFERRED;
6557 -- Create trigger function to auto-fill the copy_location field
6558 CREATE OR REPLACE FUNCTION action.fill_circ_copy_location () RETURNS TRIGGER AS $$
6560 SELECT INTO NEW.copy_location location FROM asset.copy WHERE id = NEW.target_copy;
6563 $$ LANGUAGE PLPGSQL;
6565 -- Create trigger function to auto-archive stat cat entries
6566 CREATE OR REPLACE FUNCTION action.archive_stat_cats () RETURNS TRIGGER AS $$
6568 INSERT INTO action.archive_actor_stat_cat(xact, stat_cat, value)
6569 SELECT NEW.id, asceum.stat_cat, asceum.stat_cat_entry
6570 FROM actor.stat_cat_entry_usr_map asceum
6571 JOIN actor.stat_cat sc ON asceum.stat_cat = sc.id
6572 WHERE NEW.usr = asceum.target_usr AND sc.checkout_archive;
6573 INSERT INTO action.archive_asset_stat_cat(xact, stat_cat, value)
6574 SELECT NEW.id, ascecm.stat_cat, asce.value
6575 FROM asset.stat_cat_entry_copy_map ascecm
6576 JOIN asset.stat_cat sc ON ascecm.stat_cat = sc.id
6577 JOIN asset.stat_cat_entry asce ON ascecm.stat_cat_entry = asce.id
6578 WHERE NEW.target_copy = ascecm.owning_copy AND sc.checkout_archive;
6581 $$ LANGUAGE PLPGSQL;
6584 CREATE TRIGGER fill_circ_copy_location_tgr BEFORE INSERT ON action.circulation FOR EACH ROW EXECUTE PROCEDURE action.fill_circ_copy_location();
6585 CREATE TRIGGER archive_stat_cats_tgr AFTER INSERT ON action.circulation FOR EACH ROW EXECUTE PROCEDURE action.archive_stat_cats();
6587 -- Ensure all triggers are disabled for speedy updates!
6588 ALTER TABLE action.circulation DISABLE TRIGGER ALL;
6590 -- Update view to use circ's copy_location field instead of the copy's current copy_location field
6591 CREATE OR REPLACE VIEW action.all_circulation AS
6592 SELECT id,usr_post_code, usr_home_ou, usr_profile, usr_birth_year, copy_call_number, copy_location,
6593 copy_owning_lib, copy_circ_lib, copy_bib_record, xact_start, xact_finish, target_copy,
6594 circ_lib, circ_staff, checkin_staff, checkin_lib, renewal_remaining, grace_period, due_date,
6595 stop_fines_time, checkin_time, create_time, duration, fine_interval, recurring_fine,
6596 max_fine, phone_renewal, desk_renewal, opac_renewal, duration_rule, recurring_fine_rule,
6597 max_fine_rule, stop_fines, workstation, checkin_workstation, checkin_scan_time, parent_circ
6598 FROM action.aged_circulation
6600 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,
6601 cp.call_number AS copy_call_number, circ.copy_location, cn.owning_lib AS copy_owning_lib, cp.circ_lib AS copy_circ_lib,
6602 cn.record AS copy_bib_record, circ.xact_start, circ.xact_finish, circ.target_copy, circ.circ_lib, circ.circ_staff, circ.checkin_staff,
6603 circ.checkin_lib, circ.renewal_remaining, circ.grace_period, circ.due_date, circ.stop_fines_time, circ.checkin_time, circ.create_time, circ.duration,
6604 circ.fine_interval, circ.recurring_fine, circ.max_fine, circ.phone_renewal, circ.desk_renewal, circ.opac_renewal, circ.duration_rule,
6605 circ.recurring_fine_rule, circ.max_fine_rule, circ.stop_fines, circ.workstation, circ.checkin_workstation, circ.checkin_scan_time,
6607 FROM action.circulation circ
6608 JOIN asset.copy cp ON (circ.target_copy = cp.id)
6609 JOIN asset.call_number cn ON (cp.call_number = cn.id)
6610 JOIN actor.usr p ON (circ.usr = p.id)
6611 LEFT JOIN actor.usr_address a ON (p.mailing_address = a.id)
6612 LEFT JOIN actor.usr_address b ON (p.billing_address = b.id);
6614 -- Update action.circulation with real copy_location numbers instead of all NULL
6615 DO $$BEGIN RAISE WARNING 'We are about to do an update on every row in action.circulation. This may take a while. %', timeofday(); END;$$;
6616 UPDATE action.circulation circ SET copy_location = ac.location FROM asset.copy ac WHERE ac.id = circ.target_copy;
6618 -- Set not null/default on new column, re-enable triggers
6619 ALTER TABLE action.circulation
6620 ALTER COLUMN copy_location SET NOT NULL,
6621 ALTER COLUMN copy_location SET DEFAULT 1,
6624 -- Evergreen DB patch 0664.schema.hold-current-shelf-lib.sql
6629 -- check whether patch can be applied
6630 SELECT evergreen.upgrade_deps_block_check('0664', :eg_version);
6632 -- add the new column
6633 ALTER TABLE action.hold_request ADD COLUMN current_shelf_lib
6634 INT REFERENCES actor.org_unit DEFERRABLE INITIALLY DEFERRED;
6636 -- Add some others before the UPDATE we are about to do breaks our ability to add columns
6637 -- But we need this table first.
6638 CREATE TABLE config.sms_carrier (
6639 id SERIAL PRIMARY KEY,
6643 active BOOLEAN DEFAULT TRUE
6646 ALTER TABLE action.hold_request ADD COLUMN sms_notify TEXT;
6647 ALTER TABLE action.hold_request ADD COLUMN sms_carrier INT REFERENCES config.sms_carrier (id);
6648 ALTER TABLE action.hold_request ADD CONSTRAINT sms_check CHECK (
6650 OR sms_carrier IS NOT NULL -- and implied sms_notify IS NOT NULL
6655 -- set the value for current_shelf_lib on existing shelved holds
6656 UPDATE action.hold_request
6657 SET current_shelf_lib = pickup_lib
6660 action.hold_request.shelf_time IS NOT NULL
6661 AND action.hold_request.capture_time IS NOT NULL
6662 AND action.hold_request.current_copy IS NOT NULL
6663 AND action.hold_request.fulfillment_time IS NULL
6664 AND action.hold_request.cancel_time IS NULL
6665 AND asset.copy.id = action.hold_request.current_copy
6666 AND asset.copy.status = 8; -- on holds shelf
6669 SELECT evergreen.upgrade_deps_block_check('0666', :eg_version);
6671 -- 950.data.seed-values.sql
6672 INSERT INTO config.settings_group (name, label) VALUES
6677 'SMS Text Messages',
6684 INSERT INTO config.org_unit_setting_type (name, grp, label, description, datatype) VALUES
6690 'Enable features that send SMS text messages.',
6696 '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.',
6703 'sms.disable_authentication_requirement.callnumbers',
6706 'sms.disable_authentication_requirement.callnumbers',
6707 'Disable auth requirement for texting call numbers.',
6712 'sms.disable_authentication_requirement.callnumbers',
6713 'Disable authentication requirement for sending call number information via SMS from the OPAC.',
6721 -- 090.schema.action.sql
6722 -- 950.data.seed-values.sql
6723 INSERT INTO config.usr_setting_type (name,grp,opac_visible,label,description,datatype,fm_class) VALUES (
6724 'opac.default_sms_carrier',
6728 'opac.default_sms_carrier',
6729 'Default SMS/Text Carrier',
6734 'opac.default_sms_carrier',
6735 'Default SMS/Text Carrier',
6743 INSERT INTO config.usr_setting_type (name,grp,opac_visible,label,description,datatype) VALUES (
6744 'opac.default_sms_notify',
6748 'opac.default_sms_notify',
6749 'Default SMS/Text Number',
6754 'opac.default_sms_notify',
6755 'Default SMS/Text Number',
6762 INSERT INTO config.usr_setting_type (name,grp,opac_visible,label,description,datatype) VALUES (
6763 'opac.default_phone',
6767 'opac.default_phone',
6768 'Default Phone Number',
6773 'opac.default_phone',
6774 'Default Phone Number',
6781 SELECT setval( 'config.sms_carrier_id_seq', 1000 );
6782 INSERT INTO config.sms_carrier VALUES
6799 'opensrf+$number@localhost',
6818 '$number@pcs.rogers.com',
6831 'Rogers Wireless (Alternate)',
6835 '1$number@mms.rogers.com',
6852 '$number@msg.telus.com',
6871 '$number@msg.telus.com',
6901 'Bell Mobility & Solo Mobile',
6905 '$number@txt.bell.ca',
6918 'Bell Mobility & Solo Mobile (Alternate)',
6922 '$number@txt.bellmobility.ca',
6939 '$number@sms.wirefree.informe.ca',
6956 '$number@mobiletxt.ca',
6973 '$number@sms.sasktel.com',
6990 '$number@text.mtsmobility.com',
7007 '$number@vmobile.ca',
7026 '$number@msg.iridium.com',
7043 '$number@msg.globalstarusa.com',
7060 '$number@bulletinmessenger.net', -- International Formatted number
7077 '$number@api.panaceamobile.com',
7096 '$number@cbeyond.sprintpcs.com',
7109 'General Communications, Inc.',
7113 '$number@mobile.gci.net',
7126 'Golden State Cellular',
7130 '$number@gscsms.com',
7137 'Cincinnati, Ohio, USA',
7147 '$number@gocbw.com',
7160 'Hawaiian Telcom Wireless',
7164 '$number@hawaii.sprintpcs.com',
7177 'i wireless (T-Mobile)',
7181 '$number.iws@iwspcs.net',
7194 'i-wireless (Sprint PCS)',
7198 '$number@iwirelesshometext.com',
7215 '$number@mymetropcs.com',
7232 '$number@mobile.kajeet.net',
7249 '$number@SMS.elementmobile.net',
7266 '$number@echoemail.net',
7283 '$number@myboostmobile.com',
7300 '$number@bellsouth.com',
7313 'Bluegrass Cellular',
7317 '$number@sms.bluecell.com',
7330 'AT&T Enterprise Paging',
7334 '$number@page.att.net',
7347 'AT&T Mobility/Wireless',
7351 '$number@txt.att.net',
7364 'AT&T Global Smart Messaging Suite',
7368 '$number@sms.smartmessagingsuite.com',
7381 'Alltel (Allied Wireless)',
7385 '$number@sms.alltelwireless.com',
7398 'Alaska Communications',
7402 '$number@msg.acsalaska.com',
7419 '$number@paging.acswireless.com',
7432 'Cingular (GoPhone prepaid)',
7436 '$number@cingulartext.com',
7449 'Cingular (Postpaid)',
7453 '$number@cingular.com',
7466 'Cellular One (Dobson) / O2 / Orange',
7470 '$number@mobile.celloneusa.com',
7487 '$number@csouth1.com',
7504 '$number@cellcom.quiktxt.com',
7517 'Chariton Valley Wireless',
7521 '$number@sms.cvalley.net',
7538 '$number@sms.mycricket.com',
7551 'Cleartalk Wireless',
7555 '$number@sms.cleartalk.us',
7572 '$number@sms.edgewireless.com',
7589 '$number@rinasms.com',
7606 '$number@tmomail.net',
7619 'Straight Talk / PagePlus Cellular',
7623 '$number@vtext.com',
7636 'South Central Communications',
7640 '$number@rinasms.com',
7657 '$number@smtext.com',
7674 '$number@messaging.sprintpcs.com',
7691 '$number@messaging.nextel.com',
7708 '$number@zsend.com', -- nine digit number
7725 '$number@qwestmp.com',
7742 '$number@email.uscc.net',
7759 '$number@utext.com',
7776 '$number@teleflip.com',
7793 '$number@vmobl.com',
7810 '$number@vtext.com',
7827 '$number@usamobility.net',
7844 '$number@viaerosms.com',
7861 '$number@mmst5.tracfone.com',
7874 'Centennial Wireless',
7878 '$number@cwemail.com',
7882 -- South Korea and USA
7887 'South Korea and USA',
7897 '$number@myhelio.com',
7902 INSERT INTO permission.perm_list ( id, code, description ) VALUES
7905 'ADMIN_SMS_CARRIER',
7908 'Allows a user to add/create/delete SMS Carrier entries.',
7915 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable)
7917 pgt.id, perm.id, aout.depth, TRUE
7919 permission.grp_tree pgt,
7920 permission.perm_list perm,
7921 actor.org_unit_type aout
7923 pgt.name = 'Global Administrator' AND
7924 aout.name = 'Consortium' AND
7925 perm.code = 'ADMIN_SMS_CARRIER';
7927 INSERT INTO action_trigger.reactor (
7932 'Send an SMS text message based on a user-defined template'
7935 INSERT INTO action_trigger.event_definition (
7950 'Hold Ready for Pickup SMS Notification',
7954 'CreateHoldNotification',
7959 [%- user = target.0.usr -%]
7960 From: [%- params.sender_email || default_sender %]
7961 To: [%- params.recipient_email || helpers.get_sms_gateway_email(target.0.sms_carrier,target.0.sms_notify) %]
7962 Subject: [% target.size %] hold(s) ready
7964 [% FOR hold IN target %][%-
7965 bibxml = helpers.xml_doc( hold.current_copy.call_number.record.marc );
7967 FOR part IN bibxml.findnodes(''//*[@tag="245"]/*[@code="a"]'');
7968 title = title _ part.textContent;
7970 author = bibxml.findnodes(''//*[@tag="100"]/*[@code="a"]'').textContent;
7971 %][% hold.usr.first_given_name %]:[% title %] @ [% hold.pickup_lib.name %]
7976 INSERT INTO action_trigger.environment (
7980 currval('action_trigger.event_definition_id_seq'),
7981 'current_copy.call_number.record.simple_record'
7983 currval('action_trigger.event_definition_id_seq'),
7986 currval('action_trigger.event_definition_id_seq'),
7987 'pickup_lib.billing_address'
7990 INSERT INTO action_trigger.hook(
7996 'acn.format.sms_text',
7999 'acn.format.sms_text',
8000 'A text message has been requested for a call number.',
8007 INSERT INTO action_trigger.event_definition (
8019 'acn.format.sms_text',
8023 From: [%- params.sender_email || default_sender %]
8024 To: [%- params.recipient_email || helpers.get_sms_gateway_email(user_data.sms_carrier,user_data.sms_notify) %]
8025 Subject: Call Number
8028 bibxml = helpers.xml_doc( target.record.marc );
8030 FOR part IN bibxml.findnodes(''//*[@tag="245"]/*[@code="a" or @code="b"]'');
8031 title = title _ part.textContent;
8033 author = bibxml.findnodes(''//*[@tag="100"]/*[@code="a"]'').textContent;
8035 Call Number: [% target.label %]
8036 Location: [% helpers.get_most_populous_location( target.id ).name %]
8037 Library: [% target.owning_lib.name %]
8042 Author: [% author %]
8047 INSERT INTO action_trigger.environment (
8051 currval('action_trigger.event_definition_id_seq'),
8052 'record.simple_record'
8054 currval('action_trigger.event_definition_id_seq'),
8055 'owning_lib.billing_address'
8059 -- 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';
8062 SELECT evergreen.upgrade_deps_block_check('0667', :eg_version);
8064 ALTER TABLE config.standing_penalty ADD staff_alert BOOL NOT NULL DEFAULT FALSE;
8067 -- for backwards compat, set all blocking penalties to alerts
8068 UPDATE config.standing_penalty SET staff_alert = TRUE
8069 WHERE id = 20 OR block_list IS NOT NULL;
8071 -- Evergreen DB patch 0668.schema.fix_indb_hold_permit.sql
8073 -- FIXME: insert description of change, if needed
8077 -- check whether patch can be applied
8078 SELECT evergreen.upgrade_deps_block_check('0668', :eg_version);
8080 -- FIXME: add/check SQL statements to perform the upgrade
8081 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$
8084 user_object actor.usr%ROWTYPE;
8085 age_protect_object config.rule_age_hold_protect%ROWTYPE;
8086 standing_penalty config.standing_penalty%ROWTYPE;
8087 transit_range_ou_type actor.org_unit_type%ROWTYPE;
8088 transit_source actor.org_unit%ROWTYPE;
8089 item_object asset.copy%ROWTYPE;
8090 item_cn_object asset.call_number%ROWTYPE;
8091 item_status_object config.copy_status%ROWTYPE;
8092 item_location_object asset.copy_location%ROWTYPE;
8093 ou_skip actor.org_unit_setting%ROWTYPE;
8094 result action.matrix_test_result;
8095 hold_test config.hold_matrix_matchpoint%ROWTYPE;
8096 use_active_date TEXT;
8097 age_protect_date TIMESTAMP WITH TIME ZONE;
8099 hold_transit_prox INT;
8100 frozen_hold_count INT;
8101 context_org_list INT[];
8104 SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
8105 SELECT INTO context_org_list ARRAY_ACCUM(id) FROM actor.org_unit_full_path( pickup_ou );
8107 result.success := TRUE;
8109 -- Fail if we couldn't find a user
8110 IF user_object.id IS NULL THEN
8111 result.fail_part := 'no_user';
8112 result.success := FALSE;
8118 SELECT INTO item_object * FROM asset.copy WHERE id = match_item;
8120 -- Fail if we couldn't find a copy
8121 IF item_object.id IS NULL THEN
8122 result.fail_part := 'no_item';
8123 result.success := FALSE;
8129 SELECT INTO matchpoint_id action.find_hold_matrix_matchpoint(pickup_ou, request_ou, match_item, match_user, match_requestor);
8130 result.matchpoint := matchpoint_id;
8132 SELECT INTO ou_skip * FROM actor.org_unit_setting WHERE name = 'circ.holds.target_skip_me' AND org_unit = item_object.circ_lib;
8134 -- Fail if the circ_lib for the item has circ.holds.target_skip_me set to true
8135 IF ou_skip.id IS NOT NULL AND ou_skip.value = 'true' THEN
8136 result.fail_part := 'circ.holds.target_skip_me';
8137 result.success := FALSE;
8143 -- Fail if user is barred
8144 IF user_object.barred IS TRUE THEN
8145 result.fail_part := 'actor.usr.barred';
8146 result.success := FALSE;
8152 SELECT INTO item_cn_object * FROM asset.call_number WHERE id = item_object.call_number;
8153 SELECT INTO item_status_object * FROM config.copy_status WHERE id = item_object.status;
8154 SELECT INTO item_location_object * FROM asset.copy_location WHERE id = item_object.location;
8156 -- Fail if we couldn't find any matchpoint (requires a default)
8157 IF matchpoint_id IS NULL THEN
8158 result.fail_part := 'no_matchpoint';
8159 result.success := FALSE;
8165 SELECT INTO hold_test * FROM config.hold_matrix_matchpoint WHERE id = matchpoint_id;
8167 IF hold_test.holdable IS FALSE THEN
8168 result.fail_part := 'config.hold_matrix_test.holdable';
8169 result.success := FALSE;
8174 IF item_object.holdable IS FALSE THEN
8175 result.fail_part := 'item.holdable';
8176 result.success := FALSE;
8181 IF item_status_object.holdable IS FALSE THEN
8182 result.fail_part := 'status.holdable';
8183 result.success := FALSE;
8188 IF item_location_object.holdable IS FALSE THEN
8189 result.fail_part := 'location.holdable';
8190 result.success := FALSE;
8195 IF hold_test.transit_range IS NOT NULL THEN
8196 SELECT INTO transit_range_ou_type * FROM actor.org_unit_type WHERE id = hold_test.transit_range;
8197 IF hold_test.distance_is_from_owner THEN
8198 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;
8200 SELECT INTO transit_source * FROM actor.org_unit WHERE id = item_object.circ_lib;
8203 PERFORM * FROM actor.org_unit_descendants( transit_source.id, transit_range_ou_type.depth ) WHERE id = pickup_ou;
8206 result.fail_part := 'transit_range';
8207 result.success := FALSE;
8213 FOR standing_penalty IN
8214 SELECT DISTINCT csp.*
8215 FROM actor.usr_standing_penalty usp
8216 JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
8217 WHERE usr = match_user
8218 AND usp.org_unit IN ( SELECT * FROM unnest(context_org_list) )
8219 AND (usp.stop_date IS NULL or usp.stop_date > NOW())
8220 AND csp.block_list LIKE '%HOLD%' LOOP
8222 result.fail_part := standing_penalty.name;
8223 result.success := FALSE;
8228 IF hold_test.stop_blocked_user IS TRUE THEN
8229 FOR standing_penalty IN
8230 SELECT DISTINCT csp.*
8231 FROM actor.usr_standing_penalty usp
8232 JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
8233 WHERE usr = match_user
8234 AND usp.org_unit IN ( SELECT * FROM unnest(context_org_list) )
8235 AND (usp.stop_date IS NULL or usp.stop_date > NOW())
8236 AND csp.block_list LIKE '%CIRC%' LOOP
8238 result.fail_part := standing_penalty.name;
8239 result.success := FALSE;
8245 IF hold_test.max_holds IS NOT NULL AND NOT retargetting THEN
8246 SELECT INTO hold_count COUNT(*)
8247 FROM action.hold_request
8248 WHERE usr = match_user
8249 AND fulfillment_time IS NULL
8250 AND cancel_time IS NULL
8251 AND CASE WHEN hold_test.include_frozen_holds THEN TRUE ELSE frozen IS FALSE END;
8253 IF hold_count >= hold_test.max_holds THEN
8254 result.fail_part := 'config.hold_matrix_test.max_holds';
8255 result.success := FALSE;
8261 IF item_object.age_protect IS NOT NULL THEN
8262 SELECT INTO age_protect_object * FROM config.rule_age_hold_protect WHERE id = item_object.age_protect;
8263 IF hold_test.distance_is_from_owner THEN
8264 SELECT INTO use_active_date value FROM actor.org_unit_ancestor_setting('circ.holds.age_protect.active_date', item_cn_object.owning_lib);
8266 SELECT INTO use_active_date value FROM actor.org_unit_ancestor_setting('circ.holds.age_protect.active_date', item_object.circ_lib);
8268 IF use_active_date = 'true' THEN
8269 age_protect_date := COALESCE(item_object.active_date, NOW());
8271 age_protect_date := item_object.create_date;
8273 IF age_protect_date + age_protect_object.age > NOW() THEN
8274 IF hold_test.distance_is_from_owner THEN
8275 SELECT INTO item_cn_object * FROM asset.call_number WHERE id = item_object.call_number;
8276 SELECT INTO hold_transit_prox prox FROM actor.org_unit_proximity WHERE from_org = item_cn_object.owning_lib AND to_org = pickup_ou;
8278 SELECT INTO hold_transit_prox prox FROM actor.org_unit_proximity WHERE from_org = item_object.circ_lib AND to_org = pickup_ou;
8281 IF hold_transit_prox > age_protect_object.prox THEN
8282 result.fail_part := 'config.rule_age_hold_protect.prox';
8283 result.success := FALSE;
8296 $func$ LANGUAGE plpgsql;
8299 -- Evergreen DB patch 0669.data.recall_and_force_holds.sql
8301 -- FIXME: insert description of change, if needed
8305 -- check whether patch can be applied
8306 SELECT evergreen.upgrade_deps_block_check('0669', :eg_version);
8308 -- FIXME: add/check SQL statements to perform the upgrade
8309 INSERT INTO permission.perm_list ( id, code, description ) VALUES
8310 ( 517, 'COPY_HOLDS_FORCE', oils_i18n_gettext( 517,
8311 'Allow a user to place a force hold on a specific copy', 'ppl', 'description' )),
8312 ( 518, 'COPY_HOLDS_RECALL', oils_i18n_gettext( 518,
8313 'Allow a user to place a cataloging recall on a specific copy', 'ppl', 'description' ));
8316 -- Evergreen DB patch 0670.data.mark-email-and-phone-invalid.sql
8318 -- Add org unit settings and standing penalty types to support
8319 -- the mark email/phone invalid features.
8322 -- check whether patch can be applied
8323 SELECT evergreen.upgrade_deps_block_check('0670', :eg_version);
8326 INSERT INTO config.standing_penalty (id, name, label, staff_alert, org_depth) VALUES
8329 'INVALID_PATRON_EMAIL_ADDRESS',
8332 'Patron had an invalid email address',
8341 'INVALID_PATRON_DAY_PHONE',
8344 'Patron had an invalid daytime phone number',
8353 'INVALID_PATRON_EVENING_PHONE',
8356 'Patron had an invalid evening phone number',
8365 'INVALID_PATRON_OTHER_PHONE',
8368 'Patron had an invalid other phone number',
8378 SELECT evergreen.upgrade_deps_block_check('0671', :eg_version);
8380 ALTER TABLE asset.copy_location
8381 ADD COLUMN checkin_alert BOOL NOT NULL DEFAULT FALSE;
8383 -- Evergreen DB patch 0672.fix-nonfiling-titles.sql
8385 -- Titles that begin with non-filing articles using apostrophes
8386 -- (for example, "L'armée") get spaces injected between the article
8387 -- and the subsequent text, which then breaks searching for titles
8388 -- beginning with those articles.
8390 -- This patch adds a nonfiling title element to MODS32 that can then
8391 -- be used to retrieve the title proper without affecting the spaces
8392 -- in the title. It's what we want, what we really really want, for
8397 -- check whether patch can be applied
8398 SELECT evergreen.upgrade_deps_block_check('0672', :eg_version);
8400 -- Update the XPath definition before the titleNonfiling element exists;
8401 -- but are you really going to read through the whole XSL below before
8402 -- seeing this important bit?
8403 UPDATE config.metabib_field
8404 SET xpath = $$//mods32:mods/mods32:titleNonfiling[mods32:title and not (@type)]$$,
8406 WHERE field_class = 'title' AND name = 'proper';
8408 UPDATE config.xml_transform SET xslt=$$<?xml version="1.0" encoding="UTF-8"?>
8409 <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">
8410 <xsl:output encoding="UTF-8" indent="yes" method="xml"/>
8412 Revision 1.14 - Fixed template isValid and fields 010, 020, 022, 024, 028, and 037 to output additional identifier elements
8413 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
8415 Revision 1.13 - Changed order of output under cartographics to reflect schema 2006/11/28 tmee
8417 Revision 1.12 - Updated to reflect MODS 3.2 Mapping 2006/10/11 tmee
8419 Revision 1.11 - The attribute objectPart moved from <languageTerm> to <language>
8422 Revision 1.10 MODS 3.1 revisions to language and classification elements
8423 (plus ability to find marc:collection embedded in wrapper elements such as SRU zs: wrappers)
8426 Revision 1.9 subfield $y was added to field 242 2004/09/02 10:57 jrad
8428 Revision 1.8 Subject chopPunctuation expanded and attribute fixes 2004/08/12 jrad
8430 Revision 1.7 2004/03/25 08:29 jrad
8432 Revision 1.6 various validation fixes 2004/02/20 ntra
8434 Revision 1.5 2003/10/02 16:18:58 ntra
8435 MODS2 to MODS3 updates, language unstacking and
8436 de-duping, chopPunctuation expanded
8438 Revision 1.3 2003/04/03 00:07:19 ntra
8439 Revision 1.3 Additional Changes not related to MODS Version 2.0 by ntra
8441 Revision 1.2 2003/03/24 19:37:42 ckeith
8445 <xsl:template match="/">
8447 <xsl:when test="//marc:collection">
8448 <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">
8449 <xsl:for-each select="//marc:collection/marc:record">
8450 <mods version="3.2">
8451 <xsl:call-template name="marcRecord"/>
8457 <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">
8458 <xsl:for-each select="//marc:record">
8459 <xsl:call-template name="marcRecord"/>
8465 <xsl:template name="marcRecord">
8466 <xsl:variable name="leader" select="marc:leader"/>
8467 <xsl:variable name="leader6" select="substring($leader,7,1)"/>
8468 <xsl:variable name="leader7" select="substring($leader,8,1)"/>
8469 <xsl:variable name="controlField008" select="marc:controlfield[@tag='008']"/>
8470 <xsl:variable name="typeOf008">
8472 <xsl:when test="$leader6='a'">
8474 <xsl:when test="$leader7='a' or $leader7='c' or $leader7='d' or $leader7='m'">BK</xsl:when>
8475 <xsl:when test="$leader7='b' or $leader7='i' or $leader7='s'">SE</xsl:when>
8478 <xsl:when test="$leader6='t'">BK</xsl:when>
8479 <xsl:when test="$leader6='p'">MM</xsl:when>
8480 <xsl:when test="$leader6='m'">CF</xsl:when>
8481 <xsl:when test="$leader6='e' or $leader6='f'">MP</xsl:when>
8482 <xsl:when test="$leader6='g' or $leader6='k' or $leader6='o' or $leader6='r'">VM</xsl:when>
8483 <xsl:when test="$leader6='c' or $leader6='d' or $leader6='i' or $leader6='j'">MU</xsl:when>
8486 <xsl:for-each select="marc:datafield[@tag='245']">
8488 <xsl:variable name="title">
8490 <xsl:when test="marc:subfield[@code='b']">
8491 <xsl:call-template name="specialSubfieldSelect">
8492 <xsl:with-param name="axis">b</xsl:with-param>
8493 <xsl:with-param name="beforeCodes">afgk</xsl:with-param>
8494 </xsl:call-template>
8497 <xsl:call-template name="subfieldSelect">
8498 <xsl:with-param name="codes">abfgk</xsl:with-param>
8499 </xsl:call-template>
8503 <xsl:variable name="titleChop">
8504 <xsl:call-template name="chopPunctuation">
8505 <xsl:with-param name="chopString">
8506 <xsl:value-of select="$title"/>
8508 </xsl:call-template>
8511 <xsl:when test="@ind2>0">
8513 <xsl:value-of select="substring($titleChop,1,@ind2)"/>
8516 <xsl:value-of select="substring($titleChop,@ind2+1)"/>
8521 <xsl:value-of select="$titleChop"/>
8525 <xsl:if test="marc:subfield[@code='b']">
8527 <xsl:call-template name="chopPunctuation">
8528 <xsl:with-param name="chopString">
8529 <xsl:call-template name="specialSubfieldSelect">
8530 <xsl:with-param name="axis">b</xsl:with-param>
8531 <xsl:with-param name="anyCodes">b</xsl:with-param>
8532 <xsl:with-param name="afterCodes">afgk</xsl:with-param>
8533 </xsl:call-template>
8535 </xsl:call-template>
8538 <xsl:call-template name="part"></xsl:call-template>
8540 <!-- A form of title that ignores non-filing characters; useful
8541 for not converting "L'Oreal" into "L' Oreal" at index time -->
8543 <xsl:variable name="title">
8545 <xsl:when test="marc:subfield[@code='b']">
8546 <xsl:call-template name="specialSubfieldSelect">
8547 <xsl:with-param name="axis">b</xsl:with-param>
8548 <xsl:with-param name="beforeCodes">afgk</xsl:with-param>
8549 </xsl:call-template>
8552 <xsl:call-template name="subfieldSelect">
8553 <xsl:with-param name="codes">abfgk</xsl:with-param>
8554 </xsl:call-template>
8559 <xsl:value-of select="$title"/>
8561 <xsl:if test="marc:subfield[@code='b']">
8563 <xsl:call-template name="chopPunctuation">
8564 <xsl:with-param name="chopString">
8565 <xsl:call-template name="specialSubfieldSelect">
8566 <xsl:with-param name="axis">b</xsl:with-param>
8567 <xsl:with-param name="anyCodes">b</xsl:with-param>
8568 <xsl:with-param name="afterCodes">afgk</xsl:with-param>
8569 </xsl:call-template>
8571 </xsl:call-template>
8574 <xsl:call-template name="part"></xsl:call-template>
8577 <xsl:for-each select="marc:datafield[@tag='210']">
8578 <titleInfo type="abbreviated">
8580 <xsl:call-template name="chopPunctuation">
8581 <xsl:with-param name="chopString">
8582 <xsl:call-template name="subfieldSelect">
8583 <xsl:with-param name="codes">a</xsl:with-param>
8584 </xsl:call-template>
8586 </xsl:call-template>
8588 <xsl:call-template name="subtitle"/>
8591 <xsl:for-each select="marc:datafield[@tag='242']">
8592 <titleInfo type="translated">
8593 <!--09/01/04 Added subfield $y-->
8594 <xsl:for-each select="marc:subfield[@code='y']">
8595 <xsl:attribute name="lang">
8596 <xsl:value-of select="text()"/>
8600 <xsl:call-template name="chopPunctuation">
8601 <xsl:with-param name="chopString">
8602 <xsl:call-template name="subfieldSelect">
8603 <!-- 1/04 removed $h, b -->
8604 <xsl:with-param name="codes">a</xsl:with-param>
8605 </xsl:call-template>
8607 </xsl:call-template>
8610 <xsl:call-template name="subtitle"/>
8611 <xsl:call-template name="part"/>
8614 <xsl:for-each select="marc:datafield[@tag='246']">
8615 <titleInfo type="alternative">
8616 <xsl:for-each select="marc:subfield[@code='i']">
8617 <xsl:attribute name="displayLabel">
8618 <xsl:value-of select="text()"/>
8622 <xsl:call-template name="chopPunctuation">
8623 <xsl:with-param name="chopString">
8624 <xsl:call-template name="subfieldSelect">
8625 <!-- 1/04 removed $h, $b -->
8626 <xsl:with-param name="codes">af</xsl:with-param>
8627 </xsl:call-template>
8629 </xsl:call-template>
8631 <xsl:call-template name="subtitle"/>
8632 <xsl:call-template name="part"/>
8635 <xsl:for-each select="marc:datafield[@tag='130']|marc:datafield[@tag='240']|marc:datafield[@tag='730'][@ind2!='2']">
8636 <titleInfo type="uniform">
8638 <xsl:variable name="str">
8639 <xsl:for-each select="marc:subfield">
8640 <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'])))">
8641 <xsl:value-of select="text()"/>
8642 <xsl:text> </xsl:text>
8646 <xsl:call-template name="chopPunctuation">
8647 <xsl:with-param name="chopString">
8648 <xsl:value-of select="substring($str,1,string-length($str)-1)"/>
8650 </xsl:call-template>
8652 <xsl:call-template name="part"/>
8655 <xsl:for-each select="marc:datafield[@tag='740'][@ind2!='2']">
8656 <titleInfo type="alternative">
8658 <xsl:call-template name="chopPunctuation">
8659 <xsl:with-param name="chopString">
8660 <xsl:call-template name="subfieldSelect">
8661 <xsl:with-param name="codes">ah</xsl:with-param>
8662 </xsl:call-template>
8664 </xsl:call-template>
8666 <xsl:call-template name="part"/>
8669 <xsl:for-each select="marc:datafield[@tag='100']">
8670 <name type="personal">
8671 <xsl:call-template name="nameABCDQ"/>
8672 <xsl:call-template name="affiliation"/>
8674 <roleTerm authority="marcrelator" type="text">creator</roleTerm>
8676 <xsl:call-template name="role"/>
8679 <xsl:for-each select="marc:datafield[@tag='110']">
8680 <name type="corporate">
8681 <xsl:call-template name="nameABCDN"/>
8683 <roleTerm authority="marcrelator" type="text">creator</roleTerm>
8685 <xsl:call-template name="role"/>
8688 <xsl:for-each select="marc:datafield[@tag='111']">
8689 <name type="conference">
8690 <xsl:call-template name="nameACDEQ"/>
8692 <roleTerm authority="marcrelator" type="text">creator</roleTerm>
8694 <xsl:call-template name="role"/>
8697 <xsl:for-each select="marc:datafield[@tag='700'][not(marc:subfield[@code='t'])]">
8698 <name type="personal">
8699 <xsl:call-template name="nameABCDQ"/>
8700 <xsl:call-template name="affiliation"/>
8701 <xsl:call-template name="role"/>
8704 <xsl:for-each select="marc:datafield[@tag='710'][not(marc:subfield[@code='t'])]">
8705 <name type="corporate">
8706 <xsl:call-template name="nameABCDN"/>
8707 <xsl:call-template name="role"/>
8710 <xsl:for-each select="marc:datafield[@tag='711'][not(marc:subfield[@code='t'])]">
8711 <name type="conference">
8712 <xsl:call-template name="nameACDEQ"/>
8713 <xsl:call-template name="role"/>
8716 <xsl:for-each select="marc:datafield[@tag='720'][not(marc:subfield[@code='t'])]">
8718 <xsl:if test="@ind1=1">
8719 <xsl:attribute name="type">
8720 <xsl:text>personal</xsl:text>
8724 <xsl:value-of select="marc:subfield[@code='a']"/>
8726 <xsl:call-template name="role"/>
8730 <xsl:if test="$leader7='c'">
8731 <xsl:attribute name="collection">yes</xsl:attribute>
8733 <xsl:if test="$leader6='d' or $leader6='f' or $leader6='p' or $leader6='t'">
8734 <xsl:attribute name="manuscript">yes</xsl:attribute>
8737 <xsl:when test="$leader6='a' or $leader6='t'">text</xsl:when>
8738 <xsl:when test="$leader6='e' or $leader6='f'">cartographic</xsl:when>
8739 <xsl:when test="$leader6='c' or $leader6='d'">notated music</xsl:when>
8740 <xsl:when test="$leader6='i'">sound recording-nonmusical</xsl:when>
8741 <xsl:when test="$leader6='j'">sound recording-musical</xsl:when>
8742 <xsl:when test="$leader6='k'">still image</xsl:when>
8743 <xsl:when test="$leader6='g'">moving image</xsl:when>
8744 <xsl:when test="$leader6='r'">three dimensional object</xsl:when>
8745 <xsl:when test="$leader6='m'">software, multimedia</xsl:when>
8746 <xsl:when test="$leader6='p'">mixed material</xsl:when>
8749 <xsl:if test="substring($controlField008,26,1)='d'">
8750 <genre authority="marc">globe</genre>
8752 <xsl:if test="marc:controlfield[@tag='007'][substring(text(),1,1)='a'][substring(text(),2,1)='r']">
8753 <genre authority="marc">remote sensing image</genre>
8755 <xsl:if test="$typeOf008='MP'">
8756 <xsl:variable name="controlField008-25" select="substring($controlField008,26,1)"></xsl:variable>
8758 <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']">
8759 <genre authority="marc">map</genre>
8761 <xsl:when test="$controlField008-25='e' or marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='d']">
8762 <genre authority="marc">atlas</genre>
8766 <xsl:if test="$typeOf008='SE'">
8767 <xsl:variable name="controlField008-21" select="substring($controlField008,22,1)"></xsl:variable>
8769 <xsl:when test="$controlField008-21='d'">
8770 <genre authority="marc">database</genre>
8772 <xsl:when test="$controlField008-21='l'">
8773 <genre authority="marc">loose-leaf</genre>
8775 <xsl:when test="$controlField008-21='m'">
8776 <genre authority="marc">series</genre>
8778 <xsl:when test="$controlField008-21='n'">
8779 <genre authority="marc">newspaper</genre>
8781 <xsl:when test="$controlField008-21='p'">
8782 <genre authority="marc">periodical</genre>
8784 <xsl:when test="$controlField008-21='w'">
8785 <genre authority="marc">web site</genre>
8789 <xsl:if test="$typeOf008='BK' or $typeOf008='SE'">
8790 <xsl:variable name="controlField008-24" select="substring($controlField008,25,4)"></xsl:variable>
8792 <xsl:when test="contains($controlField008-24,'a')">
8793 <genre authority="marc">abstract or summary</genre>
8795 <xsl:when test="contains($controlField008-24,'b')">
8796 <genre authority="marc">bibliography</genre>
8798 <xsl:when test="contains($controlField008-24,'c')">
8799 <genre authority="marc">catalog</genre>
8801 <xsl:when test="contains($controlField008-24,'d')">
8802 <genre authority="marc">dictionary</genre>
8804 <xsl:when test="contains($controlField008-24,'e')">
8805 <genre authority="marc">encyclopedia</genre>
8807 <xsl:when test="contains($controlField008-24,'f')">
8808 <genre authority="marc">handbook</genre>
8810 <xsl:when test="contains($controlField008-24,'g')">
8811 <genre authority="marc">legal article</genre>
8813 <xsl:when test="contains($controlField008-24,'i')">
8814 <genre authority="marc">index</genre>
8816 <xsl:when test="contains($controlField008-24,'k')">
8817 <genre authority="marc">discography</genre>
8819 <xsl:when test="contains($controlField008-24,'l')">
8820 <genre authority="marc">legislation</genre>
8822 <xsl:when test="contains($controlField008-24,'m')">
8823 <genre authority="marc">theses</genre>
8825 <xsl:when test="contains($controlField008-24,'n')">
8826 <genre authority="marc">survey of literature</genre>
8828 <xsl:when test="contains($controlField008-24,'o')">
8829 <genre authority="marc">review</genre>
8831 <xsl:when test="contains($controlField008-24,'p')">
8832 <genre authority="marc">programmed text</genre>
8834 <xsl:when test="contains($controlField008-24,'q')">
8835 <genre authority="marc">filmography</genre>
8837 <xsl:when test="contains($controlField008-24,'r')">
8838 <genre authority="marc">directory</genre>
8840 <xsl:when test="contains($controlField008-24,'s')">
8841 <genre authority="marc">statistics</genre>
8843 <xsl:when test="contains($controlField008-24,'t')">
8844 <genre authority="marc">technical report</genre>
8846 <xsl:when test="contains($controlField008-24,'v')">
8847 <genre authority="marc">legal case and case notes</genre>
8849 <xsl:when test="contains($controlField008-24,'w')">
8850 <genre authority="marc">law report or digest</genre>
8852 <xsl:when test="contains($controlField008-24,'z')">
8853 <genre authority="marc">treaty</genre>
8856 <xsl:variable name="controlField008-29" select="substring($controlField008,30,1)"></xsl:variable>
8858 <xsl:when test="$controlField008-29='1'">
8859 <genre authority="marc">conference publication</genre>
8863 <xsl:if test="$typeOf008='CF'">
8864 <xsl:variable name="controlField008-26" select="substring($controlField008,27,1)"></xsl:variable>
8866 <xsl:when test="$controlField008-26='a'">
8867 <genre authority="marc">numeric data</genre>
8869 <xsl:when test="$controlField008-26='e'">
8870 <genre authority="marc">database</genre>
8872 <xsl:when test="$controlField008-26='f'">
8873 <genre authority="marc">font</genre>
8875 <xsl:when test="$controlField008-26='g'">
8876 <genre authority="marc">game</genre>
8880 <xsl:if test="$typeOf008='BK'">
8881 <xsl:if test="substring($controlField008,25,1)='j'">
8882 <genre authority="marc">patent</genre>
8884 <xsl:if test="substring($controlField008,31,1)='1'">
8885 <genre authority="marc">festschrift</genre>
8887 <xsl:variable name="controlField008-34" select="substring($controlField008,35,1)"></xsl:variable>
8888 <xsl:if test="$controlField008-34='a' or $controlField008-34='b' or $controlField008-34='c' or $controlField008-34='d'">
8889 <genre authority="marc">biography</genre>
8891 <xsl:variable name="controlField008-33" select="substring($controlField008,34,1)"></xsl:variable>
8893 <xsl:when test="$controlField008-33='e'">
8894 <genre authority="marc">essay</genre>
8896 <xsl:when test="$controlField008-33='d'">
8897 <genre authority="marc">drama</genre>
8899 <xsl:when test="$controlField008-33='c'">
8900 <genre authority="marc">comic strip</genre>
8902 <xsl:when test="$controlField008-33='l'">
8903 <genre authority="marc">fiction</genre>
8905 <xsl:when test="$controlField008-33='h'">
8906 <genre authority="marc">humor, satire</genre>
8908 <xsl:when test="$controlField008-33='i'">
8909 <genre authority="marc">letter</genre>
8911 <xsl:when test="$controlField008-33='f'">
8912 <genre authority="marc">novel</genre>
8914 <xsl:when test="$controlField008-33='j'">
8915 <genre authority="marc">short story</genre>
8917 <xsl:when test="$controlField008-33='s'">
8918 <genre authority="marc">speech</genre>
8922 <xsl:if test="$typeOf008='MU'">
8923 <xsl:variable name="controlField008-30-31" select="substring($controlField008,31,2)"></xsl:variable>
8924 <xsl:if test="contains($controlField008-30-31,'b')">
8925 <genre authority="marc">biography</genre>
8927 <xsl:if test="contains($controlField008-30-31,'c')">
8928 <genre authority="marc">conference publication</genre>
8930 <xsl:if test="contains($controlField008-30-31,'d')">
8931 <genre authority="marc">drama</genre>
8933 <xsl:if test="contains($controlField008-30-31,'e')">
8934 <genre authority="marc">essay</genre>
8936 <xsl:if test="contains($controlField008-30-31,'f')">
8937 <genre authority="marc">fiction</genre>
8939 <xsl:if test="contains($controlField008-30-31,'o')">
8940 <genre authority="marc">folktale</genre>
8942 <xsl:if test="contains($controlField008-30-31,'h')">
8943 <genre authority="marc">history</genre>
8945 <xsl:if test="contains($controlField008-30-31,'k')">
8946 <genre authority="marc">humor, satire</genre>
8948 <xsl:if test="contains($controlField008-30-31,'m')">
8949 <genre authority="marc">memoir</genre>
8951 <xsl:if test="contains($controlField008-30-31,'p')">
8952 <genre authority="marc">poetry</genre>
8954 <xsl:if test="contains($controlField008-30-31,'r')">
8955 <genre authority="marc">rehearsal</genre>
8957 <xsl:if test="contains($controlField008-30-31,'g')">
8958 <genre authority="marc">reporting</genre>
8960 <xsl:if test="contains($controlField008-30-31,'s')">
8961 <genre authority="marc">sound</genre>
8963 <xsl:if test="contains($controlField008-30-31,'l')">
8964 <genre authority="marc">speech</genre>
8967 <xsl:if test="$typeOf008='VM'">
8968 <xsl:variable name="controlField008-33" select="substring($controlField008,34,1)"></xsl:variable>
8970 <xsl:when test="$controlField008-33='a'">
8971 <genre authority="marc">art original</genre>
8973 <xsl:when test="$controlField008-33='b'">
8974 <genre authority="marc">kit</genre>
8976 <xsl:when test="$controlField008-33='c'">
8977 <genre authority="marc">art reproduction</genre>
8979 <xsl:when test="$controlField008-33='d'">
8980 <genre authority="marc">diorama</genre>
8982 <xsl:when test="$controlField008-33='f'">
8983 <genre authority="marc">filmstrip</genre>
8985 <xsl:when test="$controlField008-33='g'">
8986 <genre authority="marc">legal article</genre>
8988 <xsl:when test="$controlField008-33='i'">
8989 <genre authority="marc">picture</genre>
8991 <xsl:when test="$controlField008-33='k'">
8992 <genre authority="marc">graphic</genre>
8994 <xsl:when test="$controlField008-33='l'">
8995 <genre authority="marc">technical drawing</genre>
8997 <xsl:when test="$controlField008-33='m'">
8998 <genre authority="marc">motion picture</genre>
9000 <xsl:when test="$controlField008-33='n'">
9001 <genre authority="marc">chart</genre>
9003 <xsl:when test="$controlField008-33='o'">
9004 <genre authority="marc">flash card</genre>
9006 <xsl:when test="$controlField008-33='p'">
9007 <genre authority="marc">microscope slide</genre>
9009 <xsl:when test="$controlField008-33='q' or marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='q']">
9010 <genre authority="marc">model</genre>
9012 <xsl:when test="$controlField008-33='r'">
9013 <genre authority="marc">realia</genre>
9015 <xsl:when test="$controlField008-33='s'">
9016 <genre authority="marc">slide</genre>
9018 <xsl:when test="$controlField008-33='t'">
9019 <genre authority="marc">transparency</genre>
9021 <xsl:when test="$controlField008-33='v'">
9022 <genre authority="marc">videorecording</genre>
9024 <xsl:when test="$controlField008-33='w'">
9025 <genre authority="marc">toy</genre>
9029 <xsl:for-each select="marc:datafield[@tag=655]">
9030 <genre authority="marc">
9031 <xsl:attribute name="authority">
9032 <xsl:value-of select="marc:subfield[@code='2']"/>
9034 <xsl:call-template name="subfieldSelect">
9035 <xsl:with-param name="codes">abvxyz</xsl:with-param>
9036 <xsl:with-param name="delimeter">-</xsl:with-param>
9037 </xsl:call-template>
9041 <xsl:variable name="MARCpublicationCode" select="normalize-space(substring($controlField008,16,3))"></xsl:variable>
9042 <xsl:if test="translate($MARCpublicationCode,'|','')">
9045 <xsl:attribute name="type">code</xsl:attribute>
9046 <xsl:attribute name="authority">marccountry</xsl:attribute>
9047 <xsl:value-of select="$MARCpublicationCode"/>
9051 <xsl:for-each select="marc:datafield[@tag=044]/marc:subfield[@code='c']">
9054 <xsl:attribute name="type">code</xsl:attribute>
9055 <xsl:attribute name="authority">iso3166</xsl:attribute>
9056 <xsl:value-of select="."/>
9060 <xsl:for-each select="marc:datafield[@tag=260]/marc:subfield[@code='a']">
9063 <xsl:attribute name="type">text</xsl:attribute>
9064 <xsl:call-template name="chopPunctuationFront">
9065 <xsl:with-param name="chopString">
9066 <xsl:call-template name="chopPunctuation">
9067 <xsl:with-param name="chopString" select="."/>
9068 </xsl:call-template>
9070 </xsl:call-template>
9074 <xsl:for-each select="marc:datafield[@tag=046]/marc:subfield[@code='m']">
9075 <dateValid point="start">
9076 <xsl:value-of select="."/>
9079 <xsl:for-each select="marc:datafield[@tag=046]/marc:subfield[@code='n']">
9080 <dateValid point="end">
9081 <xsl:value-of select="."/>
9084 <xsl:for-each select="marc:datafield[@tag=046]/marc:subfield[@code='j']">
9086 <xsl:value-of select="."/>
9089 <xsl:for-each select="marc:datafield[@tag=260]/marc:subfield[@code='b' or @code='c' or @code='g']">
9091 <xsl:when test="@code='b'">
9093 <xsl:call-template name="chopPunctuation">
9094 <xsl:with-param name="chopString" select="."/>
9095 <xsl:with-param name="punctuation">
9096 <xsl:text>:,;/ </xsl:text>
9098 </xsl:call-template>
9101 <xsl:when test="@code='c'">
9103 <xsl:call-template name="chopPunctuation">
9104 <xsl:with-param name="chopString" select="."/>
9105 </xsl:call-template>
9108 <xsl:when test="@code='g'">
9110 <xsl:value-of select="."/>
9115 <xsl:variable name="dataField260c">
9116 <xsl:call-template name="chopPunctuation">
9117 <xsl:with-param name="chopString" select="marc:datafield[@tag=260]/marc:subfield[@code='c']"></xsl:with-param>
9118 </xsl:call-template>
9120 <xsl:variable name="controlField008-7-10" select="normalize-space(substring($controlField008, 8, 4))"></xsl:variable>
9121 <xsl:variable name="controlField008-11-14" select="normalize-space(substring($controlField008, 12, 4))"></xsl:variable>
9122 <xsl:variable name="controlField008-6" select="normalize-space(substring($controlField008, 7, 1))"></xsl:variable>
9123 <xsl:if test="$controlField008-6='e' or $controlField008-6='p' or $controlField008-6='r' or $controlField008-6='t' or $controlField008-6='s'">
9124 <xsl:if test="$controlField008-7-10 and ($controlField008-7-10 != $dataField260c)">
9125 <dateIssued encoding="marc">
9126 <xsl:value-of select="$controlField008-7-10"/>
9130 <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'">
9131 <xsl:if test="$controlField008-7-10">
9132 <dateIssued encoding="marc" point="start">
9133 <xsl:value-of select="$controlField008-7-10"/>
9137 <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'">
9138 <xsl:if test="$controlField008-11-14">
9139 <dateIssued encoding="marc" point="end">
9140 <xsl:value-of select="$controlField008-11-14"/>
9144 <xsl:if test="$controlField008-6='q'">
9145 <xsl:if test="$controlField008-7-10">
9146 <dateIssued encoding="marc" point="start" qualifier="questionable">
9147 <xsl:value-of select="$controlField008-7-10"/>
9151 <xsl:if test="$controlField008-6='q'">
9152 <xsl:if test="$controlField008-11-14">
9153 <dateIssued encoding="marc" point="end" qualifier="questionable">
9154 <xsl:value-of select="$controlField008-11-14"/>
9158 <xsl:if test="$controlField008-6='t'">
9159 <xsl:if test="$controlField008-11-14">
9160 <copyrightDate encoding="marc">
9161 <xsl:value-of select="$controlField008-11-14"/>
9165 <xsl:for-each select="marc:datafield[@tag=033][@ind1=0 or @ind1=1]/marc:subfield[@code='a']">
9166 <dateCaptured encoding="iso8601">
9167 <xsl:value-of select="."/>
9170 <xsl:for-each select="marc:datafield[@tag=033][@ind1=2]/marc:subfield[@code='a'][1]">
9171 <dateCaptured encoding="iso8601" point="start">
9172 <xsl:value-of select="."/>
9175 <xsl:for-each select="marc:datafield[@tag=033][@ind1=2]/marc:subfield[@code='a'][2]">
9176 <dateCaptured encoding="iso8601" point="end">
9177 <xsl:value-of select="."/>
9180 <xsl:for-each select="marc:datafield[@tag=250]/marc:subfield[@code='a']">
9182 <xsl:value-of select="."/>
9185 <xsl:for-each select="marc:leader">
9188 <xsl:when test="$leader7='a' or $leader7='c' or $leader7='d' or $leader7='m'">monographic</xsl:when>
9189 <xsl:when test="$leader7='b' or $leader7='i' or $leader7='s'">continuing</xsl:when>
9193 <xsl:for-each select="marc:datafield[@tag=310]|marc:datafield[@tag=321]">
9195 <xsl:call-template name="subfieldSelect">
9196 <xsl:with-param name="codes">ab</xsl:with-param>
9197 </xsl:call-template>
9201 <xsl:variable name="controlField008-35-37" select="normalize-space(translate(substring($controlField008,36,3),'|#',''))"></xsl:variable>
9202 <xsl:if test="$controlField008-35-37">
9204 <languageTerm authority="iso639-2b" type="code">
9205 <xsl:value-of select="substring($controlField008,36,3)"/>
9209 <xsl:for-each select="marc:datafield[@tag=041]">
9210 <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']">
9211 <xsl:variable name="langCodes" select="."/>
9213 <xsl:when test="../marc:subfield[@code='2']='rfc3066'">
9214 <!-- not stacked but could be repeated -->
9215 <xsl:call-template name="rfcLanguages">
9216 <xsl:with-param name="nodeNum">
9217 <xsl:value-of select="1"/>
9219 <xsl:with-param name="usedLanguages">
9220 <xsl:text></xsl:text>
9222 <xsl:with-param name="controlField008-35-37">
9223 <xsl:value-of select="$controlField008-35-37"></xsl:value-of>
9225 </xsl:call-template>
9229 <xsl:variable name="allLanguages">
9230 <xsl:copy-of select="$langCodes"></xsl:copy-of>
9232 <xsl:variable name="currentLanguage">
9233 <xsl:value-of select="substring($allLanguages,1,3)"></xsl:value-of>
9235 <xsl:call-template name="isoLanguage">
9236 <xsl:with-param name="currentLanguage">
9237 <xsl:value-of select="substring($allLanguages,1,3)"></xsl:value-of>
9239 <xsl:with-param name="remainingLanguages">
9240 <xsl:value-of select="substring($allLanguages,4,string-length($allLanguages)-3)"></xsl:value-of>
9242 <xsl:with-param name="usedLanguages">
9243 <xsl:if test="$controlField008-35-37">
9244 <xsl:value-of select="$controlField008-35-37"></xsl:value-of>
9247 </xsl:call-template>
9252 <xsl:variable name="physicalDescription">
9253 <!--3.2 change tmee 007/11 -->
9254 <xsl:if test="$typeOf008='CF' and marc:controlfield[@tag=007][substring(.,12,1)='a']">
9255 <digitalOrigin>reformatted digital</digitalOrigin>
9257 <xsl:if test="$typeOf008='CF' and marc:controlfield[@tag=007][substring(.,12,1)='b']">
9258 <digitalOrigin>digitized microfilm</digitalOrigin>
9260 <xsl:if test="$typeOf008='CF' and marc:controlfield[@tag=007][substring(.,12,1)='d']">
9261 <digitalOrigin>digitized other analog</digitalOrigin>
9263 <xsl:variable name="controlField008-23" select="substring($controlField008,24,1)"></xsl:variable>
9264 <xsl:variable name="controlField008-29" select="substring($controlField008,30,1)"></xsl:variable>
9265 <xsl:variable name="check008-23">
9266 <xsl:if test="$typeOf008='BK' or $typeOf008='MU' or $typeOf008='SE' or $typeOf008='MM'">
9267 <xsl:value-of select="true()"></xsl:value-of>
9270 <xsl:variable name="check008-29">
9271 <xsl:if test="$typeOf008='MP' or $typeOf008='VM'">
9272 <xsl:value-of select="true()"></xsl:value-of>
9276 <xsl:when test="($check008-23 and $controlField008-23='f') or ($check008-29 and $controlField008-29='f')">
9277 <form authority="marcform">braille</form>
9279 <xsl:when test="($controlField008-23=' ' and ($leader6='c' or $leader6='d')) or (($typeOf008='BK' or $typeOf008='SE') and ($controlField008-23=' ' or $controlField008='r'))">
9280 <form authority="marcform">print</form>
9282 <xsl:when test="$leader6 = 'm' or ($check008-23 and $controlField008-23='s') or ($check008-29 and $controlField008-29='s')">
9283 <form authority="marcform">electronic</form>
9285 <xsl:when test="($check008-23 and $controlField008-23='b') or ($check008-29 and $controlField008-29='b')">
9286 <form authority="marcform">microfiche</form>
9288 <xsl:when test="($check008-23 and $controlField008-23='a') or ($check008-29 and $controlField008-29='a')">
9289 <form authority="marcform">microfilm</form>
9293 <xsl:if test="marc:datafield[@tag=130]/marc:subfield[@code='h']">
9294 <form authority="gmd">
9295 <xsl:call-template name="chopBrackets">
9296 <xsl:with-param name="chopString">
9297 <xsl:value-of select="marc:datafield[@tag=130]/marc:subfield[@code='h']"></xsl:value-of>
9299 </xsl:call-template>
9302 <xsl:if test="marc:datafield[@tag=240]/marc:subfield[@code='h']">
9303 <form authority="gmd">
9304 <xsl:call-template name="chopBrackets">
9305 <xsl:with-param name="chopString">
9306 <xsl:value-of select="marc:datafield[@tag=240]/marc:subfield[@code='h']"></xsl:value-of>
9308 </xsl:call-template>
9311 <xsl:if test="marc:datafield[@tag=242]/marc:subfield[@code='h']">
9312 <form authority="gmd">
9313 <xsl:call-template name="chopBrackets">
9314 <xsl:with-param name="chopString">
9315 <xsl:value-of select="marc:datafield[@tag=242]/marc:subfield[@code='h']"></xsl:value-of>
9317 </xsl:call-template>
9320 <xsl:if test="marc:datafield[@tag=245]/marc:subfield[@code='h']">
9321 <form authority="gmd">
9322 <xsl:call-template name="chopBrackets">
9323 <xsl:with-param name="chopString">
9324 <xsl:value-of select="marc:datafield[@tag=245]/marc:subfield[@code='h']"></xsl:value-of>
9326 </xsl:call-template>
9329 <xsl:if test="marc:datafield[@tag=246]/marc:subfield[@code='h']">
9330 <form authority="gmd">
9331 <xsl:call-template name="chopBrackets">
9332 <xsl:with-param name="chopString">
9333 <xsl:value-of select="marc:datafield[@tag=246]/marc:subfield[@code='h']"></xsl:value-of>
9335 </xsl:call-template>
9338 <xsl:if test="marc:datafield[@tag=730]/marc:subfield[@code='h']">
9339 <form authority="gmd">
9340 <xsl:call-template name="chopBrackets">
9341 <xsl:with-param name="chopString">
9342 <xsl:value-of select="marc:datafield[@tag=730]/marc:subfield[@code='h']"></xsl:value-of>
9344 </xsl:call-template>
9347 <xsl:for-each select="marc:datafield[@tag=256]/marc:subfield[@code='a']">
9349 <xsl:value-of select="."></xsl:value-of>
9352 <xsl:for-each select="marc:controlfield[@tag=007][substring(text(),1,1)='c']">
9354 <xsl:when test="substring(text(),14,1)='a'">
9355 <reformattingQuality>access</reformattingQuality>
9357 <xsl:when test="substring(text(),14,1)='p'">
9358 <reformattingQuality>preservation</reformattingQuality>
9360 <xsl:when test="substring(text(),14,1)='r'">
9361 <reformattingQuality>replacement</reformattingQuality>
9365 <!--3.2 change tmee 007/01 -->
9366 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='b']">
9367 <form authority="smd">chip cartridge</form>
9369 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='c']">
9370 <form authority="smd">computer optical disc cartridge</form>
9372 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='j']">
9373 <form authority="smd">magnetic disc</form>
9375 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='m']">
9376 <form authority="smd">magneto-optical disc</form>
9378 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='o']">
9379 <form authority="smd">optical disc</form>
9381 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='r']">
9382 <form authority="smd">remote</form>
9384 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='a']">
9385 <form authority="smd">tape cartridge</form>
9387 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='f']">
9388 <form authority="smd">tape cassette</form>
9390 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='h']">
9391 <form authority="smd">tape reel</form>
9394 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='d'][substring(text(),2,1)='a']">
9395 <form authority="smd">celestial globe</form>
9397 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='d'][substring(text(),2,1)='e']">
9398 <form authority="smd">earth moon globe</form>
9400 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='d'][substring(text(),2,1)='b']">
9401 <form authority="smd">planetary or lunar globe</form>
9403 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='d'][substring(text(),2,1)='c']">
9404 <form authority="smd">terrestrial globe</form>
9407 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='o'][substring(text(),2,1)='o']">
9408 <form authority="smd">kit</form>
9411 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='d']">
9412 <form authority="smd">atlas</form>
9414 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='g']">
9415 <form authority="smd">diagram</form>
9417 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='j']">
9418 <form authority="smd">map</form>
9420 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='q']">
9421 <form authority="smd">model</form>
9423 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='k']">
9424 <form authority="smd">profile</form>
9426 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='r']">
9427 <form authority="smd">remote-sensing image</form>
9429 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='s']">
9430 <form authority="smd">section</form>
9432 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='y']">
9433 <form authority="smd">view</form>
9436 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='a']">
9437 <form authority="smd">aperture card</form>
9439 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='e']">
9440 <form authority="smd">microfiche</form>
9442 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='f']">
9443 <form authority="smd">microfiche cassette</form>
9445 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='b']">
9446 <form authority="smd">microfilm cartridge</form>
9448 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='c']">
9449 <form authority="smd">microfilm cassette</form>
9451 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='d']">
9452 <form authority="smd">microfilm reel</form>
9454 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='g']">
9455 <form authority="smd">microopaque</form>
9458 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='m'][substring(text(),2,1)='c']">
9459 <form authority="smd">film cartridge</form>
9461 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='m'][substring(text(),2,1)='f']">
9462 <form authority="smd">film cassette</form>
9464 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='m'][substring(text(),2,1)='r']">
9465 <form authority="smd">film reel</form>
9468 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='n']">
9469 <form authority="smd">chart</form>
9471 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='c']">
9472 <form authority="smd">collage</form>
9474 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='d']">
9475 <form authority="smd">drawing</form>
9477 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='o']">
9478 <form authority="smd">flash card</form>
9480 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='e']">
9481 <form authority="smd">painting</form>
9483 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='f']">
9484 <form authority="smd">photomechanical print</form>
9486 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='g']">
9487 <form authority="smd">photonegative</form>
9489 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='h']">
9490 <form authority="smd">photoprint</form>
9492 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='i']">
9493 <form authority="smd">picture</form>
9495 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='j']">
9496 <form authority="smd">print</form>
9498 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='l']">
9499 <form authority="smd">technical drawing</form>
9502 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='q'][substring(text(),2,1)='q']">
9503 <form authority="smd">notated music</form>
9506 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='d']">
9507 <form authority="smd">filmslip</form>
9509 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='c']">
9510 <form authority="smd">filmstrip cartridge</form>
9512 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='o']">
9513 <form authority="smd">filmstrip roll</form>
9515 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='f']">
9516 <form authority="smd">other filmstrip type</form>
9518 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='s']">
9519 <form authority="smd">slide</form>
9521 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='t']">
9522 <form authority="smd">transparency</form>
9524 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='r'][substring(text(),2,1)='r']">
9525 <form authority="smd">remote-sensing image</form>
9527 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='e']">
9528 <form authority="smd">cylinder</form>
9530 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='q']">
9531 <form authority="smd">roll</form>
9533 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='g']">
9534 <form authority="smd">sound cartridge</form>
9536 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='s']">
9537 <form authority="smd">sound cassette</form>
9539 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='d']">
9540 <form authority="smd">sound disc</form>
9542 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='t']">
9543 <form authority="smd">sound-tape reel</form>
9545 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='i']">
9546 <form authority="smd">sound-track film</form>
9548 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='w']">
9549 <form authority="smd">wire recording</form>
9552 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='f'][substring(text(),2,1)='c']">
9553 <form authority="smd">braille</form>
9555 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='f'][substring(text(),2,1)='b']">
9556 <form authority="smd">combination</form>
9558 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='f'][substring(text(),2,1)='a']">
9559 <form authority="smd">moon</form>
9561 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='f'][substring(text(),2,1)='d']">
9562 <form authority="smd">tactile, with no writing system</form>
9565 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='t'][substring(text(),2,1)='c']">
9566 <form authority="smd">braille</form>
9568 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='t'][substring(text(),2,1)='b']">
9569 <form authority="smd">large print</form>
9571 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='t'][substring(text(),2,1)='a']">
9572 <form authority="smd">regular print</form>
9574 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='t'][substring(text(),2,1)='d']">
9575 <form authority="smd">text in looseleaf binder</form>
9578 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='v'][substring(text(),2,1)='c']">
9579 <form authority="smd">videocartridge</form>
9581 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='v'][substring(text(),2,1)='f']">
9582 <form authority="smd">videocassette</form>
9584 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='v'][substring(text(),2,1)='d']">
9585 <form authority="smd">videodisc</form>
9587 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='v'][substring(text(),2,1)='r']">
9588 <form authority="smd">videoreel</form>
9591 <xsl:for-each select="marc:datafield[@tag=856]/marc:subfield[@code='q'][string-length(.)>1]">
9593 <xsl:value-of select="."></xsl:value-of>
9594 </internetMediaType>
9596 <xsl:for-each select="marc:datafield[@tag=300]">
9598 <xsl:call-template name="subfieldSelect">
9599 <xsl:with-param name="codes">abce</xsl:with-param>
9600 </xsl:call-template>
9604 <xsl:if test="string-length(normalize-space($physicalDescription))">
9605 <physicalDescription>
9606 <xsl:copy-of select="$physicalDescription"></xsl:copy-of>
9607 </physicalDescription>
9609 <xsl:for-each select="marc:datafield[@tag=520]">
9611 <xsl:call-template name="uri"></xsl:call-template>
9612 <xsl:call-template name="subfieldSelect">
9613 <xsl:with-param name="codes">ab</xsl:with-param>
9614 </xsl:call-template>
9617 <xsl:for-each select="marc:datafield[@tag=505]">
9619 <xsl:call-template name="uri"></xsl:call-template>
9620 <xsl:call-template name="subfieldSelect">
9621 <xsl:with-param name="codes">agrt</xsl:with-param>
9622 </xsl:call-template>
9625 <xsl:for-each select="marc:datafield[@tag=521]">
9627 <xsl:call-template name="subfieldSelect">
9628 <xsl:with-param name="codes">ab</xsl:with-param>
9629 </xsl:call-template>
9632 <xsl:if test="$typeOf008='BK' or $typeOf008='CF' or $typeOf008='MU' or $typeOf008='VM'">
9633 <xsl:variable name="controlField008-22" select="substring($controlField008,23,1)"></xsl:variable>
9636 <xsl:when test="$controlField008-22='d'">
9637 <targetAudience authority="marctarget">adolescent</targetAudience>
9639 <xsl:when test="$controlField008-22='e'">
9640 <targetAudience authority="marctarget">adult</targetAudience>
9642 <xsl:when test="$controlField008-22='g'">
9643 <targetAudience authority="marctarget">general</targetAudience>
9645 <xsl:when test="$controlField008-22='b' or $controlField008-22='c' or $controlField008-22='j'">
9646 <targetAudience authority="marctarget">juvenile</targetAudience>
9648 <xsl:when test="$controlField008-22='a'">
9649 <targetAudience authority="marctarget">preschool</targetAudience>
9651 <xsl:when test="$controlField008-22='f'">
9652 <targetAudience authority="marctarget">specialized</targetAudience>
9656 <xsl:for-each select="marc:datafield[@tag=245]/marc:subfield[@code='c']">
9657 <note type="statement of responsibility">
9658 <xsl:value-of select="."></xsl:value-of>
9661 <xsl:for-each select="marc:datafield[@tag=500]">
9663 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
9664 <xsl:call-template name="uri"></xsl:call-template>
9668 <!--3.2 change tmee additional note fields-->
9670 <xsl:for-each select="marc:datafield[@tag=506]">
9671 <note type="restrictions">
9672 <xsl:call-template name="uri"></xsl:call-template>
9673 <xsl:variable name="str">
9674 <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
9675 <xsl:value-of select="."></xsl:value-of>
9676 <xsl:text> </xsl:text>
9679 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
9683 <xsl:for-each select="marc:datafield[@tag=510]">
9684 <note type="citation/reference">
9685 <xsl:call-template name="uri"></xsl:call-template>
9686 <xsl:variable name="str">
9687 <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
9688 <xsl:value-of select="."></xsl:value-of>
9689 <xsl:text> </xsl:text>
9692 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
9697 <xsl:for-each select="marc:datafield[@tag=511]">
9698 <note type="performers">
9699 <xsl:call-template name="uri"></xsl:call-template>
9700 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
9703 <xsl:for-each select="marc:datafield[@tag=518]">
9705 <xsl:call-template name="uri"></xsl:call-template>
9706 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
9710 <xsl:for-each select="marc:datafield[@tag=530]">
9711 <note type="additional physical form">
9712 <xsl:call-template name="uri"></xsl:call-template>
9713 <xsl:variable name="str">
9714 <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
9715 <xsl:value-of select="."></xsl:value-of>
9716 <xsl:text> </xsl:text>
9719 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
9723 <xsl:for-each select="marc:datafield[@tag=533]">
9724 <note type="reproduction">
9725 <xsl:call-template name="uri"></xsl:call-template>
9726 <xsl:variable name="str">
9727 <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
9728 <xsl:value-of select="."></xsl:value-of>
9729 <xsl:text> </xsl:text>
9732 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
9736 <xsl:for-each select="marc:datafield[@tag=534]">
9737 <note type="original version">
9738 <xsl:call-template name="uri"></xsl:call-template>
9739 <xsl:variable name="str">
9740 <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
9741 <xsl:value-of select="."></xsl:value-of>
9742 <xsl:text> </xsl:text>
9745 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
9749 <xsl:for-each select="marc:datafield[@tag=538]">
9750 <note type="system details">
9751 <xsl:call-template name="uri"></xsl:call-template>
9752 <xsl:variable name="str">
9753 <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
9754 <xsl:value-of select="."></xsl:value-of>
9755 <xsl:text> </xsl:text>
9758 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
9762 <xsl:for-each select="marc:datafield[@tag=583]">
9763 <note type="action">
9764 <xsl:call-template name="uri"></xsl:call-template>
9765 <xsl:variable name="str">
9766 <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
9767 <xsl:value-of select="."></xsl:value-of>
9768 <xsl:text> </xsl:text>
9771 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
9779 <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]">
9781 <xsl:call-template name="uri"></xsl:call-template>
9782 <xsl:variable name="str">
9783 <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
9784 <xsl:value-of select="."></xsl:value-of>
9785 <xsl:text> </xsl:text>
9788 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
9791 <xsl:for-each select="marc:datafield[@tag=034][marc:subfield[@code='d' or @code='e' or @code='f' or @code='g']]">
9795 <xsl:call-template name="subfieldSelect">
9796 <xsl:with-param name="codes">defg</xsl:with-param>
9797 </xsl:call-template>
9802 <xsl:for-each select="marc:datafield[@tag=043]">
9804 <xsl:for-each select="marc:subfield[@code='a' or @code='b' or @code='c']">
9806 <xsl:attribute name="authority">
9807 <xsl:if test="@code='a'">
9808 <xsl:text>marcgac</xsl:text>
9810 <xsl:if test="@code='b'">
9811 <xsl:value-of select="following-sibling::marc:subfield[@code=2]"></xsl:value-of>
9813 <xsl:if test="@code='c'">
9814 <xsl:text>iso3166</xsl:text>
9817 <xsl:value-of select="self::marc:subfield"></xsl:value-of>
9822 <!-- tmee 2006/11/27 -->
9823 <xsl:for-each select="marc:datafield[@tag=255]">
9825 <xsl:for-each select="marc:subfield[@code='a' or @code='b' or @code='c']">
9827 <xsl:if test="@code='a'">
9829 <xsl:value-of select="."></xsl:value-of>
9832 <xsl:if test="@code='b'">
9834 <xsl:value-of select="."></xsl:value-of>
9837 <xsl:if test="@code='c'">
9839 <xsl:value-of select="."></xsl:value-of>
9847 <xsl:apply-templates select="marc:datafield[653 >= @tag and @tag >= 600]"></xsl:apply-templates>
9848 <xsl:apply-templates select="marc:datafield[@tag=656]"></xsl:apply-templates>
9849 <xsl:for-each select="marc:datafield[@tag=752]">
9851 <hierarchicalGeographic>
9852 <xsl:for-each select="marc:subfield[@code='a']">
9854 <xsl:call-template name="chopPunctuation">
9855 <xsl:with-param name="chopString" select="."></xsl:with-param>
9856 </xsl:call-template>
9859 <xsl:for-each select="marc:subfield[@code='b']">
9861 <xsl:call-template name="chopPunctuation">
9862 <xsl:with-param name="chopString" select="."></xsl:with-param>
9863 </xsl:call-template>
9866 <xsl:for-each select="marc:subfield[@code='c']">
9868 <xsl:call-template name="chopPunctuation">
9869 <xsl:with-param name="chopString" select="."></xsl:with-param>
9870 </xsl:call-template>
9873 <xsl:for-each select="marc:subfield[@code='d']">
9875 <xsl:call-template name="chopPunctuation">
9876 <xsl:with-param name="chopString" select="."></xsl:with-param>
9877 </xsl:call-template>
9880 </hierarchicalGeographic>
9883 <xsl:for-each select="marc:datafield[@tag=045][marc:subfield[@code='b']]">
9886 <xsl:when test="@ind1=2">
9887 <temporal encoding="iso8601" point="start">
9888 <xsl:call-template name="chopPunctuation">
9889 <xsl:with-param name="chopString">
9890 <xsl:value-of select="marc:subfield[@code='b'][1]"></xsl:value-of>
9892 </xsl:call-template>
9894 <temporal encoding="iso8601" point="end">
9895 <xsl:call-template name="chopPunctuation">
9896 <xsl:with-param name="chopString">
9897 <xsl:value-of select="marc:subfield[@code='b'][2]"></xsl:value-of>
9899 </xsl:call-template>
9903 <xsl:for-each select="marc:subfield[@code='b']">
9904 <temporal encoding="iso8601">
9905 <xsl:call-template name="chopPunctuation">
9906 <xsl:with-param name="chopString" select="."></xsl:with-param>
9907 </xsl:call-template>
9914 <xsl:for-each select="marc:datafield[@tag=050]">
9915 <xsl:for-each select="marc:subfield[@code='b']">
9916 <classification authority="lcc">
9917 <xsl:if test="../marc:subfield[@code='3']">
9918 <xsl:attribute name="displayLabel">
9919 <xsl:value-of select="../marc:subfield[@code='3']"></xsl:value-of>
9922 <xsl:value-of select="preceding-sibling::marc:subfield[@code='a'][1]"></xsl:value-of>
9923 <xsl:text> </xsl:text>
9924 <xsl:value-of select="text()"></xsl:value-of>
9927 <xsl:for-each select="marc:subfield[@code='a'][not(following-sibling::marc:subfield[@code='b'])]">
9928 <classification authority="lcc">
9929 <xsl:if test="../marc:subfield[@code='3']">
9930 <xsl:attribute name="displayLabel">
9931 <xsl:value-of select="../marc:subfield[@code='3']"></xsl:value-of>
9934 <xsl:value-of select="text()"></xsl:value-of>
9938 <xsl:for-each select="marc:datafield[@tag=082]">
9939 <classification authority="ddc">
9940 <xsl:if test="marc:subfield[@code='2']">
9941 <xsl:attribute name="edition">
9942 <xsl:value-of select="marc:subfield[@code='2']"></xsl:value-of>
9945 <xsl:call-template name="subfieldSelect">
9946 <xsl:with-param name="codes">ab</xsl:with-param>
9947 </xsl:call-template>
9950 <xsl:for-each select="marc:datafield[@tag=080]">
9951 <classification authority="udc">
9952 <xsl:call-template name="subfieldSelect">
9953 <xsl:with-param name="codes">abx</xsl:with-param>
9954 </xsl:call-template>
9957 <xsl:for-each select="marc:datafield[@tag=060]">
9958 <classification authority="nlm">
9959 <xsl:call-template name="subfieldSelect">
9960 <xsl:with-param name="codes">ab</xsl:with-param>
9961 </xsl:call-template>
9964 <xsl:for-each select="marc:datafield[@tag=086][@ind1=0]">
9965 <classification authority="sudocs">
9966 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
9969 <xsl:for-each select="marc:datafield[@tag=086][@ind1=1]">
9970 <classification authority="candoc">
9971 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
9974 <xsl:for-each select="marc:datafield[@tag=086]">
9976 <xsl:attribute name="authority">
9977 <xsl:value-of select="marc:subfield[@code='2']"></xsl:value-of>
9979 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
9982 <xsl:for-each select="marc:datafield[@tag=084]">
9984 <xsl:attribute name="authority">
9985 <xsl:value-of select="marc:subfield[@code='2']"></xsl:value-of>
9987 <xsl:call-template name="subfieldSelect">
9988 <xsl:with-param name="codes">ab</xsl:with-param>
9989 </xsl:call-template>
9992 <xsl:for-each select="marc:datafield[@tag=440]">
9993 <relatedItem type="series">
9996 <xsl:call-template name="chopPunctuation">
9997 <xsl:with-param name="chopString">
9998 <xsl:call-template name="subfieldSelect">
9999 <xsl:with-param name="codes">av</xsl:with-param>
10000 </xsl:call-template>
10002 </xsl:call-template>
10004 <xsl:call-template name="part"></xsl:call-template>
10008 <xsl:for-each select="marc:datafield[@tag=490][@ind1=0]">
10009 <relatedItem type="series">
10012 <xsl:call-template name="chopPunctuation">
10013 <xsl:with-param name="chopString">
10014 <xsl:call-template name="subfieldSelect">
10015 <xsl:with-param name="codes">av</xsl:with-param>
10016 </xsl:call-template>
10018 </xsl:call-template>
10020 <xsl:call-template name="part"></xsl:call-template>
10024 <xsl:for-each select="marc:datafield[@tag=510]">
10025 <relatedItem type="isReferencedBy">
10027 <xsl:call-template name="subfieldSelect">
10028 <xsl:with-param name="codes">abcx3</xsl:with-param>
10029 </xsl:call-template>
10033 <xsl:for-each select="marc:datafield[@tag=534]">
10034 <relatedItem type="original">
10035 <xsl:call-template name="relatedTitle"></xsl:call-template>
10036 <xsl:call-template name="relatedName"></xsl:call-template>
10037 <xsl:if test="marc:subfield[@code='b' or @code='c']">
10039 <xsl:for-each select="marc:subfield[@code='c']">
10041 <xsl:value-of select="."></xsl:value-of>
10044 <xsl:for-each select="marc:subfield[@code='b']">
10046 <xsl:value-of select="."></xsl:value-of>
10051 <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
10052 <xsl:for-each select="marc:subfield[@code='z']">
10053 <identifier type="isbn">
10054 <xsl:value-of select="."></xsl:value-of>
10057 <xsl:call-template name="relatedNote"></xsl:call-template>
10060 <xsl:for-each select="marc:datafield[@tag=700][marc:subfield[@code='t']]">
10062 <xsl:call-template name="constituentOrRelatedType"></xsl:call-template>
10065 <xsl:call-template name="chopPunctuation">
10066 <xsl:with-param name="chopString">
10067 <xsl:call-template name="specialSubfieldSelect">
10068 <xsl:with-param name="anyCodes">tfklmorsv</xsl:with-param>
10069 <xsl:with-param name="axis">t</xsl:with-param>
10070 <xsl:with-param name="afterCodes">g</xsl:with-param>
10071 </xsl:call-template>
10073 </xsl:call-template>
10075 <xsl:call-template name="part"></xsl:call-template>
10077 <name type="personal">
10079 <xsl:call-template name="specialSubfieldSelect">
10080 <xsl:with-param name="anyCodes">aq</xsl:with-param>
10081 <xsl:with-param name="axis">t</xsl:with-param>
10082 <xsl:with-param name="beforeCodes">g</xsl:with-param>
10083 </xsl:call-template>
10085 <xsl:call-template name="termsOfAddress"></xsl:call-template>
10086 <xsl:call-template name="nameDate"></xsl:call-template>
10087 <xsl:call-template name="role"></xsl:call-template>
10089 <xsl:call-template name="relatedForm"></xsl:call-template>
10090 <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
10093 <xsl:for-each select="marc:datafield[@tag=710][marc:subfield[@code='t']]">
10095 <xsl:call-template name="constituentOrRelatedType"></xsl:call-template>
10098 <xsl:call-template name="chopPunctuation">
10099 <xsl:with-param name="chopString">
10100 <xsl:call-template name="specialSubfieldSelect">
10101 <xsl:with-param name="anyCodes">tfklmorsv</xsl:with-param>
10102 <xsl:with-param name="axis">t</xsl:with-param>
10103 <xsl:with-param name="afterCodes">dg</xsl:with-param>
10104 </xsl:call-template>
10106 </xsl:call-template>
10108 <xsl:call-template name="relatedPartNumName"></xsl:call-template>
10110 <name type="corporate">
10111 <xsl:for-each select="marc:subfield[@code='a']">
10113 <xsl:value-of select="."></xsl:value-of>
10116 <xsl:for-each select="marc:subfield[@code='b']">
10118 <xsl:value-of select="."></xsl:value-of>
10121 <xsl:variable name="tempNamePart">
10122 <xsl:call-template name="specialSubfieldSelect">
10123 <xsl:with-param name="anyCodes">c</xsl:with-param>
10124 <xsl:with-param name="axis">t</xsl:with-param>
10125 <xsl:with-param name="beforeCodes">dgn</xsl:with-param>
10126 </xsl:call-template>
10128 <xsl:if test="normalize-space($tempNamePart)">
10130 <xsl:value-of select="$tempNamePart"></xsl:value-of>
10133 <xsl:call-template name="role"></xsl:call-template>
10135 <xsl:call-template name="relatedForm"></xsl:call-template>
10136 <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
10139 <xsl:for-each select="marc:datafield[@tag=711][marc:subfield[@code='t']]">
10141 <xsl:call-template name="constituentOrRelatedType"></xsl:call-template>
10144 <xsl:call-template name="chopPunctuation">
10145 <xsl:with-param name="chopString">
10146 <xsl:call-template name="specialSubfieldSelect">
10147 <xsl:with-param name="anyCodes">tfklsv</xsl:with-param>
10148 <xsl:with-param name="axis">t</xsl:with-param>
10149 <xsl:with-param name="afterCodes">g</xsl:with-param>
10150 </xsl:call-template>
10152 </xsl:call-template>
10154 <xsl:call-template name="relatedPartNumName"></xsl:call-template>
10156 <name type="conference">
10158 <xsl:call-template name="specialSubfieldSelect">
10159 <xsl:with-param name="anyCodes">aqdc</xsl:with-param>
10160 <xsl:with-param name="axis">t</xsl:with-param>
10161 <xsl:with-param name="beforeCodes">gn</xsl:with-param>
10162 </xsl:call-template>
10165 <xsl:call-template name="relatedForm"></xsl:call-template>
10166 <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
10169 <xsl:for-each select="marc:datafield[@tag=730][@ind2=2]">
10171 <xsl:call-template name="constituentOrRelatedType"></xsl:call-template>
10174 <xsl:call-template name="chopPunctuation">
10175 <xsl:with-param name="chopString">
10176 <xsl:call-template name="subfieldSelect">
10177 <xsl:with-param name="codes">adfgklmorsv</xsl:with-param>
10178 </xsl:call-template>
10180 </xsl:call-template>
10182 <xsl:call-template name="part"></xsl:call-template>
10184 <xsl:call-template name="relatedForm"></xsl:call-template>
10185 <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
10188 <xsl:for-each select="marc:datafield[@tag=740][@ind2=2]">
10190 <xsl:call-template name="constituentOrRelatedType"></xsl:call-template>
10193 <xsl:call-template name="chopPunctuation">
10194 <xsl:with-param name="chopString">
10195 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
10197 </xsl:call-template>
10199 <xsl:call-template name="part"></xsl:call-template>
10201 <xsl:call-template name="relatedForm"></xsl:call-template>
10204 <xsl:for-each select="marc:datafield[@tag=760]|marc:datafield[@tag=762]">
10205 <relatedItem type="series">
10206 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
10209 <xsl:for-each select="marc:datafield[@tag=765]|marc:datafield[@tag=767]|marc:datafield[@tag=777]|marc:datafield[@tag=787]">
10211 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
10214 <xsl:for-each select="marc:datafield[@tag=775]">
10215 <relatedItem type="otherVersion">
10216 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
10219 <xsl:for-each select="marc:datafield[@tag=770]|marc:datafield[@tag=774]">
10220 <relatedItem type="constituent">
10221 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
10224 <xsl:for-each select="marc:datafield[@tag=772]|marc:datafield[@tag=773]">
10225 <relatedItem type="host">
10226 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
10229 <xsl:for-each select="marc:datafield[@tag=776]">
10230 <relatedItem type="otherFormat">
10231 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
10234 <xsl:for-each select="marc:datafield[@tag=780]">
10235 <relatedItem type="preceding">
10236 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
10239 <xsl:for-each select="marc:datafield[@tag=785]">
10240 <relatedItem type="succeeding">
10241 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
10244 <xsl:for-each select="marc:datafield[@tag=786]">
10245 <relatedItem type="original">
10246 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
10249 <xsl:for-each select="marc:datafield[@tag=800]">
10250 <relatedItem type="series">
10253 <xsl:call-template name="chopPunctuation">
10254 <xsl:with-param name="chopString">
10255 <xsl:call-template name="specialSubfieldSelect">
10256 <xsl:with-param name="anyCodes">tfklmorsv</xsl:with-param>
10257 <xsl:with-param name="axis">t</xsl:with-param>
10258 <xsl:with-param name="afterCodes">g</xsl:with-param>
10259 </xsl:call-template>
10261 </xsl:call-template>
10263 <xsl:call-template name="part"></xsl:call-template>
10265 <name type="personal">
10267 <xsl:call-template name="chopPunctuation">
10268 <xsl:with-param name="chopString">
10269 <xsl:call-template name="specialSubfieldSelect">
10270 <xsl:with-param name="anyCodes">aq</xsl:with-param>
10271 <xsl:with-param name="axis">t</xsl:with-param>
10272 <xsl:with-param name="beforeCodes">g</xsl:with-param>
10273 </xsl:call-template>
10275 </xsl:call-template>
10277 <xsl:call-template name="termsOfAddress"></xsl:call-template>
10278 <xsl:call-template name="nameDate"></xsl:call-template>
10279 <xsl:call-template name="role"></xsl:call-template>
10281 <xsl:call-template name="relatedForm"></xsl:call-template>
10284 <xsl:for-each select="marc:datafield[@tag=810]">
10285 <relatedItem type="series">
10288 <xsl:call-template name="chopPunctuation">
10289 <xsl:with-param name="chopString">
10290 <xsl:call-template name="specialSubfieldSelect">
10291 <xsl:with-param name="anyCodes">tfklmorsv</xsl:with-param>
10292 <xsl:with-param name="axis">t</xsl:with-param>
10293 <xsl:with-param name="afterCodes">dg</xsl:with-param>
10294 </xsl:call-template>
10296 </xsl:call-template>
10298 <xsl:call-template name="relatedPartNumName"></xsl:call-template>
10300 <name type="corporate">
10301 <xsl:for-each select="marc:subfield[@code='a']">
10303 <xsl:value-of select="."></xsl:value-of>
10306 <xsl:for-each select="marc:subfield[@code='b']">
10308 <xsl:value-of select="."></xsl:value-of>
10312 <xsl:call-template name="specialSubfieldSelect">
10313 <xsl:with-param name="anyCodes">c</xsl:with-param>
10314 <xsl:with-param name="axis">t</xsl:with-param>
10315 <xsl:with-param name="beforeCodes">dgn</xsl:with-param>
10316 </xsl:call-template>
10318 <xsl:call-template name="role"></xsl:call-template>
10320 <xsl:call-template name="relatedForm"></xsl:call-template>
10323 <xsl:for-each select="marc:datafield[@tag=811]">
10324 <relatedItem type="series">
10327 <xsl:call-template name="chopPunctuation">
10328 <xsl:with-param name="chopString">
10329 <xsl:call-template name="specialSubfieldSelect">
10330 <xsl:with-param name="anyCodes">tfklsv</xsl:with-param>
10331 <xsl:with-param name="axis">t</xsl:with-param>
10332 <xsl:with-param name="afterCodes">g</xsl:with-param>
10333 </xsl:call-template>
10335 </xsl:call-template>
10337 <xsl:call-template name="relatedPartNumName"/>
10339 <name type="conference">
10341 <xsl:call-template name="specialSubfieldSelect">
10342 <xsl:with-param name="anyCodes">aqdc</xsl:with-param>
10343 <xsl:with-param name="axis">t</xsl:with-param>
10344 <xsl:with-param name="beforeCodes">gn</xsl:with-param>
10345 </xsl:call-template>
10347 <xsl:call-template name="role"/>
10349 <xsl:call-template name="relatedForm"/>
10352 <xsl:for-each select="marc:datafield[@tag='830']">
10353 <relatedItem type="series">
10356 <xsl:call-template name="chopPunctuation">
10357 <xsl:with-param name="chopString">
10358 <xsl:call-template name="subfieldSelect">
10359 <xsl:with-param name="codes">adfgklmorsv</xsl:with-param>
10360 </xsl:call-template>
10362 </xsl:call-template>
10364 <xsl:call-template name="part"/>
10366 <xsl:call-template name="relatedForm"/>
10369 <xsl:for-each select="marc:datafield[@tag='856'][@ind2='2']/marc:subfield[@code='q']">
10371 <internetMediaType>
10372 <xsl:value-of select="."/>
10373 </internetMediaType>
10376 <xsl:for-each select="marc:datafield[@tag='020']">
10377 <xsl:call-template name="isInvalid">
10378 <xsl:with-param name="type">isbn</xsl:with-param>
10379 </xsl:call-template>
10380 <xsl:if test="marc:subfield[@code='a']">
10381 <identifier type="isbn">
10382 <xsl:value-of select="marc:subfield[@code='a']"/>
10386 <xsl:for-each select="marc:datafield[@tag='024'][@ind1='0']">
10387 <xsl:call-template name="isInvalid">
10388 <xsl:with-param name="type">isrc</xsl:with-param>
10389 </xsl:call-template>
10390 <xsl:if test="marc:subfield[@code='a']">
10391 <identifier type="isrc">
10392 <xsl:value-of select="marc:subfield[@code='a']"/>
10396 <xsl:for-each select="marc:datafield[@tag='024'][@ind1='2']">
10397 <xsl:call-template name="isInvalid">
10398 <xsl:with-param name="type">ismn</xsl:with-param>
10399 </xsl:call-template>
10400 <xsl:if test="marc:subfield[@code='a']">
10401 <identifier type="ismn">
10402 <xsl:value-of select="marc:subfield[@code='a']"/>
10406 <xsl:for-each select="marc:datafield[@tag='024'][@ind1='4']">
10407 <xsl:call-template name="isInvalid">
10408 <xsl:with-param name="type">sici</xsl:with-param>
10409 </xsl:call-template>
10410 <identifier type="sici">
10411 <xsl:call-template name="subfieldSelect">
10412 <xsl:with-param name="codes">ab</xsl:with-param>
10413 </xsl:call-template>
10416 <xsl:for-each select="marc:datafield[@tag='022']">
10417 <xsl:call-template name="isInvalid">
10418 <xsl:with-param name="type">issn</xsl:with-param>
10419 </xsl:call-template>
10420 <identifier type="issn">
10421 <xsl:value-of select="marc:subfield[@code='a']"/>
10424 <xsl:for-each select="marc:datafield[@tag='010']">
10425 <xsl:call-template name="isInvalid">
10426 <xsl:with-param name="type">lccn</xsl:with-param>
10427 </xsl:call-template>
10428 <identifier type="lccn">
10429 <xsl:value-of select="normalize-space(marc:subfield[@code='a'])"/>
10432 <xsl:for-each select="marc:datafield[@tag='028']">
10434 <xsl:attribute name="type">
10436 <xsl:when test="@ind1='0'">issue number</xsl:when>
10437 <xsl:when test="@ind1='1'">matrix number</xsl:when>
10438 <xsl:when test="@ind1='2'">music plate</xsl:when>
10439 <xsl:when test="@ind1='3'">music publisher</xsl:when>
10440 <xsl:when test="@ind1='4'">videorecording identifier</xsl:when>
10443 <!--<xsl:call-template name="isInvalid"/>--> <!-- no $z in 028 -->
10444 <xsl:call-template name="subfieldSelect">
10445 <xsl:with-param name="codes">
10447 <xsl:when test="@ind1='0'">ba</xsl:when>
10448 <xsl:otherwise>ab</xsl:otherwise>
10451 </xsl:call-template>
10454 <xsl:for-each select="marc:datafield[@tag='037']">
10455 <identifier type="stock number">
10456 <!--<xsl:call-template name="isInvalid"/>--> <!-- no $z in 037 -->
10457 <xsl:call-template name="subfieldSelect">
10458 <xsl:with-param name="codes">ab</xsl:with-param>
10459 </xsl:call-template>
10462 <xsl:for-each select="marc:datafield[@tag='856'][marc:subfield[@code='u']]">
10464 <xsl:attribute name="type">
10466 <xsl:when test="starts-with(marc:subfield[@code='u'],'urn:doi') or starts-with(marc:subfield[@code='u'],'doi')">doi</xsl:when>
10467 <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>
10468 <xsl:otherwise>uri</xsl:otherwise>
10472 <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') ">
10473 <xsl:value-of select="concat('hdl:',substring-after(marc:subfield[@code='u'],'http://hdl.loc.gov/'))"></xsl:value-of>
10476 <xsl:value-of select="marc:subfield[@code='u']"></xsl:value-of>
10480 <xsl:if test="starts-with(marc:subfield[@code='u'],'urn:hdl') or starts-with(marc:subfield[@code='u'],'hdl')">
10481 <identifier type="hdl">
10482 <xsl:if test="marc:subfield[@code='y' or @code='3' or @code='z']">
10483 <xsl:attribute name="displayLabel">
10484 <xsl:call-template name="subfieldSelect">
10485 <xsl:with-param name="codes">y3z</xsl:with-param>
10486 </xsl:call-template>
10489 <xsl:value-of select="concat('hdl:',substring-after(marc:subfield[@code='u'],'http://hdl.loc.gov/'))"></xsl:value-of>
10493 <xsl:for-each select="marc:datafield[@tag=024][@ind1=1]">
10494 <identifier type="upc">
10495 <xsl:call-template name="isInvalid"/>
10496 <xsl:value-of select="marc:subfield[@code='a']"/>
10499 <!-- 1/04 fix added $y -->
10500 <xsl:for-each select="marc:datafield[@tag=856][marc:subfield[@code='u']]">
10503 <xsl:if test="marc:subfield[@code='y' or @code='3']">
10504 <xsl:attribute name="displayLabel">
10505 <xsl:call-template name="subfieldSelect">
10506 <xsl:with-param name="codes">y3</xsl:with-param>
10507 </xsl:call-template>
10510 <xsl:if test="marc:subfield[@code='z' ]">
10511 <xsl:attribute name="note">
10512 <xsl:call-template name="subfieldSelect">
10513 <xsl:with-param name="codes">z</xsl:with-param>
10514 </xsl:call-template>
10517 <xsl:value-of select="marc:subfield[@code='u']"></xsl:value-of>
10523 <!-- 3.2 change tmee 856z -->
10526 <xsl:for-each select="marc:datafield[@tag=852]">
10529 <xsl:call-template name="displayLabel"></xsl:call-template>
10530 <xsl:call-template name="subfieldSelect">
10531 <xsl:with-param name="codes">abje</xsl:with-param>
10532 </xsl:call-template>
10533 </physicalLocation>
10536 <xsl:for-each select="marc:datafield[@tag=506]">
10537 <accessCondition type="restrictionOnAccess">
10538 <xsl:call-template name="subfieldSelect">
10539 <xsl:with-param name="codes">abcd35</xsl:with-param>
10540 </xsl:call-template>
10543 <xsl:for-each select="marc:datafield[@tag=540]">
10544 <accessCondition type="useAndReproduction">
10545 <xsl:call-template name="subfieldSelect">
10546 <xsl:with-param name="codes">abcde35</xsl:with-param>
10547 </xsl:call-template>
10551 <xsl:for-each select="marc:datafield[@tag=040]">
10552 <recordContentSource authority="marcorg">
10553 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
10554 </recordContentSource>
10556 <xsl:for-each select="marc:controlfield[@tag=008]">
10557 <recordCreationDate encoding="marc">
10558 <xsl:value-of select="substring(.,1,6)"></xsl:value-of>
10559 </recordCreationDate>
10561 <xsl:for-each select="marc:controlfield[@tag=005]">
10562 <recordChangeDate encoding="iso8601">
10563 <xsl:value-of select="."></xsl:value-of>
10564 </recordChangeDate>
10566 <xsl:for-each select="marc:controlfield[@tag=001]">
10568 <xsl:if test="../marc:controlfield[@tag=003]">
10569 <xsl:attribute name="source">
10570 <xsl:value-of select="../marc:controlfield[@tag=003]"></xsl:value-of>
10573 <xsl:value-of select="."></xsl:value-of>
10574 </recordIdentifier>
10576 <xsl:for-each select="marc:datafield[@tag=040]/marc:subfield[@code='b']">
10577 <languageOfCataloging>
10578 <languageTerm authority="iso639-2b" type="code">
10579 <xsl:value-of select="."></xsl:value-of>
10581 </languageOfCataloging>
10585 <xsl:template name="displayForm">
10586 <xsl:for-each select="marc:subfield[@code='c']">
10588 <xsl:value-of select="."></xsl:value-of>
10592 <xsl:template name="affiliation">
10593 <xsl:for-each select="marc:subfield[@code='u']">
10595 <xsl:value-of select="."></xsl:value-of>
10599 <xsl:template name="uri">
10600 <xsl:for-each select="marc:subfield[@code='u']">
10601 <xsl:attribute name="xlink:href">
10602 <xsl:value-of select="."></xsl:value-of>
10606 <xsl:template name="role">
10607 <xsl:for-each select="marc:subfield[@code='e']">
10609 <roleTerm type="text">
10610 <xsl:value-of select="."></xsl:value-of>
10614 <xsl:for-each select="marc:subfield[@code='4']">
10616 <roleTerm authority="marcrelator" type="code">
10617 <xsl:value-of select="."></xsl:value-of>
10622 <xsl:template name="part">
10623 <xsl:variable name="partNumber">
10624 <xsl:call-template name="specialSubfieldSelect">
10625 <xsl:with-param name="axis">n</xsl:with-param>
10626 <xsl:with-param name="anyCodes">n</xsl:with-param>
10627 <xsl:with-param name="afterCodes">fgkdlmor</xsl:with-param>
10628 </xsl:call-template>
10630 <xsl:variable name="partName">
10631 <xsl:call-template name="specialSubfieldSelect">
10632 <xsl:with-param name="axis">p</xsl:with-param>
10633 <xsl:with-param name="anyCodes">p</xsl:with-param>
10634 <xsl:with-param name="afterCodes">fgkdlmor</xsl:with-param>
10635 </xsl:call-template>
10637 <xsl:if test="string-length(normalize-space($partNumber))">
10639 <xsl:call-template name="chopPunctuation">
10640 <xsl:with-param name="chopString" select="$partNumber"></xsl:with-param>
10641 </xsl:call-template>
10644 <xsl:if test="string-length(normalize-space($partName))">
10646 <xsl:call-template name="chopPunctuation">
10647 <xsl:with-param name="chopString" select="$partName"></xsl:with-param>
10648 </xsl:call-template>
10652 <xsl:template name="relatedPart">
10653 <xsl:if test="@tag=773">
10654 <xsl:for-each select="marc:subfield[@code='g']">
10657 <xsl:value-of select="."></xsl:value-of>
10661 <xsl:for-each select="marc:subfield[@code='q']">
10663 <xsl:call-template name="parsePart"></xsl:call-template>
10668 <xsl:template name="relatedPartNumName">
10669 <xsl:variable name="partNumber">
10670 <xsl:call-template name="specialSubfieldSelect">
10671 <xsl:with-param name="axis">g</xsl:with-param>
10672 <xsl:with-param name="anyCodes">g</xsl:with-param>
10673 <xsl:with-param name="afterCodes">pst</xsl:with-param>
10674 </xsl:call-template>
10676 <xsl:variable name="partName">
10677 <xsl:call-template name="specialSubfieldSelect">
10678 <xsl:with-param name="axis">p</xsl:with-param>
10679 <xsl:with-param name="anyCodes">p</xsl:with-param>
10680 <xsl:with-param name="afterCodes">fgkdlmor</xsl:with-param>
10681 </xsl:call-template>
10683 <xsl:if test="string-length(normalize-space($partNumber))">
10685 <xsl:value-of select="$partNumber"></xsl:value-of>
10688 <xsl:if test="string-length(normalize-space($partName))">
10690 <xsl:value-of select="$partName"></xsl:value-of>
10694 <xsl:template name="relatedName">
10695 <xsl:for-each select="marc:subfield[@code='a']">
10698 <xsl:value-of select="."></xsl:value-of>
10703 <xsl:template name="relatedForm">
10704 <xsl:for-each select="marc:subfield[@code='h']">
10705 <physicalDescription>
10707 <xsl:value-of select="."></xsl:value-of>
10709 </physicalDescription>
10712 <xsl:template name="relatedExtent">
10713 <xsl:for-each select="marc:subfield[@code='h']">
10714 <physicalDescription>
10716 <xsl:value-of select="."></xsl:value-of>
10718 </physicalDescription>
10721 <xsl:template name="relatedNote">
10722 <xsl:for-each select="marc:subfield[@code='n']">
10724 <xsl:value-of select="."></xsl:value-of>
10728 <xsl:template name="relatedSubject">
10729 <xsl:for-each select="marc:subfield[@code='j']">
10731 <temporal encoding="iso8601">
10732 <xsl:call-template name="chopPunctuation">
10733 <xsl:with-param name="chopString" select="."></xsl:with-param>
10734 </xsl:call-template>
10739 <xsl:template name="relatedIdentifierISSN">
10740 <xsl:for-each select="marc:subfield[@code='x']">
10741 <identifier type="issn">
10742 <xsl:value-of select="."></xsl:value-of>
10746 <xsl:template name="relatedIdentifierLocal">
10747 <xsl:for-each select="marc:subfield[@code='w']">
10748 <identifier type="local">
10749 <xsl:value-of select="."></xsl:value-of>
10753 <xsl:template name="relatedIdentifier">
10754 <xsl:for-each select="marc:subfield[@code='o']">
10756 <xsl:value-of select="."></xsl:value-of>
10760 <xsl:template name="relatedItem76X-78X">
10761 <xsl:call-template name="displayLabel"></xsl:call-template>
10762 <xsl:call-template name="relatedTitle76X-78X"></xsl:call-template>
10763 <xsl:call-template name="relatedName"></xsl:call-template>
10764 <xsl:call-template name="relatedOriginInfo"></xsl:call-template>
10765 <xsl:call-template name="relatedLanguage"></xsl:call-template>
10766 <xsl:call-template name="relatedExtent"></xsl:call-template>
10767 <xsl:call-template name="relatedNote"></xsl:call-template>
10768 <xsl:call-template name="relatedSubject"></xsl:call-template>
10769 <xsl:call-template name="relatedIdentifier"></xsl:call-template>
10770 <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
10771 <xsl:call-template name="relatedIdentifierLocal"></xsl:call-template>
10772 <xsl:call-template name="relatedPart"></xsl:call-template>
10774 <xsl:template name="subjectGeographicZ">
10776 <xsl:call-template name="chopPunctuation">
10777 <xsl:with-param name="chopString" select="."></xsl:with-param>
10778 </xsl:call-template>
10781 <xsl:template name="subjectTemporalY">
10783 <xsl:call-template name="chopPunctuation">
10784 <xsl:with-param name="chopString" select="."></xsl:with-param>
10785 </xsl:call-template>
10788 <xsl:template name="subjectTopic">
10790 <xsl:call-template name="chopPunctuation">
10791 <xsl:with-param name="chopString" select="."></xsl:with-param>
10792 </xsl:call-template>
10795 <!-- 3.2 change tmee 6xx $v genre -->
10796 <xsl:template name="subjectGenre">
10798 <xsl:call-template name="chopPunctuation">
10799 <xsl:with-param name="chopString" select="."></xsl:with-param>
10800 </xsl:call-template>
10804 <xsl:template name="nameABCDN">
10805 <xsl:for-each select="marc:subfield[@code='a']">
10807 <xsl:call-template name="chopPunctuation">
10808 <xsl:with-param name="chopString" select="."></xsl:with-param>
10809 </xsl:call-template>
10812 <xsl:for-each select="marc:subfield[@code='b']">
10814 <xsl:value-of select="."></xsl:value-of>
10817 <xsl:if test="marc:subfield[@code='c'] or marc:subfield[@code='d'] or marc:subfield[@code='n']">
10819 <xsl:call-template name="subfieldSelect">
10820 <xsl:with-param name="codes">cdn</xsl:with-param>
10821 </xsl:call-template>
10825 <xsl:template name="nameABCDQ">
10827 <xsl:call-template name="chopPunctuation">
10828 <xsl:with-param name="chopString">
10829 <xsl:call-template name="subfieldSelect">
10830 <xsl:with-param name="codes">aq</xsl:with-param>
10831 </xsl:call-template>
10833 <xsl:with-param name="punctuation">
10834 <xsl:text>:,;/ </xsl:text>
10836 </xsl:call-template>
10838 <xsl:call-template name="termsOfAddress"></xsl:call-template>
10839 <xsl:call-template name="nameDate"></xsl:call-template>
10841 <xsl:template name="nameACDEQ">
10843 <xsl:call-template name="subfieldSelect">
10844 <xsl:with-param name="codes">acdeq</xsl:with-param>
10845 </xsl:call-template>
10848 <xsl:template name="constituentOrRelatedType">
10849 <xsl:if test="@ind2=2">
10850 <xsl:attribute name="type">constituent</xsl:attribute>
10853 <xsl:template name="relatedTitle">
10854 <xsl:for-each select="marc:subfield[@code='t']">
10857 <xsl:call-template name="chopPunctuation">
10858 <xsl:with-param name="chopString">
10859 <xsl:value-of select="."></xsl:value-of>
10861 </xsl:call-template>
10866 <xsl:template name="relatedTitle76X-78X">
10867 <xsl:for-each select="marc:subfield[@code='t']">
10870 <xsl:call-template name="chopPunctuation">
10871 <xsl:with-param name="chopString">
10872 <xsl:value-of select="."></xsl:value-of>
10874 </xsl:call-template>
10876 <xsl:if test="marc:datafield[@tag!=773]and marc:subfield[@code='g']">
10877 <xsl:call-template name="relatedPartNumName"></xsl:call-template>
10881 <xsl:for-each select="marc:subfield[@code='p']">
10882 <titleInfo type="abbreviated">
10884 <xsl:call-template name="chopPunctuation">
10885 <xsl:with-param name="chopString">
10886 <xsl:value-of select="."></xsl:value-of>
10888 </xsl:call-template>
10890 <xsl:if test="marc:datafield[@tag!=773]and marc:subfield[@code='g']">
10891 <xsl:call-template name="relatedPartNumName"></xsl:call-template>
10895 <xsl:for-each select="marc:subfield[@code='s']">
10896 <titleInfo type="uniform">
10898 <xsl:call-template name="chopPunctuation">
10899 <xsl:with-param name="chopString">
10900 <xsl:value-of select="."></xsl:value-of>
10902 </xsl:call-template>
10904 <xsl:if test="marc:datafield[@tag!=773]and marc:subfield[@code='g']">
10905 <xsl:call-template name="relatedPartNumName"></xsl:call-template>
10910 <xsl:template name="relatedOriginInfo">
10911 <xsl:if test="marc:subfield[@code='b' or @code='d'] or marc:subfield[@code='f']">
10913 <xsl:if test="@tag=775">
10914 <xsl:for-each select="marc:subfield[@code='f']">
10917 <xsl:attribute name="type">code</xsl:attribute>
10918 <xsl:attribute name="authority">marcgac</xsl:attribute>
10919 <xsl:value-of select="."></xsl:value-of>
10924 <xsl:for-each select="marc:subfield[@code='d']">
10926 <xsl:value-of select="."></xsl:value-of>
10929 <xsl:for-each select="marc:subfield[@code='b']">
10931 <xsl:value-of select="."></xsl:value-of>
10937 <xsl:template name="relatedLanguage">
10938 <xsl:for-each select="marc:subfield[@code='e']">
10939 <xsl:call-template name="getLanguage">
10940 <xsl:with-param name="langString">
10941 <xsl:value-of select="."></xsl:value-of>
10943 </xsl:call-template>
10946 <xsl:template name="nameDate">
10947 <xsl:for-each select="marc:subfield[@code='d']">
10948 <namePart type="date">
10949 <xsl:call-template name="chopPunctuation">
10950 <xsl:with-param name="chopString" select="."></xsl:with-param>
10951 </xsl:call-template>
10955 <xsl:template name="subjectAuthority">
10956 <xsl:if test="@ind2!=4">
10957 <xsl:if test="@ind2!=' '">
10958 <xsl:if test="@ind2!=8">
10959 <xsl:if test="@ind2!=9">
10960 <xsl:attribute name="authority">
10962 <xsl:when test="@ind2=0">lcsh</xsl:when>
10963 <xsl:when test="@ind2=1">lcshac</xsl:when>
10964 <xsl:when test="@ind2=2">mesh</xsl:when>
10966 <xsl:when test="@ind2=3">nal</xsl:when>
10967 <xsl:when test="@ind2=5">csh</xsl:when>
10968 <xsl:when test="@ind2=6">rvm</xsl:when>
10969 <xsl:when test="@ind2=7">
10970 <xsl:value-of select="marc:subfield[@code='2']"></xsl:value-of>
10979 <xsl:template name="subjectAnyOrder">
10980 <xsl:for-each select="marc:subfield[@code='v' or @code='x' or @code='y' or @code='z']">
10982 <xsl:when test="@code='v'">
10983 <xsl:call-template name="subjectGenre"></xsl:call-template>
10985 <xsl:when test="@code='x'">
10986 <xsl:call-template name="subjectTopic"></xsl:call-template>
10988 <xsl:when test="@code='y'">
10989 <xsl:call-template name="subjectTemporalY"></xsl:call-template>
10991 <xsl:when test="@code='z'">
10992 <xsl:call-template name="subjectGeographicZ"></xsl:call-template>
10997 <xsl:template name="specialSubfieldSelect">
10998 <xsl:param name="anyCodes"></xsl:param>
10999 <xsl:param name="axis"></xsl:param>
11000 <xsl:param name="beforeCodes"></xsl:param>
11001 <xsl:param name="afterCodes"></xsl:param>
11002 <xsl:variable name="str">
11003 <xsl:for-each select="marc:subfield">
11004 <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])">
11005 <xsl:value-of select="text()"></xsl:value-of>
11006 <xsl:text> </xsl:text>
11010 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
11013 <!-- 3.2 change tmee 6xx $v genre -->
11014 <xsl:template match="marc:datafield[@tag=600]">
11016 <xsl:call-template name="subjectAuthority"></xsl:call-template>
11017 <name type="personal">
11018 <xsl:call-template name="termsOfAddress"></xsl:call-template>
11020 <xsl:call-template name="chopPunctuation">
11021 <xsl:with-param name="chopString">
11022 <xsl:call-template name="subfieldSelect">
11023 <xsl:with-param name="codes">aq</xsl:with-param>
11024 </xsl:call-template>
11026 </xsl:call-template>
11028 <xsl:call-template name="nameDate"></xsl:call-template>
11029 <xsl:call-template name="affiliation"></xsl:call-template>
11030 <xsl:call-template name="role"></xsl:call-template>
11032 <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
11035 <xsl:template match="marc:datafield[@tag=610]">
11037 <xsl:call-template name="subjectAuthority"></xsl:call-template>
11038 <name type="corporate">
11039 <xsl:for-each select="marc:subfield[@code='a']">
11041 <xsl:value-of select="."></xsl:value-of>
11044 <xsl:for-each select="marc:subfield[@code='b']">
11046 <xsl:value-of select="."></xsl:value-of>
11049 <xsl:if test="marc:subfield[@code='c' or @code='d' or @code='n' or @code='p']">
11051 <xsl:call-template name="subfieldSelect">
11052 <xsl:with-param name="codes">cdnp</xsl:with-param>
11053 </xsl:call-template>
11056 <xsl:call-template name="role"></xsl:call-template>
11058 <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
11061 <xsl:template match="marc:datafield[@tag=611]">
11063 <xsl:call-template name="subjectAuthority"></xsl:call-template>
11064 <name type="conference">
11066 <xsl:call-template name="subfieldSelect">
11067 <xsl:with-param name="codes">abcdeqnp</xsl:with-param>
11068 </xsl:call-template>
11070 <xsl:for-each select="marc:subfield[@code='4']">
11072 <roleTerm authority="marcrelator" type="code">
11073 <xsl:value-of select="."></xsl:value-of>
11078 <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
11081 <xsl:template match="marc:datafield[@tag=630]">
11083 <xsl:call-template name="subjectAuthority"></xsl:call-template>
11086 <xsl:call-template name="chopPunctuation">
11087 <xsl:with-param name="chopString">
11088 <xsl:call-template name="subfieldSelect">
11089 <xsl:with-param name="codes">adfhklor</xsl:with-param>
11090 </xsl:call-template>
11092 </xsl:call-template>
11093 <xsl:call-template name="part"></xsl:call-template>
11096 <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
11099 <xsl:template match="marc:datafield[@tag=650]">
11101 <xsl:call-template name="subjectAuthority"></xsl:call-template>
11103 <xsl:call-template name="chopPunctuation">
11104 <xsl:with-param name="chopString">
11105 <xsl:call-template name="subfieldSelect">
11106 <xsl:with-param name="codes">abcd</xsl:with-param>
11107 </xsl:call-template>
11109 </xsl:call-template>
11111 <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
11114 <xsl:template match="marc:datafield[@tag=651]">
11116 <xsl:call-template name="subjectAuthority"></xsl:call-template>
11117 <xsl:for-each select="marc:subfield[@code='a']">
11119 <xsl:call-template name="chopPunctuation">
11120 <xsl:with-param name="chopString" select="."></xsl:with-param>
11121 </xsl:call-template>
11124 <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
11127 <xsl:template match="marc:datafield[@tag=653]">
11129 <xsl:for-each select="marc:subfield[@code='a']">
11131 <xsl:value-of select="."></xsl:value-of>
11136 <xsl:template match="marc:datafield[@tag=656]">
11138 <xsl:if test="marc:subfield[@code=2]">
11139 <xsl:attribute name="authority">
11140 <xsl:value-of select="marc:subfield[@code=2]"></xsl:value-of>
11144 <xsl:call-template name="chopPunctuation">
11145 <xsl:with-param name="chopString">
11146 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
11148 </xsl:call-template>
11152 <xsl:template name="termsOfAddress">
11153 <xsl:if test="marc:subfield[@code='b' or @code='c']">
11154 <namePart type="termsOfAddress">
11155 <xsl:call-template name="chopPunctuation">
11156 <xsl:with-param name="chopString">
11157 <xsl:call-template name="subfieldSelect">
11158 <xsl:with-param name="codes">bc</xsl:with-param>
11159 </xsl:call-template>
11161 </xsl:call-template>
11165 <xsl:template name="displayLabel">
11166 <xsl:if test="marc:subfield[@code='i']">
11167 <xsl:attribute name="displayLabel">
11168 <xsl:value-of select="marc:subfield[@code='i']"></xsl:value-of>
11171 <xsl:if test="marc:subfield[@code='3']">
11172 <xsl:attribute name="displayLabel">
11173 <xsl:value-of select="marc:subfield[@code='3']"></xsl:value-of>
11177 <xsl:template name="isInvalid">
11178 <xsl:param name="type"/>
11179 <xsl:if test="marc:subfield[@code='z'] or marc:subfield[@code='y']">
11181 <xsl:attribute name="type">
11182 <xsl:value-of select="$type"/>
11184 <xsl:attribute name="invalid">
11185 <xsl:text>yes</xsl:text>
11187 <xsl:if test="marc:subfield[@code='z']">
11188 <xsl:value-of select="marc:subfield[@code='z']"/>
11190 <xsl:if test="marc:subfield[@code='y']">
11191 <xsl:value-of select="marc:subfield[@code='y']"/>
11196 <xsl:template name="subtitle">
11197 <xsl:if test="marc:subfield[@code='b']">
11199 <xsl:call-template name="chopPunctuation">
11200 <xsl:with-param name="chopString">
11201 <xsl:value-of select="marc:subfield[@code='b']"/>
11202 <!--<xsl:call-template name="subfieldSelect">
11203 <xsl:with-param name="codes">b</xsl:with-param>
11204 </xsl:call-template>-->
11206 </xsl:call-template>
11210 <xsl:template name="script">
11211 <xsl:param name="scriptCode"></xsl:param>
11212 <xsl:attribute name="script">
11214 <xsl:when test="$scriptCode='(3'">Arabic</xsl:when>
11215 <xsl:when test="$scriptCode='(B'">Latin</xsl:when>
11216 <xsl:when test="$scriptCode='$1'">Chinese, Japanese, Korean</xsl:when>
11217 <xsl:when test="$scriptCode='(N'">Cyrillic</xsl:when>
11218 <xsl:when test="$scriptCode='(2'">Hebrew</xsl:when>
11219 <xsl:when test="$scriptCode='(S'">Greek</xsl:when>
11223 <xsl:template name="parsePart">
11224 <!-- assumes 773$q= 1:2:3<4
11225 with up to 3 levels and one optional start page
11227 <xsl:variable name="level1">
11229 <xsl:when test="contains(text(),':')">
11231 <xsl:value-of select="substring-before(text(),':')"></xsl:value-of>
11233 <xsl:when test="not(contains(text(),':'))">
11235 <xsl:if test="contains(text(),'<')">
11237 <xsl:value-of select="substring-before(text(),'<')"></xsl:value-of>
11239 <xsl:if test="not(contains(text(),'<'))">
11241 <xsl:value-of select="text()"></xsl:value-of>
11246 <xsl:variable name="sici2">
11248 <xsl:when test="starts-with(substring-after(text(),$level1),':')">
11249 <xsl:value-of select="substring(substring-after(text(),$level1),2)"></xsl:value-of>
11252 <xsl:value-of select="substring-after(text(),$level1)"></xsl:value-of>
11256 <xsl:variable name="level2">
11258 <xsl:when test="contains($sici2,':')">
11260 <xsl:value-of select="substring-before($sici2,':')"></xsl:value-of>
11262 <xsl:when test="contains($sici2,'<')">
11264 <xsl:value-of select="substring-before($sici2,'<')"></xsl:value-of>
11267 <xsl:value-of select="$sici2"></xsl:value-of>
11272 <xsl:variable name="sici3">
11274 <xsl:when test="starts-with(substring-after($sici2,$level2),':')">
11275 <xsl:value-of select="substring(substring-after($sici2,$level2),2)"></xsl:value-of>
11278 <xsl:value-of select="substring-after($sici2,$level2)"></xsl:value-of>
11282 <xsl:variable name="level3">
11284 <xsl:when test="contains($sici3,'<')">
11286 <xsl:value-of select="substring-before($sici3,'<')"></xsl:value-of>
11289 <xsl:value-of select="$sici3"></xsl:value-of>
11294 <xsl:variable name="page">
11295 <xsl:if test="contains(text(),'<')">
11296 <xsl:value-of select="substring-after(text(),'<')"></xsl:value-of>
11299 <xsl:if test="$level1">
11302 <xsl:value-of select="$level1"></xsl:value-of>
11306 <xsl:if test="$level2">
11309 <xsl:value-of select="$level2"></xsl:value-of>
11313 <xsl:if test="$level3">
11316 <xsl:value-of select="$level3"></xsl:value-of>
11320 <xsl:if test="$page">
11321 <extent unit="page">
11323 <xsl:value-of select="$page"></xsl:value-of>
11328 <xsl:template name="getLanguage">
11329 <xsl:param name="langString"></xsl:param>
11330 <xsl:param name="controlField008-35-37"></xsl:param>
11331 <xsl:variable name="length" select="string-length($langString)"></xsl:variable>
11333 <xsl:when test="$length=0"></xsl:when>
11334 <xsl:when test="$controlField008-35-37=substring($langString,1,3)">
11335 <xsl:call-template name="getLanguage">
11336 <xsl:with-param name="langString" select="substring($langString,4,$length)"></xsl:with-param>
11337 <xsl:with-param name="controlField008-35-37" select="$controlField008-35-37"></xsl:with-param>
11338 </xsl:call-template>
11342 <languageTerm authority="iso639-2b" type="code">
11343 <xsl:value-of select="substring($langString,1,3)"></xsl:value-of>
11346 <xsl:call-template name="getLanguage">
11347 <xsl:with-param name="langString" select="substring($langString,4,$length)"></xsl:with-param>
11348 <xsl:with-param name="controlField008-35-37" select="$controlField008-35-37"></xsl:with-param>
11349 </xsl:call-template>
11353 <xsl:template name="isoLanguage">
11354 <xsl:param name="currentLanguage"></xsl:param>
11355 <xsl:param name="usedLanguages"></xsl:param>
11356 <xsl:param name="remainingLanguages"></xsl:param>
11358 <xsl:when test="string-length($currentLanguage)=0"></xsl:when>
11359 <xsl:when test="not(contains($usedLanguages, $currentLanguage))">
11361 <xsl:if test="@code!='a'">
11362 <xsl:attribute name="objectPart">
11364 <xsl:when test="@code='b'">summary or subtitle</xsl:when>
11365 <xsl:when test="@code='d'">sung or spoken text</xsl:when>
11366 <xsl:when test="@code='e'">libretto</xsl:when>
11367 <xsl:when test="@code='f'">table of contents</xsl:when>
11368 <xsl:when test="@code='g'">accompanying material</xsl:when>
11369 <xsl:when test="@code='h'">translation</xsl:when>
11373 <languageTerm authority="iso639-2b" type="code">
11374 <xsl:value-of select="$currentLanguage"></xsl:value-of>
11377 <xsl:call-template name="isoLanguage">
11378 <xsl:with-param name="currentLanguage">
11379 <xsl:value-of select="substring($remainingLanguages,1,3)"></xsl:value-of>
11381 <xsl:with-param name="usedLanguages">
11382 <xsl:value-of select="concat($usedLanguages,$currentLanguage)"></xsl:value-of>
11384 <xsl:with-param name="remainingLanguages">
11385 <xsl:value-of select="substring($remainingLanguages,4,string-length($remainingLanguages))"></xsl:value-of>
11387 </xsl:call-template>
11390 <xsl:call-template name="isoLanguage">
11391 <xsl:with-param name="currentLanguage">
11392 <xsl:value-of select="substring($remainingLanguages,1,3)"></xsl:value-of>
11394 <xsl:with-param name="usedLanguages">
11395 <xsl:value-of select="concat($usedLanguages,$currentLanguage)"></xsl:value-of>
11397 <xsl:with-param name="remainingLanguages">
11398 <xsl:value-of select="substring($remainingLanguages,4,string-length($remainingLanguages))"></xsl:value-of>
11400 </xsl:call-template>
11404 <xsl:template name="chopBrackets">
11405 <xsl:param name="chopString"></xsl:param>
11406 <xsl:variable name="string">
11407 <xsl:call-template name="chopPunctuation">
11408 <xsl:with-param name="chopString" select="$chopString"></xsl:with-param>
11409 </xsl:call-template>
11411 <xsl:if test="substring($string, 1,1)='['">
11412 <xsl:value-of select="substring($string,2, string-length($string)-2)"></xsl:value-of>
11414 <xsl:if test="substring($string, 1,1)!='['">
11415 <xsl:value-of select="$string"></xsl:value-of>
11418 <xsl:template name="rfcLanguages">
11419 <xsl:param name="nodeNum"></xsl:param>
11420 <xsl:param name="usedLanguages"></xsl:param>
11421 <xsl:param name="controlField008-35-37"></xsl:param>
11422 <xsl:variable name="currentLanguage" select="."></xsl:variable>
11424 <xsl:when test="not($currentLanguage)"></xsl:when>
11425 <xsl:when test="$currentLanguage!=$controlField008-35-37 and $currentLanguage!='rfc3066'">
11426 <xsl:if test="not(contains($usedLanguages,$currentLanguage))">
11428 <xsl:if test="@code!='a'">
11429 <xsl:attribute name="objectPart">
11431 <xsl:when test="@code='b'">summary or subtitle</xsl:when>
11432 <xsl:when test="@code='d'">sung or spoken text</xsl:when>
11433 <xsl:when test="@code='e'">libretto</xsl:when>
11434 <xsl:when test="@code='f'">table of contents</xsl:when>
11435 <xsl:when test="@code='g'">accompanying material</xsl:when>
11436 <xsl:when test="@code='h'">translation</xsl:when>
11440 <languageTerm authority="rfc3066" type="code">
11441 <xsl:value-of select="$currentLanguage"/>
11450 <xsl:template name="datafield">
11451 <xsl:param name="tag"/>
11452 <xsl:param name="ind1"><xsl:text> </xsl:text></xsl:param>
11453 <xsl:param name="ind2"><xsl:text> </xsl:text></xsl:param>
11454 <xsl:param name="subfields"/>
11455 <xsl:element name="marc:datafield">
11456 <xsl:attribute name="tag">
11457 <xsl:value-of select="$tag"/>
11459 <xsl:attribute name="ind1">
11460 <xsl:value-of select="$ind1"/>
11462 <xsl:attribute name="ind2">
11463 <xsl:value-of select="$ind2"/>
11465 <xsl:copy-of select="$subfields"/>
11469 <xsl:template name="subfieldSelect">
11470 <xsl:param name="codes"/>
11471 <xsl:param name="delimeter"><xsl:text> </xsl:text></xsl:param>
11472 <xsl:variable name="str">
11473 <xsl:for-each select="marc:subfield">
11474 <xsl:if test="contains($codes, @code)">
11475 <xsl:value-of select="text()"/><xsl:value-of select="$delimeter"/>
11479 <xsl:value-of select="substring($str,1,string-length($str)-string-length($delimeter))"/>
11482 <xsl:template name="buildSpaces">
11483 <xsl:param name="spaces"/>
11484 <xsl:param name="char"><xsl:text> </xsl:text></xsl:param>
11485 <xsl:if test="$spaces>0">
11486 <xsl:value-of select="$char"/>
11487 <xsl:call-template name="buildSpaces">
11488 <xsl:with-param name="spaces" select="$spaces - 1"/>
11489 <xsl:with-param name="char" select="$char"/>
11490 </xsl:call-template>
11494 <xsl:template name="chopPunctuation">
11495 <xsl:param name="chopString"/>
11496 <xsl:param name="punctuation"><xsl:text>.:,;/ </xsl:text></xsl:param>
11497 <xsl:variable name="length" select="string-length($chopString)"/>
11499 <xsl:when test="$length=0"/>
11500 <xsl:when test="contains($punctuation, substring($chopString,$length,1))">
11501 <xsl:call-template name="chopPunctuation">
11502 <xsl:with-param name="chopString" select="substring($chopString,1,$length - 1)"/>
11503 <xsl:with-param name="punctuation" select="$punctuation"/>
11504 </xsl:call-template>
11506 <xsl:when test="not($chopString)"/>
11507 <xsl:otherwise><xsl:value-of select="$chopString"/></xsl:otherwise>
11511 <xsl:template name="chopPunctuationFront">
11512 <xsl:param name="chopString"/>
11513 <xsl:variable name="length" select="string-length($chopString)"/>
11515 <xsl:when test="$length=0"/>
11516 <xsl:when test="contains('.:,;/[ ', substring($chopString,1,1))">
11517 <xsl:call-template name="chopPunctuationFront">
11518 <xsl:with-param name="chopString" select="substring($chopString,2,$length - 1)"/>
11519 </xsl:call-template>
11521 <xsl:when test="not($chopString)"/>
11522 <xsl:otherwise><xsl:value-of select="$chopString"/></xsl:otherwise>
11525 </xsl:stylesheet>$$ WHERE name = 'mods32';
11527 -- Currently, the only difference from naco_normalize is that search_normalize
11528 -- turns apostrophes into spaces, while naco_normalize collapses them.
11529 CREATE OR REPLACE FUNCTION public.search_normalize( TEXT, TEXT ) RETURNS TEXT AS $func$
11532 use Unicode::Normalize;
11535 my $str = decode_utf8(shift);
11538 # Apply NACO normalization to input string; based on
11539 # http://www.loc.gov/catdir/pcc/naco/SCA_PccNormalization_Final_revised.pdf
11541 # Note that unlike a strict reading of the NACO normalization rules,
11542 # output is returned as lowercase instead of uppercase for compatibility
11543 # with previous versions of the Evergreen naco_normalize routine.
11545 # Convert to upper-case first; even though final output will be lowercase, doing this will
11546 # ensure that the German eszett (ß) and certain ligatures (ff, fi, ffl, etc.) will be handled correctly.
11547 # If there are any bugs in Perl's implementation of upcasing, they will be passed through here.
11550 # remove non-filing strings
11551 $str =~ s/\x{0098}.*?\x{009C}//g;
11555 # additional substitutions - 3.6.
11556 $str =~ s/\x{00C6}/AE/g;
11557 $str =~ s/\x{00DE}/TH/g;
11558 $str =~ s/\x{0152}/OE/g;
11559 $str =~ tr/\x{0110}\x{00D0}\x{00D8}\x{0141}\x{2113}\x{02BB}\x{02BC}][/DDOLl/d;
11561 # transformations based on Unicode category codes
11562 $str =~ s/[\p{Cc}\p{Cf}\p{Co}\p{Cs}\p{Lm}\p{Mc}\p{Me}\p{Mn}]//g;
11564 if ($sf && $sf =~ /^a/o) {
11565 my $commapos = index($str, ',');
11566 if ($commapos > -1) {
11567 if ($commapos != length($str) - 1) {
11568 $str =~ s/,/\x07/; # preserve first comma
11573 # since we've stripped out the control characters, we can now
11574 # use a few as placeholders temporarily
11575 $str =~ tr/+&@\x{266D}\x{266F}#/\x01\x02\x03\x04\x05\x06/;
11576 $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;
11577 $str =~ tr/\x01\x02\x03\x04\x05\x06\x07/+&@\x{266D}\x{266F}#,/;
11580 $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/;
11582 # intentionally skipping step 8 of the NACO algorithm; if the string
11583 # gets normalized away, that's fine.
11585 # leading and trailing spaces
11591 $func$ LANGUAGE 'plperlu' STRICT IMMUTABLE;
11593 CREATE OR REPLACE FUNCTION public.search_normalize_keep_comma( TEXT ) RETURNS TEXT AS $func$
11594 SELECT public.search_normalize($1,'a');
11595 $func$ LANGUAGE SQL STRICT IMMUTABLE;
11597 CREATE OR REPLACE FUNCTION public.search_normalize( TEXT ) RETURNS TEXT AS $func$
11598 SELECT public.search_normalize($1,'');
11599 $func$ LANGUAGE 'sql' STRICT IMMUTABLE;
11601 INSERT INTO config.index_normalizer (name, description, func, param_count) VALUES (
11602 'Search Normalize',
11603 'Apply search normalization rules to the extracted text. A less extreme version of NACO normalization.',
11604 'search_normalize',
11608 UPDATE config.metabib_field_index_norm_map
11610 SELECT id FROM config.index_normalizer WHERE func = 'search_normalize'
11613 SELECT id FROM config.index_normalizer WHERE func = 'naco_normalize'
11618 -- This could take a long time if you have a very non-English bib database
11619 -- Run it outside of a transaction to avoid lock escalation
11620 SELECT metabib.reingest_metabib_field_entries(record)
11621 FROM metabib.full_rec
11624 AND value LIKE '%''%'
11626 -- Evergreen DB patch 0673.data.acq-cancel-reason-cleanup.sql
11629 -- check whether patch can be applied
11630 SELECT evergreen.upgrade_deps_block_check('0673', :eg_version);
11635 -- any entries with id >= 2000 were added locally.
11638 -- these cancel_reason's are actively used by the system
11639 AND id NOT IN (1, 2, 3, 1002, 1003, 1004, 1005, 1010, 1024, 1211, 1221, 1246, 1283)
11641 -- don't delete any cancel_reason's that may be in use locally
11642 AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.user_request WHERE cancel_reason IS NOT NULL)
11643 AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.purchase_order WHERE cancel_reason IS NOT NULL)
11644 AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.lineitem WHERE cancel_reason IS NOT NULL)
11645 AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.lineitem_detail WHERE cancel_reason IS NOT NULL)
11646 AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.acq_lineitem_history WHERE cancel_reason IS NOT NULL)
11647 AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.acq_purchase_order_history WHERE cancel_reason IS NOT NULL);
11650 SELECT evergreen.upgrade_deps_block_check('0674', :eg_version);
11652 ALTER TABLE config.copy_status
11653 ADD COLUMN restrict_copy_delete BOOL NOT NULL DEFAULT FALSE;
11655 UPDATE config.copy_status
11656 SET restrict_copy_delete = TRUE
11657 WHERE id IN (1,3,6,8);
11659 INSERT INTO permission.perm_list (id, code, description) VALUES (
11661 'COPY_DELETE_WARNING.override',
11662 'Allow a user to override warnings about deleting copies in problematic situations.'
11666 SELECT evergreen.upgrade_deps_block_check('0675', :eg_version);
11668 -- set expected row count to low value to avoid problem
11669 -- where use of this function by the circ tagging feature
11670 -- results in full scans of asset.call_number
11671 CREATE OR REPLACE FUNCTION action.usr_visible_circ_copies( INTEGER ) RETURNS SETOF BIGINT AS $$
11672 SELECT DISTINCT(target_copy) FROM action.usr_visible_circs($1)
11673 $$ LANGUAGE SQL ROWS 10;
11676 SELECT evergreen.upgrade_deps_block_check('0676', :eg_version);
11678 INSERT INTO config.global_flag (name, label, enabled, value) VALUES (
11679 'opac.use_autosuggest',
11680 '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)',
11685 CREATE TABLE metabib.browse_entry (
11686 id BIGSERIAL PRIMARY KEY,
11688 index_vector tsvector
11690 --Skip this, will be created differently later
11691 --CREATE INDEX metabib_browse_entry_index_vector_idx ON metabib.browse_entry USING GIST (index_vector);
11692 CREATE TRIGGER metabib_browse_entry_fti_trigger
11693 BEFORE INSERT OR UPDATE ON metabib.browse_entry
11694 FOR EACH ROW EXECUTE PROCEDURE oils_tsearch2('keyword');
11697 CREATE TABLE metabib.browse_entry_def_map (
11698 id BIGSERIAL PRIMARY KEY,
11699 entry BIGINT REFERENCES metabib.browse_entry (id),
11700 def INT REFERENCES config.metabib_field (id),
11701 source BIGINT REFERENCES biblio.record_entry (id)
11704 ALTER TABLE config.metabib_field ADD COLUMN browse_field BOOLEAN DEFAULT TRUE NOT NULL;
11705 ALTER TABLE config.metabib_field ADD COLUMN browse_xpath TEXT;
11707 ALTER TABLE config.metabib_class ADD COLUMN bouyant BOOLEAN DEFAULT FALSE NOT NULL;
11708 ALTER TABLE config.metabib_class ADD COLUMN restrict BOOLEAN DEFAULT FALSE NOT NULL;
11709 ALTER TABLE config.metabib_field ADD COLUMN restrict BOOLEAN DEFAULT FALSE NOT NULL;
11711 -- one good exception to default true:
11712 UPDATE config.metabib_field
11713 SET browse_field = FALSE
11714 WHERE (field_class = 'keyword' AND name = 'keyword') OR
11715 (field_class = 'subject' AND name = 'complete');
11717 -- AFTER UPDATE OR INSERT trigger for biblio.record_entry
11718 -- We're only touching it here to add a DELETE statement to the IF NEW.deleted
11721 CREATE OR REPLACE FUNCTION biblio.indexing_ingest_or_delete () RETURNS TRIGGER AS $func$
11723 transformed_xml TEXT;
11726 xfrm config.xml_transform%ROWTYPE;
11728 new_attrs HSTORE := ''::HSTORE;
11729 attr_def config.record_attr_definition%ROWTYPE;
11732 IF NEW.deleted IS TRUE THEN -- If this bib is deleted
11733 DELETE FROM metabib.metarecord_source_map WHERE source = NEW.id; -- Rid ourselves of the search-estimate-killing linkage
11734 DELETE FROM metabib.record_attr WHERE id = NEW.id; -- Kill the attrs hash, useless on deleted records
11735 DELETE FROM authority.bib_linking WHERE bib = NEW.id; -- Avoid updating fields in bibs that are no longer visible
11736 DELETE FROM biblio.peer_bib_copy_map WHERE peer_record = NEW.id; -- Separate any multi-homed items
11737 DELETE FROM metabib.browse_entry_def_map WHERE source = NEW.id; -- Don't auto-suggest deleted bibs
11738 RETURN NEW; -- and we're done
11741 IF TG_OP = 'UPDATE' THEN -- re-ingest?
11742 PERFORM * FROM config.internal_flag WHERE name = 'ingest.reingest.force_on_same_marc' AND enabled;
11744 IF NOT FOUND AND OLD.marc = NEW.marc THEN -- don't do anything if the MARC didn't change
11749 -- Record authority linking
11750 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_linking' AND enabled;
11752 PERFORM biblio.map_authority_linking( NEW.id, NEW.marc );
11755 -- Flatten and insert the mfr data
11756 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_metabib_full_rec' AND enabled;
11758 PERFORM metabib.reingest_metabib_full_rec(NEW.id);
11760 -- Now we pull out attribute data, which is dependent on the mfr for all but XPath-based fields
11761 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_metabib_rec_descriptor' AND enabled;
11763 FOR attr_def IN SELECT * FROM config.record_attr_definition ORDER BY format LOOP
11765 IF attr_def.tag IS NOT NULL THEN -- tag (and optional subfield list) selection
11766 SELECT ARRAY_TO_STRING(ARRAY_ACCUM(value), COALESCE(attr_def.joiner,' ')) INTO attr_value
11767 FROM (SELECT * FROM metabib.full_rec ORDER BY tag, subfield) AS x
11768 WHERE record = NEW.id
11769 AND tag LIKE attr_def.tag
11771 WHEN attr_def.sf_list IS NOT NULL
11772 THEN POSITION(subfield IN attr_def.sf_list) > 0
11779 ELSIF attr_def.fixed_field IS NOT NULL THEN -- a named fixed field, see config.marc21_ff_pos_map.fixed_field
11780 attr_value := biblio.marc21_extract_fixed_field(NEW.id, attr_def.fixed_field);
11782 ELSIF attr_def.xpath IS NOT NULL THEN -- and xpath expression
11784 SELECT INTO xfrm * FROM config.xml_transform WHERE name = attr_def.format;
11786 -- See if we can skip the XSLT ... it's expensive
11787 IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
11788 -- Can't skip the transform
11789 IF xfrm.xslt <> '---' THEN
11790 transformed_xml := oils_xslt_process(NEW.marc,xfrm.xslt);
11792 transformed_xml := NEW.marc;
11795 prev_xfrm := xfrm.name;
11798 IF xfrm.name IS NULL THEN
11799 -- just grab the marcxml (empty) transform
11800 SELECT INTO xfrm * FROM config.xml_transform WHERE xslt = '---' LIMIT 1;
11801 prev_xfrm := xfrm.name;
11804 attr_value := oils_xpath_string(attr_def.xpath, transformed_xml, COALESCE(attr_def.joiner,' '), ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]);
11806 ELSIF attr_def.phys_char_sf IS NOT NULL THEN -- a named Physical Characteristic, see config.marc21_physical_characteristic_*_map
11807 SELECT m.value INTO attr_value
11808 FROM biblio.marc21_physical_characteristics(NEW.id) v
11809 JOIN config.marc21_physical_characteristic_value_map m ON (m.id = v.value)
11810 WHERE v.subfield = attr_def.phys_char_sf
11811 LIMIT 1; -- Just in case ...
11815 -- apply index normalizers to attr_value
11817 SELECT n.func AS func,
11818 n.param_count AS param_count,
11820 FROM config.index_normalizer n
11821 JOIN config.record_attr_index_norm_map m ON (m.norm = n.id)
11822 WHERE attr = attr_def.name
11823 ORDER BY m.pos LOOP
11824 EXECUTE 'SELECT ' || normalizer.func || '(' ||
11825 COALESCE( quote_literal( attr_value ), 'NULL' ) ||
11827 WHEN normalizer.param_count > 0
11828 THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
11831 ')' INTO attr_value;
11835 -- Add the new value to the hstore
11836 new_attrs := new_attrs || hstore( attr_def.name, attr_value );
11840 IF TG_OP = 'INSERT' OR OLD.deleted THEN -- initial insert OR revivication
11841 INSERT INTO metabib.record_attr (id, attrs) VALUES (NEW.id, new_attrs);
11843 UPDATE metabib.record_attr SET attrs = new_attrs WHERE id = NEW.id;
11849 -- Gather and insert the field entry data
11850 PERFORM metabib.reingest_metabib_field_entries(NEW.id);
11852 -- Located URI magic
11853 IF TG_OP = 'INSERT' THEN
11854 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_located_uri' AND enabled;
11856 PERFORM biblio.extract_located_uris( NEW.id, NEW.marc, NEW.editor );
11859 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_located_uri' AND enabled;
11861 PERFORM biblio.extract_located_uris( NEW.id, NEW.marc, NEW.editor );
11865 -- (re)map metarecord-bib linking
11866 IF TG_OP = 'INSERT' THEN -- if not deleted and performing an insert, check for the flag
11867 PERFORM * FROM config.internal_flag WHERE name = 'ingest.metarecord_mapping.skip_on_insert' AND enabled;
11869 PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint );
11871 ELSE -- we're doing an update, and we're not deleted, remap
11872 PERFORM * FROM config.internal_flag WHERE name = 'ingest.metarecord_mapping.skip_on_update' AND enabled;
11874 PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint );
11880 $func$ LANGUAGE PLPGSQL;
11882 CREATE OR REPLACE FUNCTION metabib.browse_normalize(facet_text TEXT, mapped_field INT) RETURNS TEXT AS $$
11888 SELECT n.func AS func,
11889 n.param_count AS param_count,
11891 FROM config.index_normalizer n
11892 JOIN config.metabib_field_index_norm_map m ON (m.norm = n.id)
11893 WHERE m.field = mapped_field AND m.pos < 0
11894 ORDER BY m.pos LOOP
11896 EXECUTE 'SELECT ' || normalizer.func || '(' ||
11897 quote_literal( facet_text ) ||
11899 WHEN normalizer.param_count > 0
11900 THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
11903 ')' INTO facet_text;
11910 $$ LANGUAGE PLPGSQL;
11912 DROP FUNCTION biblio.extract_metabib_field_entry(bigint, text);
11913 DROP FUNCTION biblio.extract_metabib_field_entry(bigint);
11915 DROP TYPE metabib.field_entry_template;
11916 CREATE TYPE metabib.field_entry_template AS (
11927 CREATE OR REPLACE FUNCTION biblio.extract_metabib_field_entry ( rid BIGINT, default_joiner TEXT ) RETURNS SETOF metabib.field_entry_template AS $func$
11929 bib biblio.record_entry%ROWTYPE;
11930 idx config.metabib_field%ROWTYPE;
11931 xfrm config.xml_transform%ROWTYPE;
11933 transformed_xml TEXT;
11935 xml_node_list TEXT[];
11940 joiner TEXT := default_joiner; -- XXX will index defs supply a joiner?
11941 output_row metabib.field_entry_template%ROWTYPE;
11945 SELECT INTO bib * FROM biblio.record_entry WHERE id = rid;
11947 -- Loop over the indexing entries
11948 FOR idx IN SELECT * FROM config.metabib_field ORDER BY format LOOP
11950 SELECT INTO xfrm * from config.xml_transform WHERE name = idx.format;
11952 -- See if we can skip the XSLT ... it's expensive
11953 IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
11954 -- Can't skip the transform
11955 IF xfrm.xslt <> '---' THEN
11956 transformed_xml := oils_xslt_process(bib.marc,xfrm.xslt);
11958 transformed_xml := bib.marc;
11961 prev_xfrm := xfrm.name;
11964 xml_node_list := oils_xpath( idx.xpath, transformed_xml, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
11967 FOR xml_node IN SELECT x FROM unnest(xml_node_list) AS x LOOP
11968 CONTINUE WHEN xml_node !~ E'^\\s*<';
11970 curr_text := ARRAY_TO_STRING(
11971 oils_xpath( '//text()',
11972 REGEXP_REPLACE( -- This escapes all &s not followed by "amp;". Data ise returned from oils_xpath (above) in UTF-8, not entity encoded
11973 REGEXP_REPLACE( -- This escapes embeded <s
11975 $re$(>[^<]+)(<)([^>]+<)$re$,
11987 CONTINUE WHEN curr_text IS NULL OR curr_text = '';
11989 IF raw_text IS NOT NULL THEN
11990 raw_text := raw_text || joiner;
11993 raw_text := COALESCE(raw_text,'') || curr_text;
11995 -- autosuggest/metabib.browse_entry
11996 IF idx.browse_field THEN
11998 IF idx.browse_xpath IS NOT NULL AND idx.browse_xpath <> '' THEN
11999 browse_text := oils_xpath_string( idx.browse_xpath, xml_node, joiner, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
12001 browse_text := curr_text;
12004 output_row.field_class = idx.field_class;
12005 output_row.field = idx.id;
12006 output_row.source = rid;
12007 output_row.value = BTRIM(REGEXP_REPLACE(browse_text, E'\\s+', ' ', 'g'));
12009 output_row.browse_field = TRUE;
12010 RETURN NEXT output_row;
12011 output_row.browse_field = FALSE;
12014 -- insert raw node text for faceting
12015 IF idx.facet_field THEN
12017 IF idx.facet_xpath IS NOT NULL AND idx.facet_xpath <> '' THEN
12018 facet_text := oils_xpath_string( idx.facet_xpath, xml_node, joiner, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
12020 facet_text := curr_text;
12023 output_row.field_class = idx.field_class;
12024 output_row.field = -1 * idx.id;
12025 output_row.source = rid;
12026 output_row.value = BTRIM(REGEXP_REPLACE(facet_text, E'\\s+', ' ', 'g'));
12028 output_row.facet_field = TRUE;
12029 RETURN NEXT output_row;
12030 output_row.facet_field = FALSE;
12035 CONTINUE WHEN raw_text IS NULL OR raw_text = '';
12037 -- insert combined node text for searching
12038 IF idx.search_field THEN
12039 output_row.field_class = idx.field_class;
12040 output_row.field = idx.id;
12041 output_row.source = rid;
12042 output_row.value = BTRIM(REGEXP_REPLACE(raw_text, E'\\s+', ' ', 'g'));
12044 output_row.search_field = TRUE;
12045 RETURN NEXT output_row;
12051 $func$ LANGUAGE PLPGSQL;
12053 -- default to a space joiner
12054 CREATE OR REPLACE FUNCTION biblio.extract_metabib_field_entry ( BIGINT ) RETURNS SETOF metabib.field_entry_template AS $func$
12055 SELECT * FROM biblio.extract_metabib_field_entry($1, ' ');
12056 $func$ LANGUAGE SQL;
12059 CREATE OR REPLACE FUNCTION metabib.reingest_metabib_field_entries( bib_id BIGINT ) RETURNS VOID AS $func$
12062 ind_data metabib.field_entry_template%ROWTYPE;
12063 mbe_row metabib.browse_entry%ROWTYPE;
12066 PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled;
12068 FOR fclass IN SELECT * FROM config.metabib_class LOOP
12069 -- RAISE NOTICE 'Emptying out %', fclass.name;
12070 EXECUTE $$DELETE FROM metabib.$$ || fclass.name || $$_field_entry WHERE source = $$ || bib_id;
12072 DELETE FROM metabib.facet_entry WHERE source = bib_id;
12073 DELETE FROM metabib.browse_entry_def_map WHERE source = bib_id;
12076 FOR ind_data IN SELECT * FROM biblio.extract_metabib_field_entry( bib_id ) LOOP
12077 IF ind_data.field < 0 THEN
12078 ind_data.field = -1 * ind_data.field;
12081 IF ind_data.facet_field THEN
12082 INSERT INTO metabib.facet_entry (field, source, value)
12083 VALUES (ind_data.field, ind_data.source, ind_data.value);
12086 IF ind_data.browse_field THEN
12087 SELECT INTO mbe_row * FROM metabib.browse_entry WHERE value = ind_data.value;
12089 mbe_id := mbe_row.id;
12091 INSERT INTO metabib.browse_entry (value) VALUES
12092 (metabib.browse_normalize(ind_data.value, ind_data.field));
12093 mbe_id := CURRVAL('metabib.browse_entry_id_seq'::REGCLASS);
12096 INSERT INTO metabib.browse_entry_def_map (entry, def, source)
12097 VALUES (mbe_id, ind_data.field, ind_data.source);
12100 IF ind_data.search_field THEN
12102 INSERT INTO metabib.$$ || ind_data.field_class || $$_field_entry (field, source, value)
12104 quote_literal(ind_data.field) || $$, $$ ||
12105 quote_literal(ind_data.source) || $$, $$ ||
12106 quote_literal(ind_data.value) ||
12114 $func$ LANGUAGE PLPGSQL;
12116 -- This mimics a specific part of QueryParser, turning the first part of a
12117 -- classed search (search_class) into a set of classes and possibly fields.
12118 -- search_class might look like "author" or "title|proper" or "ti|uniform"
12119 -- or "au" or "au|corporate|personal" or anything like that, where the first
12120 -- element of the list you get by separating on the "|" character is either
12121 -- a registered class (config.metabib_class) or an alias
12122 -- (config.metabib_search_alias), and the rest of any such elements are
12123 -- fields (config.metabib_field).
12125 FUNCTION metabib.search_class_to_registered_components(search_class TEXT)
12126 RETURNS SETOF RECORD AS $func$
12128 search_parts TEXT[];
12130 search_part_count INTEGER;
12132 registered_class config.metabib_class%ROWTYPE;
12133 registered_alias config.metabib_search_alias%ROWTYPE;
12134 registered_field config.metabib_field%ROWTYPE;
12136 search_parts := REGEXP_SPLIT_TO_ARRAY(search_class, E'\\|');
12138 search_part_count := ARRAY_LENGTH(search_parts, 1);
12139 IF search_part_count = 0 THEN
12142 SELECT INTO registered_class
12143 * FROM config.metabib_class WHERE name = search_parts[1];
12145 IF search_part_count < 2 THEN -- all fields
12146 rec := (registered_class.name, NULL::INTEGER);
12150 FOR field_name IN SELECT *
12151 FROM UNNEST(search_parts[2:search_part_count]) LOOP
12152 SELECT INTO registered_field
12153 * FROM config.metabib_field
12154 WHERE name = field_name AND
12155 field_class = registered_class.name;
12157 rec := (registered_class.name, registered_field.id);
12162 -- maybe we have an alias?
12163 SELECT INTO registered_alias
12164 * FROM config.metabib_search_alias WHERE alias=search_parts[1];
12168 IF search_part_count < 2 THEN -- return w/e the alias says
12170 registered_alias.field_class, registered_alias.field
12175 FOR field_name IN SELECT *
12176 FROM UNNEST(search_parts[2:search_part_count]) LOOP
12177 SELECT INTO registered_field
12178 * FROM config.metabib_field
12179 WHERE name = field_name AND
12180 field_class = registered_alias.field_class;
12183 registered_alias.field_class,
12184 registered_field.id
12194 $func$ LANGUAGE PLPGSQL;
12198 FUNCTION metabib.suggest_browse_entries(
12199 query_text TEXT, -- 'foo' or 'foo & ba:*',ready for to_tsquery()
12200 search_class TEXT, -- 'alias' or 'class' or 'class|field..', etc
12201 headline_opts TEXT, -- markup options for ts_headline()
12202 visibility_org INTEGER,-- null if you don't want opac visibility test
12203 query_limit INTEGER,-- use in LIMIT clause of interal query
12204 normalization INTEGER -- argument to TS_RANK_CD()
12206 value TEXT, -- plain
12208 bouyant_and_class_match BOOL,
12210 field_weight INTEGER,
12213 match TEXT -- marked up
12217 opac_visibility_join TEXT;
12218 search_class_join TEXT;
12221 query := TO_TSQUERY('keyword', query_text);
12223 IF visibility_org IS NOT NULL THEN
12224 opac_visibility_join := '
12225 JOIN asset.opac_visible_copies aovc ON (
12226 aovc.record = mbedm.source AND
12227 aovc.circ_lib IN (SELECT id FROM actor.org_unit_descendants($4))
12230 opac_visibility_join := '';
12233 -- The following determines whether we only provide suggestsons matching
12234 -- the user's selected search_class, or whether we show other suggestions
12235 -- too. The reason for MIN() is that for search_classes like
12236 -- 'title|proper|uniform' you would otherwise get multiple rows. The
12237 -- implication is that if title as a class doesn't have restrict,
12238 -- nor does the proper field, but the uniform field does, you're going
12239 -- to get 'false' for your overall evaluation of 'should we restrict?'
12240 -- To invert that, change from MIN() to MAX().
12244 MIN(cmc.restrict::INT) AS restrict_class,
12245 MIN(cmf.restrict::INT) AS restrict_field
12246 FROM metabib.search_class_to_registered_components(search_class)
12247 AS _registered (field_class TEXT, field INT)
12249 config.metabib_class cmc ON (cmc.name = _registered.field_class)
12251 config.metabib_field cmf ON (cmf.id = _registered.field);
12253 -- evaluate 'should we restrict?'
12254 IF r_fields.restrict_field::BOOL OR r_fields.restrict_class::BOOL THEN
12255 search_class_join := '
12257 metabib.search_class_to_registered_components($2)
12258 AS _registered (field_class TEXT, field INT) ON (
12259 (_registered.field IS NULL AND
12260 _registered.field_class = cmf.field_class) OR
12261 (_registered.field = cmf.id)
12265 search_class_join := '
12267 metabib.search_class_to_registered_components($2)
12268 AS _registered (field_class TEXT, field INT) ON (
12269 _registered.field_class = cmc.name
12274 RETURN QUERY EXECUTE 'SELECT *, TS_HEADLINE(value, $1, $3) FROM (SELECT DISTINCT
12277 cmc.bouyant AND _registered.field_class IS NOT NULL,
12278 _registered.field = cmf.id,
12280 TS_RANK_CD(mbe.index_vector, $1, $6),
12282 FROM metabib.browse_entry_def_map mbedm
12283 JOIN metabib.browse_entry mbe ON (mbe.id = mbedm.entry)
12284 JOIN config.metabib_field cmf ON (cmf.id = mbedm.def)
12285 JOIN config.metabib_class cmc ON (cmf.field_class = cmc.name)
12286 ' || search_class_join || opac_visibility_join ||
12287 ' WHERE $1 @@ mbe.index_vector
12288 ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
12290 ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
12291 ' -- sic, repeat the order by clause in the outer select too
12293 query, search_class, headline_opts,
12294 visibility_org, query_limit, normalization
12298 -- bouyant AND chosen class = match class
12299 -- chosen field = match field
12306 $func$ LANGUAGE PLPGSQL;
12308 -- The advantage of this over the stock regexp_split_to_array() is that it
12309 -- won't degrade unicode strings.
12310 CREATE OR REPLACE FUNCTION evergreen.regexp_split_to_array(TEXT, TEXT)
12311 RETURNS TEXT[] AS $$
12312 return encode_array_literal([split $_[1], $_[0]]);
12313 $$ LANGUAGE PLPERLU STRICT IMMUTABLE;
12316 -- Adds some logic for browse_entry to split on non-word chars for index_vector, post-normalize
12317 CREATE OR REPLACE FUNCTION oils_tsearch2 () RETURNS TRIGGER AS $$
12323 value := NEW.value;
12325 IF TG_TABLE_NAME::TEXT ~ 'field_entry$' THEN
12327 SELECT n.func AS func,
12328 n.param_count AS param_count,
12330 FROM config.index_normalizer n
12331 JOIN config.metabib_field_index_norm_map m ON (m.norm = n.id)
12332 WHERE field = NEW.field AND m.pos < 0
12333 ORDER BY m.pos LOOP
12334 EXECUTE 'SELECT ' || normalizer.func || '(' ||
12335 quote_literal( value ) ||
12337 WHEN normalizer.param_count > 0
12338 THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
12345 NEW.value := value;
12348 IF NEW.index_vector = ''::tsvector THEN
12352 IF TG_TABLE_NAME::TEXT ~ 'field_entry$' THEN
12354 SELECT n.func AS func,
12355 n.param_count AS param_count,
12357 FROM config.index_normalizer n
12358 JOIN config.metabib_field_index_norm_map m ON (m.norm = n.id)
12359 WHERE field = NEW.field AND m.pos >= 0
12360 ORDER BY m.pos LOOP
12361 EXECUTE 'SELECT ' || normalizer.func || '(' ||
12362 quote_literal( value ) ||
12364 WHEN normalizer.param_count > 0
12365 THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
12373 IF TG_TABLE_NAME::TEXT ~ 'browse_entry$' THEN
12374 value := ARRAY_TO_STRING(
12375 evergreen.regexp_split_to_array(value, E'\\W+'), ' '
12379 NEW.index_vector = to_tsvector((TG_ARGV[0])::regconfig, value);
12383 $$ LANGUAGE PLPGSQL;
12385 -- Evergreen DB patch 0677.schema.circ_limits.sql
12387 -- FIXME: insert description of change, if needed
12391 -- check whether patch can be applied
12392 SELECT evergreen.upgrade_deps_block_check('0677', :eg_version);
12394 -- FIXME: add/check SQL statements to perform the upgrade
12395 -- Limit groups for circ counting
12396 CREATE TABLE config.circ_limit_group (
12397 id SERIAL PRIMARY KEY,
12398 name TEXT UNIQUE NOT NULL,
12403 CREATE TABLE config.circ_limit_set (
12404 id SERIAL PRIMARY KEY,
12405 name TEXT UNIQUE NOT NULL,
12406 owning_lib INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
12407 items_out INT NOT NULL, -- Total current active circulations must be less than this. 0 means skip counting (always pass)
12408 depth INT NOT NULL DEFAULT 0, -- Depth count starts at
12409 global BOOL NOT NULL DEFAULT FALSE, -- If enabled, include everything below depth, otherwise ancestors/descendants only
12413 -- Linkage between matchpoints and limit sets
12414 CREATE TABLE config.circ_matrix_limit_set_map (
12415 id SERIAL PRIMARY KEY,
12416 matchpoint INT NOT NULL REFERENCES config.circ_matrix_matchpoint (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
12417 limit_set INT NOT NULL REFERENCES config.circ_limit_set (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
12418 fallthrough BOOL NOT NULL DEFAULT FALSE, -- If true fallthrough will grab this rule as it goes along
12419 active BOOL NOT NULL DEFAULT TRUE,
12420 CONSTRAINT circ_limit_set_once_per_matchpoint UNIQUE (matchpoint, limit_set)
12423 -- Linkage between limit sets and circ mods
12424 CREATE TABLE config.circ_limit_set_circ_mod_map (
12425 id SERIAL PRIMARY KEY,
12426 limit_set INT NOT NULL REFERENCES config.circ_limit_set (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
12427 circ_mod TEXT NOT NULL REFERENCES config.circ_modifier (code) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
12428 CONSTRAINT cm_once_per_set UNIQUE (limit_set, circ_mod)
12431 -- Linkage between limit sets and limit groups
12432 CREATE TABLE config.circ_limit_set_group_map (
12433 id SERIAL PRIMARY KEY,
12434 limit_set INT NOT NULL REFERENCES config.circ_limit_set (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
12435 limit_group INT NOT NULL REFERENCES config.circ_limit_group (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
12436 check_only BOOL NOT NULL DEFAULT FALSE, -- If true, don't accumulate this limit_group for storing with the circulation
12437 CONSTRAINT clg_once_per_set UNIQUE (limit_set, limit_group)
12440 -- Linkage between limit groups and circulations
12441 CREATE TABLE action.circulation_limit_group_map (
12442 circ BIGINT NOT NULL REFERENCES action.circulation (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
12443 limit_group INT NOT NULL REFERENCES config.circ_limit_group (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
12444 PRIMARY KEY (circ, limit_group)
12447 -- Function for populating the circ/limit group mappings
12448 CREATE OR REPLACE FUNCTION action.link_circ_limit_groups ( BIGINT, INT[] ) RETURNS VOID AS $func$
12449 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));
12450 $func$ LANGUAGE SQL;
12452 DROP TYPE IF EXISTS action.circ_matrix_test_result CASCADE;
12453 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[] );
12455 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$
12457 user_object actor.usr%ROWTYPE;
12458 standing_penalty config.standing_penalty%ROWTYPE;
12459 item_object asset.copy%ROWTYPE;
12460 item_status_object config.copy_status%ROWTYPE;
12461 item_location_object asset.copy_location%ROWTYPE;
12462 result action.circ_matrix_test_result;
12463 circ_test action.found_circ_matrix_matchpoint;
12464 circ_matchpoint config.circ_matrix_matchpoint%ROWTYPE;
12465 circ_limit_set config.circ_limit_set%ROWTYPE;
12466 hold_ratio action.hold_stats%ROWTYPE;
12469 context_org_list INT[];
12470 done BOOL := FALSE;
12472 -- Assume success unless we hit a failure condition
12473 result.success := TRUE;
12475 -- Need user info to look up matchpoints
12476 SELECT INTO user_object * FROM actor.usr WHERE id = match_user AND NOT deleted;
12478 -- (Insta)Fail if we couldn't find the user
12479 IF user_object.id IS NULL THEN
12480 result.fail_part := 'no_user';
12481 result.success := FALSE;
12483 RETURN NEXT result;
12487 -- Need item info to look up matchpoints
12488 SELECT INTO item_object * FROM asset.copy WHERE id = match_item AND NOT deleted;
12490 -- (Insta)Fail if we couldn't find the item
12491 IF item_object.id IS NULL THEN
12492 result.fail_part := 'no_item';
12493 result.success := FALSE;
12495 RETURN NEXT result;
12499 SELECT INTO circ_test * FROM action.find_circ_matrix_matchpoint(circ_ou, item_object, user_object, renewal);
12501 circ_matchpoint := circ_test.matchpoint;
12502 result.matchpoint := circ_matchpoint.id;
12503 result.circulate := circ_matchpoint.circulate;
12504 result.duration_rule := circ_matchpoint.duration_rule;
12505 result.recurring_fine_rule := circ_matchpoint.recurring_fine_rule;
12506 result.max_fine_rule := circ_matchpoint.max_fine_rule;
12507 result.hard_due_date := circ_matchpoint.hard_due_date;
12508 result.renewals := circ_matchpoint.renewals;
12509 result.grace_period := circ_matchpoint.grace_period;
12510 result.buildrows := circ_test.buildrows;
12512 -- (Insta)Fail if we couldn't find a matchpoint
12513 IF circ_test.success = false THEN
12514 result.fail_part := 'no_matchpoint';
12515 result.success := FALSE;
12517 RETURN NEXT result;
12521 -- All failures before this point are non-recoverable
12522 -- Below this point are possibly overridable failures
12524 -- Fail if the user is barred
12525 IF user_object.barred IS TRUE THEN
12526 result.fail_part := 'actor.usr.barred';
12527 result.success := FALSE;
12529 RETURN NEXT result;
12532 -- Fail if the item can't circulate
12533 IF item_object.circulate IS FALSE THEN
12534 result.fail_part := 'asset.copy.circulate';
12535 result.success := FALSE;
12537 RETURN NEXT result;
12540 -- Fail if the item isn't in a circulateable status on a non-renewal
12541 IF NOT renewal AND item_object.status NOT IN ( 0, 7, 8 ) THEN
12542 result.fail_part := 'asset.copy.status';
12543 result.success := FALSE;
12545 RETURN NEXT result;
12546 -- Alternately, fail if the item isn't checked out on a renewal
12547 ELSIF renewal AND item_object.status <> 1 THEN
12548 result.fail_part := 'asset.copy.status';
12549 result.success := FALSE;
12551 RETURN NEXT result;
12554 -- Fail if the item can't circulate because of the shelving location
12555 SELECT INTO item_location_object * FROM asset.copy_location WHERE id = item_object.location;
12556 IF item_location_object.circulate IS FALSE THEN
12557 result.fail_part := 'asset.copy_location.circulate';
12558 result.success := FALSE;
12560 RETURN NEXT result;
12563 -- Use Circ OU for penalties and such
12564 SELECT INTO context_org_list ARRAY_AGG(id) FROM actor.org_unit_full_path( circ_ou );
12567 penalty_type = '%RENEW%';
12569 penalty_type = '%CIRC%';
12572 FOR standing_penalty IN
12573 SELECT DISTINCT csp.*
12574 FROM actor.usr_standing_penalty usp
12575 JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
12576 WHERE usr = match_user
12577 AND usp.org_unit IN ( SELECT * FROM unnest(context_org_list) )
12578 AND (usp.stop_date IS NULL or usp.stop_date > NOW())
12579 AND csp.block_list LIKE penalty_type LOOP
12581 result.fail_part := standing_penalty.name;
12582 result.success := FALSE;
12584 RETURN NEXT result;
12587 -- Fail if the test is set to hard non-circulating
12588 IF circ_matchpoint.circulate IS FALSE THEN
12589 result.fail_part := 'config.circ_matrix_test.circulate';
12590 result.success := FALSE;
12592 RETURN NEXT result;
12595 -- Fail if the total copy-hold ratio is too low
12596 IF circ_matchpoint.total_copy_hold_ratio IS NOT NULL THEN
12597 SELECT INTO hold_ratio * FROM action.copy_related_hold_stats(match_item);
12598 IF hold_ratio.total_copy_ratio IS NOT NULL AND hold_ratio.total_copy_ratio < circ_matchpoint.total_copy_hold_ratio THEN
12599 result.fail_part := 'config.circ_matrix_test.total_copy_hold_ratio';
12600 result.success := FALSE;
12602 RETURN NEXT result;
12606 -- Fail if the available copy-hold ratio is too low
12607 IF circ_matchpoint.available_copy_hold_ratio IS NOT NULL THEN
12608 IF hold_ratio.hold_count IS NULL THEN
12609 SELECT INTO hold_ratio * FROM action.copy_related_hold_stats(match_item);
12611 IF hold_ratio.available_copy_ratio IS NOT NULL AND hold_ratio.available_copy_ratio < circ_matchpoint.available_copy_hold_ratio THEN
12612 result.fail_part := 'config.circ_matrix_test.available_copy_hold_ratio';
12613 result.success := FALSE;
12615 RETURN NEXT result;
12619 -- Fail if the user has too many items out by defined limit sets
12620 FOR circ_limit_set IN SELECT ccls.* FROM config.circ_limit_set ccls
12621 JOIN config.circ_matrix_limit_set_map ccmlsm ON ccmlsm.limit_set = ccls.id
12622 WHERE ccmlsm.active AND ( ccmlsm.matchpoint = circ_matchpoint.id OR
12623 ( ccmlsm.matchpoint IN (SELECT * FROM unnest(result.buildrows)) AND ccmlsm.fallthrough )
12625 IF circ_limit_set.items_out > 0 AND NOT renewal THEN
12626 SELECT INTO context_org_list ARRAY_AGG(aou.id)
12627 FROM actor.org_unit_full_path( circ_ou ) aou
12628 JOIN actor.org_unit_type aout ON aou.ou_type = aout.id
12629 WHERE aout.depth >= circ_limit_set.depth;
12630 IF circ_limit_set.global THEN
12631 WITH RECURSIVE descendant_depth AS (
12634 FROM actor.org_unit ou
12635 WHERE ou.id IN (SELECT * FROM unnest(context_org_list))
12639 FROM actor.org_unit ou
12640 JOIN descendant_depth ot ON (ot.id = ou.parent_ou)
12641 ) SELECT INTO context_org_list ARRAY_AGG(ou.id) FROM actor.org_unit ou JOIN descendant_depth USING (id);
12643 SELECT INTO items_out COUNT(DISTINCT circ.id)
12644 FROM action.circulation circ
12645 JOIN asset.copy copy ON (copy.id = circ.target_copy)
12646 LEFT JOIN action.circulation_limit_group_map aclgm ON (circ.id = aclgm.circ)
12647 WHERE circ.usr = match_user
12648 AND circ.circ_lib IN (SELECT * FROM unnest(context_org_list))
12649 AND circ.checkin_time IS NULL
12650 AND (circ.stop_fines IN ('MAXFINES','LONGOVERDUE') OR circ.stop_fines IS NULL)
12651 AND (copy.circ_modifier IN (SELECT circ_mod FROM config.circ_limit_set_circ_mod_map WHERE limit_set = circ_limit_set.id)
12652 OR aclgm.limit_group IN (SELECT limit_group FROM config.circ_limit_set_group_map WHERE limit_set = circ_limit_set.id)
12654 IF items_out >= circ_limit_set.items_out THEN
12655 result.fail_part := 'config.circ_matrix_circ_mod_test';
12656 result.success := FALSE;
12658 RETURN NEXT result;
12661 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;
12664 -- If we passed everything, return the successful matchpoint
12666 RETURN NEXT result;
12671 $func$ LANGUAGE plpgsql;
12673 -- We need to re-create these, as they got dropped with the type above.
12674 CREATE OR REPLACE FUNCTION action.item_user_circ_test( INT, BIGINT, INT ) RETURNS SETOF action.circ_matrix_test_result AS $func$
12675 SELECT * FROM action.item_user_circ_test( $1, $2, $3, FALSE );
12676 $func$ LANGUAGE SQL;
12678 CREATE OR REPLACE FUNCTION action.item_user_renew_test( INT, BIGINT, INT ) RETURNS SETOF action.circ_matrix_test_result AS $func$
12679 SELECT * FROM action.item_user_circ_test( $1, $2, $3, TRUE );
12680 $func$ LANGUAGE SQL;
12682 -- Temp function for migrating circ mod limits.
12683 CREATE OR REPLACE FUNCTION evergreen.temp_migrate_circ_mod_limits() RETURNS VOID AS $func$
12685 circ_mod_group config.circ_matrix_circ_mod_test%ROWTYPE;
12687 circ_mod_count INT;
12689 FOR circ_mod_group IN SELECT * FROM config.circ_matrix_circ_mod_test LOOP
12690 INSERT INTO config.circ_limit_set(name, owning_lib, items_out, depth, global, description)
12691 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'
12692 FROM config.circ_matrix_matchpoint WHERE id = circ_mod_group.matchpoint
12693 RETURNING id INTO current_set;
12694 INSERT INTO config.circ_matrix_limit_set_map(matchpoint, limit_set, fallthrough, active) VALUES (circ_mod_group.matchpoint, current_set, false, true);
12695 INSERT INTO config.circ_limit_set_circ_mod_map(limit_set, circ_mod)
12696 SELECT current_set, circ_mod FROM config.circ_matrix_circ_mod_test_map WHERE circ_mod_test = circ_mod_group.id;
12697 SELECT INTO circ_mod_count count(id) FROM config.circ_limit_set_circ_mod_map WHERE limit_set = current_set;
12698 RAISE NOTICE 'Created limit set with id % and % circ modifiers attached to matchpoint %', current_set, circ_mod_count, circ_mod_group.matchpoint;
12701 $func$ LANGUAGE plpgsql;
12703 -- Run the temp function
12704 SELECT * FROM evergreen.temp_migrate_circ_mod_limits();
12706 -- Drop the temp function
12707 DROP FUNCTION evergreen.temp_migrate_circ_mod_limits();
12709 --Drop the old tables
12710 --Not sure we want to do this. Keeping them may help "something went wrong" correction.
12711 --DROP TABLE IF EXISTS config.circ_matrix_circ_mod_test_map, config.circ_matrix_circ_mod_test;
12714 -- Evergreen DB patch 0678.data.vandelay-default-merge-profiles.sql
12716 -- check whether patch can be applied
12717 SELECT evergreen.upgrade_deps_block_check('0678', :eg_version);
12719 INSERT INTO vandelay.merge_profile (owner, name, replace_spec)
12720 VALUES (1, 'Match-Only Merge', '901c');
12722 INSERT INTO vandelay.merge_profile (owner, name, preserve_spec)
12723 VALUES (1, 'Full Overlay', '901c');
12725 SELECT evergreen.upgrade_deps_block_check('0679', :eg_version);
12727 -- Address typo in column name
12728 ALTER TABLE config.metabib_class ADD COLUMN buoyant BOOL DEFAULT FALSE NOT NULL;
12729 UPDATE config.metabib_class SET buoyant = bouyant;
12730 ALTER TABLE config.metabib_class DROP COLUMN bouyant;
12732 CREATE OR REPLACE FUNCTION oils_tsearch2 () RETURNS TRIGGER AS $$
12738 value := NEW.value;
12740 IF TG_TABLE_NAME::TEXT ~ 'field_entry$' THEN
12742 SELECT n.func AS func,
12743 n.param_count AS param_count,
12745 FROM config.index_normalizer n
12746 JOIN config.metabib_field_index_norm_map m ON (m.norm = n.id)
12747 WHERE field = NEW.field AND m.pos < 0
12748 ORDER BY m.pos LOOP
12749 EXECUTE 'SELECT ' || normalizer.func || '(' ||
12750 quote_literal( value ) ||
12752 WHEN normalizer.param_count > 0
12753 THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
12760 NEW.value := value;
12763 IF NEW.index_vector = ''::tsvector THEN
12767 IF TG_TABLE_NAME::TEXT ~ 'field_entry$' THEN
12769 SELECT n.func AS func,
12770 n.param_count AS param_count,
12772 FROM config.index_normalizer n
12773 JOIN config.metabib_field_index_norm_map m ON (m.norm = n.id)
12774 WHERE field = NEW.field AND m.pos >= 0
12775 ORDER BY m.pos LOOP
12776 EXECUTE 'SELECT ' || normalizer.func || '(' ||
12777 quote_literal( value ) ||
12779 WHEN normalizer.param_count > 0
12780 THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
12788 IF TG_TABLE_NAME::TEXT ~ 'browse_entry$' THEN
12789 value := ARRAY_TO_STRING(
12790 evergreen.regexp_split_to_array(value, E'\\W+'), ' '
12792 value := public.search_normalize(value);
12795 NEW.index_vector = to_tsvector((TG_ARGV[0])::regconfig, value);
12799 $$ LANGUAGE PLPGSQL;
12801 -- Given a string such as a user might type into a search box, prepare
12802 -- two changed variants for TO_TSQUERY(). See
12803 -- http://www.postgresql.org/docs/9.0/static/textsearch-controls.html
12804 -- The first variant is normalized to match indexed documents regardless
12805 -- of diacritics. The second variant keeps its diacritics for proper
12806 -- highlighting via TS_HEADLINE().
12808 FUNCTION metabib.autosuggest_prepare_tsquery(orig TEXT) RETURNS TEXT[] AS
12811 orig_ended_in_space BOOLEAN;
12816 orig_ended_in_space := orig ~ E'\\s$';
12818 orig := ARRAY_TO_STRING(
12819 evergreen.regexp_split_to_array(orig, E'\\W+'), ' '
12822 normalized := public.search_normalize(orig); -- also trim()s
12823 plain := trim(orig);
12825 IF NOT orig_ended_in_space THEN
12826 plain := plain || ':*';
12827 normalized := normalized || ':*';
12830 plain := ARRAY_TO_STRING(
12831 evergreen.regexp_split_to_array(plain, E'\\s+'), ' & '
12833 normalized := ARRAY_TO_STRING(
12834 evergreen.regexp_split_to_array(normalized, E'\\s+'), ' & '
12837 RETURN ARRAY[normalized, plain];
12839 $$ LANGUAGE PLPGSQL;
12842 -- Definition of OUT parameters changes, so must drop first
12843 DROP FUNCTION IF EXISTS metabib.suggest_browse_entries (TEXT, TEXT, TEXT, INTEGER, INTEGER, INTEGER);
12846 FUNCTION metabib.suggest_browse_entries(
12847 raw_query_text TEXT, -- actually typed by humans at the UI level
12848 search_class TEXT, -- 'alias' or 'class' or 'class|field..', etc
12849 headline_opts TEXT, -- markup options for ts_headline()
12850 visibility_org INTEGER,-- null if you don't want opac visibility test
12851 query_limit INTEGER,-- use in LIMIT clause of interal query
12852 normalization INTEGER -- argument to TS_RANK_CD()
12854 value TEXT, -- plain
12856 buoyant_and_class_match BOOL,
12858 field_weight INTEGER,
12861 match TEXT -- marked up
12864 prepared_query_texts TEXT[];
12866 plain_query TSQUERY;
12867 opac_visibility_join TEXT;
12868 search_class_join TEXT;
12871 prepared_query_texts := metabib.autosuggest_prepare_tsquery(raw_query_text);
12873 query := TO_TSQUERY('keyword', prepared_query_texts[1]);
12874 plain_query := TO_TSQUERY('keyword', prepared_query_texts[2]);
12876 IF visibility_org IS NOT NULL THEN
12877 opac_visibility_join := '
12878 JOIN asset.opac_visible_copies aovc ON (
12879 aovc.record = mbedm.source AND
12880 aovc.circ_lib IN (SELECT id FROM actor.org_unit_descendants($4))
12883 opac_visibility_join := '';
12886 -- The following determines whether we only provide suggestsons matching
12887 -- the user's selected search_class, or whether we show other suggestions
12888 -- too. The reason for MIN() is that for search_classes like
12889 -- 'title|proper|uniform' you would otherwise get multiple rows. The
12890 -- implication is that if title as a class doesn't have restrict,
12891 -- nor does the proper field, but the uniform field does, you're going
12892 -- to get 'false' for your overall evaluation of 'should we restrict?'
12893 -- To invert that, change from MIN() to MAX().
12897 MIN(cmc.restrict::INT) AS restrict_class,
12898 MIN(cmf.restrict::INT) AS restrict_field
12899 FROM metabib.search_class_to_registered_components(search_class)
12900 AS _registered (field_class TEXT, field INT)
12902 config.metabib_class cmc ON (cmc.name = _registered.field_class)
12904 config.metabib_field cmf ON (cmf.id = _registered.field);
12906 -- evaluate 'should we restrict?'
12907 IF r_fields.restrict_field::BOOL OR r_fields.restrict_class::BOOL THEN
12908 search_class_join := '
12910 metabib.search_class_to_registered_components($2)
12911 AS _registered (field_class TEXT, field INT) ON (
12912 (_registered.field IS NULL AND
12913 _registered.field_class = cmf.field_class) OR
12914 (_registered.field = cmf.id)
12918 search_class_join := '
12920 metabib.search_class_to_registered_components($2)
12921 AS _registered (field_class TEXT, field INT) ON (
12922 _registered.field_class = cmc.name
12927 RETURN QUERY EXECUTE 'SELECT *, TS_HEADLINE(value, $7, $3) FROM (SELECT DISTINCT
12930 cmc.buoyant AND _registered.field_class IS NOT NULL,
12931 _registered.field = cmf.id,
12933 TS_RANK_CD(mbe.index_vector, $1, $6),
12935 FROM metabib.browse_entry_def_map mbedm
12936 JOIN metabib.browse_entry mbe ON (mbe.id = mbedm.entry)
12937 JOIN config.metabib_field cmf ON (cmf.id = mbedm.def)
12938 JOIN config.metabib_class cmc ON (cmf.field_class = cmc.name)
12939 ' || search_class_join || opac_visibility_join ||
12940 ' WHERE $1 @@ mbe.index_vector
12941 ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
12943 ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
12944 ' -- sic, repeat the order by clause in the outer select too
12946 query, search_class, headline_opts,
12947 visibility_org, query_limit, normalization, plain_query
12951 -- buoyant AND chosen class = match class
12952 -- chosen field = match field
12959 $func$ LANGUAGE PLPGSQL;
12963 \qecho The following takes about a minute per 100,000 rows in
12964 \qecho metabib.browse_entry on my development system, which is only a VM with
12965 \qecho 4 GB of memory and 2 cores.
12967 \qecho The following is a very loose estimate of how long the next UPDATE
12968 \qecho statement would take to finish on MY machine, based on YOUR number
12969 \qecho of rows in metabib.browse_entry:
12972 SELECT (COUNT(id) / 100000.0) * INTERVAL '1 minute'
12973 AS "approximate duration of following UPDATE statement"
12974 FROM metabib.browse_entry;
12976 UPDATE metabib.browse_entry SET index_vector = TO_TSVECTOR(
12978 public.search_normalize(
12980 evergreen.regexp_split_to_array(value, E'\\W+'), ' '
12986 SELECT evergreen.upgrade_deps_block_check('0680', :eg_version);
12988 -- Not much use in having identifier-class fields be suggestions. Credit for the idea goes to Ben Shum.
12989 UPDATE config.metabib_field SET browse_field = FALSE WHERE id < 100 AND field_class = 'identifier';
12992 ---------------------------------------------------------------------------
12993 -- The rest of this was tested on Evergreen Indiana's dev server, which has
12994 -- a large data set of 2.6M bibs, and was instrumental in sussing out the
12995 -- needed adjustments. Thanks, EG-IN!
12996 ---------------------------------------------------------------------------
12998 -- GIN indexes are /much/ better for prefix matching, which is important for browse and autosuggest
12999 --Commented out the creation earlier, so we don't need to drop it here.
13000 --DROP INDEX metabib.metabib_browse_entry_index_vector_idx;
13001 CREATE INDEX metabib_browse_entry_index_vector_idx ON metabib.browse_entry USING GIN (index_vector);
13004 -- We need thes to make the autosuggest limiting joins fast
13005 CREATE INDEX browse_entry_def_map_def_idx ON metabib.browse_entry_def_map (def);
13006 CREATE INDEX browse_entry_def_map_entry_idx ON metabib.browse_entry_def_map (entry);
13007 CREATE INDEX browse_entry_def_map_source_idx ON metabib.browse_entry_def_map (source);
13009 -- In practice this will always be ~1 row, and the default of 1000 causes terrible plans
13010 ALTER FUNCTION metabib.search_class_to_registered_components(text) ROWS 1;
13012 -- Reworking of the generated query to act in a sane manner in the face of large datasets
13014 FUNCTION metabib.suggest_browse_entries(
13015 raw_query_text TEXT, -- actually typed by humans at the UI level
13016 search_class TEXT, -- 'alias' or 'class' or 'class|field..', etc
13017 headline_opts TEXT, -- markup options for ts_headline()
13018 visibility_org INTEGER,-- null if you don't want opac visibility test
13019 query_limit INTEGER,-- use in LIMIT clause of interal query
13020 normalization INTEGER -- argument to TS_RANK_CD()
13022 value TEXT, -- plain
13024 buoyant_and_class_match BOOL,
13026 field_weight INTEGER,
13029 match TEXT -- marked up
13032 prepared_query_texts TEXT[];
13034 plain_query TSQUERY;
13035 opac_visibility_join TEXT;
13036 search_class_join TEXT;
13039 prepared_query_texts := metabib.autosuggest_prepare_tsquery(raw_query_text);
13041 query := TO_TSQUERY('keyword', prepared_query_texts[1]);
13042 plain_query := TO_TSQUERY('keyword', prepared_query_texts[2]);
13044 IF visibility_org IS NOT NULL THEN
13045 opac_visibility_join := '
13046 JOIN asset.opac_visible_copies aovc ON (
13047 aovc.record = x.source AND
13048 aovc.circ_lib IN (SELECT id FROM actor.org_unit_descendants($4))
13051 opac_visibility_join := '';
13054 -- The following determines whether we only provide suggestsons matching
13055 -- the user's selected search_class, or whether we show other suggestions
13056 -- too. The reason for MIN() is that for search_classes like
13057 -- 'title|proper|uniform' you would otherwise get multiple rows. The
13058 -- implication is that if title as a class doesn't have restrict,
13059 -- nor does the proper field, but the uniform field does, you're going
13060 -- to get 'false' for your overall evaluation of 'should we restrict?'
13061 -- To invert that, change from MIN() to MAX().
13065 MIN(cmc.restrict::INT) AS restrict_class,
13066 MIN(cmf.restrict::INT) AS restrict_field
13067 FROM metabib.search_class_to_registered_components(search_class)
13068 AS _registered (field_class TEXT, field INT)
13070 config.metabib_class cmc ON (cmc.name = _registered.field_class)
13072 config.metabib_field cmf ON (cmf.id = _registered.field);
13074 -- evaluate 'should we restrict?'
13075 IF r_fields.restrict_field::BOOL OR r_fields.restrict_class::BOOL THEN
13076 search_class_join := '
13078 metabib.search_class_to_registered_components($2)
13079 AS _registered (field_class TEXT, field INT) ON (
13080 (_registered.field IS NULL AND
13081 _registered.field_class = cmf.field_class) OR
13082 (_registered.field = cmf.id)
13086 search_class_join := '
13088 metabib.search_class_to_registered_components($2)
13089 AS _registered (field_class TEXT, field INT) ON (
13090 _registered.field_class = cmc.name
13095 RETURN QUERY EXECUTE '
13104 TS_HEADLINE(value, $7, $3)
13105 FROM (SELECT DISTINCT
13108 cmc.buoyant AND _registered.field_class IS NOT NULL AS push,
13109 _registered.field = cmf.id AS restrict,
13111 TS_RANK_CD(mbe.index_vector, $1, $6),
13114 FROM metabib.browse_entry_def_map mbedm
13116 -- Start with a pre-limited set of 10k possible suggestions. More than that is not going to be useful anyway
13117 JOIN (SELECT * FROM metabib.browse_entry WHERE index_vector @@ $1 LIMIT 10000) mbe ON (mbe.id = mbedm.entry)
13119 JOIN config.metabib_field cmf ON (cmf.id = mbedm.def)
13120 JOIN config.metabib_class cmc ON (cmf.field_class = cmc.name)
13121 ' || search_class_join || '
13122 ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
13123 LIMIT 1000) AS x -- This outer limit makes testing for opac visibility usably fast
13124 ' || opac_visibility_join || '
13125 ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
13127 ' -- sic, repeat the order by clause in the outer select too
13129 query, search_class, headline_opts,
13130 visibility_org, query_limit, normalization, plain_query
13134 -- buoyant AND chosen class = match class
13135 -- chosen field = match field
13142 $func$ LANGUAGE PLPGSQL;
13145 -- Evergreen DB patch 0681.schema.user-activity.sql
13148 -- check whether patch can be applied
13149 SELECT evergreen.upgrade_deps_block_check('0681', :eg_version);
13153 CREATE TYPE config.usr_activity_group AS ENUM ('authen','authz','circ','hold','search');
13155 CREATE TABLE config.usr_activity_type (
13156 id SERIAL PRIMARY KEY,
13160 label TEXT NOT NULL, -- i18n
13161 egroup config.usr_activity_group NOT NULL,
13162 enabled BOOL NOT NULL DEFAULT TRUE,
13163 transient BOOL NOT NULL DEFAULT FALSE,
13164 CONSTRAINT one_of_wwh CHECK (COALESCE(ewho,ewhat,ehow) IS NOT NULL)
13167 CREATE UNIQUE INDEX unique_wwh ON config.usr_activity_type
13168 (COALESCE(ewho,''), COALESCE (ewhat,''), COALESCE(ehow,''));
13170 CREATE TABLE actor.usr_activity (
13171 id BIGSERIAL PRIMARY KEY,
13172 usr INT REFERENCES actor.usr (id) ON DELETE SET NULL,
13173 etype INT NOT NULL REFERENCES config.usr_activity_type (id),
13174 event_time TIMESTAMPTZ NOT NULL DEFAULT NOW()
13177 -- remove transient activity entries on insert of new entries
13178 CREATE OR REPLACE FUNCTION actor.usr_activity_transient_trg () RETURNS TRIGGER AS $$
13180 DELETE FROM actor.usr_activity act USING config.usr_activity_type atype
13181 WHERE atype.transient AND
13182 NEW.etype = atype.id AND
13183 act.etype = atype.id AND
13187 $$ LANGUAGE PLPGSQL;
13189 CREATE TRIGGER remove_transient_usr_activity
13190 BEFORE INSERT ON actor.usr_activity
13191 FOR EACH ROW EXECUTE PROCEDURE actor.usr_activity_transient_trg();
13193 -- given a set of activity criteria, find the most approprate activity type
13194 CREATE OR REPLACE FUNCTION actor.usr_activity_get_type (
13198 ) RETURNS SETOF config.usr_activity_type AS $$
13199 SELECT * FROM config.usr_activity_type
13202 (ewho IS NULL OR ewho = $1) AND
13203 (ewhat IS NULL OR ewhat = $2) AND
13204 (ehow IS NULL OR ehow = $3)
13206 -- BOOL comparisons sort false to true
13207 COALESCE(ewho, '') != COALESCE($1, ''),
13208 COALESCE(ewhat,'') != COALESCE($2, ''),
13209 COALESCE(ehow, '') != COALESCE($3, '')
13213 -- given a set of activity criteria, finds the best
13214 -- activity type and inserts the activity entry
13215 CREATE OR REPLACE FUNCTION actor.insert_usr_activity (
13220 ) RETURNS SETOF actor.usr_activity AS $$
13222 new_row actor.usr_activity%ROWTYPE;
13224 SELECT id INTO new_row.etype FROM actor.usr_activity_get_type(ewho, ewhat, ehow);
13226 new_row.usr := usr;
13227 INSERT INTO actor.usr_activity (usr, etype)
13228 VALUES (usr, new_row.etype)
13229 RETURNING * INTO new_row;
13230 RETURN NEXT new_row;
13233 $$ LANGUAGE plpgsql;
13237 INSERT INTO config.usr_activity_type (id, ewho, ewhat, ehow, egroup, label) VALUES
13239 -- authen/authz actions
13240 -- note: "opensrf" is the default ingress/ehow
13241 (1, NULL, 'login', 'opensrf', 'authen', oils_i18n_gettext(1 , 'Login via opensrf', 'cuat', 'label'))
13242 ,(2, NULL, 'login', 'srfsh', 'authen', oils_i18n_gettext(2 , 'Login via srfsh', 'cuat', 'label'))
13243 ,(3, NULL, 'login', 'gateway-v1', 'authen', oils_i18n_gettext(3 , 'Login via gateway-v1', 'cuat', 'label'))
13244 ,(4, NULL, 'login', 'translator-v1','authen', oils_i18n_gettext(4 , 'Login via translator-v1', 'cuat', 'label'))
13245 ,(5, NULL, 'login', 'xmlrpc', 'authen', oils_i18n_gettext(5 , 'Login via xmlrpc', 'cuat', 'label'))
13246 ,(6, NULL, 'login', 'remoteauth', 'authen', oils_i18n_gettext(6 , 'Login via remoteauth', 'cuat', 'label'))
13247 ,(7, NULL, 'login', 'sip2', 'authen', oils_i18n_gettext(7 , 'SIP2 Proxy Login', 'cuat', 'label'))
13248 ,(8, NULL, 'login', 'apache', 'authen', oils_i18n_gettext(8 , 'Login via Apache module', 'cuat', 'label'))
13250 ,(9, NULL, 'verify', 'opensrf', 'authz', oils_i18n_gettext(9 , 'Verification via opensrf', 'cuat', 'label'))
13251 ,(10, NULL, 'verify', 'srfsh', 'authz', oils_i18n_gettext(10, 'Verification via srfsh', 'cuat', 'label'))
13252 ,(11, NULL, 'verify', 'gateway-v1', 'authz', oils_i18n_gettext(11, 'Verification via gateway-v1', 'cuat', 'label'))
13253 ,(12, NULL, 'verify', 'translator-v1','authz', oils_i18n_gettext(12, 'Verification via translator-v1', 'cuat', 'label'))
13254 ,(13, NULL, 'verify', 'xmlrpc', 'authz', oils_i18n_gettext(13, 'Verification via xmlrpc', 'cuat', 'label'))
13255 ,(14, NULL, 'verify', 'remoteauth', 'authz', oils_i18n_gettext(14, 'Verification via remoteauth', 'cuat', 'label'))
13256 ,(15, NULL, 'verify', 'sip2', 'authz', oils_i18n_gettext(15, 'SIP2 User Verification', 'cuat', 'label'))
13258 -- authen/authz actions w/ known uses of "who"
13259 ,(16, 'opac', 'login', 'gateway-v1', 'authen', oils_i18n_gettext(16, 'OPAC Login (jspac)', 'cuat', 'label'))
13260 ,(17, 'opac', 'login', 'apache', 'authen', oils_i18n_gettext(17, 'OPAC Login (tpac)', 'cuat', 'label'))
13261 ,(18, 'staffclient', 'login', 'gateway-v1', 'authen', oils_i18n_gettext(18, 'Staff Client Login', 'cuat', 'label'))
13262 ,(19, 'selfcheck', 'login', 'translator-v1','authen', oils_i18n_gettext(19, 'Self-Check Proxy Login', 'cuat', 'label'))
13263 ,(20, 'ums', 'login', 'xmlrpc', 'authen', oils_i18n_gettext(20, 'Unique Mgt Login', 'cuat', 'label'))
13264 ,(21, 'authproxy', 'login', 'apache', 'authen', oils_i18n_gettext(21, 'Apache Auth Proxy Login', 'cuat', 'label'))
13265 ,(22, 'libraryelf', 'login', 'xmlrpc', 'authz', oils_i18n_gettext(22, 'LibraryElf Login', 'cuat', 'label'))
13267 ,(23, 'selfcheck', 'verify', 'translator-v1','authz', oils_i18n_gettext(23, 'Self-Check User Verification', 'cuat', 'label'))
13268 ,(24, 'ezproxy', 'verify', 'remoteauth', 'authz', oils_i18n_gettext(24, 'EZProxy Verification', 'cuat', 'label'))
13272 -- reserve the first 1000 slots
13273 SELECT SETVAL('config.usr_activity_type_id_seq'::TEXT, 1000);
13275 INSERT INTO config.org_unit_setting_type
13276 (name, label, description, grp, datatype)
13278 'circ.patron.usr_activity_retrieve.max',
13280 'circ.patron.usr_activity_retrieve.max',
13281 'Max user activity entries to retrieve (staff client)',
13286 'circ.patron.usr_activity_retrieve.max',
13287 '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.',
13296 SELECT evergreen.upgrade_deps_block_check('0682', :eg_version);
13298 CREATE TABLE asset.copy_location_group (
13299 id SERIAL PRIMARY KEY,
13300 name TEXT NOT NULL, -- i18n
13301 owner INT NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
13302 pos INT NOT NULL DEFAULT 0,
13303 top BOOL NOT NULL DEFAULT FALSE,
13304 opac_visible BOOL NOT NULL DEFAULT TRUE,
13305 CONSTRAINT lgroup_once_per_owner UNIQUE (owner,name)
13308 CREATE TABLE asset.copy_location_group_map (
13309 id SERIAL PRIMARY KEY,
13310 location INT NOT NULL REFERENCES asset.copy_location (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
13311 lgroup INT NOT NULL REFERENCES asset.copy_location_group (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
13312 CONSTRAINT lgroup_once_per_group UNIQUE (lgroup,location)
13315 -- check whether patch can be applied
13316 SELECT evergreen.upgrade_deps_block_check('0683', :eg_version);
13318 INSERT INTO action_trigger.event_params (event_def, param, value)
13319 VALUES (5, 'check_email_notify', 1);
13320 INSERT INTO action_trigger.event_params (event_def, param, value)
13321 VALUES (7, 'check_email_notify', 1);
13322 INSERT INTO action_trigger.event_params (event_def, param, value)
13323 VALUES (9, 'check_email_notify', 1);
13324 INSERT INTO action_trigger.validator (module,description) VALUES
13325 ('HoldNotifyCheck',
13328 'Check Hold notification flag(s)',
13332 UPDATE action_trigger.event_definition SET validator = 'HoldNotifyCheck' WHERE id = 9;
13334 -- NOT COVERED: Adding check_sms_notify to the proper trigger. It doesn't have a static id.
13336 -- check whether patch can be applied
13337 SELECT evergreen.upgrade_deps_block_check('0684', :eg_version);
13341 -- Replace the constraints with more flexible ENUM's
13342 ALTER TABLE vandelay.queue DROP CONSTRAINT queue_queue_type_check;
13343 ALTER TABLE vandelay.bib_queue DROP CONSTRAINT bib_queue_queue_type_check;
13344 ALTER TABLE vandelay.authority_queue DROP CONSTRAINT authority_queue_queue_type_check;
13346 CREATE TYPE vandelay.bib_queue_queue_type AS ENUM ('bib', 'acq');
13347 CREATE TYPE vandelay.authority_queue_queue_type AS ENUM ('authority');
13349 -- dropped column is also implemented by the child tables
13350 ALTER TABLE vandelay.queue DROP COLUMN queue_type;
13352 -- to recover after using the undo sql from below
13353 -- alter table vandelay.bib_queue add column queue_type text default 'bib' not null;
13354 -- alter table vandelay.authority_queue add column queue_type text default 'authority' not null;
13356 -- modify the child tables to use the ENUMs
13357 ALTER TABLE vandelay.bib_queue
13358 ALTER COLUMN queue_type DROP DEFAULT,
13359 ALTER COLUMN queue_type TYPE vandelay.bib_queue_queue_type
13360 USING (queue_type::vandelay.bib_queue_queue_type),
13361 ALTER COLUMN queue_type SET DEFAULT 'bib';
13363 ALTER TABLE vandelay.authority_queue
13364 ALTER COLUMN queue_type DROP DEFAULT,
13365 ALTER COLUMN queue_type TYPE vandelay.authority_queue_queue_type
13366 USING (queue_type::vandelay.authority_queue_queue_type),
13367 ALTER COLUMN queue_type SET DEFAULT 'authority';
13369 -- give lineitems a pointer to their vandelay queued_record
13371 ALTER TABLE acq.lineitem ADD COLUMN queued_record BIGINT
13372 REFERENCES vandelay.queued_bib_record (id)
13373 ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
13375 ALTER TABLE acq.acq_lineitem_history ADD COLUMN queued_record BIGINT
13376 REFERENCES vandelay.queued_bib_record (id)
13377 ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
13381 INSERT INTO permission.perm_list ( id, code, description )
13384 'IMPORT_ACQ_LINEITEM_BIB_RECORD_UPLOAD',
13387 'Allows a user to create new bibs directly from an ACQ MARC file upload',
13394 INSERT INTO vandelay.import_error ( code, description )
13396 'import.record.perm_failure',
13398 'import.record.perm_failure',
13399 'Perm failure creating a record', 'vie', 'description')
13405 -- Evergreen DB patch 0685.data.bluray_vr_format.sql
13407 -- FIXME: insert description of change, if needed
13411 -- check whether patch can be applied
13412 SELECT evergreen.upgrade_deps_block_check('0685', :eg_version);
13414 -- FIXME: add/check SQL statements to perform the upgrade
13419 -- Check if it is already there
13420 PERFORM * FROM config.marc21_physical_characteristic_value_map v
13421 JOIN config.marc21_physical_characteristic_subfield_map s ON v.ptype_subfield = s.id
13422 WHERE s.ptype_key = 'v' AND s.subfield = 'e' AND s.start_pos = '4' AND s.length = '1'
13430 -- Otherwise, insert it
13431 INSERT INTO config.marc21_physical_characteristic_value_map (value,ptype_subfield,label)
13432 SELECT 's',id,'Blu-ray'
13433 FROM config.marc21_physical_characteristic_subfield_map
13434 WHERE ptype_key = 'v' AND subfield = 'e' AND start_pos = '4' AND length = '1';
13436 -- And reingest the blue-ray items so that things see the new value
13437 SELECT INTO same_marc enabled FROM config.internal_flag WHERE name = 'ingest.reingest.force_on_same_marc';
13438 UPDATE config.internal_flag SET enabled = true WHERE name = 'ingest.reingest.force_on_same_marc';
13439 UPDATE biblio.record_entry SET marc=marc WHERE id IN (SELECT record
13441 metabib.full_rec a JOIN metabib.full_rec b USING (record)
13443 a.tag = 'LDR' AND a.value LIKE '______g%'
13444 AND b.tag = '007' AND b.value LIKE 'v___s%');
13445 UPDATE config.internal_flag SET enabled = same_marc WHERE name = 'ingest.reingest.force_on_same_marc';
13450 -- Evergreen DB patch 0686.schema.auditor_boost.sql
13452 -- FIXME: insert description of change, if needed
13454 -- check whether patch can be applied
13455 SELECT evergreen.upgrade_deps_block_check('0686', :eg_version);
13457 -- FIXME: add/check SQL statements to perform the upgrade
13458 -- These three functions are for capturing, getting, and clearing user and workstation information
13460 -- Set the User AND workstation in one call. Tis faster. And less calls.
13461 -- First argument is user, second is workstation
13462 CREATE OR REPLACE FUNCTION auditor.set_audit_info(INT, INT) RETURNS VOID AS $$
13463 $_SHARED{"eg_audit_user"} = $_[0];
13464 $_SHARED{"eg_audit_ws"} = $_[1];
13465 $$ LANGUAGE plperl;
13467 -- Get the User AND workstation in one call. Less calls, useful for joins ;)
13468 CREATE OR REPLACE FUNCTION auditor.get_audit_info() RETURNS TABLE (eg_user INT, eg_ws INT) AS $$
13469 return [{eg_user => $_SHARED{"eg_audit_user"}, eg_ws => $_SHARED{"eg_audit_ws"}}];
13470 $$ LANGUAGE plperl;
13472 -- Clear the audit info, for whatever reason
13473 CREATE OR REPLACE FUNCTION auditor.clear_audit_info() RETURNS VOID AS $$
13474 delete($_SHARED{"eg_audit_user"});
13475 delete($_SHARED{"eg_audit_ws"});
13476 $$ LANGUAGE plperl;
13478 CREATE OR REPLACE FUNCTION auditor.create_auditor_history ( sch TEXT, tbl TEXT ) RETURNS BOOL AS $creator$
13481 CREATE TABLE auditor.$$ || sch || $$_$$ || tbl || $$_history (
13482 audit_id BIGINT PRIMARY KEY,
13483 audit_time TIMESTAMP WITH TIME ZONE NOT NULL,
13484 audit_action TEXT NOT NULL,
13487 LIKE $$ || sch || $$.$$ || tbl || $$
13492 $creator$ LANGUAGE 'plpgsql';
13494 CREATE OR REPLACE FUNCTION auditor.create_auditor_func ( sch TEXT, tbl TEXT ) RETURNS BOOL AS $creator$
13496 column_list TEXT[];
13498 SELECT INTO column_list array_agg(a.attname)
13499 FROM pg_catalog.pg_attribute a
13500 JOIN pg_catalog.pg_class c ON a.attrelid = c.oid
13501 JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
13502 WHERE relkind = 'r' AND n.nspname = sch AND c.relname = tbl AND a.attnum > 0 AND NOT a.attisdropped;
13505 CREATE OR REPLACE FUNCTION auditor.audit_$$ || sch || $$_$$ || tbl || $$_func ()
13506 RETURNS TRIGGER AS $func$
13508 INSERT INTO auditor.$$ || sch || $$_$$ || tbl || $$_history ( audit_id, audit_time, audit_action, audit_user, audit_ws, $$
13509 || array_to_string(column_list, ', ') || $$ )
13510 SELECT nextval('auditor.$$ || sch || $$_$$ || tbl || $$_pkey_seq'),
13515 OLD.$$ || array_to_string(column_list, ', OLD.') || $$
13516 FROM auditor.get_audit_info();
13519 $func$ LANGUAGE 'plpgsql';
13523 $creator$ LANGUAGE 'plpgsql';
13525 CREATE OR REPLACE FUNCTION auditor.create_auditor_lifecycle ( sch TEXT, tbl TEXT ) RETURNS BOOL AS $creator$
13527 column_list TEXT[];
13529 SELECT INTO column_list array_agg(a.attname)
13530 FROM pg_catalog.pg_attribute a
13531 JOIN pg_catalog.pg_class c ON a.attrelid = c.oid
13532 JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
13533 WHERE relkind = 'r' AND n.nspname = sch AND c.relname = tbl AND a.attnum > 0 AND NOT a.attisdropped;
13536 CREATE VIEW auditor.$$ || sch || $$_$$ || tbl || $$_lifecycle AS
13537 SELECT -1 AS audit_id,
13538 now() AS audit_time,
13539 '-' AS audit_action,
13542 $$ || array_to_string(column_list, ', ') || $$
13543 FROM $$ || sch || $$.$$ || tbl || $$
13545 SELECT audit_id, audit_time, audit_action, audit_user, audit_ws,
13546 $$ || array_to_string(column_list, ', ') || $$
13547 FROM auditor.$$ || sch || $$_$$ || tbl || $$_history;
13551 $creator$ LANGUAGE 'plpgsql';
13553 -- Corrects all column discrepencies between audit table and core table:
13554 -- Adds missing columns
13555 -- Removes leftover columns
13557 -- Also, ensures all core auditor columns exist.
13558 CREATE OR REPLACE FUNCTION auditor.fix_columns() RETURNS VOID AS $BODY$
13560 current_table TEXT = ''; -- Storage for post-loop main table name
13561 current_audit_table TEXT = ''; -- Storage for post-loop audit table name
13562 query TEXT = ''; -- Storage for built query
13563 cr RECORD; -- column record object
13564 alter_t BOOL = false; -- Has the alter table command been appended yet
13565 auditor_cores TEXT[] = ARRAY[]::TEXT[]; -- Core auditor function list (filled inside of loop)
13566 core_column TEXT; -- The current core column we are adding
13569 WITH audit_tables AS ( -- Basic grab of auditor tables. Anything in the auditor namespace, basically. With oids.
13570 SELECT c.oid AS audit_oid, c.relname AS audit_table
13571 FROM pg_catalog.pg_class c
13572 JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
13573 WHERE relkind='r' AND nspname = 'auditor'
13575 table_set AS ( -- Union of auditor tables with their "main" tables. With oids.
13576 SELECT a.audit_oid, a.audit_table, c.oid AS main_oid, n.nspname as main_namespace, c.relname as main_table
13577 FROM pg_catalog.pg_class c
13578 JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
13579 JOIN audit_tables a ON a.audit_table = n.nspname || '_' || c.relname || '_history'
13580 WHERE relkind = 'r'
13582 column_lists AS ( -- All columns associated with the auditor or main table, grouped by the main table's oid.
13583 SELECT DISTINCT ON (main_oid, attname) t.main_oid, a.attname
13585 JOIN pg_catalog.pg_attribute a ON a.attrelid IN (t.main_oid, t.audit_oid)
13586 WHERE attnum > 0 AND NOT attisdropped
13588 column_defs AS ( -- The motherload, every audit table and main table plus column names and defs.
13589 SELECT audit_table,
13592 a.attname AS main_column, -- These two will be null for columns that have since been deleted, or for auditor core columns
13593 pg_catalog.format_type(a.atttypid, a.atttypmod) AS main_column_def,
13594 b.attname AS audit_column, -- These two will be null for columns that have since been added
13595 pg_catalog.format_type(b.atttypid, b.atttypmod) AS audit_column_def
13597 JOIN column_lists c USING (main_oid)
13598 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
13599 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
13601 -- Nice sorted output from the above
13602 SELECT * FROM column_defs WHERE main_column_def IS DISTINCT FROM audit_column_def ORDER BY main_namespace, main_table, main_column, audit_column
13604 IF current_table <> (cr.main_namespace || '.' || cr.main_table) THEN -- New table?
13605 FOR core_column IN SELECT DISTINCT unnest(auditor_cores) LOOP -- Update missing core auditor columns
13606 IF NOT alter_t THEN -- Add ALTER TABLE if we haven't already
13607 query:=query || $$ALTER TABLE auditor.$$ || current_audit_table;
13610 query:=query || $$,$$;
13612 -- 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.
13613 query:=query || $$ ADD COLUMN $$ || CASE WHEN core_column = 'audit_id bigint' THEN $$audit_id bigserial PRIMARY KEY$$ ELSE core_column END;
13615 IF alter_t THEN -- Open alter table = needs a semicolon
13616 query:=query || $$; $$;
13618 IF 'audit_id bigint' = ANY(auditor_cores) THEN -- We added a primary key...
13619 -- Fun! Drop the default on audit_id, drop the auto-created sequence, create a new one, and set the current value
13620 -- For added fun, we have to execute in chunks due to the parser checking setval/currval arguments at parse time.
13622 EXECUTE $$ALTER TABLE auditor.$$ || current_audit_table || $$ ALTER COLUMN audit_id DROP DEFAULT; $$ ||
13623 $$CREATE SEQUENCE auditor.$$ || current_audit_table || $$_pkey_seq;$$;
13624 EXECUTE $$SELECT setval('auditor.$$ || current_audit_table || $$_pkey_seq', currval('auditor.$$ || current_audit_table || $$_audit_id_seq')); $$ ||
13625 $$DROP SEQUENCE auditor.$$ || current_audit_table || $$_audit_id_seq;$$;
13629 -- New table means we reset the list of needed auditor core columns
13630 auditor_cores = ARRAY['audit_id bigint', 'audit_time timestamp with time zone', 'audit_action text', 'audit_user integer', 'audit_ws integer'];
13631 -- And store some values for use later, because we can't rely on cr in all places.
13632 current_table:=cr.main_namespace || '.' || cr.main_table;
13633 current_audit_table:=cr.audit_table;
13635 IF cr.main_column IS NULL AND cr.audit_column LIKE 'audit_%' THEN -- Core auditor column?
13636 -- Remove core from list of cores
13637 SELECT INTO auditor_cores array_agg(core) FROM unnest(auditor_cores) AS core WHERE core != (cr.audit_column || ' ' || cr.audit_column_def);
13638 ELSIF cr.main_column IS NULL THEN -- Main column doesn't exist, and it isn't an auditor column. Needs dropping from the auditor.
13639 IF NOT alter_t THEN
13640 query:=query || $$ALTER TABLE auditor.$$ || current_audit_table;
13643 query:=query || $$,$$;
13645 query:=query || $$ DROP COLUMN $$ || cr.audit_column;
13646 ELSIF cr.audit_column IS NULL AND cr.main_column IS NOT NULL THEN -- New column auditor doesn't have. Add it.
13647 IF NOT alter_t THEN
13648 query:=query || $$ALTER TABLE auditor.$$ || current_audit_table;
13651 query:=query || $$,$$;
13653 query:=query || $$ ADD COLUMN $$ || cr.main_column || $$ $$ || cr.main_column_def;
13654 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.
13655 IF NOT alter_t THEN
13656 query:=query || $$ALTER TABLE auditor.$$ || current_audit_table;
13659 query:=query || $$,$$;
13661 query:=query || $$ ALTER COLUMN $$ || cr.audit_column || $$ TYPE $$ || cr.main_column_def;
13664 FOR core_column IN SELECT DISTINCT unnest(auditor_cores) LOOP -- Repeat this outside of the loop to catch the last table
13665 IF NOT alter_t THEN
13666 query:=query || $$ALTER TABLE auditor.$$ || current_audit_table;
13669 query:=query || $$,$$;
13671 -- 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.
13672 query:=query || $$ ADD COLUMN $$ || CASE WHEN core_column = 'audit_id bigint' THEN $$audit_id bigserial PRIMARY KEY$$ ELSE core_column END;
13674 IF alter_t THEN -- Open alter table = needs a semicolon
13675 query:=query || $$;$$;
13676 IF 'audit_id bigint' = ANY(auditor_cores) THEN -- We added a primary key...
13677 -- Fun! Drop the default on audit_id, drop the auto-created sequence, create a new one, and set the current value
13678 -- For added fun, we have to execute in chunks due to the parser checking setval/currval arguments at parse time.
13680 EXECUTE $$ALTER TABLE auditor.$$ || current_audit_table || $$ ALTER COLUMN audit_id DROP DEFAULT; $$ ||
13681 $$CREATE SEQUENCE auditor.$$ || current_audit_table || $$_pkey_seq;$$;
13682 EXECUTE $$SELECT setval('auditor.$$ || current_audit_table || $$_pkey_seq', currval('auditor.$$ || current_audit_table || $$_audit_id_seq')); $$ ||
13683 $$DROP SEQUENCE auditor.$$ || current_audit_table || $$_audit_id_seq;$$;
13689 $BODY$ LANGUAGE plpgsql;
13691 -- Update it all routine
13692 CREATE OR REPLACE FUNCTION auditor.update_auditors() RETURNS boolean AS $BODY$
13698 -- Drop Lifecycle view(s) before potential column changes
13699 FOR auditor_name IN
13701 FROM pg_catalog.pg_class c
13702 JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
13703 WHERE relkind = 'v' AND n.nspname = 'auditor' LOOP
13704 EXECUTE $$ DROP VIEW auditor.$$ || auditor_name || $$;$$;
13706 -- Fix all column discrepencies
13707 PERFORM auditor.fix_columns();
13708 -- Re-create trigger functions and lifecycle views
13709 FOR table_schema, table_name IN
13710 WITH audit_tables AS (
13711 SELECT c.oid AS audit_oid, c.relname AS audit_table
13712 FROM pg_catalog.pg_class c
13713 JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
13714 WHERE relkind='r' AND nspname = 'auditor'
13717 SELECT a.audit_oid, a.audit_table, c.oid AS main_oid, n.nspname as main_namespace, c.relname as main_table
13718 FROM pg_catalog.pg_class c
13719 JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
13720 JOIN audit_tables a ON a.audit_table = n.nspname || '_' || c.relname || '_history'
13721 WHERE relkind = 'r'
13723 SELECT main_namespace, main_table FROM table_set LOOP
13725 PERFORM auditor.create_auditor_func(table_schema, table_name);
13726 PERFORM auditor.create_auditor_lifecycle(table_schema, table_name);
13730 $BODY$ LANGUAGE plpgsql;
13732 -- Go ahead and update them all now
13733 SELECT auditor.update_auditors();
13736 -- Evergreen DB patch 0687.schema.enhance_reingest.sql
13738 -- FIXME: insert description of change, if needed
13742 -- check whether patch can be applied
13743 SELECT evergreen.upgrade_deps_block_check('0687', :eg_version);
13745 -- FIXME: add/check SQL statements to perform the upgrade
13746 -- New function def
13747 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$
13750 ind_data metabib.field_entry_template%ROWTYPE;
13751 mbe_row metabib.browse_entry%ROWTYPE;
13754 PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled;
13756 IF NOT skip_search THEN
13757 FOR fclass IN SELECT * FROM config.metabib_class LOOP
13758 -- RAISE NOTICE 'Emptying out %', fclass.name;
13759 EXECUTE $$DELETE FROM metabib.$$ || fclass.name || $$_field_entry WHERE source = $$ || bib_id;
13762 IF NOT skip_facet THEN
13763 DELETE FROM metabib.facet_entry WHERE source = bib_id;
13765 IF NOT skip_browse THEN
13766 DELETE FROM metabib.browse_entry_def_map WHERE source = bib_id;
13770 FOR ind_data IN SELECT * FROM biblio.extract_metabib_field_entry( bib_id ) LOOP
13771 IF ind_data.field < 0 THEN
13772 ind_data.field = -1 * ind_data.field;
13775 IF ind_data.facet_field AND NOT skip_facet THEN
13776 INSERT INTO metabib.facet_entry (field, source, value)
13777 VALUES (ind_data.field, ind_data.source, ind_data.value);
13780 IF ind_data.browse_field AND NOT skip_browse THEN
13781 -- A caveat about this SELECT: this should take care of replacing
13782 -- old mbe rows when data changes, but not if normalization (by
13783 -- which I mean specifically the output of
13784 -- evergreen.oils_tsearch2()) changes. It may or may not be
13785 -- expensive to add a comparison of index_vector to index_vector
13786 -- to the WHERE clause below.
13787 SELECT INTO mbe_row * FROM metabib.browse_entry WHERE value = ind_data.value;
13789 mbe_id := mbe_row.id;
13791 INSERT INTO metabib.browse_entry (value) VALUES
13792 (metabib.browse_normalize(ind_data.value, ind_data.field));
13793 mbe_id := CURRVAL('metabib.browse_entry_id_seq'::REGCLASS);
13796 INSERT INTO metabib.browse_entry_def_map (entry, def, source)
13797 VALUES (mbe_id, ind_data.field, ind_data.source);
13800 IF ind_data.search_field AND NOT skip_search THEN
13802 INSERT INTO metabib.$$ || ind_data.field_class || $$_field_entry (field, source, value)
13804 quote_literal(ind_data.field) || $$, $$ ||
13805 quote_literal(ind_data.source) || $$, $$ ||
13806 quote_literal(ind_data.value) ||
13814 $func$ LANGUAGE PLPGSQL;
13817 DROP FUNCTION IF EXISTS metabib.reingest_metabib_field_entries(BIGINT);
13820 -- Evergreen DB patch 0690.schema.unapi_limit_rank.sql
13822 -- Rewrite the in-database unapi functions to include per-object limits and
13823 -- offsets, such as a maximum number of copies and call numbers for given
13824 -- bib record via the HSTORE syntax (for example, 'acn => 5, acp => 10' would
13825 -- limit to a maximum of 5 call numbers for the bib, with up to 10 copies per
13828 -- Add some notion of "preferred library" that will provide copy counts
13829 -- and optionally affect the sorting of returned copies.
13831 -- Sort copies by availability, preferring the most available copies.
13833 -- Return located URIs.
13837 -- check whether patch can be applied
13838 SELECT evergreen.upgrade_deps_block_check('0690', :eg_version);
13840 -- The simplest way to apply all of these changes is just to replace the unapi
13841 -- schema entirely -- the following is a copy of 990.schema.unapi.sql with
13842 -- the initial COMMIT in place in case the upgrade_deps_block_check fails;
13843 -- if it does, then the attempt to create the unapi schema in the following
13844 -- transaction will also fail. Not graceful, but safe!
13845 DROP SCHEMA IF EXISTS unapi CASCADE;
13847 CREATE SCHEMA unapi;
13849 CREATE OR REPLACE FUNCTION evergreen.org_top()
13850 RETURNS SETOF actor.org_unit AS $$
13851 SELECT * FROM actor.org_unit WHERE parent_ou IS NULL LIMIT 1;
13852 $$ LANGUAGE SQL STABLE
13855 CREATE OR REPLACE FUNCTION evergreen.array_remove_item_by_value(inp ANYARRAY, el ANYELEMENT)
13856 RETURNS anyarray AS $$
13857 SELECT ARRAY_ACCUM(x.e) FROM UNNEST( $1 ) x(e) WHERE x.e <> $2;
13858 $$ LANGUAGE SQL STABLE;
13860 CREATE OR REPLACE FUNCTION evergreen.rank_ou(lib INT, search_lib INT, pref_lib INT DEFAULT NULL)
13861 RETURNS INTEGER AS $$
13862 WITH search_libs AS (
13863 SELECT id, distance FROM actor.org_unit_descendants_distance($2)
13866 (SELECT -10000 FROM actor.org_unit
13867 WHERE $1 = $3 AND id = $3 AND $2 IN (
13868 SELECT id FROM actor.org_unit WHERE parent_ou IS NULL
13871 (SELECT distance FROM search_libs WHERE id = $1),
13874 $$ LANGUAGE SQL STABLE;
13876 CREATE OR REPLACE FUNCTION evergreen.rank_cp_status(status INT)
13877 RETURNS INTEGER AS $$
13878 WITH totally_available AS (
13879 SELECT id, 0 AS avail_rank
13880 FROM config.copy_status
13881 WHERE opac_visible IS TRUE
13882 AND copy_active IS TRUE
13883 AND id != 1 -- "Checked out"
13884 ), almost_available AS (
13885 SELECT id, 10 AS avail_rank
13886 FROM config.copy_status
13887 WHERE holdable IS TRUE
13888 AND opac_visible IS TRUE
13889 AND copy_active IS FALSE
13890 OR id = 1 -- "Checked out"
13893 (SELECT avail_rank FROM totally_available WHERE $1 IN (id)),
13894 (SELECT avail_rank FROM almost_available WHERE $1 IN (id)),
13897 $$ LANGUAGE SQL STABLE;
13899 CREATE OR REPLACE FUNCTION evergreen.ranked_volumes(
13902 depth INT DEFAULT NULL,
13903 slimit HSTORE DEFAULT NULL,
13904 soffset HSTORE DEFAULT NULL,
13905 pref_lib INT DEFAULT NULL
13906 ) RETURNS TABLE (id BIGINT, name TEXT, label_sortkey TEXT, rank BIGINT) AS $$
13907 SELECT ua.id, ua.name, ua.label_sortkey, MIN(ua.rank) AS rank FROM (
13908 SELECT acn.id, aou.name, acn.label_sortkey,
13909 evergreen.rank_ou(aou.id, $2, $6), evergreen.rank_cp_status(acp.status),
13911 FROM asset.call_number acn
13912 JOIN asset.copy acp ON (acn.id = acp.call_number)
13913 JOIN actor.org_unit_descendants( $2, COALESCE(
13916 FROM actor.org_unit_type aout
13917 INNER JOIN actor.org_unit ou ON ou_type = aout.id
13920 ) AS aou ON (acp.circ_lib = aou.id)
13921 WHERE acn.record = $1
13922 AND acn.deleted IS FALSE
13923 AND acp.deleted IS FALSE
13924 GROUP BY acn.id, acp.status, aou.name, acn.label_sortkey, aou.id
13926 ORDER BY evergreen.rank_ou(aou.id, $2, $6), evergreen.rank_cp_status(acp.status)
13929 GROUP BY ua.id, ua.name, ua.label_sortkey
13930 ORDER BY rank, ua.name, ua.label_sortkey
13931 LIMIT ($4 -> 'acn')::INT
13932 OFFSET ($5 -> 'acn')::INT;
13934 LANGUAGE SQL STABLE;
13936 CREATE OR REPLACE FUNCTION evergreen.located_uris (
13939 pref_lib INT DEFAULT NULL
13940 ) RETURNS TABLE (id BIGINT, name TEXT, label_sortkey TEXT, rank INT) AS $$
13941 SELECT acn.id, aou.name, acn.label_sortkey, evergreen.rank_ou(aou.id, $2, $3) AS pref_ou
13942 FROM asset.call_number acn
13943 INNER JOIN asset.uri_call_number_map auricnm ON acn.id = auricnm.call_number
13944 INNER JOIN asset.uri auri ON auri.id = auricnm.uri
13945 INNER JOIN actor.org_unit_ancestors( COALESCE($3, $2) ) aou ON (acn.owning_lib = aou.id)
13946 WHERE acn.record = $1
13947 AND acn.deleted IS FALSE
13948 AND auri.active IS TRUE
13950 SELECT acn.id, aou.name, acn.label_sortkey, evergreen.rank_ou(aou.id, $2, $3) AS pref_ou
13951 FROM asset.call_number acn
13952 INNER JOIN asset.uri_call_number_map auricnm ON acn.id = auricnm.call_number
13953 INNER JOIN asset.uri auri ON auri.id = auricnm.uri
13954 INNER JOIN actor.org_unit_ancestors( $2 ) aou ON (acn.owning_lib = aou.id)
13955 WHERE acn.record = $1
13956 AND acn.deleted IS FALSE
13957 AND auri.active IS TRUE;
13959 LANGUAGE SQL STABLE;
13961 CREATE TABLE unapi.bre_output_layout (
13962 name TEXT PRIMARY KEY,
13963 transform TEXT REFERENCES config.xml_transform (name) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
13964 mime_type TEXT NOT NULL,
13965 feed_top TEXT NOT NULL,
13966 holdings_element TEXT,
13967 title_element TEXT,
13968 description_element TEXT,
13969 creator_element TEXT,
13970 update_ts_element TEXT
13973 INSERT INTO unapi.bre_output_layout
13974 (name, transform, mime_type, holdings_element, feed_top, title_element, description_element, creator_element, update_ts_element)
13976 ('holdings_xml', NULL, 'application/xml', NULL, 'hxml', NULL, NULL, NULL, NULL),
13977 ('marcxml', 'marcxml', 'application/marc+xml', 'record', 'collection', NULL, NULL, NULL, NULL),
13978 ('mods32', 'mods32', 'application/mods+xml', 'mods', 'modsCollection', NULL, NULL, NULL, NULL)
13981 -- Dummy functions, so we can create the real ones out of order
13982 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;
13983 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;
13984 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;
13985 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;
13986 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;
13987 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;
13988 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;
13989 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;
13990 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;
13991 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;
13992 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;
13993 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;
13994 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;
13995 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;
13996 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;
13997 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;
13998 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;
13999 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;
14000 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;
14001 CREATE OR REPLACE FUNCTION unapi.bre (
14007 depth INT DEFAULT NULL,
14008 slimit HSTORE DEFAULT NULL,
14009 soffset HSTORE DEFAULT NULL,
14010 include_xmlns BOOL DEFAULT TRUE,
14011 pref_lib INT DEFAULT NULL
14013 RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
14014 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;
14015 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;
14016 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;
14018 CREATE OR REPLACE FUNCTION unapi.holdings_xml (
14022 depth INT DEFAULT NULL,
14023 includes TEXT[] DEFAULT NULL::TEXT[],
14024 slimit HSTORE DEFAULT NULL,
14025 soffset HSTORE DEFAULT NULL,
14026 include_xmlns BOOL DEFAULT TRUE,
14027 pref_lib INT DEFAULT NULL
14029 RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
14031 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;
14033 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$
14039 'id' || COALESCE(obj_id::TEXT,'') ||
14040 'format' || COALESCE(format::TEXT,'') ||
14041 'ename' || COALESCE(ename::TEXT,'') ||
14042 'includes' || COALESCE(includes::TEXT,'{}'::TEXT[]::TEXT) ||
14043 'org' || COALESCE(org::TEXT,'') ||
14044 'depth' || COALESCE(depth::TEXT,'') ||
14045 'slimit' || COALESCE(slimit::TEXT,'') ||
14046 'soffset' || COALESCE(soffset::TEXT,'') ||
14047 'include_xmlns' || COALESCE(include_xmlns::TEXT,'');
14048 -- RAISE NOTICE 'memoize key: %', key;
14051 -- RAISE NOTICE 'memoize hash: %', key;
14053 -- XXX cache logic ... memcached? table?
14055 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;
14058 $F$ LANGUAGE PLPGSQL STABLE;
14060 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$
14062 layout unapi.bre_output_layout%ROWTYPE;
14063 transform config.xml_transform%ROWTYPE;
14066 xmlns_uri TEXT := 'http://open-ils.org/spec/feed-xml/v1';
14068 element_list TEXT[];
14071 IF org = '-' OR org IS NULL THEN
14072 SELECT shortname INTO org FROM evergreen.org_top();
14075 SELECT id INTO ouid FROM actor.org_unit WHERE shortname = org;
14076 SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format;
14078 IF layout.name IS NULL THEN
14082 SELECT * INTO transform FROM config.xml_transform WHERE name = layout.transform;
14083 xmlns_uri := COALESCE(transform.namespace_uri,xmlns_uri);
14085 -- Gather the bib xml
14086 SELECT XMLAGG( unapi.bre(i, format, '', includes, org, depth, slimit, soffset, include_xmlns)) INTO tmp_xml FROM UNNEST( id_list ) i;
14088 IF layout.title_element IS NOT NULL THEN
14089 EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.title_element ||', XMLATTRIBUTES( $1 AS xmlns), $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, title;
14092 IF layout.description_element IS NOT NULL THEN
14093 EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.description_element ||', XMLATTRIBUTES( $1 AS xmlns), $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, description;
14096 IF layout.creator_element IS NOT NULL THEN
14097 EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.creator_element ||', XMLATTRIBUTES( $1 AS xmlns), $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, creator;
14100 IF layout.update_ts_element IS NOT NULL THEN
14101 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;
14104 IF unapi_url IS NOT NULL THEN
14105 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;
14108 IF header_xml IS NOT NULL THEN tmp_xml := XMLCONCAT(header_xml,tmp_xml::XML); END IF;
14110 element_list := regexp_split_to_array(layout.feed_top,E'\\.');
14111 FOR i IN REVERSE ARRAY_UPPER(element_list, 1) .. 1 LOOP
14112 EXECUTE 'SELECT XMLELEMENT( name '|| quote_ident(element_list[i]) ||', XMLATTRIBUTES( $1 AS xmlns), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML;
14115 RETURN tmp_xml::XML;
14117 $F$ LANGUAGE PLPGSQL STABLE;
14119 CREATE OR REPLACE FUNCTION unapi.bre (
14125 depth INT DEFAULT NULL,
14126 slimit HSTORE DEFAULT NULL,
14127 soffset HSTORE DEFAULT NULL,
14128 include_xmlns BOOL DEFAULT TRUE,
14129 pref_lib INT DEFAULT NULL
14133 me biblio.record_entry%ROWTYPE;
14134 layout unapi.bre_output_layout%ROWTYPE;
14135 xfrm config.xml_transform%ROWTYPE;
14144 IF org = '-' OR org IS NULL THEN
14145 SELECT shortname INTO org FROM evergreen.org_top();
14148 SELECT id INTO ouid FROM actor.org_unit WHERE shortname = org;
14150 IF ouid IS NULL THEN
14154 IF format = 'holdings_xml' THEN -- the special case
14155 output := unapi.holdings_xml( obj_id, ouid, org, depth, includes, slimit, soffset, include_xmlns);
14159 SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format;
14161 IF layout.name IS NULL THEN
14165 SELECT * INTO xfrm FROM config.xml_transform WHERE name = layout.transform;
14167 SELECT * INTO me FROM biblio.record_entry WHERE id = obj_id;
14169 -- grab SVF if we need them
14170 IF ('mra' = ANY (includes)) THEN
14171 axml := unapi.mra(obj_id,NULL,NULL,NULL,NULL);
14176 -- grab holdings if we need them
14177 IF ('holdings_xml' = ANY (includes)) THEN
14178 hxml := unapi.holdings_xml(obj_id, ouid, org, depth, evergreen.array_remove_item_by_value(includes,'holdings_xml'), slimit, soffset, include_xmlns, pref_lib);
14184 -- generate our item node
14187 IF format = 'marcxml' THEN
14188 tmp_xml := me.marc;
14189 IF tmp_xml !~ E'<marc:' THEN -- If we're not using the prefixed namespace in this record, then remove all declarations of it
14190 tmp_xml := REGEXP_REPLACE(tmp_xml, ' xmlns:marc="http://www.loc.gov/MARC21/slim"', '', 'g');
14193 tmp_xml := oils_xslt_process(me.marc, xfrm.xslt)::XML;
14196 top_el := REGEXP_REPLACE(tmp_xml, E'^.*?<((?:\\S+:)?' || layout.holdings_element || ').*$', E'\\1');
14198 IF axml IS NOT NULL THEN
14199 tmp_xml := REGEXP_REPLACE(tmp_xml, '</' || top_el || '>(.*?)$', axml || '</' || top_el || E'>\\1');
14202 IF hxml IS NOT NULL THEN -- XXX how do we configure the holdings position?
14203 tmp_xml := REGEXP_REPLACE(tmp_xml, '</' || top_el || '>(.*?)$', hxml || '</' || top_el || E'>\\1');
14206 IF ('bre.unapi' = ANY (includes)) THEN
14207 output := REGEXP_REPLACE(
14209 '</' || top_el || '>(.*?)',
14213 'http://www.w3.org/1999/xhtml' AS xmlns,
14214 'unapi-id' AS class,
14215 'tag:open-ils.org:U2@bre/' || obj_id || '/' || org AS title
14217 )::TEXT || '</' || top_el || E'>\\1'
14223 output := REGEXP_REPLACE(output::TEXT,E'>\\s+<','><','gs')::XML;
14226 $F$ LANGUAGE PLPGSQL STABLE;
14228 CREATE OR REPLACE FUNCTION unapi.holdings_xml (
14232 depth INT DEFAULT NULL,
14233 includes TEXT[] DEFAULT NULL::TEXT[],
14234 slimit HSTORE DEFAULT NULL,
14235 soffset HSTORE DEFAULT NULL,
14236 include_xmlns BOOL DEFAULT TRUE,
14237 pref_lib INT DEFAULT NULL
14243 CASE WHEN $8 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14244 CASE WHEN ('bre' = ANY ($5)) THEN 'tag:open-ils.org:U2@bre/' || $1 || '/' || $3 ELSE NULL END AS id
14248 (SELECT XMLAGG(XMLELEMENT::XML) FROM (
14251 XMLATTRIBUTES('public' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
14253 FROM asset.opac_ou_record_copy_count($2, $1)
14257 XMLATTRIBUTES('staff' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
14259 FROM asset.staff_ou_record_copy_count($2, $1)
14263 XMLATTRIBUTES('pref_lib' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
14265 FROM asset.opac_ou_record_copy_count($9, $1)
14270 WHEN ('bmp' = ANY ($5)) THEN
14272 name monograph_parts,
14273 (SELECT XMLAGG(bmp) FROM (
14274 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)
14275 FROM biblio.monograph_part
14283 (SELECT XMLAGG(acn ORDER BY rank, name, label_sortkey) FROM (
14285 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
14286 FROM evergreen.ranked_volumes($1, $2, $4, $6, $7, $9) AS y
14289 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
14290 FROM evergreen.located_uris($1, $2, $9) AS uris
14293 CASE WHEN ('ssub' = ANY ($5)) THEN
14295 name subscriptions,
14296 (SELECT XMLAGG(ssub) FROM (
14297 SELECT unapi.ssub(id,'xml','subscription','{}'::TEXT[], $3, $4, $6, $7, FALSE)
14298 FROM serial.subscription
14299 WHERE record_entry = $1
14303 CASE WHEN ('acp' = ANY ($5)) THEN
14305 name foreign_copies,
14306 (SELECT XMLAGG(acp) FROM (
14307 SELECT unapi.acp(p.target_copy,'xml','copy',evergreen.array_remove_item_by_value($5,'acp'), $3, $4, $6, $7, FALSE)
14308 FROM biblio.peer_bib_copy_map p
14309 JOIN asset.copy c ON (p.target_copy = c.id)
14310 WHERE NOT c.deleted AND p.peer_record = $1
14311 LIMIT ($6 -> 'acp')::INT
14312 OFFSET ($7 -> 'acp')::INT
14317 $F$ LANGUAGE SQL STABLE;
14319 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$
14323 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14324 'tag:open-ils.org:U2@ssub/' || id AS id,
14325 'tag:open-ils.org:U2@aou/' || owning_lib AS owning_lib,
14326 start_date AS start, end_date AS end, expected_date_offset
14329 WHEN ('sdist' = ANY ($4)) THEN
14330 XMLELEMENT( name distributions,
14331 (SELECT XMLAGG(sdist) FROM (
14332 SELECT unapi.sdist( id, 'xml', 'distribution', evergreen.array_remove_item_by_value($4,'ssub'), $5, $6, $7, $8, FALSE)
14333 FROM serial.distribution
14334 WHERE subscription = ssub.id
14340 FROM serial.subscription ssub
14342 GROUP BY id, start_date, end_date, expected_date_offset, owning_lib;
14343 $F$ LANGUAGE SQL STABLE;
14345 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$
14349 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14350 'tag:open-ils.org:U2@sdist/' || id AS id,
14351 'tag:open-ils.org:U2@acn/' || receive_call_number AS receive_call_number,
14352 'tag:open-ils.org:U2@acn/' || bind_call_number AS bind_call_number,
14353 unit_label_prefix, label, unit_label_suffix, summary_method
14355 unapi.aou( holding_lib, $2, 'holding_lib', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8),
14356 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,
14358 WHEN ('sstr' = ANY ($4)) THEN
14359 XMLELEMENT( name streams,
14360 (SELECT XMLAGG(sstr) FROM (
14361 SELECT unapi.sstr( id, 'xml', 'stream', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE)
14363 WHERE distribution = sdist.id
14368 XMLELEMENT( name summaries,
14370 WHEN ('sbsum' = ANY ($4)) THEN
14371 (SELECT XMLAGG(sbsum) FROM (
14372 SELECT unapi.sbsum( id, 'xml', 'serial_summary', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE)
14373 FROM serial.basic_summary
14374 WHERE distribution = sdist.id
14379 WHEN ('sisum' = ANY ($4)) THEN
14380 (SELECT XMLAGG(sisum) FROM (
14381 SELECT unapi.sisum( id, 'xml', 'serial_summary', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE)
14382 FROM serial.index_summary
14383 WHERE distribution = sdist.id
14388 WHEN ('sssum' = ANY ($4)) THEN
14389 (SELECT XMLAGG(sssum) FROM (
14390 SELECT unapi.sssum( id, 'xml', 'serial_summary', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE)
14391 FROM serial.supplement_summary
14392 WHERE distribution = sdist.id
14398 FROM serial.distribution sdist
14400 GROUP BY id, label, unit_label_prefix, unit_label_suffix, holding_lib, summary_method, subscription, receive_call_number, bind_call_number;
14401 $F$ LANGUAGE SQL STABLE;
14403 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$
14407 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14408 'tag:open-ils.org:U2@sstr/' || id AS id,
14411 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,
14413 WHEN ('sitem' = ANY ($4)) THEN
14414 XMLELEMENT( name items,
14415 (SELECT XMLAGG(sitem) FROM (
14416 SELECT unapi.sitem( id, 'xml', 'serial_item', evergreen.array_remove_item_by_value($4,'sstr'), $5, $6, $7, $8, FALSE)
14418 WHERE stream = sstr.id
14424 FROM serial.stream sstr
14426 GROUP BY id, routing_label, distribution;
14427 $F$ LANGUAGE SQL STABLE;
14429 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$
14433 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14434 'tag:open-ils.org:U2@siss/' || id AS id,
14435 create_date, edit_date, label, date_published,
14436 holding_code, holding_type, holding_link_id
14438 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,
14440 WHEN ('sitem' = ANY ($4)) THEN
14441 XMLELEMENT( name items,
14442 (SELECT XMLAGG(sitem) FROM (
14443 SELECT unapi.sitem( id, 'xml', 'serial_item', evergreen.array_remove_item_by_value($4,'siss'), $5, $6, $7, $8, FALSE)
14445 WHERE issuance = sstr.id
14451 FROM serial.issuance sstr
14453 GROUP BY id, create_date, edit_date, label, date_published, holding_code, holding_type, holding_link_id, subscription;
14454 $F$ LANGUAGE SQL STABLE;
14456 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$
14460 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14461 'tag:open-ils.org:U2@sitem/' || id AS id,
14462 'tag:open-ils.org:U2@siss/' || issuance AS issuance,
14463 date_expected, date_received
14465 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,
14466 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,
14467 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,
14468 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
14469 -- XMLELEMENT( name notes,
14471 -- WHEN ('acpn' = ANY ($4)) THEN
14472 -- (SELECT XMLAGG(acpn) FROM (
14473 -- SELECT unapi.acpn( id, 'xml', 'copy_note', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8)
14474 -- FROM asset.copy_note
14475 -- WHERE owning_copy = cp.id AND pub
14481 FROM serial.item sitem
14483 $F$ LANGUAGE SQL STABLE;
14486 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$
14488 name serial_summary,
14490 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14491 'tag:open-ils.org:U2@sbsum/' || id AS id,
14492 'sssum' AS type, generated_coverage, textual_holdings, show_generated
14494 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
14496 FROM serial.supplement_summary ssum
14498 GROUP BY id, generated_coverage, textual_holdings, distribution, show_generated;
14499 $F$ LANGUAGE SQL STABLE;
14501 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$
14503 name serial_summary,
14505 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14506 'tag:open-ils.org:U2@sbsum/' || id AS id,
14507 'sbsum' AS type, generated_coverage, textual_holdings, show_generated
14509 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
14511 FROM serial.basic_summary ssum
14513 GROUP BY id, generated_coverage, textual_holdings, distribution, show_generated;
14514 $F$ LANGUAGE SQL STABLE;
14516 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$
14518 name serial_summary,
14520 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14521 'tag:open-ils.org:U2@sbsum/' || id AS id,
14522 'sisum' AS type, generated_coverage, textual_holdings, show_generated
14524 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
14526 FROM serial.index_summary ssum
14528 GROUP BY id, generated_coverage, textual_holdings, distribution, show_generated;
14529 $F$ LANGUAGE SQL STABLE;
14532 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$
14536 IF ename = 'circlib' THEN
14540 'http://open-ils.org/spec/actors/v1' AS xmlns,
14545 FROM actor.org_unit aou
14548 EXECUTE $$SELECT XMLELEMENT(
14549 name $$ || ename || $$,
14551 'http://open-ils.org/spec/actors/v1' AS xmlns,
14552 'tag:open-ils.org:U2@aou/' || id AS id,
14553 shortname, name, opac_visible
14556 FROM actor.org_unit aou
14557 WHERE id = $1 $$ INTO output USING obj_id;
14563 $F$ LANGUAGE PLPGSQL STABLE;
14565 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$
14569 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14573 label_prefix AS prefix,
14574 label_suffix AS suffix
14578 FROM asset.copy_location
14580 $F$ LANGUAGE SQL STABLE;
14582 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$
14586 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14593 FROM config.copy_status
14595 $F$ LANGUAGE SQL STABLE;
14597 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$
14601 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14602 create_date AS date,
14607 FROM asset.copy_note
14609 $F$ LANGUAGE SQL STABLE;
14611 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$
14615 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14621 FROM asset.stat_cat_entry asce
14622 JOIN asset.stat_cat sc ON (sc.id = asce.stat_cat)
14623 WHERE asce.id = $1;
14624 $F$ LANGUAGE SQL STABLE;
14626 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$
14628 name monograph_part,
14630 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14631 'tag:open-ils.org:U2@bmp/' || id AS id,
14635 'tag:open-ils.org:U2@bre/' || record AS record
14638 WHEN ('acp' = ANY ($4)) THEN
14639 XMLELEMENT( name copies,
14640 (SELECT XMLAGG(acp) FROM (
14641 SELECT unapi.acp( cp.id, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'bmp'), $5, $6, $7, $8, FALSE)
14643 JOIN asset.copy_part_map cpm ON (cpm.target_copy = cp.id)
14644 WHERE cpm.part = $1
14645 AND cp.deleted IS FALSE
14646 ORDER BY COALESCE(cp.copy_number,0), cp.barcode
14647 LIMIT ($7 -> 'acp')::INT
14648 OFFSET ($8 -> 'acp')::INT
14654 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
14656 FROM biblio.monograph_part
14658 GROUP BY id, label, label_sortkey, record;
14659 $F$ LANGUAGE SQL STABLE;
14661 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$
14665 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14666 'tag:open-ils.org:U2@acp/' || id AS id, id AS copy_id,
14667 create_date, edit_date, copy_number, circulate, deposit,
14668 ref, holdable, deleted, deposit_amount, price, barcode,
14669 circ_modifier, circ_as_type, opac_visible, age_protect
14671 unapi.ccs( status, $2, 'status', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE),
14672 unapi.acl( location, $2, 'location', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE),
14673 unapi.aou( circ_lib, $2, 'circ_lib', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8),
14674 unapi.aou( circ_lib, $2, 'circlib', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8),
14675 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,
14677 WHEN ('acpn' = ANY ($4)) THEN
14678 XMLELEMENT( name copy_notes,
14679 (SELECT XMLAGG(acpn) FROM (
14680 SELECT unapi.acpn( id, 'xml', 'copy_note', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
14681 FROM asset.copy_note
14682 WHERE owning_copy = cp.id AND pub
14688 WHEN ('ascecm' = ANY ($4)) THEN
14689 XMLELEMENT( name statcats,
14690 (SELECT XMLAGG(ascecm) FROM (
14691 SELECT unapi.ascecm( stat_cat_entry, 'xml', 'statcat', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
14692 FROM asset.stat_cat_entry_copy_map
14693 WHERE owning_copy = cp.id
14699 WHEN ('bre' = ANY ($4)) THEN
14700 XMLELEMENT( name foreign_records,
14701 (SELECT XMLAGG(bre) FROM (
14702 SELECT unapi.bre(peer_record,'marcxml','record','{}'::TEXT[], $5, $6, $7, $8, FALSE)
14703 FROM biblio.peer_bib_copy_map
14704 WHERE target_copy = cp.id
14711 WHEN ('bmp' = ANY ($4)) THEN
14712 XMLELEMENT( name monograph_parts,
14713 (SELECT XMLAGG(bmp) FROM (
14714 SELECT unapi.bmp( part, 'xml', 'monograph_part', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
14715 FROM asset.copy_part_map
14716 WHERE target_copy = cp.id
14722 WHEN ('circ' = ANY ($4)) THEN
14723 XMLELEMENT( name current_circulation,
14724 (SELECT XMLAGG(circ) FROM (
14725 SELECT unapi.circ( id, 'xml', 'circ', evergreen.array_remove_item_by_value($4,'circ'), $5, $6, $7, $8, FALSE)
14726 FROM action.circulation
14727 WHERE target_copy = cp.id
14728 AND checkin_time IS NULL
14736 AND cp.deleted IS FALSE
14737 GROUP BY id, status, location, circ_lib, call_number, create_date,
14738 edit_date, copy_number, circulate, deposit, ref, holdable,
14739 deleted, deposit_amount, price, barcode, circ_modifier,
14740 circ_as_type, opac_visible, age_protect;
14741 $F$ LANGUAGE SQL STABLE;
14743 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$
14747 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14748 'tag:open-ils.org:U2@acp/' || id AS id, id AS copy_id,
14749 create_date, edit_date, copy_number, circulate, deposit,
14750 ref, holdable, deleted, deposit_amount, price, barcode,
14751 circ_modifier, circ_as_type, opac_visible, age_protect,
14752 status_changed_time, floating, mint_condition,
14753 detailed_contents, sort_key, summary_contents, cost
14755 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),
14756 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),
14757 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),
14758 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),
14759 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,
14760 XMLELEMENT( name copy_notes,
14762 WHEN ('acpn' = ANY ($4)) THEN
14763 (SELECT XMLAGG(acpn) FROM (
14764 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)
14765 FROM asset.copy_note
14766 WHERE owning_copy = cp.id AND pub
14771 XMLELEMENT( name statcats,
14773 WHEN ('ascecm' = ANY ($4)) THEN
14774 (SELECT XMLAGG(ascecm) FROM (
14775 SELECT unapi.ascecm( stat_cat_entry, 'xml', 'statcat', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
14776 FROM asset.stat_cat_entry_copy_map
14777 WHERE owning_copy = cp.id
14782 XMLELEMENT( name foreign_records,
14784 WHEN ('bre' = ANY ($4)) THEN
14785 (SELECT XMLAGG(bre) FROM (
14786 SELECT unapi.bre(peer_record,'marcxml','record','{}'::TEXT[], $5, $6, $7, $8, FALSE)
14787 FROM biblio.peer_bib_copy_map
14788 WHERE target_copy = cp.id
14794 WHEN ('bmp' = ANY ($4)) THEN
14795 XMLELEMENT( name monograph_parts,
14796 (SELECT XMLAGG(bmp) FROM (
14797 SELECT unapi.bmp( part, 'xml', 'monograph_part', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
14798 FROM asset.copy_part_map
14799 WHERE target_copy = cp.id
14805 WHEN ('circ' = ANY ($4)) THEN
14806 XMLELEMENT( name current_circulation,
14807 (SELECT XMLAGG(circ) FROM (
14808 SELECT unapi.circ( id, 'xml', 'circ', evergreen.array_remove_item_by_value($4,'circ'), $5, $6, $7, $8, FALSE)
14809 FROM action.circulation
14810 WHERE target_copy = cp.id
14811 AND checkin_time IS NULL
14817 FROM serial.unit cp
14819 AND cp.deleted IS FALSE
14820 GROUP BY id, status, location, circ_lib, call_number, create_date,
14821 edit_date, copy_number, circulate, floating, mint_condition,
14822 deposit, ref, holdable, deleted, deposit_amount, price,
14823 barcode, circ_modifier, circ_as_type, opac_visible,
14824 status_changed_time, detailed_contents, sort_key,
14825 summary_contents, cost, age_protect;
14826 $F$ LANGUAGE SQL STABLE;
14828 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$
14832 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14833 'tag:open-ils.org:U2@acn/' || acn.id AS id,
14834 acn.id AS vol_id, o.shortname AS lib,
14835 o.opac_visible AS opac_visible,
14836 deleted, label, label_sortkey, label_class, record
14838 unapi.aou( owning_lib, $2, 'owning_lib', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8),
14840 WHEN ('acp' = ANY ($4)) THEN
14841 CASE WHEN $6 IS NOT NULL THEN
14842 XMLELEMENT( name copies,
14843 (SELECT XMLAGG(acp ORDER BY rank_avail) FROM (
14844 SELECT unapi.acp( cp.id, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE),
14845 evergreen.rank_cp_status(cp.status) AS rank_avail
14847 JOIN actor.org_unit_descendants( (SELECT id FROM actor.org_unit WHERE shortname = $5), $6) aoud ON (cp.circ_lib = aoud.id)
14848 WHERE cp.call_number = acn.id
14849 AND cp.deleted IS FALSE
14850 ORDER BY rank_avail, COALESCE(cp.copy_number,0), cp.barcode
14851 LIMIT ($7 -> 'acp')::INT
14852 OFFSET ($8 -> 'acp')::INT
14856 XMLELEMENT( name copies,
14857 (SELECT XMLAGG(acp ORDER BY rank_avail) FROM (
14858 SELECT unapi.acp( cp.id, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE),
14859 evergreen.rank_cp_status(cp.status) AS rank_avail
14861 JOIN actor.org_unit_descendants( (SELECT id FROM actor.org_unit WHERE shortname = $5) ) aoud ON (cp.circ_lib = aoud.id)
14862 WHERE cp.call_number = acn.id
14863 AND cp.deleted IS FALSE
14864 ORDER BY rank_avail, COALESCE(cp.copy_number,0), cp.barcode
14865 LIMIT ($7 -> 'acp')::INT
14866 OFFSET ($8 -> 'acp')::INT
14874 (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)
14876 unapi.acnp( acn.prefix, 'marcxml', 'prefix', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE),
14877 unapi.acns( acn.suffix, 'marcxml', 'suffix', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE),
14878 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
14880 FROM asset.call_number acn
14881 JOIN actor.org_unit o ON (o.id = acn.owning_lib)
14883 AND acn.deleted IS FALSE
14884 GROUP BY acn.id, o.shortname, o.opac_visible, deleted, label, label_sortkey, label_class, owning_lib, record, acn.prefix, acn.suffix;
14885 $F$ LANGUAGE SQL STABLE;
14887 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$
14889 name call_number_prefix,
14891 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14894 'tag:open-ils.org:U2@aou/' || owning_lib AS owning_lib,
14898 FROM asset.call_number_prefix
14900 $F$ LANGUAGE SQL STABLE;
14902 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$
14904 name call_number_suffix,
14906 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14909 'tag:open-ils.org:U2@aou/' || owning_lib AS owning_lib,
14913 FROM asset.call_number_suffix
14915 $F$ LANGUAGE SQL STABLE;
14917 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$
14921 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14922 'tag:open-ils.org:U2@auri/' || uri.id AS id,
14928 WHEN ('acn' = ANY ($4)) THEN
14929 XMLELEMENT( name copies,
14930 (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)
14937 GROUP BY uri.id, use_restriction, href, label;
14938 $F$ LANGUAGE SQL STABLE;
14940 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$
14944 CASE WHEN $9 THEN 'http://open-ils.org/spec/indexing/v1' ELSE NULL END AS xmlns,
14945 'tag:open-ils.org:U2@mra/' || mra.id AS id,
14946 'tag:open-ils.org:U2@bre/' || mra.id AS record
14948 (SELECT XMLAGG(foo.y)
14949 FROM (SELECT XMLELEMENT(
14953 cvm.value AS "coded-value",
14959 FROM EACH(mra.attrs) AS x
14960 JOIN config.record_attr_definition rad ON (x.key = rad.name)
14961 LEFT JOIN config.coded_value_map cvm ON (cvm.ctype = x.key AND code = x.value)
14965 FROM metabib.record_attr mra
14967 $F$ LANGUAGE SQL STABLE;
14969 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$
14973 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
14974 'tag:open-ils.org:U2@circ/' || id AS id,
14978 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,
14979 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
14981 FROM action.circulation
14983 $F$ LANGUAGE SQL STABLE;
14987 -- Some test queries
14989 SELECT unapi.memoize( 'bre', 1,'mods32','','{holdings_xml,acp}'::TEXT[], 'SYS1');
14990 SELECT unapi.memoize( 'bre', 1,'marcxml','','{holdings_xml,acp}'::TEXT[], 'SYS1');
14991 SELECT unapi.memoize( 'bre', 1,'holdings_xml','','{holdings_xml,acp}'::TEXT[], 'SYS1');
14993 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>');
14995 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>');
14996 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>');
14997 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>');
14998 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>');
15000 SELECT unapi.biblio_record_entry_feed('{216}'::BIGINT[],'marcxml','{}'::TEXT[], 'BR1');
15001 EXPLAIN ANALYZE SELECT unapi.bre(216,'marcxml','record','{holdings_xml,bre.unapi}'::TEXT[], 'BR1');
15002 EXPLAIN ANALYZE SELECT unapi.bre(216,'holdings_xml','record','{}'::TEXT[], 'BR1');
15003 EXPLAIN ANALYZE SELECT unapi.holdings_xml(216,4,'BR1',2,'{bre}'::TEXT[]);
15004 EXPLAIN ANALYZE SELECT unapi.bre(216,'mods32','record','{}'::TEXT[], 'BR1');
15006 -- Limit to 5 call numbers, 5 copies, with a preferred library of 4 (BR1), in SYS2 at a depth of 0
15007 EXPLAIN ANALYZE SELECT unapi.bre(36,'marcxml','record','{holdings_xml,mra,acp,acnp,acns,bmp}','SYS2',0,'acn=>5,acp=>5',NULL,TRUE,4);
15011 -- Evergreen DB patch 0693.schema.do_not_despace_issns.sql
15013 -- FIXME: insert description of change, if needed
15017 -- check whether patch can be applied
15018 SELECT evergreen.upgrade_deps_block_check('0693', :eg_version);
15020 -- FIXME: add/check SQL statements to perform the upgrade
15021 -- Delete the index normalizer that was meant to remove spaces from ISSNs
15022 -- but ended up breaking records with multiple ISSNs
15023 DELETE FROM config.metabib_field_index_norm_map WHERE id IN (
15024 SELECT map.id FROM config.metabib_field_index_norm_map map
15025 INNER JOIN config.metabib_field cmf ON cmf.id = map.field
15026 INNER JOIN config.index_normalizer cin ON cin.id = map.norm
15027 WHERE cin.func = 'replace'
15028 AND cmf.field_class = 'identifier'
15029 AND cmf.name = 'issn'
15030 AND map.params = $$[" ",""]$$
15033 -- Reindex records that have more than just a single ISSN
15034 -- to ensure that spaces are maintained
15035 SELECT metabib.reingest_metabib_field_entries(source)
15036 FROM metabib.identifier_field_entry mife
15037 INNER JOIN config.metabib_field cmf ON cmf.id = mife.field
15038 WHERE cmf.field_class = 'identifier'
15039 AND cmf.name = 'issn'
15040 AND char_length(value) > 9
15044 SELECT evergreen.upgrade_deps_block_check('0694', :eg_version);
15046 INSERT into config.org_unit_setting_type
15047 ( name, grp, label, description, datatype, fm_class ) VALUES
15049 ( 'ui.patron.edit.au.prefix.require', 'gui',
15050 oils_i18n_gettext('ui.patron.edit.au.prefix.require',
15051 'Require prefix field on patron registration',
15053 oils_i18n_gettext('ui.patron.edit.au.prefix.require',
15054 'The prefix field will be required on the patron registration screen.'
15055 'coust', 'description'),
15058 ,( 'ui.patron.edit.au.prefix.show', 'gui',
15059 oils_i18n_gettext('ui.patron.edit.au.prefix.show',
15060 'Show prefix field on patron registration',
15062 oils_i18n_gettext('ui.patron.edit.au.prefix.show',
15063 '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.'
15064 'coust', 'description'),
15067 ,( 'ui.patron.edit.au.prefix.suggest', 'gui',
15068 oils_i18n_gettext('ui.patron.edit.au.prefix.suggest',
15069 'Suggest prefix field on patron registration',
15071 oils_i18n_gettext('ui.patron.edit.au.prefix.suggest',
15072 '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.'
15073 'coust', 'description'),
15078 SELECT evergreen.upgrade_deps_block_check('0699', :eg_version);
15080 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype, grp )
15082 'ui.hide_copy_editor_fields',
15084 'ui.hide_copy_editor_fields',
15085 'GUI: Hide these fields within the Item Attribute Editor',
15090 'ui.hide_copy_editor_fields',
15091 'This setting may be best maintained with the dedicated configuration'
15092 || ' interface within the Item Attribute Editor. However, here it'
15093 || ' shows up as comma separated list of field identifiers to hide.',