1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
// This file is part of BOINC. |
4
|
|
|
// http://boinc.berkeley.edu |
5
|
|
|
// Copyright (C) 2019 University of California |
6
|
|
|
// |
7
|
|
|
// BOINC is free software; you can redistribute it and/or modify it |
8
|
|
|
// under the terms of the GNU Lesser General Public License |
9
|
|
|
// as published by the Free Software Foundation, |
10
|
|
|
// either version 3 of the License, or (at your option) any later version. |
11
|
|
|
// |
12
|
|
|
// BOINC is distributed in the hope that it will be useful, |
13
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
14
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
15
|
|
|
// See the GNU Lesser General Public License for more details. |
16
|
|
|
// |
17
|
|
|
// You should have received a copy of the GNU Lesser General Public License |
18
|
|
|
// along with BOINC. If not, see <http://www.gnu.org/licenses/>. |
19
|
|
|
|
20
|
|
|
require_once("../inc/util.inc"); |
21
|
|
|
require_once("../inc/keywords.inc"); |
22
|
|
|
require_once("../inc/kw_prefs.inc"); |
23
|
|
|
|
24
|
|
|
// Interactive page for editing keyword prefs |
25
|
|
|
// |
26
|
|
|
// The PHP generates rows for all keywords, and hides the appropriate ones. |
27
|
|
|
// The Javascript hides and unhides rows as needed. |
28
|
|
|
// |
29
|
|
|
// element IDs for keyword K |
30
|
|
|
// |
31
|
|
|
// rowK: the <tr> row element |
32
|
|
|
// textK: the plus or minus sign, or spaces |
33
|
|
|
// radioK: the radio buttons |
34
|
|
|
|
35
|
|
|
// for each kw K |
36
|
|
|
// nprefs(K) = how many descendants of K have nonzero pref |
37
|
|
|
// expanded(K): true if user chose to expand K |
38
|
|
|
// persists if ancestors are contracted; |
39
|
|
|
// doesn't persist if nprefs becomes nonzero |
40
|
|
|
// |
41
|
|
|
// actions: |
42
|
|
|
// click on expand or contract: set or clear expanded(K) |
43
|
|
|
// radio: recompute nprefs for all ancestors |
44
|
|
|
// |
45
|
|
|
// render: |
46
|
|
|
// for each terminal node K |
47
|
|
|
// if nprefs(parent) |
48
|
|
|
// unhide |
49
|
|
|
// else if all ancesters are either nprefs<>0 or expanded |
50
|
|
|
// unhide |
51
|
|
|
// else |
52
|
|
|
// hide |
53
|
|
|
// |
54
|
|
|
// for each nonterminal node K |
55
|
|
|
// if nprefs(K) |
56
|
|
|
// expanded=true |
57
|
|
|
// unhide |
58
|
|
|
// button=none |
59
|
|
|
// else if nprefs(parent) |
60
|
|
|
// unhide |
61
|
|
|
// set button according to expanded |
62
|
|
|
// else if all ancestors are expanded |
63
|
|
|
// set button according to expanded |
64
|
|
|
// else |
65
|
|
|
// hide |
66
|
|
|
|
67
|
|
|
// for each keyword K: |
68
|
|
|
// - generate list of K's children |
69
|
|
|
// |
70
|
|
|
function keyword_setup($uprefs) { |
71
|
|
|
global $job_keywords; |
72
|
|
|
foreach ($job_keywords as $id=>$k) { |
|
|
|
|
73
|
|
|
$job_keywords[$id]->children = array(); |
74
|
|
|
$job_keywords[$id]->expand = 0; |
75
|
|
|
} |
76
|
|
|
foreach ($job_keywords as $id=>$k) { |
|
|
|
|
77
|
|
|
if (!$k->parent) continue; |
78
|
|
|
$job_keywords[$k->parent]->children[] = $id; |
79
|
|
|
} |
80
|
|
|
foreach ($uprefs as $id=>$n) { |
|
|
|
|
81
|
|
|
while (1) { |
82
|
|
|
$id = $job_keywords[$id]->parent; |
83
|
|
|
if (!$id) break; |
84
|
|
|
$job_keywords[$id]->expand = true; |
85
|
|
|
} |
86
|
|
|
} |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
// output a keyword and (recursively) its descendants. |
90
|
|
|
// If its parent is expanded, show it, else hide |
91
|
|
|
// |
92
|
|
|
function generate_html_kw($id, $uprefs) { |
93
|
|
|
global $job_keywords; |
94
|
|
|
$kw = $job_keywords[$id]; |
95
|
|
|
$u = $uprefs[$id]; |
96
|
|
|
$yes_checked = ($u == KW_YES)?"checked":""; |
97
|
|
|
$no_checked = ($u == KW_NO)?"checked":""; |
98
|
|
|
$maybe_checked = ($u == KW_MAYBE)?"checked":""; |
99
|
|
|
|
100
|
|
|
echo sprintf('<tr id="%s" hidden>%s', "row$id", "\n"); |
101
|
|
|
echo sprintf(' <td width=50%% id="%s"></td>%s', "text$id", "\n"); |
102
|
|
|
echo sprintf(' <td><input onclick="radio(%d, 1)" type="radio" name="%s" id="%s" value="%d" %s></td>%s', |
103
|
|
|
$id, "radio$id", "radio$id"."_0", KW_YES, $yes_checked, "\n" |
104
|
|
|
); |
105
|
|
|
echo sprintf(' <td><input onclick="radio(%d, 0)" type="radio" name="%s" id="%s" value="%d" %s></td>%s', |
106
|
|
|
$id, "radio$id", "radio$id"."_1", KW_MAYBE, $maybe_checked, "\n" |
107
|
|
|
); |
108
|
|
|
echo sprintf(' <td><input onclick="radio(%d, -1)" type="radio" name="%s" id="%s" value="%d" %s></td>%s', |
109
|
|
|
$id, "radio$id", "radio$id"."_2", KW_NO, $no_checked, "\n" |
110
|
|
|
); |
111
|
|
|
echo "</tr>\n"; |
112
|
|
|
|
113
|
|
|
foreach ($kw->children as $k) { |
114
|
|
|
generate_html_kw($k, $uprefs); |
115
|
|
|
} |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
function generate_html_category($category, $uprefs) { |
119
|
|
|
global $job_keywords; |
120
|
|
|
row_heading_array(array( |
121
|
|
|
'', |
122
|
|
|
tra('Prefer'), |
123
|
|
|
tra('As needed'), |
124
|
|
|
tra('Never')), |
|
|
|
|
125
|
|
|
null, |
126
|
|
|
'bg-default' |
127
|
|
|
); |
128
|
|
|
foreach ($job_keywords as $id=>$k) { |
|
|
|
|
129
|
|
|
if ($k->category != $category) continue; |
130
|
|
|
if ($k->parent) continue; |
131
|
|
|
generate_html_kw($id, $uprefs); |
132
|
|
|
} |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
function generate_javascript($uprefs) { |
136
|
|
|
global $job_keywords; |
137
|
|
|
echo "<script> |
138
|
|
|
var ids = new Array; |
139
|
|
|
var names = new Array; |
140
|
|
|
var levels = new Array; |
141
|
|
|
var parent = new Array; |
142
|
|
|
var radio_value = new Array; |
143
|
|
|
"; |
144
|
|
|
foreach ($job_keywords as $id=>$k) { |
|
|
|
|
145
|
|
|
$val = $uprefs[$id]; |
146
|
|
|
echo " |
147
|
|
|
names[$id] = '$k->name'; |
148
|
|
|
levels[$id] = $k->level; |
149
|
|
|
parent[$id] = $k->parent; |
150
|
|
|
radio_value[$id] = $val; |
151
|
|
|
ids.push($id); |
152
|
|
|
"; |
153
|
|
|
} |
154
|
|
|
echo sprintf("var nkws = %d;\n", count($job_keywords)); |
155
|
|
|
echo <<<EOT |
|
|
|
|
156
|
|
|
var rows = new Array; |
157
|
|
|
var texts = new Array; |
158
|
|
|
var expanded = new Array; |
159
|
|
|
var terminal = new Array; |
160
|
|
|
var nprefs = new Array; |
161
|
|
|
|
162
|
|
|
// initialize stuff |
163
|
|
|
|
164
|
|
|
for (i=0; i<nkws; i++) { |
165
|
|
|
terminal[ids[i]] = true; |
166
|
|
|
nprefs[ids[i]] = 0; |
167
|
|
|
} |
168
|
|
|
for (i=0; i<nkws; i++) { |
169
|
|
|
var j = ids[i]; |
170
|
|
|
var rowid = "row"+j; |
171
|
|
|
var textid = "text"+j; |
172
|
|
|
rows[j] = document.getElementById(rowid); |
173
|
|
|
texts[j] = document.getElementById(textid); |
174
|
|
|
if (parent[j]) { |
175
|
|
|
terminal[parent[j]] = false; |
176
|
|
|
} |
177
|
|
|
expanded[j] = false; |
178
|
|
|
|
179
|
|
|
if (radio_value[j]) { |
180
|
|
|
k = j; |
181
|
|
|
while (1) { |
182
|
|
|
k = parent[k]; |
183
|
|
|
if (!k) break; |
184
|
|
|
nprefs[k]++; |
185
|
|
|
} |
186
|
|
|
} |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
// Firefox doesn't set radio buttons correctly. |
190
|
|
|
// |
191
|
|
|
for (i=0; i<nkws; i++) { |
192
|
|
|
var j = ids[i]; |
193
|
|
|
if (!terminal[j]) continue; |
194
|
|
|
for (k=0; k<3; k++) { |
195
|
|
|
id = "radio"+j+"_"+k; |
196
|
|
|
r = document.getElementById(id); |
197
|
|
|
//console.log(id, r); |
198
|
|
|
r.checked= (k == 1-radio_value[j]); |
199
|
|
|
} |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
var font_size = [120, 108, 92, 80]; |
203
|
|
|
var indent = [0, 1.3, 2.6, 3.9]; |
204
|
|
|
var button_indent = 1.0; |
205
|
|
|
|
206
|
|
|
// -1: show "contract" button |
207
|
|
|
// 0: no button |
208
|
|
|
// 1: show "expand" button |
209
|
|
|
// |
210
|
|
|
function set_expand(k, val) { |
211
|
|
|
//console.log('set_expand ', k, val); |
212
|
|
|
var level = levels[k]; |
213
|
|
|
var x = '<span style="font-size:'+font_size[level]+'%">'; |
214
|
|
|
x += '<span style="padding-left:'+indent[level]+'em"/>'; |
215
|
|
|
if (val < 0) { |
216
|
|
|
x += '<a onclick="expand_contract('+k+')" id="t'+k+'" href=#/>⊟</a> '; |
217
|
|
|
} else if (val == 0) { |
218
|
|
|
x += '<span style="padding-left:'+button_indent+'em"/>'; |
219
|
|
|
} else { |
220
|
|
|
x += '<a onclick="expand_contract('+k+')" id="t'+k+'" href=#/>⊞</a> '; |
221
|
|
|
} |
222
|
|
|
x += names[k]; |
223
|
|
|
texts[k].innerHTML = x; |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
function radio(k, val) { |
227
|
|
|
//console.log('radio ', k, val, parent[k]); |
228
|
|
|
old_val = radio_value[k]; |
229
|
|
|
radio_value[k] = val; |
230
|
|
|
inc = 0; |
231
|
|
|
if (val && !old_val) inc = 1; |
232
|
|
|
if (!val && old_val) inc = -1; |
233
|
|
|
if (inc) { |
234
|
|
|
while (1) { |
235
|
|
|
k = parent[k]; |
236
|
|
|
if (!k) break; |
237
|
|
|
nprefs[k] += inc; |
238
|
|
|
} |
239
|
|
|
} |
240
|
|
|
render(); |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
// click on expand/contract link |
244
|
|
|
// |
245
|
|
|
function expand_contract(k) { |
246
|
|
|
expanded[k] = !expanded[k]; |
247
|
|
|
set_expand(k, expanded[k]?-1:1); |
248
|
|
|
var h = expanded[k]?false:true; |
249
|
|
|
//console.log('expand_contract ', k, h); |
250
|
|
|
render(); |
251
|
|
|
return false; |
252
|
|
|
} |
253
|
|
|
|
254
|
|
|
// return true if all ancestrors of i are expanded or nprefs>0 |
255
|
|
|
// |
256
|
|
|
function ancestors_expanded(i) { |
257
|
|
|
while (1) { |
258
|
|
|
i = parent[i]; |
259
|
|
|
if (!i) break; |
260
|
|
|
if (!nprefs[i] && !expanded[i]) return false; |
261
|
|
|
} |
262
|
|
|
return true; |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
function render() { |
266
|
|
|
for (i=0; i<nkws; i++) { |
267
|
|
|
j = ids[i]; |
268
|
|
|
if (terminal[j]) { |
269
|
|
|
set_expand(j, 0); |
270
|
|
|
if (nprefs[parent[j]]>0 || ancestors_expanded(j)) { |
271
|
|
|
rows[j].hidden = false; |
272
|
|
|
} else { |
273
|
|
|
rows[j].hidden = true; |
274
|
|
|
} |
275
|
|
|
} else { |
276
|
|
|
//console.log("nprefs ", j, nprefs[j]); |
277
|
|
|
if (nprefs[j]) { |
278
|
|
|
expanded[j] = true; |
279
|
|
|
rows[j].hidden = false; |
280
|
|
|
set_expand(j, 0); |
281
|
|
|
} else { |
282
|
|
|
p = parent[j]; |
283
|
|
|
if (p) { |
284
|
|
|
if (nprefs[parent[j]]>0 || ancestors_expanded(j)) { |
285
|
|
|
rows[j].hidden = false; |
286
|
|
|
set_expand(j, expanded[j]?-1:1); |
287
|
|
|
} else { |
288
|
|
|
rows[j].hidden = true; |
289
|
|
|
} |
290
|
|
|
} else { |
291
|
|
|
rows[j].hidden = false; |
292
|
|
|
set_expand(j, expanded[j]?-1:1); |
293
|
|
|
} |
294
|
|
|
} |
295
|
|
|
} |
296
|
|
|
} |
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
render(); |
300
|
|
|
|
301
|
|
|
EOT; |
302
|
|
|
echo "</script>\n"; |
303
|
|
|
} |
304
|
|
|
|
305
|
|
|
function prefs_edit_form($user, $show_saved) { |
306
|
|
|
global $job_keywords; |
307
|
|
|
|
308
|
|
|
[$yes, $no] = read_kw_prefs($user); |
309
|
|
|
|
310
|
|
|
// convert user prefs to a map id=>-1/0/1 |
311
|
|
|
// |
312
|
|
|
$uprefs = array(); |
313
|
|
|
foreach ($job_keywords as $id=>$kw) { |
|
|
|
|
314
|
|
|
$uprefs[$id] = 0; |
315
|
|
|
} |
316
|
|
|
foreach ($no as $id) { |
317
|
|
|
$uprefs[$id] = KW_NO; |
318
|
|
|
} |
319
|
|
|
foreach ($yes as $id) { |
320
|
|
|
$uprefs[$id] = KW_YES; |
321
|
|
|
} |
322
|
|
|
|
323
|
|
|
keyword_setup($uprefs); |
324
|
|
|
|
325
|
|
|
page_head(tra("Science and location preferences")); |
326
|
|
|
|
327
|
|
|
if ($show_saved) { |
328
|
|
|
echo '<span class="text-success">'.tra("Preferences saved.").'</span><p><p>'; |
329
|
|
|
} |
330
|
|
|
|
331
|
|
|
echo tra("Select science areas and locations you do or don't want to support. Click %1 for more detail, %2 for less. When done, click the Save button at the bottom.", |
332
|
|
|
"<a>⊞</a>", |
333
|
|
|
"<a>⊟</a>" |
334
|
|
|
); |
335
|
|
|
echo " |
336
|
|
|
<p> |
337
|
|
|
"; |
338
|
|
|
form_start("kw_prefs.php"); |
339
|
|
|
form_input_hidden('action', 'submit'); |
340
|
|
|
echo "<h3>".tra("Science areas")."</h3>\n"; |
341
|
|
|
start_table("table-striped"); |
342
|
|
|
generate_html_category(KW_CATEGORY_SCIENCE, $uprefs); |
343
|
|
|
end_table(); |
344
|
|
|
echo "<h3>".tra("Locations")."</h3>\n"; |
345
|
|
|
start_table("table-striped"); |
346
|
|
|
generate_html_category(KW_CATEGORY_LOC, $uprefs); |
347
|
|
|
end_table(); |
348
|
|
|
echo sprintf( |
349
|
|
|
'<button type="submit" %s class="btn">%s</button>', |
350
|
|
|
button_style(), |
351
|
|
|
tra("Save") |
352
|
|
|
); |
353
|
|
|
form_end(); |
354
|
|
|
generate_javascript($uprefs); |
355
|
|
|
page_tail(); |
356
|
|
|
} |
357
|
|
|
|
358
|
|
|
function prefs_edit_action($user) { |
359
|
|
|
global $job_keywords; |
360
|
|
|
$yes = []; |
361
|
|
|
$no = []; |
362
|
|
|
foreach ($job_keywords as $id=>$kw) { |
|
|
|
|
363
|
|
|
$name = "radio$id"; |
364
|
|
|
$val = get_int($name, true); |
365
|
|
|
switch ($val) { |
366
|
|
|
case KW_NO: |
367
|
|
|
$no[] = $id; |
368
|
|
|
break; |
369
|
|
|
case KW_YES: |
370
|
|
|
$yes[] = $id; |
371
|
|
|
break; |
372
|
|
|
} |
373
|
|
|
} |
374
|
|
|
write_kw_prefs($user, $yes, $no); |
375
|
|
|
} |
376
|
|
|
|
377
|
|
|
$user = get_logged_in_user(); |
|
|
|
|
378
|
|
|
$action = get_str('action', true); |
379
|
|
|
|
380
|
|
|
if ($action == "submit") { |
381
|
|
|
prefs_edit_action($user); |
382
|
|
|
prefs_edit_form($user, true); |
383
|
|
|
} else { |
384
|
|
|
prefs_edit_form($user, false); |
385
|
|
|
} |
386
|
|
|
|
387
|
|
|
?> |
388
|
|
|
|