1<?php
2// This file is part of BOINC.
3// http://boinc.berkeley.edu
4// Copyright (C) 2014 University of California
5//
6// BOINC is free software; you can redistribute it and/or modify it
7// under the terms of the GNU Lesser General Public License
8// as published by the Free Software Foundation,
9// either version 3 of the License, or (at your option) any later version.
10//
11// BOINC is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14// See the GNU Lesser General Public License for more details.
15//
16// You should have received a copy of the GNU Lesser General Public License
17// along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
18
19
20// functions for display and editing global preferences.
21// Preferences are represented in two ways:
22// - As a PHP structure (usually called $prefs)
23//   This has fields run_if_user_active, etc.
24// - As XML (usually called $prefs_xml)
25//
26// This XML has the general structure
27// <global_preferences>
28//    <mod_time>...</mod_time>
29//    <run_if_user_active/>
30//    <work_buf_min_days>1.3</work_buf_min_days>
31//    ...
32//    <venue name="home">
33//       <run_if_user_active/>
34//       ...
35//    </venue>
36// </global_preferences>
37//
38
39// Various functions are defined below for converting between these forms,
40// and also to/from HTML form elements
41
42include_once("../inc/prefs_util.inc");
43include_once("../inc/translation.inc");
44
45global $cpu_prefs;
46global $disk_prefs;
47global $mem_prefs;
48global $net_prefs;
49
50$cpu_prefs = array(
51    tra("Usage limits"),
52    new PREF_NUM(
53        tra("Use at most"),
54        // xgettext:no-php-format
55        tra("Keep some CPUs free for other applications. Example: 75% means use 6 cores on an 8-core CPU."),
56        "max_ncpus_pct",
57        // xgettext:no-php-format
58        new NUM_SPEC(tra("% of the CPUs"), 1, 100, 100)
59    ),
60    new PREF_NUM(
61        tra("Use at most"),
62        // xgettext:no-php-format
63        tra("Suspend/resume computing every few seconds to reduce CPU temperature and energy usage. Example: 75% means compute for 3 seconds, wait for 1 second, and repeat."),
64        "cpu_usage_limit",
65        // xgettext:no-php-format
66        new NUM_SPEC(tra("% of CPU time"), 1, 100, 100)
67    ),
68    tra("When to suspend"),
69    new PREF_BOOL(
70        tra("Suspend when computer is on battery"),
71        tra("Check this to suspend computing on portables when running on battery power."),
72        "run_on_batteries",
73        false, true
74    ),
75    new PREF_BOOL(
76        tra("Suspend when computer is in use"),
77        tra("Check this to suspend computing and file transfers when you're using the computer."),
78        "run_if_user_active",
79        true, true
80    ),
81    new PREF_BOOL(
82        tra("Suspend GPU computing when computer is in use"),
83        tra("Check this to suspend GPU computing when you're using the computer."),
84        "run_gpu_if_user_active",
85        false, true
86    ),
87    new PREF_NUM(
88        tra("'In use' means mouse/keyboard input in last"),
89        tra("This determines when the computer is considered 'in use'."),
90        "idle_time_to_run",
91        new NUM_SPEC(tra("minutes"), 1, 9999, 3),
92        true
93    ),
94    new PREF_OPT_NUM(
95        tra("Suspend when no mouse/keyboard input in last"),
96        tra("This allows some computers to enter low-power mode when not in use."),
97        "suspend_if_no_recent_input",
98        new NUM_SPEC(tra("minutes"), 0, 9999, 0, 1, 60)
99    ),
100    new PREF_OPT_NUM(
101        tra("Suspend when non-BOINC CPU usage is above"),
102        tra("Suspend computing when your computer is busy running other programs."),
103        "suspend_cpu_usage",
104        new NUM_SPEC("%", 0, 100, 25)
105    ),
106    new PREF_HOUR_RANGE(
107        tra("Compute only between"),
108        tra("Compute only during a particular period each day."),
109        "start_hour", "end_hour"
110    ),
111    tra("Other"),
112    new PREF_NUM(
113        tra("Store at least"),
114        tra("Store at least enough tasks to keep the computer busy for this long."),
115        "work_buf_min_days",
116        new NUM_SPEC(tra("days of work"), 0, 10, .1)
117    ),
118    new PREF_NUM(
119        tra("Store up to an additional"),
120        tra("Store additional tasks above the minimum level.  Determines how much work is requested when contacting a project."),
121        "work_buf_additional_days",
122        new NUM_SPEC(tra("days of work"), 0, 10, .5)
123    ),
124    new PREF_NUM(
125        tra("Switch between tasks every"),
126        tra("If you run several projects, BOINC may switch between them this often."),
127        "cpu_scheduling_period_minutes",
128        new NUM_SPEC(tra("minutes"), 1, 9999, 60)
129    ),
130    new PREF_NUM(
131        tra("Request tasks to checkpoint at most every"),
132        tra("This controls how often tasks save their state to disk, so that later they can be continued from that point."),
133        "disk_interval",
134        new NUM_SPEC(tra("seconds"), 0, 9999999, 60)
135    ),
136);
137
138$dp = get_disk_space_config();
139
140$disk_prefs = array(
141    new PREF_OPT_NUM(
142        tra("Use no more than"),
143        tra("Limit the total amount of disk space used by BOINC."),
144        "disk_max_used_gb",
145        new NUM_SPEC(tra("GB"), 0, 9999999, $dp->disk_max_used_gb, 1, 100)
146    ),
147    new PREF_OPT_NUM(
148        tra("Leave at least"),
149        tra("Limit disk usage to leave this much free space on the volume where BOINC stores data."),
150        "disk_min_free_gb",
151        new NUM_SPEC(tra("GB free"), 0, 9999999, $dp->disk_min_free_gb, 1, 1)
152    ),
153    new PREF_OPT_NUM(
154        tra("Use no more than"),
155        tra("Limit the percentage of disk space used by BOINC on the volume where it stores data."),
156        "disk_max_used_pct",
157        // xgettext:no-php-format
158        new NUM_SPEC(tra("% of total"), 0, 100, $dp->disk_max_used_pct)
159    ),
160);
161
162$mem_prefs = array(
163    new PREF_NUM(
164        tra("When computer is in use, use at most"),
165        tra("Limit the memory used by BOINC when you're using the computer."),
166        "ram_max_used_busy_pct",
167        // xgettext:no-php-format
168        new NUM_SPEC(tra("%"), 1, 100, 50)
169    ),
170    new PREF_NUM(
171        tra("When computer is not in use, use at most"),
172        tra("Limit the memory used by BOINC when you're not using the computer."),
173        "ram_max_used_idle_pct",
174        // xgettext:no-php-format
175        new NUM_SPEC(tra("%"), 1, 100, 90)
176    ),
177    new PREF_BOOL(
178        tra("Leave non-GPU tasks in memory while suspended"),
179        tra("If checked, suspended tasks stay in memory, and resume with no work lost. If unchecked, suspended tasks are removed from memory, and resume from their last checkpoint."),
180        "leave_apps_in_memory",
181        false
182    ),
183    new PREF_NUM(
184        tra("Page/swap file: use at most"),
185        tra("Limit the swap space (page file) used by BOINC."),
186        "vm_max_used_pct",
187        // xgettext:no-php-format
188        new NUM_SPEC(tra("%"), 1, 100, 75)
189    ),
190);
191
192$net_prefs = array(
193    tra("Usage limits"),
194    new PREF_OPT_NUM(
195        tra("Limit download rate to"),
196        tra("Limit the download rate of file transfers."),
197        "max_bytes_sec_down",
198        new NUM_SPEC(tra("KB/second"), 0, 9999999, 0, 1024, 100)
199    ),
200    new PREF_OPT_NUM(
201        tra("Limit upload rate to"),
202        tra("Limit the upload rate of file transfers."),
203        "max_bytes_sec_up",
204        new NUM_SPEC(tra("KB/second"), 0, 9999999, 0, 1024, 100)
205    ),
206    new PREF_NUM2(
207        tra("Limit usage to"),
208        tra("Example: BOINC should transfer at most 2000 MB of data every 30 days."),
209        "daily_xfer_limit_mb",
210        "daily_xfer_period_days",
211        new NUM_SPEC(tra("MB every"), 0, 9999999, 0, 1, 10000),
212        new NUM_SPEC(tra("days"), 0, 9999999, 0, 1, 30)
213    ),
214    tra("When to suspend"),
215    new PREF_HOUR_RANGE(
216        tra("Transfer files only between"),
217        tra("Transfer files only during a particular period each day."),
218        "net_start_hour", "net_end_hour"
219    ),
220    tra("Other"),
221    new PREF_BOOL(
222        tra("Skip data verification for image files"),
223        tra("Check this only if your Internet provider modifies image files. Skipping verification reduces the security of BOINC."),
224        "dont_verify_images",
225        false
226    ),
227    new PREF_BOOL(
228        tra("Confirm before connecting to Internet"),
229        tra("Useful only if you have a modem, ISDN or VPN connection."),
230        "confirm_before_connecting",
231        false
232    ),
233    new PREF_BOOL(
234        tra("Disconnect when done"),
235        tra("Useful only if you have a modem, ISDN or VPN connection."),
236        "hangup_if_dialed",
237        false
238    ),
239);
240
241define("CPU_LIMIT_DESC", tra("Computing"));
242define("DISK_LIMIT_DESC", tra("Disk"));
243define("MEM_LIMIT_DESC", tra("Memory"));
244define("NETWORK_LIMIT_DESC", tra("Network"));
245
246// These texts are used in multiple places in prefs_edit.php and add_venue.php
247define("PREFS_FORM_DESC1", tra("These preferences apply to all the BOINC projects in which you participate.")."<br><br>");
248define("PREFS_FORM_ERROR_DESC",
249    tra(
250        "%1Unable to update preferences.%2 The values marked in red below were out of range or not numeric.",
251        "<strong>",
252        "</strong>"
253    ).
254    "<br><br>"
255);
256
257global $text;
258global $parse_result;
259global $top_parse_result;
260global $venue_name;
261
262// get default settings for disk space usage so the default user
263// preferences match the settings used by the scheduler.
264// Defaults are set if the tags are missing, they depend on
265// which scheduler is running:
266// - 'old' has the default hardcoded
267// - 'new' uses config settings
268// if running the old scheduler, set <scheduler_disk_space_check_hardcoded>
269// in config.xml so the right default is set for minimum free disk space
270//
271function get_disk_space_config() {
272    global $config;
273    $config = get_config();
274    $dp = new StdClass;
275    $dp->disk_max_used_gb = parse_config($config, "<default_disk_max_used_gb>");
276    $dp->disk_max_used_pct = parse_config($config, "<default_disk_max_used_pct>");
277    $dp->disk_min_free_gb = parse_config($config, "<default_disk_min_free_gb>");
278    // set some defaults if not found
279    if (!$dp->disk_max_used_gb) $dp->disk_max_used_gb = 0;  // no limit
280    if (!$dp->disk_max_used_pct) $dp->disk_max_used_pct = 90; // 90 percent
281    if (!$dp->disk_min_free_gb) $dp->disk_min_free_gb = 1;   // 1 GB
282    // set mininimum free space scheduler allows
283    // - depends on which scheduler is running
284    $dp->new_sched_flag = 1;
285    $dp->sched_disk_min_free_gb = $dp->disk_min_free_gb;
286    if (parse_config($config, "scheduler_disk_space_check_hardcoded>")) {
287        $dp->new_sched_flag = 0;
288        $dp->sched_disk_min_free_gb = 0;
289    }
290
291    return $dp;
292}
293
294function group_header($t) {
295    echo "<tr><th class=\"bg-info\">$t</th><td class=\"bg-info\" colspan=4><br></td></tr>\n";
296}
297
298// functions to parse preferences XML into a struct
299//
300
301function element_start_global($parser, $name, $attrs) {
302    global $top_parse_result;
303    global $parse_result;
304    global $text;
305    global $venue_name;
306
307    switch($name) {
308    case "venue":
309        if (array_key_exists("name", $attrs)) {
310            $venue_name = $attrs["name"];
311        } else {
312            $venue_name = "home";
313        }
314        $top_parse_result = $parse_result;
315        $parse_result = default_prefs_global();
316        break;
317    }
318    $text = "";
319}
320
321function element_end_global($parser, $name) {
322    global $text;
323    global $parse_result;
324    global $top_parse_result;
325    global $venue_name;
326    global $cpu_prefs;
327    global $disk_prefs;
328    global $mem_prefs;
329    global $net_prefs;
330
331    foreach ($cpu_prefs as $p) {
332        if (is_string($p)) continue;
333        if ($p->xml_parse($parse_result, $name, $text)) {
334            return;
335        }
336    }
337    foreach ($disk_prefs as $p) {
338        if ($p->xml_parse($parse_result, $name, $text)) {
339            return;
340        }
341    }
342    foreach ($mem_prefs as $p) {
343        if ($p->xml_parse($parse_result, $name, $text)) {
344            return;
345        }
346    }
347    foreach ($net_prefs as $p) {
348        if (is_string($p)) continue;
349        if ($p->xml_parse($parse_result, $name, $text)) {
350            return;
351        }
352    }
353    switch($name) {
354    case "venue":
355        $top_parse_result->$venue_name = $parse_result;
356        $parse_result = $top_parse_result;
357        break;
358    case "mod_time":
359        $parse_result->mod_time = $text;
360        break;
361    case "global_preferences":
362        break;
363    default:
364        //echo "Unknown tag: $name\n";
365    }
366}
367
368function char_handler($parser, $x) {
369    global $text;
370    $text = $text.$x;
371}
372
373
374// state of prefs before parsing; defines prefs for new users
375//
376function default_prefs_global() {
377    global $cpu_prefs;
378    global $disk_prefs;
379    global $mem_prefs;
380    global $net_prefs;
381
382    $p = new StdClass;
383    foreach ($cpu_prefs as $pref) {
384        if (is_string($pref)) continue;
385        $pref->set_default($p);
386    }
387    foreach ($disk_prefs as $pref) {
388        $pref->set_default($p);
389    }
390    foreach ($mem_prefs as $pref) {
391        $pref->set_default($p);
392    }
393    foreach ($net_prefs as $pref) {
394        if (is_string($pref)) continue;
395        $pref->set_default($p);
396    }
397    return $p;
398}
399
400// parse prefs from XML to a struct
401//
402
403function prefs_parse_global($prefs_xml) {
404    global $parse_result;
405    $parse_result = default_prefs_global();
406    $xml_parser = xml_parser_create();
407    xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, 0);
408    xml_set_element_handler($xml_parser, "element_start_global", "element_end_global");
409    xml_set_character_data_handler($xml_parser, "char_handler");
410    xml_parse($xml_parser, $prefs_xml, 1);
411    return $parse_result;
412}
413
414// Display all venues as columns next to descriptions
415//
416function prefs_show_columns_global($prefs) {
417    global $cpu_prefs;
418    global $disk_prefs;
419    global $mem_prefs;
420    global $net_prefs;
421
422    row_top(CPU_LIMIT_DESC);
423    foreach ($cpu_prefs as $p) {
424        if (is_string($p)) {
425            group_header($p);
426            continue;
427        }
428        $p->show_cols($prefs);
429    }
430    row_top(DISK_LIMIT_DESC);
431    foreach ($disk_prefs as $p) {
432        $p->show_cols($prefs);
433    }
434    row_top(MEM_LIMIT_DESC);
435    foreach ($mem_prefs as $p) {
436        $p->show_cols($prefs);
437    }
438    row_top(NETWORK_LIMIT_DESC);
439    foreach ($net_prefs as $p) {
440        if (is_string($p)) {
441            group_header($p);
442            continue;
443        }
444        $p->show_cols($prefs);
445    }
446    row_links("global", $prefs);
447}
448
449function prefs_show_global($prefs) {
450    global $cpu_prefs;
451    global $disk_prefs;
452    global $mem_prefs;
453    global $net_prefs;
454
455    row1(CPU_LIMIT_DESC);
456    foreach ($cpu_prefs as $p) {
457        if (is_string($p)) {
458            group_header($p);
459            continue;
460        }
461        $p->show($prefs);
462    }
463    row1(DISK_LIMIT_DESC);
464    foreach ($disk_prefs as $p) {
465        $p->show($prefs);
466    }
467    row1(MEM_LIMIT_DESC);
468    foreach ($mem_prefs as $p) {
469        $p->show($prefs);
470    }
471    row1(NETWORK_LIMIT_DESC);
472    foreach ($net_prefs as $p) {
473        if (is_string($p)) {
474            group_header($p);
475            continue;
476        }
477        $p->show($prefs);
478    }
479}
480
481function subset_name($subset) {
482    if ($subset == "global") return tra("Computing");
483    return PROJECT;
484}
485
486function prefs_display_venue($prefs, $venue, $subset) {
487    global $g_logged_in_user;
488    $tokens = url_tokens($g_logged_in_user->authenticator);
489    $x = false;
490    if (isset($prefs->$venue)) $x = $prefs->$venue;
491
492    if ($x) {
493        start_table();
494        row_heading(tra("Separate preferences for %1", $venue));
495        if ($subset == "global") {
496            prefs_show_global($x);
497        } else {
498            prefs_show_project($x);
499            prefs_show_project_specific($x);
500        }
501        row2("<br>",
502            "<a href=prefs_edit.php?venue=$venue&subset=$subset$tokens>".tra("Edit preferences")."</a>
503            | <a href=prefs_remove.php?venue=$venue&subset=$subset$tokens>".tra("Remove")."</a>
504        ");
505        end_table();
506        echo "</td></tr>\n";
507    } else {
508        echo "<p><a href=add_venue.php?venue=$venue&subset=$subset$tokens>".tra("Add separate preferences for %1", $venue)."</a>";
509    }
510}
511
512function print_prefs_display_global($user, $columns=false) {
513    $global_prefs = prefs_parse_global($user->global_prefs);
514
515    echo tra("These settings apply to all computers using this account except")
516        ."<ul><li>"
517        .tra("computers where you have set preferences locally using the BOINC Manager")
518        ."<li>"
519        .tra("Android devices")
520        ."</ul>
521    ";
522    $switch_link = " <font size=\"-1\"><a href=prefs.php?subset=global&cols=". (int)!$columns .">".tra("(Switch view)")."</a></font>";
523    if ($columns) {
524        echo "<h3>".tra("Combined preferences").$switch_link."</h3>";
525        start_table();
526        prefs_show_columns_global($global_prefs);
527        end_table();
528
529    } else {
530        if (isset($global_prefs->home) || isset($global_prefs->work) || isset($global_prefs->school)) {
531            echo "<h3>".tra("Primary (default) preferences").$switch_link."</h3>";
532        }
533        start_table();
534        prefs_show_global($global_prefs);
535        $tokens = url_tokens($user->authenticator);
536        row2("<br>",
537            "<a href=prefs_edit.php?subset=global$tokens>".tra("Edit preferences")."</a>
538        ");
539        end_table();
540
541        prefs_display_venue($global_prefs, "home", "global");
542        prefs_display_venue($global_prefs, "school", "global");
543        prefs_display_venue($global_prefs, "work", "global");
544    }
545    if (isset($global_prefs->mod_time)) {
546        echo "<p>".tra("Preferences last modified:")." ".pretty_time_str($global_prefs->mod_time)."<p>\n";
547    }
548}
549
550// This functions is used in prefs_edit.php to be able to display
551// the prefs form in case of an error again.
552// $error and $project_error should be an object of the form:
553// $error->idle_time_to_run=true if an error occurred
554// otherwise false
555//
556function print_prefs_form(
557    $action, $subset, $venue, $user, $prefs, $cols, $error=false,
558    $project_error=false
559){
560    if ($action == "add") {
561        $script = "add_venue.php";
562        $submit_value = tra("Add preferences");
563    }
564    if ($action == "edit") {
565        $script = "prefs_edit.php";
566        $submit_value = tra("Update preferences");
567    }
568    echo "<form class=\"form-inline\" action=$script><input type=hidden name=subset value=$subset>
569        ".form_tokens($user->authenticator);
570    if ($venue) {
571        echo "<input type=hidden name=venue value=$venue>\n";
572    }
573    if ($cols) {
574        echo "<input type=hidden name=cols value=$cols>\n";
575    }
576
577    start_table();
578    if ($subset == "global") {
579        prefs_form_global($user, $prefs, $error);
580    } else {
581        prefs_form_project($prefs, $error);
582        if (!$venue) {
583            prefs_form_privacy($user);
584            venue_form($user);
585        }
586        prefs_form_project_specific($prefs->project_specific, $project_error);
587    }
588
589    row2("", "<input class=\"btn btn-primary\" type=submit value=\"$submit_value\" name=\"action\">");
590    end_table();
591    echo "</form>\n";
592}
593
594////////////////////////////////////////////
595//
596// Functions to display preference subsets as forms
597//
598function prefs_form_global($user, $prefs, $error=false) {
599    global $cpu_prefs;
600    global $disk_prefs;
601    global $mem_prefs;
602    global $net_prefs;
603
604    row1(CPU_LIMIT_DESC);
605    foreach ($cpu_prefs as $p) {
606        if (is_string($p)) {
607            group_header($p);
608            continue;
609        }
610        $p->show_form_row($prefs, $error);
611    }
612    row1(DISK_LIMIT_DESC);
613    foreach ($disk_prefs as $p) {
614        $p->show_form_row($prefs, $error);
615    }
616    row1(MEM_LIMIT_DESC);
617    foreach ($mem_prefs as $p) {
618        $p->show_form_row($prefs, $error);
619    }
620    row1(NETWORK_LIMIT_DESC);
621    foreach ($net_prefs as $p) {
622        if (is_string($p)) {
623            group_header($p);
624            continue;
625        }
626        $p->show_form_row($prefs, $error);
627    }
628}
629
630// returns a set of translated yes/no radio buttons for editing prefs forms
631// Example: prefs_form_radio_buttons("allow_beta_work", $user->allow_beta_work);
632//
633// @param string $name name of the radio buttons
634// @param bool $yesno toggles the preset of the buttons; true=yes, false=no
635//
636function prefs_form_radio_buttons($name, $yesno) {
637    $rb = tra("yes")." <input type=radio name=$name value=yes "
638        .($yesno?"checked":"")
639        ."> ".tra("no")." <input type=radio name=$name value=no "
640        .($yesno?"":"checked")
641        .">\n";
642    return $rb;
643}
644
645// TODO: make this a subclass of PREF
646//
647define('VENUE_DESC', tra('Default computer location'));
648define('VENUE_TOOLTIP', tra('New computers will use this location for computing and project preferences.'));
649
650function tooltip_row2($t, $x, $y) {
651    echo "<tr title=\"$t\">
652        <td ".NAME_ATTRS.">$x</td>
653        <td ".VALUE_ATTRS.">$y</td>
654        </tr>
655    ";
656}
657function venue_show($user) {
658    $venue = $user->venue;
659    if ($venue =='') $venue = '---';
660    tooltip_row2(VENUE_TOOLTIP, VENUE_DESC, $venue);
661}
662
663function venue_form($user) {
664    $n=$h=$w=$s=$m='';
665    if ($user->venue == '') $n = 'selected';
666    if ($user->venue == 'home') $h = 'selected';
667    if ($user->venue == 'work') $w = 'selected';
668    if ($user->venue == 'school') $s = 'selected';
669    tooltip_row2(
670        VENUE_TOOLTIP,
671        VENUE_DESC,
672        "<select class=\"form-control input-sm\" name=default_venue>
673        <option value=\"\" $n>---
674        <option value=home $h>".tra("Home")."
675        <option value=work $w>".tra("Work")."
676        <option value=school $s>".tra("School")."
677        </select>
678    ");
679}
680
681function venue_parse_form(&$user) {
682    $user->venue = $_GET['default_venue'];
683}
684
685////////////////////////////////////////////
686//
687// Functions to parse form elements, modifying a preferences structure
688// prefs is preferences object to modify
689// returns an object with errorvalues or false in success case
690//
691function prefs_global_parse_form(&$prefs) {
692    global $cpu_prefs;
693    global $disk_prefs;
694    global $mem_prefs;
695    global $net_prefs;
696
697    $error = false;
698    foreach ($cpu_prefs as $p) {
699        if (is_string($p)) continue;
700        $p->parse_form($prefs, $error);
701    }
702    foreach ($disk_prefs as $p) {
703        $p->parse_form($prefs, $error);
704    }
705    foreach ($mem_prefs as $p) {
706        $p->parse_form($prefs, $error);
707    }
708    foreach ($net_prefs as $p) {
709        if (is_string($p)) continue;
710        $p->parse_form($prefs, $error);
711    }
712    return $error;
713}
714
715
716////////////////////////////////////////////
717//
718// convert prefs from structure to XML
719//
720function global_prefs_make_xml($prefs, $primary=true) {
721    global $cpu_prefs;
722    global $disk_prefs;
723    global $mem_prefs;
724    global $net_prefs;
725
726    $xml = "";
727    if ($primary) {
728        $xml = "<global_preferences>\n";
729        $now = time();
730        $xml = $xml."<mod_time>$now</mod_time>\n";
731    }
732
733    foreach ($cpu_prefs as $p) {
734        if (is_string($p)) continue;
735        $xml .= $p->xml_string($prefs);
736    }
737    foreach ($disk_prefs as $p) {
738        $xml .= $p->xml_string($prefs);
739    }
740    foreach ($mem_prefs as $p) {
741        $xml .= $p->xml_string($prefs);
742    }
743    foreach ($net_prefs as $p) {
744        if (is_string($p)) continue;
745        $xml .= $p->xml_string($prefs);
746    }
747
748    if (isset($prefs->home)) {
749        $xml = $xml."<venue name=\"home\">\n".global_prefs_make_xml($prefs->home, false)."</venue>\n";
750    }
751    if (isset($prefs->work)) {
752        $xml = $xml."<venue name=\"work\">\n".global_prefs_make_xml($prefs->work, false)."</venue>\n";
753    }
754    if (isset($prefs->school)) {
755        $xml = $xml."<venue name=\"school\">\n".global_prefs_make_xml($prefs->school, false)."</venue>\n";
756    }
757    if ($primary) {
758        $xml = $xml."</global_preferences>\n";
759    }
760    return $xml;
761}
762
763////////////////////////////////////////////
764//
765// Update user's prefs in database, from a given structure
766//
767function global_prefs_update(&$user, $prefs) {
768    $prefs_xml = BoincDb::escape_string(global_prefs_make_xml($prefs));
769    $retval = $user->update("global_prefs='$prefs_xml'");
770    if (!$retval) {
771        return 1;
772    }
773    $user->global_prefs = $prefs_xml;
774    return 0;
775}
776
777?>
778