xref: /web-bugs/www/bug.php (revision c9451f7a)
1<?php
2/* User interface for viewing and editing bug details */
3
4use App\Repository\BugRepository;
5use App\Repository\CommentRepository;
6use App\Repository\ObsoletePatchRepository;
7use App\Repository\PackageRepository;
8use App\Repository\PatchRepository;
9use App\Utils\Captcha;
10use App\Repository\PullRequestRepository;
11use App\Repository\ReasonRepository;
12
13// Obtain common includes
14require_once '../include/prepend.php';
15
16// Start session
17session_start();
18
19$obsoletePatchRepository = $container->get(ObsoletePatchRepository::class);
20$patchRepository = $container->get(PatchRepository::class);
21
22$email = null;
23
24// Handle preview
25if (isset($_REQUEST['id']) && $_REQUEST['id'] == 'preview') {
26    $bug_id = 'PREVIEW';
27    $bug = $_SESSION['bug_preview'];
28    $bug['submitted'] = time();
29    $bug['modified'] = null;
30    $bug['votes'] = 0;
31    $bug['assign'] = '';
32
33    if (!$bug) {
34        redirect('index.php');
35    }
36} else {
37    // Bailout early if no/invalid bug id is passed
38    if (empty($_REQUEST['id']) || !((int) $_REQUEST['id'])) {
39        redirect('index.php');
40    } else {
41        $bug_id = (int) $_REQUEST['id'];
42    }
43}
44
45// Init common variables
46$errors = [];
47
48// Set edit mode
49$edit = isset($_REQUEST['edit']) ? (int) $_REQUEST['edit'] : 0;
50
51// Authenticate
52bugs_authenticate($user, $pw, $logged_in, $user_flags);
53
54$is_trusted_developer = ($user_flags & BUGS_TRUSTED_DEV);
55$is_security_developer = ($user_flags & (BUGS_TRUSTED_DEV | BUGS_SECURITY_DEV));
56
57// Handle unsubscription
58if (isset($_GET['unsubscribe'])) {
59    $unsubcribe = (int) $_GET['unsubscribe'];
60
61    $hash = isset($_GET['t']) ? $_GET['t'] : false;
62
63    if (!$hash) {
64        redirect("bug.php?id={$bug_id}");
65    }
66    unsubscribe($bug_id, $hash);
67    $_GET['thanks'] = 9;
68}
69
70// Subscription / Unsubscription
71if (isset($_POST['subscribe_to_bug']) || isset($_POST['unsubscribe_to_bug'])) {
72
73    // Check if session answer is set, then compare it with the post captcha value.
74    // If it's not the same, then it's an incorrect password.
75    if (!$logged_in) {
76        if (!isset($_SESSION['answer'])) {
77            $errors[] = 'Please enable cookies so the Captcha system can work';
78        } elseif ($_POST['captcha'] != $_SESSION['answer']) {
79            $errors[] = 'Incorrect Captcha';
80        }
81    }
82
83    if (empty($errors)) {
84        if ($logged_in && !empty($auth_user->email)) {
85            $email = $auth_user->email;
86        } else {
87            $email = isset($_POST['in']['commentemail']) ? $_POST['in']['commentemail'] : '';
88        }
89        if ($email == '' || !is_valid_email($email, $logged_in)) {
90            $errors[] = 'You must provide a valid email address.';
91        } else {
92            // Unsubscribe
93            if (isset($_POST['unsubscribe_to_bug'])) {
94                // Generate the hash
95                unsubscribe_hash($bug_id, $email);
96                $thanks = 8;
97            }
98            else // Subscribe
99            {
100                $dbh->prepare('REPLACE INTO bugdb_subscribe SET bug_id = ?, email = ?')->execute([$bug_id, $email]);
101                $thanks = 7;
102            }
103            redirect("bug.php?id={$bug_id}&thanks={$thanks}");
104        }
105    }
106    // If we get here, display errors
107    response_header('Error in subscription');
108    display_bug_error($errors);
109    response_footer();
110    exit;
111}
112
113// Delete comment
114if ($edit == 1 && $is_trusted_developer && isset($_GET['delete_comment'])) {
115    $delete_comment = (int) $_GET['delete_comment'];
116    $addon = '';
117
118    if ($delete_comment) {
119        delete_comment($bug_id, $delete_comment);
120        $addon = '&thanks=1';
121    }
122    redirect("bug.php?id=$bug_id&edit=1$addon");
123}
124
125// captcha is not necessary if the user is logged in
126if (!$logged_in) {
127    $captcha = $container->get(Captcha::class);
128}
129
130$trytoforce = isset($_POST['trytoforce']) ? (int) $_POST['trytoforce'] : 0;
131
132// fetch info about the bug into $bug
133if (!isset($bug)) {
134    $bugRepository = $container->get(BugRepository::class);
135    $bug = $bugRepository->findOneById($bug_id);
136}
137
138// DB error
139if (is_object($bug)) {
140    response_header('DB error');
141    display_bug_error($bug);
142    response_footer();
143    exit;
144}
145
146// Bug not found with passed id
147if (!$bug) {
148    response_header('No Such Bug');
149    display_bug_error("No such bug #{$bug_id}");
150    response_footer();
151    exit;
152}
153
154$show_bug_info = bugs_has_access($bug_id, $bug, $pw, $user_flags);
155if ($edit == 2 && !$show_bug_info && $pw && verify_bug_passwd($bug_id, bugs_get_hash($pw))) {
156    $show_bug_info = true;
157}
158
159if (isset($_POST['ncomment'])) {
160    /* Bugs blocked to user comments can only be commented by the team */
161    if ($bug['block_user_comment'] == 'Y' && $logged_in != 'developer') {
162        response_header('Adding comments not allowed');
163        display_bug_error("You're not allowed to add a comment to bug #{$bug_id}");
164        response_footer();
165        exit;
166    }
167}
168
169/* Just developers can change private/block_user_comment options */
170if (!empty($_POST['in'])) {
171    if ($user_flags & BUGS_DEV_USER) {
172        $block_user = isset($_POST['in']['block_user_comment']) ? 'Y' : 'N';
173    }
174    if ($is_security_developer) {
175        $is_private = isset($_POST['in']['private']) ? 'Y': 'N';
176    }
177}
178
179$block_user = isset($block_user) ? $block_user : $bug['block_user_comment'];
180$is_private = isset($is_private) ? $is_private : $bug['private'];
181
182// Handle any updates, displaying errors if there were any
183$RESOLVE_REASONS = $FIX_VARIATIONS = $pseudo_pkgs = [];
184
185$project = $bug['project'];
186
187// Only fetch stuff when it's really needed
188if ($edit && $edit < 3) {
189    $packageRepository = $container->get(PackageRepository::class);
190    $pseudo_pkgs = $packageRepository->findEnabled();
191}
192
193// Fetch RESOLVE_REASONS array
194if ($edit === 1) {
195    $reasonRepository = $container->get(ReasonRepository::class);
196    list($RESOLVE_REASONS, $FIX_VARIATIONS) = $reasonRepository->findByProject($project);
197}
198
199if (isset($_POST['ncomment']) && !isset($_POST['preview']) && $edit == 3) {
200    // Submission of additional comment by others
201
202    // Bug is private (just should be available to trusted developers and to reporter)
203    if (!$is_security_developer && $bug['private'] == 'Y') {
204        response_header('Private report');
205        display_bug_error("The bug #{$bug_id} is not available to public, if you are the original reporter use the Edit tab");
206        response_footer();
207        exit;
208    }
209
210    // Check if session answer is set, then compare it with the post captcha value.
211    // If it's not the same, then it's an incorrect password.
212    if (!$logged_in) {
213        response_header('Developers only');
214        display_bug_error('Only developers are allowed to comment; if you are the original reporter use the Edit tab');
215        response_footer();
216        exit;
217    }
218
219    $ncomment = trim($_POST['ncomment']);
220    if (!$ncomment) {
221        $errors[] = 'You must provide a comment.';
222    }
223
224    // primitive spam detection
225    if ($message = is_spam($ncomment)) {
226        $errors[] = $message;
227    }
228    if (is_spam($_POST['in']['commentemail'])) {
229        $errors[] = "Please do not SPAM our bug system.";
230    }
231    if (is_spam_user($_POST['in']['commentemail'])) {
232        $errors[] = "Please do not SPAM our bug system.";
233    }
234
235    if (!$errors) {
236        do {
237            if (!$logged_in) {
238
239                if (!is_valid_email($_POST['in']['commentemail'], $logged_in)) {
240                    $errors[] = 'You must provide a valid email address.';
241                    response_header('Add Comment - Problems');
242                    break; // skip bug comment addition
243                }
244
245                $_POST['in']['name'] = '';
246            } else {
247                $_POST['in']['commentemail'] = $auth_user->email;
248                $_POST['in']['name'] = $auth_user->name;
249            }
250
251            $res = bugs_add_comment($bug_id, $_POST['in']['commentemail'], $_POST['in']['name'], $ncomment, 'comment');
252
253            mark_related_bugs($_POST['in']['commentemail'], $_POST['in']['name'], $ncomment);
254
255        } while (false);
256
257        $from = spam_protect($_POST['in']['commentemail'], 'text');
258    } else {
259        $from = '';
260    }
261} elseif (isset($_POST['ncomment']) && isset($_POST['preview']) && $edit == 3) {
262    $ncomment = trim($_POST['ncomment']);
263
264    // primitive spam detection
265    if ($message = is_spam($ncomment)) {
266        $errors[] = $message;
267    }
268
269    $from = $_POST['in']['commentemail'];
270    if (is_spam_user($from)) {
271        $errors[] = "Please do not SPAM our bug system.";
272    }
273
274} elseif (isset($_POST['in']) && !isset($_POST['preview']) && $edit == 2) {
275    // Edits submitted by original reporter for old bugs
276
277    if (!$show_bug_info || !verify_bug_passwd($bug_id, bugs_get_hash($pw))) {
278        $errors[] = 'The password you supplied was incorrect.';
279    }
280
281    // Bug is private (just should be available to trusted developers, original reporter and assigned dev)
282    if (!$show_bug_info && $bug['private'] == 'Y') {
283        response_header('Private report');
284        display_bug_error("The bug #{$bug_id} is not available to public");
285        response_footer();
286        exit;
287    }
288
289    // Just trusted dev can change the package name of a Security related bug to another package
290    if ($bug['private'] == 'Y' && !$is_security_developer
291        && $bug['bug_type'] == 'Security'
292        && $_POST['in']['bug_type'] != $bug['bug_type']) {
293
294        $errors[] = 'You cannot change the bug type of a Security bug!';
295    }
296
297    $ncomment = trim($_POST['ncomment']);
298    if (!$ncomment) {
299        $errors[] = 'You must provide a comment.';
300    }
301
302    // check that they aren't being bad and setting a status they aren't allowed to (oh, the horrors.)
303    if (isset($_POST['in']['status'])
304        && isset($state_types[$_POST['in']['status']])
305        && $_POST['in']['status'] != $bug['status'] && $state_types[$_POST['in']['status']] != 2) {
306        $errors[] = 'You aren\'t allowed to change a bug to that state.';
307    }
308
309    // check that they aren't changing the mail to a php.net address (gosh, somebody might be fooled!)
310    if (preg_match('/^(.+)@php\.net/i', $_POST['in']['email'], $m)) {
311        if ($user != $m[1] || $logged_in != 'developer') {
312            $errors[] = 'You have to be logged in as a developer to use your php.net email address.';
313            $errors[] = 'Tip: log in via another browser window then resubmit the form in this window.';
314        }
315    }
316
317    // primitive spam detection
318    if ($ncomment && $message = is_spam($ncomment)) {
319        $errors[] = $message;
320    }
321
322    if (!empty($_POST['in']['email']) &&
323        $bug['email'] != $_POST['in']['email']
324    ) {
325        $from = $_POST['in']['email'];
326    } else {
327        $from = $bug['email'];
328    }
329
330    if (is_spam_user($from)) {
331        $errors[] = "Please do not SPAM our bug system.";
332    }
333
334    if (!$errors && !($errors = incoming_details_are_valid($_POST['in'], false))) {
335        // Allow the reporter to change the bug type to 'Security', hence mark
336        // the report as private
337        if ($bug['private'] == 'N' && $_POST['in']['bug_type'] == 'Security'
338            && $_POST['in']['bug_type'] != $bug['bug_type']) {
339
340            $is_private = $_POST['in']['private'] = 'Y';
341        }
342
343        $dbh->prepare("
344            UPDATE bugdb
345            SET
346                sdesc = ?,
347                status = ?,
348                package_name = ?,
349                bug_type = ?,
350                php_version = ?,
351                php_os = ?,
352                email = ?,
353                ts2 = NOW(),
354                private = ?
355            WHERE id={$bug_id}
356        ")->execute([
357            $_POST['in']['sdesc'],
358            $_POST['in']['status'],
359            $_POST['in']['package_name'],
360            $_POST['in']['bug_type'],
361            $_POST['in']['php_version'],
362            $_POST['in']['php_os'],
363            $from,
364            $is_private
365        ]);
366
367        // Add changelog entry
368        $changed = bug_diff($bug, $_POST['in']);
369        if (!empty($changed)) {
370            $log_comment = bug_diff_render_html($changed);
371
372            if (!empty($log_comment)) {
373                $res = bugs_add_comment($bug_id, $from, '', $log_comment, 'log');
374            }
375        }
376
377        // Add normal comment
378        if (!empty($ncomment)) {
379            $res = bugs_add_comment($bug_id, $from, '', $ncomment, 'comment');
380
381            mark_related_bugs($from, '', $ncomment);
382        }
383    }
384} elseif (isset($_POST['in']) && isset($_POST['preview']) && $edit == 2) {
385    $ncomment = trim($_POST['ncomment']);
386    $from = isset($_POST['in']['commentemail']) ? $_POST['in']['commentemail'] : '';
387
388    // primitive spam detection
389    if ($message = is_spam($ncomment)) {
390        $errors[] = $message;
391    }
392    if (is_spam_user($from)) {
393        $errors[] = "Please do not SPAM our bug system.";
394    }
395
396} elseif (isset($_POST['in']) && is_array($_POST['in']) && !isset($_POST['preview']) && $edit == 1) {
397    // Edits submitted by developer
398
399    // Bug is private (just should be available to trusted developers, submitter and assigned dev)
400    if (!$show_bug_info && $bug['private'] == 'Y') {
401        response_header('Private report');
402        display_bug_error("The bug #{$bug_id} is not available to public");
403        response_footer();
404        exit;
405    }
406
407    if ($logged_in != 'developer') {
408        $errors[] = 'You have to login first in order to edit the bug report.';
409    }
410    $comment_name = $auth_user->name;
411    if (empty($_POST['ncomment'])) {
412        $ncomment = '';
413    } else {
414        $ncomment = trim($_POST['ncomment']);
415    }
416
417    // primitive spam detection
418    if ($ncomment && $message = is_spam($ncomment)) {
419        $errors[] = $message;
420    }
421
422    // Just trusted dev can set CVE-ID
423    if ($is_security_developer && !empty($_POST['in']['cve_id'])) {
424        // Remove the CVE- prefix
425        $_POST['in']['cve_id'] = preg_replace('/^\s*CVE-/i', '', $_POST['in']['cve_id']);
426    }
427    if (empty($_POST['in']['cve_id'])) {
428        $_POST['in']['cve_id'] = $bug['cve_id'];
429    }
430
431    if ($bug['private'] == 'N' && $bug['private'] != $is_private) {
432        if ($_POST['in']['bug_type'] != 'Security') {
433            $errors[] = 'Only Security bugs can be marked as private.';
434        }
435    }
436
437    global $state_types;
438    $allowed_state_types = array_filter($state_types, function ($var) {
439        return $var !== 0;
440    });
441    // Require comment for open bugs only
442    if (empty($_POST['in']['status']) || !isset($allowed_state_types[$_POST['in']['status']])) {
443        $errors[] = "You must provide a status";
444    } else {
445        if ($_POST['in']['status'] == 'Not a bug' &&
446            !in_array($bug['status'], ['Not a bug', 'Closed', 'Duplicate', 'No feedback', 'Wont fix']) &&
447            strlen(trim($ncomment)) == 0
448        ) {
449            $errors[] = "You must provide a comment when marking a bug 'Not a bug'";
450        } elseif (!empty($_POST['in']['resolve'])) {
451            if (!$trytoforce && isset($RESOLVE_REASONS[$_POST['in']['resolve']]) &&
452                $RESOLVE_REASONS[$_POST['in']['resolve']]['status'] == $bug['status'])
453            {
454                $errors[] = 'The bug is already marked "'.$bug['status'].'". (Submit again to ignore this.)';
455            } elseif (!$errors) {
456                if ($_POST['in']['status'] == $bug['status']) {
457                    $_POST['in']['status'] = $RESOLVE_REASONS[$_POST['in']['resolve']]['status'];
458                }
459                if (isset($FIX_VARIATIONS) && isset($FIX_VARIATIONS[$_POST['in']['resolve']][$bug['package_name']])) {
460                    $reason = $FIX_VARIATIONS[$_POST['in']['resolve']][$bug['package_name']];
461                } else {
462                    $reason = isset($RESOLVE_REASONS[$_POST['in']['resolve']]) ? $RESOLVE_REASONS[$_POST['in']['resolve']]['message'] : '';
463                }
464
465                // do a replacement on @svn@ to the likely location of SVN for this package
466                if ($_POST['in']['resolve'] == 'trysvn') {
467                    switch ($bug['package_name']) {
468                        case 'Documentation' :
469                        case 'Web Site' :
470                        case 'Bug System' :
471                        case 'PEPr' :
472                            $errors[] = 'Cannot use "try svn" with ' . $bug['package_name'];
473                            break;
474                        case 'PEAR' :
475                            $reason = str_replace('@svn@', 'pear-core', $reason);
476                            $ncomment = "$reason\n\n$ncomment";
477                            break;
478                        default :
479                            $reason = str_replace('@svn@', $bug['package_name'], $reason);
480                            $ncomment = "$reason\n\n$ncomment";
481                            break;
482                    }
483                } else {
484                    $ncomment = "$reason\n\n$ncomment";
485                }
486            }
487        }
488    }
489
490    $from = $auth_user->email;
491
492    if (!$errors && !($errors = incoming_details_are_valid($_POST['in']))) {
493        $query = 'UPDATE bugdb SET';
494
495        // Update email only if it's passed
496        if ($bug['email'] != $_POST['in']['email'] && !empty($_POST['in']['email'])) {
497            $query .= " email='{$_POST['in']['email']}',";
498        }
499
500        // Changing the package to 'Security related' should mark the bug as private automatically
501        if ($bug['bug_type'] != $_POST['in']['bug_type']) {
502            if ($_POST['in']['bug_type'] == 'Security' && $_POST['in']['status'] != 'Closed') {
503                $is_private = $_POST['in']['private'] = 'Y';
504            }
505        }
506
507        if ($logged_in != 'developer') {
508            // don't reset assigned status
509            $_POST['in']['assign'] = $bug['assign'];
510        }
511        if (!empty($_POST['in']['assign']) && $_POST['in']['status'] == 'Open') {
512            $status = 'Assigned';
513        } elseif (empty($_POST['in']['assign']) && $_POST['in']['status'] == 'Assigned') {
514            $status = 'Open';
515        } else {
516            $status = $_POST['in']['status'];
517        }
518
519        // Assign automatically when closed
520        if ($status == 'Closed' && $_POST['in']['assign'] == '') {
521            $_POST['in']['assign'] = $auth_user->handle;
522        }
523
524        $dbh->prepare($query . "
525                sdesc = ?,
526                status = ?,
527                package_name = ?,
528                bug_type = ?,
529                assign = ?,
530                php_version = ?,
531                php_os = ?,
532                block_user_comment = ?,
533                cve_id = ?,
534                private = ?,
535                ts2 = NOW()
536            WHERE id = {$bug_id}
537        ")->execute([
538            $_POST['in']['sdesc'],
539            $status,
540            $_POST['in']['package_name'],
541            $_POST['in']['bug_type'],
542            $_POST['in']['assign'],
543            $_POST['in']['php_version'],
544            $_POST['in']['php_os'],
545            $block_user,
546            $_POST['in']['cve_id'],
547            $is_private
548        ]);
549
550        // Add changelog entry
551        $changed = bug_diff($bug, $_POST['in']);
552        if (!empty($changed)) {
553            $log_comment = bug_diff_render_html($changed);
554
555            if (!empty($log_comment)) {
556                $res = bugs_add_comment($bug_id, $from, $comment_name, $log_comment, 'log');
557            }
558        }
559
560        // Add normal comment
561        if (!empty($ncomment)) {
562            $res = bugs_add_comment($bug_id, $from, $comment_name, $ncomment, 'comment');
563
564            mark_related_bugs($from, $comment_name, $ncomment);
565        }
566    }
567} elseif (isset($_POST['in']) && isset($_POST['preview']) && $edit == 1) {
568    $ncomment = trim($_POST['ncomment']);
569    $from = $auth_user->email;
570} elseif (isset($_POST['in'])) {
571    $errors[] = 'Invalid edit mode.';
572    $ncomment = '';
573} else {
574    $ncomment = '';
575}
576
577if (isset($_POST['in']) && !isset($_POST['preview']) && !$errors) {
578    mail_bug_updates($bug, $_POST['in'], $from, $ncomment, $edit, $bug_id);
579    redirect("bug.php?id=$bug_id&thanks=$edit");
580}
581
582switch (txfield('bug_type', $bug, isset($_POST['in']) ? $_POST['in'] : null))
583{
584    case 'Feature/Change Request':
585        $bug_type = 'Request';
586        break;
587    case 'Documentation Problem':
588        $bug_type = 'Doc Bug';
589        break;
590    case 'Security':
591        $bug_type = 'Sec Bug';
592        break;
593    default:
594    case 'Bug':
595        $bug_type = 'Bug';
596        break;
597}
598
599response_header(
600    $show_bug_info ? "{$bug_type} #{$bug_id} :: " . htmlspecialchars($bug['sdesc']) : "You must be logged in",
601    ($bug_id != 'PREVIEW') ? "
602        <link rel='alternate' type='application/rss+xml' title='{$bug['package_name']} Bug #{$bug['id']} - RDF' href='rss/bug.php?id={$bug_id}'>
603        <link rel='alternate' type='application/rss+xml' title='{$bug['package_name']} Bug #{$bug['id']} - RSS 2.0' href='rss/bug.php?id={$bug_id}&format=rss2'>
604    " : ''
605);
606
607// DISPLAY BUG
608$thanks = (isset($_GET['thanks'])) ? (int) $_GET['thanks'] : 0;
609switch ($thanks)
610{
611    case 1:
612    case 2:
613        echo '<div class="success">The bug was updated successfully.</div>';
614        break;
615    case 3:
616        echo '<div class="success">Your comment was added to the bug successfully.</div>';
617        break;
618    case 4:
619        $bug_url = "{$site_method}://{$site_url}{$basedir}/bug.php?id={$bug_id}";
620        echo '<div class="success">
621            Thank you for your help!
622            If the status of the bug report you submitted changes, you will be notified.
623            You may return here and check the status or update your report at any time.<br>
624            The URL for your bug report is: <a href="'.$bug_url.'">'.$bug_url.'</a>.
625            </div>';
626        break;
627    case 6:
628        echo '<div class="success">Thanks for voting! Your vote should be reflected in the statistics below.</div>';
629        break;
630    case 7:
631        echo '<div class="success">Your subscribe request has been processed.</div>';
632        break;
633    case 8:
634        echo '<div class="success">Your unsubscribe request has been processed, please check your email.</div>';
635        break;
636    case 9:
637        echo '<div class="success">You have successfully unsubscribed.</div>';
638        break;
639    case 10:
640        echo '<div class="success">Your vote has been updated.</div>';
641    break;
642
643    default:
644        break;
645}
646
647display_bug_error($errors);
648
649if (!$show_bug_info) {
650    echo '<div id="bugheader"></div>';
651} else{
652?>
653<div id="bugheader">
654    <table id="details">
655        <tr id="title">
656            <th class="details" id="number"><a href="bug.php?id=<?php echo $bug_id, '">', $bug_type , '</a>&nbsp;#' , $bug_id; ?></th>
657            <td id="summary" colspan="5"><?php echo htmlspecialchars($bug['sdesc']); ?></td>
658        </tr>
659        <tr id="submission">
660            <th class="details">Submitted:</th>
661            <td style="white-space: nowrap;"><?php echo format_date($bug['submitted']); ?></td>
662            <th class="details">Modified:</th>
663            <td style="white-space: nowrap;"><?php echo ($bug['modified']) ? format_date($bug['modified']) : '-'; ?></td>
664            <td rowspan="6">
665
666<?php    if ($bug['votes']) { ?>
667                <table id="votes">
668                    <tr><th class="details">Votes:</th><td><?php echo $bug['votes'] ?></td></tr>
669                    <tr><th class="details">Avg. Score:</th><td><?php printf("%.1f &plusmn; %.1f", $bug['average'], $bug['deviation']); ?></td></tr>
670                    <tr><th class="details">Reproduced:</th><td><?php printf("%d of %d (%.1f%%)", $bug['reproduced'], $bug['tried'], $bug['tried'] ? ($bug['reproduced'] / $bug['tried']) * 100 : 0); ?></td></tr>
671<?php        if ($bug['reproduced']) { ?>
672                    <tr><th class="details">Same Version:</th><td><?php printf("%d (%.1f%%)", $bug['samever'], ($bug['samever'] / $bug['reproduced']) * 100); ?></td></tr>
673                    <tr><th class="details">Same OS:</th><td><?php printf("%d (%.1f%%)", $bug['sameos'], ($bug['sameos'] / $bug['reproduced']) * 100); ?></td></tr>
674<?php        } ?>
675                </table>
676<?php    } ?>
677
678            </td>
679        </tr>
680
681        <tr id="submitter">
682            <th class="details">From:</th>
683            <td><?php echo ($bug['status'] !== 'Spam') ? spam_protect(htmlspecialchars($bug['email'])) : 'Hidden because of SPAM'; ?></td>
684            <th class="details">Assigned:</th>
685<?php if (!empty($bug['assign'])) { ?>
686            <td><a href="search.php?cmd=display&amp;assign=<?php echo urlencode($bug['assign']), '">', htmlspecialchars($bug['assign']); ?></a> (<a href="https://people.php.net/<?php echo urlencode($bug['assign']); ?>">profile</a>)</td>
687<?php } else { ?>
688            <td><?php echo htmlspecialchars($bug['assign'] ?? ''); ?></td>
689<?php } ?>
690        </tr>
691
692        <tr id="categorization">
693            <th class="details">Status:</th>
694            <td><?php echo htmlspecialchars($bug['status']); ?></td>
695            <th class="details">Package:</th>
696            <td><a href="search.php?cmd=display&amp;package_name[]=<?php echo urlencode($bug['package_name']), '">', htmlspecialchars($bug['package_name']); ?></a><?php echo $bug['project'] == 'pecl' ? ' (<a href="https://pecl.php.net/package/'. htmlspecialchars($bug['package_name']) . '" target="_blank">PECL</a>)' : ''; ?></td>
697        </tr>
698
699        <tr id="situation">
700            <th class="details">PHP Version:</th>
701            <td><?php echo htmlspecialchars($bug['php_version']); ?></td>
702            <th class="details">OS:</th>
703            <td><?php echo htmlspecialchars($bug['php_os'] ?? ''); ?></td>
704        </tr>
705
706        <tr id="private">
707            <th class="details">Private report:</th>
708            <td><?php echo $bug['private'] == 'Y' ? 'Yes' : 'No'; ?></td>
709            <th class="details">CVE-ID:</th>
710            <td><?php if (!empty($bug['cve_id'])) { printf('<a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-%s" target="_blank">%1$s</a>', htmlspecialchars($bug['cve_id'])); } else { ?><em>None</em><?php } ?></td>
711        </tr>
712    </table>
713</div>
714
715<?php
716}
717
718if ($bug_id !== 'PREVIEW') {
719    echo '<div class="controls">', "\n",
720        control(0, 'View'),
721        control(1, 'Developer'),
722        (!$email || $bug['email'] == $email? control(2, 'Edit') : ''),
723        '</div>', "\n";
724?>
725<div class="clear"></div>
726
727<?php if ($show_bug_info && !$edit && canvote($thanks, $bug['status'])) { ?>
728<form id="vote" method="post" action="vote.php">
729    <div class="sect">
730        <fieldset>
731            <legend>Have you experienced this issue?</legend>
732            <div>
733                <input type="radio" id="rep-y" name="reproduced" value="1" onchange="show('canreproduce')"> <label for="rep-y">yes</label>
734                <input type="radio" id="rep-n" name="reproduced" value="0" onchange="hide('canreproduce')"> <label for="rep-n">no</label>
735                <input type="radio" id="rep-d" name="reproduced" value="2" onchange="hide('canreproduce')" checked="checked"> <label for="rep-d">don't know</label>
736            </div>
737        </fieldset>
738        <fieldset>
739            <legend>Rate the importance of this bug to you:</legend>
740            <div>
741                <label for="score-5">high</label>
742                <input type="radio" id="score-5" name="score" value="2">
743                <input type="radio" id="score-4" name="score" value="1">
744                <input type="radio" id="score-3" name="score" value="0" checked="checked">
745                <input type="radio" id="score-2" name="score" value="-1">
746                <input type="radio" id="score-1" name="score" value="-2">
747                <label for="score-1">low</label>
748            </div>
749        </fieldset>
750    </div>
751    <div id="canreproduce" class="sect" style="display: none">
752        <fieldset>
753            <legend>Are you using the same PHP version?</legend>
754            <div>
755                <input type="radio" id="ver-y" name="samever" value="1"> <label for="ver-y">yes</label>
756                <input type="radio" id="ver-n" name="samever" value="0" checked="checked"> <label for="ver-n">no</label>
757            </div>
758        </fieldset>
759        <fieldset>
760            <legend>Are you using the same operating system?</legend>
761            <div>
762                <input type="radio" id="os-y" name="sameos" value="1"> <label for="os-y">yes</label>
763                <input type="radio" id="os-n" name="sameos" value="0" checked="checked"> <label for="os-n">no</label>
764            </div>
765        </fieldset>
766    </div>
767    <div id="submit" class="sect">
768        <input type="hidden" name="id" value="<?php echo $bug_id?>">
769        <input type="submit" value="Vote">
770    </div>
771</form>
772<br clear="all">
773<?php    }
774
775} // if ($bug_id != 'PREVIEW') {
776
777//
778// FIXME! Do not wrap here either. Re-use the comment display function!
779//
780
781if (isset($_POST['preview']) && !empty($ncomment)) {
782    $preview = '<div class="comment">';
783    $preview .= "<strong>[" . format_date(time()) . "] ";
784    $preview .= spam_protect(htmlspecialchars($from));
785    $preview .= "</strong>\n<pre class=\"note\">";
786    $comment = wordwrap($ncomment, 72);
787    $preview .= make_ticket_links(addlinks($comment));
788    $preview .= "</pre>\n";
789    $preview .= '</div>';
790} else {
791    $preview = '';
792}
793
794if ($edit == 1 || $edit == 2) { ?>
795
796<form id="update" action="bug.php?id=<?php echo $bug_id; ?>&amp;edit=<?php echo $edit; ?>" method="post">
797
798<?php
799    if ($edit == 2) {
800?>
801        <div class="explain">
802        <?php if (!isset($_POST['in'])) { ?>
803            Welcome back! If you're the original bug submitter, here's
804            where you can edit the bug or add additional notes.<br>
805            If you forgot your password, <a href="bug-pwd-finder.php?id=<?php echo $bug_id; ?>">you can retrieve your password here</a>.<br>
806        <?php } ?>
807
808            <table>
809                <tr>
810                    <td class="details">Passw<span class="accesskey">o</span>rd:</td>
811                    <td><input type="password" name="pw" value="<?php echo htmlspecialchars($pw); ?>" size="10" maxlength="20" accesskey="o"></td>
812                    <?php if (!$show_bug_info) { ?>
813                    <td><input type="submit" value="Submit"></td>
814                    <?php } ?>
815                </tr>
816            </table>
817        </div>
818<?php
819    } elseif ($logged_in == 'developer') {
820?>
821        <div class="explain">
822            Welcome back, <?php echo $user; ?>!
823            (Not <?php echo $user; ?>? <a href="logout.php">Log out.</a>)
824        </div>
825<?php
826    } else {
827?>
828        <div class="explain">
829            Welcome! If you don't have a Git account, you can't do anything here.<br>
830            If you reported this bug, you can <a href="bug.php?id=<?php echo $bug_id; ?>&amp;edit=2">edit this bug over here</a>.
831            <div class="details">
832                <label for="svnuser">php.net Username:</label>
833                <input type="text" id="svnuser" name="user" value="<?php echo htmlspecialchars($user); ?>" size="10" maxlength="20">
834                <label for="svnpw">php.net Password:</label>
835                <input type="password" id="svnpw" name="pw" value="<?php echo htmlspecialchars($pw); ?>" size="10">
836                <!--<label for="save">Remember:</label><input style="vertical-align:middle;" type="checkbox" id="save" name="save" <?php echo !empty($_POST['save']) ? 'checked="checked"' : ''; ?>>-->
837                <?php if (!$show_bug_info) { ?>
838                <input type="submit" value="Submit">
839                <?php } ?>
840            </div>
841        </div>
842<?php
843    }
844
845    echo $preview;
846?>
847    <table>
848
849<?php
850      if ($edit == 1 && $show_bug_info) { /* Developer Edit Form */
851        if (isset($RESOLVE_REASONS) && $RESOLVE_REASONS) {
852?>
853        <tr>
854            <th class="details"><label for="in" accesskey="c">Qui<span class="accesskey">c</span>k Fix:</label></th>
855            <td colspan="3">
856                <select name="in[resolve]" id="in">
857                    <?php show_reason_types((isset($_POST['in']) && isset($_POST['in']['resolve'])) ? $_POST['in']['resolve'] : -1, 1); ?>
858                </select>
859
860<?php      if (isset($_POST['in']) && !empty($_POST['in']['resolve'])) { ?>
861                <input type="hidden" name="trytoforce" value="1">
862<?php      } ?>
863
864                <small>(<a href="quick-fix-desc.php">description</a>)</small>
865            </td>
866        </tr>
867<?php
868        }
869
870        if ($is_security_developer) { ?>
871        <tr>
872            <th class="details">CVE-ID:</th>
873            <td colspan="3">
874                <input type="text" size="15" maxlength="15" name="in[cve_id]" value="<?php echo field('cve_id'); ?>" id="cve_id">
875            </td>
876        </tr>
877        <tr>
878            <th class="details"></th>
879            <td colspan="3">
880                <input type="checkbox" name="in[private]" value="Y" <?php print $is_private == 'Y' ? 'checked="checked"' : ''; ?>> Private report (Normal user should not see it)
881            </td>
882        </tr>
883<?php   } ?>
884        <tr>
885            <th class="details"></th>
886            <td colspan="3">
887                <input type="checkbox" name="in[block_user_comment]" value="Y" <?php print $block_user == 'Y' ? 'checked="checked"' : ''; ?>> Block user comment
888            </td>
889        </tr>
890<?php } ?>
891
892<?php if ($show_bug_info) { ?>
893
894        <tr>
895            <th class="details">Status:</th>
896            <td <?php echo (($edit != 1) ? 'colspan="3"' : '' ); ?>>
897                <select name="in[status]">
898                    <?php show_state_options(isset($_POST['in']) && isset($_POST['in']['status']) ? $_POST['in']['status'] : '', $edit, $bug['status'], $bug['assign']); ?>
899                </select>
900
901<?php if ($edit == 1) { ?>
902            </td>
903            <th class="details">Assign to:</th>
904            <td>
905                <input type="text" size="10" maxlength="16" name="in[assign]" value="<?php echo field('assign'); ?>" id="assigned_user">
906<?php } ?>
907
908                <input type="hidden" name="id" value="<?php echo $bug_id ?>">
909                <input type="hidden" name="edit" value="<?php echo $edit ?>">
910                <input type="submit" value="Submit">
911            </td>
912        </tr>
913        <tr>
914            <th class="details">Package:</th>
915            <td colspan="3">
916                <select name="in[package_name]">
917                    <?php show_package_options(isset($_POST['in']) && isset($_POST['in']['package_name']) ? $_POST['in']['package_name'] : '', 0, $bug['package_name']); ?>
918                </select>
919            </td>
920        </tr>
921        <tr>
922            <th class="details">Bug Type:</th>
923            <td colspan="3">
924                <select name="in[bug_type]">
925                    <?php show_type_options($bug['bug_type'], /* deprecated */ true); ?>
926                </select>
927            </td>
928        </tr>
929        <tr>
930            <th class="details">Summary:</th>
931            <td colspan="3">
932                <input type="text" size="60" maxlength="80" name="in[sdesc]" value="<?php echo ($bug['status'] !== 'Spam') ? field('sdesc') : 'Hidden because of SPAM'; ?>">
933            </td>
934        </tr>
935        <tr>
936            <th class="details">From:</th>
937            <td colspan="3">
938                <?php echo ($bug['status'] !== 'Spam') ? spam_protect(field('email')) : 'Hidden because of SPAM'; ?>
939            </td>
940        </tr>
941        <tr>
942            <th class="details">New email:</th>
943            <td colspan="3">
944                <input type="text" size="40" maxlength="40" name="in[email]" value="<?php echo isset($_POST['in']) && isset($_POST['in']['email']) ? htmlspecialchars($_POST['in']['email']) : ''; ?>">
945            </td>
946        </tr>
947        <tr>
948            <th class="details">PHP Version:</th>
949            <td><input type="text" size="20" maxlength="100" name="in[php_version]" value="<?php echo field('php_version'); ?>"></td>
950            <th class="details">OS:</th>
951            <td><input type="text" size="20" maxlength="32" name="in[php_os]" value="<?php echo field('php_os'); ?>"></td>
952        </tr>
953    </table>
954
955    <p style="margin-bottom: 0em;">
956        <label for="ncomment" accesskey="m"><b>New<?php if ($edit == 1) echo "/Additional"; ?> Co<span class="accesskey">m</span>ment:</b></label>
957    </p>
958    <?php
959    if ($bug['block_user_comment'] == 'Y' && $logged_in != 'developer') {
960        echo 'Further comment on this bug is unnecessary.';
961    } elseif ($bug['status'] === 'Spam' && $logged_in != 'developer') {
962        echo 'This bug has a SPAM status, so no additional comments are needed.';
963    } else {
964    ?>
965        <textarea cols="80" rows="8" name="ncomment" id="ncomment" wrap="soft"><?php echo htmlspecialchars($ncomment); ?></textarea>
966    <?php
967    }
968    ?>
969
970    <p style="margin-top: 0em">
971        <input type="submit" name="preview" value="Preview">&nbsp;<input type="submit" value="Submit">
972    </p>
973
974</form>
975
976<?php } // if ($show_bug_info)
977} // if ($edit == 1 || $edit == 2)
978?>
979
980<?php
981    if ($edit == 3 && $bug['private'] == 'N') {
982
983    if ($bug['status'] === 'Spam') {
984        echo 'This bug has a SPAM status, so no additional comments are needed.';
985        response_footer();
986        exit;
987    }
988
989?>
990
991    <form name="comment" id="comment" action="bug.php" method="post">
992
993<?php if ($logged_in) { ?>
994    <div class="explain">
995        <h1>
996            <a href="patch-add.php?bug_id=<?php echo $bug_id; ?>">Click Here to Submit a Patch</a>
997            <input type="submit" name="subscribe_to_bug" value="Subscribe">
998            <input type="submit" name="unsubscribe_to_bug" value="Unsubscribe">
999        </h1>
1000    </div>
1001<?php } ?>
1002
1003<?php if (!isset($_POST['in'])) { ?>
1004
1005        <div class="explain">
1006            Anyone can comment on a bug. Have a simpler test case? Does it
1007            work for you on a different platform? Let us know!<br>
1008            Just going to say 'Me too!'? Don't clutter the database with that please
1009
1010<?php
1011            if (canvote($thanks, $bug['status'])) {
1012                echo ' &mdash; but make sure to <a href="bug.php?id=' , $bug_id , '">vote on the bug</a>';
1013            }
1014?>!
1015        </div>
1016
1017<?php }
1018
1019echo $preview;
1020
1021if (!$logged_in) {
1022    $_SESSION['answer'] = $captcha->getAnswer();
1023?>
1024    <table>
1025        <tr>
1026            <th class="details">Y<span class="accesskey">o</span>ur email address:<br><strong>MUST BE VALID</strong></th>
1027            <td class="form-input">
1028                <input type="text" size="40" maxlength="40" name="in[commentemail]" value="<?php echo isset($_POST['in']['commentemail']) ? htmlspecialchars($_POST['in']['commentemail'], ENT_COMPAT, 'UTF-8') : ''; ?>" accesskey="o">
1029            </td>
1030        </tr>
1031        <tr>
1032            <th>Solve the problem:<br><?php echo htmlspecialchars($captcha->getQuestion()); ?></th>
1033            <td class="form-input"><input type="text" name="captcha"></td>
1034        </tr>
1035        <tr>
1036            <th class="details">Subscribe to this entry?</th>
1037            <td class="form-input">
1038                <input type="submit" name="subscribe_to_bug" value="Subscribe">
1039                <input type="submit" name="unsubscribe_to_bug" value="Unsubscribe">
1040            </td>
1041        </tr>
1042    </table>
1043</div>
1044<?php } ?>
1045
1046    <div>
1047        <input type="hidden" name="id" value="<?php echo $bug_id; ?>">
1048        <input type="hidden" name="edit" value="<?php echo $edit; ?>">
1049
1050    <?php
1051    if ($bug['block_user_comment'] == 'Y' && $logged_in != 'developer') {
1052        echo 'Further comment on this bug is unnecessary.';
1053    } elseif ($bug['status'] === 'Spam' && $logged_in != 'developer') {
1054        echo 'This bug has a SPAM status, so no additional comments are needed.';
1055    } else {
1056    ?>
1057        <textarea cols="80" rows="10" name="ncomment" wrap="soft"><?php echo htmlspecialchars($ncomment); ?></textarea>
1058    <?php
1059    }
1060    ?>
1061
1062        <br><input type="submit" name="preview" value="Preview">&nbsp;<input type="submit" value="Submit">
1063    </div>
1064
1065    </form>
1066
1067<?php } ?>
1068
1069<?php
1070
1071// Display original report
1072if ($bug['ldesc']) {
1073    if (!$show_bug_info) {
1074        echo 'This bug report is marked as private.';
1075    } else if ($bug['status'] !== 'Spam') {
1076        output_note(0, $bug['submitted'], $bug['email'], $bug['ldesc'], 'comment', $bug['reporter_name'], false);
1077    } else {
1078        echo 'The original report has been hidden, due to the SPAM status.';
1079    }
1080}
1081
1082// Display patches
1083if ($show_bug_info && $bug_id != 'PREVIEW' && $bug['status'] !== 'Spam') {
1084    $p = $patchRepository->findAllByBugId($bug_id);
1085    $revs = [];
1086    echo "<h2>Patches</h2>\n";
1087
1088    foreach ($p as $patch) {
1089        $revs[$patch['patch']][] = [$patch['revision'], $patch['developer']];
1090    }
1091
1092    foreach ($revs as $name => $revisions)
1093    {
1094        $obsolete = $obsoletePatchRepository->findObsoletingPatches($bug_id, $name, $revisions[0][0]);
1095        $style = !empty($obsolete) ? ' style="background-color: yellow; text-decoration: line-through;" ' : '';
1096        $url_name = urlencode($name);
1097        $clean_name = clean($name);
1098        $formatted_date = format_date($revisions[0][0]);
1099        $submitter = spam_protect($revisions[0][1]);
1100
1101        echo <<< OUTPUT
1102<a href="patch-display.php?bug_id={$bug_id}&amp;patch={$url_name}&amp;revision=latest" {$style}>{$clean_name}</a>
1103(last revision {$formatted_date} by {$submitter})
1104<br>
1105OUTPUT;
1106    }
1107    if ($logged_in) {
1108        echo "<p><a href='patch-add.php?bug_id={$bug_id}'>Add a Patch</a></p>";
1109    }
1110
1111    $pullRequestRepository = $container->get(PullRequestRepository::class);
1112    $pulls = $pullRequestRepository->findAllByBugId($bug_id);
1113    echo "<h2>Pull Requests</h2>\n";
1114
1115    require "{$ROOT_DIR}/templates/listpulls.php";
1116    if ($logged_in) {
1117        echo "<p><a href='gh-pull-add.php?bug_id={$bug_id}'>Add a Pull Request</a></p>";
1118    }
1119}
1120
1121// Display comments
1122$commentRepository = $container->get(CommentRepository::class);
1123$bug_comments = is_int($bug_id) ? $commentRepository->findByBugId($bug_id) : [];
1124
1125if ($show_bug_info && is_array($bug_comments) && count($bug_comments) && $bug['status'] !== 'Spam') {
1126    $history_tabs = [
1127        'type_all'     => 'All',
1128        'type_comment' => 'Comments',
1129        'type_log'     => 'Changes',
1130        'type_svn'     => 'Git/SVN commits',
1131        'type_related' => 'Related reports'
1132    ];
1133
1134    if (!isset($_COOKIE['history_tab']) || !isset($history_tabs[$_COOKIE['history_tab']])) {
1135        $active_history_tab = 'type_all';
1136    } else {
1137        $active_history_tab = $_COOKIE['history_tab'];
1138    }
1139    echo '<h2 style="border-bottom:2px solid #666;margin-bottom:0;padding:5px 0;">History</h2>',
1140            "<div id='comment_filter' class='controls comments'>";
1141
1142    foreach ($history_tabs as $id => $label)
1143    {
1144        $class_extra = ($id == $active_history_tab) ? 'active' : '';
1145        echo "<span id='{$id}' class='control {$class_extra}' onclick='do_comment(this);'>{$label}</span>";
1146    }
1147
1148    echo '            </div>
1149            ';
1150
1151    echo "<div id='comments_view' style='clear:both;'>\n";
1152    foreach ($bug_comments as $row) {
1153        output_note($row['id'], $row['added'], $row['email'], $row['comment'], $row['comment_type'], $row['comment_name'], !($active_history_tab == 'type_all' || ('type_' . $row['comment_type']) == $active_history_tab));
1154    }
1155    echo "</div>\n";
1156}
1157
1158if ($bug_id == 'PREVIEW') {
1159?>
1160
1161<form action="report.php?package=<?php htmlspecialchars($_SESSION['bug_preview']['package_name']); ?>" method="post">
1162<?php foreach($_SESSION['bug_preview'] as $k => $v) {
1163    if ($k !== 'ldesc') {
1164        if ($k === 'ldesc_orig') {
1165            $k = 'ldesc';
1166        }
1167        echo "<input type='hidden' name='in[", htmlspecialchars($k, ENT_QUOTES), "]' value='", htmlentities($v, ENT_QUOTES, 'UTF-8'), "'>";
1168    }
1169}
1170    echo "<input type='hidden' name='captcha' value='", htmlspecialchars($_SESSION['captcha'], ENT_QUOTES), "'>";
1171?>
1172    <input type='submit' value='Send bug report'> <input type='submit' name='edit_after_preview' value='Edit'>
1173</form>
1174
1175<?php }
1176
1177$bug_JS = <<< bug_JS
1178<script src='js/util.js'></script>
1179<script src='https://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js'></script>
1180<script src="js/jquery.cookie.js"></script>
1181<script>
1182function do_comment(nd)
1183{
1184    $('#comment_filter > .control.active').removeClass("active");
1185    $(nd).addClass("active");
1186
1187    $.cookie('history_tab', nd.id, { expires: 365 });
1188
1189    if (nd.id == 'type_all') {
1190        $('#comments_view > .comment:hidden').show('slow');
1191    } else {
1192        $('#comments_view > .comment').each(function(i) {
1193            if ($(this).hasClass(nd.id)) {
1194                $(this).show('slow');
1195            } else {
1196                $(this).hide('slow');
1197            }
1198        });
1199    }
1200    return false;
1201}
1202</script>
1203bug_JS;
1204
1205if ($edit == 1) {
1206    $bug_JS .= '
1207<script src="js/jquery.autocomplete-min.js"></script>
1208<script src="js/userlisting.php"></script>
1209<script src="js/search.js"></script>
1210    ';
1211
1212}
1213
1214response_footer($bug_JS);
1215
1216// Helper functions
1217
1218function mark_related_bugs($from, $comment_name, $ncomment)
1219{
1220    global $bug_id;
1221
1222    $related = get_ticket_links($ncomment);
1223
1224    /**
1225     * Adds a new comment on the related bug pointing to the current report
1226     */
1227    foreach ($related as $bug) {
1228        bugs_add_comment($bug, $from, $comment_name,
1229            'Related To: Bug #'. $bug_id, 'related');
1230    }
1231}
1232
1233function link_to_people($email, $text)
1234{
1235    $domain = strstr($email, "@");
1236    if ($domain == "@php.net") {
1237        $username = strstr($email, "@", true);
1238        return '<a href="//people.php.net/' . urlencode($username) . '">' . $text . '</a>';
1239    }
1240    return $text;
1241}
1242
1243function output_note($com_id, $ts, $email, $comment, $comment_type, $comment_name, $is_hidden = false)
1244{
1245    global $edit, $bug_id, $dbh, $is_trusted_developer, $logged_in;
1246
1247    $display = (!$is_hidden) ? '' : 'style="display:none;"';
1248
1249    echo "<div class='comment type_{$comment_type}' {$display}>";
1250    echo '<a name="' , urlencode($ts) , '">&nbsp;</a>';
1251    echo "<strong>[" , format_date($ts) , "] ";
1252    echo link_to_people($email, spam_protect(htmlspecialchars($email))) , "</strong>\n";
1253
1254    switch ($comment_type)
1255    {
1256        case 'log':
1257            echo "<div class='log_note'>{$comment}</div>";
1258            break;
1259
1260        default:
1261            // Delete comment action only for trusted developers
1262            echo ($edit == 1 && $com_id !== 0 && $is_trusted_developer) ? "<a href='bug.php?id={$bug_id}&amp;edit=1&amp;delete_comment={$com_id}'>[delete]</a>\n" : '';
1263
1264            $comment = make_ticket_links(addlinks($comment));
1265            echo "<pre class='note'>{$comment}\n</pre>\n";
1266    }
1267
1268    echo '</div>';
1269}
1270
1271function delete_comment($bug_id, $com_id)
1272{
1273    global $dbh;
1274
1275    $dbh->prepare("DELETE FROM bugdb_comments WHERE bug='{$bug_id}' AND id='{$com_id}'")->execute();
1276}
1277
1278function control($num, $desc)
1279{
1280    global $bug_id, $edit;
1281
1282    $str = "<span id='control_{$num}' class='control";
1283    if ($edit == $num) {
1284        $str .= " active'>{$desc}";
1285    } else {
1286        $str .= "'><a href='bug.php?id={$bug_id}" . (($num) ? "&amp;edit={$num}" : '') . "'>{$desc}</a>";
1287    }
1288    return "{$str}</span>\n";
1289}
1290
1291function canvote($thanks, $status)
1292{
1293    return false;
1294}
1295