app_action()   F
last analyzed

Complexity

Conditions 21
Paths 1536

Size

Total Lines 102
Code Lines 77

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 21
eloc 77
nc 1536
nop 1
dl 0
loc 102
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
    if (!$desc) {
72
        echo "$app_dir: No desc.json file";
73
        return;
74
    }
75
    echo '<hr>';
76
    echo sprintf('<h3>%s</h3><p>', $desc->long_name);
77
    show_button_small(
78
        sprintf('buda.php?action=app_details&name=%s', $desc->name),
79
        'App details'
80
    );
81
    $var_dirs = get_buda_variants($app_dir);
82
    if ($var_dirs) {
83
        echo "<p>Variants:<ul>";
84
        foreach ($var_dirs as $var_dir) {
85
            $var_desc = get_buda_var_desc($app_dir, $var_dir);
86
            echo sprintf(
87
                '<li><a href=buda.php?action=variant_view&app=%s&variant=%s>%s</a>',
88
                $app_dir, $var_dir, variant_name($var_desc)
89
            );
90
        }
91
        echo '</ul>';
92
    } else {
93
        echo '<p>No variants';
94
    }
95
    echo "<p>";
96
}
97
98
function file_row($app, $variant, $dir, $f) {
99
    [$md5, $size] = parse_info_file("$dir/.md5/$f");
100
    table_row(
101
        "<a href=buda.php?action=view_file&app=$app&variant=$variant&fname=$f>$f</a>",
102
        $size,
103
        $md5
104
    );
105
}
106
107
function variant_view($user) {
108
    global $buda_root, $manage_access;
109
    $app = get_str('app');
110
    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...
111
    $app_desc = get_buda_app_desc($app);
112
    $variant = get_str('variant');
113
    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...
114
115
    page_head("BUDA variant");
116
    $dir = "$buda_root/$app/$variant";
117
    $variant_desc = json_decode(file_get_contents("$dir/variant.json"));
118
    start_table('table-striped');
119
    row2("BUDA App", $app);
120
    row2("Variant name", $variant);
121
    row2("CPU type", $variant_desc->cpu_type);
122
    row2("Plan class", $variant_desc->plan_class);
123
    end_table();
124
    echo "<h3>App files</h3>";
125
    start_table();
126
    table_header('Dockerfile', 'size', 'md5');
127
    file_row($app, $variant, $dir, $variant_desc->dockerfile);
128
    table_header('App files', '', '');
129
    foreach ($variant_desc->app_files as $f) {
130
        file_row($app, $variant, $dir, $f);
131
    }
132
    table_header('Auto-generated files', '', '');
133
    file_row($app, $variant, $dir, 'variant.json');
134
    end_table();
135
    echo '<hr>';
136
137
    if ($manage_access && user_can_manage($user, $app_desc)) {
138
        echo '<p>';
139
        show_button_small(
140
            "buda.php?action=variant_form&app=$app&variant=$variant",
141
            'Edit variant'
142
        );
143
        echo '<p>';
144
        show_button(
145
            "buda.php?action=variant_delete&app=$app&variant=$variant",
146
            'Delete variant',
147
            null,
148
            'btn btn-xs btn-warning'
149
        );
150
    }
151
    page_tail();
152
}
153
154
// form for creating an app variant or editing an existing one
155
//
156
function variant_form($user) {
157
    $app = get_str('app');
158
    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...
159
160
    // name of variant directory, if we're editing
161
    $variant = get_str('variant', true);
162
163
    $sbitems = sandbox_select_items($user);
164
    if ($variant) {
165
        global $buda_root;
166
        $variant_dir = "$buda_root/$app/$variant";
167
        $variant_desc = json_decode(
168
            file_get_contents("$variant_dir/variant.json")
169
        );
170
        if (!$variant_desc) error_page('no such variant');
171
        page_head_select2("Edit variant $variant of BUDA app $app");
172
    } else {
173
        $variant_desc = new StdClass;
174
        $variant_desc->cpu_type = 'intel';
175
        $variant_desc->plan_class = '';
176
        $variant_desc->dockerfile = '';
177
        $variant_desc->app_files = [];
178
        page_head_select2("Create a variant of BUDA app $app");
179
    }
180
    echo "
181
        Details are <a href=https://github.com/BOINC/boinc/wiki/BUDA-job-submission#adding-a-variant>here</a>.
182
    ";
183
    $sb = '<br><small>From your <a href=sandbox.php>file sandbox</a></small>';
184
    $pc = '<br><small>Specify
185
    <a href=https://github.com/BOINC/boinc/wiki/AppPlan>GPU and other host requirements</a>.<br>Leave blank if none.</small>';
186
    form_start('buda.php');
187
    form_input_hidden('app', $app);
188
    form_input_hidden('action', 'variant_action');
189
    if ($variant) {
190
        // can't change CPU type of existing variant
191
        form_input_hidden('variant', $variant);
192
        form_input_hidden('edit', 'true');
193
        $x = explode('_', $variant);
194
        $cpu_type = $x[0];
195
        form_input_hidden('cpu_type', $cpu_type);
196
    } else {
197
        form_radio_buttons(
198
            'CPU type', 'cpu_type',
199
            [
200
                ['intel', 'Intel'],
201
                ['arm', 'ARM']
202
            ],
203
            $variant_desc->cpu_type
204
        );
205
    }
206
    form_input_text("Plan class$pc", 'plan_class', $variant_desc->plan_class);
207
    form_select("Dockerfile$sb", 'dockerfile', $sbitems, $variant_desc->dockerfile);
208
    form_select2_multi("Application files$sb", 'app_files', $sbitems, $variant_desc->app_files);
209
    form_submit('OK');
210
    form_end();
211
    page_tail();
212
}
213
214
function buda_file_phys_name($app, $variant, $md5) {
215
    return sprintf('buda_%s_%s_%s', $app, $variant, $md5);
216
}
217
218
// copy file from sandbox to variant dir, and stage to download hier
219
// return physical name, md5, and size
220
//
221
function copy_and_stage_file($user, $fname, $variant_dir, $app, $variant) {
222
    copy_sandbox_file($user, $fname, $variant_dir);
223
    [$md5, $size] = parse_info_file("$variant_dir/.md5/$fname");
224
    $phys_name = buda_file_phys_name($app, $variant, $md5);
225
    stage_file_aux("$variant_dir/$fname", $md5, $size, $phys_name);
226
    return [$phys_name, $md5, $size];
227
}
228
229
// create templates and put them in app dir
230
//
231
function create_templates($app, $desc, $dir) {
232
    // input template
233
    //
234
    $x = "<input_template>\n";
235
    $ninfiles = count($desc->input_file_names);
236
    for ($i=0; $i<$ninfiles; $i++) {
237
        $x .= "   <file_info>\n      <no_delete/>\n   </file_info>\n";
238
    }
239
    $x .= "   <workunit>\n";
240
    foreach ($desc->input_file_names as $fname) {
241
        $x .= file_ref_in($fname);
242
    }
243
244
    // replication params
245
    //
246
    $x .= sprintf("      <target_nresults>%d</target_nresults>\n",
247
        $desc->min_nsuccess
248
    );
249
    $x .= sprintf("      <min_quorum>%d</min_quorum>\n",
250
        $desc->min_nsuccess
251
    );
252
    $x .= sprintf("      <max_total_results>%d</max_total_results>\n",
253
        $desc->max_total
254
    );
255
256
    $x .= sprintf("      <max_delay>%f</max_delay>\n",
257
        $desc->max_delay_days * 86400.
258
    );
259
260
    $x .= "      <buda_app_name>$app</buda_app_name>\n";
261
    $x .= "   </workunit>\n</input_template>\n";
262
    file_put_contents("$dir/template_in", $x);
263
264
    // output template
265
    //
266
    $x = "<output_template>\n";
267
    $i = 0;
268
    foreach ($desc->output_file_names as $fname) {
269
        $x .= file_info_out($i++);
270
    }
271
    $x .= "   <result>\n";
272
    $i = 0;
273
    foreach ($desc->output_file_names as $fname) {
274
        $x .= file_ref_out($i++, $fname);
275
    }
276
    $x .= "   </result>\n</output_template>\n";
277
    file_put_contents("$dir/template_out", $x);
278
}
279
280
// return <file_info> and <file_ref> elements for given file
281
//
282
function file_xml_elements($log_name, $phys_name, $md5, $nbytes) {
283
    static $download_dir, $download_url, $fanout;
284
    if ($download_dir == null) {
285
        $download_dir = parse_element(get_config(), "<download_dir>");
286
        $download_url = parse_element(get_config(), "<download_url>");
287
        $fanout = (int)(parse_element(get_config(), "<uldl_dir_fanout>"));
288
    }
289
    $file_info = sprintf(
290
"<file_info>
291
    <sticky/>
292
    <no_delete/>
293
    <executable/>
294
    <name>%s</name>
295
    <url>%s/%s/%s</url>
296
    <md5_cksum>%s</md5_cksum>
297
    <nbytes>%d</nbytes>
298
</file_info>
299
",
300
        $phys_name,
301
        $download_url, filename_hash($phys_name, $fanout), $phys_name,
302
        $md5,
303
        $nbytes
304
    );
305
    $file_ref = sprintf(
306
"<file_ref>
307
    <file_name>%s</file_name>
308
    <open_name>%s</open_name>
309
    <copy_file/>
310
</file_ref>
311
",
312
        $phys_name,
313
        $log_name
314
    );
315
    return [$file_info, $file_ref];
316
}
317
318
// create or edit variant
319
//
320
function variant_action($user) {
321
    global $buda_root;
322
    $app = get_str('app');
323
    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...
324
    $app_desc = get_buda_app_desc($app);
325
    if (!user_can_manage($user, $app_desc)) {
326
        error_page('no access');
327
    }
328
329
    $cpu_type = get_str('cpu_type');
330
    $plan_class = get_str('plan_class');
331
    $variant = get_str('variant', true);
332
    if ($variant) {
333
        $creating = false;
334
    } else {
335
        $creating = true;
336
    }
337
    $variant = sprintf('%s_%s', $cpu_type, $plan_class?$plan_class:'cpu');
338
    if (!is_valid_filename($variant)) {
339
        error_page(filename_rules());
340
    }
341
342
    $dockerfile = get_str('dockerfile');
343
    if (!is_valid_filename($dockerfile)) {
344
        error_page("Invalid dockerfile name: ".filename_rules());
345
    }
346
    $app_files = get_array('app_files');
347
    foreach ($app_files as $fname) {
348
        if (!is_valid_filename($fname)) {
349
            error_page("Invalid app file name: ".filename_rules());
350
        }
351
    }
352
353
    $dir = "$buda_root/$app/$variant";
354
    if ($creating) {
355
        if (file_exists($dir)) {
356
            error_page("Variant '$variant' already exists.");
357
        }
358
    } else {
359
        system("rm -r $dir");
360
    }
361
    mkdir($dir);
362
    mkdir("$dir/.md5");
363
364
    // collect variant params into a struct
365
    //
366
    $desc = new StdClass;
367
    $desc->dockerfile = $dockerfile;
368
    $desc->app_files = $app_files;
369
    $desc->cpu_type = $cpu_type;
370
    $desc->plan_class = $plan_class;
371
    $desc->file_infos = '';
372
    $desc->file_refs = '';
373
374
    // copy dockerfile and app files from sandbox to variant dir,
375
    // and stage them to download dir
376
    //
377
    [$pname, $md5, $nbytes] = copy_and_stage_file(
378
        $user, $dockerfile, $dir, $app, $variant
379
    );
380
    $desc->dockerfile_phys = $pname;
381
    $desc->app_files_phys = [];
382
    [$file_info, $file_ref] = file_xml_elements(
383
        'Dockerfile', $pname, $md5, $nbytes
384
    );
385
    $desc->file_infos .= $file_info;
386
    $desc->file_refs .= $file_ref;
387
388
    foreach ($app_files as $fname) {
389
        [$pname, $md5, $nbytes] = copy_and_stage_file(
390
            $user, $fname, $dir, $app, $variant
391
        );
392
        $desc->app_files_phys[] = $pname;
393
        [$file_info, $file_ref] = file_xml_elements(
394
            $fname, $pname, $md5, $nbytes
395
        );
396
        $desc->file_infos .= $file_info;
397
        $desc->file_refs .= $file_ref;
398
    }
399
400
    // write variant params to a JSON file
401
    //
402
    file_put_contents(
403
        "$dir/variant.json",
404
        json_encode($desc, JSON_PRETTY_PRINT)
405
    );
406
407
    // Note: we don't currently allow indirect file access.
408
    // If we did, we'd need to create job.toml to mount project dir
409
410
    app_list("Variant $variant added for app $app.");
411
}
412
413
function variant_delete() {
414
    global $buda_root;
415
    $app = get_str('app');
416
    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...
417
    $variant = get_str('variant');
418
    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...
419
    $confirmed = get_str('confirmed', true);
420
    if ($confirmed) {
421
        $dir = "$buda_root/$app/$variant";
422
        if (!file_exists($dir)) error_page('no such variant');
423
        // delete staged files
424
        //
425
        foreach (scandir("$dir/.md5") as $fname) {
426
            if ($fname[0] == '.') continue;
427
            [$md5, $size] = parse_info_file("$dir/.md5/$fname");
428
            $phys_name = buda_file_phys_name($app, $variant, $md5);
429
            $phys_path = download_hier_path($phys_name);
430
            unlink($phys_path);
431
            unlink("$phys_path.md5");
432
        }
433
        system("rm -r $buda_root/$app/$variant", $ret);
434
        if ($ret) {
435
            error_page("delete failed");
436
        }
437
        $notice = "Variant $variant of app $app removed.";
438
        app_list($notice);
439
    } else {
440
        page_head("Confirm");
441
        echo "<p>Are you sure you want to delete variant $variant of BUDA app $app?  <p>";
442
        show_button(
443
            "buda.php?action=variant_delete&app=$app&variant=$variant&confirmed=yes",
444
            "Yes"
445
        );
446
        page_tail();
447
    }
448
}
449
450
function app_delete() {
451
    global $buda_root;
452
    $app = get_str('app');
453
    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...
454
    $confirmed = get_str('confirmed', true);
455
    if ($confirmed) {
456
        $dir = "$buda_root/$app";
457
        if (!file_exists($dir)) error_page('no such app');
458
        $vars = get_buda_variants($app);
459
        if ($vars) {
460
            error_page("You must delete all variants first.");
461
        }
462
        system("rm $buda_root/$app/desc.json", $ret);
463
        system("rmdir $buda_root/$app", $ret);
464
        if ($ret) {
465
            error_page('delete failed');
466
        }
467
        $notice = "App $app removed.";
468
        app_list($notice);
469
    } else {
470
        page_head('Confirm');
471
        echo "<p>Are you sure you want to delete BUDA science app $app?  <p>";
472
        show_button(
473
            "buda.php?action=app_delete&app=$app&confirmed=yes",
474
            "Yes"
475
        );
476
        page_tail();
477
    }
478
}
479
480
function app_form($desc=null) {
481
    page_head_select2($desc?"Edit BUDA app $desc->name":'Create BUDA app');
482
    form_start('buda.php');
483
    form_input_hidden('action', 'app_action');
484
    if ($desc) {
485
        form_input_hidden('edit_name', $desc->name);
486
        form_input_hidden('user_id', $desc->user_id);
487
        form_input_hidden('create_time', $desc->create_time);
488
    } else {
489
        $desc = new StdClass;
490
        $desc->long_name = null;
491
        $desc->input_file_names = [];
492
        $desc->output_file_names = [];
493
        $desc->min_nsuccess = 1;
494
        $desc->max_total = 2;
495
        $desc->max_delay_days = 7;
496
        $desc->description = null;
497
        $desc->sci_kw = null;
498
        $desc->url = null;
499
        $desc->submitters = [];
500
        form_input_text('Internal name<br><small>No spaces</small>', 'name');
501
    }
502
    form_input_text('User-visible name', 'long_name', $desc->long_name);
503
    form_input_text(
504
        'Input file names<br><small>Space-separated</small>',
505
        'input_file_names',
506
        implode(' ', $desc->input_file_names)
507
    );
508
    form_input_text(
509
        'Output file names<br><small>Space-separated</small>',
510
        'output_file_names',
511
        implode(' ', $desc->output_file_names)
512
    );
513
    form_input_text(
514
        'Run at most this many total instances of each job',
515
        'max_total',
516
        $desc->max_total
517
    );
518
    form_input_text(
519
        'Get this many successful instances of each job
520
            <br><small>(subject to the above limit)</small>
521
        ',
522
        'min_nsuccess',
523
        $desc->min_nsuccess
524
    );
525
    form_input_text(
526
        'Max job turnaround time, days',
527
        'max_delay_days',
528
        $desc->max_delay_days
529
    );
530
    form_input_textarea(
531
        'Description<br><small>... of what the app does and of the research goals</small>',
532
        'description',
533
        $desc->description
534
    );
535
    form_select2_multi('Science keywords',
536
        'sci_kw',
537
        keyword_select_options(KW_CATEGORY_SCIENCE),
538
        $desc->sci_kw
539
    );
540
    form_input_text(
541
        'Additional submitters<br><small>(user IDs)</small>',
542
        'submitters',
543
        implode(' ', $desc->submitters)
544
    );
545
    // don't include location keywords;
546
    // various people may submit jobs to this app
547
    form_submit('OK');
548
    form_end();
549
    page_tail();
550
}
551
552
function app_action($user) {
553
    global $buda_root;
554
    $edit_name = get_str('edit_name', true);
555
    $desc = new StdClass;
556
    if ($edit_name) {
557
        // editing existing app
558
        $dir = "$buda_root/$edit_name";
559
        $app_name = $edit_name;
560
        $desc->user_id = get_int('user_id');
561
        $desc->create_time = get_int('create_time');
562
        $app_desc = get_buda_app_desc($app_name);
563
        if (!user_can_manage($user, $app_desc)) {
564
            error_page('no access');
565
        }
566
    } else {
567
        // creating new app
568
        $app_name = get_str('name');
569
        if (!is_valid_filename($app_name)) {
570
            error_page(filename_rules());
571
        }
572
        $dir = "$buda_root/$app_name";
573
        if (file_exists($dir)) {
574
            error_page("App $app_name already exists.");
575
        }
576
        mkdir($dir);
577
        $desc->user_id = $user->id;
578
        $desc->create_time = time();
579
    }
580
    $desc->name = $app_name;
581
    $min_nsuccess = get_int('min_nsuccess');
582
    if ($min_nsuccess <= 0) {
583
        error_page('Must specify a positive number of successful instances.');
584
    }
585
    $max_total = get_int('max_total');
586
    if ($max_total <= 0) {
587
        error_page('Must specify a positive max number of instances.');
588
    }
589
    if ($min_nsuccess > $max_total) {
590
        error_page('Target # of successful instances must be <= max total');
591
    }
592
    $max_delay_days = get_str('max_delay_days');
593
    if (!is_numeric($max_delay_days)) {
594
        error_page('Must specify max delay');
595
    }
596
    $max_delay_days = floatval($max_delay_days);
597
    if ($max_delay_days <= 0) {
598
        error_page('Must specify positive max delay');
599
    }
600
601
    $input_file_names = get_str('input_file_names', true);
602
    if ($input_file_names) {
603
        $input_file_names = explode(' ', $input_file_names);
604
        foreach ($input_file_names as $fname) {
605
            if (!is_valid_filename($fname)) {
606
                error_page("Invalid input file name: ".filename_rules());
607
            }
608
        }
609
    } else {
610
        $input_file_names = [];
611
    }
612
    $output_file_names = get_str('output_file_names', true);
613
    if ($output_file_names) {
614
        $output_file_names = explode(' ', $output_file_names);
615
        foreach ($output_file_names as $fname) {
616
            if (!is_valid_filename($fname)) {
617
                error_page("Invalid output file name: ".filename_rules());
618
            }
619
        }
620
    } else {
621
        $output_file_names = [];
622
    }
623
    $desc->long_name = get_str('long_name');
624
    $desc->input_file_names = $input_file_names;
625
    $desc->output_file_names = $output_file_names;
626
    $desc->min_nsuccess = $min_nsuccess;
627
    $desc->max_total = $max_total;
628
    $desc->max_delay_days = $max_delay_days;
629
    $desc->description = get_str('description');
630
    $desc->sci_kw = array_map('intval', get_array('sci_kw'));
631
    $desc->submitters = [];
632
    $x = get_str('submitters');
633
    if ($x) {
634
        $x = explode(' ', $x);
635
        global $buda_app;
636
        foreach ($x as $id) {
637
            if (!is_numeric($id)) {
638
                error_page('bad user ID');
639
            }
640
            $id = intval($id);
641
            $u = BoincUser::lookup_id($id);
642
            if (!$u) error_page("no user $id");
643
            if (!has_submit_access($u, $buda_app->id)) {
644
                error_page("user $id has no BUDA submit access");
645
            }
646
            $desc->submitters[] = $id;
647
        }
648
    }
649
    file_put_contents("$dir/desc.json", json_encode($desc, JSON_PRETTY_PRINT));
650
651
    create_templates($app_name, $desc, $dir);
652
653
    header("Location: buda.php");
654
}
655
656
function view_file() {
657
    global $buda_root;
658
    $app = get_str('app');
659
    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...
660
    $variant = get_str('variant');
661
    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...
662
    $fname = get_str('fname');
663
    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...
664
    echo "<pre>\n";
665
    $x = file_get_contents("$buda_root/$app/$variant/$fname");
666
    echo htmlspecialchars($x);
667
    echo "</pre>\n";
668
}
669
670
function handle_app_edit() {
671
    global $buda_root;
672
    $name = get_str('name');
673
    app_form(get_buda_app_desc($name));
674
}
675
676
function app_details($user) {
677
    global $buda_root, $manage_access;
678
    $name = get_str('name');
679
    $desc = get_buda_app_desc($name);
680
    if (!$desc) error_page("no desc file $path");
681
    page_head("BUDA app: $desc->long_name");
682
    start_table('table-striped');
683
    row2('Internal name', $desc->name);
684
    $user2 = BoincUser::lookup_id($desc->user_id);
685
    row2('Creator',
686
        sprintf('<a href=show_user.php?userid=%d>%s</a>',
687
            $user2->id,
688
            $user2->name
689
        )
690
    );
691
    row2('Created', date_str($desc->create_time));
692
    row2('Description', $desc->description);
693
    row2('Science keywords', kw_array_to_str($desc->sci_kw));
694
    row2(
695
        'Input filenames:',
696
        implode(',', $desc->input_file_names)
697
    );
698
    row2(
699
        'Output filenames:',
700
        implode(',', $desc->output_file_names)
701
    );
702
    if (!empty($desc->max_total)) {
703
        row2('Max total instances per job:', $desc->max_total);
704
    } else {
705
        row2('Max total instances per job:', '1');
706
    }
707
    if (!empty($desc->min_nsuccess)) {
708
        row2('Target successful instances per job:', $desc->min_nsuccess);
709
    } else {
710
        row2('Target successful instances per job:', '1');
711
    }
712
    if (!empty($desc->max_delay_days)) {
713
        row2('Max job turnaround time, days:', $desc->max_delay_days);
714
    } else {
715
        row2('Max job turnaround time, days:', '7');
716
    }
717
    if ($manage_access && user_can_manage($user, $desc)) {
718
        row2('',
719
            button_text_small(
720
                sprintf('buda.php?action=%s&name=%s', 'app_edit', $desc->name),
721
                'Edit app info'
722
            )
723
        );
724
    }
725
    $vars = get_buda_variants($name);
726
    if ($vars) {
727
        $x = [];
728
        foreach ($vars as $var) {
729
            $x[] = sprintf('<a href=buda.php?action=variant_view&app=%s&variant=%s>%s</a>',
730
                $name, $var, $var
731
            );
732
        }
733
        row2('Variants', implode('<p>', $x));
734
        if ($manage_access && user_can_manage($user, $desc)) {
735
            row2('',
736
                button_text_small(
737
                    "buda.php?action=variant_form&app=$name",
738
                    'Add variant'
739
                )
740
            );
741
        }
742
    } else if ($manage_access) {
743
        row2('Variants',
744
            button_text_small(
745
                "buda.php?action=variant_form&app=$name",
746
                'Add variant'
747
            )
748
        );
749
        row2('',
750
            button_text(
751
                "buda.php?action=app_delete&app=$name",
752
                "Delete app",
753
                null,
754
                'btn btn-xs btn-warning'
755
            )
756
        );
757
    }
758
    end_table();
759
    page_tail();
760
}
761
762
// Users with manage access to BUDA can add/delete apps and variants.
763
// Others can just view.
764
// Might want to refine this at some point
765
766
$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...
767
$buda_app = BoincApp::lookup("name='buda'");
768
if (!$buda_app) error_page('no buda app');
769
$manage_access = has_manage_access($user, $buda_app->id);
770
771
$action = get_str('action', true);
772
switch ($action) {
773
case 'app_edit':
774
    if (!$manage_access) error_page('no access');
775
    handle_app_edit(); break;
776
case 'app_form':
777
    if (!$manage_access) error_page('no access');
778
    app_form(); break;
779
case 'app_action':
780
    if (!$manage_access) error_page('no access');
781
    app_action($user); break;
782
case 'app_details':
783
    app_details($user); break;
784
case 'app_delete':
785
    if (!$manage_access) error_page('no access');
786
    app_delete(); break;
787
case 'variant_view':
788
    variant_view($user); break;
789
case 'variant_form':
790
    if (!$manage_access) error_page('no access');
791
    variant_form($user); break;
792
case 'variant_action':
793
    if (!$manage_access) error_page('no access');
794
    variant_action($user);
795
    break;
796
case 'variant_delete':
797
    if (!$manage_access) error_page('no access');
798
    variant_delete();
799
    break;
800
case 'view_file':
801
    view_file(); break;
802
case null:
803
    app_list(); break;
804
default:
805
    error_page("unknown action");
806
}
807
808
?>
809