Issues (1963)

html/user/buda.php (14 issues)

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
// scan BUDA apps and variants, and write a file 'buda_plan_classes'
37
// in the project dir with list of plan classes
38
//
39
function write_plan_class_file() {
40
    $pcs = [];
41
    global $buda_root;
42
    $apps = get_buda_apps();
43
    foreach ($apps as $app) {
44
        $vars = get_buda_variants($app);
45
        $pcs = array_merge($pcs, $vars);
46
    }
47
    $pcs = array_unique($pcs);
48
    file_put_contents(
49
        "../../buda_plan_classes",
50
        implode("\n", $pcs)."\n"
51
    );
52
}
53
54
// show list of BUDA apps and variants,
55
// w/ buttons for adding and deleting
56
//
57
function app_list($notice=null) {
58
    global $buda_root;
59
    if (!is_dir($buda_root)) {
60
        mkdir($buda_root);
61
    }
62
    page_head('Manage BUDA apps');
63
    if ($notice) {
64
        echo "$notice <p>\n";
65
    }
66
    text_start();
67
    echo "
68
        <p>BUDA lets you submit Docker jobs using a web interface.
69
        <a href=https://github.com/BOINC/boinc/wiki/BUDA-overview>Learn more</a>.
70
        <p>
71
        <h3>BUDA science apps</h3>
72
    ";
73
74
    $apps = get_buda_apps();
75
    foreach ($apps as $app) {
76
        show_app($app);
77
    }
78
    echo '<hr>';
79
    show_button_small('buda.php?action=app_form', 'Add science app');
80
    text_end();
81
    page_tail();
82
}
83
84
function show_app($dir) {
85
    global $buda_root;
86
    $desc = null;
87
    $desc_path = "$buda_root/$dir/desc.json";
88
    $desc = json_decode(file_get_contents($desc_path));
89
    echo '<hr>';
90
    echo sprintf('<h3>%s</h3>', $desc->long_name);
91
    echo sprintf('<a href=buda.php?action=app_details&name=%s>Details</a>',
92
        $desc->name
93
    );
94
    $vars = get_buda_variants($dir);
95
    if ($vars) {
96
        echo "<p>Variants:<ul>";
97
        foreach ($vars as $var) {
98
            echo sprintf(
99
                '<li><a href=buda.php?action=variant_view&app=%s&variant=%s>%s</a>',
100
                $dir, $var, $var
101
            );
102
        }
103
        echo '</ul>';
104
    } else {
105
        echo '<p>No variants';
106
    }
107
    echo "<p>";
108
}
109
110
function file_row($app, $variant, $dir, $f) {
111
    [$md5, $size] = parse_info_file("$dir/.md5/$f");
112
    table_row(
113
        "<a href=buda.php?action=view_file&app=$app&variant=$variant&fname=$f>$f</a>",
114
        $size,
115
        $md5
116
    );
117
}
118
119
function variant_view() {
120
    global $buda_root, $manage_access;
121
    $app = get_str('app');
122
    if (!is_valid_filename($app)) die('bad arg');
0 ignored issues
show
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...
123
    $variant = get_str('variant');
124
    if (!is_valid_filename($variant)) die('bad arg');
0 ignored issues
show
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...
125
    page_head("BUDA: variant '$variant' of science app '$app'");
126
    $dir = "$buda_root/$app/$variant";
127
    $desc = json_decode(file_get_contents("$dir/variant.json"));
128
    echo "<h3>Files</h3>";
129
    start_table();
130
    table_header('Dockerfile', 'size', 'md5');
131
    file_row($app, $variant, $dir, $desc->dockerfile);
132
    table_header('App files', '', '');
133
    foreach ($desc->app_files as $f) {
134
        file_row($app, $variant, $dir, $f);
135
    }
136
    table_header('Auto-generated files', '', '');
137
    file_row($app, $variant, $dir, 'template_in');
138
    file_row($app, $variant, $dir, 'template_out');
139
    file_row($app, $variant, $dir, 'variant.json');
140
    end_table();
141
    echo '<hr>';
142
143
    start_table();
144
    row2(
145
        'Input filenames:',
146
        implode(',', $desc->input_file_names)
147
    );
148
    row2(
149
        'Output filenames:',
150
        implode(',', $desc->output_file_names)
151
    );
152
    if (!empty($desc->max_total)) {
153
        row2('Max total instances per job:', $desc->max_total);
154
    } else {
155
        row2('Max total instances per job:', '1');
156
    }
157
    if (!empty($desc->min_nsuccess)) {
158
        row2('Target successful instances per job:', $desc->min_nsuccess);
159
    } else {
160
        row2('Target successful instances per job:', '1');
161
    }
162
    if (!empty($desc->max_delay_days)) {
163
        row2('Max job turnaround time, days:', $desc->max_delay_days);
164
    } else {
165
        row2('Max job turnaround time, days:', '7');
166
    }
167
    end_table();
168
169
    if ($manage_access) {
170
        echo '<p>';
171
        show_button_small(
172
            "buda.php?action=variant_form&app=$app&variant=$variant",
173
            'Edit variant'
174
        );
175
        echo '<p>';
176
        show_button(
177
            "buda.php?action=variant_delete&app=$app&variant=$variant",
178
            'Delete variant',
179
            null,
180
            'btn btn-xs btn-warning'
181
        );
182
    }
183
    page_tail();
184
}
185
186
// form for creating/editing app variant.
187
//
188
function variant_form($user) {
189
    $sbitems = sandbox_select_items($user);
190
    $app = get_str('app');
191
    if (!is_valid_filename($app)) die('bad arg');
0 ignored issues
show
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...
192
    $variant = get_str('variant', true);
193
194
    if ($variant) {
195
        global $buda_root;
196
        $variant_dir = "$buda_root/$app/$variant";
197
        $variant_desc = json_decode(
198
            file_get_contents("$variant_dir/variant.json")
199
        );
200
        if (!$variant_desc) error_page('no such variant');
201
        page_head_select2("Edit variant $variant of BUDA app $app");
202
    } else {
203
        $variant_desc = new StdClass;
204
        $variant_desc->dockerfile = '';
205
        $variant_desc->app_files = [];
206
        $variant_desc->input_file_names = [];
207
        $variant_desc->output_file_names = [];
208
        $variant_desc->min_nsuccess = 1;
209
        $variant_desc->max_total = 1;
210
        $variant_desc->max_delay_days = 7;
211
        page_head_select2("Create a variant of BUDA app $app");
212
    }
213
    echo "
214
        Details are <a href=https://github.com/BOINC/boinc/wiki/BUDA-job-submission#adding-a-variant>here</a>.
215
    ";
216
    $sb = '<br><small>From your <a href=sandbox.php>file sandbox</a></small>';
217
    $pc = '<br><small>Specify
218
    <a href=https://github.com/BOINC/boinc/wiki/AppPlan>GPU and other requirements</a>';
219
    form_start('buda.php');
220
    form_input_hidden('app', $app);
221
    form_input_hidden('action', 'variant_action');
222
    if ($variant) {
223
        form_input_hidden('variant', $variant);
224
        form_input_hidden('edit', 'true');
225
    } else {
226
        form_input_text("Plan class$pc", 'variant', $variant);
227
    }
228
    form_select("Dockerfile$sb", 'dockerfile', $sbitems, $variant_desc->dockerfile);
229
    form_select2_multi("Application files$sb", 'app_files', $sbitems, $variant_desc->app_files);
230
    form_input_text(
231
        'Input file names<br><small>Space-separated</small>',
232
        'input_file_names',
233
        implode(' ', $variant_desc->input_file_names)
234
    );
235
    form_input_text(
236
        'Output file names<br><small>Space-separated</small>',
237
        'output_file_names',
238
        implode(' ', $variant_desc->output_file_names)
239
    );
240
    form_input_text(
241
        'Run at most this many total instances of each job',
242
        'max_total',
243
        $variant_desc->max_total
244
    );
245
    form_input_text(
246
        'Get this many successful instances of each job
247
            <br><small>(subject to the above limit)</small>
248
        ',
249
        'min_nsuccess',
250
        $variant_desc->min_nsuccess
251
    );
252
    form_input_text(
253
        'Max job turnaround time, days',
254
        'max_delay_days',
255
        $variant_desc->max_delay_days
256
    );
257
    form_submit('OK');
258
    form_end();
259
    page_tail();
260
}
261
262
function buda_file_phys_name($app, $variant, $md5) {
263
    return sprintf('buda_%s_%s_%s', $app, $variant, $md5);
264
}
265
266
// copy file from sandbox to variant dir, and stage to download hier
267
//
268
function copy_and_stage_file($user, $fname, $dir, $app, $variant) {
269
    copy_sandbox_file($user, $fname, $dir);
270
    [$md5, $size] = parse_info_file("$dir/.md5/$fname");
271
    $phys_name = buda_file_phys_name($app, $variant, $md5);
272
    stage_file_aux("$dir/$fname", $md5, $size, $phys_name);
273
    return $phys_name;
274
}
275
276
// create templates and put them in variant dir
277
//
278
function create_templates($variant, $variant_desc, $dir) {
279
    // input template
280
    //
281
    $x = "<input_template>\n";
282
    $ninfiles = 1 + count($variant_desc->input_file_names) + count($variant_desc->app_files);
283
    for ($i=0; $i<$ninfiles; $i++) {
284
        $x .= "   <file_info>\n      <sticky/>\n      <no_delete/>\n      <executable/>\n   </file_info>\n";
285
    }
286
    $x .= "   <workunit>\n";
287
    $x .= file_ref_in($variant_desc->dockerfile);
288
    foreach ($variant_desc->app_files as $fname) {
289
        $x .= file_ref_in($fname);
290
    }
291
    foreach ($variant_desc->input_file_names as $fname) {
292
        $x .= file_ref_in($fname);
293
    }
294
    if (strstr($variant, 'cpu')) {
295
        $x .= "      <plan_class></plan_class>\n";
296
    } else {
297
        $x .= "      <plan_class>$variant</plan_class>\n";
298
    }
299
300
    // replication params
301
    //
302
    $x .= sprintf("      <target_nresults>%d</target_nresults>\n",
303
        $variant_desc->min_nsuccess
304
    );
305
    $x .= sprintf("      <min_quorum>%d</min_quorum>\n",
306
        $variant_desc->min_nsuccess
307
    );
308
    $x .= sprintf("      <max_total_results>%d</max_total_results>\n",
309
        $variant_desc->max_total
310
    );
311
312
    $x .= sprintf("      <max_delay>%f</max_delay>\n",
313
        $variant_desc->max_delay_days * 86400.
314
    );
315
316
    $x .= "   </workunit>\n<input_template>\n";
317
    file_put_contents("$dir/template_in", $x);
318
319
    // output template
320
    //
321
    $x = "<output_template>\n";
322
    $i = 0;
323
    foreach ($variant_desc->output_file_names as $fname) {
324
        $x .= file_info_out($i++);
325
    }
326
    $x .= "   <result>\n";
327
    $i = 0;
328
    foreach ($variant_desc->output_file_names as $fname) {
329
        $x .= file_ref_out($i++, $fname);
330
    }
331
    $x .= "   </result>\n</output_template>\n";
332
    file_put_contents("$dir/template_out", $x);
333
}
334
335
// create variant
336
//
337
function variant_action($user) {
338
    global $buda_root;
339
    $variant = get_str('variant');
340
    if (!$variant) $variant = 'cpu';
341
    if (!is_valid_filename($variant)) {
342
        error_page(filename_rules());
343
    }
344
    $app = get_str('app');
345
    if (!is_valid_filename($app)) die('bad arg');
0 ignored issues
show
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...
346
    $dockerfile = get_str('dockerfile');
347
    if (!is_valid_filename($dockerfile)) {
348
        error_page("Invalid dockerfile name: ".filename_rules());
349
    }
350
    $app_files = get_array('app_files');
351
    foreach ($app_files as $fname) {
352
        if (!is_valid_filename($fname)) {
353
            error_page("Invalid app file name: ".filename_rules());
354
        }
355
    }
356
    $min_nsuccess = get_int('min_nsuccess');
357
    if ($min_nsuccess <= 0) {
358
        error_page('Must specify a positive number of successful instances.');
359
    }
360
    $max_total = get_int('max_total');
361
    if ($max_total <= 0) {
362
        error_page('Must specify a positive max number of instances.');
363
    }
364
    if ($min_nsuccess > $max_total) {
365
        error_page('Target # of successful instances must be <= max total');
366
    }
367
    $max_delay_days = get_str('max_delay_days');
368
    if (!is_numeric($max_delay_days)) {
369
        error_page('Must specify max delay');
370
    }
371
    $max_delay_days = floatval($max_delay_days);
372
    if ($max_delay_days <= 0) {
373
        error_page('Must specify positive max delay');
374
    }
375
376
    $input_file_names = get_str('input_file_names', true);
377
    if ($input_file_names) {
378
        $input_file_names = explode(' ', $input_file_names);
379
        foreach ($input_file_names as $fname) {
380
            if (!is_valid_filename($fname)) {
381
                error_page("Invalid input file name: ".filename_rules());
382
            }
383
        }
384
    } else {
385
        $input_file_names = [];
386
    }
387
388
    $output_file_names = get_str('output_file_names', true);
389
    if ($output_file_names) {
390
        $output_file_names = explode(' ', $output_file_names);
391
        foreach ($output_file_names as $fname) {
392
            if (!is_valid_filename($fname)) {
393
                error_page("Invalid output file name: ".filename_rules());
394
            }
395
        }
396
    } else {
397
        $output_file_names = [];
398
    }
399
400
    $dir = "$buda_root/$app/$variant";
401
    if (get_str('edit', true)) {
402
        system("rm -r $dir");
403
    } else {
404
        if (file_exists($dir)) {
405
            error_page("Variant '$variant' already exists.");
406
        }
407
    }
408
    mkdir($dir);
409
    mkdir("$dir/.md5");
410
411
    // collect variant params into a struct
412
    //
413
    $desc = new StdClass;
414
    $desc->dockerfile = $dockerfile;
415
    $desc->app_files = $app_files;
416
    $desc->input_file_names = $input_file_names;
417
    $desc->output_file_names = $output_file_names;
418
    $desc->min_nsuccess = $min_nsuccess;
419
    $desc->max_total = $max_total;
420
    $desc->max_delay_days = $max_delay_days;
421
422
    // copy files from sandbox to variant dir
423
    //
424
    $pname = copy_and_stage_file($user, $dockerfile, $dir, $app, $variant);
425
    $desc->dockerfile_phys = $pname;
426
    $desc->app_files_phys = [];
427
    foreach ($app_files as $fname) {
428
        $pname = copy_and_stage_file($user, $fname, $dir, $app, $variant);
429
        $desc->app_files_phys[] = $pname;
430
    }
431
432
    // write variant params to a JSON file
433
    //
434
    file_put_contents(
435
        "$dir/variant.json",
436
        json_encode($desc, JSON_PRETTY_PRINT)
437
    );
438
439
    create_templates($variant, $desc, $dir);
440
441
    // Note: we don't currently allow indirect file access.
442
    // If we did, we'd need to create job.toml to mount project dir
443
444
    app_list("Variant $variant added for app $app.");
445
}
446
447
function variant_delete() {
448
    global $buda_root;
449
    $app = get_str('app');
450
    if (!is_valid_filename($app)) die('bad arg');
0 ignored issues
show
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...
451
    $variant = get_str('variant');
452
    if (!is_valid_filename($variant)) die('bad arg');
0 ignored issues
show
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...
453
    $confirmed = get_str('confirmed', true);
454
    if ($confirmed) {
455
        $dir = "$buda_root/$app/$variant";
456
        if (!file_exists($dir)) error_page('no such variant');
457
        // delete staged files
458
        //
459
        foreach (scandir("$dir/.md5") as $fname) {
460
            if ($fname[0] == '.') continue;
461
            [$md5, $size] = parse_info_file("$dir/.md5/$fname");
462
            $phys_name = buda_file_phys_name($app, $variant, $md5);
463
            $phys_path = download_hier_path($phys_name);
464
            unlink($phys_path);
465
            unlink("$phys_path.md5");
466
        }
467
        system("rm -r $buda_root/$app/$variant", $ret);
468
        if ($ret) {
469
            error_page("delete failed");
470
        }
471
        $notice = "Variant $variant of app $app removed.";
472
        app_list($notice);
473
    } else {
474
        page_head("Confirm");
475
        echo "<p>Are you sure you want to delete variant $variant of BUDA app $app?  <p>";
476
        show_button(
477
            "buda.php?action=variant_delete&app=$app&variant=$variant&confirmed=yes",
478
            "Yes"
479
        );
480
        page_tail();
481
    }
482
}
483
484
function app_delete() {
485
    global $buda_root;
486
    $app = get_str('app');
487
    if (!is_valid_filename($app)) die('bad arg');
0 ignored issues
show
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...
488
    $confirmed = get_str('confirmed', true);
489
    if ($confirmed) {
490
        $dir = "$buda_root/$app";
491
        if (!file_exists($dir)) error_page('no such app');
492
        $vars = get_buda_variants($app);
493
        if ($vars) {
494
            error_page("You must delete all variants first.");
495
        }
496
        system("rm $buda_root/$app/desc.json", $ret);
497
        system("rmdir $buda_root/$app", $ret);
498
        if ($ret) {
499
            error_page('delete failed');
500
        }
501
        $notice = "App $app removed.";
502
        app_list($notice);
503
    } else {
504
        page_head('Confirm');
505
        echo "<p>Are you sure you want to delete BUDA science app $app?  <p>";
506
        show_button(
507
            "buda.php?action=app_delete&app=$app&confirmed=yes",
508
            "Yes"
509
        );
510
        page_tail();
511
    }
512
}
513
514
function app_form($desc=null) {
515
    page_head_select2($desc?"Edit BUDA app $desc->name":'Create BUDA app');
516
    form_start('buda.php');
517
    form_input_hidden('action', 'app_action');
518
    if ($desc) {
519
        form_input_hidden('edit_name', $desc->name);
520
        form_input_hidden('user_id', $desc->user_id);
521
        form_input_hidden('create_time', $desc->create_time);
522
    } else {
523
        form_input_text('Internal name<br><small>No spaces</small>', 'name');
524
    }
525
    form_input_text('User-visible name', 'long_name',
526
        $desc?$desc->long_name:null
527
    );
528
    form_input_textarea(
529
        'Description<br><small>... of what the app does and of the research goals</small>',
530
        'description',
531
        $desc?$desc->description:null
532
    );
533
    form_select2_multi('Science keywords',
534
        'sci_kw',
535
        keyword_select_options(KW_CATEGORY_SCIENCE),
536
        $desc?$desc->sci_kw:null
537
    );
538
    form_input_text(
539
        'URL of web page describing app',
540
        'url',
541
        !empty($desc->url)?$desc->url:''
542
    );
543
    // don't include location keywords;
544
    // various people may submit jobs to this app
545
    form_submit('OK');
546
    form_end();
547
    page_tail();
548
}
549
550
function app_action($user) {
551
    global $buda_root;
552
    $edit_name = get_str('edit_name', true);
553
    $desc = new StdClass;
554
    if ($edit_name) {
555
        $dir = "$buda_root/$edit_name";
556
        $name = $edit_name;
557
        $desc->user_id = get_int('user_id');
558
        $desc->create_time = get_int('create_time');
559
    } else {
560
        $name = get_str('name');
561
        if (!is_valid_filename($name)) {
562
            error_page(filename_rules());
563
        }
564
        $dir = "$buda_root/$name";
565
        if (file_exists($dir)) {
566
            error_page("App $name already exists.");
567
        }
568
        mkdir($dir);
569
        $desc->user_id = $user->id;
570
        $desc->create_time = time();
571
    }
572
    $desc->name = $name;
573
    $desc->long_name = get_str('long_name');
574
    $desc->description = get_str('description');
575
    $desc->sci_kw = array_map('intval', get_array('sci_kw'));
576
    file_put_contents("$dir/desc.json", json_encode($desc, JSON_PRETTY_PRINT));
577
    header("Location: buda.php");
578
}
579
580
function view_file() {
581
    global $buda_root;
582
    $app = get_str('app');
583
    if (!is_valid_filename($app)) die('bad arg');
0 ignored issues
show
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...
584
    $variant = get_str('variant');
585
    if (!is_valid_filename($variant)) die('bad arg');
0 ignored issues
show
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...
586
    $fname = get_str('fname');
587
    if (!is_valid_filename($fname)) die('bad arg');
0 ignored issues
show
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...
588
    echo "<pre>\n";
589
    $x = file_get_contents("$buda_root/$app/$variant/$fname");
590
    echo htmlspecialchars($x);
591
    echo "</pre>\n";
592
}
593
594
function handle_app_edit() {
595
    global $buda_root;
596
    $name = get_str('name');
597
    app_form(get_buda_desc($name));
598
}
599
600
function app_details() {
601
    global $buda_root, $manage_access;
602
    $name = get_str('name');
603
    $desc = get_buda_desc($name);
604
    if (!$desc) error_page("no desc file $path");
605
    page_head("BUDA app: $desc->long_name");
606
    start_table();
607
    row2('Internal name', $desc->name);
608
    $user = BoincUser::lookup_id($desc->user_id);
609
    row2('Creator',
610
        sprintf('<a href=show_user.php?userid=%d>%s</a>',
611
            $user->id,
612
            $user->name
613
        )
614
    );
615
    row2('Created', date_str($desc->create_time));
616
    row2('Description', $desc->description);
617
    row2('Science keywords', kw_array_to_str($desc->sci_kw));
618
    if ($manage_access) {
619
        row2('',
620
            button_text_small(
621
                sprintf('buda.php?action=%s&name=%s', 'app_edit', $desc->name),
622
                'Edit app info'
623
            )
624
        );
625
    }
626
    $vars = get_buda_variants($name);
627
    if ($vars) {
628
        $x = [];
629
        foreach ($vars as $var) {
630
            $x[] = sprintf('<a href=buda.php?action=variant_view&app=%s&variant=%s>%s</a>',
631
                $name, $var, $var
632
            );
633
        }
634
        row2('Variants', implode('<p>', $x));
635
        if ($manage_access) {
636
            row2('',
637
                button_text_small(
638
                    "buda.php?action=variant_form&app=$name",
639
                    'Add variant'
640
                )
641
            );
642
        }
643
    } else if ($manage_access) {
644
        row2('Variants',
645
            button_text_small(
646
                "buda.php?action=variant_form&app=$name",
647
                'Add variant'
648
            )
649
        );
650
        row2('',
651
            button_text(
652
                "buda.php?action=app_delete&app=$name",
653
                "Delete app",
654
                null,
655
                'btn btn-xs btn-warning'
656
            )
657
        );
658
    }
659
    end_table();
660
    page_tail();
661
}
662
663
// Users with manage access to BUDA can add/delete apps and variants.
664
// Others can just view.
665
// Might want to refine this at some point
666
667
$user = get_logged_in_user();
0 ignored issues
show
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...
668
$buda_app = BoincApp::lookup("name='buda'");
669
if (!$buda_app) error_page('no buda app');
670
$manage_access = has_manage_access($user, $buda_app->id);
671
672
$action = get_str('action', true);
673
switch ($action) {
674
case 'app_edit':
675
    if (!$manage_access) error_page('no access');
676
    handle_app_edit(); break;
677
case 'app_form':
678
    if (!$manage_access) error_page('no access');
679
    app_form(); break;
680
case 'app_action':
681
    if (!$manage_access) error_page('no access');
682
    app_action($user); break;
683
case 'app_details':
684
    app_details(); break;
685
case 'app_delete':
686
    if (!$manage_access) error_page('no access');
687
    app_delete(); break;
688
case 'variant_view':
689
    variant_view($user); break;
0 ignored issues
show
The call to variant_view() has too many arguments starting with $user. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

689
    /** @scrutinizer ignore-call */ 
690
    variant_view($user); break;

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
690
case 'variant_form':
691
    if (!$manage_access) error_page('no access');
692
    variant_form($user); break;
693
case 'variant_action':
694
    if (!$manage_access) error_page('no access');
695
    variant_action($user);
696
    write_plan_class_file();
697
    break;
698
case 'variant_delete':
699
    if (!$manage_access) error_page('no access');
700
    variant_delete();
701
    write_plan_class_file();
702
    break;
703
case 'view_file':
704
    view_file(); break;
705
case null:
706
    app_list(); break;
707
default:
0 ignored issues
show
DEFAULT keyword must be indented 4 spaces from SWITCH keyword
Loading history...
DEFAULT case must have a breaking statement
Loading history...
708
    error_page("unknown action");
709
}
710
711
?>
712