Add avatar uploading.

This commit is contained in:
flash 2018-03-24 05:31:42 +01:00
parent a23b2729cc
commit 073389c964
21 changed files with 450 additions and 74 deletions

View file

@ -0,0 +1,7 @@
.mio__input__upload {
display: none;
&__label {
.mio__input__button();
}
}

View file

@ -0,0 +1,20 @@
.mio__settings__avatar {
display: flex;
min-height: 200px;
justify-content: space-between;
&__form {
display: block;
margin-bottom: 2px;
}
&__forms {
text-align: center;
flex-grow: 1;
}
&__preview {
flex-grow: 1;
flex-shrink: 0;
}
}

View file

@ -2,4 +2,8 @@
&--account {
margin: 1px;
}
&--avatar {
margin: 2px;
}
}

View file

@ -36,6 +36,7 @@ body {
@import "classes/input/button";
@import "classes/input/text";
@import "classes/input/textarea";
@import "classes/input/upload";
// Base styles
@import "classes/avatar";
@ -52,6 +53,7 @@ body {
@import "classes/settings/content";
@import "classes/settings/errors";
@import "classes/settings/account";
@import "classes/settings/avatar";
// Forums
@import "classes/forum/listing";

View file

@ -9,7 +9,9 @@
"require": {
"php": ">=7.2",
"ext-bcmath": "*",
"ext-imagick": "*",
"ext-mbstring": "*",
"ext-redis": "*",
"twig/twig": "~2.4",
"nesbot/carbon": "~1.22",
"illuminate/database": "~5.5",

62
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"content-hash": "bfc5b8cbdbf22514c4b51ae1af8c333b",
"content-hash": "2a199e9d03d4a7caedecbe72d7ea6b35",
"packages": [
{
"name": "composer/ca-bundle",
@ -696,7 +696,7 @@
},
{
"name": "illuminate/container",
"version": "v5.6.11",
"version": "v5.6.12",
"source": {
"type": "git",
"url": "https://github.com/illuminate/container.git",
@ -740,7 +740,7 @@
},
{
"name": "illuminate/contracts",
"version": "v5.6.11",
"version": "v5.6.12",
"source": {
"type": "git",
"url": "https://github.com/illuminate/contracts.git",
@ -784,16 +784,16 @@
},
{
"name": "illuminate/database",
"version": "v5.6.11",
"version": "v5.6.12",
"source": {
"type": "git",
"url": "https://github.com/illuminate/database.git",
"reference": "4d2fc3c816ed402fcac290e6ca7bc855d5313000"
"reference": "104cd99c17d46e6f96eafd4f1469ea921a289279"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/illuminate/database/zipball/4d2fc3c816ed402fcac290e6ca7bc855d5313000",
"reference": "4d2fc3c816ed402fcac290e6ca7bc855d5313000",
"url": "https://api.github.com/repos/illuminate/database/zipball/104cd99c17d46e6f96eafd4f1469ea921a289279",
"reference": "104cd99c17d46e6f96eafd4f1469ea921a289279",
"shasum": ""
},
"require": {
@ -839,11 +839,11 @@
"orm",
"sql"
],
"time": "2018-03-09T13:55:05+00:00"
"time": "2018-03-14T12:21:13+00:00"
},
{
"name": "illuminate/filesystem",
"version": "v5.6.11",
"version": "v5.6.12",
"source": {
"type": "git",
"url": "https://github.com/illuminate/filesystem.git",
@ -894,7 +894,7 @@
},
{
"name": "illuminate/pagination",
"version": "v5.6.11",
"version": "v5.6.12",
"source": {
"type": "git",
"url": "https://github.com/illuminate/pagination.git",
@ -938,30 +938,30 @@
},
{
"name": "illuminate/support",
"version": "v5.6.11",
"version": "v5.6.12",
"source": {
"type": "git",
"url": "https://github.com/illuminate/support.git",
"reference": "259f6f17a11b0379340ec5311fcba27bc2a04070"
"reference": "f0776f5bbfeeb9d4c4cac8f64d96f8f0cbe4f3f7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/illuminate/support/zipball/259f6f17a11b0379340ec5311fcba27bc2a04070",
"reference": "259f6f17a11b0379340ec5311fcba27bc2a04070",
"url": "https://api.github.com/repos/illuminate/support/zipball/f0776f5bbfeeb9d4c4cac8f64d96f8f0cbe4f3f7",
"reference": "f0776f5bbfeeb9d4c4cac8f64d96f8f0cbe4f3f7",
"shasum": ""
},
"require": {
"doctrine/inflector": "~1.1",
"ext-mbstring": "*",
"illuminate/contracts": "5.6.*",
"nesbot/carbon": "^1.20",
"nesbot/carbon": "^1.24.1",
"php": "^7.1.3"
},
"conflict": {
"tightenco/collect": "<5.5.33"
},
"suggest": {
"illuminate/filesystem": "Required to use the composer class (5.2.*).",
"illuminate/filesystem": "Required to use the composer class (5.6.*).",
"symfony/process": "Required to use the composer class (~4.0).",
"symfony/var-dumper": "Required to use the dd function (~4.0)."
},
@ -991,7 +991,7 @@
],
"description": "The Illuminate Support package.",
"homepage": "https://laravel.com",
"time": "2018-03-09T16:52:54+00:00"
"time": "2018-03-14T12:56:14+00:00"
},
{
"name": "maxmind-db/reader",
@ -1097,16 +1097,16 @@
},
{
"name": "nesbot/carbon",
"version": "1.24.2",
"version": "1.25.0",
"source": {
"type": "git",
"url": "https://github.com/briannesbitt/Carbon.git",
"reference": "bba6c6e410c6b4317e37a9474aeaa753808c3875"
"reference": "cbcf13da0b531767e39eb86e9687f5deba9857b4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/bba6c6e410c6b4317e37a9474aeaa753808c3875",
"reference": "bba6c6e410c6b4317e37a9474aeaa753808c3875",
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/cbcf13da0b531767e39eb86e9687f5deba9857b4",
"reference": "cbcf13da0b531767e39eb86e9687f5deba9857b4",
"shasum": ""
},
"require": {
@ -1146,7 +1146,7 @@
"datetime",
"time"
],
"time": "2018-03-10T10:10:14+00:00"
"time": "2018-03-19T15:50:49+00:00"
},
{
"name": "psr/container",
@ -1478,16 +1478,16 @@
},
{
"name": "twig/twig",
"version": "v2.4.6",
"version": "v2.4.7",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "d2117ec118c1ff3d28ccddca8212d82787a4809f"
"reference": "69aacd44dbbaa3199d5afb68605c996d577896fc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/d2117ec118c1ff3d28ccddca8212d82787a4809f",
"reference": "d2117ec118c1ff3d28ccddca8212d82787a4809f",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/69aacd44dbbaa3199d5afb68605c996d577896fc",
"reference": "69aacd44dbbaa3199d5afb68605c996d577896fc",
"shasum": ""
},
"require": {
@ -1496,8 +1496,8 @@
},
"require-dev": {
"psr/container": "^1.0",
"symfony/debug": "~2.7",
"symfony/phpunit-bridge": "~3.3@dev"
"symfony/debug": "^2.7",
"symfony/phpunit-bridge": "^3.3"
},
"type": "library",
"extra": {
@ -1540,7 +1540,7 @@
"keywords": [
"templating"
],
"time": "2018-03-03T16:23:01+00:00"
"time": "2018-03-20T04:31:17+00:00"
}
],
"packages-dev": [
@ -3010,7 +3010,9 @@
"platform": {
"php": ">=7.2",
"ext-bcmath": "*",
"ext-mbstring": "*"
"ext-imagick": "*",
"ext-mbstring": "*",
"ext-redis": "*"
},
"platform-dev": []
}

View file

@ -10,11 +10,20 @@ $app = Application::start(
$app->startDatabase();
if (PHP_SAPI !== 'cli') {
$storage_dir = $app->getStoragePath();
if (!$storage_dir->isReadable()
|| !$storage_dir->isWritable()) {
echo 'Cannot access storage directory.';
exit;
}
if (isset($_COOKIE['msz_uid'], $_COOKIE['msz_sid'])) {
$app->startSession((int)$_COOKIE['msz_uid'], $_COOKIE['msz_sid']);
}
//ob_start('ob_gzhandler');
if (!$app->inDebugMode()) {
ob_start('ob_gzhandler');
}
$app->startTemplating();
}

BIN
public/images/no-avatar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

View file

@ -1,16 +1,56 @@
<?php
use Misuzu\IO\File;
use Misuzu\IO\FileStream;
use Misuzu\Users\User;
require_once __DIR__ . '/../misuzu.php';
$user_id = (int)($_GET['u'] ?? 0);
$mode = (string)($_GET['m'] ?? 'view');
$profile_user = User::find($user_id);
try {
$app->templating->vars(['profile' => User::findOrFail($user_id)]);
} catch (Exception $ex) {
http_response_code(404);
echo $app->templating->render('user.notfound');
return;
switch ($mode) {
case 'avatar':
$avatar_filename = $app->getPath(
$app->config->get('Avatar', 'default_path', 'string', 'public/images/no-avatar.png')
);
if ($profile_user !== null) {
$user_avatar = "{$profile_user->user_id}.msz";
$cropped_avatar = $app->getStore('avatars/200x200')->filename($user_avatar);
if (File::exists($cropped_avatar)) {
$avatar_filename = $cropped_avatar;
} else {
$original_avatar = $app->getStore('avatars/original')->filename($user_avatar);
if (File::exists($original_avatar)) {
try {
File::writeAll(
$cropped_avatar,
crop_image_centred_path($original_avatar, 200, 200)->getImagesBlob()
);
$avatar_filename = $cropped_avatar;
} catch (Exception $ex) {
}
}
}
}
header('Content-Type: ' . mime_content_type($avatar_filename));
echo File::readToEnd($avatar_filename);
break;
case 'view':
default:
if ($profile_user === null) {
http_response_code(404);
echo $app->templating->render('user.notfound');
break;
}
$app->templating->var('profile', $profile_user);
echo $app->templating->render('user.view');
break;
}
echo $app->templating->render('user.view');

View file

@ -1,5 +1,6 @@
<?php
use Misuzu\Application;
use Misuzu\IO\File;
use Misuzu\Users\User;
use Misuzu\Users\Session;
@ -12,6 +13,8 @@ if ($settings_session === null) {
return;
}
$csrf_error_str = "Couldn't verify you, please refresh the page and retry.";
$settings_profile_fields = [
'twitter' => [
'name' => 'Twitter',
@ -91,11 +94,17 @@ if (!array_key_exists($settings_mode, $settings_modes)) {
$settings_errors = [];
$avatar_filename = "{$settings_user->user_id}.msz";
$avatar_max_width = $app->config->get('Avatar', 'max_width', 'int', 4000);
$avatar_max_height = $app->config->get('Avatar', 'max_height', 'int', 4000);
$avatar_max_filesize = $app->config->get('Avatar', 'max_filesize', 'int', 1000000);
$avatar_max_filesize_human = byte_symbol($avatar_max_filesize, true);
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
switch ($settings_mode) {
case 'account':
if (!tmp_csrf_verify($_POST['csrf'] ?? '')) {
$settings_errors[] = "Couldn't verify you, please refresh the page and retry.";
$settings_errors[] = $csrf_error_str;
break;
}
@ -193,6 +202,110 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$settings_user->save();
}
break;
case 'avatar':
if (isset($_POST['import'])
&& !File::exists($app->getStore('avatars/original')->filename($avatar_filename))) {
if (!tmp_csrf_verify($_POST['import'])) {
$settings_errors[] = $csrf_error_str;
break;
}
$old_avatar_url = trim(file_get_contents(
"https://secret.flashii.net/avatar-serve.php?id={$settings_user->user_id}&r"
));
if (empty($old_avatar_url)) {
$settings_errors[] = 'No old avatar was found for you.';
break;
}
File::writeAll(
$app->getStore('avatars/original')->filename($avatar_filename),
file_get_contents($old_avatar_url)
);
break;
}
if (isset($_POST['delete'])) {
if (!tmp_csrf_verify($_POST['delete'])) {
$settings_errors[] = $csrf_error_str;
break;
}
File::delete($app->getStore('avatars/original')->filename($avatar_filename));
File::delete($app->getStore('avatars/200x200')->filename($avatar_filename));
break;
}
if (isset($_POST['upload'])) {
if (!tmp_csrf_verify($_POST['upload'])) {
$settings_errors[] = $csrf_error_str;
break;
}
switch ($_FILES['avatar']['error']) {
case UPLOAD_ERR_OK:
break;
case UPLOAD_ERR_PARTIAL:
$settings_errors[] = 'The upload was interrupted, please try again!';
break;
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
$settings_errors[] = "Your avatar is not allowed to be larger in filesize than {$avatar_max_filesize_human}!";
break;
case UPLOAD_ERR_NO_TMP_DIR:
case UPLOAD_ERR_CANT_WRITE:
$settings_errors[] = 'Unable to save your avatar, contact an administator!';
break;
case UPLOAD_ERR_EXTENSION:
default:
$settings_errors[] = 'Something happened?';
break;
}
if (count($settings_errors) > 0) {
break;
}
$upload_path = $_FILES['avatar']['tmp_name'];
$upload_meta = getimagesize($upload_path);
if (!$upload_meta
|| !in_array($upload_meta[2], [IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG], true)
|| $upload_meta[0] < 1
|| $upload_meta[1] < 1) {
$settings_errors[] = 'Please provide a valid image.';
break;
}
if ($upload_meta[0] > $avatar_max_width || $upload_meta[1] > $avatar_max_height) {
$settings_errors[] = "Your avatar can't be larger than {$avatar_max_width}x{$avatar_max_height}, yours was {$upload_meta[0]}x{$upload_meta[1]}";
break;
}
if (filesize($upload_path) > $avatar_max_filesize) {
$settings_errors[] = "Your avatar is not allowed to be larger in filesize than {$avatar_max_filesize_human}!";
break;
}
$avatar_path = $app->getStore('avatars/original')->filename($avatar_filename);
move_uploaded_file($upload_path, $avatar_path);
$crop_path = $app->getStore('avatars/200x200')->filename($avatar_filename);
if (File::exists($crop_path)) {
File::delete($crop_path);
}
break;
}
$settings_errors[] = "You shouldn't have done that.";
break;
}
}
@ -204,6 +317,13 @@ switch ($settings_mode) {
$app->templating->vars(compact('settings_profile_fields'));
break;
case 'avatar':
$app->templating->var(
'can_import_old_avatar',
!File::exists($app->getStore('avatars/original')->filename($avatar_filename))
);
break;
case 'sessions':
$app->templating->var('user_sessions', $settings_user->sessions->reverse());
break;

View file

@ -2,6 +2,7 @@
namespace Misuzu;
use Misuzu\Config\ConfigManager;
use Misuzu\IO\Directory;
use Misuzu\Users\Session;
use UnexpectedValueException;
use InvalidArgumentException;
@ -48,6 +49,48 @@ class Application extends ApplicationBase
ExceptionHandler::unregister();
}
public function inDebugMode(): bool
{
return $this->debugMode;
}
public function getPath(string $path): string
{
if (!starts_with($path, '/')) {
$path = __DIR__ . '/../' . $path;
}
return Directory::fixSlashes(rtrim($path, '/'));
}
public function getStoragePath(string $append = ''): Directory
{
$path = '';
if (starts_with($append, '/')) {
$path = $append;
} else {
$path = $this->config->get('Storage', 'path', 'string', __DIR__ . '/../store');
if (!empty($append)) {
$path .= '/' . $append;
}
}
return Directory::createOrOpen($this->getPath($path));
}
public function getStore(string $purpose): Directory
{
$override_key = "override_{$purpose}";
if ($this->config->contains('Storage', $override_key)) {
return new Directory($this->config->get('Storage', $override_key));
}
return $this->getStoragePath($purpose);
}
public function startSession(int $user_id, string $session_key): void
{
$session = Session::where('session_key', $session_key)

View file

@ -12,7 +12,22 @@ class Directory
* Path to this directory.
* @var string
*/
public $path;
private $path;
public function getPath(): string
{
return $this->path;
}
public function isReadable(): bool
{
return is_readable($this->getPath());
}
public function isWritable(): bool
{
return is_writable($this->getPath());
}
/**
* Fixes the path, sets proper slashes and checks if the directory exists.
@ -21,11 +36,13 @@ class Directory
*/
public function __construct(string $path)
{
$this->path = static::fixSlashes(rtrim($path, '/\\'));
$path = static::fixSlashes(rtrim($path, '/\\'));
if (!static::exists($this->path)) {
if (!static::exists($path)) {
throw new DirectoryDoesNotExistException;
}
$this->path = realpath($path);
}
/**
@ -44,6 +61,11 @@ class Directory
}, glob($this->path . '/' . $pattern));
}
public function filename(string $filename): string
{
return $this->getPath() . '/' . $filename;
}
/**
* Creates a directory if it doesn't already exist.
* @param string $path
@ -52,17 +74,33 @@ class Directory
*/
public static function create(string $path): Directory
{
$path = static::fixSlashes($path);
if (static::exists($path)) {
throw new DirectoryExistsException;
}
mkdir($path);
$split_path = explode('/', $path);
$existing_path = '/';
foreach ($split_path as $path_part) {
$existing_path .= $path_part . '/';
if (!Directory::exists($existing_path)) {
mkdir($existing_path);
}
}
return new static($path);
}
public static function createOrOpen(string $path): Directory
{
if (static::exists($path)) {
return new Directory($path);
} else {
return Directory::create($path);
}
}
/**
* Deletes a directory, recursively if requested. Use $purge with care!
* @param string $path

View file

@ -1,6 +1,8 @@
<?php
namespace Misuzu\IO;
use Exception;
/**
* Static file meta functions.
* @package Misuzu\IO
@ -13,6 +15,27 @@ class File
return new FileStream($filename, FileStream::MODE_READ_WRITE, true);
}
public static function readToEnd(string $filename, bool $lock = false): string
{
$output = '';
try {
$file = new FileStream($filename, FileStream::MODE_READ, $lock);
$output = $file->read($file->getLength());
$file->close();
} catch (Exception $ex) {
}
return $output;
}
public static function writeAll(string $filename, string $data): void
{
$file = new FileStream($filename, FileStream::MODE_TRUNCATE, true);
$file->write($data);
$file->close();
}
/**
* Creates an instance of a temporary file.
* @param string $prefix

View file

@ -124,44 +124,44 @@ class FileStream extends Stream
}
}
protected function getCanRead(): bool
public function getCanRead(): bool
{
return ($this->fileMode & static::MODE_READ) > 0 && is_readable($this->filePath);
}
protected function getCanSeek(): bool
public function getCanSeek(): bool
{
return ($this->fileMode & static::MODE_APPEND_RAW) == 0 && $this->getCanRead();
}
protected function getCanTimeout(): bool
public function getCanTimeout(): bool
{
return false;
}
protected function getCanWrite(): bool
public function getCanWrite(): bool
{
return ($this->fileMode & static::MODE_WRITE) > 0 && is_writable($this->filePath);
}
protected function getLength(): int
public function getLength(): int
{
$this->ensureHandleActive();
return fstat($this->fileHandle)['size'];
}
protected function getPosition(): int
public function getPosition(): int
{
$this->ensureHandleActive();
return ftell($this->fileHandle);
}
protected function getReadTimeout(): int
public function getReadTimeout(): int
{
return -1;
}
protected function getWriteTimeout(): int
public function getWriteTimeout(): int
{
return -1;
}

View file

@ -49,42 +49,42 @@ class NetworkStream extends Stream
}
}
protected function getCanRead(): bool
public function getCanRead(): bool
{
return true;
}
protected function getCanSeek(): bool
public function getCanSeek(): bool
{
return false;
}
protected function getCanTimeout(): bool
public function getCanTimeout(): bool
{
return true;
}
protected function getCanWrite(): bool
public function getCanWrite(): bool
{
return true;
}
protected function getLength(): int
public function getLength(): int
{
return -1;
}
protected function getPosition(): int
public function getPosition(): int
{
return -1;
}
protected function getReadTimeout(): int
public function getReadTimeout(): int
{
return -1;
}
protected function getWriteTimeout(): int
public function getWriteTimeout(): int
{
return -1;
}

View file

@ -20,14 +20,14 @@ abstract class Stream
throw new InvalidArgumentException;
}
abstract protected function getCanRead(): bool;
abstract protected function getCanSeek(): bool;
abstract protected function getCanTimeout(): bool;
abstract protected function getCanWrite(): bool;
abstract protected function getLength(): int;
abstract protected function getPosition(): int;
abstract protected function getReadTimeout(): int;
abstract protected function getWriteTimeout(): int;
abstract public function getCanRead(): bool;
abstract public function getCanSeek(): bool;
abstract public function getCanTimeout(): bool;
abstract public function getCanWrite(): bool;
abstract public function getLength(): int;
abstract public function getPosition(): int;
abstract public function getReadTimeout(): int;
abstract public function getWriteTimeout(): int;
abstract public function flush(): void;
abstract public function close(): void;

View file

@ -150,6 +150,53 @@ function tmp_csrf_token(?\Misuzu\Users\Session $session = null): string
return md5($session->session_key);
}
function crop_image_centred_path(string $filename, int $target_width, int $target_height): \Imagick
{
return crop_image_centred(new \Imagick($filename), $target_width, $target_height);
}
function crop_image_centred(Imagick $image, int $target_width, int $target_height): Imagick
{
$image->setImageFormat($image->getNumberImages() > 1 ? 'gif' : 'png');
$image = $image->coalesceImages();
$width = $image->getImageWidth();
$height = $image->getImageHeight();
if ($width > $height) {
$resize_width = $width * $target_height / $height;
$resize_height = $target_height;
} else {
$resize_width = $target_width;
$resize_height = $height * $target_width / $width;
}
do {
$image->resizeImage(
$resize_width,
$resize_height,
Imagick::FILTER_LANCZOS,
0.9
);
$image->cropImage(
$target_width,
$target_height,
($resize_width - $target_width) / 2,
($resize_height - $target_height) / 2
);
$image->setImagePage(
$target_width,
$target_height,
0,
0
);
} while ($image->nextImage());
return $image->deconstructImages();
}
function is_int_ex($value, int $boundary_low, int $boundary_high): bool
{
return is_int($value) && $value >= $boundary_low && $value <= $boundary_high;

View file

@ -28,7 +28,7 @@
<div class="mio__container mio__header__user">
<div class="mio__container__title">Hey, {{ app.session.user.username }}!</div>
<div class="mio__container__content mio__header__user__content">
<div class="mio__avatar mio__header__user__avatar" style="background-image:url('https://secret.flashii.net/avatar-serve.php?id={{ app.session.user.user_id }}');"></div>
<div class="mio__avatar mio__header__user__avatar" style="background-image:url('/profile.php?u={{ app.session.user.user_id }}&amp;m=avatar');"></div>
<div class="mio__header__user__links__container">
<ul class="mio__header__user__links">
<li class="mio__header__user__option"><a class="mio__header__user__link" href="/profile.php?u={{ app.session.user.user_id }}">Profile</a></li>

View file

@ -1,5 +1,24 @@
{% extends '@mio/settings/master.twig' %}
{% block settings_content %}
<p>Sorry for getting your hopes up with the button, but not yet! Read the front page while logged in.</p>
<div class="mio__settings__avatar">
<div class="mio__settings__avatar__forms">
<form class="mio__settings__avatar__form" method="post" action="?m=avatar" enctype="multipart/form-data">
<label class="mio__input__upload__label">
<input type="file" name="avatar" class="mio__input__upload">
Pick file...
</label>
<button class="mio__input__button" name="upload" value="{{ csrf_token() }}">Upload</button>
</form>
<form class="mio__settings__avatar__form" method="post" action="?m=avatar">
<button class="mio__input__button" name="delete" value="{{ csrf_token() }}">Delete</button>
</form>
{% if can_import_old_avatar %}
<form class="mio__settings__avatar__form" method="post" action="?m=avatar">
<button class="mio__input__button" name="import" value="{{ csrf_token() }}">Import old avatar</button>
</form>
{% endif %}
</div>
<div class="mio__avatar mio__settings__avatar__preview" style="background-image:url('/profile.php?u={{ settings_user.user_id }}&amp;m=avatar')"></div>
</div>
{% endblock %}

View file

@ -115,7 +115,7 @@
</div>
{% endspaceless %}
</div>
<div class="mio__avatar mio__profile__avatar" style="background-image:url('https://secret.flashii.net/avatar-serve.php?id={{ profile.user_id }}');"></div>
<div class="mio__avatar mio__profile__avatar" style="background-image:url('/profile.php?u={{ profile.user_id }}&amp;m=avatar');"></div>
</div>
</div>
</div>

View file

@ -55,7 +55,7 @@
<div class="profile__content">
{% spaceless %}
<div class="profile__container profile__container--left">
<div class="profile__avatar" style="background-image: url('https://secret.flashii.net/avatar-serve.php?id={{ profile.user_id }}');"></div>
<div class="profile__avatar" style="background-image: url('/profile.php?u={{ profile.user_id }}&amp;m=avatar');"></div>
<div class="platform profile__platform profile__hierarchies">
{% for id, data in hierarchies %}
{% if data.display %}