|
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
|
|
|
|