Backlog of things.

This commit is contained in:
flash 2018-04-01 00:28:32 +02:00
parent 5b828279a1
commit c1259ef503
22 changed files with 490 additions and 211 deletions

View file

@ -0,0 +1,20 @@
.button {
display: inline-block;
font: 12px/20px @manage-font;
background-color: #666;
border: 1px solid #555;
border-radius: 2px;
text-decoration: none;
color: #111;
padding: 2px 5px;
cursor: pointer;
transition: background-color .2s;
&:hover {
background-color: #777;
}
&:active {
background-color: #666;
}
}

View file

@ -5,4 +5,16 @@
&--center { &--center {
text-align: center; text-align: center;
} }
&__title {
font-size: 2em;
line-height: 1.7em;
padding: 4px;
}
&__subtitle {
font-size: 1.5em;
line-height: 1.5em;
padding: 4px;
}
} }

View file

@ -0,0 +1,14 @@
.form {
&__label {
display: inline-block;
background-color: #222;
border: 1px solid #333;
border-radius: 2px;
min-width: 250px;
margin: 2px;
padding: 2px;
min-height: 50px;
vertical-align: top;
}
}

View file

@ -0,0 +1,9 @@
.input {
background: #333;
border: 1px solid #444;
border-radius: 2px;
padding: 2px;
color: #fff;
width: 100%;
vertical-align: top;
}

View file

@ -3,15 +3,22 @@
flex-direction: column; flex-direction: column;
&__entry { &__entry {
border-right: 4px solid #a00; border-right: 4px solid #666;
padding-right: 1px; padding-right: 1px;
display: block; display: block;
text-decoration: none;
color: inherit;
&:not(:last-child) { &:not(:last-child) {
margin-bottom: 2px; margin-bottom: 2px;
} }
&__selector {
vertical-align: top;
}
&__content { &__content {
padding: 2px;
width: 100%; width: 100%;
background-color: #333; background-color: #333;
min-height: 50px; min-height: 50px;

View file

@ -4,13 +4,12 @@
justify-content: center; justify-content: center;
&__entry { &__entry {
min-width: 296px;
margin: 2px; margin: 2px;
min-width: 296px;
&__content { &__content {
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
padding: 2px;
} }
} }
} }

View file

@ -1,3 +1,5 @@
@manage-font: 'Open Sans', sans-serif;
* { * {
margin: 0; margin: 0;
padding: 0; padding: 0;
@ -13,13 +15,16 @@ body {
.manage { .manage {
background-color: #222; background-color: #222;
font: 12px/20px 'Open Sans', sans-serif; font: 12px/20px @manage-font;
color: #fff; color: #fff;
} }
@import "classes/button";
@import "classes/container"; @import "classes/container";
@import "classes/footer"; @import "classes/footer";
@import "classes/form";
@import "classes/header"; @import "classes/header";
@import "classes/input";
@import "classes/listing"; @import "classes/listing";
@import "classes/pagination"; @import "classes/pagination";

View file

@ -17,21 +17,29 @@ if (PHP_SAPI !== 'cli') {
exit; exit;
} }
if (isset($_COOKIE['msz_uid'], $_COOKIE['msz_sid'])) {
$app->startSession((int)$_COOKIE['msz_uid'], $_COOKIE['msz_sid']);
}
if (!$app->inDebugMode()) { if (!$app->inDebugMode()) {
ob_start('ob_gzhandler'); ob_start('ob_gzhandler');
} }
if ($app->config->get('Auth', 'lockdown', 'bool', false)) {
http_response_code(503);
$app->startTemplating();
$app->templating->addPath('auth', __DIR__ . '/views/auth');
echo $app->templating->render('lockdown');
exit;
}
if (isset($_COOKIE['msz_uid'], $_COOKIE['msz_sid'])) {
$app->startSession((int)$_COOKIE['msz_uid'], $_COOKIE['msz_sid']);
}
$manage_mode = starts_with($_SERVER['REQUEST_URI'], '/manage'); $manage_mode = starts_with($_SERVER['REQUEST_URI'], '/manage');
$app->startTemplating(); $app->startTemplating();
$app->templating->addPath('mio', __DIR__ . '/views/mio'); $app->templating->addPath('mio', __DIR__ . '/views/mio');
if ($manage_mode) { if ($manage_mode) {
if (Application::getInstance()->getSession() === null) { if (Application::getInstance()->getSession() === null || $_SERVER['HTTP_HOST'] !== 'misuzu.misaka.nl') {
http_response_code(403); http_response_code(403);
echo $app->templating->render('errors.403'); echo $app->templating->render('errors.403');
exit; exit;

View file

@ -4,6 +4,7 @@ use Illuminate\Database\Eloquent\ModelNotFoundException;
use Misuzu\Application; use Misuzu\Application;
use Misuzu\Database; use Misuzu\Database;
use Misuzu\Net\IPAddress; use Misuzu\Net\IPAddress;
use Misuzu\Users\Role;
use Misuzu\Users\User; use Misuzu\Users\User;
use Misuzu\Users\Session; use Misuzu\Users\Session;
use Misuzu\Users\LoginAttempt; use Misuzu\Users\LoginAttempt;
@ -166,7 +167,8 @@ switch ($mode) {
break; break;
} }
User::createUser($username, $password, $email); $user = User::createUser($username, $password, $email);
$user->addRole(Role::find(1), true);
$app->templating->var('auth_register_message', 'Welcome to Flashii! You may now log in.'); $app->templating->var('auth_register_message', 'Welcome to Flashii! You may now log in.');
break; break;
} }

View file

@ -0,0 +1,110 @@
<?php
use Misuzu\Application;
use Misuzu\Colour;
use Misuzu\Users\Role;
require_once __DIR__ . '/../../misuzu.php';
$role_mode = (string)($_GET['m'] ?? 'list');
$role_id = (int)($_GET['i'] ?? 0);
while ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!tmp_csrf_verify($_POST['csrf'] ?? '')) {
echo 'csrf err';
break;
}
if (!in_array($role_mode, ['create', 'edit'], true)) {
echo 'invalid mode';
break;
}
if (!isset($_POST['role'])) {
echo 'no';
break;
}
$role_name = $_POST['role']['name'] ?? '';
$role_name_length = strlen($role_name);
if ($role_name_length < 1 || $role_name_length > 255) {
echo 'invalid name length';
break;
}
$role_secret = !empty($_POST['role']['secret']);
$role_hierarchy = (int)($_POST['role']['hierarchy'] ?? -1);
if ($role_hierarchy < 1 || $role_hierarchy > 100) {
echo 'Invalid hierarchy value.';
break;
}
$role_colour = Colour::none();
$role_colour->setInherit(!empty($_POST['role']['colour']['inherit']));
if (!$role_colour->getInherit()) {
foreach (['red', 'green', 'blue'] as $key) {
$value = (int)($_POST['role']['colour'][$key] ?? -1);
$setter = 'set' . ucfirst($key);
if ($value < 0 || $value > 0xFF) {
echo 'invalid colour value';
break 2;
}
$role_colour->{$setter}($value);
}
}
$role_description = $_POST['role']['description'] ?? '';
if (strlen($role_description) > 1000) {
echo 'description is too long';
break;
}
$edit_role = $role_id < 1 ? new Role : Role::find($role_id);
$edit_role->role_name = $role_name;
$edit_role->role_hierarchy = $role_hierarchy;
$edit_role->role_secret = $role_secret;
$edit_role->role_colour = $role_colour;
$edit_role->role_description = $role_description;
$edit_role->save();
header('Location: ?m=list');
break;
}
switch ($role_mode) {
case 'list':
$users_page = (int)($_GET['p'] ?? 1);
$manage_roles = Role::paginate(32, ['*'], 'p', $users_page);
$app->templating->vars(compact('manage_roles'));
echo $app->templating->render('@manage.users.roles');
break;
case 'edit':
if (!isset($edit_role)) {
if ($role_id < 1) {
echo 'no';
break;
}
$edit_role = Role::find($role_id);
}
if ($edit_role === null) {
echo 'invalid role';
break;
}
$app->templating->vars(compact('edit_role'));
// no break
case 'create':
echo $app->templating->render('@manage.users.roles_create');
break;
}

View file

@ -9,83 +9,71 @@ class Colour
private $rawValue = 0; private $rawValue = 0;
public function __get(string $name) public function getRaw(): int
{ {
switch ($name) { return $this->rawValue;
case 'raw':
return $this->rawValue;
case 'inherit':
return ($this->rawValue & self::INHERIT) > 0;
case 'red':
return $this->rawValue >> 16 & 0xFF;
case 'green':
return $this->rawValue >> 8 & 0xFF;
case 'blue':
return $this->rawValue & 0xFF;
case 'hex':
return dechex_pad($this->red) . dechex_pad($this->green) . dechex_pad($this->blue);
}
return null;
} }
public function __set(string $name, $value): void public function setRaw(int $raw): void
{ {
switch ($name) { $this->rawValue = $raw;
case 'raw': }
if (!is_int32($value) && !is_uint32($value)) {
break;
}
$this->rawValue = $value; public function getInherit(): bool
break; {
return ($this->rawValue & self::INHERIT) > 0;
}
case 'inherit': public function setInherit(bool $state): void
if (!is_bool($value)) { {
break; if ($state) {
} $this->rawValue |= self::INHERIT;
} else {
if ($value) { $this->rawValue &= ~self::INHERIT;
$this->rawValue |= self::INHERIT;
} else {
$this->rawValue &= ~self::INHERIT;
}
break;
case 'red':
if (!is_byte($value)) {
break;
}
$this->rawValue &= ~0xFF0000;
$this->rawValue |= $value << 16;
break;
case 'green':
if (!is_byte($value)) {
break;
}
$this->rawValue &= ~0xFF00;
$this->rawValue |= $value << 8;
break;
case 'blue':
if (!is_byte($value)) {
break;
}
$this->rawValue &= ~0xFF;
$this->rawValue |= $value;
break;
} }
} }
public function getRed(): int
{
return $this->rawValue >> 16 & 0xFF;
}
public function setRed(int $red): void
{
$red = $red & 0xFF;
$this->rawValue &= ~0xFF0000;
$this->rawValue |= $red << 16;
}
public function getGreen(): int
{
return $this->rawValue >> 8 & 0xFF;
}
public function setGreen(int $green): void
{
$green = $green & 0xFF;
$this->rawValue &= ~0xFF00;
$this->rawValue |= $green << 8;
}
public function getBlue(): int
{
return $this->rawValue & 0xFF;
}
public function setBlue(int $blue): void
{
$blue = $blue & 0xFF;
$this->rawValue &= ~0xFF;
$this->rawValue |= $blue;
}
public function getHex(): string
{
return dechex_pad($this->getRed()) . dechex_pad($this->getGreen()) . dechex_pad($this->getBlue());
}
public function __construct(?int $raw) public function __construct(?int $raw)
{ {
$this->rawValue = $raw ?? self::INHERIT; $this->rawValue = $raw ?? self::INHERIT;
@ -125,6 +113,6 @@ class Colour
public function __toString() public function __toString()
{ {
return "#{$this->hex}"; return "#{$this->getHex()}";
} }
} }

View file

@ -14,6 +14,8 @@ class Directory
*/ */
private $path; private $path;
public const SEPARATOR = DIRECTORY_SEPARATOR;
public function getPath(): string public function getPath(): string
{ {
return $this->path; return $this->path;
@ -58,12 +60,12 @@ class Directory
} }
return realpath($path); return realpath($path);
}, glob($this->path . '/' . $pattern)); }, glob($this->path . self::SEPARATOR . $pattern));
} }
public function filename(string $filename): string public function filename(string $filename): string
{ {
return $this->getPath() . '/' . $filename; return $this->getPath() . self::SEPARATOR . $filename;
} }
/** /**
@ -78,11 +80,12 @@ class Directory
throw new DirectoryExistsException; throw new DirectoryExistsException;
} }
$split_path = explode('/', $path); $path = Directory::fixSlashes($path);
$existing_path = '/'; $split_path = explode(self::SEPARATOR, $path);
$existing_path = running_on_windows() ? '' : self::SEPARATOR;
foreach ($split_path as $path_part) { foreach ($split_path as $path_part) {
$existing_path .= $path_part . '/'; $existing_path .= $path_part . self::SEPARATOR;
if (!Directory::exists($existing_path)) { if (!Directory::exists($existing_path)) {
mkdir($existing_path); mkdir($existing_path);
@ -146,8 +149,8 @@ class Directory
* @param string $path * @param string $path
* @return string * @return string
*/ */
public static function fixSlashes(string $path): string public static function fixSlashes(string $path, string $separator = self::SEPARATOR): string
{ {
return str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $path); return str_replace(['/', '\\'], $separator, $path);
} }
} }

View file

@ -46,6 +46,26 @@ class Role extends Model
return $user->hasRole($this); return $user->hasRole($this);
} }
public function getRoleColourAttribute(int $colour): Colour
{
return new Colour($colour);
}
public function setRoleColourAttribute(Colour $colour): void
{
$this->attributes['role_colour'] = $colour->getRaw();
}
public function getRoleDescriptionAttribute(?string $description): string
{
return empty($description) ? '' : $description;
}
public function setRoleDescriptionAttribute(string $description): void
{
$this->attributes['role_description'] = empty($description) ? null : $description;
}
public function users() public function users()
{ {
return $this->hasMany(UserRole::class, 'role_id'); return $this->hasMany(UserRole::class, 'role_id');

View file

@ -2,6 +2,7 @@
namespace Misuzu\Users; namespace Misuzu\Users;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
use Misuzu\Colour;
use Misuzu\Database; use Misuzu\Database;
use Misuzu\Model; use Misuzu\Model;
use Misuzu\Net\IPAddress; use Misuzu\Net\IPAddress;
@ -109,6 +110,12 @@ class User extends Model
return ''; return '';
} }
public function getDisplayColour(): Colour
{
$role = Role::find($this->display_role);
return $role === null ? Colour::none() : $role->role_colour;
}
public function addRole(Role $role, bool $setDisplay = false): void public function addRole(Role $role, bool $setDisplay = false): void
{ {
$relation = new UserRole; $relation = new UserRole;

View file

@ -25,72 +25,72 @@ class ColourTest extends TestCase
{ {
$colour = Colour::none(); $colour = Colour::none();
$this->assertTrue($colour->inherit); $this->assertTrue($colour->getInherit());
$this->assertEquals($colour->raw, 0x40000000); $this->assertEquals($colour->getRaw(), 0x40000000);
$this->assertEquals($colour->red, 0); $this->assertEquals($colour->getRed(), 0);
$this->assertEquals($colour->green, 0); $this->assertEquals($colour->getGreen(), 0);
$this->assertEquals($colour->blue, 0); $this->assertEquals($colour->getBlue(), 0);
$this->assertEquals($colour->hex, '000000'); $this->assertEquals($colour->getHex(), '000000');
} }
public function testNull() public function testNull()
{ {
$colour = new Colour(null); $colour = new Colour(null);
$this->assertTrue($colour->inherit); $this->assertTrue($colour->getInherit());
$this->assertEquals($colour->raw, 0x40000000); $this->assertEquals($colour->getRaw(), 0x40000000);
$this->assertEquals($colour->red, 0); $this->assertEquals($colour->getRed(), 0);
$this->assertEquals($colour->green, 0); $this->assertEquals($colour->getGreen(), 0);
$this->assertEquals($colour->blue, 0); $this->assertEquals($colour->getBlue(), 0);
$this->assertEquals($colour->hex, '000000'); $this->assertEquals($colour->getHex(), '000000');
} }
public function testFromRaw() public function testFromRaw()
{ {
$colour = new Colour(static::RAW_HEX6); $colour = new Colour(static::RAW_HEX6);
$this->assertEquals($colour->hex, static::STR_HEX6); $this->assertEquals($colour->getHex(), static::STR_HEX6);
$this->assertEquals($colour->raw, static::RAW_HEX6); $this->assertEquals($colour->getRaw(), static::RAW_HEX6);
$this->assertEquals($colour->red, static::RED_HEX6); $this->assertEquals($colour->getRed(), static::RED_HEX6);
$this->assertEquals($colour->green, static::GREEN_HEX6); $this->assertEquals($colour->getGreen(), static::GREEN_HEX6);
$this->assertEquals($colour->blue, static::BLUE_HEX6); $this->assertEquals($colour->getBlue(), static::BLUE_HEX6);
$this->assertFalse($colour->inherit); $this->assertFalse($colour->getInherit());
} }
public function testFromRGB() public function testFromRGB()
{ {
$colour = Colour::fromRGB(static::RED_HEX6, static::GREEN_HEX6, static::BLUE_HEX6); $colour = Colour::fromRGB(static::RED_HEX6, static::GREEN_HEX6, static::BLUE_HEX6);
$this->assertEquals($colour->hex, static::STR_HEX6); $this->assertEquals($colour->getHex(), static::STR_HEX6);
$this->assertEquals($colour->raw, static::RAW_HEX6); $this->assertEquals($colour->getRaw(), static::RAW_HEX6);
$this->assertEquals($colour->red, static::RED_HEX6); $this->assertEquals($colour->getRed(), static::RED_HEX6);
$this->assertEquals($colour->green, static::GREEN_HEX6); $this->assertEquals($colour->getGreen(), static::GREEN_HEX6);
$this->assertEquals($colour->blue, static::BLUE_HEX6); $this->assertEquals($colour->getBlue(), static::BLUE_HEX6);
$this->assertFalse($colour->inherit); $this->assertFalse($colour->getInherit());
} }
public function testFromHex() public function testFromHex()
{ {
$colour = Colour::fromHex(static::SSTR_HEX6); $colour = Colour::fromHex(static::SSTR_HEX6);
$this->assertEquals($colour->hex, static::STR_HEX6); $this->assertEquals($colour->getHex(), static::STR_HEX6);
$this->assertEquals($colour->raw, static::RAW_HEX6); $this->assertEquals($colour->getRaw(), static::RAW_HEX6);
$this->assertEquals($colour->red, static::RED_HEX6); $this->assertEquals($colour->getRed(), static::RED_HEX6);
$this->assertEquals($colour->green, static::GREEN_HEX6); $this->assertEquals($colour->getGreen(), static::GREEN_HEX6);
$this->assertEquals($colour->blue, static::BLUE_HEX6); $this->assertEquals($colour->getBlue(), static::BLUE_HEX6);
$this->assertFalse($colour->inherit); $this->assertFalse($colour->getInherit());
} }
public function testFromHex3() public function testFromHex3()
{ {
$colour = Colour::fromHex(static::SSTR_HEX3); $colour = Colour::fromHex(static::SSTR_HEX3);
$this->assertEquals($colour->hex, static::STR_HEX3); $this->assertEquals($colour->getHex(), static::STR_HEX3);
$this->assertEquals($colour->raw, static::RAW_HEX3); $this->assertEquals($colour->getRaw(), static::RAW_HEX3);
$this->assertEquals($colour->red, static::RED_HEX3); $this->assertEquals($colour->getRed(), static::RED_HEX3);
$this->assertEquals($colour->green, static::GREEN_HEX3); $this->assertEquals($colour->getGreen(), static::GREEN_HEX3);
$this->assertEquals($colour->blue, static::BLUE_HEX3); $this->assertEquals($colour->getBlue(), static::BLUE_HEX3);
$this->assertFalse($colour->inherit); $this->assertFalse($colour->getInherit());
} }
/** /**

View file

@ -202,42 +202,7 @@ function create_pagination($paginator)
return \Illuminate\Pagination\UrlWindow::make($paginator); return \Illuminate\Pagination\UrlWindow::make($paginator);
} }
function is_int_ex($value, int $boundary_low, int $boundary_high): bool function running_on_windows(): bool
{ {
return is_int($value) && $value >= $boundary_low && $value <= $boundary_high; return starts_with(strtolower(PHP_OS), 'win');
}
function is_sbyte($value): bool
{
return is_int_ex($value, -0x80, 0x7F);
}
function is_byte($value): bool
{
return is_int_ex($value, 0x0, 0xFF);
}
function is_int16($value): bool
{
return is_int_ex($value, -0x8000, 0x7FFF);
}
function is_uint16($value): bool
{
return is_int_ex($value, 0x0, 0xFFFF);
}
function is_int32($value): bool
{
return is_int_ex($value, -0x80000000, 0x7FFFFFFF);
}
function is_uint32($value): bool
{
return is_int_ex($value, 0x0, 0xFFFFFFFF);
}
function is_int64($value): bool
{
return is_int_ex($value, -0x8000000000000000, 0x7FFFFFFFFFFFFFFF);
} }

9
views/auth/lockdown.twig Normal file
View file

@ -0,0 +1,9 @@
{% extends '@auth/master.twig' %}
{% block content %}
<div class="container logout">
<div class="logout__message">
<p class="logout__paragraph">The site is currently unavailable, try again later.</p>
</div>
</div>
{% endblock %}

View file

@ -14,57 +14,61 @@
{% endfor %} {% endfor %}
{% endmacro %} {% endmacro %}
{% macro paginate(paginator, base_url, className) %} {% macro paginate(paginator, base_url, className, alwaysRender) %}
{% from _self import pagination_segment %} {% set alwaysRender = alwaysRender|default(false) %}
{% set url_window = paginator|create_pagination %} {% if alwaysRender or paginator.hasMorePages %}
{% set separator = '%3F' in base_url|default('')|url_encode ? '&' : '?' %} {% set separator = '%3F' in base_url|default('')|url_encode ? '&' : '?' %}
{% set base_url = base_url ~ separator %} {% set base_url = base_url ~ separator %}
<ul class="pagination{{ className is defined and className|length > 0 ? ' ' ~ className : '' }}"> <ul class="pagination{{ className is defined and className|length > 0 ? ' ' ~ className : '' }}">
{% if paginator.onFirstPage %} {% if paginator.onFirstPage %}
<li class="pagination__option pagination__option--prev"> <li class="pagination__option pagination__option--prev">
<span class="pagination__link pagination__link--prev"> <span class="pagination__link pagination__link--prev">
&laquo; &laquo;
</span> </span>
</li> </li>
{% else %} {% else %}
<li class="pagination__option pagination__option--prev"> <li class="pagination__option pagination__option--prev">
<a href="{{ base_url ~ paginator.previousPageUrl|slice(2) }}" class="pagination__link pagination__link--prev" rel="prev"> <a href="{{ base_url ~ paginator.previousPageUrl|slice(2) }}" class="pagination__link pagination__link--prev" rel="prev">
&laquo; &laquo;
</a> </a>
</li> </li>
{% endif %} {% endif %}
<li class="pagination__separator"></li>
{% if url_window.first is iterable %}
{{ pagination_segment(url_window.first, base_url, paginator.currentPage) }}
<li class="pagination__separator"></li> <li class="pagination__separator"></li>
{% endif %}
{% if url_window.slider is iterable %} {% from _self import pagination_segment %}
{{ pagination_segment(url_window.slider, base_url, paginator.currentPage) }} {% set url_window = paginator|create_pagination %}
<li class="pagination__separator"></li>
{% endif %}
{% if url_window.last is iterable %} {% if url_window.first is iterable %}
{{ pagination_segment(url_window.last, base_url, paginator.currentPage) }} {{ pagination_segment(url_window.first, base_url, paginator.currentPage) }}
<li class="pagination__separator"></li> <li class="pagination__separator"></li>
{% endif %} {% endif %}
{% if paginator.hasMorePages %} {% if url_window.slider is iterable %}
<li class="pagination__option pagination__option--next"> {{ pagination_segment(url_window.slider, base_url, paginator.currentPage) }}
<a href="{{ base_url ~ paginator.nextPageUrl|slice(2) }}" class="pagination__link pagination__link--next" rel="next"> <li class="pagination__separator"></li>
&raquo; {% endif %}
</a>
</li> {% if url_window.last is iterable %}
{% else %} {{ pagination_segment(url_window.last, base_url, paginator.currentPage) }}
<li class="pagination__option pagination__option--next"> <li class="pagination__separator"></li>
<span class="pagination__link pagination__link--next"> {% endif %}
&raquo;
</span> {% if paginator.hasMorePages %}
</li> <li class="pagination__option pagination__option--next">
{% endif %} <a href="{{ base_url ~ paginator.nextPageUrl|slice(2) }}" class="pagination__link pagination__link--next" rel="next">
</ul> &raquo;
</a>
</li>
{% else %}
<li class="pagination__option pagination__option--next">
<span class="pagination__link pagination__link--next">
&raquo;
</span>
</li>
{% endif %}
</ul>
{% endif %}
{% endmacro %} {% endmacro %}

View file

@ -4,12 +4,13 @@
{% block content %} {% block content %}
<div class="container listing user-listing"> <div class="container listing user-listing">
{% for user in manage_users %} {% for user in manage_users %}
<label class="listing__entry user-listing__entry"> <label class="listing__entry user-listing__entry"{% if not user.displayColour.inherit %} style="border-color: {{ user.displayColour }}"{% endif %}>
<div class="listing__entry__content user-listing__entry__content"> <div class="listing__entry__content user-listing__entry__content">
<input type="checkbox"> <input class="listing__entry__selector" type="checkbox">
<a href="/profile.php?u={{ user.user_id }}" class="listing__entry__column user-listing__entry__column user-listing__entry__column--username"> <a href="/profile.php?u={{ user.user_id }}" class="listing__entry__column user-listing__entry__column user-listing__entry__column--username">
{{ user.username }} {{ user.username }}
</a> </a>
<div class="user-listing__avatar" style="background-image:url('/profile.php?u={{ user.user_id }}&amp;m=avatar');"></div>
</div> </div>
</label> </label>
{% endfor %} {% endfor %}

View file

@ -0,0 +1,23 @@
{% extends '@manage/users/master.twig' %}
{% from '@manage/macros.twig' import paginate %}
{% block content %}
<div class="container">
<a href="?m=create" class="button">Create new Role</a>
</div>
<div class="container listing role-listing">
{% for role in manage_roles %}
<a href="?m=edit&amp;i={{ role.role_id }}" class="listing__entry role-listing__entry"{% if not role.role_colour.inherit %} style="border-color: {{ role.role_colour }}"{% endif %}>
<div class="listing__entry__content role-listing__entry__content">
{{ role.role_name }}
{{ role.users.count }} users
</div>
</a>
{% endfor %}
</div>
<div class="container container--center">
{{ paginate(manage_roles) }}
</div>
{% endblock %}

View file

@ -0,0 +1,73 @@
{% extends '@manage/users/master.twig' %}
{% block content %}
<form action="?m={{ edit_role is defined ? 'edit&i=' ~ edit_role.role_id : 'create' }}" method="post">
<div class="container">
<h1 class="container__title container__title">Creating a new Role</h1>
<label class="form__label">
<div class="form__label__text">Role Name</div>
<div class="form__label__input">
<input class="input input--text" type="text" value="{{ edit_role is defined ? edit_role.role_name : '' }}" name="role[name]" maxlength="255">
</div>
</label>
<label class="form__label">
<div class="form__label__text">Hide Rank</div>
<div class="form__label__input">
<input class="input" type="checkbox" name="role[secret]"{% if edit_role is defined and edit_role.role_secret %} checked{% endif %}>
</div>
</label>
<label class="form__label">
<div class="form__label__text">Hierarchy</div>
<div class="form__label__input">
<input class="input input--number" type="number" value="{{ edit_role is defined ? edit_role.role_hierarchy : '1' }}" min="1" max="100" name="role[hierarchy]">
</div>
</label>
<h2 class="container__subtitle">Colour</h2>
<label class="form__label">
<div class="form__label__text">Inherit Colour</div>
<div class="form__label__input">
<input class="input" type="checkbox" name="role[colour][inherit]"{% if edit_role is defined and edit_role.role_colour.inherit %} checked{% endif %}>
</div>
</label>
<label class="form__label">
<div class="form__label__text">Red</div>
<div class="form__label__input">
<input class="input input--number" type="number" value="{{ edit_role is defined ? edit_role.role_colour.red : '0' }}" min="0" max="255" name="role[colour][red]">
</div>
</label>
<label class="form__label">
<div class="form__label__text">Green</div>
<div class="form__label__input">
<input class="input input--number" type="number" value="{{ edit_role is defined ? edit_role.role_colour.green : '0' }}" min="0" max="255" name="role[colour][green]">
</div>
</label>
<label class="form__label">
<div class="form__label__text">Blue</div>
<div class="form__label__input">
<input class="input input--number" type="number" value="{{ edit_role is defined ? edit_role.role_colour.blue : '0' }}" min="0" max="255" name="role[colour][blue]">
</div>
</label>
<h2 class="container__subtitle">Additional</h2>
<label class="form__label">
<div class="form__label__text">Description</div>
<div class="form__label__input">
<textarea class="input input--textarea" name="role[description]" maxlength="1000">{{ edit_role is defined ? edit_role.role_description : '' }}</textarea>
</div>
</label>
<div>
<button class="button" name="csrf" value="{{ csrf_token() }}">{{ edit_role is defined ? 'Update role' : 'Create role' }}</button>
</div>
</div>
</form>
{% endblock %}

View file

@ -64,10 +64,10 @@
{{ link('https://github.com/flashwave/misuzu/tree/' ~ git_branch(), git_branch(), 'mio__footer__copyright__link') }} # {{ link('https://github.com/flashwave/misuzu/commit/' ~ git_hash(true), git_hash(), 'mio__footer__copyright__link') }} {{ link('https://github.com/flashwave/misuzu/tree/' ~ git_branch(), git_branch(), 'mio__footer__copyright__link') }} # {{ link('https://github.com/flashwave/misuzu/commit/' ~ git_hash(true), git_hash(), 'mio__footer__copyright__link') }}
</div> </div>
<div class="mio__footer__links"> <div class="mio__footer__links">
{{ link('#', 'Terms of Service', 'mio__footer__links__link') }} {{ link('#', 'Terms of Service', 'mio__footer__links__link') }} |
{{ link('#', 'Rules', 'mio__footer__links__link') }} {{ link('#', 'Rules', 'mio__footer__links__link') }} |
{{ link('#', 'Contact', 'mio__footer__links__link') }} {{ link('#', 'Contact', 'mio__footer__links__link') }} |
{{ link('#', 'Status', 'mio__footer__links__link') }} {{ link('#', 'Status', 'mio__footer__links__link') }} |
{{ link('https://twitter.com/flashiinet', 'Twitter', 'mio__footer__links__link') }} {{ link('https://twitter.com/flashiinet', 'Twitter', 'mio__footer__links__link') }}
</div> </div>
</footer> </footer>