mirror of
https://github.com/openvk/openvk
synced 2025-07-02 22:09:53 +03:00
Reports enhancements
This commit is contained in:
parent
6f9cbdfa84
commit
84c732674f
7 changed files with 296 additions and 33 deletions
|
@ -4,7 +4,7 @@ use openvk\Web\Util\DateTime;
|
|||
use Nette\Database\Table\ActiveRow;
|
||||
use openvk\Web\Models\RowModel;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
use openvk\Web\Models\Repositories\{Applications, Comments, Notes, Users, Posts, Photos, Videos, Clubs};
|
||||
use openvk\Web\Models\Repositories\{Applications, Comments, Notes, Reports, Users, Posts, Photos, Videos, Clubs};
|
||||
use Chandler\Database\DatabaseConnection as DB;
|
||||
use Nette\InvalidStateException as ISE;
|
||||
use Nette\Database\Table\Selection;
|
||||
|
@ -81,6 +81,11 @@ class Report extends RowModel
|
|||
return (new Posts)->get($this->getContentId())->getOwner();
|
||||
}
|
||||
|
||||
function getReportAuthor(): User
|
||||
{
|
||||
return (new Users)->get($this->getRecord()->user_id);
|
||||
}
|
||||
|
||||
function banUser($initiator)
|
||||
{
|
||||
$reason = $this->getContentType() !== "user" ? ("**content-" . $this->getContentType() . "-" . $this->getContentId() . "**") : ("Подозрительная активность");
|
||||
|
@ -95,6 +100,42 @@ class Report extends RowModel
|
|||
$this->getAuthor()->adminNotify("Ваш контент, который вы опубликовали $pubTime ($name) был удалён модераторами инстанса. За повторные или серьёзные нарушения вас могут заблокировать.");
|
||||
$this->getContentObject()->delete($this->getContentType() !== "app");
|
||||
}
|
||||
|
||||
$this->delete();
|
||||
}
|
||||
|
||||
function getDuplicates(): \Traversable
|
||||
{
|
||||
return (new Reports)->getDuplicates($this->getContentType(), $this->getContentId(), $this->getId());
|
||||
}
|
||||
|
||||
function getDuplicatesCount(): int
|
||||
{
|
||||
return count(iterator_to_array($this->getDuplicates()));
|
||||
}
|
||||
|
||||
function hasDuplicates(): bool
|
||||
{
|
||||
return $this->getDuplicatesCount() > 0;
|
||||
}
|
||||
|
||||
function getContentName(): string
|
||||
{
|
||||
if (method_exists($this->getContentObject(), "getCanonicalName"))
|
||||
return $this->getContentObject()->getCanonicalName();
|
||||
|
||||
return $this->getContentType() . " #" . $this->getContentId();
|
||||
}
|
||||
|
||||
public function delete(bool $softly = true): void
|
||||
{
|
||||
if ($this->hasDuplicates()) {
|
||||
foreach ($this->getDuplicates() as $duplicate) {
|
||||
$duplicate->setDeleted(1);
|
||||
$duplicate->save();
|
||||
}
|
||||
}
|
||||
|
||||
$this->setDeleted(1);
|
||||
$this->save();
|
||||
}
|
||||
|
|
|
@ -20,15 +20,22 @@ class Reports
|
|||
return is_null($ar) ? NULL : new Report($ar);
|
||||
}
|
||||
|
||||
function getReports(int $state = 0, int $page = 1): \Traversable
|
||||
function getReports(int $state = 0, int $page = 1, ?string $type = NULL, ?bool $pagination = true): \Traversable
|
||||
{
|
||||
foreach($this->reports->where(["deleted" => 0])->order("created DESC")->page($page, 15) as $t)
|
||||
$filter = ["deleted" => 0];
|
||||
if ($type) $filter["type"] = $type;
|
||||
|
||||
$reports = $this->reports->where($filter)->order("created DESC")->group("target_id, type");
|
||||
if ($pagination)
|
||||
$reports = $reports->page($page, 15);
|
||||
|
||||
foreach($reports as $t)
|
||||
yield new Report($t);
|
||||
}
|
||||
|
||||
function getReportsCount(int $state = 0): int
|
||||
{
|
||||
return sizeof($this->reports->where(["deleted" => 0, "type" => $state]));
|
||||
return sizeof($this->reports->where(["deleted" => 0, "type" => $state])->group("target_id, type"));
|
||||
}
|
||||
|
||||
function get(int $id): ?Report
|
||||
|
@ -45,6 +52,16 @@ class Reports
|
|||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
function getDuplicates(string $type, int $target_id, ?int $orig = NULL, ?int $user_id = NULL): \Traversable
|
||||
{
|
||||
$filter = ["deleted" => 0, "type" => $type, "target_id" => $target_id];
|
||||
if ($orig) $filter[] = "id != $orig";
|
||||
if ($user_id) $filter["user_id"] = $user_id;
|
||||
|
||||
foreach ($this->reports->where($filter) as $report)
|
||||
yield new Report($report);
|
||||
}
|
||||
|
||||
use \Nette\SmartObject;
|
||||
}
|
||||
|
|
|
@ -20,15 +20,52 @@ final class ReportPresenter extends OpenVKPresenter
|
|||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->assertPermission('openvk\Web\Models\Entities\TicketReply', 'write', 0);
|
||||
if ($_SERVER["REQUEST_METHOD"] === "POST")
|
||||
$this->assertNoCSRF();
|
||||
|
||||
$this->template->reports = $this->reports->getReports(0, (int)($this->queryParam("p") ?? 1));
|
||||
$this->template->count = $this->reports->getReportsCount();
|
||||
$act = in_array($this->queryParam("act"), ["post", "photo", "video", "group", "comment", "note", "app", "user"]) ? $this->queryParam("act") : NULL;
|
||||
|
||||
if (!$this->queryParam("orig")) {
|
||||
$this->template->reports = $this->reports->getReports(0, (int)($this->queryParam("p") ?? 1), $act, $_SERVER["REQUEST_METHOD"] !== "POST");
|
||||
$this->template->count = $this->reports->getReportsCount();
|
||||
} else {
|
||||
$orig = $this->reports->get((int) $this->queryParam("orig"));
|
||||
if (!$orig) $this->redirect("/scumfeed");
|
||||
|
||||
$this->template->reports = $orig->getDuplicates();
|
||||
$this->template->count = $orig->getDuplicatesCount();
|
||||
$this->template->orig = $orig->getId();
|
||||
}
|
||||
$this->template->paginatorConf = (object) [
|
||||
"count" => $this->template->count,
|
||||
"page" => $this->queryParam("p") ?? 1,
|
||||
"amount" => NULL,
|
||||
"perPage" => 15,
|
||||
];
|
||||
$this->template->mode = $act ?? "all";
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$reports = [];
|
||||
foreach ($this->reports->getReports(0, 0, $act, false) as $report) {
|
||||
$reports[] = [
|
||||
"id" => $report->getId(),
|
||||
"author" => [
|
||||
"id" => $report->getReportAuthor()->getId(),
|
||||
"url" => $report->getReportAuthor()->getURL(),
|
||||
"name" => $report->getReportAuthor()->getCanonicalName(),
|
||||
"is_female" => $report->getReportAuthor()->isFemale()
|
||||
],
|
||||
"content" => [
|
||||
"name" => $report->getContentName(),
|
||||
"type" => $report->getContentType(),
|
||||
"id" => $report->getContentId(),
|
||||
"url" => $report->getContentType() === "user" ? (new Users)->get((int) $report->getContentId())->getURL() : NULL
|
||||
],
|
||||
"duplicates" => $report->getDuplicatesCount(),
|
||||
];
|
||||
}
|
||||
$this->returnJson(["reports" => $reports]);
|
||||
}
|
||||
}
|
||||
|
||||
function renderView(int $id): void
|
||||
|
@ -52,13 +89,15 @@ final class ReportPresenter extends OpenVKPresenter
|
|||
exit(json_encode([ "error" => tr("error_segmentation") ]));
|
||||
|
||||
if(in_array($this->queryParam("type"), ["post", "photo", "video", "group", "comment", "note", "app", "user"])) {
|
||||
$report = new Report;
|
||||
$report->setUser_id($this->user->id);
|
||||
$report->setTarget_id($id);
|
||||
$report->setType($this->queryParam("type"));
|
||||
$report->setReason($this->queryParam("reason"));
|
||||
$report->setCreated(time());
|
||||
$report->save();
|
||||
if (count(iterator_to_array($this->reports->getDuplicates($this->queryParam("type"), $id, NULL, $this->user->id))) <= 0) {
|
||||
$report = new Report;
|
||||
$report->setUser_id($this->user->id);
|
||||
$report->setTarget_id($id);
|
||||
$report->setType($this->queryParam("type"));
|
||||
$report->setReason($this->queryParam("reason"));
|
||||
$report->setCreated(time());
|
||||
$report->save();
|
||||
}
|
||||
|
||||
exit(json_encode([ "reason" => $this->queryParam("reason") ]));
|
||||
} else {
|
||||
|
|
|
@ -12,16 +12,24 @@
|
|||
{include size, x => $dat}
|
||||
{/ifset}
|
||||
|
||||
{ifset before_content}
|
||||
{include before_content, x => $dat}
|
||||
{/ifset}
|
||||
|
||||
{ifset specpage}
|
||||
{include specpage, x => $dat}
|
||||
{else}
|
||||
<div class="container_gray">
|
||||
{var $data = is_array($iterator) ? $iterator : iterator_to_array($iterator)}
|
||||
|
||||
{ifset top}
|
||||
{include top, x => $dat}
|
||||
{/ifset}
|
||||
|
||||
{if sizeof($data) > 0}
|
||||
<div class="content" n:foreach="$data as $dat">
|
||||
<table>
|
||||
<tbody>
|
||||
<tbody n:attr="id => is_null($table_body_id) ? NULL : $table_body_id">
|
||||
<tr>
|
||||
<td valign="top">
|
||||
<a href="{include link, x => $dat}">
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
{extends "../@listView.xml"}
|
||||
{var iterator = iterator_to_array($reports)}
|
||||
{var page = $paginatorConf->page}
|
||||
{var table_body_id = "reports"}
|
||||
|
||||
{block tabs}{include "../NoSpam/Tabs.xml", mode => "reports"}{/block}
|
||||
{block before_content}
|
||||
{include "./Tabs.xml", mode => $mode}
|
||||
{/block}
|
||||
|
||||
{block title}{_list_of_reports}{/block}
|
||||
|
||||
|
@ -14,6 +18,12 @@
|
|||
|
||||
{/block}
|
||||
|
||||
{block top}
|
||||
{if !is_null($orig)}
|
||||
<h4>Дубликаты жалобы №{$orig}</h4>
|
||||
{/if}
|
||||
{/block}
|
||||
|
||||
{* BEGIN ELEMENTS DESCRIPTION *}
|
||||
|
||||
{block link|strip|stripHtml}
|
||||
|
@ -25,25 +35,26 @@
|
|||
{/block}
|
||||
|
||||
{block name}
|
||||
{$x->getAuthor()->getCanonicalName()}
|
||||
Жалоба №{$x->getId()}
|
||||
{/block}
|
||||
|
||||
{block description}
|
||||
{if $x->getContentType() == "post"}
|
||||
{_text_of_the_post}: {$x->getContentObject()->getText()}
|
||||
{elseif $x->getContentType() == "photo"}
|
||||
{_photo}
|
||||
{elseif $x->getContentType() == "video"}
|
||||
{_video}
|
||||
{elseif $x->getContentType() == "group"}
|
||||
{_groups}
|
||||
{elseif $x->getContentType() == "comment"}
|
||||
{_comment}
|
||||
{elseif $x->getContentType() == "app"}
|
||||
{_app}
|
||||
{elseif $x->getContentType() == "user"}
|
||||
{_users}
|
||||
{else}
|
||||
Unknown
|
||||
<a href="{$x->getReportAuthor()->getURL()}">
|
||||
{$x->getReportAuthor()->getCanonicalName()}
|
||||
</a>
|
||||
пожаловал{!$x->getReportAuthor()->isFemale() ? 'ся' : 'ась'} на
|
||||
{if $x->getContentType() === "user"}<a href="{$x->getContentObject()->getURL()}">{/if}
|
||||
{$x->getContentName()}
|
||||
{if $x->getContentType() === "user"}</a>{/if}
|
||||
|
||||
{if $x->hasDuplicates() && !$orig}
|
||||
<br />
|
||||
<b>Другие жалобы на этот контент: <a href="/scumfeed?orig={$x->getId()}">{$x->getDuplicatesCount()} шт.</a></b>
|
||||
{/if}
|
||||
{/block}
|
||||
|
||||
{block bottom}
|
||||
<center id="reports-loader" style="display: none; padding: 64px;">
|
||||
<img src="/assets/packages/static/openvk/img/loading_mini.gif" style="width: 40px;">
|
||||
</center>
|
||||
{/block}
|
||||
|
|
145
Web/Presenters/templates/Report/Tabs.xml
Normal file
145
Web/Presenters/templates/Report/Tabs.xml
Normal file
|
@ -0,0 +1,145 @@
|
|||
<style>
|
||||
.reportsTabs {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
row-gap: 4px;
|
||||
gap: 4px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.reportsTabs .tab {
|
||||
display: flex;
|
||||
flex: 0 0 calc(16.66% - 20px);
|
||||
justify-content: center;
|
||||
border-radius: 3px;
|
||||
padding: 4px;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
<center class="tabs reportsTabs stupid-fix">
|
||||
<div n:attr="id => ($mode === 'all' ? 'activetabs' : 'ki')" class="tab" mode="all">
|
||||
<a n:attr="id => ($mode === 'all' ? 'act_tab_a' : 'ki')" mode="all">Все</a>
|
||||
</div>
|
||||
<div n:attr="id => ($mode === 'post' ? 'activetabs' : 'ki')" class="tab" mode="post">
|
||||
<a n:attr="id => ($mode === 'post' ? 'act_tab_a' : 'ki')">Записи</a>
|
||||
</div>
|
||||
<div n:attr="id => ($mode === 'photo' ? 'activetabs' : 'ki')" class="tab" mode="photo">
|
||||
<a n:attr="id => ($mode === 'photo' ? 'act_tab_a' : 'ki')">Фотографии</a>
|
||||
</div>
|
||||
<div n:attr="id => ($mode === 'video' ? 'activetabs' : 'ki')" class="tab" mode="video">
|
||||
<a n:attr="id => ($mode === 'video' ? 'act_tab_a' : 'ki')">Видеозаписи</a>
|
||||
</div>
|
||||
<div n:attr="id => ($mode === 'group' ? 'activetabs' : 'ki')" class="tab" mode="group">
|
||||
<a n:attr="id => ($mode === 'group' ? 'act_tab_a' : 'ki')">Сообщества</a>
|
||||
</div>
|
||||
<div n:attr="id => ($mode === 'comment' ? 'activetabs' : 'ki')" class="tab" mode="comment">
|
||||
<a n:attr="id => ($mode === 'comment' ? 'act_tab_a' : 'ki')">Комментарии</a>
|
||||
</div>
|
||||
<div n:attr="id => ($mode === 'note' ? 'activetabs' : 'ki')" class="tab" mode="note">
|
||||
<a n:attr="id => ($mode === 'note' ? 'act_tab_a' : 'ki')">Заметки</a>
|
||||
</div>
|
||||
<div n:attr="id => ($mode === 'app' ? 'activetabs' : 'ki')" class="tab" mode="app">
|
||||
<a n:attr="id => ($mode === 'app' ? 'act_tab_a' : 'ki')">Приложения</a>
|
||||
</div>
|
||||
<div n:attr="id => ($mode === 'user' ? 'activetabs' : 'ki')" class="tab" mode="user">
|
||||
<a n:attr="id => ($mode === 'user' ? 'act_tab_a' : 'ki')">Пользователи</a>
|
||||
</div>
|
||||
</center>
|
||||
|
||||
<script>
|
||||
async function getReports(mode) {
|
||||
let _content = $(".content").length;
|
||||
$(".container_gray").empty();
|
||||
|
||||
await $.ajax({
|
||||
type: "POST",
|
||||
url: `/scumfeed?act=${ mode}`,
|
||||
data: {
|
||||
hash: {=$csrfToken}
|
||||
},
|
||||
success: (response) => {
|
||||
if (response?.reports?.length != _content) {
|
||||
NewNotification("Обратите внимание", "В списке появились новые жалобы. Работа ждёт :)");
|
||||
}
|
||||
|
||||
if (response.reports.length > 0) {
|
||||
response.reports.forEach((report) => {
|
||||
$(".container_gray").append(`
|
||||
<div class="content">
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td valign="top">
|
||||
<a href="/admin/report${ report.id}">
|
||||
<center>
|
||||
<img src="/assets/packages/static/openvk/img/note_icon.png" style="margin-top: 17px;">
|
||||
</center>
|
||||
</a>
|
||||
</td>
|
||||
<td valign="top" style="width: 100%">
|
||||
<a href="/admin/report${ report.id}">
|
||||
<b>
|
||||
Жалоба №${ report.id}
|
||||
</b>
|
||||
</a>
|
||||
<br>
|
||||
<a href="${ report.author.url}">
|
||||
${ report.author.name}
|
||||
</a>
|
||||
пожаловал${ report.author.is_female ? "ась" : "ся"} на
|
||||
${ report.content.type === "user" ? `<a href="${ report.content.url}">` : ''}
|
||||
${ report.content.name}
|
||||
${ report.content.type === "user" ? '</a>' : ''}
|
||||
|
||||
${ report.duplicates > 0 ? `
|
||||
<br />
|
||||
<b>Другие жалобы на этот контент: <a href="/scumfeed?orig=${ report.id}">${ report.duplicates} шт.</a></b>
|
||||
` : ''}
|
||||
</td>
|
||||
<td valign="top" class="action_links" style="width: 150px;">
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
`);
|
||||
});
|
||||
} else {
|
||||
$(".content table").width("100%")
|
||||
$(".container_gray").html(`
|
||||
<center style="background: white;border: #DEDEDE solid 1px;">
|
||||
<span style="color: #707070;margin: 60px 0;display: block;">
|
||||
{_no_data_description|noescape}
|
||||
</span>
|
||||
</center>
|
||||
`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(".reportsTabs .tab").on("click", async function () {
|
||||
let mode = $(this).attr("mode");
|
||||
|
||||
$(".reportsTabs #activetabs").attr("id", "ki");
|
||||
$(".reportsTabs #act_tab_a").attr("id", "ki");
|
||||
$(`.reportsTabs .tab[mode='${ mode}']`).attr("id", "activetabs");
|
||||
$(`.reportsTabs .tab[mode='${ mode}'] a`).attr("id", "act_tab_a");
|
||||
|
||||
$(".container_gray").hide();
|
||||
$("#reports-loader").show();
|
||||
|
||||
history.pushState(null, null, `/scumfeed?act=${ mode}`);
|
||||
|
||||
await getReports(mode);
|
||||
|
||||
$(".container_gray").show();
|
||||
$("#reports-loader").hide();
|
||||
});
|
||||
|
||||
setInterval(async () => {
|
||||
await getReports($(".reportsTabs #activetabs").attr("mode"));
|
||||
}, 10000);
|
||||
</script>
|
|
@ -12,6 +12,8 @@
|
|||
<div class="tabs">{include "../NoSpam/Tabs.xml", mode => "reports"}</div>
|
||||
<br />
|
||||
<p>
|
||||
<b>{$report->getReportAuthor()->getCanonicalName()}</b> пожаловался на <b>{$report->getContentName()}</b>
|
||||
<br />
|
||||
<b>{_comment}:</b> {$report->getReason()}
|
||||
</p>
|
||||
{include "ViewContent.xml", type => $report->getContentType(), object => $report->getContentObject()}
|
||||
|
@ -21,7 +23,7 @@
|
|||
<form n:if="$report->getContentType() != 'group'" action="/admin/reportAction{$report->getId()}" method="post">
|
||||
<input type="hidden" name="hash" value="{$csrfToken}"/>
|
||||
<input type="submit" name="ban" value="{_ban_user_action}" class="button">
|
||||
<input type="submit" name="delete" value="{_delete}" class="button">
|
||||
<input n:if="$report->getContentType() !== 'user'" type="submit" name="delete" value="{_delete}" class="button">
|
||||
<input type="submit" name="ignore" value="{_ignore_report}" class="button">
|
||||
</form>
|
||||
<form n:if="$report->getContentType() == 'group'" action="/admin/reportAction{$report->getId()}" method="post">
|
||||
|
|
Loading…
Reference in a new issue