From 43ae3d28a795d453033d0e712c52d0d041484c98 Mon Sep 17 00:00:00 2001 From: n1rwana Date: Thu, 27 Jul 2023 20:36:56 +0300 Subject: [PATCH] Better reports --- Web/Models/Entities/Application.php | 5 +- Web/Models/Entities/Ban.php | 66 ++++++++++ Web/Models/Entities/Postable.php | 3 +- Web/Models/Entities/Report.php | 30 +++-- Web/Models/Entities/User.php | 118 ++++++++++++++++-- Web/Models/Repositories/Bans.php | 33 +++++ Web/Presenters/AdminPresenter.php | 32 ++++- Web/Presenters/AuthPresenter.php | 11 +- Web/Presenters/BlobPresenter.php | 2 + Web/Presenters/ReportPresenter.php | 57 +++------ Web/Presenters/templates/@banned.xml | 15 ++- Web/Presenters/templates/@layout.xml | 19 +-- .../templates/Admin/BansHistory.xml | 86 +++++++++++++ Web/Presenters/templates/Apps/Play.xml | 26 ++++ Web/Presenters/templates/Group/View.xml | 2 +- Web/Presenters/templates/Photos/Photo.xml | 2 +- Web/Presenters/templates/Report/List.xml | 6 +- Web/Presenters/templates/Report/View.xml | 42 +++---- .../templates/Report/ViewContent.xml | 30 +++++ .../templates/Report/content/app.xml | 22 ++++ .../templates/Report/content/note.xml | 18 +++ .../templates/Report/content/photo.xml | 26 ++++ .../templates/Report/content/video.xml | 32 +++++ .../templates/Support/AnswerTicket.xml | 37 +++++- Web/Presenters/templates/User/View.xml | 7 +- Web/Presenters/templates/User/banned.xml | 4 +- Web/Presenters/templates/Videos/View.xml | 2 +- .../templates/components/comment.xml | 50 ++++++-- Web/Presenters/templates/components/group.xml | 61 ++++++--- Web/routes.yml | 6 +- install/sqls/00032-better-reports.sql | 19 +++ locales/en.strings | 2 + locales/ru.strings | 2 + 33 files changed, 729 insertions(+), 144 deletions(-) create mode 100644 Web/Models/Entities/Ban.php create mode 100644 Web/Models/Repositories/Bans.php create mode 100644 Web/Presenters/templates/Admin/BansHistory.xml create mode 100644 Web/Presenters/templates/Report/ViewContent.xml create mode 100644 Web/Presenters/templates/Report/content/app.xml create mode 100644 Web/Presenters/templates/Report/content/note.xml create mode 100644 Web/Presenters/templates/Report/content/photo.xml create mode 100644 Web/Presenters/templates/Report/content/video.xml create mode 100644 install/sqls/00032-better-reports.sql diff --git a/Web/Models/Entities/Application.php b/Web/Models/Entities/Application.php index 74569485..48975645 100644 --- a/Web/Models/Entities/Application.php +++ b/Web/Models/Entities/Application.php @@ -306,11 +306,14 @@ class Application extends RowModel function delete(bool $softly = true): void { if($softly) - throw new \UnexpectedValueException("Can't delete apps softly."); + throw new \UnexpectedValueException("Can't delete apps softly."); // why $cx = DatabaseConnection::i()->getContext(); $cx->table("app_users")->where("app", $this->getId())->delete(); parent::delete(false); } + + function getPublicationTime(): string + { return tr("recently"); } } \ No newline at end of file diff --git a/Web/Models/Entities/Ban.php b/Web/Models/Entities/Ban.php new file mode 100644 index 00000000..3962c6cb --- /dev/null +++ b/Web/Models/Entities/Ban.php @@ -0,0 +1,66 @@ +getRecord()->id; + } + + function getReason(): ?string + { + return $this->getRecord()->reason; + } + + function getUser(): ?User + { + return (new Users)->get($this->getRecord()->user); + } + + function getInitiator(): ?User + { + return (new Users)->get($this->getRecord()->initiator); + } + + function getStartTime(): int + { + return $this->getRecord()->iat; + } + + function getEndTime(): int + { + return $this->getRecord()->exp; + } + + function getTime(): int + { + return $this->getRecord()->time; + } + + function isPermanent(): bool + { + return $this->getEndTime() === 0; + } + + function isRemovedManually(): bool + { + return (bool) $this->getRecord()->removed_manually; + } + + function isOver(): bool + { + return $this->isRemovedManually(); + } + + function whoRemoved(): ?User + { + return (new Users)->get($this->getRecord()->removed_by); + } +} diff --git a/Web/Models/Entities/Postable.php b/Web/Models/Entities/Postable.php index b4f8b4c6..8cf1c4c8 100644 --- a/Web/Models/Entities/Postable.php +++ b/Web/Models/Entities/Postable.php @@ -34,7 +34,8 @@ abstract class Postable extends Attachable $oid = (int) $this->getRecord()->owner; if(!$real && $this->isAnonymous()) $oid = OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["account"]; - + + $oid = abs($oid); if($oid > 0) return (new Users)->get($oid); else diff --git a/Web/Models/Entities/Report.php b/Web/Models/Entities/Report.php index ff91d2d5..c7e9c4b8 100644 --- a/Web/Models/Entities/Report.php +++ b/Web/Models/Entities/Report.php @@ -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\{Users, Posts, Photos, Videos, Clubs}; +use openvk\Web\Models\Repositories\{Applications, Comments, Notes, Users, Posts, Photos, Videos, Clubs}; use Chandler\Database\DatabaseConnection as DB; use Nette\InvalidStateException as ISE; use Nette\Database\Table\Selection; @@ -53,22 +53,25 @@ class Report extends RowModel return $this->getRecord()->user_id; } - function getUser(): user + function getUser(): User { - return (new Users)->get($this->getRecord()->user_id); + return (new Users)->get((int) $this->getRecord()->user_id); } function getContentId(): int { - return $this->getRecord()->target_id; + return (int) $this->getRecord()->target_id; } function getContentObject() { - if ($this->getContentType() == "post") return (new Posts)->get($this->getContentId()); - else if ($this->getContentType() == "photo") return (new Photos)->get($this->getContentId()); - else if ($this->getContentType() == "video") return (new Videos)->get($this->getContentId()); - else if ($this->getContentType() == "group") return (new Clubs)->get($this->getContentId()); + if ($this->getContentType() == "post") return (new Posts)->get($this->getContentId()); + else if ($this->getContentType() == "photo") return (new Photos)->get($this->getContentId()); + else if ($this->getContentType() == "video") return (new Videos)->get($this->getContentId()); + else if ($this->getContentType() == "group") return (new Clubs)->get($this->getContentId()); + else if ($this->getContentType() == "comment") return (new Comments)->get($this->getContentId()); + else if ($this->getContentType() == "note") return (new Notes)->get($this->getContentId()); + else if ($this->getContentType() == "app") return (new Applications)->get($this->getContentId()); else return null; } @@ -77,16 +80,17 @@ class Report extends RowModel return (new Posts)->get($this->getContentId())->getOwner(); } - // TODO: Localize that - function banUser() + function banUser($initiator) { - $this->getAuthor()->ban("Banned by report. Ask Technical support for ban reason"); + $this->getAuthor()->ban("**content-" . $this->getContentType() . "-" . $this->getContentId() . "**", false, time() + $this->getAuthor()->getNewBanTime(), $initiator); } function deleteContent() { - $this->getAuthor()->adminNotify("Ваш контент, который вы опубликовали " . $this->getContentObject()->getPublicationTime() . " был удалён модераторами инстанса. За повторные или серьёзные нарушения вас могут заблокировать."); - $this->getContentObject()->delete(); + $pubTime = $this->getContentObject()->getPublicationTime(); + $name = $this->getContentObject()->getName(); + $this->getAuthor()->adminNotify("Ваш контент, который вы опубликовали $pubTime ($name) был удалён модераторами инстанса. За повторные или серьёзные нарушения вас могут заблокировать."); + $this->getContentObject()->delete($this->getContentType() !== "app"); $this->setDeleted(1); $this->save(); } diff --git a/Web/Models/Entities/User.php b/Web/Models/Entities/User.php index 115bf8c2..aa3afa74 100644 --- a/Web/Models/Entities/User.php +++ b/Web/Models/Entities/User.php @@ -5,7 +5,18 @@ use openvk\Web\Themes\{Themepack, Themepacks}; use openvk\Web\Util\DateTime; use openvk\Web\Models\RowModel; use openvk\Web\Models\Entities\{Photo, Message, Correspondence, Gift}; -use openvk\Web\Models\Repositories\{Users, Clubs, Albums, Gifts, Notifications}; +use openvk\Web\Models\Repositories\{Applications, + Bans, + Comments, + Notes, + Posts, + Users, + Clubs, + Albums, + Gifts, + Notifications, + Videos, + Photos}; use openvk\Web\Models\Exceptions\InvalidUserNameException; use Nette\Database\Table\ActiveRow; use Chandler\Database\DatabaseConnection; @@ -236,11 +247,55 @@ class User extends RowModel return $this->getRecord()->alert; } - function getBanReason(): ?string + function getTextForContentBan(string $type): string + { + switch ($type) { + case "post": return "за размещение от Вашего лица таких записей:"; + case "photo": return "за размещение от Вашего лица таких фотографий:"; + case "video": return "за размещение от Вашего лица таких видеозаписей:"; + case "group": return "за подозрительное вступление от Вашего лица в группу:"; + case "comment": return "за размещение от Вашего лица таких комментариев:"; + case "note": return "за размещение от Вашего лица таких заметок:"; + case "app": return "за создание от Вашего имени подозрительных приложений."; + default: return "за размещение от Вашего лица такого контента:"; + } + } + + function getRawBanReason(): ?string { return $this->getRecord()->block_reason; } + function getBanReason(?string $for = null) + { + $ban = (new Bans)->get((int) $this->getRecord()->block_reason); + if (!$ban || $ban->isOver()) return null; + + $reason = $ban->getReason(); + + preg_match('/\*\*content-(post|photo|video|group|comment|note|app)-(\d+)\*\*$/', $reason, $matches); + if (sizeof($matches) === 3) { + if ($for !== "banned") { + $reason = "Подозрительная активность"; + } else { + $content_type = $matches[1]; $content_id = (int) $matches[2]; + $reason = [$this->getTextForContentBan($content_type), $content_type]; + switch ($content_type) { + case "post": $reason[] = (new Posts)->get($content_id); break; + case "photo": $reason[] = (new Photos)->get($content_id); break; + case "video": $reason[] = (new Videos)->get($content_id); break; + case "group": $reason[] = (new Clubs)->get($content_id); break; + case "comment": $reason[] = (new Comments)->get($content_id); break; + case "note": $reason[] = (new Notes)->get($content_id); break; + case "app": $reason[] = (new Applications)->get($content_id); break; + default: $reason[] = null; + } + } + } + + return $reason; + } + function getBanInSupportReason(): ?string { return $this->getRecord()->block_in_support_reason; @@ -768,7 +823,7 @@ class User extends RowModel ]); } - function ban(string $reason, bool $deleteSubscriptions = true, ?int $unban_time = NULL): void + function ban(string $reason, bool $deleteSubscriptions = true, $unban_time = NULL, ?int $initiator = NULL): void { if($deleteSubscriptions) { $subs = DatabaseConnection::i()->getContext()->table("subscriptions"); @@ -781,8 +836,18 @@ class User extends RowModel $subs->delete(); } - $this->setBlock_Reason($reason); - $this->setUnblock_time($unban_time); + $iat = time(); + $ban = new Ban; + $ban->setUser($this->getId()); + $ban->setReason($reason); + $ban->setInitiator($initiator); + $ban->setIat($iat); + $ban->setExp($unban_time !== "permanent" ? $unban_time : 0); + $ban->setTime($unban_time === "permanent" ? 0 : ($unban_time ? ($unban_time - $iat) : 0)); + $ban->save(); + + $this->setBlock_Reason($ban->getId()); + // $this->setUnblock_time($unban_time); $this->save(); } @@ -956,7 +1021,7 @@ class User extends RowModel function adminNotify(string $message): bool { - $admId = OPENVK_ROOT_CONF["openvk"]["preferences"]["support"]["adminAccount"]; + $admId = (int) OPENVK_ROOT_CONF["openvk"]["preferences"]["support"]["adminAccount"]; if(!$admId) return false; else if(is_null($admin = (new Users)->get($admId))) @@ -1021,7 +1086,10 @@ class User extends RowModel function getUnbanTime(): ?string { - return !is_null($this->getRecord()->unblock_time) ? date('d.m.Y', $this->getRecord()->unblock_time) : NULL; + $ban = (new Bans)->get((int) $this->getRecord()->block_reason); + if (!$ban || $ban->isOver() || $ban->isPermanent()) return null; + + return date('d.m.Y', $ban->getEndTime()); } function canUnbanThemself(): bool @@ -1029,10 +1097,40 @@ class User extends RowModel if (!$this->isBanned()) return false; - if ($this->getRecord()->unblock_time > time() || $this->getRecord()->unblock_time == 0) - return false; + $ban = (new Bans)->get((int) $this->getRecord()->block_reason); + if (!$ban || $ban->isOver() || $ban->isPermanent()) return false; - return true; + return $ban->getEndTime() <= time() && !$ban->isPermanent(); + } + + function getNewBanTime() + { + $bans = iterator_to_array((new Bans)->getByUser($this->getid())); + if (!$bans || count($bans) === 0) + return 0; + + $last_ban = end($bans); + if (!$last_ban) return 0; + + if ($last_ban->isPermanent()) return "permanent"; + + $values = [0, 3600, 7200, 86400, 172800, 604800, 1209600, 3024000, 9072000]; + $response = 0; + $i = 0; + + foreach ($values as $value) { + $i++; + if ($last_ban->getTime() === 0 && $value === 0) continue; + if ($last_ban->getTime() < $value) { + $response = $value; + break; + } else if ($last_ban->getTime() >= $value) { + if ($i < count($values)) continue; + $response = "permanent"; + break; + } + } + return $response; } use Traits\TSubscribable; diff --git a/Web/Models/Repositories/Bans.php b/Web/Models/Repositories/Bans.php new file mode 100644 index 00000000..7123459d --- /dev/null +++ b/Web/Models/Repositories/Bans.php @@ -0,0 +1,33 @@ +context = DB::i()->getContext(); + $this->bans = $this->context->table("bans"); + } + + function toBan(?ActiveRow $ar): ?Ban + { + return is_null($ar) ? NULL : new Ban($ar); + } + + function get(int $id): ?Ban + { + return $this->toBan($this->bans->get($id)); + } + + function getByUser(int $user_id): \Traversable + { + foreach ($this->bans->where("user", $user_id) as $ban) + yield new Ban($ban); + } +} \ No newline at end of file diff --git a/Web/Presenters/AdminPresenter.php b/Web/Presenters/AdminPresenter.php index ceda6f6b..0a720601 100644 --- a/Web/Presenters/AdminPresenter.php +++ b/Web/Presenters/AdminPresenter.php @@ -1,7 +1,7 @@ assertNoCSRF(); + if (str_contains($this->queryParam("reason"), "*")) + exit(json_encode([ "error" => "Incorrect reason" ])); + $unban_time = strtotime($this->queryParam("date")) ?: NULL; $user = $this->users->get($id); if(!$user) exit(json_encode([ "error" => "User does not exist" ])); - - $user->ban($this->queryParam("reason"), true, $unban_time); + + if ($this->queryParam("incr")) + $unban_time = time() + $user->getNewBanTime(); + + $user->ban($this->queryParam("reason"), true, $unban_time, $this->user->identity->getId()); exit(json_encode([ "success" => true, "reason" => $this->queryParam("reason") ])); } @@ -360,9 +366,17 @@ final class AdminPresenter extends OpenVKPresenter $user = $this->users->get($id); if(!$user) exit(json_encode([ "error" => "User does not exist" ])); - + + $ban = (new Bans)->get((int)$user->getRawBanReason()); + if (!$ban || $ban->isOver()) + exit(json_encode([ "error" => "User is not banned" ])); + + $ban->setRemoved_Manually(true); + $ban->setRemoved_By($this->user->identity->getId()); + $ban->save(); + $user->setBlock_Reason(NULL); - $user->setUnblock_time(NULL); + // $user->setUnblock_time(NULL); $user->save(); exit(json_encode([ "success" => true ])); } @@ -447,4 +461,12 @@ final class AdminPresenter extends OpenVKPresenter $this->redirect("/admin/bannedLinks"); } + + function renderBansHistory(int $user_id) :void + { + $user = (new Users)->get($user_id); + if (!$user) $this->notFound(); + + $this->template->bans = (new Bans)->getByUser($user_id); + } } diff --git a/Web/Presenters/AuthPresenter.php b/Web/Presenters/AuthPresenter.php index f934f7fe..5bacc9f6 100644 --- a/Web/Presenters/AuthPresenter.php +++ b/Web/Presenters/AuthPresenter.php @@ -1,7 +1,7 @@ flashFail("err", tr("error"), tr("forbidden")); $user = $this->users->get($this->user->id); + $ban = (new Bans)->get((int)$user->getRawBanReason()); + if (!$ban || $ban->isOver() || $ban->isPermanent()) + $this->flashFail("err", tr("error"), tr("forbidden")); + + $ban->setRemoved_Manually(2); + $ban->setRemoved_By($this->user->identity->getId()); + $ban->save(); $user->setBlock_Reason(NULL); - $user->setUnblock_Time(NULL); + // $user->setUnblock_Time(NULL); $user->save(); $this->flashFail("succ", tr("banned_unban_title"), tr("banned_unban_description")); diff --git a/Web/Presenters/BlobPresenter.php b/Web/Presenters/BlobPresenter.php index 970b8f19..7bb3e2be 100644 --- a/Web/Presenters/BlobPresenter.php +++ b/Web/Presenters/BlobPresenter.php @@ -3,6 +3,8 @@ namespace openvk\Web\Presenters; final class BlobPresenter extends OpenVKPresenter { + protected $banTolerant = true; + private function getDirName($dir): string { if(gettype($dir) === "integer") { diff --git a/Web/Presenters/ReportPresenter.php b/Web/Presenters/ReportPresenter.php index 12a052d4..7d09ddf4 100644 --- a/Web/Presenters/ReportPresenter.php +++ b/Web/Presenters/ReportPresenter.php @@ -51,12 +51,7 @@ final class ReportPresenter extends OpenVKPresenter if(!$id) exit(json_encode([ "error" => tr("error_segmentation") ])); - // At this moment, only Posts will be implemented - if(in_array($this->queryParam("type"), ["post", "photo", "video", "group"])) { - $post = (new Posts)->get(intval($id)); - if(!$post) - exit(json_encode([ "error" => "Unable to report nonexistent content" ])); - + if(in_array($this->queryParam("type"), ["post", "photo", "video", "group", "comment", "note", "app"])) { $report = new Report; $report->setUser_id($this->user->id); $report->setTarget_id($id); @@ -78,52 +73,40 @@ final class ReportPresenter extends OpenVKPresenter $this->assertPermission('openvk\Web\Models\Entities\TicketReply', 'write', 0); $report = $this->reports->get($id); - if(!$report) $this->notFound(); - if($report->isDeleted()) $this->notFound(); + if(!$report || $report->isDeleted()) $this->notFound(); - if($this->postParam("ban")) { - if(is_null($this->user)) - $this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса."); - - $report->banUser(); + if ($this->postParam("ban")) { $report->deleteContent(); + $report->banUser($this->user->identity->getId()); + $this->flash("suc", "Смэрть...", "Пользователь успешно забанен."); - }else if($this->postParam("delete")){ - if(is_null($this->user)) - $this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса."); - + } else if ($this->postParam("delete")) { $report->deleteContent(); + $this->flash("suc", "Нехай живе!", "Контент удалён, а пользователю прилетело предупреждение."); - }else if($this->postParam("ignore")){ - if(is_null($this->user)) - $this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса."); - + } else if ($this->postParam("ignore")) { $report->delete(); + $this->flash("suc", "Нехай живе!", "Жалоба проигнорирована."); - }else if($this->postParam("banClubOwner")) { - if($report->getContentType() != "group") + } else if ($this->postParam("banClubOwner") || $this->postParam("banClub")) { + if ($report->getContentType() !== "group") $this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса."); $club = $report->getContentObject(); - - if(is_null($club)) + if (!$club || $club->isBanned()) $this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса."); - $owner = $club->getOwner(); - $owner->ban("Banned by report. Ask Technical support for ban reason"); + if ($this->postParam("banClubOwner")) { + $club->getOwner()->ban("**content-" . $report->getContentType() . "-" . $report->getContentId() . "**", false, $club->getOwner()->getNewBanTime(), $this->user->identity->getId()); + } else { + $club->ban("**content-" . $report->getContentType() . "-" . $report->getContentId() . "**"); + } $report->delete(); - $this->flash("suc", "Смэрть...", "Создатель сообщества успешно забанен."); - }else if($this->postParam("banClub")) { - if($report->getContentType() != "group") - $this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса."); - $club = $report->getContentObject(); - $club->ban("Banned by report. Ask Technical support for ban reason"); - - $report->delete(); - $this->flash("suc", "Смэрть...", "Сообщество успешно забанено."); + $this->flash("suc", "Смэрть...", ($this->postParam("banClubOwner") ? "Создатель сообщества успешно забанен." : "Сообщество успешно забанено")); } - $this->redirect("/support/reports"); + + $this->redirect("/scumfeed"); } } diff --git a/Web/Presenters/templates/@banned.xml b/Web/Presenters/templates/@banned.xml index 48c29ddb..7640838c 100644 --- a/Web/Presenters/templates/@banned.xml +++ b/Web/Presenters/templates/@banned.xml @@ -10,8 +10,19 @@ {_banned_alt}

- {tr("banned_1", htmlentities($thisUser->getCanonicalName()))|noescape}
- {tr("banned_2", htmlentities($thisUser->getBanReason()))|noescape} + {var $ban = $thisUser->getBanReason("banned")} + {if is_string($ban)} + {tr("banned_1", htmlentities($thisUser->getCanonicalName()))|noescape}
+ {tr("banned_2", htmlentities($thisUser->getBanReason()))|noescape} + {else} + {tr("banned_1", htmlentities($thisUser->getCanonicalName()))|noescape} +

+ Эта страница была заморожена {$ban[0]|noescape} + {if $ban[1] !== "app"} + {include "Report/ViewContent.xml", type => $ban[1], object => $ban[2]} + {/if} +
+ {/if} {if !$thisUser->getUnbanTime()} {_banned_perm} diff --git a/Web/Presenters/templates/@layout.xml b/Web/Presenters/templates/@layout.xml index 382a7156..821955b0 100644 --- a/Web/Presenters/templates/@layout.xml +++ b/Web/Presenters/templates/@layout.xml @@ -186,8 +186,11 @@ ({$helpdeskTicketNotAnsweredCount}) {/if} - {tr("reports")} - {/if} + {tr("reports")} + {if $reportNotAnsweredCount > 0} + ({$reportNotAnsweredCount}) + {/if} + {$club->getName()} - Reports - {if $reportNotAnsweredCount > 0} - ({$reportNotAnsweredCount}) - {/if} - {strpos($menuItem["name"], "@") === 0 ? tr(substr($menuItem["name"], 1)) : $menuItem["name"]} @@ -278,8 +276,13 @@ {_forgot_password} {/ifset} + - + {ifset $thisUser} + {if !$thisUser->isBanned()} + + {/if} + {/ifset}
diff --git a/Web/Presenters/templates/Admin/BansHistory.xml b/Web/Presenters/templates/Admin/BansHistory.xml new file mode 100644 index 00000000..c0dc1b64 --- /dev/null +++ b/Web/Presenters/templates/Admin/BansHistory.xml @@ -0,0 +1,86 @@ +{extends "./@layout.xml"} + +{block title} + История блокировок +{/block} + +{block heading} + {include title} +{/block} + +{block content} + + + + + + + + + + + + + + + + + + + + + + + + + +
IDЗабаненныйИнициаторНачалоКонецВремяПричинаСнята
{$ban->getId()} + + + {$ban->getUser()->getCanonicalName()} + + + + {$ban->getUser()->getCanonicalName()} + + + {_admin_banned} + + + + + {$ban->getInitiator()->getCanonicalName()} + + + + {$ban->getInitiator()->getCanonicalName()} + + {_admin_banned} + + {date('d.m.Y в H:i:s', $ban->getStartTime())}{date('d.m.Y в H:i:s', $ban->getEndTime())}{$ban->getTime()} + {$ban->getReason()} + + {if $ban->isRemovedManually()} + + + {$ban->whoRemoved()->getCanonicalName()} + + + + {$ban->whoRemoved()->getCanonicalName()} + + + {_admin_banned} + + {else} + Активная блокировка + {/if} +
+{/block} \ No newline at end of file diff --git a/Web/Presenters/templates/Apps/Play.xml b/Web/Presenters/templates/Apps/Play.xml index 91cfe5d5..facaa273 100644 --- a/Web/Presenters/templates/Apps/Play.xml +++ b/Web/Presenters/templates/Apps/Play.xml @@ -1,4 +1,5 @@ {extends "../@layout.xml"} +{var $canReport = $owner->getId() !== $thisUser->getId()} {block title} {$name} @@ -6,6 +7,7 @@ {block header} {$name} + Пожаловаться {/block} {block content} @@ -33,5 +35,29 @@ window.appOrigin = {$origin}; + + {script "js/al_games.js"} {/block} diff --git a/Web/Presenters/templates/Group/View.xml b/Web/Presenters/templates/Group/View.xml index d4b18f4a..1f09d889 100644 --- a/Web/Presenters/templates/Group/View.xml +++ b/Web/Presenters/templates/Group/View.xml @@ -134,7 +134,7 @@ (function() { res = document.querySelector("#uReportMsgInput").value; xhr = new XMLHttpRequest(); - xhr.open("GET", "/report.pl/" + {$club->getId()} + "?reason=" + res + "&type=group", true); + xhr.open("GET", "/report/" + {$club->getId()} + "?reason=" + res + "&type=group", true); xhr.onload = (function() { if(xhr.responseText.indexOf("reason") === -1) MessageBox("Ошибка", "Не удалось подать жалобу...", ["OK"], [Function.noop]); diff --git a/Web/Presenters/templates/Photos/Photo.xml b/Web/Presenters/templates/Photos/Photo.xml index eb87df75..047f382a 100644 --- a/Web/Presenters/templates/Photos/Photo.xml +++ b/Web/Presenters/templates/Photos/Photo.xml @@ -61,7 +61,7 @@ (function() { res = document.querySelector("#uReportMsgInput").value; xhr = new XMLHttpRequest(); - xhr.open("GET", "/report.pl/" + {$photo->getId()} + "?reason=" + res + "&type=photo", true); + xhr.open("GET", "/report/" + {$photo->getId()} + "?reason=" + res + "&type=photo", true); xhr.onload = (function() { if(xhr.responseText.indexOf("reason") === -1) MessageBox("Ошибка", "Не удалось подать жалобу...", ["OK"], [Function.noop]); diff --git a/Web/Presenters/templates/Report/List.xml b/Web/Presenters/templates/Report/List.xml index 06091ed1..fb07994e 100644 --- a/Web/Presenters/templates/Report/List.xml +++ b/Web/Presenters/templates/Report/List.xml @@ -15,7 +15,7 @@ {* BEGIN ELEMENTS DESCRIPTION *} {block link|strip|stripHtml} - /admin/support/report{$x->getId()} + /admin/report{$x->getId()} {/block} {block preview} @@ -35,6 +35,10 @@ {_video} {elseif $x->getContentType() == "group"} {_groups} + {elseif $x->getContentType() == "comment"} + {_comment} + {elseif $x->getContentType() == "app"} + {_app} {else} Unknown {/if} diff --git a/Web/Presenters/templates/Report/View.xml b/Web/Presenters/templates/Report/View.xml index c028c199..68bb891e 100644 --- a/Web/Presenters/templates/Report/View.xml +++ b/Web/Presenters/templates/Report/View.xml @@ -10,32 +10,22 @@ {block content}

{_comment}: {$report->getReason()}

- {if $report->getContentType() == "post"} - {include "../components/post/oldpost.xml", post => $report->getContentObject()} - {elseif $report->getContentType() == "photo"} - {include "../components/photo.xml", photo => $report->getContentObject()} - {elseif $report->getContentType() == "video"} - {include "../components/video.xml", video => $report->getContentObject()} - {elseif $report->getContentType() == "group"} - {include "../components/group.xml", group => $report->getContentObject()} - {else} - Error - {/if} + {include "ViewContent.xml", type => $report->getContentType(), object => $report->getContentObject()}
-
-{include "../components/post/oldpost.xml", post => $report->getContentObject(), forceNoPinLink => TRUE, forceNoDeleteLink => TRUE, forceNoShareLink => TRUE, forceNoLike => TRUE} -
- - - - - + +
+ + + + + + +
+ + + + +
+
-
- - - - -
-
{/block} diff --git a/Web/Presenters/templates/Report/ViewContent.xml b/Web/Presenters/templates/Report/ViewContent.xml new file mode 100644 index 00000000..ebb413ff --- /dev/null +++ b/Web/Presenters/templates/Report/ViewContent.xml @@ -0,0 +1,30 @@ +{block ViewContent} +
+ {if $type == "post"} + {include "../components/post/oldpost.xml", + post => $object, + forceNoDeleteLink => true, + forceNoPinLink => true, + forceNoCommentsLink => true, + forceNoShareLink => true, + forceNoLike => true + } + {elseif $type == "photo"} + {include "./content/photo.xml", photo => $object} + {elseif $type == "video"} + {include "./content/video.xml", video => $object} + {elseif $type == "group"} + {include "../components/group.xml", group => $object} + {elseif $type == "comment"} + {include "../components/comment.xml", comment => $object, timeOnly => true} + {elseif $type == "note"} + {include "./content/note.xml", note => $object} + {elseif $type == "app"} + {if $appsSoftDeleting} + {include "./content/app.xml", app => $object} + {/if} + {else} + {include "../components/error.xml", description => tr("version_incompatibility")} + {/if} +
+{/block} diff --git a/Web/Presenters/templates/Report/content/app.xml b/Web/Presenters/templates/Report/content/app.xml new file mode 100644 index 00000000..cd82caf2 --- /dev/null +++ b/Web/Presenters/templates/Report/content/app.xml @@ -0,0 +1,22 @@ +{block content} +
+ + + + + + + +
+ + + + + + {$app->getName()} + +
+ {$app->getDescription()} +
+
+{/block} diff --git a/Web/Presenters/templates/Report/content/note.xml b/Web/Presenters/templates/Report/content/note.xml new file mode 100644 index 00000000..f4f2e054 --- /dev/null +++ b/Web/Presenters/templates/Report/content/note.xml @@ -0,0 +1,18 @@ +{block content} + +{/block} diff --git a/Web/Presenters/templates/Report/content/photo.xml b/Web/Presenters/templates/Report/content/photo.xml new file mode 100644 index 00000000..ac9f4c97 --- /dev/null +++ b/Web/Presenters/templates/Report/content/photo.xml @@ -0,0 +1,26 @@ +{block content} +
+
+ +
+ + + + + + + +
+ +
+

{_information}

+ {_info_description}: + {$photo->getDescription() ?? "(" . tr("none") . ")"}
+ {_info_uploaded_by}: + {$photo->getOwner()->getFullName()}
+ {_info_upload_date}: + {$photo->getPublicationTime()} +
+
+
+{/block} diff --git a/Web/Presenters/templates/Report/content/video.xml b/Web/Presenters/templates/Report/content/video.xml new file mode 100644 index 00000000..f5e07b28 --- /dev/null +++ b/Web/Presenters/templates/Report/content/video.xml @@ -0,0 +1,32 @@ +{block content} +
+ + + + + + + +
+ +
+ {$video->getName()} +
+
+
+ + + {$video->getName()} + + +
+

+ {$video->getDescription() ?? ""} +

+ {_video_uploaded} {$video->getPublicationTime()}
+ {_video_updated} {$video->getEditTime() ?? $video->getPublicationTime()} +
+
+{/block} diff --git a/Web/Presenters/templates/Support/AnswerTicket.xml b/Web/Presenters/templates/Support/AnswerTicket.xml index f2fbfed7..503ed31e 100644 --- a/Web/Presenters/templates/Support/AnswerTicket.xml +++ b/Web/Presenters/templates/Support/AnswerTicket.xml @@ -8,7 +8,42 @@ {block content}
{$ticket->getText()|noescape} diff --git a/Web/Presenters/templates/User/View.xml b/Web/Presenters/templates/User/View.xml index fb38133d..0d23190b 100644 --- a/Web/Presenters/templates/User/View.xml +++ b/Web/Presenters/templates/User/View.xml @@ -92,6 +92,9 @@ {_warn_user_action} + + Блокировки + {/if} {if $thisUser->getChandlerUser()->can('write')->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)} @@ -544,13 +547,15 @@ uBanMsgTxt += "
Предупреждение: Это действие удалит все подписки пользователя и отпишет всех от него."; uBanMsgTxt += "

Причина бана: " uBanMsgTxt += "

Заблокировать до: "; + uBanMsgTxt += "

Автоматически (до " + {date('d.m.Y H\h', time() + $user->getNewBanTime())} + ")"; MessageBox("Забанить " + {$user->getFirstName()}, uBanMsgTxt, ["Подтвердить", "Отмена"], [ (function() { res = document.querySelector("#uBanMsgInput").value; date = document.querySelector("#uBanMsgDate").value; + incr = document.querySelector("#uBanMsgIncr").checked ? '1' : '0'; xhr = new XMLHttpRequest(); - xhr.open("GET", "/admin/ban/" + {$user->getId()} + "?reason=" + res + "&date=" + date + "&hash=" + {rawurlencode($csrfToken)}, true); + xhr.open("GET", "/admin/ban/" + {$user->getId()} + "?reason=" + res + "&incr=" + incr + "&date=" + date + "&hash=" + {rawurlencode($csrfToken)}, true); xhr.onload = (function() { if(xhr.responseText.indexOf("success") === -1) MessageBox("Ошибка", "Не удалось забанить пользователя...", ["OK"], [Function.noop]); diff --git a/Web/Presenters/templates/User/banned.xml b/Web/Presenters/templates/User/banned.xml index a495a59d..8abe4d89 100644 --- a/Web/Presenters/templates/User/banned.xml +++ b/Web/Presenters/templates/User/banned.xml @@ -3,7 +3,9 @@

{tr("user_banned", htmlentities($user->getFirstName()))|noescape}
{_user_banned_comment} {$user->getBanReason()}.
- Пользователь заблокирован до: {$user->getUnbanTime()} + Пользователь заблокирован + до: {$user->getUnbanTime()} + навсегда

{if isset($thisUser)}

diff --git a/Web/Presenters/templates/Videos/View.xml b/Web/Presenters/templates/Videos/View.xml index 47f390fa..3cfc45c6 100644 --- a/Web/Presenters/templates/Videos/View.xml +++ b/Web/Presenters/templates/Videos/View.xml @@ -76,7 +76,7 @@ (function() { res = document.querySelector("#uReportMsgInput").value; xhr = new XMLHttpRequest(); - xhr.open("GET", "/report.pl/" + {$video->getId()} + "?reason=" + res + "&type=video", true); + xhr.open("GET", "/report/" + {$video->getId()} + "?reason=" + res + "&type=video", true); xhr.onload = (function() { if(xhr.responseText.indexOf("reason") === -1) MessageBox("Ошибка", "Не удалось подать жалобу...", ["OK"], [Function.noop]); diff --git a/Web/Presenters/templates/components/comment.xml b/Web/Presenters/templates/components/comment.xml index 125c1cef..f26fa41a 100644 --- a/Web/Presenters/templates/components/comment.xml +++ b/Web/Presenters/templates/components/comment.xml @@ -29,20 +29,50 @@

- {$comment->getPublicationTime()} | - {if $comment->canBeDeletedBy($thisUser)} - {_delete} | + {$comment->getPublicationTime()} + {if !$timeOnly} +  | + {if $comment->canBeDeletedBy($thisUser)} + {_delete} | + {/if} + {_reply} + {if $thisUser->getId() != $comment->getOwner()->getId()} + {var $canReport = true} + | Пожаловаться + {/if} +
+ +
+ {if $comment->getLikesCount() > 0}{$comment->getLikesCount()}{/if} +
+
{/if} - {_reply} -
- -
- {if $comment->getLikesCount() > 0}{$comment->getLikesCount()}{/if} -
-
+ \ No newline at end of file diff --git a/Web/Presenters/templates/components/group.xml b/Web/Presenters/templates/components/group.xml index 2ddca6af..1beaa889 100644 --- a/Web/Presenters/templates/components/group.xml +++ b/Web/Presenters/templates/components/group.xml @@ -1,22 +1,43 @@ {block content} -
- - - - - - - -
- - Фотография группы - - - - - asdasdasd - - -
-
+
+ + + + + + + +
+ + Фотография группы + + + + + + + + + + + + + +
+ {_name}: + + {$group->getName()} + +
+ {_size}: + + {tr("participants", + $group->getFollowersCount())} + +
+
+
{/block} diff --git a/Web/routes.yml b/Web/routes.yml index b12bc257..7f7233bf 100644 --- a/Web/routes.yml +++ b/Web/routes.yml @@ -311,9 +311,9 @@ routes: handler: "Report->list" - url: "/admin/report{num}" handler: "Report->view" - - url: "/admin/support/report{num}" + - url: "/admin/report{num}" handler: "Report->view" - - url: "/admin/support/reportAction{num}" + - url: "/admin/reportAction{num}" handler: "Report->action" - url: "/report/{num}" handler: "Report->create" @@ -323,6 +323,8 @@ routes: handler: "Admin->bannedLink" - url: "/admin/bannedLink/id{num}/unban" handler: "Admin->unbanLink" + - url: "/admin/user{num}/bans" + handler: "Admin->bansHistory" - url: "/upload/photo/{text}" handler: "VKAPI->photoUpload" - url: "/method/{text}.{text}" diff --git a/install/sqls/00032-better-reports.sql b/install/sqls/00032-better-reports.sql new file mode 100644 index 00000000..86de58e7 --- /dev/null +++ b/install/sqls/00032-better-reports.sql @@ -0,0 +1,19 @@ +CREATE TABLE `bans` +( + `id` bigint(20) UNSIGNED NOT NULL, + `user` bigint(20) UNSIGNED NOT NULL, + `initiator` bigint(20) UNSIGNED NOT NULL, + `iat` bigint(20) UNSIGNED NOT NULL, + `exp` bigint(20) NOT NULL, + `time` bigint(20) NOT NULL, + `reason` text COLLATE utf8mb4_unicode_ci NOT NULL, + `removed_manually` tinyint(1) DEFAULT 0, + `removed_by` bigint(20) UNSIGNED NOT NULL DEFAULT 0 +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +ALTER TABLE `bans` + ADD PRIMARY KEY (`id`); + +ALTER TABLE `bans` + MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT; +COMMIT; diff --git a/locales/en.strings b/locales/en.strings index cf634c77..dfd58dab 100644 --- a/locales/en.strings +++ b/locales/en.strings @@ -1154,3 +1154,5 @@ "url_is_banned_default_reason" = "The link you are trying to open may lead you to a site that was created for the purpose of deceiving users with the intention of gaining profit."; "url_is_banned_title" = "Link to a suspicious site"; "url_is_banned_proceed" = "Follow the link"; + +"recently" = "Recently"; diff --git a/locales/ru.strings b/locales/ru.strings index 5b0d765b..4b18c2e3 100644 --- a/locales/ru.strings +++ b/locales/ru.strings @@ -1213,3 +1213,5 @@ "url_is_banned_default_reason" = "Ссылка, по которой вы попытались перейти, может вести на сайт, который был создан с целью обмана пользователей и получения за счёт этого прибыли."; "url_is_banned_title" = "Ссылка на подозрительный сайт"; "url_is_banned_proceed" = "Перейти по ссылке"; + +"recently" = "Недавно";