2022-08-20 20:30:04 +03:00
|
|
|
|
<?php declare(strict_types=1);
|
|
|
|
|
namespace openvk\Web\Presenters;
|
|
|
|
|
use Chandler\Session\Session;
|
|
|
|
|
use Chandler\Database\DatabaseConnection as DB;
|
|
|
|
|
use openvk\Web\Models\Repositories\{BugtrackerProducts, BugtrackerReports, BugtrackerComments, Users};
|
|
|
|
|
use openvk\Web\Models\Entities\{BugtrackerProduct, BugReport};
|
|
|
|
|
|
|
|
|
|
final class BugtrackerPresenter extends OpenVKPresenter
|
|
|
|
|
{
|
|
|
|
|
private $reports;
|
|
|
|
|
private $products;
|
|
|
|
|
private $comments;
|
|
|
|
|
|
|
|
|
|
function __construct(BugtrackerReports $reports, BugtrackerProducts $products, BugtrackerComments $comments)
|
|
|
|
|
{
|
|
|
|
|
$this->reports = $reports;
|
|
|
|
|
$this->products = $products;
|
|
|
|
|
$this->comments = $comments;
|
|
|
|
|
|
|
|
|
|
parent::__construct();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function renderIndex(): void
|
|
|
|
|
{
|
|
|
|
|
$this->assertUserLoggedIn();
|
2022-08-22 02:11:48 +03:00
|
|
|
|
$this->template->mode = in_array($this->queryParam("act"), ["list", "products", "new_product", "reporter", "new"]) ? $this->queryParam("act") : "list";
|
|
|
|
|
|
|
|
|
|
if($this->queryParam("act") === "show")
|
|
|
|
|
$this->redirect("/bug" . $this->queryParam("id"));
|
2022-08-20 20:30:04 +03:00
|
|
|
|
|
|
|
|
|
$this->template->user = $this->user;
|
2022-08-22 02:11:48 +03:00
|
|
|
|
$this->template->page = (int) ($this->queryParam("p") ?? 1);
|
2022-08-20 20:30:04 +03:00
|
|
|
|
|
2022-08-22 02:11:48 +03:00
|
|
|
|
$this->template->open_products = $this->products->getOpen();
|
2022-08-20 20:30:04 +03:00
|
|
|
|
|
2022-08-22 02:11:48 +03:00
|
|
|
|
switch ($this->template->mode) {
|
|
|
|
|
case 'reporter':
|
|
|
|
|
$this->template->reporter = (new Users)->get((int) $this->queryParam("id"));
|
|
|
|
|
$this->template->reporter_stats = [$this->reports->getCountByReporter((int) $this->queryParam("id")), $this->reports->getSuccCountByReporter((int) $this->queryParam("id"))];
|
|
|
|
|
|
|
|
|
|
$this->template->iterator = $this->reports->getByReporter((int) $this->queryParam("id"), $this->template->page);
|
|
|
|
|
$this->template->count = $this->reports->getCountByReporter((int) $this->queryParam("id"));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'products':
|
2022-08-23 02:40:11 +03:00
|
|
|
|
$this->template->filter = $this->queryParam("filter") ?? "all";
|
|
|
|
|
$this->template->count = $this->products->getCount($this->template->filter, $this->user->identity);
|
|
|
|
|
$this->template->iterator = $this->products->getFiltered($this->user->identity, $this->template->filter, $this->template->page);
|
2022-08-22 02:11:48 +03:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
2022-08-23 02:40:11 +03:00
|
|
|
|
$this->template->count = $this->reports->getReportsCount((int) $this->queryParam("product"), (int) $this->queryParam("priority"), $this->user->identity);
|
2022-08-22 02:11:48 +03:00
|
|
|
|
$this->template->iterator = $this->queryParam("product")
|
2022-08-23 02:40:11 +03:00
|
|
|
|
? $this->reports->getReports((int) $this->queryParam("product"), (int) $this->queryParam("priority"), $this->template->page, $this->user->identity)
|
|
|
|
|
: $this->reports->getAllReports($this->user->identity, $this->template->page);
|
2022-08-22 02:11:48 +03:00
|
|
|
|
break;
|
2022-08-20 20:30:04 +03:00
|
|
|
|
}
|
|
|
|
|
|
2022-08-22 02:11:48 +03:00
|
|
|
|
$this->template->isModerator = $this->user->identity->isBtModerator();
|
2022-08-20 20:30:04 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function renderView(int $id): void
|
|
|
|
|
{
|
|
|
|
|
$this->assertUserLoggedIn();
|
|
|
|
|
|
|
|
|
|
$this->template->user = $this->user;
|
|
|
|
|
|
2022-08-23 02:40:11 +03:00
|
|
|
|
if (!$this->reports->get($id)->getProduct()->hasAccess($this->template->user->identity))
|
|
|
|
|
$this->flashFail("err", tr("forbidden"));
|
|
|
|
|
|
2022-08-20 20:30:04 +03:00
|
|
|
|
if ($this->reports->get($id)) {
|
|
|
|
|
$this->template->bug = $this->reports->get($id);
|
|
|
|
|
$this->template->reporter = $this->template->bug->getReporter();
|
|
|
|
|
$this->template->comments = $this->comments->getByReport($this->template->bug);
|
|
|
|
|
|
2022-08-22 02:11:48 +03:00
|
|
|
|
$this->template->isModerator = $this->user->identity->isBtModerator();
|
2022-08-20 20:30:04 +03:00
|
|
|
|
} else {
|
2022-08-20 23:59:25 +03:00
|
|
|
|
$this->flashFail("err", tr("bug_tracker_report_not_found"));
|
2022-08-20 20:30:04 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function renderChangeStatus(int $report_id): void
|
|
|
|
|
{
|
|
|
|
|
$this->assertUserLoggedIn();
|
|
|
|
|
$this->willExecuteWriteAction();
|
|
|
|
|
|
|
|
|
|
$status = $this->postParam("status");
|
|
|
|
|
$comment = $this->postParam("text");
|
2022-08-20 21:45:28 +03:00
|
|
|
|
$points = $this->postParam("points-count");
|
2022-08-20 23:59:25 +03:00
|
|
|
|
$list = [
|
|
|
|
|
tr("bug_tracker_status_open"),
|
|
|
|
|
tr("bug_tracker_status_under_review"),
|
|
|
|
|
tr("bug_tracker_status_in_progress"),
|
|
|
|
|
tr("bug_tracker_status_fixed"),
|
|
|
|
|
tr("bug_tracker_status_closed"),
|
|
|
|
|
tr("bug_tracker_status_requires_adjustment"),
|
|
|
|
|
tr("bug_tracker_status_locked"),
|
|
|
|
|
tr("bug_tracker_status_rejected")
|
|
|
|
|
];
|
2022-08-20 20:30:04 +03:00
|
|
|
|
|
|
|
|
|
$report = (new BugtrackerReports)->get($report_id);
|
|
|
|
|
$report->setStatus($status);
|
2022-08-20 21:45:28 +03:00
|
|
|
|
|
|
|
|
|
if ($points)
|
|
|
|
|
DB::i()->getContext()->query("UPDATE `profiles` SET `coins` = `coins` + " . $points . " WHERE `id` = " . $report->getReporter()->getId());
|
|
|
|
|
|
2022-08-20 20:30:04 +03:00
|
|
|
|
$report->save();
|
|
|
|
|
|
2022-08-21 23:30:25 +03:00
|
|
|
|
$this->createComment($report, $comment, tr("bug_tracker_new_report_status") . " — $list[$status]", TRUE, FALSE, $points);
|
2022-08-20 23:59:25 +03:00
|
|
|
|
$this->flashFail("succ", tr("changes_saved"), tr("bug_tracker_new_report_status") . " — $list[$status]");
|
2022-08-20 20:30:04 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function renderChangePriority(int $report_id): void
|
|
|
|
|
{
|
|
|
|
|
$this->assertUserLoggedIn();
|
|
|
|
|
$this->willExecuteWriteAction();
|
|
|
|
|
|
|
|
|
|
$priority = $this->postParam("priority");
|
|
|
|
|
$comment = $this->postParam("text");
|
2022-08-20 21:45:28 +03:00
|
|
|
|
$points = $this->postParam("points-count");
|
2022-08-20 23:59:25 +03:00
|
|
|
|
$list = [
|
|
|
|
|
tr("bug_tracker_priority_feature"),
|
|
|
|
|
tr("bug_tracker_priority_low"),
|
|
|
|
|
tr("bug_tracker_priority_medium"),
|
|
|
|
|
tr("bug_tracker_priority_high"),
|
|
|
|
|
tr("bug_tracker_priority_critical"),
|
|
|
|
|
tr("bug_tracker_priority_vulnerability")
|
|
|
|
|
];
|
2022-08-20 20:30:04 +03:00
|
|
|
|
|
|
|
|
|
$report = (new BugtrackerReports)->get($report_id);
|
|
|
|
|
$report->setPriority($priority);
|
2022-08-20 21:45:28 +03:00
|
|
|
|
|
|
|
|
|
if ($points)
|
|
|
|
|
DB::i()->getContext()->query("UPDATE `profiles` SET `coins` = `coins` + " . $points . " WHERE `id` = " . $report->getReporter()->getId());
|
|
|
|
|
|
2022-08-20 20:30:04 +03:00
|
|
|
|
$report->save();
|
|
|
|
|
|
2022-08-21 23:30:25 +03:00
|
|
|
|
$this->createComment($report, $comment, tr("bug_tracker_new_report_priority") . " — $list[$priority]", TRUE, FALSE, $points);
|
2022-08-20 23:59:25 +03:00
|
|
|
|
$this->flashFail("succ", tr("changes_saved"), tr("bug_tracker_new_report_priority") . " — $list[$priority]");
|
2022-08-20 20:30:04 +03:00
|
|
|
|
}
|
|
|
|
|
|
2022-08-21 23:30:25 +03:00
|
|
|
|
function createComment(?BugReport $report, string $text, string $label = "", bool $is_moder = FALSE, bool $is_hidden = FALSE, string $point_actions = NULL)
|
2022-08-20 20:30:04 +03:00
|
|
|
|
{
|
2022-08-22 02:11:48 +03:00
|
|
|
|
$moder = $this->user->identity->isBtModerator();
|
2022-08-20 20:30:04 +03:00
|
|
|
|
|
|
|
|
|
if (!$text && !$label)
|
2022-08-20 23:59:25 +03:00
|
|
|
|
$this->flashFail("err", tr("error"), tr("bug_tracker_empty_comment"));
|
2022-08-20 20:30:04 +03:00
|
|
|
|
|
2022-08-22 02:11:48 +03:00
|
|
|
|
if (in_array($report->getRawStatus(), [5, 6]) && !$moder)
|
2022-08-20 23:59:25 +03:00
|
|
|
|
$this->flashFail("err", tr("forbidden"));
|
2022-08-20 20:30:04 +03:00
|
|
|
|
|
|
|
|
|
DB::i()->getContext()->table("bt_comments")->insert([
|
|
|
|
|
"report" => $report->getId(),
|
|
|
|
|
"author" => $this->user->identity->getId(),
|
|
|
|
|
"is_moder" => $moder === $is_moder,
|
|
|
|
|
"is_hidden" => $moder === $is_hidden,
|
2022-08-21 23:30:25 +03:00
|
|
|
|
"point_actions" => $point_actions,
|
2022-08-20 20:30:04 +03:00
|
|
|
|
"text" => $text,
|
2022-08-22 02:11:48 +03:00
|
|
|
|
"label" => $label,
|
|
|
|
|
"created" => time()
|
2022-08-20 20:30:04 +03:00
|
|
|
|
]);
|
|
|
|
|
|
2022-08-20 23:59:25 +03:00
|
|
|
|
$this->flashFail("succ", tr("bug_tracker_success"), tr("bug_tracker_comment_sent"));
|
2022-08-20 20:30:04 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function renderAddComment(int $report_id): void
|
|
|
|
|
{
|
|
|
|
|
$this->assertUserLoggedIn();
|
|
|
|
|
$this->willExecuteWriteAction();
|
|
|
|
|
|
|
|
|
|
$text = $this->postParam("text");
|
|
|
|
|
$is_moder = (bool) $this->postParam("is_moder");
|
|
|
|
|
$is_hidden = (bool) $this->postParam("is_hidden");
|
|
|
|
|
|
|
|
|
|
$this->createComment($this->reports->get($report_id), $text, "", $is_moder, $is_hidden);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function renderCreate(): void
|
|
|
|
|
{
|
|
|
|
|
$this->assertUserLoggedIn();
|
|
|
|
|
$this->willExecuteWriteAction();
|
|
|
|
|
|
|
|
|
|
$title = $this->postParam("title");
|
|
|
|
|
$text = $this->postParam("text");
|
|
|
|
|
$priority = $this->postParam("priority");
|
|
|
|
|
$product = $this->postParam("product");
|
|
|
|
|
$device = $this->postParam("device");
|
|
|
|
|
|
|
|
|
|
if (!$title || !$text || !$priority || !$product || !$device)
|
2022-08-20 23:59:25 +03:00
|
|
|
|
$this->flashFail("err", tr("error"), tr("bug_tracker_fields_error"));
|
2022-08-20 20:30:04 +03:00
|
|
|
|
|
|
|
|
|
$id = DB::i()->getContext()->table("bugs")->insert([
|
|
|
|
|
"reporter" => $this->user->identity->getId(),
|
|
|
|
|
"title" => $title,
|
|
|
|
|
"text" => $text,
|
|
|
|
|
"product_id" => $product,
|
|
|
|
|
"device" => $device,
|
|
|
|
|
"priority" => $priority,
|
|
|
|
|
"created" => time()
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$this->redirect("/bug$id");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function renderCreateProduct(): void
|
|
|
|
|
{
|
|
|
|
|
$this->assertUserLoggedIn();
|
|
|
|
|
$this->willExecuteWriteAction();
|
|
|
|
|
|
2022-08-23 02:40:11 +03:00
|
|
|
|
if (!$this->user->identity->isBtModerator())
|
2022-08-20 23:59:25 +03:00
|
|
|
|
$this->flashFail("err", tr("forbidden"));
|
2022-08-20 20:30:04 +03:00
|
|
|
|
|
|
|
|
|
$title = $this->postParam("title");
|
|
|
|
|
$description = $this->postParam("description");
|
2022-08-23 02:40:11 +03:00
|
|
|
|
$is_closed = (bool) $this->postParam("is_closed");
|
|
|
|
|
$is_private = (bool) $this->postParam("is_private");
|
2022-08-20 20:30:04 +03:00
|
|
|
|
|
|
|
|
|
DB::i()->getContext()->table("bt_products")->insert([
|
|
|
|
|
"creator_id" => $this->user->identity->getId(),
|
|
|
|
|
"title" => $title,
|
2022-08-21 23:37:33 +03:00
|
|
|
|
"description" => $description,
|
2022-08-23 02:40:11 +03:00
|
|
|
|
"created" => time(),
|
|
|
|
|
"closed" => $is_closed,
|
|
|
|
|
"private" => $is_private
|
2022-08-20 20:30:04 +03:00
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$this->redirect("/bugtracker?act=products");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function renderReproduced(int $report_id): void
|
|
|
|
|
{
|
|
|
|
|
$this->assertUserLoggedIn();
|
|
|
|
|
$this->willExecuteWriteAction();
|
|
|
|
|
|
|
|
|
|
$report = (new BugtrackerReports)->get($report_id);
|
|
|
|
|
|
|
|
|
|
if ($report->getReporter()->getId() === $this->user->identity->getId())
|
2022-08-20 23:59:25 +03:00
|
|
|
|
$this->flashFail("err", tr("forbidden"));
|
2022-08-20 20:30:04 +03:00
|
|
|
|
|
2022-08-22 12:43:36 +03:00
|
|
|
|
DB::i()->getContext()->table("bugs")->where("id", $report_id)->update(["reproduced" => $report->getReproducedCount() + 1]);
|
2022-08-20 20:30:04 +03:00
|
|
|
|
|
2022-08-20 23:59:25 +03:00
|
|
|
|
$this->flashFail("succ", tr("bug_tracker_success"), tr("bug_tracker_reproduced_text"));
|
2022-08-20 20:30:04 +03:00
|
|
|
|
}
|
2022-08-23 02:40:11 +03:00
|
|
|
|
|
|
|
|
|
function renderManageAccess(int $product_id): void
|
|
|
|
|
{
|
|
|
|
|
$this->assertUserLoggedIn();
|
|
|
|
|
$this->willExecuteWriteAction();
|
|
|
|
|
|
|
|
|
|
if (!$this->user->identity->isBtModerator())
|
|
|
|
|
$this->flashFail("err", tr("forbidden"));
|
|
|
|
|
|
|
|
|
|
$user = (new Users)->get((int) $this->postParam("uid"));
|
|
|
|
|
$product = $this->products->get($product_id);
|
|
|
|
|
$action = $this->postParam("action");
|
|
|
|
|
|
|
|
|
|
if ($action === "give") {
|
|
|
|
|
if (!$product->isPrivate() || $product->hasAccess($user))
|
|
|
|
|
$this->flashFail("err", "Ошибка", $user->getCanonicalName() . " уже имеет доступ к продукту " . $product->getCanonicalName());
|
|
|
|
|
|
|
|
|
|
DB::i()->getContext()->table("bt_products_access")->insert([
|
|
|
|
|
"created" => time(),
|
|
|
|
|
"tester" => $user->getId(),
|
|
|
|
|
"product" => $product_id,
|
|
|
|
|
"moderator" => $this->user->identity->getId()
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$this->flashFail("succ", "Успех", $user->getCanonicalName() . " теперь имеет доступ к продукту " . $product->getCanonicalName());
|
|
|
|
|
} else {
|
|
|
|
|
if ($user->isBtModerator())
|
|
|
|
|
$this->flashFail("err", "Ошибка", "Невозможно забрать доступ к продукту у модератора.");
|
|
|
|
|
|
|
|
|
|
if (!$product->hasAccess($user))
|
|
|
|
|
$this->flashFail("err", "Ошибка", $user->getCanonicalName() . " и так не имеет доступа к продукту " . $product->getCanonicalName());
|
|
|
|
|
|
|
|
|
|
DB::i()->getContext()->table("bt_products_access")->where([
|
|
|
|
|
"tester" => $user->getId(),
|
|
|
|
|
"product" => $product_id,
|
|
|
|
|
])->delete();
|
|
|
|
|
|
|
|
|
|
$this->flashFail("succ", "Успех", $user->getCanonicalName() . " теперь не имеет доступа к продукту " . $product->getCanonicalName());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function renderManagePrivacy(int $product_id): void
|
|
|
|
|
{
|
|
|
|
|
$this->assertUserLoggedIn();
|
|
|
|
|
$this->willExecuteWriteAction();
|
|
|
|
|
|
|
|
|
|
if (!$this->user->identity->isBtModerator())
|
|
|
|
|
$this->flashFail("err", tr("forbidden"));
|
|
|
|
|
|
|
|
|
|
$user = (new Users)->get((int) $this->postParam("uid"));
|
|
|
|
|
$product = $this->products->get($product_id);
|
|
|
|
|
$action = $this->postParam("action");
|
|
|
|
|
|
|
|
|
|
if ($action == "open") {
|
|
|
|
|
if (!$product->isPrivate())
|
|
|
|
|
$this->flashFail("err", "Ошибка", "Продукт " . $product->getCanonicalName() . " и так открытый.");
|
|
|
|
|
|
|
|
|
|
DB::i()->getContext()->table("bt_products")->where("id", $product_id)->update(["private" => 0]);
|
|
|
|
|
|
|
|
|
|
$this->flashFail("succ", "Успех", "Продукт " . $product->getCanonicalName() . " теперь открытый.");
|
|
|
|
|
} else {
|
|
|
|
|
if ($product->isPrivate())
|
|
|
|
|
$this->flashFail("err", "Ошибка", "Продукт " . $product->getCanonicalName() . " и так приватный.");
|
|
|
|
|
|
|
|
|
|
DB::i()->getContext()->table("bt_products")->where("id", $product_id)->update(["private" => 1]);
|
|
|
|
|
|
|
|
|
|
$this->flashFail("succ", "Успех", "Продукт " . $product->getCanonicalName() . " теперь приватный.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function renderManageStatus(int $product_id): void
|
|
|
|
|
{
|
|
|
|
|
$this->assertUserLoggedIn();
|
|
|
|
|
$this->willExecuteWriteAction();
|
|
|
|
|
|
|
|
|
|
if (!$this->user->identity->isBtModerator())
|
|
|
|
|
$this->flashFail("err", tr("forbidden"));
|
|
|
|
|
|
|
|
|
|
$user = (new Users)->get((int) $this->postParam("uid"));
|
|
|
|
|
$product = $this->products->get($product_id);
|
|
|
|
|
$action = $this->postParam("action");
|
|
|
|
|
|
|
|
|
|
if ($action == "open") {
|
|
|
|
|
if (!$product->isClosed())
|
|
|
|
|
$this->flashFail("err", "Ошибка", "Продукт " . $product->getCanonicalName() . " и так открытый.");
|
|
|
|
|
|
|
|
|
|
DB::i()->getContext()->table("bt_products")->where("id", $product_id)->update(["closed" => 0]);
|
|
|
|
|
|
|
|
|
|
$this->flashFail("succ", "Успех", "Продукт " . $product->getCanonicalName() . " теперь открытый.");
|
|
|
|
|
} else {
|
|
|
|
|
if ($product->isClosed())
|
|
|
|
|
$this->flashFail("err", "Ошибка", "Продукт " . $product->getCanonicalName() . " и так закрытый.");
|
|
|
|
|
|
|
|
|
|
DB::i()->getContext()->table("bt_products")->where("id", $product_id)->update(["closed" => 1]);
|
|
|
|
|
|
|
|
|
|
$this->flashFail("succ", "Успех", "Продукт " . $product->getCanonicalName() . " теперь закрытый.");
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-08-20 20:30:04 +03:00
|
|
|
|
}
|