From c70de412226115bda32ccad901b98b2493522d59 Mon Sep 17 00:00:00 2001
From: flashwave <me@flash.moe>
Date: Fri, 18 Jan 2019 23:24:37 +0100
Subject: [PATCH] Added forum signatures.

---
 assets/less/classes/forum/post.less           |  10 +-
 assets/less/classes/profile/about.less        |   8 +-
 assets/less/classes/profile/signature.less    |  24 ++++
 assets/less/main.less                         |   1 +
 ...019_01_18_202512_add_signature_columns.php |  31 +++++
 public/forum/posting.php                      |   7 +-
 public/profile.php                            |  22 +++-
 src/Forum/forum.php                           |   6 +
 src/Forum/post.php                            |  28 ++--
 src/Users/profile.php                         |   5 +-
 src/Users/user.php                            |  78 +++++++++--
 src/manage.php                                |   5 +
 templates/_layout/header-old.twig             | 124 ------------------
 templates/forum/macros.twig                   |   6 +
 templates/forum/posting.twig                  |  12 +-
 templates/user/profile.twig                   |  31 ++++-
 templates/user/settings.twig                  |   2 +-
 17 files changed, 230 insertions(+), 170 deletions(-)
 create mode 100644 assets/less/classes/profile/signature.less
 create mode 100644 database/2019_01_18_202512_add_signature_columns.php
 delete mode 100644 templates/_layout/header-old.twig

diff --git a/assets/less/classes/forum/post.less b/assets/less/classes/forum/post.less
index b6308947..ee8274a3 100644
--- a/assets/less/classes/forum/post.less
+++ b/assets/less/classes/forum/post.less
@@ -180,17 +180,23 @@
     }
 
     &__options {
-        margin: 2px;
+        margin: 5px;
         display: flex;
         justify-content: space-between;
+        align-items: center;
 
         @media (max-width: @site-mobile-width) {
             flex-direction: column;
         }
     }
 
+    &__settings {
+        display: flex;
+        align-items: center;
+    }
+
     &__dropdown {
-        margin: 0;
+        margin-right: 5px;
     }
 
     &__actions {
diff --git a/assets/less/classes/profile/about.less b/assets/less/classes/profile/about.less
index 33ae7b01..4e5c158d 100644
--- a/assets/less/classes/profile/about.less
+++ b/assets/less/classes/profile/about.less
@@ -4,13 +4,13 @@
         max-height: 1000px;
         overflow: auto;
         padding: 2px 5px;
-
-        &--edit {
-            padding: 5px;
-        }
     }
 
     &__editor {
+        padding: 5px;
+    }
+
+    &__text {
         width: 100%;
         height: 500px;
         max-width: 100%;
diff --git a/assets/less/classes/profile/signature.less b/assets/less/classes/profile/signature.less
new file mode 100644
index 00000000..514886e5
--- /dev/null
+++ b/assets/less/classes/profile/signature.less
@@ -0,0 +1,24 @@
+.profile__signature {
+
+    &__content {
+        max-height: 200px;
+        overflow: auto;
+        padding: 2px 5px;
+    }
+
+    &__editor {
+        padding: 5px;
+    }
+
+    &__text {
+        width: 100%;
+        height: 200px;
+        max-width: 100%;
+        min-width: 100%;
+    }
+
+    &__select {
+        width: 100%;
+        margin-bottom: 5px;
+    }
+}
diff --git a/assets/less/main.less b/assets/less/main.less
index 04e59fd9..6474b473 100644
--- a/assets/less/main.less
+++ b/assets/less/main.less
@@ -140,6 +140,7 @@ html {
 @import "classes/profile/background-settings";
 @import "classes/profile/warning";
 @import "classes/profile/birthdate";
+@import "classes/profile/signature";
 
 // Changelog
 @import "classes/changelog";
diff --git a/database/2019_01_18_202512_add_signature_columns.php b/database/2019_01_18_202512_add_signature_columns.php
new file mode 100644
index 00000000..49a0a0e6
--- /dev/null
+++ b/database/2019_01_18_202512_add_signature_columns.php
@@ -0,0 +1,31 @@
+<?php
+namespace Misuzu\DatabaseMigrations\AddSignatureColumns;
+
+use PDO;
+
+function migrate_up(PDO $conn): void
+{
+    $conn->exec("
+        ALTER TABLE `msz_users`
+            ADD COLUMN `user_signature_content` TEXT NULL AFTER `user_about_parser`,
+            ADD COLUMN `user_signature_parser` TINYINT(4) NOT NULL DEFAULT '0' AFTER `user_signature_content`;
+    ");
+    $conn->exec("
+        ALTER TABLE `msz_forum_posts`
+            ADD COLUMN `post_display_signature` TINYINT(4) UNSIGNED NOT NULL DEFAULT '1' AFTER `post_parse`;
+    ");
+}
+
+function migrate_down(PDO $conn): void
+{
+    $conn->exec("
+        ALTER TABLE `msz_forum_posts`
+            DROP COLUMN `post_display_signature`;
+    ");
+
+    $conn->exec("
+        ALTER TABLE `msz_users`
+            DROP COLUMN `user_signature_content`,
+            DROP COLUMN `user_signature_parser`;
+    ");
+}
diff --git a/public/forum/posting.php b/public/forum/posting.php
index dc9ce039..c30bf097 100644
--- a/public/forum/posting.php
+++ b/public/forum/posting.php
@@ -113,6 +113,7 @@ if (!empty($_POST)) {
     $postText = $_POST['post']['text'] ?? '';
     $postParser = (int)($_POST['post']['parser'] ?? MSZ_PARSER_BBCODE);
     $topicType = isset($_POST['post']['type']) ? (int)$_POST['post']['type'] : null;
+    $postSignature = isset($_POST['post']['signature']);
 
     if (!csrf_verify('forum_post', $_POST['csrf'] ?? '')) {
         $notices[] = 'Could not verify request.';
@@ -182,13 +183,14 @@ if (!empty($_POST)) {
                         user_session_current('user_id', 0),
                         ip_remote_address(),
                         $postText,
-                        $postParser
+                        $postParser,
+                        $postSignature
                     );
                     forum_topic_mark_read(user_session_current('user_id', 0), $topicId, $forum['forum_id']);
                     break;
 
                 case 'edit':
-                    if (!forum_post_update($postId, ip_remote_address(), $postText, $postParser, $postText !== $post['post_text'])) {
+                    if (!forum_post_update($postId, ip_remote_address(), $postText, $postParser, $postSignature, $postText !== $post['post_text'])) {
                         $notices[] = 'Post edit failed.';
                     }
 
@@ -232,5 +234,6 @@ echo tpl_render('forum.posting', [
         'type' => $topicType ?? null,
         'text' => $postText ?? null,
         'parser' => $postParser ?? null,
+        'signature' => $postSignature ?? null,
     ],
 ]);
diff --git a/public/profile.php b/public/profile.php
index 81ced802..3b9ab99c 100644
--- a/public/profile.php
+++ b/public/profile.php
@@ -134,6 +134,7 @@ switch ($mode) {
                 'edit_background' => perms_check($userPerms, MSZ_PERM_USER_CHANGE_BACKGROUND),
                 'edit_about' => perms_check($userPerms, MSZ_PERM_USER_EDIT_ABOUT),
                 'edit_birthdate' => perms_check($userPerms, MSZ_PERM_USER_EDIT_BIRTHDATE),
+                'edit_signature' => perms_check($userPerms, MSZ_PERM_USER_EDIT_SIGNATURE),
             ];
 
             tpl_vars([
@@ -177,7 +178,7 @@ switch ($mode) {
                                 (int)($_POST['about']['parser'] ?? MSZ_PARSER_PLAIN)
                             );
 
-                            if ($setAboutError !== MSZ_USER_ABOUT_OK) {
+                            if ($setAboutError !== MSZ_E_USER_ABOUT_OK) {
                                 $notices[] = sprintf(
                                     MSZ_TMP_USER_ERROR_STRINGS['about'][$setAboutError] ?? MSZ_TMP_USER_ERROR_STRINGS['about']['_'],
                                     MSZ_USER_ABOUT_MAX_LENGTH
@@ -186,6 +187,25 @@ switch ($mode) {
                         }
                     }
 
+                    if (!empty($_POST['signature']) && is_array($_POST['signature'])) {
+                        if (!$perms['edit_signature']) {
+                            $notices[] = MSZ_TMP_USER_ERROR_STRINGS['signature']['not-allowed'];
+                        } else {
+                            $setSignatureError = user_set_signature(
+                                $userId,
+                                $_POST['signature']['text'] ?? '',
+                                (int)($_POST['signature']['parser'] ?? MSZ_PARSER_PLAIN)
+                            );
+
+                            if ($setSignatureError !== MSZ_E_USER_SIGNATURE_OK) {
+                                $notices[] = sprintf(
+                                    MSZ_TMP_USER_ERROR_STRINGS['signature'][$setSignatureError] ?? MSZ_TMP_USER_ERROR_STRINGS['signature']['_'],
+                                    MSZ_USER_SIGNATURE_MAX_LENGTH
+                                );
+                            }
+                        }
+                    }
+
                     if (!empty($_POST['birthdate']) && is_array($_POST['birthdate'])) {
                         if (!$perms['edit_birthdate']) {
                             $notices[] = "You aren't allow to change your birthdate.";
diff --git a/src/Forum/forum.php b/src/Forum/forum.php
index 8c411c49..e263925b 100644
--- a/src/Forum/forum.php
+++ b/src/Forum/forum.php
@@ -1,6 +1,12 @@
 <?php
+/**********************
+ * GLOBAL PERMISSIONS *
+ **********************/
 define('MSZ_PERM_FORUM_MANAGE_FORUMS', 1);
 
+/*************************
+ * PER-FORUM PERMISSIONS *
+ *************************/
 define('MSZ_FORUM_PERM_LIST_FORUM', 1); // can see stats, but will get error when trying to view
 define('MSZ_FORUM_PERM_VIEW_FORUM', 1 << 1);
 
diff --git a/src/Forum/post.php b/src/Forum/post.php
index f860592c..2c15be46 100644
--- a/src/Forum/post.php
+++ b/src/Forum/post.php
@@ -7,13 +7,14 @@ function forum_post_create(
     int $userId,
     string $ipAddress,
     string $text,
-    int $parser = MSZ_PARSER_PLAIN
+    int $parser = MSZ_PARSER_PLAIN,
+    bool $displaySignature = true
 ): int {
     $createPost = db_prepare('
         INSERT INTO `msz_forum_posts`
-            (`topic_id`, `forum_id`, `user_id`, `post_ip`, `post_text`, `post_parse`)
+            (`topic_id`, `forum_id`, `user_id`, `post_ip`, `post_text`, `post_parse`, `post_display_signature`)
         VALUES
-            (:topic_id, :forum_id, :user_id, INET6_ATON(:post_ip), :post_text, :post_parse)
+            (:topic_id, :forum_id, :user_id, INET6_ATON(:post_ip), :post_text, :post_parse, :post_display_signature)
     ');
     $createPost->bindValue('topic_id', $topicId);
     $createPost->bindValue('forum_id', $forumId);
@@ -21,6 +22,7 @@ function forum_post_create(
     $createPost->bindValue('post_ip', $ipAddress);
     $createPost->bindValue('post_text', $text);
     $createPost->bindValue('post_parse', $parser);
+    $createPost->bindValue('post_display_signature', $displaySignature ? 1 : 0);
 
     return $createPost->execute() ? db_last_insert_id() : 0;
 }
@@ -30,6 +32,7 @@ function forum_post_update(
     string $ipAddress,
     string $text,
     int $parser = MSZ_PARSER_PLAIN,
+    bool $displaySignature = true,
     bool $bumpUpdate = true
 ): bool {
     if ($postId < 1) {
@@ -41,6 +44,7 @@ function forum_post_update(
         SET `post_ip` = INET6_ATON(:post_ip),
             `post_text` = :post_text,
             `post_parse` = :post_parse,
+            `post_display_signature` = :post_display_signature,
             `post_edited` = IF(:bump, NOW(), `post_edited`)
         WHERE `post_id` = :post_id
     ');
@@ -48,6 +52,7 @@ function forum_post_update(
     $updatePost->bindValue('post_ip', $ipAddress);
     $updatePost->bindValue('post_text', $text);
     $updatePost->bindValue('post_parse', $parser);
+    $updatePost->bindValue('post_display_signature', $displaySignature ? 1 : 0);
     $updatePost->bindValue('bump', $bumpUpdate ? 1 : 0);
 
     return $updatePost->execute();
@@ -85,13 +90,11 @@ function forum_post_get(int $postId, bool $allowDeleted = false): array
     $getPost = db_prepare(sprintf(
         '
             SELECT
-                p.`post_id`, p.`post_text`, p.`post_created`, p.`post_parse`,
+                p.`post_id`, p.`post_text`, p.`post_created`, p.`post_parse`, p.`post_display_signature`,
                 p.`topic_id`, p.`post_deleted`, p.`post_edited`, p.`topic_id`, p.`forum_id`,
                 INET6_NTOA(p.`post_ip`) AS `post_ip`,
-                u.`user_id` AS `poster_id`,
-                u.`username` AS `poster_name`,
-                u.`user_created` AS `poster_joined`,
-                u.`user_country` AS `poster_country`,
+                u.`user_id` AS `poster_id`, u.`username` AS `poster_name`,
+                u.`user_created` AS `poster_joined`, u.`user_country` AS `poster_country`,
                 COALESCE(u.`user_colour`, r.`role_colour`) AS `poster_colour`,
                 (
                     SELECT COUNT(`post_id`)
@@ -126,12 +129,11 @@ function forum_post_listing(int $topicId, int $offset = 0, int $take = 0, bool $
         '
             SELECT
                 p.`post_id`, p.`post_text`, p.`post_created`, p.`post_parse`,
-                p.`topic_id`, p.`post_deleted`, p.`post_edited`,
+                p.`topic_id`, p.`post_deleted`, p.`post_edited`, p.`post_display_signature`,
                 INET6_NTOA(p.`post_ip`) AS `post_ip`,
-                u.`user_id` AS `poster_id`,
-                u.`username` AS `poster_name`,
-                u.`user_created` AS `poster_joined`,
-                u.`user_country` AS `poster_country`,
+                u.`user_id` AS `poster_id`, u.`username` AS `poster_name`,
+                u.`user_created` AS `poster_joined`, u.`user_country` AS `poster_country`,
+                u.`user_signature_content` AS `poster_signature_content`, u.`user_signature_parser` AS `poster_signature_parser`,
                 COALESCE(u.`user_colour`, r.`role_colour`) AS `poster_colour`,
                 COALESCE(u.`user_title`, r.`role_title`) AS `poster_title`,
                 (
diff --git a/src/Users/profile.php b/src/Users/profile.php
index 928af7ab..a1d0f8e5 100644
--- a/src/Users/profile.php
+++ b/src/Users/profile.php
@@ -211,8 +211,9 @@ function user_profile_get(int $userId): array
             '
                 SELECT
                     u.`user_id`, u.`username`, u.`user_country`, u.`user_birthdate`,
-                    u.`user_created`, u.`user_active`,
-                    u.`user_about_parser`, u.`user_about_content`, u.`user_background_settings`,
+                    u.`user_created`, u.`user_active`, u.`user_background_settings`,
+                    u.`user_about_parser`, u.`user_about_content`,
+                    u.`user_signature_parser`, u.`user_signature_content`,
                     %1$s,
                     COALESCE(u.`user_title`, r.`role_title`) as `user_title`,
                     COALESCE(u.`user_colour`, r.`role_colour`) as `user_colour`,
diff --git a/src/Users/user.php b/src/Users/user.php
index 74bd6735..528a4ffb 100644
--- a/src/Users/user.php
+++ b/src/Users/user.php
@@ -8,6 +8,7 @@ define('MSZ_PERM_USER_CHANGE_AVATAR', 1 << 1);
 define('MSZ_PERM_USER_CHANGE_BACKGROUND', 1 << 2);
 define('MSZ_PERM_USER_EDIT_ABOUT', 1 << 3);
 define('MSZ_PERM_USER_EDIT_BIRTHDATE', 1 << 4);
+define('MSZ_PERM_USER_EDIT_SIGNATURE', 1 << 5);
 
 define('MSZ_PERM_USER_MANAGE_USERS', 1 << 20);
 define('MSZ_PERM_USER_MANAGE_ROLES', 1 << 21);
@@ -297,26 +298,26 @@ function user_get_birthdays(int $day = 0, int $month = 0)
 
 define('MSZ_USER_ABOUT_MAX_LENGTH', 0xFFFF);
 
-define('MSZ_USER_ABOUT_OK', 0);
-define('MSZ_USER_ABOUT_INVALID_USER', 1);
-define('MSZ_USER_ABOUT_INVALID_PARSER', 2);
-define('MSZ_USER_ABOUT_TOO_LONG', 3);
-define('MSZ_USER_ABOUT_UPDATE_FAILED', 4);
+define('MSZ_E_USER_ABOUT_OK', 0);
+define('MSZ_E_USER_ABOUT_INVALID_USER', 1);
+define('MSZ_E_USER_ABOUT_INVALID_PARSER', 2);
+define('MSZ_E_USER_ABOUT_TOO_LONG', 3);
+define('MSZ_E_USER_ABOUT_UPDATE_FAILED', 4);
 
 function user_set_about_page(int $userId, string $content, int $parser = MSZ_PARSER_PLAIN): int
 {
     if ($userId < 1) {
-        return MSZ_USER_ABOUT_INVALID_USER;
+        return MSZ_E_USER_ABOUT_INVALID_USER;
     }
 
     if (!parser_is_valid($parser)) {
-        return MSZ_USER_ABOUT_INVALID_PARSER;
+        return MSZ_E_USER_ABOUT_INVALID_PARSER;
     }
 
     $length = strlen($content);
 
     if ($length > MSZ_USER_ABOUT_MAX_LENGTH) {
-        return MSZ_USER_ABOUT_TOO_LONG;
+        return MSZ_E_USER_ABOUT_TOO_LONG;
     }
 
     $setAbout = db_prepare('
@@ -329,7 +330,48 @@ function user_set_about_page(int $userId, string $content, int $parser = MSZ_PAR
     $setAbout->bindValue('content', $length < 1 ? null : $content);
     $setAbout->bindValue('parser', $parser);
 
-    return $setAbout->execute() ? MSZ_USER_ABOUT_OK : MSZ_USER_ABOUT_UPDATE_FAILED;
+    return $setAbout->execute()
+        ? MSZ_E_USER_ABOUT_OK
+        : MSZ_E_USER_ABOUT_UPDATE_FAILED;
+}
+
+define('MSZ_USER_SIGNATURE_MAX_LENGTH', 2000);
+
+define('MSZ_E_USER_SIGNATURE_OK', 0);
+define('MSZ_E_USER_SIGNATURE_INVALID_USER', 1);
+define('MSZ_E_USER_SIGNATURE_INVALID_PARSER', 2);
+define('MSZ_E_USER_SIGNATURE_TOO_LONG', 3);
+define('MSZ_E_USER_SIGNATURE_UPDATE_FAILED', 4);
+
+function user_set_signature(int $userId, string $content, int $parser = MSZ_PARSER_PLAIN): int
+{
+    if ($userId < 1) {
+        return MSZ_E_USER_SIGNATURE_INVALID_USER;
+    }
+
+    if (!parser_is_valid($parser)) {
+        return MSZ_E_USER_SIGNATURE_INVALID_PARSER;
+    }
+
+    $length = strlen($content);
+
+    if ($length > MSZ_USER_SIGNATURE_MAX_LENGTH) {
+        return MSZ_E_USER_SIGNATURE_TOO_LONG;
+    }
+
+    $setSignature = db_prepare('
+        UPDATE `msz_users`
+        SET `user_signature_content` = :content,
+            `user_signature_parser` = :parser
+        WHERE `user_id` = :user
+    ');
+    $setSignature->bindValue('user', $userId);
+    $setSignature->bindValue('content', $length < 1 ? null : $content);
+    $setSignature->bindValue('parser', $parser);
+
+    return $setSignature->execute()
+        ? MSZ_E_USER_SIGNATURE_OK
+        : MSZ_E_USER_SIGNATURE_UPDATE_FAILED;
 }
 
 // all the way down here bc of defines, this define is temporary
@@ -388,14 +430,22 @@ define('MSZ_TMP_USER_ERROR_STRINGS', [
         'not-allowed' => "You're not allowed to edit your profile.",
         MSZ_USER_PROFILE_INVALID_FIELD => "Field '%1\$s' does not exist!",
         MSZ_USER_PROFILE_FILTER_FAILED => '%2$s field was invalid!',
-        MSZ_USER_PROFILE_UPDATE_FAILED => 'Failed to update values, contact an administator.',
+        MSZ_USER_PROFILE_UPDATE_FAILED => 'Failed to update profile, contact an administator.',
     ],
     'about' => [
         '_' => 'An unexpected error occurred, contact an administator.',
         'not-allowed' => "You're not allowed to edit your about page.",
-        MSZ_USER_ABOUT_INVALID_USER => 'The requested user does not exist.',
-        MSZ_USER_ABOUT_INVALID_PARSER => 'The selected parser is invalid.',
-        MSZ_USER_ABOUT_TOO_LONG => 'Please keep the length of your about section below %1$d characters.',
-        MSZ_USER_ABOUT_UPDATE_FAILED => 'Failed to update values, contact an administator.',
+        MSZ_E_USER_ABOUT_INVALID_USER => 'The requested user does not exist.',
+        MSZ_E_USER_ABOUT_INVALID_PARSER => 'The selected parser is invalid.',
+        MSZ_E_USER_ABOUT_TOO_LONG => 'Please keep the length of your about section below %1$d characters.',
+        MSZ_E_USER_ABOUT_UPDATE_FAILED => 'Failed to update about section, contact an administator.',
+    ],
+    'signature' => [
+        '_' => 'An unexpected error occurred, contact an administator.',
+        'not-allowed' => "You're not allowed to edit your about page.",
+        MSZ_E_USER_SIGNATURE_INVALID_USER => 'The requested user does not exist.',
+        MSZ_E_USER_SIGNATURE_INVALID_PARSER => 'The selected parser is invalid.',
+        MSZ_E_USER_SIGNATURE_TOO_LONG => 'Please keep the length of your signature below %1$d characters.',
+        MSZ_E_USER_SIGNATURE_UPDATE_FAILED => 'Failed to update signature, contact an administator.',
     ],
 ]);
diff --git a/src/manage.php b/src/manage.php
index bc696917..8a59c3f9 100644
--- a/src/manage.php
+++ b/src/manage.php
@@ -225,6 +225,11 @@ function manage_perms_list(array $rawPerms): array
                     'title' => 'Can change own birthdate.',
                     'perm' => MSZ_PERM_USER_EDIT_BIRTHDATE,
                 ],
+                [
+                    'section' => 'edit-signature',
+                    'title' => 'Can change own signature.',
+                    'perm' => MSZ_PERM_USER_EDIT_SIGNATURE,
+                ],
                 [
                     'section' => 'manage-users',
                     'title' => 'Can manage other users.',
diff --git a/templates/_layout/header-old.twig b/templates/_layout/header-old.twig
deleted file mode 100644
index 081cc7cf..00000000
--- a/templates/_layout/header-old.twig
+++ /dev/null
@@ -1,124 +0,0 @@
-{% from '_layout/input.twig' import input_checkbox_raw %}
-{% set in_manage = manage_menu is defined %}
-
-{% set site_menu = [
-    {
-        'title': 'Home',
-        'url': '/',
-        'menu': [
-            {
-                'title': 'Members',
-                'url': '/members.php',
-            },
-            {
-                'title': 'Changelog',
-                'url': '/changelog.php',
-            },
-            {
-                'title': 'Contact',
-                'url': '/info.php/contact',
-            },
-            {
-                'title': 'Rules',
-                'url': '/info.php/rules',
-            },
-            {
-                'title': 'Twitter',
-                'url': 'https://twitter.com/flashiinet',
-            },
-        ],
-    },
-    {
-        'title': 'News',
-        'url': '/news.php',
-    },
-    {
-        'title': 'Forum',
-        'url': '/forum',
-    },
-    {
-        'title': 'Chat',
-        'url': 'https://chat.flashii.net',
-    },
-] %}
-
-<nav class="headerv1">
-    <div class="headerv1__background"></div>
-    <div class="headerv1__wrapper">
-        <div class="headerv1__icons">
-            <label class="headerv1__icon headerv1__icon--menu" for="toggle-mobile-header-menu">
-                <i class="fas fa-bars"></i>
-            </label>
-
-            <a class="headerv1__logo{% if in_manage %} headerv1__logo--manage{% endif %}" href="/">
-                {{ globals.site_name }}
-            </a>
-
-            <label style="background-image:url('/profile.php?u={{ current_user.user_id|default(0) }}&amp;m=avatar');"
-                class="avatar headerv1__icon headerv1__icon--user"
-                for="toggle-mobile-header-user"></label>
-        </div>
-
-        {{ input_checkbox_raw('', false, 'headerv1__menu-toggle', '', false, {'id':'toggle-mobile-header-menu'}) }}
-        <ul class="headerv1__menu">
-            {% for item in site_menu %}
-                <li class="headerv1__menu__item">
-                    {{ item.url|html_link(item.title, {'class': 'headerv1__menu__link'})|raw }}
-
-                    {% if item.menu is defined and item.menu is iterable %}
-                        <ul class="headerv1__submenu">
-                            {% for subitem in item.menu %}
-                                <li class="headerv1__submenu__item">
-                                    {{ subitem.url|html_link(subitem.title, {'class': 'headerv1__submenu__link'})|raw }}
-                                </li>
-                            {% endfor %}
-                        </ul>
-                    {% endif %}
-                </li>
-            {% endfor %}
-        </ul>
-
-        {{ input_checkbox_raw('', false, 'headerv1__user-toggle', '', false, {'id':'toggle-mobile-header-user'}) }}
-        <div class="headerv1__user">
-            {% if current_user is defined %}
-                <a href="/profile.php?u={{ current_user.user_id }}" title="Profile" class="headerv1__user__button">
-                    <i class="fas fa-user fa-fw"></i>
-                </a>
-
-                <a href="/settings.php" title="Settings" class="headerv1__user__button">
-                    <i class="fas fa-cog fa-fw"></i>
-                </a>
-
-                {% if has_manage_access %}
-                    {% if in_manage %}
-                        <a href="{{ site_link|default('/') }}" title="Return to the Site" class="headerv1__user__button">
-                            <i class="fas fa-door-open fa-fw"></i>
-                        </a>
-                    {% else %}
-                        <a href="{{ manage_link|default('/manage/index.php') }}" title="Enter Manage" class="headerv1__user__button">
-                            <i class="fas fa-door-closed fa-fw"></i>
-                        </a>
-                    {% endif %}
-                {% endif %}
-
-                <a href="/auth.php?m=logout&amp;s={{ csrf_token('logout') }}" title="Log out" class="headerv1__user__button">
-                    <i class="fas fa-sign-out-alt fa-fw"></i>
-                </a>
-
-                <a href="/profile.php?u={{ current_user.user_id }}" class="avatar headerv1__user__avatar"
-                    style="background-image:url('/profile.php?u={{ current_user.user_id }}&amp;m=avatar');" title="{{ current_user.username }}"></a>
-            {% else %}
-                <a href="/auth.php?m=register" title="Register" class="headerv1__user__button">
-                    <i class="fas fa-user-plus fa-fw"></i>
-                </a>
-
-                <a href="/auth.php?m=login" title="Log in" class="headerv1__user__button">
-                    <i class="fas fa-sign-in-alt fa-fw"></i>
-                </a>
-
-                <a href="/auth.php?m=login" class="avatar headerv1__user__avatar"
-                    style="background-image:url('/profile.php?m=avatar');"></a>
-            {% endif %}
-        </div>
-    </div>
-</nav>
diff --git a/templates/forum/macros.twig b/templates/forum/macros.twig
index 14209348..2dac600a 100644
--- a/templates/forum/macros.twig
+++ b/templates/forum/macros.twig
@@ -447,6 +447,12 @@
                     {% endif %}
                 </div>
             {% endif %}
+
+            {% if post.post_display_signature and post.poster_signature_content|length > 0 %}
+                <div class="forum__post__signature{% if post.poster_signature_parser == constant('MSZ_PARSER_MARKDOWN') %} markdown{% endif %}">
+                    {{ post.poster_signature_content|escape|parse_text(post.poster_signature_parser)|raw }}
+                </div>
+            {% endif %}
         </div>
     </div>
 {% endmacro %}
diff --git a/templates/forum/posting.twig b/templates/forum/posting.twig
index 6b6251cc..99fc6e29 100644
--- a/templates/forum/posting.twig
+++ b/templates/forum/posting.twig
@@ -1,6 +1,6 @@
 {% extends 'forum/master.twig' %}
 {% from 'forum/macros.twig' import forum_header %}
-{% from '_layout/input.twig' import input_hidden, input_csrf, input_text, input_button, input_select %}
+{% from '_layout/input.twig' import input_hidden, input_csrf, input_text, input_button, input_select, input_checkbox %}
 
 {% set title = 'Posting' %}
 {% set is_reply = posting_topic is defined %}
@@ -80,6 +80,16 @@
                                 null, null, null, 'forum__post__dropdown'
                             ) }}
                         {% endif %}
+                        {{ input_checkbox(
+                            'post[signature]',
+                            'Display Signature',
+                            posting_defaults.signature is not null
+                                ? posting_defaults.signature : (
+                                    posting_post.post_display_signature is defined
+                                        ? posting_post.post_display_signature
+                                        : true
+                                )
+                        ) }}
                     </div>
 
                     <div class="forum__post__buttons">
diff --git a/templates/user/profile.twig b/templates/user/profile.twig
index a5412911..8a24c94e 100644
--- a/templates/user/profile.twig
+++ b/templates/user/profile.twig
@@ -206,14 +206,33 @@
                         <div class="container profile__container profile__about" id="about">
                             {{ container_title('About ' ~ profile.username) }}
 
-                            <div class="profile__about__content{% if is_editing %} profile__about__content--edit{% elseif profile.user_about_parser == constant('MSZ_PARSER_MARKDOWN') %} markdown{% endif %}">
-                                {% if is_editing %}
+                            {% if is_editing %}
+                                <div class="profile__signature__editor">
                                     {{ input_select('about[parser]', constant('MSZ_PARSERS_NAMES'), profile.user_about_parser, '', '', false, 'profile__about__select') }}
-                                    <textarea name="about[text]" class="input__textarea profile__about__editor" id="about-textarea">{{ profile.user_about_content|escape }}</textarea>
-                                {% else %}
+                                    <textarea name="about[text]" class="input__textarea profile__about__text" id="about-textarea">{{ profile.user_about_content|escape }}</textarea>
+                                </div>
+                            {% else %}
+                                <div class="profile__about__content{% if is_editing %} profile__about__content--edit{% elseif profile.user_about_parser == constant('MSZ_PARSER_MARKDOWN') %} markdown{% endif %}">
                                     {{ profile.user_about_content|escape|parse_text(profile.user_about_parser)|raw }}
-                                {% endif %}
-                            </div>
+                                </div>
+                            {% endif %}
+                        </div>
+                    {% endif %}
+
+                    {% if (is_editing and perms.edit_signature) or profile.user_signature_content|length > 0 %}
+                        <div class="container profile__container profile__signature" id="signature">
+                            {{ container_title('Signature') }}
+
+                            {% if is_editing %}
+                                <div class="profile__signature__editor">
+                                    {{ input_select('signature[parser]', constant('MSZ_PARSERS_NAMES'), profile.user_signature_parser, '', '', false, 'profile__signature__select') }}
+                                    <textarea name="signature[text]" class="input__textarea profile__signature__text" id="signature-textarea">{{ profile.user_signature_content|escape }}</textarea>
+                                </div>
+                            {% else %}
+                                <div class="profile__signature__content{% if is_editing %} profile__signature__content--edit{% elseif profile.user_signature_parser == constant('MSZ_PARSER_MARKDOWN') %} markdown{% endif %}">
+                                {{ profile.user_signature_content|escape|parse_text(profile.user_signature_parser)|raw }}
+                                </div>
+                            {% endif %}
                         </div>
                     {% endif %}
 
diff --git a/templates/user/settings.twig b/templates/user/settings.twig
index 7055d43e..5d9f3088 100644
--- a/templates/user/settings.twig
+++ b/templates/user/settings.twig
@@ -1,7 +1,7 @@
 {% extends 'user/master.twig' %}
 {% from 'macros.twig' import container_title, pagination %}
 {% from 'user/macros.twig' import user_session, user_login_attempt, user_account_log %}
-{% from '_layout/input.twig' import input_hidden, input_csrf, input_text %}
+{% from '_layout/input.twig' import input_hidden, input_csrf, input_text, input_select %}
 
 {% set title = 'Settings' %}
 {% set menu = {