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
    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']
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
        $desc->submitters = [];
496
        form_input_text('Internal name<br><small>No spaces</small>', 'name');
497
    }
498
    form_input_text('User-visible name', 'long_name', $desc->long_name);
499
    form_input_text(
500
        'Input file names<br><small>Space-separated</small>',
501
        'input_file_names',
502
        implode(' ', $desc->input_file_names)
503
    );
504
    form_input_text(
505
        'Output file names<br><small>Space-separated</small>',
506
        'output_file_names',
507
        implode(' ', $desc->output_file_names)
508
    );
509
    form_input_text(
510
        'Run at most this many total instances of each job',
511
        'max_total',
512
        $desc->max_total
513
    );
514
    form_input_text(
515
        'Get this many successful instances of each job
516
            <br><small>(subject to the above limit)</small>
517
        ',
518
        'min_nsuccess',
519
        $desc->min_nsuccess
520
    );
521
    form_input_text(
522
        'Max job turnaround time, days',
523
        'max_delay_days',
524
        $desc->max_delay_days
525
    );
526
    form_input_textarea(
527
        'Description<br><small>... of what the app does and of the research goals</small>',
528
        'description',
529
        $desc->description
530
    );
531
    form_select2_multi('Science keywords',
532
        'sci_kw',
533
        keyword_select_options(KW_CATEGORY_SCIENCE),
534
        $desc->sci_kw
535
    );
536
    form_input_text(
537
        'Additional submitters<br><small>(user IDs)</small>',
538
        'submitters',
539
        implode(' ', $desc->submitters)
540
    );
541
    // don't include location keywords;
542
    // various people may submit jobs to this app
543
    form_submit('OK');
544
    form_end();
545
    page_tail();
546
}
547
548
function app_action($user) {
549
    global $buda_root;
550
    $edit_name = get_str('edit_name', true);
551
    $desc = new StdClass;
552
    if ($edit_name) {
553
        // editing existing app
554
        $dir = "$buda_root/$edit_name";
555
        $app_name = $edit_name;
556
        $desc->user_id = get_int('user_id');
557
        $desc->create_time = get_int('create_time');
558
        $app_desc = get_buda_app_desc($app_name);
559
        if (!user_can_manage($user, $app_desc)) {
560
            error_page('no access');
561
        }
562
    } else {
563
        // creating new app
564
        $app_name = get_str('name');
565
        if (!is_valid_filename($app_name)) {
566
            error_page(filename_rules());
567
        }
568
        $dir = "$buda_root/$app_name";
569
        if (file_exists($dir)) {
570
            error_page("App $app_name already exists.");
571
        }
572
        mkdir($dir);
573
        $desc->user_id = $user->id;
574
        $desc->create_time = time();
575
    }
576
    $desc->name = $app_name;
577
    $min_nsuccess = get_int('min_nsuccess');
578
    if ($min_nsuccess <= 0) {
579
        error_page('Must specify a positive number of successful instances.');
580
    }
581
    $max_total = get_int('max_total');
582
    if ($max_total <= 0) {
583
        error_page('Must specify a positive max number of instances.');
584
    }
585
    if ($min_nsuccess > $max_total) {
586
        error_page('Target # of successful instances must be <= max total');
587
    }
588
    $max_delay_days = get_str('max_delay_days');
589
    if (!is_numeric($max_delay_days)) {
590
        error_page('Must specify max delay');
591
    }
592
    $max_delay_days = floatval($max_delay_days);
593
    if ($max_delay_days <= 0) {
594
        error_page('Must specify positive max delay');
595
    }
596
597
    $input_file_names = get_str('input_file_names', true);
598
    if ($input_file_names) {
599
        $input_file_names = explode(' ', $input_file_names);
600
        foreach ($input_file_names as $fname) {
601
            if (!is_valid_filename($fname)) {
602
                error_page("Invalid input file name: ".filename_rules());
603
            }
604
        }
605
    } else {
606
        $input_file_names = [];
607
    }
608
    $output_file_names = get_str('output_file_names', true);
609
    if ($output_file_names) {
610
        $output_file_names = explode(' ', $output_file_names);
611
        foreach ($output_file_names as $fname) {
612
            if (!is_valid_filename($fname)) {
613
                error_page("Invalid output file name: ".filename_rules());
614
            }
615
        }
616
    } else {
617
        $output_file_names = [];
618
    }
619
    $desc->long_name = get_str('long_name');
620
    $desc->input_file_names = $input_file_names;
621
    $desc->output_file_names = $output_file_names;
622
    $desc->min_nsuccess = $min_nsuccess;
623
    $desc->max_total = $max_total;
624
    $desc->max_delay_days = $max_delay_days;
625
    $desc->description = get_str('description');
626
    $desc->sci_kw = array_map('intval', get_array('sci_kw'));
627
    $desc->submitters = [];
628
    $x = get_str('submitters');
629
    if ($x) {
630
        $x = explode(' ', $x);
631
        global $buda_app;
632
        foreach ($x as $id) {
633
            if (!is_numeric($id)) {
634
                error_page('bad user ID');
635
            }
636
            $id = intval($id);
637
            $u = BoincUser::lookup_id($id);
638
            if (!$u) error_page("no user $id");
639
            if (!has_submit_access($u, $buda_app->id)) {
640
                error_page("user $id has no BUDA submit access");
641
            }
642
            $desc->submitters[] = $id;
643
        }
644
    }
645
    file_put_contents("$dir/desc.json", json_encode($desc, JSON_PRETTY_PRINT));
646
647
    create_templates($app_name, $desc, $dir);
648
649
    header("Location: buda.php");
650
}
651
652
function view_file() {
653
    global $buda_root;
654
    $app = get_str('app');
655
    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...
656
    $variant = get_str('variant');
657
    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...
658
    $fname = get_str('fname');
659
    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...
660
    echo "<pre>\n";
661
    $x = file_get_contents("$buda_root/$app/$variant/$fname");
662
    echo htmlspecialchars($x);
663
    echo "</pre>\n";
664
}
665
666
function handle_app_edit() {
667
    global $buda_root;
668
    $name = get_str('name');
669
    app_form(get_buda_app_desc($name));
670
}
671
672
function app_details($user) {
673
    global $buda_root, $manage_access;
674
    $name = get_str('name');
675
    $desc = get_buda_app_desc($name);
676
    if (!$desc) error_page("no desc file $path");
677
    page_head("BUDA app: $desc->long_name");
678
    start_table('table-striped');
679
    row2('Internal name', $desc->name);
680
    $user2 = BoincUser::lookup_id($desc->user_id);
681
    row2('Creator',
682
        sprintf('<a href=show_user.php?userid=%d>%s</a>',
683
            $user2->id,
684
            $user2->name
685
        )
686
    );
687
    row2('Created', date_str($desc->create_time));
688
    row2('Description', $desc->description);
689
    row2('Science keywords', kw_array_to_str($desc->sci_kw));
690
    row2(
691
        'Input filenames:',
692
        implode(',', $desc->input_file_names)
693
    );
694
    row2(
695
        'Output filenames:',
696
        implode(',', $desc->output_file_names)
697
    );
698
    if (!empty($desc->max_total)) {
699
        row2('Max total instances per job:', $desc->max_total);
700
    } else {
701
        row2('Max total instances per job:', '1');
702
    }
703
    if (!empty($desc->min_nsuccess)) {
704
        row2('Target successful instances per job:', $desc->min_nsuccess);
705
    } else {
706
        row2('Target successful instances per job:', '1');
707
    }
708
    if (!empty($desc->max_delay_days)) {
709
        row2('Max job turnaround time, days:', $desc->max_delay_days);
710
    } else {
711
        row2('Max job turnaround time, days:', '7');
712
    }
713
    if ($manage_access && user_can_manage($user, $desc)) {
714
        row2('',
715
            button_text_small(
716
                sprintf('buda.php?action=%s&name=%s', 'app_edit', $desc->name),
717
                'Edit app info'
718
            )
719
        );
720
    }
721
    $vars = get_buda_variants($name);
722
    if ($vars) {
723
        $x = [];
724
        foreach ($vars as $var) {
725
            $x[] = sprintf('<a href=buda.php?action=variant_view&app=%s&variant=%s>%s</a>',
726
                $name, $var, $var
727
            );
728
        }
729
        row2('Variants', implode('<p>', $x));
730
        if ($manage_access && user_can_manage($user, $desc)) {
731
            row2('',
732
                button_text_small(
733
                    "buda.php?action=variant_form&app=$name",
734
                    'Add variant'
735
                )
736
            );
737
        }
738
    } else if ($manage_access) {
739
        row2('Variants',
740
            button_text_small(
741
                "buda.php?action=variant_form&app=$name",
742
                'Add variant'
743
            )
744
        );
745
        row2('',
746
            button_text(
747
                "buda.php?action=app_delete&app=$name",
748
                "Delete app",
749
                null,
750
                'btn btn-xs btn-warning'
751
            )
752
        );
753
    }
754
    end_table();
755
    page_tail();
756
}
757
758
// Users with manage access to BUDA can add/delete apps and variants.
759
// Others can just view.
760
// Might want to refine this at some point
761
762
$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...
763
$buda_app = BoincApp::lookup("name='buda'");
764
if (!$buda_app) error_page('no buda app');
765
$manage_access = has_manage_access($user, $buda_app->id);
766
767
$action = get_str('action', true);
768
switch ($action) {
769
case 'app_edit':
770
    if (!$manage_access) error_page('no access');
771
    handle_app_edit(); break;
772
case 'app_form':
773
    if (!$manage_access) error_page('no access');
774
    app_form(); break;
775
case 'app_action':
776
    if (!$manage_access) error_page('no access');
777
    app_action($user); break;
778
case 'app_details':
779
    app_details($user); break;
780
case 'app_delete':
781
    if (!$manage_access) error_page('no access');
782
    app_delete(); break;
783
case 'variant_view':
784
    variant_view($user); break;
785
case 'variant_form':
786
    if (!$manage_access) error_page('no access');
787
    variant_form($user); break;
788
case 'variant_action':
789
    if (!$manage_access) error_page('no access');
790
    variant_action($user);
791
    break;
792
case 'variant_delete':
793
    if (!$manage_access) error_page('no access');
794
    variant_delete();
795
    break;
796
case 'view_file':
797
    view_file(); break;
798
case null:
799
    app_list(); break;
800
default:
801
    error_page("unknown action");
802
}
803
804
?>
805