Completed
Push — master ( 29f81b...f2b19b )
by Vitalii
49s queued 16s
created

variant_view()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 45
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 38
nc 6
nop 1
dl 0
loc 45
rs 8.6897
c 0
b 0
f 0
1
<?php
2
// This file is part of BOINC.
3
// https://boinc.berkeley.edu
4
// Copyright (C) 2024 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
// interface for:
20
//      - viewing details of BUDA science apps and variants
21
//      - managing these if user has permission
22
//
23
// in the following, 'app' means BUDA science app
24
// and 'variant' means a variant of one of these (e.g. CPU, GPU)
25
26
require_once('../inc/util.inc');
27
require_once('../inc/sandbox.inc');
28
require_once('../inc/keywords.inc');
29
require_once('../inc/submit_util.inc');
30
require_once('../inc/buda.inc');
31
32
display_errors();
33
34
$buda_root = "../../buda_apps";
35
36
// show list of BUDA apps and variants,
37
// w/ buttons for adding and deleting
38
//
39
function app_list($notice=null) {
40
    global $buda_root;
41
    if (!is_dir($buda_root)) {
42
        mkdir($buda_root);
43
    }
44
    page_head('Manage BUDA apps');
45
    if ($notice) {
46
        echo "$notice <p>\n";
47
    }
48
    text_start();
49
    echo "
50
        <p>BUDA lets you submit Docker jobs using a web interface.
51
        <a href=https://github.com/BOINC/boinc/wiki/BUDA-overview>Learn more</a>.
52
        <p>
53
        <h3>BUDA science apps</h3>
54
    ";
55
56
    $apps = get_buda_apps();
57
    foreach ($apps as $app) {
58
        show_app($app);
59
    }
60
    echo '<hr>';
61
    show_button_small('buda.php?action=app_form', 'Add science app');
62
    text_end();
63
    page_tail();
64
}
65
66
function show_app($app_dir) {
67
    global $buda_root;
68
    $desc = null;
69
    $desc_path = "$buda_root/$app_dir/desc.json";
70
    $desc = json_decode(file_get_contents($desc_path));
71
    echo '<hr>';
72
    echo sprintf('<h3>%s</h3><p>', $desc->long_name);
73
    show_button_small(
74
        sprintf('buda.php?action=app_details&name=%s', $desc->name),
75
        'App details'
76
    );
77
    $var_dirs = get_buda_variants($app_dir);
78
    if ($var_dirs) {
79
        echo "<p>Variants:<ul>";
80
        foreach ($var_dirs as $var_dir) {
81
            $var_desc = get_buda_var_desc($app_dir, $var_dir);
82
            echo sprintf(
83
                '<li><a href=buda.php?action=variant_view&app=%s&variant=%s>%s</a>',
84
                $app_dir, $var_dir, variant_name($var_desc)
85
            );
86
        }
87
        echo '</ul>';
88
    } else {
89
        echo '<p>No variants';
90
    }
91
    echo "<p>";
92
}
93
94
function file_row($app, $variant, $dir, $f) {
95
    [$md5, $size] = parse_info_file("$dir/.md5/$f");
96
    table_row(
97
        "<a href=buda.php?action=view_file&app=$app&variant=$variant&fname=$f>$f</a>",
98
        $size,
99
        $md5
100
    );
101
}
102
103
function variant_view($user) {
104
    global $buda_root, $manage_access;
105
    $app = get_str('app');
106
    if (!is_valid_filename($app)) die('bad arg');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
107
    $app_desc = get_buda_app_desc($app);
108
    $variant = get_str('variant');
109
    if (!is_valid_filename($variant)) die('bad arg');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
110
111
    page_head("BUDA variant");
112
    $dir = "$buda_root/$app/$variant";
113
    $variant_desc = json_decode(file_get_contents("$dir/variant.json"));
114
    start_table('table-striped');
115
    row2("BUDA App", $app);
116
    row2("Variant name", $variant);
117
    row2("CPU type", $variant_desc->cpu_type);
118
    row2("Plan class", $variant_desc->plan_class);
119
    end_table();
120
    echo "<h3>App files</h3>";
121
    start_table();
122
    table_header('Dockerfile', 'size', 'md5');
123
    file_row($app, $variant, $dir, $variant_desc->dockerfile);
124
    table_header('App files', '', '');
125
    foreach ($variant_desc->app_files as $f) {
126
        file_row($app, $variant, $dir, $f);
127
    }
128
    table_header('Auto-generated files', '', '');
129
    file_row($app, $variant, $dir, 'variant.json');
130
    end_table();
131
    echo '<hr>';
132
133
    if ($manage_access && user_can_manage($user, $app_desc)) {
134
        echo '<p>';
135
        show_button_small(
136
            "buda.php?action=variant_form&app=$app&variant=$variant",
137
            'Edit variant'
138
        );
139
        echo '<p>';
140
        show_button(
141
            "buda.php?action=variant_delete&app=$app&variant=$variant",
142
            'Delete variant',
143
            null,
144
            'btn btn-xs btn-warning'
145
        );
146
    }
147
    page_tail();
148
}
149
150
// form for creating an app variant or editing an existing one
151
//
152
function variant_form($user) {
153
    $app = get_str('app');
154
    if (!is_valid_filename($app)) die('bad arg');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
155
156
    // name of variant directory, if we're editing
157
    $variant = get_str('variant', true);
158
159
    $sbitems = sandbox_select_items($user);
160
    if ($variant) {
161
        global $buda_root;
162
        $variant_dir = "$buda_root/$app/$variant";
163
        $variant_desc = json_decode(
164
            file_get_contents("$variant_dir/variant.json")
165
        );
166
        if (!$variant_desc) error_page('no such variant');
167
        page_head_select2("Edit variant $variant of BUDA app $app");
168
    } else {
169
        $variant_desc = new StdClass;
170
        $variant_desc->cpu_type = 'intel';
171
        $variant_desc->plan_class = '';
172
        $variant_desc->dockerfile = '';
173
        $variant_desc->app_files = [];
174
        page_head_select2("Create a variant of BUDA app $app");
175
    }
176
    echo "
177
        Details are <a href=https://github.com/BOINC/boinc/wiki/BUDA-job-submission#adding-a-variant>here</a>.
178
    ";
179
    $sb = '<br><small>From your <a href=sandbox.php>file sandbox</a></small>';
180
    $pc = '<br><small>Specify
181
    <a href=https://github.com/BOINC/boinc/wiki/AppPlan>GPU and other host requirements</a>.<br>Leave blank if none.</small>';
182
    form_start('buda.php');
183
    form_input_hidden('app', $app);
184
    form_input_hidden('action', 'variant_action');
185
    if ($variant) {
186
        // can't change CPU type of existing variant
187
        form_input_hidden('variant', $variant);
188
        form_input_hidden('edit', 'true');
189
        $x = explode('_', $variant);
190
        $cpu_type = $x[0];
191
        form_input_hidden('cpu_type', $cpu_type);
192
    } else {
193
        form_radio_buttons(
194
            'CPU type', 'cpu_type',
195
            [
196
                ['intel', 'Intel'],
197
                ['arm', 'ARM']
0 ignored issues
show
Coding Style introduced by
There should be a trailing comma after the last value of an array declaration.
Loading history...
198
            ],
199
            $variant_desc->cpu_type
200
        );
201
    }
202
    form_input_text("Plan class$pc", 'plan_class', $variant_desc->plan_class);
203
    form_select("Dockerfile$sb", 'dockerfile', $sbitems, $variant_desc->dockerfile);
204
    form_select2_multi("Application files$sb", 'app_files', $sbitems, $variant_desc->app_files);
205
    form_submit('OK');
206
    form_end();
207
    page_tail();
208
}
209
210
function buda_file_phys_name($app, $variant, $md5) {
211
    return sprintf('buda_%s_%s_%s', $app, $variant, $md5);
212
}
213
214
// copy file from sandbox to variant dir, and stage to download hier
215
// return physical name, md5, and size
216
//
217
function copy_and_stage_file($user, $fname, $variant_dir, $app, $variant) {
218
    copy_sandbox_file($user, $fname, $variant_dir);
219
    [$md5, $size] = parse_info_file("$variant_dir/.md5/$fname");
220
    $phys_name = buda_file_phys_name($app, $variant, $md5);
221
    stage_file_aux("$variant_dir/$fname", $md5, $size, $phys_name);
222
    return [$phys_name, $md5, $size];
223
}
224
225
// create templates and put them in app dir
226
//
227
function create_templates($app, $desc, $dir) {
228
    // input template
229
    //
230
    $x = "<input_template>\n";
231
    $ninfiles = count($desc->input_file_names);
232
    for ($i=0; $i<$ninfiles; $i++) {
233
        $x .= "   <file_info>\n      <no_delete/>\n   </file_info>\n";
234
    }
235
    $x .= "   <workunit>\n";
236
    foreach ($desc->input_file_names as $fname) {
237
        $x .= file_ref_in($fname);
238
    }
239
240
    // replication params
241
    //
242
    $x .= sprintf("      <target_nresults>%d</target_nresults>\n",
243
        $desc->min_nsuccess
244
    );
245
    $x .= sprintf("      <min_quorum>%d</min_quorum>\n",
246
        $desc->min_nsuccess
247
    );
248
    $x .= sprintf("      <max_total_results>%d</max_total_results>\n",
249
        $desc->max_total
250
    );
251
252
    $x .= sprintf("      <max_delay>%f</max_delay>\n",
253
        $desc->max_delay_days * 86400.
254
    );
255
256
    $x .= "      <buda_app_name>$app</buda_app_name>\n";
257
    $x .= "   </workunit>\n</input_template>\n";
258
    file_put_contents("$dir/template_in", $x);
259
260
    // output template
261
    //
262
    $x = "<output_template>\n";
263
    $i = 0;
264
    foreach ($desc->output_file_names as $fname) {
265
        $x .= file_info_out($i++);
266
    }
267
    $x .= "   <result>\n";
268
    $i = 0;
269
    foreach ($desc->output_file_names as $fname) {
270
        $x .= file_ref_out($i++, $fname);
271
    }
272
    $x .= "   </result>\n</output_template>\n";
273
    file_put_contents("$dir/template_out", $x);
274
}
275
276
// return <file_info> and <file_ref> elements for given file
277
//
278
function file_xml_elements($log_name, $phys_name, $md5, $nbytes) {
279
    static $download_dir, $download_url, $fanout;
280
    if ($download_dir == null) {
281
        $download_dir = parse_element(get_config(), "<download_dir>");
282
        $download_url = parse_element(get_config(), "<download_url>");
283
        $fanout = (int)(parse_element(get_config(), "<uldl_dir_fanout>"));
284
    }
285
    $file_info = sprintf(
286
"<file_info>
287
    <sticky/>
288
    <no_delete/>
289
    <executable/>
290
    <name>%s</name>
291
    <url>%s/%s/%s</url>
292
    <md5_cksum>%s</md5_cksum>
293
    <nbytes>%d</nbytes>
294
</file_info>
295
",
296
        $phys_name,
297
        $download_url, filename_hash($phys_name, $fanout), $phys_name,
298
        $md5,
299
        $nbytes
300
    );
301
    $file_ref = sprintf(
302
"<file_ref>
303
    <file_name>%s</file_name>
304
    <open_name>%s</open_name>
305
    <copy_file/>
306
</file_ref>
307
",
308
        $phys_name,
309
        $log_name
310
    );
311
    return [$file_info, $file_ref];
312
}
313
314
// create or edit variant
315
//
316
function variant_action($user) {
317
    global $buda_root;
318
    $app = get_str('app');
319
    if (!is_valid_filename($app)) die('bad arg');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
320
    $app_desc = get_buda_app_desc($app);
321
    if (!user_can_manage($user, $app_desc)) {
322
        error_page('no access');
323
    }
324
325
    $cpu_type = get_str('cpu_type');
326
    $plan_class = get_str('plan_class');
327
    $variant = get_str('variant', true);
328
    if ($variant) {
329
        $creating = false;
330
    } else {
331
        $creating = true;
332
    }
333
    $variant = sprintf('%s_%s', $cpu_type, $plan_class?$plan_class:'cpu');
334
    if (!is_valid_filename($variant)) {
335
        error_page(filename_rules());
336
    }
337
338
    $dockerfile = get_str('dockerfile');
339
    if (!is_valid_filename($dockerfile)) {
340
        error_page("Invalid dockerfile name: ".filename_rules());
341
    }
342
    $app_files = get_array('app_files');
343
    foreach ($app_files as $fname) {
344
        if (!is_valid_filename($fname)) {
345
            error_page("Invalid app file name: ".filename_rules());
346
        }
347
    }
348
349
    $dir = "$buda_root/$app/$variant";
350
    if ($creating) {
351
        if (file_exists($dir)) {
352
            error_page("Variant '$variant' already exists.");
353
        }
354
    } else {
355
        system("rm -r $dir");
356
    }
357
    mkdir($dir);
358
    mkdir("$dir/.md5");
359
360
    // collect variant params into a struct
361
    //
362
    $desc = new StdClass;
363
    $desc->dockerfile = $dockerfile;
364
    $desc->app_files = $app_files;
365
    $desc->cpu_type = $cpu_type;
366
    $desc->plan_class = $plan_class;
367
    $desc->file_infos = '';
368
    $desc->file_refs = '';
369
370
    // copy dockerfile and app files from sandbox to variant dir,
371
    // and stage them to download dir
372
    //
373
    [$pname, $md5, $nbytes] = copy_and_stage_file(
374
        $user, $dockerfile, $dir, $app, $variant
375
    );
376
    $desc->dockerfile_phys = $pname;
377
    $desc->app_files_phys = [];
378
    [$file_info, $file_ref] = file_xml_elements(
379
        'Dockerfile', $pname, $md5, $nbytes
380
    );
381
    $desc->file_infos .= $file_info;
382
    $desc->file_refs .= $file_ref;
383
384
    foreach ($app_files as $fname) {
385
        [$pname, $md5, $nbytes] = copy_and_stage_file(
386
            $user, $fname, $dir, $app, $variant
387
        );
388
        $desc->app_files_phys[] = $pname;
389
        [$file_info, $file_ref] = file_xml_elements(
390
            $fname, $pname, $md5, $nbytes
391
        );
392
        $desc->file_infos .= $file_info;
393
        $desc->file_refs .= $file_ref;
394
    }
395
396
    // write variant params to a JSON file
397
    //
398
    file_put_contents(
399
        "$dir/variant.json",
400
        json_encode($desc, JSON_PRETTY_PRINT)
401
    );
402
403
    // Note: we don't currently allow indirect file access.
404
    // If we did, we'd need to create job.toml to mount project dir
405
406
    app_list("Variant $variant added for app $app.");
407
}
408
409
function variant_delete() {
410
    global $buda_root;
411
    $app = get_str('app');
412
    if (!is_valid_filename($app)) die('bad arg');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
413
    $variant = get_str('variant');
414
    if (!is_valid_filename($variant)) die('bad arg');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
415
    $confirmed = get_str('confirmed', true);
416
    if ($confirmed) {
417
        $dir = "$buda_root/$app/$variant";
418
        if (!file_exists($dir)) error_page('no such variant');
419
        // delete staged files
420
        //
421
        foreach (scandir("$dir/.md5") as $fname) {
422
            if ($fname[0] == '.') continue;
423
            [$md5, $size] = parse_info_file("$dir/.md5/$fname");
424
            $phys_name = buda_file_phys_name($app, $variant, $md5);
425
            $phys_path = download_hier_path($phys_name);
426
            unlink($phys_path);
427
            unlink("$phys_path.md5");
428
        }
429
        system("rm -r $buda_root/$app/$variant", $ret);
430
        if ($ret) {
431
            error_page("delete failed");
432
        }
433
        $notice = "Variant $variant of app $app removed.";
434
        app_list($notice);
435
    } else {
436
        page_head("Confirm");
437
        echo "<p>Are you sure you want to delete variant $variant of BUDA app $app?  <p>";
438
        show_button(
439
            "buda.php?action=variant_delete&app=$app&variant=$variant&confirmed=yes",
440
            "Yes"
441
        );
442
        page_tail();
443
    }
444
}
445
446
function app_delete() {
447
    global $buda_root;
448
    $app = get_str('app');
449
    if (!is_valid_filename($app)) die('bad arg');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
450
    $confirmed = get_str('confirmed', true);
451
    if ($confirmed) {
452
        $dir = "$buda_root/$app";
453
        if (!file_exists($dir)) error_page('no such app');
454
        $vars = get_buda_variants($app);
455
        if ($vars) {
456
            error_page("You must delete all variants first.");
457
        }
458
        system("rm $buda_root/$app/desc.json", $ret);
459
        system("rmdir $buda_root/$app", $ret);
460
        if ($ret) {
461
            error_page('delete failed');
462
        }
463
        $notice = "App $app removed.";
464
        app_list($notice);
465
    } else {
466
        page_head('Confirm');
467
        echo "<p>Are you sure you want to delete BUDA science app $app?  <p>";
468
        show_button(
469
            "buda.php?action=app_delete&app=$app&confirmed=yes",
470
            "Yes"
471
        );
472
        page_tail();
473
    }
474
}
475
476
function app_form($desc=null) {
477
    page_head_select2($desc?"Edit BUDA app $desc->name":'Create BUDA app');
478
    form_start('buda.php');
479
    form_input_hidden('action', 'app_action');
480
    if ($desc) {
481
        form_input_hidden('edit_name', $desc->name);
482
        form_input_hidden('user_id', $desc->user_id);
483
        form_input_hidden('create_time', $desc->create_time);
484
    } else {
485
        $desc = new StdClass;
486
        $desc->long_name = null;
487
        $desc->input_file_names = [];
488
        $desc->output_file_names = [];
489
        $desc->min_nsuccess = 1;
490
        $desc->max_total = 2;
491
        $desc->max_delay_days = 7;
492
        $desc->description = null;
493
        $desc->sci_kw = null;
494
        $desc->url = null;
495
        form_input_text('Internal name<br><small>No spaces</small>', 'name');
496
    }
497
    form_input_text('User-visible name', 'long_name', $desc->long_name);
498
    form_input_text(
499
        'Input file names<br><small>Space-separated</small>',
500
        'input_file_names',
501
        implode(' ', $desc->input_file_names)
502
    );
503
    form_input_text(
504
        'Output file names<br><small>Space-separated</small>',
505
        'output_file_names',
506
        implode(' ', $desc->output_file_names)
507
    );
508
    form_input_text(
509
        'Run at most this many total instances of each job',
510
        'max_total',
511
        $desc->max_total
512
    );
513
    form_input_text(
514
        'Get this many successful instances of each job
515
            <br><small>(subject to the above limit)</small>
516
        ',
517
        'min_nsuccess',
518
        $desc->min_nsuccess
519
    );
520
    form_input_text(
521
        'Max job turnaround time, days',
522
        'max_delay_days',
523
        $desc->max_delay_days
524
    );
525
    form_input_textarea(
526
        'Description<br><small>... of what the app does and of the research goals</small>',
527
        'description',
528
        $desc->description
529
    );
530
    form_select2_multi('Science keywords',
531
        'sci_kw',
532
        keyword_select_options(KW_CATEGORY_SCIENCE),
533
        $desc->sci_kw
534
    );
535
    form_input_text(
536
        'Additional submitters<br><small>(user IDs)</small>',
537
        'submitters',
538
        implode(' ', $desc->submitters)
539
    );
540
    // don't include location keywords;
541
    // various people may submit jobs to this app
542
    form_submit('OK');
543
    form_end();
544
    page_tail();
545
}
546
547
function app_action($user) {
548
    global $buda_root;
549
    $edit_name = get_str('edit_name', true);
550
    $desc = new StdClass;
551
    if ($edit_name) {
552
        // editing existing app
553
        $dir = "$buda_root/$edit_name";
554
        $app_name = $edit_name;
555
        $desc->user_id = get_int('user_id');
556
        $desc->create_time = get_int('create_time');
557
        $app_desc = get_buda_app_desc($app_name);
558
        if (!user_can_manage($user, $app_desc)) {
559
            error_page('no access');
560
        }
561
    } else {
562
        // creating new app
563
        $app_name = get_str('name');
564
        if (!is_valid_filename($app_name)) {
565
            error_page(filename_rules());
566
        }
567
        $dir = "$buda_root/$app_name";
568
        if (file_exists($dir)) {
569
            error_page("App $app_name already exists.");
570
        }
571
        mkdir($dir);
572
        $desc->user_id = $user->id;
573
        $desc->create_time = time();
574
    }
575
    $desc->name = $app_name;
576
    $min_nsuccess = get_int('min_nsuccess');
577
    if ($min_nsuccess <= 0) {
578
        error_page('Must specify a positive number of successful instances.');
579
    }
580
    $max_total = get_int('max_total');
581
    if ($max_total <= 0) {
582
        error_page('Must specify a positive max number of instances.');
583
    }
584
    if ($min_nsuccess > $max_total) {
585
        error_page('Target # of successful instances must be <= max total');
586
    }
587
    $max_delay_days = get_str('max_delay_days');
588
    if (!is_numeric($max_delay_days)) {
589
        error_page('Must specify max delay');
590
    }
591
    $max_delay_days = floatval($max_delay_days);
592
    if ($max_delay_days <= 0) {
593
        error_page('Must specify positive max delay');
594
    }
595
596
    $input_file_names = get_str('input_file_names', true);
597
    if ($input_file_names) {
598
        $input_file_names = explode(' ', $input_file_names);
599
        foreach ($input_file_names as $fname) {
600
            if (!is_valid_filename($fname)) {
601
                error_page("Invalid input file name: ".filename_rules());
602
            }
603
        }
604
    } else {
605
        $input_file_names = [];
606
    }
607
    $output_file_names = get_str('output_file_names', true);
608
    if ($output_file_names) {
609
        $output_file_names = explode(' ', $output_file_names);
610
        foreach ($output_file_names as $fname) {
611
            if (!is_valid_filename($fname)) {
612
                error_page("Invalid output file name: ".filename_rules());
613
            }
614
        }
615
    } else {
616
        $output_file_names = [];
617
    }
618
    $desc->long_name = get_str('long_name');
619
    $desc->input_file_names = $input_file_names;
620
    $desc->output_file_names = $output_file_names;
621
    $desc->min_nsuccess = $min_nsuccess;
622
    $desc->max_total = $max_total;
623
    $desc->max_delay_days = $max_delay_days;
624
    $desc->description = get_str('description');
625
    $desc->sci_kw = array_map('intval', get_array('sci_kw'));
626
    $x = get_str('submitters');
627
    $x = explode(' ', $x);
628
    $desc->submitters = [];
629
    global $buda_app;
630
    foreach ($x as $id) {
631
        if (!is_numeric($id)) error_page('bad user ID');
632
        $id = intval($id);
633
        $u = BoincUser::lookup_id($id);
634
        if (!$u) error_page("no user $id");
635
        if (!has_submit_access($u, $buda_app->id)) {
636
            error_page("user $id has no BUDA submit access");
637
        }
638
        $desc->submitters[] = $id;
639
    }
640
    file_put_contents("$dir/desc.json", json_encode($desc, JSON_PRETTY_PRINT));
641
642
    create_templates($app_name, $desc, $dir);
643
644
    header("Location: buda.php");
645
}
646
647
function view_file() {
648
    global $buda_root;
649
    $app = get_str('app');
650
    if (!is_valid_filename($app)) die('bad arg');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
651
    $variant = get_str('variant');
652
    if (!is_valid_filename($variant)) die('bad arg');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
653
    $fname = get_str('fname');
654
    if (!is_valid_filename($fname)) die('bad arg');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
655
    echo "<pre>\n";
656
    $x = file_get_contents("$buda_root/$app/$variant/$fname");
657
    echo htmlspecialchars($x);
658
    echo "</pre>\n";
659
}
660
661
function handle_app_edit() {
662
    global $buda_root;
663
    $name = get_str('name');
664
    app_form(get_buda_app_desc($name));
665
}
666
667
function app_details($user) {
668
    global $buda_root, $manage_access;
669
    $name = get_str('name');
670
    $desc = get_buda_app_desc($name);
671
    if (!$desc) error_page("no desc file $path");
672
    page_head("BUDA app: $desc->long_name");
673
    start_table('table-striped');
674
    row2('Internal name', $desc->name);
675
    $user2 = BoincUser::lookup_id($desc->user_id);
676
    row2('Creator',
677
        sprintf('<a href=show_user.php?userid=%d>%s</a>',
678
            $user2->id,
679
            $user2->name
680
        )
681
    );
682
    row2('Created', date_str($desc->create_time));
683
    row2('Description', $desc->description);
684
    row2('Science keywords', kw_array_to_str($desc->sci_kw));
685
    row2(
686
        'Input filenames:',
687
        implode(',', $desc->input_file_names)
688
    );
689
    row2(
690
        'Output filenames:',
691
        implode(',', $desc->output_file_names)
692
    );
693
    if (!empty($desc->max_total)) {
694
        row2('Max total instances per job:', $desc->max_total);
695
    } else {
696
        row2('Max total instances per job:', '1');
697
    }
698
    if (!empty($desc->min_nsuccess)) {
699
        row2('Target successful instances per job:', $desc->min_nsuccess);
700
    } else {
701
        row2('Target successful instances per job:', '1');
702
    }
703
    if (!empty($desc->max_delay_days)) {
704
        row2('Max job turnaround time, days:', $desc->max_delay_days);
705
    } else {
706
        row2('Max job turnaround time, days:', '7');
707
    }
708
    if ($manage_access && user_can_manage($user, $desc)) {
709
        row2('',
710
            button_text_small(
711
                sprintf('buda.php?action=%s&name=%s', 'app_edit', $desc->name),
712
                'Edit app info'
713
            )
714
        );
715
    }
716
    $vars = get_buda_variants($name);
717
    if ($vars) {
718
        $x = [];
719
        foreach ($vars as $var) {
720
            $x[] = sprintf('<a href=buda.php?action=variant_view&app=%s&variant=%s>%s</a>',
721
                $name, $var, $var
722
            );
723
        }
724
        row2('Variants', implode('<p>', $x));
725
        if ($manage_access && user_can_manage($user, $desc)) {
726
            row2('',
727
                button_text_small(
728
                    "buda.php?action=variant_form&app=$name",
729
                    'Add variant'
730
                )
731
            );
732
        }
733
    } else if ($manage_access) {
734
        row2('Variants',
735
            button_text_small(
736
                "buda.php?action=variant_form&app=$name",
737
                'Add variant'
738
            )
739
        );
740
        row2('',
741
            button_text(
742
                "buda.php?action=app_delete&app=$name",
743
                "Delete app",
744
                null,
745
                'btn btn-xs btn-warning'
746
            )
747
        );
748
    }
749
    end_table();
750
    page_tail();
751
}
752
753
// Users with manage access to BUDA can add/delete apps and variants.
754
// Others can just view.
755
// Might want to refine this at some point
756
757
$user = get_logged_in_user();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $user is correct as get_logged_in_user() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
758
$buda_app = BoincApp::lookup("name='buda'");
759
if (!$buda_app) error_page('no buda app');
760
$manage_access = has_manage_access($user, $buda_app->id);
761
762
$action = get_str('action', true);
763
switch ($action) {
764
case 'app_edit':
765
    if (!$manage_access) error_page('no access');
766
    handle_app_edit(); break;
767
case 'app_form':
768
    if (!$manage_access) error_page('no access');
769
    app_form(); break;
770
case 'app_action':
771
    if (!$manage_access) error_page('no access');
772
    app_action($user); break;
773
case 'app_details':
774
    app_details($user); break;
775
case 'app_delete':
776
    if (!$manage_access) error_page('no access');
777
    app_delete(); break;
778
case 'variant_view':
779
    variant_view($user); break;
780
case 'variant_form':
781
    if (!$manage_access) error_page('no access');
782
    variant_form($user); break;
783
case 'variant_action':
784
    if (!$manage_access) error_page('no access');
785
    variant_action($user);
786
    break;
787
case 'variant_delete':
788
    if (!$manage_access) error_page('no access');
789
    variant_delete();
790
    break;
791
case 'view_file':
792
    view_file(); break;
793
case null:
794
    app_list(); break;
795
default:
0 ignored issues
show
Coding Style introduced by
DEFAULT keyword must be indented 4 spaces from SWITCH keyword
Loading history...
Coding Style introduced by
DEFAULT case must have a breaking statement
Loading history...
796
    error_page("unknown action");
797
}
798
799
?>
800