Added private Forum Leaderboard page.

This commit is contained in:
flash 2019-03-31 18:49:16 +02:00
parent 9451cc384e
commit a7b82293a6
16 changed files with 355 additions and 27 deletions

View file

@ -0,0 +1,10 @@
@import "actions";
@import "categories";
@import "category";
@import "confirm";
@import "header";
@import "post";
@import "status";
@import "topic";
@import "topics";
@import "leaderboard/leaderboard";

View file

@ -0,0 +1,8 @@
.forum__leaderboard__categories {
display: block;
overflow-x: auto;
overflow-y: hidden;
white-space: nowrap;
margin: 2px 0;
scrollbar-width: thin;
}

View file

@ -0,0 +1,19 @@
.forum__leaderboard__category {
display: inline-block;
color: inherit;
text-decoration: none;
margin: 2px;
padding: 2px 5px;
border-radius: 4px;
transition: background-color .2s;
&:hover,
&:focus {
background-color: fade(#fff, 20%);
}
&--active,
&:active {
background-color: fade(#fff, 10%);
}
}

View file

@ -0,0 +1,4 @@
@import "categories";
@import "category";
@import "markdown";
@import "user";

View file

@ -0,0 +1,8 @@
.forum__leaderboard__markdown {
display: block;
width: 100%;
min-width: 100%;
max-width: 100%;
min-height: 500px;
margin: 2px auto;
}

View file

@ -0,0 +1,83 @@
.forum__leaderboard__user {
margin: 2px 0;
font-size: 1.2em;
&--rank-1 {
font-size: 1.6em;
}
&--rank-2,
&--rank-3 {
font-size: 1.4em;
}
&__background {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
text-decoration: none;
color: inherit;
}
&__content {
display: flex;
pointer-events: none;
}
&__rank {
height: 40px;
min-width: 50px;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
flex: 0 0 auto;
&:before {
content: "#";
}
}
&--rank-1 &__rank {
height: 50px;
}
&__avatar {
width: 40px;
height: 40px;
margin: 2px 7px;
flex: 0 0 auto;
}
&--rank-1 &__avatar {
width: 50px;
height: 50px;
margin: 2px;
}
&__username {
flex: 1 1 auto;
line-height: 30px;
padding: 5px;
margin: 2px;
}
&--rank-1 &__username {
line-height: 40px;
}
&__posts {
flex: 0 0 auto;
min-width: 150px;
border-left: 1px solid fade(#fff, 20%);
line-height: 30px;
padding: 5px;
margin: 2px;
}
&--rank-1 &__posts {
line-height: 40px;
}
}

View file

@ -194,15 +194,7 @@ html {
@import "classes/news/post"; // post needs to be able to override sidebar
// Forums
@import "classes/forum/actions";
@import "classes/forum/categories";
@import "classes/forum/category";
@import "classes/forum/confirm";
@import "classes/forum/post";
@import "classes/forum/topic";
@import "classes/forum/topics";
@import "classes/forum/status";
@import "classes/forum/header";
@import "classes/forum/forum";
// User stuff
@import "classes/usercard";

View file

@ -50,6 +50,7 @@ require_once 'src/twitter.php';
require_once 'src/url.php';
require_once 'src/zalgo.php';
require_once 'src/Forum/forum.php';
require_once 'src/Forum/leaderboard.php';
require_once 'src/Forum/perms.php';
require_once 'src/Forum/post.php';
require_once 'src/Forum/topic.php';

View file

@ -0,0 +1,54 @@
<?php
require_once '../../misuzu.php';
if (!perms_check_user(MSZ_PERMS_FORUM, user_session_current('user_id'), MSZ_PERM_FORUM_VIEW_LEADERBOARD)) {
echo render_error(403);
return;
}
$leaderboardMode = !empty($_GET['mode']) && is_string($_GET['mode']) && ctype_lower($_GET['mode']) ? $_GET['mode'] : '';
$leaderboardId = !empty($_GET['id']) && is_string($_GET['id'])
&& ctype_digit($_GET['id'])
? $_GET['id']
: MSZ_FORUM_LEADERBOARD_CATEGORY_ALL;
$leaderboardIdLength = strlen($leaderboardId);
$leaderboardYear = $leaderboardIdLength === 4 || $leaderboardIdLength === 6 ? substr($leaderboardId, 0, 4) : null;
$leaderboardMonth = $leaderboardIdLength === 6 ? substr($leaderboardId, 4, 2) : null;
$leaderboards = forum_leaderboard_categories();
$leaderboard = forum_leaderboard_listing($leaderboardYear, $leaderboardMonth);
$leaderboardName = 'All Time';
if($leaderboardYear) {
$leaderboardName = "Leaderboard {$leaderboardYear}";
if($leaderboardMonth) {
$leaderboardName .= "-{$leaderboardMonth}";
}
}
if($leaderboardMode === 'markdown') {
$markdown = <<<MD
# {$leaderboardName}
| Rank | Usename | Post count |
| ----:|:------- | ----------:|
MD;
foreach($leaderboard as $user) {
$markdown .= sprintf("| %s | [%s](%s%s) | %s |\r\n", $user['rank'], $user['username'], url_prefix(false), url('user-profile', ['user' => $user['user_id']]), $user['posts']);
}
tpl_var('leaderboard_markdown', $markdown);
}
echo tpl_render('forum.leaderboard', [
'leaderboard_id' => $leaderboardId,
'leaderboard_name' => $leaderboardName,
'leaderboard_categories' => $leaderboards,
'leaderboard_data' => $leaderboard,
'leaderboard_mode' => $leaderboardMode,
]);

View file

@ -3,6 +3,7 @@
* GLOBAL PERMISSIONS *
**********************/
define('MSZ_PERM_FORUM_MANAGE_FORUMS', 1);
define('MSZ_PERM_FORUM_VIEW_LEADERBOARD', 2);
/*************************
* PER-FORUM PERMISSIONS *

93
src/Forum/leaderboard.php Normal file
View file

@ -0,0 +1,93 @@
<?php
define('MSZ_FORUM_LEADERBOARD_START_YEAR', 2018);
define('MSZ_FORUM_LEADERBOARD_START_MONTH', 12);
define('MSZ_FORUM_LEADERBOARD_CATEGORY_ALL', 0);
function forum_leaderboard_year_valid(?int $year): bool
{
return !is_null($year) && $year >= MSZ_FORUM_LEADERBOARD_START_YEAR && $year <= date('Y');
}
function forum_leaderboard_month_valid(?int $year, ?int $month): bool
{
if (is_null($month) || !forum_leaderboard_year_valid($year) || $month < 1 || $month > 12) {
return false;
}
$combo = sprintf('%04d%02d', $year, $month);
$start = sprintf('%04d%02d', MSZ_FORUM_LEADERBOARD_START_YEAR, MSZ_FORUM_LEADERBOARD_START_MONTH);
$current = date('Ym');
return $combo >= $start && $combo <= $current;
}
function forum_leaderboard_categories(): array
{
$categories = [
MSZ_FORUM_LEADERBOARD_CATEGORY_ALL => 'All Time',
];
$currentYear = date('Y');
$currentMonth = date('m');
for ($i = MSZ_FORUM_LEADERBOARD_START_YEAR; $i <= $currentYear; $i++) {
$categories[$i] = sprintf('Leaderboard %d', $i);
}
for ($i = MSZ_FORUM_LEADERBOARD_START_YEAR, $j = MSZ_FORUM_LEADERBOARD_START_MONTH;;) {
$categories[sprintf('%d%02d', $i, $j)] = sprintf('Leaderboard %d-%02d', $i, $j);
if ($j >= 12) {
$i++; $j = 1;
} else $j++;
if ($i >= $currentYear && $j > $currentMonth)
break;
}
return $categories;
}
function forum_leaderboard_listing(?int $year = null, ?int $month = null): array
{
$hasYear = forum_leaderboard_year_valid($year);
$hasMonth = $hasYear && forum_leaderboard_month_valid($year, $month);
$rawLeaderboard = db_fetch_all(db_query(sprintf(
'
SELECT
u.`user_id`, u.`username`,
COUNT(fp.`post_id`) as `posts`
FROM `msz_users` AS u
INNER JOIN `msz_forum_posts` AS fp
ON fp.`user_id` = u.`user_id`
WHERE fp.`post_deleted` IS NULL
%s
GROUP BY u.`user_id`
HAVING `posts` > 0
ORDER BY `posts` DESC
',
!$hasYear ? '' : sprintf(
'AND DATE(fp.`post_created`) BETWEEN \'%1$04d-%2$02d-01\' AND \'%1$04d-%3$02d-31\'',
$year,
$hasMonth ? $month : 1,
$hasMonth ? $month : 12
)
)));
$leaderboard = [];
$ranking = 0;
$lastPosts = null;
foreach ($rawLeaderboard as $entry) {
if (is_null($lastPosts) || $lastPosts > $entry['posts']) {
$ranking++;
$lastPosts = $entry['posts'];
}
$entry['rank'] = $ranking;
$leaderboard[] = $entry;
}
return $leaderboard;
}

View file

@ -282,6 +282,11 @@ function manage_perms_list(array $rawPerms): array
'title' => 'Can manage forum sections.',
'perm' => MSZ_PERM_FORUM_MANAGE_FORUMS,
],
[
'section' => 'view-leaderboard',
'title' => 'Can view the forum leaderboard live.',
'perm' => MSZ_PERM_FORUM_VIEW_LEADERBOARD,
],
],
],
[

View file

@ -202,7 +202,7 @@ function perms_check(int $perms, int $perm): bool
return ($perms & $perm) > 0;
}
function perms_check_user(string $prefix, int $userId, int $perm): bool
function perms_check_user(string $prefix, ?int $userId, int $perm): bool
{
return perms_check(perms_get_user($prefix, $userId), $perm);
return $userId > 0 && perms_check(perms_get_user($prefix, $userId), $perm);
}

View file

@ -32,6 +32,7 @@ define('MSZ_URLS', [
'news-category' => ['/news.php', ['c' => '<category>', 'page' => '<page>']],
'forum-index' => ['/forum'],
'forum-leaderboard' => ['/forum/leaderboard.php', ['id' => '<id>', 'mode' => '<mode>']],
'forum-mark-global' => ['/forum/index.php', ['m' => 'mark', 'c' => '{forum_mark}']],
'forum-mark-single' => ['/forum/index.php', ['m' => 'mark', 'c' => '{forum_mark}', 'f' => '<forum>']],
'forum-topic-new' => ['/forum/posting.php', ['f' => '<forum>']],
@ -224,3 +225,23 @@ function url_proxy_media(?string $url): ?string
return url('media-proxy', compact('hash', 'url'));
}
function url_prefix(bool $trailingSlash = true): string
{
return 'http' . (empty($_SERVER['HTTPS']) ? '' : 's') . '://' . $_SERVER['HTTP_HOST'] . ($trailingSlash ? '/' : '');
}
function is_local_url(string $url): bool
{
$length = mb_strlen($url);
if ($length < 1) {
return false;
}
if ($url[0] === '/' && ($length > 1 ? $url[1] !== '/' : true)) {
return true;
}
return starts_with($url, url_prefix());
}

View file

@ -0,0 +1,45 @@
{% extends 'forum/master.twig' %}
{% from 'forum/macros.twig' import forum_header %}
{% set title = 'Forum Leaderboard » ' ~ leaderboard_name %}
{% set canonical_url = url('forum-leaderboard', {
'id': leaderboard_id,
'mode': '',
}) %}
{% block content %}
{{ forum_header(title, [], false, canonical_url, [
{
'html': '<i class="fab fa-markdown fa-fw"></i> Markdown',
'url': url('forum-leaderboard', {'id': leaderboard_id, 'mode': 'markdown'}),
'display': leaderboard_mode != 'markdown',
},
{
'html': '<i class="fas fa-table fa-fw"></i> Table',
'url': url('forum-leaderboard', {'id': leaderboard_id}),
'display': leaderboard_mode == 'markdown',
},
]) }}
<div class="container forum__leaderboard__categories">
{% for id, name in leaderboard_categories %}
<a href="{{ url('forum-leaderboard', {'id': id, 'mode': leaderboard_mode}) }}" class="forum__leaderboard__category{% if leaderboard_id == id %} forum__leaderboard__category--active{% endif %}">{{ name }}</a>
{% endfor %}
</div>
{% if leaderboard_mode == 'markdown' %}
<textarea class="input__textarea forum__leaderboard__markdown">{{ leaderboard_markdown }}</textarea>
{% else %}
{% for user in leaderboard_data %}
<div class="container forum__leaderboard__user forum__leaderboard__user--rank-{{ user.rank }}">
<a href="{{ url('user-profile', {'user': user.user_id}) }}" class="forum__leaderboard__user__background"></a>
<div class="forum__leaderboard__user__content">
<div class="forum__leaderboard__user__rank">{{ user.rank|number_format }}</div>
<div class="avatar forum__leaderboard__user__avatar" style="background-image:url('{{ url('user-avatar', {'user': user.user_id, 'r': 80}) }}')"></div>
<div class="forum__leaderboard__user__username">{{ user.username }}</div>
<div class="forum__leaderboard__user__posts">{{ user.posts|number_format }} posts</div>
</div>
</div>
{% endfor %}
{% endif %}
{% endblock %}

View file

@ -64,22 +64,6 @@ function pdo_prepare_array(array $keys, bool $useKeys = false, string $format =
return implode(', ', $parts);
}
function is_local_url(string $url): bool
{
$length = mb_strlen($url);
if ($length < 1) {
return false;
}
if ($url[0] === '/' && ($length > 1 ? $url[1] !== '/' : true)) {
return true;
}
$prefix = 'http' . (empty($_SERVER['HTTPS']) ? '' : 's') . '://' . $_SERVER['HTTP_HOST'] . '/';
return starts_with($url, $prefix);
}
function render_error(int $code, string $template = 'errors.%d'): string
{
return render_info(null, $code, $template);