main • app/controllers/IssueController.php
<?php
class IssueController
{
public function list(int $repoId): void {
$db = db();
$uid = auth_user_id();
if (!repo_can_read($db, $repoId, $uid)) { http_response_code(403); exit("403"); }
$repo = repo_get($db, $repoId);
$st = $db->prepare("SELECT i.*, u.username as author_name
FROM issues i
JOIN users u ON u.id=i.author_id
WHERE i.repo_id=?
ORDER BY i.id DESC");
$st->execute([$repoId]);
$issues = $st->fetchAll();
$canWrite = repo_can_write($db, $repoId, $uid);
$title = $repo['owner_username'] . "/" . $repo['name'] . " / Issues";
require __DIR__ . '/../views/issues/list.php';
}
public function showCreate(int $repoId): void {
$db = db();
$uid = require_auth();
if (!repo_can_write($db, $repoId, $uid)) { http_response_code(403); exit("403"); }
$repo = repo_get($db, $repoId);
$title = $repo['owner_username'] . "/" . $repo['name'] . " / New Issue";
require __DIR__ . '/../views/issues/create.php';
}
public function create(int $repoId): void {
$db = db();
$uid = require_auth();
csrf_verify();
if (!repo_can_write($db, $repoId, $uid)) { http_response_code(403); exit("403"); }
$title = trim($_POST['title'] ?? '');
$body = trim($_POST['body'] ?? '');
if ($title === '' || strlen($title) > 255) {
flash_set('err','Title required (max 255).');
redirect("/r/$repoId/issues/new");
}
$st = $db->prepare("SELECT COALESCE(MAX(number),0)+1 as nextn FROM issues WHERE repo_id=?");
$st->execute([$repoId]);
$num = (int)($st->fetch()['nextn'] ?? 1);
$st = $db->prepare("INSERT INTO issues (repo_id,number,title,body,state,author_id,created_at,updated_at)
VALUES (?,?,?,?,'open',?,?,?)");
$st->execute([$repoId,$num,$title,$body,$uid,now(),now()]);
flash_set('ok',"Issue created (#$num).");
redirect("/r/$repoId/issues/$num");
}
public function show(int $repoId, int $number): void {
$db = db();
$uid = auth_user_id();
if (!repo_can_read($db, $repoId, $uid)) { http_response_code(403); exit("403"); }
$repo = repo_get($db, $repoId);
$st = $db->prepare("SELECT i.*, u.username as author_name
FROM issues i JOIN users u ON u.id=i.author_id
WHERE i.repo_id=? AND i.number=? LIMIT 1");
$st->execute([$repoId,$number]);
$issue = $st->fetch();
if (!$issue) { http_response_code(404); exit("Issue not found"); }
$st = $db->prepare("SELECT c.*, u.username as user_name
FROM issue_comments c JOIN users u ON u.id=c.user_id
WHERE c.issue_id=? ORDER BY c.id ASC");
$st->execute([(int)$issue['id']]);
$comments = $st->fetchAll();
$canWrite = repo_can_write($db, $repoId, $uid);
$title = $repo['owner_username'] . "/" . $repo['name'] . " / Issue #" . $number;
require __DIR__ . '/../views/issues/show.php';
}
public function comment(int $repoId, int $number): void {
$db = db();
$uid = require_auth();
csrf_verify();
if (!repo_can_write($db, $repoId, $uid)) { http_response_code(403); exit("403"); }
$st = $db->prepare("SELECT id FROM issues WHERE repo_id=? AND number=? LIMIT 1");
$st->execute([$repoId,$number]);
$row = $st->fetch();
if (!$row) { http_response_code(404); exit("Issue not found"); }
$body = trim($_POST['body'] ?? '');
if ($body === '') { flash_set('err','Comment cannot be empty.'); redirect("/r/$repoId/issues/$number"); }
$st = $db->prepare("INSERT INTO issue_comments (issue_id,user_id,body,created_at) VALUES (?,?,?,?)");
$st->execute([(int)$row['id'], $uid, $body, now()]);
$st = $db->prepare("UPDATE issues SET updated_at=? WHERE id=?");
$st->execute([now(), (int)$row['id']]);
flash_set('ok','Comment added.');
redirect("/r/$repoId/issues/$number");
}
public function toggleState(int $repoId, int $number): void {
$db = db();
$uid = require_auth();
csrf_verify();
if (!repo_can_write($db, $repoId, $uid)) { http_response_code(403); exit("403"); }
$st = $db->prepare("SELECT id, state FROM issues WHERE repo_id=? AND number=? LIMIT 1");
$st->execute([$repoId,$number]);
$issue = $st->fetch();
if (!$issue) { http_response_code(404); exit("Issue not found"); }
$new = ($issue['state'] === 'open') ? 'closed' : 'open';
$st = $db->prepare("UPDATE issues SET state=?, updated_at=? WHERE id=?");
$st->execute([$new, now(), (int)$issue['id']]);
flash_set('ok', "Issue $new.");
redirect("/r/$repoId/issues/$number");
}
}