About: Add a page with information about an instance

Namely, brief statistics, a list of administrators (the administrator can be hidden from the list using a new right), a list of the 10 most popular groups and rules
This commit is contained in:
Maxim Leshchenko 2022-01-03 17:31:55 +02:00
parent 6f24b46292
commit 1b1c88dd96
No known key found for this signature in database
GPG key ID: BB9C44A8733FBEEE
9 changed files with 165 additions and 1 deletions

View file

@ -37,6 +37,24 @@ class Clubs
return new Util\EntityStream("Club", $result);
}
function getCount(): int
{
return sizeof(clone $this->clubs);
}
function getPopularClubs(): \Traversable
{
$query = "SELECT ROW_NUMBER() OVER (ORDER BY `subscriptions` DESC) as `place`, `target` as `id`, COUNT(`follower`) as `subscriptions` FROM `subscriptions` WHERE `model` = \"openvk\\\Web\\\Models\\\Entities\\\Club\" GROUP BY `target` ORDER BY `subscriptions` DESC, `id` LIMIT 10;";
$entries = DatabaseConnection::i()->getConnection()->query($query);
foreach($entries as $entry)
yield (object) [
"place" => $entry["place"],
"club" => $this->get($entry["id"]),
"subscriptions" => $entry["subscriptions"],
];
}
use \Nette\SmartObject;
}

View file

@ -105,4 +105,9 @@ class Posts
{
return sizeof($this->posts->where(["wall" => $user, "deleted" => 0]));
}
function getCount(): int
{
return sizeof(clone $this->posts);
}
}

View file

@ -69,6 +69,24 @@ class Users
return $this->getByShortUrl($address);
}
/**
* If you need to check if the user is an instance administrator, use `$user->getChandlerUser()->can("access")->model("admin")->whichBelongsTo(NULL)`.
* This method is more suitable for instance administrators lists
*/
function getInstanceAdmins(bool $excludeHidden = true): \Traversable
{
$query = "SELECT DISTINCT(`profiles`.`id`) FROM `ChandlerACLRelations` JOIN `profiles` ON `ChandlerACLRelations`.`user` = `profiles`.`user` COLLATE utf8mb4_unicode_520_ci WHERE `ChandlerACLRelations`.`group` IN (SELECT `group` FROM `ChandlerACLGroupsPermissions` WHERE `model` = \"admin\" AND `permission` = \"access\")";
if($excludeHidden)
$query .= " AND `ChandlerACLRelations`.`user` NOT IN (SELECT `user` FROM `ChandlerACLRelations` WHERE `group` IN (SELECT `group` FROM `ChandlerACLGroupsPermissions` WHERE `model` = \"hidden_admin\" AND `permission` = \"be\"))";
$query .= " ORDER BY `profiles`.`id`;";
$result = DatabaseConnection::i()->getConnection()->query($query);
foreach($result as $entry)
yield $this->get($entry->id);
}
use \Nette\SmartObject;
}

View file

@ -1,7 +1,7 @@
<?php declare(strict_types=1);
namespace openvk\Web\Presenters;
use openvk\Web\Themes\Themepacks;
use openvk\Web\Models\Repositories\{Users, Managers};
use openvk\Web\Models\Repositories\{Users, Managers, Clubs, Posts};
use openvk\Web\Util\Localizator;
use Chandler\Session\Session;
@ -57,6 +57,15 @@ final class AboutPresenter extends OpenVKPresenter
$this->template->themes = Themepacks::i()->getAllThemes();
$this->template->languages = getLanguages();
}
function renderAboutInstance(): void
{
$this->template->usersStats = (new Users)->getStatistics();
$this->template->clubsCount = (new Clubs)->getCount();
$this->template->postsCount = (new Posts)->getCount();
$this->template->popularClubs = (new Clubs)->getPopularClubs();
$this->template->admins = iterator_to_array((new Users)->getInstanceAdmins());
}
function renderLanguage(): void
{

View file

@ -267,6 +267,7 @@
{var dbVersion = \Chandler\Database\DatabaseConnection::i()->getConnection()->getPdo()->getAttribute(\PDO::ATTR_SERVER_VERSION)}
<div class="navigation_footer">
<a href="/about" class="link">{_footer_about_instance}</a>
<a href="/blog" class="link">{_footer_blog}</a>
<a href="/support" class="link">{_footer_help}</a>
<a href="/dev" target="_blank" class="link">{_footer_developers}</a>

View file

@ -0,0 +1,59 @@
{extends "../@layout.xml"}
{block title}{_about_this_instance}{/block}
{block header}
{_about_this_instance}
{/block}
{block content}
<table width="100%" cellspacing="0" cellpadding="0">
<tbody>
<tr valign="top">
<td width="250" {if sizeof($admins) > 0}style="padding-right: 10px;"{/if}>
<h4>{_statistics}</h4>
<div style="margin-top: 5px;">
{_on_this_instance_are}
<ul>
<li><span>{tr("about_users", $usersStats->all)|noescape}</span></li>
<li><span>{tr("about_online_users", $usersStats->online)|noescape}</span></li>
<li><span>{tr("about_active_users", $usersStats->active)|noescape}</span></li>
<li><span>{tr("about_groups", $clubsCount)|noescape}</span></li>
<li><span>{tr("about_wall_posts", $postsCount)|noescape}</span></li>
</ul>
</div>
</td>
<td n:if="sizeof($admins) > 0">
<h4>{_administrators}</h4>
<div style="margin-left: 5px; margin-top: 5px;">
<div n:foreach="$admins as $admin" class="avatar-list-item">
<div class="avatar">
<a href="{$admin->getURL()}">
<img class="ava" src="{$admin->getAvatarUrl()}" />
</a>
</div>
{* Это наверное костыль, ну да ладно *}
<div n:class="info, mb_strlen($admin->getCanonicalName()) < 22 ? info-centered">
<a href="{$admin->getURL()}" class="title">{$admin->getCanonicalName()}</a>
</div>
</div>
</div>
</td>
</tr>
</tbody>
</table>
<h4>{_most_popular_groups}</h4>
<ol>
<li n:foreach="$popularClubs as $entry" style="margin-top: 5px;">
<a href="{$entry->club->getURL()}">{$entry->club->getName()}</a>
<div>
{tr("participants", $entry->subscriptions)}
</div>
</li>
</ol>
<h4>{_rules}</h4>
<div style="margin-top: 16px;">
{presenter "openvk!Support->knowledgeBaseArticle", "rules"}
</div>
{/block}

View file

@ -39,6 +39,8 @@ routes:
handler: "About->version"
placeholders:
productName: "openvk[2]?|libresoc"
- url: "/about"
handler: "About->aboutInstance"
- url: "/privacy"
handler: "About->Privacy"
- url: "/badbrowser.php"

View file

@ -338,6 +338,7 @@
"left_menu_donate" = "Donate";
"footer_about_instance" = "about instance";
"footer_blog" = "blog";
"footer_help" = "help";
"footer_developers" = "developers";
@ -761,6 +762,26 @@
"about_openvk" = "About OpenVK";
"about_this_instance" = "About this instance";
"rules" = "Rules";
"most_popular_groups" = "Most popular groups";
"on_this_instance_are" = "On this instance are:";
"about_users_one" = "<b>1</b> user";
"about_users_other" = "<b>$1</b> users";
"about_online_users_one" = "<b>1</b> online user";
"about_online_users_other" = "<b>$1</b> online users";
"about_active_users_one" = "<b>1</b> active user";
"about_active_users_other" = "<b>$1</b> active users";
"about_groups_one" = "<b>1</b> group";
"about_groups_other" = "<b>$1</b> groups";
"about_wall_posts_one" = "<b>1</b> wall post";
"about_wall_posts_other" = "<b>$1</b> wall posts";
/* Dialogs */
"ok" = "OK";

View file

@ -356,6 +356,7 @@
"left_menu_donate" = "Поддержать";
"footer_about_instance" = "об инстанции";
"footer_blog" = "блог";
"footer_help" = "помощь";
"footer_developers" = "разработчикам";
@ -796,6 +797,36 @@
"about_openvk" = "Об OpenVK";
"about_this_instance" = "Об этой инстанции";
"rules" = "Правила";
"most_popular_groups" = "Самые популярные группы";
"on_this_instance_are" = "На этой инстанции:";
"about_users_one" = "<b>1</b> пользователь";
"about_users_few" = "<b>$1</b> пользователя";
"about_users_many" = "<b>$1</b> пользователей";
"about_users_other" = "<b>$1</b> пользователей";
"about_online_users_one" = "<b>1</b> пользователь в сети";
"about_online_users_few" = "<b>$1</b> пользователя в сети";
"about_online_users_many" = "<b>$1</b> пользователей в сети";
"about_online_users_other" = "<b>$1</b> пользователей в сети";
"about_active_users_one" = "<b>1</b> активный пользователь";
"about_active_users_few" = "<b>$1</b> активных пользователя";
"about_active_users_many" = "<b>$1</b> активных пользователей";
"about_active_users_other" = "<b>$1</b> активных пользователей";
"about_groups_one" = "<b>1</b> группа";
"about_groups_few" = "<b>$1</b> группы";
"about_groups_many" = "<b>$1</b> групп";
"about_groups_other" = "<b>$1</b> групп";
"about_wall_posts_one" = "<b>1</b> запись на стенах";
"about_wall_posts_few" = "<b>$1</b> записи на стенах";
"about_wall_posts_many" = "<b>$1</b> записей на стенах";
"about_wall_posts_other" = "<b>$1</b> записей на стенах";
/* Dialogs */
"ok" = "ОК";