CHAnGELOG

This commit is contained in:
flash 2018-07-06 03:28:06 +02:00
parent 2d7e55f103
commit 80f9c7d0dc
33 changed files with 886 additions and 95 deletions

View file

@ -0,0 +1,224 @@
@mio-changelog-mobile: 700px;
.changelog {
display: flex;
@media (max-width: @mio-changelog-mobile) {
flex-wrap: wrap;
}
&:not(:last-child) {
margin-bottom: 1px;
@media (max-width: @mio-changelog-mobile) {
border-bottom: 1px solid #9475b2;
padding-bottom: 1px;
}
}
&__content {
margin: 1px;
}
&__date {
display: block;
background: #9475b2;
color: #306;
font-weight: 700;
margin-bottom: 1px;
text-decoration: none;
padding: 1px 3px;
&:hover {
text-decoration: underline;
}
&:not(:first-child) {
margin-top: 4px;
}
}
&__user,
&__action {
display: inline-flex;
color: inherit;
flex: 0 0 auto;
margin-right: 1px;
justify-content: center;
align-items: center;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
&__user {
min-width: 100px;
justify-content: left;
padding-left: 4px;
@media (max-width: @mio-changelog-mobile) {
flex-grow: 1;
}
}
&__action {
min-width: 80px;
justify-content: right;
padding-right: 4px;
@media (max-width: @mio-changelog-mobile) {
margin-right: 0;
}
&--light {
color: #fff;
}
}
&__log {
word-wrap: break-word;
overflow: hidden;
flex: 1 1 auto;
margin-left: 2px;
@media (max-width: @mio-changelog-mobile) {
width: 100%;
}
&--link {
color: inherit;
text-decoration: underline dotted;
&:hover {
text-decoration: underline solid;
}
}
}
&__change {
&__line {
border: 1px solid #9475b2;
display: flex;
align-items: center;
flex: 1 0 auto;
}
&__row,
&__column {
display: flex;
}
&__column {
flex-direction: column;
&--user-info {
justify-content: space-between;
flex: 0 0 auto;
}
&--change-info {
flex: 1 1 auto;
}
}
&__action {
display: block;
padding: 6px 2px;
border-right: 1px solid #9475b2;
writing-mode: sideways-lr;
text-orientation: sideways;
letter-spacing: 1px;
&--light {
color: #fff;
}
}
&__log {
padding: 4px 12px;
font-size: 1.4em;
}
&__date {
border: 1px solid #9475b2;
color: inherit;
text-decoration: none;
margin-right: 1px;
text-align: center;
padding: 2px;
&:hover {
text-decoration: underline;
}
}
&__user {
border: 1px solid #9475b2;
padding: 2px;
display: flex;
align-items: center;
text-decoration: none;
color: inherit;
margin: 1px;
margin-left: 0;
&__info {
margin: 2px 5px;
}
&__title {
width: 120px;
word-wrap: break-word;
}
&__avatar {
width: 60px;
height: 60px;
margin-right: 1px;
align-self: flex-start;
}
&__name {
font-size: 1.5em;
line-height: 1.2em;
}
}
&__text {
margin: 1px 0;
padding: 1px 3px;
border-top: 1px solid #9475b2;
font-size: 1.2em;
line-height: 1.5em;
flex: 1 1 auto;
}
&__tags {
list-style: none;
display: flex;
flex-wrap: wrap;
}
&__tag {
border: 1px solid #9475b2;
margin-right: 1px;
border-radius: 2px;
&__link {
display: block;
width: 100%;
height: 100%;
color: inherit;
text-decoration: none;
padding: 0 5px;
}
&:hover {
text-decoration: underline;
}
}
}
}

View file

@ -1,15 +1,15 @@
@flag-width: 16px;
@flag-height: 11px;
.flag-position(@x, @y, @offsetX: 0px, @offsetY: 0px) {
background-position: top -(@flag-height * @x) + @offsetX left -(@flag-width * @y) + @offsetY;
.flag-position(@x, @y) {
background-position: top -((@flag-height + 1) * @x) left -(@flag-width * @y);
}
.flag {
display: inline-block;
width: @flag-width;
height: @flag-height;
background-image: url('https://static.flash.moe/images/flag-sprite-24.png');
background-image: url('https://static.flash.moe/images/flag-sprite.png');
background-repeat: no-repeat;
font-size: 0;
.flag-position(23, 23); // xx
@ -118,8 +118,6 @@
&--gw {.flag-position(6, 22);}
&--gy {.flag-position(6, 24);}
&--hd {.flag-position(7, 3);}
&--he {.flag-position(7, 4);}
&--hk {.flag-position(7, 10);}
&--hm {.flag-position(7, 12);}
&--hn {.flag-position(7, 13);}
@ -127,6 +125,8 @@
&--ht {.flag-position(7, 19);}
&--hu {.flag-position(7, 20);}
&--id {.flag-position(8, 3);}
&--ie {.flag-position(8, 4);}
&--il {.flag-position(8, 11);}
&--in {.flag-position(8, 13);}
&--io {.flag-position(8, 14);}
@ -166,7 +166,7 @@
&--ma {.flag-position(12, 0);}
&--mc {.flag-position(12, 2);}
&--md {.flag-position(12, 3);}
&--me {.flag-position(12, 4, 1); height: 12px;}
&--me {.flag-position(12, 4); height: 12px;}
&--mg {.flag-position(12, 6);}
&--mh {.flag-position(12, 7);}
&--mk {.flag-position(12, 10);}

View file

@ -91,6 +91,10 @@
&__column {
min-width: 80px;
max-width: 200px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@media (max-width: @mio-profile-mobile) {
min-width: auto;

View file

@ -84,3 +84,6 @@ body {
// Member listing
@import "classes/members/user";
@import "classes/members/users";
// Changelog
@import "classes/changelog";

View file

@ -0,0 +1,81 @@
<?php
namespace Misuzu\DatabaseMigrations\ChangelogTables;
use PDO;
function migrate_up(PDO $conn): void
{
$conn->exec("
CREATE TABLE `msz_changelog_actions` (
`action_id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`action_name` VARCHAR(50) NOT NULL,
`action_colour` INT(10) UNSIGNED NOT NULL DEFAULT '0',
`action_class` VARCHAR(20) NOT NULL,
PRIMARY KEY (`action_id`),
UNIQUE INDEX `action_class_unique` (`action_class`)
)
");
$conn->exec("
CREATE TABLE `msz_changelog_changes` (
`change_id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` INT(10) UNSIGNED NULL DEFAULT NULL,
`action_id` INT(10) UNSIGNED NULL DEFAULT NULL,
`change_created` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`change_log` VARCHAR(255) NOT NULL,
`change_text` TEXT NULL,
PRIMARY KEY (`change_id`),
INDEX `changes_user_id_foreign` (`user_id`),
INDEX `changes_action_id_foreign` (`action_id`),
INDEX `changes_change_created_index` (`change_created`),
CONSTRAINT `changes_action_id_foreign`
FOREIGN KEY (`action_id`)
REFERENCES `msz_changelog_actions` (`action_id`)
ON UPDATE CASCADE
ON DELETE SET NULL,
CONSTRAINT `changes_user_id_foreign`
FOREIGN KEY (`user_id`)
REFERENCES `msz_users` (`user_id`)
ON UPDATE CASCADE
ON DELETE SET NULL
)
");
$conn->exec("
CREATE TABLE `msz_changelog_tags` (
`tag_id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`tag_name` VARCHAR(255) NOT NULL,
`tag_description` TEXT NULL,
`tag_created` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`tag_id`),
UNIQUE INDEX `tag_name` (`tag_name`)
)
");
$conn->exec("
CREATE TABLE `msz_changelog_change_tags` (
`change_id` INT(10) UNSIGNED NOT NULL,
`tag_id` INT(10) UNSIGNED NOT NULL,
INDEX `tag_id_foreign_key` (`tag_id`),
INDEX `change_tag_constraint` (`change_id`, `tag_id`),
CONSTRAINT `change_id_foreign_key`
FOREIGN KEY (`change_id`)
REFERENCES `msz_changelog_changes` (`change_id`)
ON UPDATE CASCADE
ON DELETE CASCADE,
CONSTRAINT `tag_id_foreign_key`
FOREIGN KEY (`tag_id`)
REFERENCES `msz_changelog_tags` (`tag_id`)
ON UPDATE CASCADE
ON DELETE CASCADE
)
");
}
function migrate_down(PDO $conn): void
{
$conn->exec('DROP TABLE `msz_changelog_change_tags`');
$conn->exec('DROP TABLE `msz_changelog_tags`');
$conn->exec('DROP TABLE `msz_changelog_changes`');
$conn->exec('DROP TABLE `msz_changelog_actions`');
}

View file

@ -4,6 +4,7 @@ namespace Misuzu;
date_default_timezone_set('UTC');
require_once __DIR__ . '/vendor/autoload.php';
require_once __DIR__ . '/src/changelog.php';
require_once __DIR__ . '/src/colour.php';
require_once __DIR__ . '/src/zalgo.php';
require_once __DIR__ . '/src/Forum/forum.php';

133
public/changelog.php Normal file
View file

@ -0,0 +1,133 @@
<?php
use Misuzu\Database;
require_once __DIR__ . '/../misuzu.php';
$db = Database::connection();
$tpl = $app->getTemplating();
$changelogOffset = max((int)($_GET['o'] ?? 0), 0);
$changelogRange = 30;
$changelogChange = (int)($_GET['c'] ?? 0);
$changelogDate = $_GET['d'] ?? '';
$tpl->vars([
'changelog_offset' => $changelogOffset,
'changelog_take' => $changelogRange,
]);
if ($changelogChange > 0) {
$getChange = $db->prepare('
SELECT
c.`change_id`, c.`change_created`, c.`change_log`, c.`change_text`,
a.`action_name`, a.`action_colour`, a.`action_class`,
u.`user_id`, u.`username`,
DATE(`change_created`) as `change_date`,
COALESCE(u.`user_title`, r.`role_title`) as `user_title`,
COALESCE(r.`role_colour`, CAST(0x40000000 AS UNSIGNED)) as `user_colour`
FROM `msz_changelog_changes` as c
LEFT JOIN `msz_users` as u
ON u.`user_id` = c.`user_id`
LEFT JOIN `msz_roles` as r
ON r.`role_id` = u.`display_role`
LEFT JOIN `msz_changelog_actions` as a
ON a.`action_id` = c.`action_id`
WHERE `change_id` = :change_id
');
$getChange->bindValue('change_id', $changelogChange);
$change = $getChange->execute() ? $getChange->fetch(PDO::FETCH_ASSOC) : [];
if (!$change) {
http_response_code(404);
} else {
$getTags = $db->prepare('
SELECT
t.`tag_id`, t.`tag_name`, t.`tag_description`
FROM `msz_changelog_tags` as t
LEFT JOIN `msz_changelog_change_tags` as ct
ON ct.`tag_id` = t.`tag_id`
WHERE ct.`change_id` = :change_id
');
$getTags->bindValue('change_id', $change['change_id']);
$tags = $getTags->execute() ? $getTags->fetchAll(PDO::FETCH_ASSOC) : [];
$tpl->var('tags', $tags);
}
echo $tpl->render('changelog.change', compact('change'));
return;
}
if (!empty($changelogDate)) {
$dateParts = explode('-', $changelogDate, 3);
if (count($dateParts) !== 3 || !checkdate($dateParts[1], $dateParts[2], $dateParts[0])) {
echo render_error(404);
return;
}
$getChanges = $db->prepare('
SELECT
c.`change_id`, c.`change_log`,
a.`action_name`, a.`action_colour`, a.`action_class`,
u.`user_id`, u.`username`,
DATE(`change_created`) as `change_date`,
!ISNULL(c.`change_text`) as `change_has_text`,
COALESCE(r.`role_colour`, CAST(0x40000000 AS UNSIGNED)) as `user_colour`
FROM `msz_changelog_changes` as c
LEFT JOIN `msz_users` as u
ON u.`user_id` = c.`user_id`
LEFT JOIN `msz_roles` as r
ON r.`role_id` = u.`display_role`
LEFT JOIN `msz_changelog_actions` as a
ON a.`action_id` = c.`action_id`
WHERE DATE(c.`change_created`) = :date
GROUP BY `change_created`, `change_id`
ORDER BY `change_created` DESC, `change_id` DESC
');
$getChanges->bindValue('date', $changelogDate);
$changes = $getChanges->execute() ? $getChanges->fetchAll() : [];
// settings the response code to 404, but rendering our own page anyway.
if (count($changes) < 1) {
http_response_code(404);
}
echo $tpl->render('changelog.date', [
'changes' => $changes,
]);
return;
}
$changesCount = (int)$db->query('
SELECT COUNT(`change_id`)
FROM `msz_changelog_changes`
')->fetchColumn();
$getChanges = $db->prepare('
SELECT
c.`change_id`, c.`change_log`,
a.`action_name`, a.`action_colour`, a.`action_class`,
u.`user_id`, u.`username`,
DATE(`change_created`) as `change_date`,
!ISNULL(c.`change_text`) as `change_has_text`,
COALESCE(r.`role_colour`, CAST(0x40000000 AS UNSIGNED)) as `user_colour`
FROM `msz_changelog_changes` as c
LEFT JOIN `msz_users` as u
ON u.`user_id` = c.`user_id`
LEFT JOIN `msz_roles` as r
ON r.`role_id` = u.`display_role`
LEFT JOIN `msz_changelog_actions` as a
ON a.`action_id` = c.`action_id`
GROUP BY `change_created`, `change_id`
ORDER BY `change_created` DESC, `change_id` DESC
LIMIT :offset, :take
');
$getChanges->bindValue('offset', $changelogOffset);
$getChanges->bindValue('take', $changelogRange);
$changes = $getChanges->execute() ? $getChanges->fetchAll() : [];
echo $tpl->render('changelog.index', [
'changes' => $changes,
'changelog_count' => $changesCount,
]);

View file

@ -3,12 +3,25 @@ use Misuzu\Database;
require_once __DIR__ . '/../misuzu.php';
$config = $app->getConfig();
$tpl = $app->getTemplating();
if ($config->get('Site', 'embed_linked_data', 'bool', false)) {
$tpl->vars([
'embed_linked_data' => true,
'embed_name' => $config->get('Site', 'name'),
'embed_url' => $config->get('Site', 'url'),
'embed_logo' => $config->get('Site', 'external_logo'),
'embed_same_as' => explode(',', $config->get('Site', 'social_media')),
]);
}
$featuredNews = Database::connection()
->query('
SELECT
p.`post_id`, p.`post_title`, p.`post_text`, p.`created_at`,
u.`user_id`, u.`username`,
COALESCE(r.`role_colour`, CAST(0x40000000 AS UNSIGNED)) as `display_colour`
COALESCE(r.`role_colour`, CAST(0x40000000 AS UNSIGNED)) as `user_colour`
FROM `msz_news_posts` as p
LEFT JOIN `msz_users` as u
ON p.`user_id` = u.`user_id`
@ -21,6 +34,6 @@ $featuredNews = Database::connection()
//var_dump(Database::connection()->query('SHOW SESSION STATUS LIKE "Questions"')->fetch()['Value']);
echo $app->getTemplating()->render('home.landing', [
echo $tpl->render('home.landing', [
'featured_news' => $featuredNews,
]);

View file

@ -92,7 +92,7 @@ $getUsers = $db->prepare("
u.`user_id`, u.`username`, u.`user_country`,
u.`created_at` as `user_joined`, u.`last_seen` as `user_last_seen`,
COALESCE(u.`user_title`, r.`role_title`) as `user_title`,
COALESCE(r.`role_colour`, CAST(0x40000000 AS UNSIGNED)) as `display_colour`,
COALESCE(r.`role_colour`, CAST(0x40000000 AS UNSIGNED)) as `user_colour`,
(
SELECT COUNT(`topic_id`)
FROM `msz_forum_topics`

View file

@ -22,7 +22,7 @@ if ($postId !== null) {
p.`post_id`, p.`post_title`, p.`post_text`, p.`created_at`,
c.`category_id`, c.`category_name`,
u.`user_id`, u.`username`,
COALESCE(r.`role_colour`, CAST(0x40000000 AS UNSIGNED)) as `display_colour`
COALESCE(r.`role_colour`, CAST(0x40000000 AS UNSIGNED)) as `user_colour`
FROM `msz_news_posts` as p
LEFT JOIN `msz_news_categories` as c
ON p.`category_id` = c.`category_id`
@ -68,7 +68,7 @@ if ($categoryId !== null) {
p.`post_id`, p.`post_title`, p.`post_text`, p.`created_at`,
c.`category_id`, c.`category_name`,
u.`user_id`, u.`username`,
COALESCE(r.`role_colour`, CAST(0x40000000 AS UNSIGNED)) as `display_colour`
COALESCE(r.`role_colour`, CAST(0x40000000 AS UNSIGNED)) as `user_colour`
FROM `msz_news_posts` as p
LEFT JOIN `msz_news_categories` as c
ON p.`category_id` = c.`category_id`
@ -134,7 +134,7 @@ $getPosts = $db->prepare('
p.`post_id`, p.`post_title`, p.`post_text`, p.`created_at`,
c.`category_id`, c.`category_name`,
u.`user_id`, u.`username`,
COALESCE(r.`role_colour`, CAST(0x40000000 AS UNSIGNED)) as `display_colour`
COALESCE(r.`role_colour`, CAST(0x40000000 AS UNSIGNED)) as `user_colour`
FROM `msz_news_posts` as p
LEFT JOIN `msz_news_categories` as c
ON p.`category_id` = c.`category_id`

View file

@ -46,17 +46,22 @@ switch ($mode) {
SELECT
u.*,
COALESCE(u.`user_title`, r.`role_title`) as `user_title`,
COALESCE(r.`role_colour`, CAST(0x40000000 AS UNSIGNED)) as `display_colour`,
COALESCE(r.`role_colour`, CAST(0x40000000 AS UNSIGNED)) as `user_colour`,
(
SELECT COUNT(`topic_id`)
FROM `msz_forum_topics` as t
WHERE t.`user_id` = u.`user_id`
FROM `msz_forum_topics`
WHERE `user_id` = u.`user_id`
) as `forum_topic_count`,
(
SELECT COUNT(`post_id`)
FROM `msz_forum_posts` as p
WHERE p.`user_id` = u.`user_id`
) as `forum_post_count`
FROM `msz_forum_posts`
WHERE `user_id` = u.`user_id`
) as `forum_post_count`,
(
SELECT COUNT(`change_id`)
FROM `msz_changelog_changes`
WHERE `user_id` = u.`user_id`
) as `changelog_count`
FROM `msz_users` as u
LEFT JOIN `msz_roles` as r
ON r.`role_id` = u.`display_role`

View file

@ -238,10 +238,13 @@ class Application extends ApplicationBase
$this->templatingInstance->addFilter('json_decode');
$this->templatingInstance->addFilter('byte_symbol');
$this->templatingInstance->addFilter('html_link');
$this->templatingInstance->addFilter('html_colour');
$this->templatingInstance->addFilter('country_name', 'get_country_name');
$this->templatingInstance->addFilter('flip', 'array_flip');
$this->templatingInstance->addFilter('first_paragraph');
$this->templatingInstance->addFilter('colour_get_css');
$this->templatingInstance->addFilter('colour_get_css_contrast');
$this->templatingInstance->addFilter('colour_get_inherit');
$this->templatingInstance->addFilter('colour_get_red');
$this->templatingInstance->addFilter('colour_get_green');

43
src/changelog.php Normal file
View file

@ -0,0 +1,43 @@
<?php
use Misuzu\Database;
function changelog_action_add(string $name, ?int $colour = null, ?string $class = null): int
{
$dbc = Database::connection();
if ($colour === null) {
$colour = colour_none();
}
$class = preg_replace('#[^a-z]#', '', strtolower($class ?? $name));
$addAction = $dbc->prepare('
INSERT INTO `msz_changelog_actions`
(`action_name`, `action_colour`, `action_class`)
VALUES
(:action_name, :action_colour, :action_class)
');
$addAction->bindValue('action_name', $name);
$addAction->bindValue('action_colour', $colour);
$addAction->bindValue('action_class', $class);
return $addAction->execute() ? (int)$dbc->lastInsertId() : 0;
}
function changelog_entry_create(int $userId, int $actionId, string $log, string $text = null): int
{
$dbc = Database::connection();
$createChange = $dbc->prepare('
INSERT INTO `msz_changelog_changes`
(`user_id`, `action_id`, `change_log`, `change_text`)
VALUES
(:user_id, :action_id, :change_log, :change_text)
');
$createChange->bindValue('user_id', $userId);
$createChange->bindValue('action_id', $actionId);
$createChange->bindValue('change_log', $log);
$createChange->bindValue('change_text', $text);
return $createChange->execute() ? (int)$dbc->lastInsertId() : 0;
}

View file

@ -1,5 +1,9 @@
<?php
define('MSZ_COLOUR_INHERIT', 0x40000000);
define('MSZ_COLOUR_READABILITY_THRESHOLD', 186);
define('MSZ_COLOUR_LUMINANCE_WEIGHT_RED', 0.299);
define('MSZ_COLOUR_LUMINANCE_WEIGHT_GREEN', 0.587);
define('MSZ_COLOUR_LUMINANCE_WEIGHT_BLUE', 0.114);
function colour_create(): int
{
@ -62,6 +66,13 @@ function colour_set_blue(int &$colour, int $blue): void
$colour |= $blue;
}
function colour_get_luminance(int $colour): int
{
return MSZ_COLOUR_LUMINANCE_WEIGHT_RED * colour_get_red($colour)
+ MSZ_COLOUR_LUMINANCE_WEIGHT_GREEN * colour_get_green($colour)
+ MSZ_COLOUR_LUMINANCE_WEIGHT_BLUE * colour_get_blue($colour);
}
function colour_get_hex(int $colour, string $format = '#%s'): string
{
return sprintf(
@ -79,6 +90,21 @@ function colour_get_css(int $colour): string
return colour_get_hex($colour);
}
function colour_get_css_contrast(
int $colour,
string $dark = 'dark',
string $light = 'light',
bool $inheritIsDark = true
): string {
if (colour_get_inherit($colour)) {
return $inheritIsDark ? $dark : $light;
}
return colour_get_luminance($colour) > MSZ_COLOUR_READABILITY_THRESHOLD
? $dark
: $light;
}
function colour_from_rgb(int &$colour, int $red, int $green, int $blue): bool
{
colour_set_red($colour, $red);

View file

@ -241,3 +241,44 @@ function render_error(int $code, string $template = 'errors.%d'): string
return '';
}
}
function html_link(string $url, ?string $content = null, $attributes = []): string
{
$content = $content ?? $url;
$attributes = array_merge(
is_string($attributes) ? ['class' => $attributes] : $attributes,
['href' => $url]
);
if (strpos($url, '://') !== false) {
$attributes['target'] = '_blank';
$attributes['rel'] = 'noreferrer noopener';
}
$html = '<a';
foreach ($attributes as $name => $value) {
$value = str_replace('"', '\"', $value);
$html .= " {$name}=\"{$value}\"";
}
$html .= ">{$content}</a>";
return $html;
}
function html_colour(int $colour, array $attribs = []): string
{
if (!$attribs) {
$attribs['color'] = '%s';
}
$css = '';
$value = colour_get_css($colour);
foreach ($attribs as $name => $format) {
$css .= $name . ':' . sprintf($format, $value) . ';';
}
return $css;
}

View file

@ -137,7 +137,7 @@
<div class="header__user">
<div class="header__menu">
<input type="checkbox" id="menu-user-state" class="header__menu__state">
<label for="menu-user-state" class="header__menu__toggle header__menu__toggle--profile" style="background-image:url('/profile.php?u={{ current_user.user_id }}&amp;m=avatar');color:{{ current_user.colour|colour_get_css }}">{{ current_user.username }}</label>
<label for="menu-user-state" class="header__menu__toggle header__menu__toggle--profile" style="background-image:url('/profile.php?u={{ current_user.user_id }}&amp;m=avatar');{{ current_user.colour|html_colour }}">{{ current_user.username }}</label>
<div class="header__menu__options header__menu__options--user">
<div class="header__menu__section">
<a class="header__menu__link" href="/profile.php?u={{ current_user.user_id }}">Profile</a>

View file

@ -4,7 +4,7 @@
{% block content %}
<div class="container listing user-listing">
{% for user in manage_users %}
<a href="?v=view&amp;u={{ user.user_id }}" class="listing__entry user-listing__entry"{% if not user.colour|colour_get_inherit %} style="border-color: {{ user.colour|colour_get_css }}"{% endif %}>
<a href="?v=view&amp;u={{ user.user_id }}" class="listing__entry user-listing__entry"{% if not user.colour|colour_get_inherit %} style="{{ user.colour|html_colour({'border-color':'%s'}) }}"{% endif %}>
<div class="listing__entry__content user-listing__entry__content">
<div class="user-listing__info">
<div class="user-listing__username">

View file

@ -8,7 +8,7 @@
<div class="container listing role-listing">
{% for role in manage_roles %}
<a href="?v=role&amp;r={{ role.role_id }}" class="listing__entry role-listing__entry"{% if not role.role_colour|colour_get_inherit %} style="border-color: {{ role.role_colour|colour_get_css }}"{% endif %}>
<a href="?v=role&amp;r={{ role.role_id }}" class="listing__entry role-listing__entry"{% if not role.role_colour|colour_get_inherit %} style="{{ role.role_colour|html_colour({'border-color':'%s'}) }}"{% endif %}>
<div class="listing__entry__content role-listing__entry__content">
{{ role.role_name }}
{{ role.users }} users

View file

@ -5,7 +5,7 @@
<div class="container">
<h1 class="container__title">
{% if edit_role is defined %}
Editing <span style="color:{{ edit_role.role_colour|colour_get_css }}">{{ edit_role.role_name }}</span> ({{ edit_role.role_id }})
Editing <span style="{{ edit_role.role_colour|html_colour }}">{{ edit_role.role_name }}</span> ({{ edit_role.role_id }})
{% else %}
Creating a new Role
{% endif %}

View file

@ -3,7 +3,7 @@
{% block content %}
<div class="container">
<h1 class="container__title">
Viewing <span style="color:{{ view_user.colour|colour_get_css }}">{{ view_user.username }}</span> ({{ view_user.user_id }})
Viewing <span style="{{ view_user.colour|html_colour }}">{{ view_user.username }}</span> ({{ view_user.user_id }})
</h1>
<label class="form__label">

View file

@ -0,0 +1,89 @@
{% extends '@mio/changelog/master.twig' %}
{% from '@mio/macros.twig' import navigation %}
{% set is_valid = change|length > 0 %}
{% set title = 'Changelog » ' ~ (is_valid ? 'Change #' ~ change.change_id : 'Unknown Change') %}
{% if is_valid %}
{% set canonical_url = '/changelog.php?c=' ~ change.change_id %}
{% endif %}
{% block content %}
<div class="container">
<div class="container__title changelog">
{{ title }}
</div>
<div class="container__content changelog__content">
{% if is_valid %}
<div class="changelog__change" id="#c{{ change.change_id }}">
<div class="changelog__change__line"
style="{{ change.action_colour|html_colour({'border-color':'%s'}) }}">
<div class="changelog__change__action {{ change.action_class is defined and change.action_class is not null ? ' changelog__change__action--' ~ change.action_class : '' }} changelog__change__action--{{ change.action_colour|colour_get_css_contrast }}"
style="{{ change.action_colour|html_colour({'background-color':'%s','border-color':'%s'}) }}">
{{ change.action_name }}
</div>
<div class="changelog__change__log">
{{ change.change_log }}
</div>
</div>
<div class="changelog__change__row">
<div class="changelog__change__column changelog__change__column--user-info">
<a class="changelog__change__user" href="/profile.php?u={{ change.user_id }}">
<div class="avatar changelog__change__user__avatar"
style="background-image:url('/profile.php?u={{ change.user_id }}&amp;m=avatar')">
</div>
<div class="changelog__change__user__info">
<div class="changelog__change__user__name">
{{ change.username }}
</div>
<div class="changelog__change__user__title">
{{ change.user_title }}
</div>
</div>
</a>
<a class="changelog__change__date"
href="/changelog.php?d={{ change.change_date }}">
<time datetime="{{ change.change_created|date('c') }}"
title="{{ change.change_created|date('r') }}">
{{ change.change_created|time_diff }}
</time>
</a>
</div>
<div class="changelog__change__column changelog__change__column--change-info">
<div class="changelog__change__text">
{% if change.change_text|length >= 1 %}
{{ change.change_text|md|raw }}
{% else %}
<p>This change has no additional notes.</p>
{% endif %}
</div>
<ul class="changelog__change__tags">
{% for tag in tags %}
<li class="changelog__change__tag"
title="{{ tag.tag_description }}">
<a href="/changelog.php?t={{ tag.tag_id }}" class="changelog__change__tag__link">
{{ tag.tag_name }}
</a>
</li>
{% endfor %}
</ul>
</div>
</div>
</div>
{% else %}
<div class="changelog__none">
The requested change could not be found.
</div>
{% endif %}
</div>
</div>
{{ navigation(mio_navigation) }}
{% endblock %}

View file

@ -0,0 +1,23 @@
{% extends '@mio/changelog/master.twig' %}
{% from '@mio/macros.twig' import navigation, pagination %}
{% from '@mio/changelog/macros.twig' import changelog_listing %}
{% set is_valid = changes|length > 0 %}
{% set title = 'Changelog » ' ~ (is_valid ? changes[0].change_date : 'Unknown date') %}
{% if is_valid %}
{% set canonical_url = '/changelog.php?d=' ~ changes[0].change_date %}
{% endif %}
{% block content %}
<div class="container">
<div class="container__title">
{{ title }}
</div>
<div class="container__content changelog__content">
{{ changelog_listing(changes, true) }}
</div>
</div>
{{ navigation(mio_navigation) }}
{% endblock %}

View file

@ -0,0 +1,20 @@
{% extends '@mio/changelog/master.twig' %}
{% from '@mio/macros.twig' import navigation, pagination %}
{% from '@mio/changelog/macros.twig' import changelog_listing %}
{% set title = 'Changelog' %}
{% set canonical_url = '/changelog.php' %}
{% block content %}
<div class="container">
<div class="container__title">
Changelog
</div>
<div class="container__content changelog__content">
{{ changelog_listing(changes) }}
{{ pagination(changelog_count, changelog_take, changelog_offset, '/changelog.php') }}
</div>
</div>
{{ navigation(mio_navigation) }}
{% endblock %}

View file

@ -0,0 +1,49 @@
{% macro changelog_listing(changes, hide_dates) %}
{% set hide_dates = hide_dates ? true : false %}
{% set last_date = '' %}
{% from _self import changelog_entry %}
{% if changes|length > 0 %}
{% for change in changes %}
{% if not hide_dates and last_date != change.change_date %}
{% set last_date = change.change_date %}
<a class="changelog__date" href="?d={{ last_date }}">
{{ last_date }}
</a>
{% endif %}
{{ changelog_entry(change) }}
{% endfor %}
{% else %}
<div class="changelog__none">
There are no changes to display here.
</div>
{% endif %}
{% endmacro %}
{% macro changelog_entry(change) %}
{% set has_text = change.change_has_text|default(false)
or (change.change_text is defined and change.change_text|length > 0)
%}
<div class="changelog" id="cl{{ change.change_id }}">
<a class="changelog__action{{ change.action_class is defined and change.action_class is not null ? ' changelog__action--' ~ change.action_class : '' }} changelog__action--{{ change.action_colour|colour_get_css_contrast }}"
href="?c={{ change.change_id }}"
{% if change.action_colour is defined %}style="{{ change.action_colour|html_colour({'background-color':'%s'}) }}"{% endif %}>
{{ change.action_name|default('Unknown') }}
</a>
<a class="changelog__user"
href="/profile.php?u={{ change.user_id }}"
style="{{ change.user_colour|html_colour }}">
{{ change.username }}
</a>
<a class="changelog__log{% if has_text %} changelog__log--link{% endif %}"
{% if has_text %}href="?c={{ change.change_id }}"{% endif %}>
{{ change.change_log }}
</a>
</div>
{% endmacro %}

View file

@ -0,0 +1 @@
{% extends '@mio/master.twig' %}

View file

@ -98,7 +98,7 @@
{% if forum.recent_post_user_id is not null %}
by <a
href="/profile.php?u={{ forum.recent_post_user_id }}"
style="color:{{ forum.recent_post_user_colour|colour_get_css }}"
style="{{ forum.recent_post_user_colour|html_colour }}"
class="forum__listing__entry__activity__user">{{ forum.recent_post_username }}</a>,
{% endif %}
<time
@ -199,7 +199,7 @@
by <a
href="/profile.php?u={{ topic.author_id }}"
class="forum__topics__entry__info__author__name"
style="color:{{ topic.author_colour|colour_get_css }}">{{ topic.author_name }}</a>,
style="{{ topic.author_colour|html_colour }}">{{ topic.author_name }}</a>,
{% endif %}
<time datetime="{{ topic.topic_created|date('c') }}" title="{{ topic.topic_created|date('r') }}">{{ topic.topic_created|time_diff }}</time>
@ -226,7 +226,7 @@
</div>
{% if topic.respondent_id is not null %}
<div class="forum__topics__entry__activity__author">
by <a href="/profile.php?u={{ topic.respondent_id }}" class="forum__topics__entry__activity__author__name" style="color:{{ topic.respondent_colour|colour_get_css }}">
by <a href="/profile.php?u={{ topic.respondent_id }}" class="forum__topics__entry__activity__author__name" style="{{ topic.respondent_colour|html_colour }}">
{{ topic.respondent_name }}
</a>
</div>
@ -266,7 +266,7 @@
</div>
<div
class="forum__post__author__username"
style="color:{{ post.poster_colour|colour_get_css }}">{{ post.poster_name }}</div>
style="{{ post.poster_colour|html_colour }}">{{ post.poster_name }}</div>
</a>
<div class="forum__post__author__joined">
joined <time datetime="{{ post.poster_joined|date('c') }}" title="{{ post.poster_joined|date('r') }}">{{ post.poster_joined|time_diff }}</time>

View file

@ -1,5 +1,5 @@
{% extends '@mio/home/master.twig' %}
{% from '@mio/macros.twig' import navigation, link %}
{% from '@mio/macros.twig' import navigation %}
{% from '@mio/news/macros.twig' import news_preview %}
{% set canonical_url = '/' %}
@ -26,5 +26,20 @@
</div>
{{ navigation(mio_navigation, '/') }}
{% if embed_linked_data is defined and embed_linked_data %}
<script type="application/ld+json">
{
"@context": "http://schema.org",
"@type": "Organization",
"name": "{{ embed_name }}",
"url": "{{ embed_url }}",
"logo": "{{ embed_logo }}",
"sameAs": [
"{{ embed_same_as|join('", "')|raw }}"
]
}
</script>
{% endif %}
{% endblock %}

View file

@ -1,9 +1,3 @@
{% macro link(url, content, class) %}
{% spaceless %}
<a href="{{ url }}" {% if '://' in url %} target="_blank" rel="noreferrer noopener"{% endif %} {% if class is defined %}class="{{ class }}"{% endif %}>{{ content|raw }}</a>
{% endspaceless %}
{% endmacro %}
{% macro navigation(links, current, top, fmt, align) %}
{% set top = top|default(false) == true %}
{% set align = align|default('centre') %}

View file

@ -1,4 +1,4 @@
{% from '@mio/macros.twig' import link, navigation %}
{% from '@mio/macros.twig' import navigation %}
{% set mio_navigation = {
'Home': '/',
@ -65,17 +65,23 @@
<footer class="footer">
<div class="footer__copyright">
{{ link('https://flash.moe', 'flash.moe', 'footer__copyright__link') }} 2013-{{
{{ 'https://flash.moe'|html_link('Flashwave', 'footer__copyright__link')|raw }} 2013-{{
''|date('Y') }} /
{{ link('https://github.com/flashwave/misuzu/tree/' ~ git_branch(), git_branch(), 'footer__copyright__link') }} # {{ link('https://github.com/flashwave/misuzu/commit/' ~ git_hash(true), git_hash(), 'footer__copyright__link') }}
<span title="{{ git_hash(true) }}">
{{ git_branch() }} # {{ git_hash() }}
</span>
{# link('https://github.com/flashwave/misuzu/tree/' ~ git_branch(), git_branch(), 'footer__copyright__link') }} # {{ link('https://github.com/flashwave/misuzu/commit/' ~ git_hash(true), git_hash(), 'footer__copyright__link') #}
</div>
<div class="footer__links">
{{ link('#', 'Terms of Service', 'footer__links__link') }} |
{{ link('#', 'Rules', 'footer__links__link') }} |
{{ link('#', 'Contact', 'footer__links__link') }} |
{{ link('#', 'Status', 'footer__links__link') }} |
{{ link('https://twitter.com/flashiinet', 'Twitter', 'footer__links__link') }}
{% autoescape false %}
{{ '#'|html_link('Terms of Service', 'footer__links__link') }} |
{{ '#'|html_link('Rules', 'footer__links__link') }} |
{{ '#'|html_link('Contact', 'footer__links__link') }} |
{{ '#'|html_link('Status', 'footer__links__link') }} |
{{ '/changelog.php'|html_link('Changelog', 'footer__links__link') }} |
{{ 'https://twitter.com/flashiinet'|html_link('Twitter', 'footer__links__link') }}
{% endautoescape %}
</div>
</footer>
</div>

View file

@ -13,7 +13,7 @@
{{ post.created_at|time_diff }}
</time>
<a class="news__preview__user" href="/profile.php?u={{ post.user_id }}">
<div class="news__preview__user__name" style="color:{{ post.display_colour|colour_get_css }}">
<div class="news__preview__user__name" style="{{ post.user_colour|html_colour }}">
{{ post.username }}
</div>
<div class="avatar news__preview__user__avatar" style="background-image:url('/profile.php?u={{ post.user_id }}&amp;m=avatar')"></div>

View file

@ -17,7 +17,7 @@
<div class="news__sidebar news__post__details">
<a class="news__post__detail news__post__user" href="/profile.php?u={{ post.user_id }}">
<div class="news__post__username" style="color:{{ post.display_colour|colour_get_css }}">{{ post.username }}</div>
<div class="news__post__username" style="{{ post.user_colour|html_colour }}">{{ post.username }}</div>
<div class="avatar news__post__avatar" style="background-image:url('/profile.php?u={{ post.user_id }}&amp;m=avatar');"></div>
</a>

View file

@ -9,11 +9,11 @@
{% block content %}
<form onchange="this.submit()">
<select class="input__select" name="r"
style="color:{{ role.role_colour|colour_get_css }}">
style="{{ role.role_colour|html_colour }}">
{% for r in roles %}
<option
value="{{ r.role_id }}"
style="color:{{ r.role_colour|colour_get_css }}"
style="{{ r.role_colour|html_colour }}"
{% if r.role_id == role.role_id %} selected{% endif %}>
{{ r.role_name }}
</option>
@ -58,7 +58,7 @@
<div class="members__user__info">
<div
class="members__user__name"
style="color:{{ user.display_colour|colour_get_css }}">
style="{{ user.user_colour|html_colour }}">
{{ user.username }}
</div>

View file

@ -1,5 +1,5 @@
{% extends '@mio/user/master.twig' %}
{% from '@mio/macros.twig' import navigation, link %}
{% from '@mio/macros.twig' import navigation %}
{% set image = '/profile.php?u=' ~ profile.user_id ~ '&m=avatar' %}
{% set canonical_url = '/profile.php?u=' ~ profile.user_id %}
@ -9,56 +9,57 @@
{% set youtube_is_channel_id = profile.user_youtube|slice(0, 2) == 'UC' and profile.user_youtube|length == 24 %}
{% set profile_fields = {
"twitter": {
"title": "Twitter",
"value": profile.user_twitter|escape,
"link": "https://twitter.com/%s",
"format": "@%s",
'twitter': {
'title': 'Twitter',
'value': profile.user_twitter|escape,
'link': 'https://twitter.com/%s',
'format': '@%s',
},
"osu": {
"title": "osu!",
"value": profile.user_osu|escape,
"link": "https://osu.ppy.sh/users/%s",
'osu': {
'title': 'osu!',
'value': profile.user_osu|escape,
'link': 'https://osu.ppy.sh/users/%s',
},
"website": {
"title": "Website",
"value": profile.user_website|escape,
"link": "%s",
'website': {
'title': 'Website',
'value': profile.user_website|escape,
'link': '%s',
'tooltip': '%s',
},
"youtube": {
"title": "Youtube",
"value": profile.user_youtube|escape,
"link": "https://youtube.com/" ~ (youtube_is_channel_id ? "channel/" : '') ~ "%s",
"format": youtube_is_channel_id ? "Channel" : "%s",
'youtube': {
'title': 'Youtube',
'value': profile.user_youtube|escape,
'link': 'https://youtube.com/' ~ (youtube_is_channel_id ? 'channel/' : '') ~ '%s',
'format': youtube_is_channel_id ? 'Channel' : '%s',
},
"steam": {
"title": "Steam",
"value": profile.user_steam|escape,
"link": "https://steamcommunity.com/id/%s",
'steam': {
'title': 'Steam',
'value': profile.user_steam|escape,
'link': 'https://steamcommunity.com/id/%s',
},
"twitchtv": {
"title": "Twitch.tv",
"value": profile.user_twitchtv|escape,
"link": "https://twitch.tv/%s",
'twitchtv': {
'title': 'Twitch.tv',
'value': profile.user_twitchtv|escape,
'link': 'https://twitch.tv/%s',
},
"lastfm": {
"title": "Last.fm",
"value": profile.user_lastfm|escape,
"link": "http://last.fm/user/%s",
'lastfm': {
'title': 'Last.fm',
'value': profile.user_lastfm|escape,
'link': 'http://last.fm/user/%s',
},
"github": {
"title": "Github",
"value": profile.user_github|escape,
"link": "https://github.com/%s",
'github': {
'title': 'Github',
'value': profile.user_github|escape,
'link': 'https://github.com/%s',
},
"skype": {
"title": "Skype",
"value": profile.user_skype|escape,
"link": "skype:%s?userinfo",
'skype': {
'title': 'Skype',
'value': profile.user_skype|escape,
'link': 'skype:%s?userinfo',
},
"discord": {
"title": "Discord",
"value": profile.user_discord|escape,
'discord': {
'title': 'Discord',
'value': profile.user_discord|escape,
},
} %}
@ -74,7 +75,7 @@
<div class="profile__info__block">
{% if profile.user_title is not empty %}
<div class="profile__info__row">
<div class="profile__info__column profile__info__column--user-title" style="color:{{ profile.display_colour|colour_get_css }}">
<div class="profile__info__column profile__info__column--user-title" style="{{ profile.user_colour|html_colour }}">
{{ profile.user_title }}
</div>
</div>
@ -82,7 +83,7 @@
<div class="profile__info__row">
<div class="profile__info__column profile__info__column--icons">
<img class="profile__icon" src="https://static.flash.moe/flags/fff/{{ profile.user_country|lower }}.png" alt="{{ profile.user_country }}">
<div class="flag flag--{{ profile.user_country|lower }} profile__icon">{{ profile.user_country }}</div>
</div>
<div class="profile__info__column profile__info__column--country">
{{ profile.user_country|country_name }}
@ -118,7 +119,7 @@
Topics
</div>
<div class="profile__info__column profile__info__column--numeric">
{{ profile.forum_topic_count }}
{{ profile.forum_topic_count|number_format }}
</div>
</div>
@ -127,26 +128,41 @@
Posts
</div>
<div class="profile__info__column profile__info__column--numeric">
{{ profile.forum_post_count }}
{{ profile.forum_post_count|number_format }}
</div>
</div>
</div>
{% if profile.changelog_count > 0 %}
<div class="profile__info__block">
<div class="profile__info__row">
<div class="profile__info__column profile__info__column--heading">
Changes
</div>
<div class="profile__info__column profile__info__column--numeric">
{{ profile.changelog_count|number_format }}
</div>
</div>
</div>
{% endif %}
</div>
{% if app.hasActiveSession %}
{% spaceless %}
<div class="profile__info__section">
<div class="profile__info__block profile__info__block--links">
{% autoescape false %}
{% for name, data in profile_fields %}
{% if (data.display is defined ? data.display : true) and data.value|length > 0 %}
<div class="profile__info__row profile__info__row--field-{{ name }}">
<div class="profile__info__column profile__info__column--heading">
{{ data.title }}
</div>
<div class="profile__info__column">
<div class="profile__info__column"
{% if data.tooltip is defined %}title="{{ data.tooltip|format(data.value)|raw }}"{% endif %}>
{% set profile_field_value = (data.format is defined ? data.format : '%s')|format(data.value) %}
{% if data.link is defined %}
{{ link(data.link|format(data.value), profile_field_value, 'profile__account-link') }}
{{ data.link|format(data.value)|html_link(profile_field_value, 'profile__account-link') }}
{% else %}
{{ profile_field_value }}
{% endif %}
@ -154,6 +170,7 @@
</div>
{% endif %}
{% endfor %}
{% endautoescape %}
</div>
</div>
{% endspaceless %}