LP#1750894 Workstation & Cascade settings
[working/Evergreen.git] / Open-ILS / src / sql / Pg / upgrade / XXXX.schema.workstation-settings.sql
1 BEGIN;
2
3 CREATE TYPE actor.cascade_setting_summary AS (
4     name TEXT,
5     value JSON,
6     has_org_setting BOOLEAN,
7     has_user_setting BOOLEAN,
8     has_workstation_setting BOOLEAN
9 );
10
11 -- SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
12
13 CREATE TABLE config.workstation_setting_type (
14     name            TEXT    PRIMARY KEY,
15     label           TEXT    UNIQUE NOT NULL,
16     grp             TEXT    REFERENCES config.settings_group (name),
17     description     TEXT,
18     datatype        TEXT    NOT NULL DEFAULT 'string',
19     fm_class        TEXT,
20     --
21     -- define valid datatypes
22     --
23     CONSTRAINT cwst_valid_datatype CHECK ( datatype IN
24     ( 'bool', 'integer', 'float', 'currency', 'interval',
25       'date', 'string', 'object', 'array', 'link' ) ),
26     --
27     -- fm_class is meaningful only for 'link' datatype
28     --
29     CONSTRAINT cwst_no_empty_link CHECK
30     ( ( datatype =  'link' AND fm_class IS NOT NULL ) OR
31       ( datatype <> 'link' AND fm_class IS NULL ) )
32 );
33
34 CREATE TABLE actor.workstation_setting (
35     id          SERIAL PRIMARY KEY,
36     workstation INT    NOT NULL REFERENCES actor.workstation (id) 
37                        ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
38     name        TEXT   NOT NULL REFERENCES config.workstation_setting_type (name) 
39                        ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
40     value       JSON   NOT NULL
41 );
42
43
44 CREATE INDEX actor_workstation_setting_workstation_idx 
45     ON actor.workstation_setting (workstation);
46
47 CREATE OR REPLACE FUNCTION config.setting_is_user_or_ws()
48 RETURNS TRIGGER AS $FUNC$
49 BEGIN
50
51     IF TG_TABLE_NAME = 'usr_setting_type' THEN
52         PERFORM TRUE FROM config.workstation_setting_type cwst
53             WHERE cwst.name = NEW.name;
54         IF NOT FOUND THEN
55             RETURN NULL;
56         END IF;
57     END IF;
58
59     IF TG_TABLE_NAME = 'workstation_setting_type' THEN
60         PERFORM TRUE FROM config.usr_setting_type cust
61             WHERE cust.name = NEW.name;
62         IF NOT FOUND THEN
63             RETURN NULL;
64         END IF;
65     END IF;
66
67     RAISE EXCEPTION 
68         '% Cannot be used as both a user setting and a workstation setting.', 
69         NEW.name;
70 END;
71 $FUNC$ LANGUAGE PLPGSQL STABLE;
72
73 CREATE CONSTRAINT TRIGGER check_setting_is_usr_or_ws
74   AFTER INSERT OR UPDATE ON config.usr_setting_type
75   FOR EACH ROW EXECUTE PROCEDURE config.setting_is_user_or_ws();
76
77 CREATE CONSTRAINT TRIGGER check_setting_is_usr_or_ws
78   AFTER INSERT OR UPDATE ON config.workstation_setting_type
79   FOR EACH ROW EXECUTE PROCEDURE config.setting_is_user_or_ws();
80
81 CREATE OR REPLACE FUNCTION actor.get_cascade_setting(
82     setting_name TEXT, org_id INT, user_id INT, workstation_id INT) 
83     RETURNS actor.cascade_setting_summary AS
84 $FUNC$
85 DECLARE
86     setting_value JSON;
87     summary actor.cascade_setting_summary;
88     org_setting_type config.org_unit_setting_type%ROWTYPE;
89 BEGIN
90
91     summary.name := setting_name;
92
93     -- Collect the org setting type status first in case we exit early.
94     -- The existance of an org setting type is not considered
95     -- privileged information.
96     SELECT INTO org_setting_type * 
97         FROM config.org_unit_setting_type WHERE name = setting_name;
98     IF FOUND THEN
99         summary.has_org_setting := TRUE;
100     ELSE
101         summary.has_org_setting := FALSE;
102     END IF;
103
104     -- User and workstation settings have the same priority.
105     -- Start with user settings since that's the simplest code path.
106     -- The workstation_id is ignored if no user_id is provided.
107     IF user_id IS NOT NULL THEN
108
109         SELECT INTO summary.value value FROM actor.usr_setting
110             WHERE usr = user_id AND name = setting_name;
111
112         IF FOUND THEN
113             -- if we have a value, we have a setting type
114             summary.has_user_setting := TRUE;
115
116             IF workstation_id IS NOT NULL THEN
117                 -- Only inform the caller about the workstation
118                 -- setting type disposition when a workstation id is
119                 -- provided.  Otherwise, it's NULL to indicate UNKNOWN.
120                 summary.has_workstation_setting := FALSE;
121             END IF;
122
123             RETURN summary;
124         END IF;
125
126         -- no user setting value, but a setting type may exist
127         SELECT INTO summary.has_user_setting EXISTS (
128             SELECT TRUE FROM config.usr_setting_type 
129             WHERE name = setting_name
130         );
131
132         IF workstation_id IS NOT NULL THEN 
133
134             IF NOT summary.has_user_setting THEN
135                 -- A workstation setting type may only exist when a user
136                 -- setting type does not.
137
138                 SELECT INTO summary.value value 
139                     FROM actor.workstation_setting         
140                     WHERE workstation = workstation_id AND name = setting_name;
141
142                 IF FOUND THEN
143                     -- if we have a value, we have a setting type
144                     summary.has_workstation_setting := TRUE;
145                     RETURN summary;
146                 END IF;
147
148                 -- no value, but a setting type may exist
149                 SELECT INTO summary.has_workstation_setting EXISTS (
150                     SELECT TRUE FROM config.workstation_setting_type 
151                     WHERE name = setting_name
152                 );
153             END IF;
154
155             -- Finally make use of the workstation to determine the org
156             -- unit if none is provided.
157             IF org_id IS NULL AND summary.has_org_setting THEN
158                 SELECT INTO org_id owning_lib 
159                     FROM actor.workstation WHERE id = workstation_id;
160             END IF;
161         END IF;
162     END IF;
163
164     -- Some org unit settings are protected by a view permission.
165     -- First see if we have any data that needs protecting, then 
166     -- check the permission if needed.
167
168     IF NOT summary.has_org_setting THEN
169         RETURN summary;
170     END IF;
171
172     -- avoid putting the value into the summary until we confirm
173     -- the value should be visible to the caller.
174     SELECT INTO setting_value value 
175         FROM actor.org_unit_ancestor_setting(setting_name, org_id);
176
177     IF NOT FOUND THEN
178         -- No value found -- perm check is irrelevant.
179         RETURN summary;
180     END IF;
181
182     IF org_setting_type.view_perm IS NOT NULL THEN
183
184         IF user_id IS NULL THEN
185             RAISE NOTICE 'Perm check required but no user_id provided';
186             RETURN summary;
187         END IF;
188
189         IF NOT permission.usr_has_perm(
190             user_id, (SELECT code FROM permission.perm_list 
191                 WHERE id = org_setting_type.view_perm), org_id) 
192         THEN
193             RAISE NOTICE 'Perm check failed for user % on %',
194                 user_id, org_setting_type.view_perm;
195             RETURN summary;
196         END IF;
197     END IF;
198
199     -- Perm check succeeded or was not necessary.
200     summary.value := setting_value;
201     RETURN summary;
202 END;
203 $FUNC$ LANGUAGE PLPGSQL;
204
205
206 CREATE OR REPLACE FUNCTION actor.get_cascade_setting_batch(
207     setting_names TEXT[], org_id INT, user_id INT, workstation_id INT) 
208     RETURNS SETOF actor.cascade_setting_summary AS
209 $FUNC$
210 -- Returns a row per setting matching the setting name order.  If no 
211 -- value is applied, NULL is returned to retain name-response ordering.
212 DECLARE
213     setting_name TEXT;
214     summary actor.cascade_setting_summary;
215 BEGIN
216     FOREACH setting_name IN ARRAY setting_names LOOP
217         SELECT INTO summary * FROM actor.get_cascade_setting(
218             setting_Name, org_id, user_id, workstation_id);
219         RETURN NEXT summary;
220     END LOOP;
221 END;
222 $FUNC$ LANGUAGE PLPGSQL;
223
224 COMMIT;
225
226
227