summaryrefslogtreecommitdiff
path: root/src/application/templates
diff options
context:
space:
mode:
authorJonas Kohl2024-10-17 10:56:01 +0200
committerJonas Kohl2024-10-17 10:56:01 +0200
commitfe0f414dc0211a4014581dc03fcfd514ed7ed02d (patch)
treecd86fc00cd9b7a97eabb9668e0a39e2b4b3e5357 /src/application/templates
parente0e89b9fdbf301e0ead944636023947a67aca57d (diff)
Transition templating to Twig
Diffstat (limited to 'src/application/templates')
-rw-r--r--src/application/templates/bootstrap-3/base.twig250
-rw-r--r--src/application/templates/bootstrap-3/components/alert_error.twig8
-rw-r--r--src/application/templates/bootstrap-3/components/alert_info.twig8
-rw-r--r--src/application/templates/bootstrap-3/components/alert_success.twig8
-rw-r--r--src/application/templates/bootstrap-3/components/post.twig168
-rw-r--r--src/application/templates/bootstrap-3/components/topic_log.twig62
-rw-r--r--src/application/templates/bootstrap-3/delete_post.twig36
-rw-r--r--src/application/templates/bootstrap-3/delete_topic.twig32
-rw-r--r--src/application/templates/bootstrap-3/error_page.twig12
-rw-r--r--src/application/templates/bootstrap-3/info_page.twig12
-rw-r--r--src/application/templates/bootstrap-3/login.twig50
-rw-r--r--src/application/templates/bootstrap-3/new_password.twig42
-rw-r--r--src/application/templates/bootstrap-3/new_topic.twig58
-rw-r--r--src/application/templates/bootstrap-3/password_reset.twig42
-rw-r--r--src/application/templates/bootstrap-3/register.twig89
-rw-r--r--src/application/templates/bootstrap-3/search.twig63
-rw-r--r--src/application/templates/bootstrap-3/view_topic.twig353
-rw-r--r--src/application/templates/bootstrap-3/view_topics.twig25
-rw-r--r--src/application/templates/bootstrap-3/view_user.twig201
19 files changed, 1519 insertions, 0 deletions
diff --git a/src/application/templates/bootstrap-3/base.twig b/src/application/templates/bootstrap-3/base.twig
new file mode 100644
index 0000000..5e003b9
--- /dev/null
+++ b/src/application/templates/bootstrap-3/base.twig
@@ -0,0 +1,250 @@
+{%- if title -%}
+ {%- set title = title ~ " | " -%}
+{%- endif -%}
+{%- set title = title ~ (g.env.MYSTIC_FORUM_TITLE|default("Forum")) -%}
+{%- set nextParam = "" -%}
+{%- if g.globals.action in ["login", "register"] -%}
+ {%- set nextParam = g.get.next|default("") -%}
+{%- else -%}
+ {%- set nextParam = g.server.REQUEST_URI -%}
+{%- endif -%}
+<!DOCTYPE html>
+<!--[if lt IE 7]> <html lang="de" class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
+<!--[if IE 7]> <html lang="de" class="no-js lt-ie9 lt-ie8"> <![endif]-->
+<!--[if IE 8]> <html lang="de" class="no-js lt-ie9"> <![endif]-->
+<!--[if gt IE 8]><!--> <html lang="de" class="no-js"> <!--<![endif]-->
+<head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width,initial-scale=1">
+ <meta name="generator" content="mysticBB {{ constant("MYSTICBB_VERSION") }}">
+ <title>{{ title }}</title>
+ <link rel="stylesheet" href="?_action=ctheme">
+ <script src="/ui/jquery-1.12.4.min.js"></script>
+ <script src="/ui/dist/js/bootstrap.min.js"></script>
+ <script src="/ui/modernizr-2.6.2.min.js"></script>
+ <script src="?_action=ji18n"></script>
+ <!--[if lt IE 9]>
+ <script src="/ui/html5shiv.min.js"></script>
+ <script src="/ui/respond.min.js"></script>
+ <![endif]-->
+ {% block head_after %}{% endblock %}
+</head>
+<body>
+
+{% block nav %}
+<nav class="navbar navbar-default navbar-static-top">
+ <div class="container">
+ <div class="navbar-header">
+ <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#nav-collapse" aria-expanded="false">
+ <span class="sr-only">Toggle navigation</span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+ <a class="navbar-brand" href=".">
+ <span class="myb-icon mybblogo" aria-hidden="false"></span>
+ {{ g.env.MYSTIC_FORUM_TITLE|default("Forum") }}
+ </a>
+ </div>
+ <div class="collapse navbar-collapse" id="nav-collapse">
+ {% block navbar %}
+ {% if currentUser %}
+ <ul class="nav navbar-nav navbar-right">
+ <li><p class="navbar-text">{{ __("Welcome, %user%!", {
+ user: currentUser.id == constant("mystic\\forum\\orm\\User::SUPERUSER_ID") ? ('<strong class="text-danger">' ~ (currentUser.displayName|e("html")) ~ '</strong>')|raw : ('<strong>' ~ (currentUser.displayName|e("html")) ~ '</strong>')|raw
+ }) }}
+ </p></li>
+ <li{{ g.globals.action == "search" ? ' class="active"'|raw : '' }}><a href="?_action=search"><span class="fa fa-search" aria-hidden="true"></span><span class="sr-only">{{ __("Search") }}</span></a></li>
+ <li{{ (g.globals.action == "viewuser" and g.get.user == currentUser.id) ? ' class="active"'|raw : '' }}><a href="?_action=viewuser&amp;user={{ currentUser.id|url_encode }}"><span class="fa fa-user" aria-hidden="true"></span><span class="sr-only">{{ __("View profile") }}</span></a></li>
+ <li><a href="?_action=logout&amp;next={{ g.server.REQUEST_URI|url_encode }}"><span class="fa fa-sign-out" aria-hidden="true"></span><span class="sr-only">{{ __("Log out") }}</span></a></li>
+ </ul>
+ {% else %}
+ <ul class="nav navbar-nav navbar-right">
+ <li{{ g.globals.action == "search" ? ' class="active"'|raw : '' }}><a href="?_action=search"><span class="fa fa-search" aria-hidden="true"></span><span class="sr-only">{{ __("Search") }}</span></a></li>
+ <li{{ g.globals.action == "auth" ? ' class="active"'|raw : '' }}><a href="?_action=auth&amp;next={{ nextParam|url_encode }}">{{ __("Log in") }}</a></li>
+ {% if constant("REGISTRATION_ENABLED") %}
+ <li{{ g.globals.action == "register" ? ' class="active"'|raw : '' }}><a href="?_action=register&amp;next={{ nextParam|url_encode }}">{{ __("Register") }}</a></li>
+ {% endif %}
+ </ul>
+ {% endif %}
+ {% endblock %}
+ </div>
+ </div>
+</nav>
+{% endblock %}
+
+{% block main %}
+<div class="container">
+ {% block content %}{% endblock %}
+</div>
+{% endblock %}
+
+{% block footer %}
+<footer class="footer">
+<div class="container">
+<div class="panel panel-default">
+<div class="panel-body">
+ <table style="border-collapse: collapse; width: 100%; background: none">
+ <tbody>
+ <tr>
+ <td style="padding: 0; vertical-align: middle; text-align: left; width: 100%" class="text-normal">
+ &copy; {{ "now"|date("Y") }} {{ g.env.MYSTIC_FORUM_COPYRIGHT|default(g.env.MYSTIC_FORUM_TITLE)|default("Forum") }}.
+ Powered by <a href="https://git.jkohl.link/mystic-forum.git/tag/?h=v{{ constant("MYSTICBB_VERSION")|url_encode }}">mysticBB v{{ constant("MYSTICBB_VERSION") }}</a>.
+ </td>
+ <td style="padding: 0; vertical-align: middle; text-align: right; white-space: nowrap" class="text-normal">
+ <form action="?_action=settheme" class="form-inline seamless-inline" method="post">
+ <input type="hidden" name="next" value="{{ g.server.REQUEST_URI }}">
+ <div class="form-group">
+ <label for="theme-select">{{ __("Theme:") }}</label>
+ <select class="form-control input-sm auto-submit" id="theme-select" name="theme">
+ {% for themeKey, themeInfo in availableThemes %}
+ <option value="{{ themeKey }}"{{ themeKey == currentTheme ? " selected" : "" }}>{{ themeInfo.name }}</option>
+ {% endfor %}
+ </select>
+ </div>
+ </form>
+ &nbsp;&nbsp;&nbsp;
+ <form action="?_action=setlang" class="form-inline seamless-inline" method="post">
+ <input type="hidden" name="next" value="{{ g.server.REQUEST_URI }}">
+ <div class="form-group">
+ <label for="lang-select">{{ __("Language:") }}</label>
+ <select class="form-control input-sm auto-submit" id="lang-select" name="lang">
+ {% for langKey, langName in availableLangs %}
+ <option value="{{ langKey }}"{{ langKey == currentLang ? " selected" : "" }}>{{ langName }}</option>
+ {% endfor %}
+ </select>
+ </div>
+ </form>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+</div>
+</div>
+</div>
+</footer>
+{% endblock %}
+
+{% block scripts %}
+<script type="text/javascript">
+ $(function() {
+ function insertAroundSelection(textarea, before, after) {
+ var start = textarea.selectionStart;
+ var end = textarea.selectionEnd;
+ var text = textarea.value;
+ var pre = text.substring(0, start);
+ var inner = text.substring(start, end);
+ var post = text.substring(end);
+ start += before.length;
+ end += before.length;
+ text = pre + before + inner + after + post;
+ textarea.value = text;
+ textarea.focus();
+ textarea.selectionStart = start;
+ textarea.selectionEnd = end;
+ }
+
+ function getTextarea(btn) {
+ return $($(btn).attr("data-area"))[0];
+ }
+
+ var commands = {
+ bold: function(textarea) {
+ insertAroundSelection(textarea, "[b]", "[/b]");
+ },
+ italic: function(textarea) {
+ insertAroundSelection(textarea, "[i]", "[/i]");
+ },
+ underline: function(textarea) {
+ insertAroundSelection(textarea, "[u]", "[/u]");
+ },
+ strikethrough: function(textarea) {
+ insertAroundSelection(textarea, "[s]", "[/s]");
+ },
+ sup: function(textarea) {
+ insertAroundSelection(textarea, "[^]", "[/^]");
+ },
+ sub: function(textarea) {
+ insertAroundSelection(textarea, "[_]", "[/_]");
+ },
+ quote: function(textarea) {
+ insertAroundSelection(textarea, "> ", "");
+ },
+ spoiler: function(textarea) {
+ insertAroundSelection(textarea, "[spoiler]", "[/spoiler]");
+ }
+ }
+
+ $("button[data-editor-command]").attr("data-toggle", "tooltip").attr("data-placement", "bottom").click(function() {
+ var command = $(this).attr("data-editor-command");
+ var textarea = getTextarea(this);
+ commands[command](textarea);
+ }).tooltip();
+ });
+</script>
+
+<script>
+$(function() {
+ var _messages = {{ ({
+ selectFiles: [
+ ___("Select file", "Select files", 1),
+ ___("Select file", "Select files", 2),
+ ],
+ filesSelected: [
+ ___("%n% file selected", "No files selected", 0),
+ ___("%n% file selected", "%n% files selected", 1),
+ ___("%n% file selected", "%n% files selected", 2),
+ ],
+ })|json_encode|raw }}
+
+ $(".auto-submit").on("change", function() {
+ $(this)[0].form.submit();
+ });
+
+ $("._time").each(function(i, e) {
+ var date = new Date($(e).text());
+ $(e).text(date.toLocaleString());
+ });
+ $("._date").each(function(i, e) {
+ var date = new Date($(e).text());
+ $(e).text(date.toLocaleDateString());
+ });
+ $("._time-only").each(function(i, e) {
+ var date = new Date($(e).text());
+ $(e).text(date.toLocaleTimeString());
+ });
+
+ $("input[type=file]").each(function(i, e) {
+ var isMultiple = !!$(e).prop("multiple");
+ var isSmall = !!$(e).hasClass("small");
+ var $input = $('<input type="text" readonly class="form-control" />').attr("placeholder", _messages.filesSelected[0]).css("text-overflow", "ellipsis");
+ var $btn = $('<button class="btn btn-default" type="button"></button>');
+ if (isSmall) {
+ $input.addClass("input-sm");
+ $btn.addClass("btn-sm");
+ }
+ $(e).after($('<div class="input-group file-input-group"></div>').append(
+ $input,
+ $('<span class="input-group-btn"></span>').append(
+ $btn.text(_messages.selectFiles[isMultiple ? 1 : 0]).click(function() {
+ $(e).click();
+ })
+ )
+ )).addClass("sr-only");
+ $(e).on("change", function() {
+ var files = $(e)[0].files;
+ if (files.length < 1)
+ $input.val("");
+ else if (files.length === 1)
+ $input.val(files[0].name);
+ else
+ $input.val(_messages.filesSelected[2].replace("%n%", files.length));
+ });
+ })
+});
+</script>
+{% endblock %}
+
+</body>
+</html>
diff --git a/src/application/templates/bootstrap-3/components/alert_error.twig b/src/application/templates/bootstrap-3/components/alert_error.twig
new file mode 100644
index 0000000..f3f759b
--- /dev/null
+++ b/src/application/templates/bootstrap-3/components/alert_error.twig
@@ -0,0 +1,8 @@
+<div class="alert alert-danger" role="alert">
+ <span class="fa fa-exclamation-circle" aria-hidden="true"></span>
+ {% if message starts with "?!HTML::" %}
+ {{ message|slice(8)|raw }}
+ {% else %}
+ {{ message }}
+ {% endif %}
+</div>
diff --git a/src/application/templates/bootstrap-3/components/alert_info.twig b/src/application/templates/bootstrap-3/components/alert_info.twig
new file mode 100644
index 0000000..099d091
--- /dev/null
+++ b/src/application/templates/bootstrap-3/components/alert_info.twig
@@ -0,0 +1,8 @@
+<div class="alert alert-info" role="alert">
+ <span class="fa fa-info-circle" aria-hidden="true"></span>
+ {% if message starts with "?!HTML::" %}
+ {{ message|slice(8)|raw }}
+ {% else %}
+ {{ message }}
+ {% endif %}
+</div>
diff --git a/src/application/templates/bootstrap-3/components/alert_success.twig b/src/application/templates/bootstrap-3/components/alert_success.twig
new file mode 100644
index 0000000..1c8f403
--- /dev/null
+++ b/src/application/templates/bootstrap-3/components/alert_success.twig
@@ -0,0 +1,8 @@
+<div class="alert alert-success" role="alert">
+ <span class="fa fa-check-circle" aria-hidden="true"></span>
+ {% if message starts with "?!HTML::" %}
+ {{ message|slice(8)|raw }}
+ {% else %}
+ {{ message }}
+ {% endif %}
+</div>
diff --git a/src/application/templates/bootstrap-3/components/post.twig b/src/application/templates/bootstrap-3/components/post.twig
new file mode 100644
index 0000000..ac969c0
--- /dev/null
+++ b/src/application/templates/bootstrap-3/components/post.twig
@@ -0,0 +1,168 @@
+{%- set fileAttachments = attachments|filter(a => not (a.mimeType starts with "image/" or a.mimeType starts with "video/")) -%}
+{%- set imageAttachments = attachments|filter(a => a.mimeType starts with "image/" or a.mimeType starts with "video/") -%}
+
+{%- set canReply =
+ not post.deleted
+ and not topic.isLocked
+ and currentUser is not null
+ and currentUser.hasPermission(permission("CREATE_OWN_POST"))
+-%}
+
+{%- set canEdit =
+ not post.deleted
+ and not topic.isLocked
+ and currentUser is not null
+ and (
+ (
+ postAuthor is not null
+ and postAuthor.hasPermission(permission("EDIT_OWN_POST"))
+ )
+ or currentUser.hasPermission(permission("EDIT_OTHER_POST"))
+ ) -%}
+
+{%- set canDelete =
+ not post.deleted
+ and currentUser is not null
+ and (
+ (
+ postAuthor is not null
+ and postAuthor.id == currentUser.id
+ and postAuthor.hasPermission(permission("DELETE_OWN_POST"))
+ )
+ or currentUser.hasPermission(permission("DELETE_OTHER_POST"))
+ ) -%}
+
+{%- set canViewAttachments = currentUser is not null -%}
+
+{%- set your_are_the_author =
+ currentUser is not null
+ and postAuthor is not null
+ and currentUser.id == postAuthor.id
+-%}
+
+{%- set is_op =
+ topicAuthor is not null
+ and postAuthor is not null
+ and postAuthor.id == topicAuthor.id
+-%}
+
+{% if post.deleted %}
+ <div class="media" id="post-{{ post.id }}">
+ <div class="media-left hidden-sm hidden-xs">
+ <div class="media-object" style="width:64px"></div>
+ </div>
+ <div class="media-body">
+ <div class="well icon-well text-warning">
+ <span class="fa fa-exclamation-triangle text-warning" aria-hidden="true"></span>
+ <em>{{ __("This post has been deleted") }}</em>
+ </div>
+ </div>
+ </div>
+{% else %}
+ <div class="media" id="post-{{ post.id }}" data-text="{{ post.content }}" style="overflow: visible;">
+ {% if not hide_pfp %}
+ <div class="media-left hidden-sm hidden-xs">
+ {% if postAuthor %}
+ {% if hide_actions %}
+ <img class="media-object" alt="{{ __("Profile picture") }}" src="?_action=profilepicture&amp;user={{ postAuthor.id|url_encode }}" width="64" height="64">
+ {% else %}
+ <a href="?_action=viewuser&amp;user={{ postAuthor.id|url_encode }}">
+ <img class="media-object" alt="{{ __("Profile picture") }}" src="?_action=profilepicture&amp;user={{ postAuthor.id|url_encode }}" width="64" height="64">
+ </a>
+ {% endif %}
+ {% else %}
+ <div class="media-object" style="width:64px;height:64px"></div>
+ {% endif %}
+ </div>
+ {% endif %}
+ <div class="media-body" style="overflow: visible;">
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ <div class="panel-title h3">
+ {% if not hide_actions %}
+ <div class="pull-right">
+ <a href="#post-{{ post.id }}" class="btn btn-default"><span class="fa fa-link" aria-hidden="true"></span><span class="sr-only">{{ __("Permalink") }}</span></a>
+ {% if canReply %}
+ <button data-post-id="{{ post.id }}" class="btn btn-default js-only _reply-post"><span class="fa fa-comment" aria-hidden="true"></span><span class="sr-only">{{ __("Reply to post") }}</span></button>
+ {% endif %}
+ {% if canEdit %}
+ <button data-post-id="{{ post.id }}" class="btn btn-default js-only _edit-post"><span class="fa fa-pencil" aria-hidden="true"></span><span class="sr-only">{{ __("Edit post") }}</span></button>
+ {% endif %}
+ {% if canDelete %}
+ <form action="?_action=deletepost" method="post" class="seamless-inline">
+ <input type="hidden" name="post" value="{{ post.id }}">
+ <button type="submit" class="btn btn-danger"><span class="fa fa-trash" aria-hidden="true"></span><span class="sr-only">{{ __("Delete post") }}</span></button>
+ </form>
+ {% endif %}
+ </div>
+ {% endif %}
+ {% if postAuthor %}
+ {% if hide_actions %}
+ {{ postAuthor.displayName }}
+ {% else %}
+ <a href="?_action=viewuser&amp;user={{ postAuthor.id|url_encode }}">{{ postAuthor.displayName }}</a>
+ {% if is_op %}
+ <span title="{{ __("Created this topic") }}" class="text-info fa fa-user-circle"></span>
+ {% endif %}
+ {% endif %}
+ {% if your_are_the_author %}
+ <span class="text-normal label label-primary">{{ __("You") }}</span>
+ {% endif %}
+ {% else %}
+ <em class="text-muted">{{ __("(deleted)") }}</em>
+ {% endif %}
+ </div>
+ <span class="_time">{{ post.postDate.format("c") }}</span>
+ {% if post.edited %}
+ <em class="text-muted">{{ __("(edited)") }}</em>
+ {% endif %}
+ </div>
+ <div class="panel-body">
+ <div class="post-content">{{ renderPost(post.content) }}</div>
+ {% if imageAttachments|length > 0 %}
+ <div class="post-images clearfix">
+ {% for attachment in imageAttachments %}
+ {% if hide_actions %}
+ <span class="image-attachment" title="{{ attachment.name }}">
+ <img class="image-attachment-image" src="?_action=thumb&amp;attachment={{ attachment.id|url_encode }}" alt="" width="100">
+ </span>
+ {% else %}
+ <a class="
+ image-attachment
+ attachment
+ {{ attachment.mimeType starts with "video/" ? "video-attachment" }}
+ " href="?_action=attachment&amp;attachment={{ attachment.id|url_encode }}" title="{{ attachment.name }}" data-attachment-id="{{ attachment.id }}">
+ <img class="image-attachment-image" src="?_action=thumb&amp;attachment={{ attachment.id|url_encode }}" alt="" width="100">
+ {% if not canViewAttachments %}
+ <span class="attachment-lock fa fa-ban" aria-hidden="true"></span>
+ {% elseif attachment.mimeType starts with "video/" %}
+ <span class="video-player-icon fa fa-play-circle" aria-hidden="true"></span>
+ {% endif %}
+ </a>
+ {% endif %}
+ {% endfor %}
+ </div>
+ {% endif %}
+ </div>
+ {% if fileAttachments|length > 0 %}
+ <div class="panel-footer">
+ <div class="btn-group">
+ {% for attachment in fileAttachments %}
+ {% if hide_actions %}
+ <button class="btn btn-default">{{ attachment.name }}</button>
+ {% else %}
+ <a class="btn btn-default attachment" href="?_action=attachment&amp;attachment={{ attachment.id|url_encode }}">
+ {% if not canViewAttachments %}
+ <span class="fa fa-ban" aria-hidden="true"></span>
+ {% endif %}
+ {{ attachment.name }}
+ </a>
+ {% endif %}
+ {% endfor %}
+ </div>
+ </div>
+ {% endif %}
+ </div>
+ </div>
+ </div>
+{% endif %}
diff --git a/src/application/templates/bootstrap-3/components/topic_log.twig b/src/application/templates/bootstrap-3/components/topic_log.twig
new file mode 100644
index 0000000..9364cd1
--- /dev/null
+++ b/src/application/templates/bootstrap-3/components/topic_log.twig
@@ -0,0 +1,62 @@
+{%- set user = "" -%}
+{%- if postAuthor is null -%}
+ {%- set user = __("deleted")|e("html") -%}
+{%- else -%}
+ {%- set user =
+ '<a href="?_action=viewuser&amp;user='
+ ~ postAuthor.id|url_encode|e("html")
+ ~ '">'
+ ~ postAuthor.displayName|e("html")
+ ~ '</a>'
+ -%}
+{%- endif -%}
+
+<div class="media" id="post-{{ logMessage.id }}">
+ <div class="media-left hidden-sm hidden-xs">
+ {% if postAuthor %}
+ {% if hideActions %}
+ <img class="media-object" alt="{{ __("Profile picture") }}" src="?_action=profilepicture&amp;user={{ postAuthor.id|url_encode }}" width="64" height="64">
+ {% else %}
+ <a href="?_action=viewuser&amp;user={{ postAuthor.id|url_encode }}">
+ <img class="media-object" alt="{{ __("Profile picture") }}" src="?_action=profilepicture&amp;user={{ postAuthor.id|url_encode }}" width="64" height="64">
+ </a>
+ {% endif %}
+ {% else %}
+ <div class="media-object" style="width:64px;height:64px"></div>
+ {% endif %}
+ </div>
+ <div class="media-body">
+ {% if logMessage.type == constant("mystic\\forum\\orm\\TopicLogMessage::LOCKED") %}
+ <div class="well icon-well text-info">
+ <span class="fa fa-lock text-info" aria-hidden="true"></span>
+ <em>{{ __("%user% locked this topic", {
+ "user": user,
+ }) }}</em>
+ <br>
+ <small class="_time">{{ logMessage.postDate.format("c") }}</small>
+ </div>
+ {% elseif logMessage.type == constant("mystic\\forum\\orm\\TopicLogMessage::UNLOCKED") %}
+ <div class="well icon-well text-success">
+ <span class="fa fa-unlock text-success" aria-hidden="true"></span>
+ <em>{{ __("%user% unlocked this topic", {
+ "user": user,
+ }) }}</em>
+ <br>
+ <small class="_time">{{ logMessage.postDate.format("c") }}</small>
+ </div>
+ {% elseif logMessage.type == constant("mystic\\forum\\orm\\TopicLogMessage::TITLE_CHANGED") %}
+ <div class="well icon-well text-info">
+ <span class="fa fa-pencil text-info" aria-hidden="true"></span>
+ <em>{{ __("%user% changed the title of this topic from %old_title% to %new_title%", {
+ "user": user,
+ "old_title": '<strong>' ~ logMessage.params.old_value|default(__("unknown"))|e("html") ~ '</strong>',
+ "new_title": '<strong>' ~ logMessage.params.new_value|default(__("unknown"))|e("html") ~ '</strong>',
+ }) }}</em>
+ <br>
+ <small class="_time">{{ logMessage.postDate.format("c") }}</small>
+ </div>
+ {% else %}
+ {{ __("unknown") }}
+ {% endif %}
+ </div>
+</div>
diff --git a/src/application/templates/bootstrap-3/delete_post.twig b/src/application/templates/bootstrap-3/delete_post.twig
new file mode 100644
index 0000000..90eaf09
--- /dev/null
+++ b/src/application/templates/bootstrap-3/delete_post.twig
@@ -0,0 +1,36 @@
+{% set title = __("Delete post") %}
+
+{% extends "base.twig" %}
+
+{% block content %}
+
+<div class="panel panel-danger">
+ <div class="panel-heading">
+ <h3 class="panel-title">{{ __("Do you want to delete this post?") }}</h3>
+ </div>
+ <div class="panel-body">
+ {{ __("Are you sure you want to delete the following post:") }}<br>
+ {% include "components/post.twig" with {
+ post: ctx.post,
+ postAuthor: ctx.postAuthor,
+ attachments: ctx.attachments,
+ hide_actions: true,
+ } %}
+ </div>
+ <div class="panel-footer">
+ <div class="text-right">
+ <form action=".#post-{{ ctx.post.id }}" method="get" class="seamless-inline">
+ <input type="hidden" name="_action" value="viewtopic">
+ <input type="hidden" name="topic" value="{{ ctx.post.topicId }}">
+ <button class="btn btn-default">{{ __("Keep post") }}</button>
+ </form>
+ <form action="?_action=deletepost" method="post" class="seamless-inline">
+ <input type="hidden" name="post" value="{{ ctx.post.id }}">
+ <input type="hidden" name="confirm" value="{{ ("confirm" ~ ctx.post.id)|hash("sha256", true)|base64_encode }}">
+ <button class="btn btn-danger">{{ __("Delete post") }}</button>
+ </form>
+ </div>
+ </div>
+</div>
+
+{% endblock %}
diff --git a/src/application/templates/bootstrap-3/delete_topic.twig b/src/application/templates/bootstrap-3/delete_topic.twig
new file mode 100644
index 0000000..3107c89
--- /dev/null
+++ b/src/application/templates/bootstrap-3/delete_topic.twig
@@ -0,0 +1,32 @@
+{% set title = __("Delete topic") %}
+
+{% extends "base.twig" %}
+
+{% block content %}
+
+<div class="panel panel-danger">
+ <div class="panel-heading">
+ <h3 class="panel-title">{{ __("Do you want to delete this topic?") }}</h3>
+ </div>
+ <div class="panel-body">
+ {{ __("Are you sure you want to delete the topic <em>%topic%</em> including <strong>all posts and attachments</strong>?", {
+ "topic": ctx.topic.title|e("html"),
+ }) }}
+ </div>
+ <div class="panel-footer">
+ <div class="text-right">
+ <form action="." method="get" class="seamless-inline">
+ <input type="hidden" name="_action" value="viewtopic">
+ <input type="hidden" name="topic" value="{{ ctx.topic.id }}">
+ <button class="btn btn-default">{{ __("Keep topic") }}</button>
+ </form>
+ <form action="?_action=deletetopic" method="post" class="seamless-inline">
+ <input type="hidden" name="topic" value="{{ ctx.topic.id }}">
+ <input type="hidden" name="confirm" value="{{ ("confirm" ~ ctx.topic.id)|hash("sha256", true)|base64_encode }}">
+ <button class="btn btn-danger">{{ __("Delete topic &amp; posts") }}</button>
+ </form>
+ </div>
+ </div>
+</div>
+
+{% endblock %}
diff --git a/src/application/templates/bootstrap-3/error_page.twig b/src/application/templates/bootstrap-3/error_page.twig
new file mode 100644
index 0000000..dde4057
--- /dev/null
+++ b/src/application/templates/bootstrap-3/error_page.twig
@@ -0,0 +1,12 @@
+{% set title = __("Error") %}
+{% extends "base.twig" %}
+
+{% block navbar %}
+ {% if not ctx.skipLoginCheck %}
+ {{ parent() }}
+ {% endif %}
+{% endblock %}
+
+{% block content %}
+ {% include "components/alert_error.twig" with { message: ctx.message } %}
+{% endblock %}
diff --git a/src/application/templates/bootstrap-3/info_page.twig b/src/application/templates/bootstrap-3/info_page.twig
new file mode 100644
index 0000000..e243b36
--- /dev/null
+++ b/src/application/templates/bootstrap-3/info_page.twig
@@ -0,0 +1,12 @@
+{% set title = __("Information") %}
+{% extends "base.twig" %}
+
+{% block navbar %}
+ {% if not ctx.skipLoginCheck %}
+ {{ parent() }}
+ {% endif %}
+{% endblock %}
+
+{% block content %}
+ {% include "components/alert_info.twig" with { message: ctx.message } %}
+{% endblock %}
diff --git a/src/application/templates/bootstrap-3/login.twig b/src/application/templates/bootstrap-3/login.twig
new file mode 100644
index 0000000..63b9867
--- /dev/null
+++ b/src/application/templates/bootstrap-3/login.twig
@@ -0,0 +1,50 @@
+{% set title = __("Log in") %}
+{% set formId = "login" %}
+{% set formError = getAndClearFormError(formId) %}
+
+{% extends "base.twig" %}
+
+{% block content %}
+
+<div class="page-header margin-top-0">
+ <h1>{{ __("Log in") }}</h1>
+</div>
+
+<div class="col-md-4"></div>
+
+<div class="well col-md-4">
+ {% if formError %}
+ {% include "components/alert_error.twig" with { message: formError } %}
+ {% endif %}
+ <form action="{{ g.server.REQUEST_URI }}" method="post">
+ <input type="hidden" name="form_id" value="{{ formId }}">
+
+ <div class="form-group">
+ <label for="i_username">{{ __("Username:") }}</label>
+ <input class="form-control" type="text" id="i_username" name="username" value="{{ lastFormField(formId, "username") }}" required autofocus>
+ </div>
+
+ <div class="form-group">
+ <label for="i_password">{{ __("Password:") }}</label>
+ <input class="form-control" type="password" id="i_password" name="password" required>
+ </div>
+
+ <div class="form-group">
+ <button class="btn btn-primary" type="submit">{{ __("Log in") }}</button>
+ <a class="btn btn-link" href="?_action=pwreset">{{ __("I forgot my password") }}</a>
+ </div>
+
+ {% if constant("REGISTRATION_ENABLED") %}
+ <div class="form-group">
+ {{ __("Don't have an account? %link%Register now%/link%", {
+ "link": '<a href="?_action=register">',
+ "/link": '</a>',
+ }) }}
+ </div>
+ {% endif %}
+ </form>
+</div>
+
+<div class="col-md-4"></div>
+
+{% endblock %}
diff --git a/src/application/templates/bootstrap-3/new_password.twig b/src/application/templates/bootstrap-3/new_password.twig
new file mode 100644
index 0000000..55a4b79
--- /dev/null
+++ b/src/application/templates/bootstrap-3/new_password.twig
@@ -0,0 +1,42 @@
+{% set title = __("Reset password") %}
+{% set formId = "pwnew" %}
+{% set formError = getAndClearFormError(formId) %}
+
+{% extends "base.twig" %}
+
+{% block content %}
+
+<div class="page-header margin-top-0">
+ <h1>{{ __("Reset password") }}</h1>
+</div>
+
+<div class="col-md-4"></div>
+
+<div class="well col-md-4">
+ {% if formError %}
+ {% include "components/alert_error.twig" with { message: formError } %}
+ {% endif %}
+ <form action="{{ g.server.REQUEST_URI }}" method="post">
+ <input type="hidden" name="form_id" value="{{ formId }}">
+ <input type="hidden" name="token" value="{{ g.get.token }}">
+ <input type="hidden" name="sig" value="{{ g.get.sig }}">
+
+ <div class="form-group">
+ <label for="i_new_password">{{ __("New password:") }}</label>
+ <input class="form-control" type="password" id="i_new_password" name="new_password" required autofocus>
+ </div>
+
+ <div class="form-group">
+ <label for="i_retype_password">{{ __("Retype password:") }}</label>
+ <input class="form-control" type="password" id="i_retype_password" name="retype_password" required>
+ </div>
+
+ <div class="form-group">
+ <button class="btn btn-primary" type="submit">{{ __("Set new password") }}</button>
+ </div>
+ </form>
+</div>
+
+<div class="col-md-4"></div>
+
+{% endblock %}
diff --git a/src/application/templates/bootstrap-3/new_topic.twig b/src/application/templates/bootstrap-3/new_topic.twig
new file mode 100644
index 0000000..74be156
--- /dev/null
+++ b/src/application/templates/bootstrap-3/new_topic.twig
@@ -0,0 +1,58 @@
+{% set title = __("New topic") %}
+{% set formId = "newtopic" %}
+{% set formError = getAndClearFormError(formId) %}
+
+{% extends "base.twig" %}
+
+{% block content %}
+
+<div class="page-header margin-top-0">
+ <h1>{{ __("New topic") }}</h1>
+</div>
+
+{% if formError %}
+ {% include "components/alert_error.twig" with { message: formError } %}
+{% endif %}
+<form action="{{ g.server.REQUEST_URI }}" method="post" enctype="multipart/form-data">
+ <input type="hidden" name="form_id" value="{{ formId }}">
+ <div class="form-group">
+ <label for="i_message">{{ __("Topic title:") }}</label>
+ <input type="text" class="form-control" id="i_title" name="title" value="{{ lastFormField(formId, "title") }}" required autofocus>
+ </div>
+ <div class="form-group">
+ <label for="i_message">{{ __("Message:") }}</label>
+ <div class="panel panel-default">
+ <div class="panel-heading" style="padding:4px">
+ <div class="btn-toolbar" role="toolbar">
+ <div class="btn-group" role="group">
+ <button data-area="#i_message" title="{{ __("Bold") }}" data-editor-command="bold" type="button" class="btn btn-default btn-xs"><i class="fa fa-fw fa-bold"></i></button>
+ <button data-area="#i_message" title="{{ __("Italic") }}" data-editor-command="italic" type="button" class="btn btn-default btn-xs"><i class="fa fa-fw fa-italic"></i></button>
+ <button data-area="#i_message" title="{{ __("Underlined") }}" data-editor-command="underline" type="button" class="btn btn-default btn-xs"><i class="fa fa-fw fa-underline"></i></button>
+ <button data-area="#i_message" title="{{ __("Strikethrough") }}" data-editor-command="strikethrough" type="button" class="btn btn-default btn-xs"><i class="fa fa-fw fa-strikethrough"></i></button>
+ </div>
+ <div class="btn-group" role="group">
+ <button data-area="#i_message" title="{{ __("Superscript") }}" data-editor-command="sup" type="button" class="btn btn-default btn-xs"><i class="fa fa-fw fa-superscript"></i></button>
+ <button data-area="#i_message" title="{{ __("Subscript") }}" data-editor-command="sub" type="button" class="btn btn-default btn-xs"><i class="fa fa-fw fa-subscript"></i></button>
+ </div>
+ <div class="btn-group" role="group">
+ <button data-area="#i_message" title="{{ __("Quote") }}" data-editor-command="quote" type="button" class="btn btn-default btn-xs"><i class="fa fa-fw fa-quote-left"></i></button>
+ <button data-area="#i_message" title="{{ __("Spoiler") }}" data-editor-command="spoiler" type="button" class="btn btn-default btn-xs"><i class="fa fa-fw fa-eye"></i></button>
+ </div>
+ </div>
+ </div>
+ <div class="panel-body" style="padding:0">
+ <textarea class="form-control" id="i_message" name="message" required rows="12" cols="60" style="resize:vertical;max-height:499px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;margin:-1px;width:calc(100% + 2px)">{{ lastFormField(formId, "message") }}</textarea>
+ </div>
+ </div>
+ </div>
+ <div class="form-group">
+ <label for="i_files">{{ __("Attachments: <small>(max. %max_attachment_count% files, max. %max_attachment_size% MiB each)</small>", {
+ "max_attachment_count": constant("MAX_ATTACHMENT_COUNT"),
+ "max_attachment_size": constant("MAX_ATTACHMENT_SIZE") // (2**20),
+ }) }}</label>
+ <input type="file" name="files[]" id="i_files" multiple accept="*/*">
+ </div>
+ <button type="submit" class="btn btn-success">{{ __("Create topic") }} <span class="fa fa-send" aria-hidden="true"></span></button>
+</form>
+
+{% endblock %}
diff --git a/src/application/templates/bootstrap-3/password_reset.twig b/src/application/templates/bootstrap-3/password_reset.twig
new file mode 100644
index 0000000..f633106
--- /dev/null
+++ b/src/application/templates/bootstrap-3/password_reset.twig
@@ -0,0 +1,42 @@
+{% set title = __("Reset password") %}
+{% set formId = "pwreset" %}
+{% set formError = getAndClearFormError(formId) %}
+
+{% extends "base.twig" %}
+
+{% block content %}
+
+<div class="page-header margin-top-0">
+ <h1>{{ __("Reset password") }}</h1>
+</div>
+
+<div class="col-md-4"></div>
+
+<div class="well col-md-4">
+ {% if formError %}
+ {% include "components/alert_error.twig" with { message: formError } %}
+ {% endif %}
+ <form action="{{ g.server.REQUEST_URI }}" method="post">
+ <input type="hidden" name="form_id" value="{{ formId }}">
+
+ <div class="form-group">
+ <label for="i_username">{{ __("Email address:") }}</label>
+ <input class="form-control" type="email" id="i_email" name="email" value="{{ lastFormField(formId, "email") }}" required autofocus>
+ </div>
+
+ <div class="form-group">
+ <button class="btn btn-primary" type="submit">{{ __("Reset password") }}</button>
+ </div>
+
+ <div class="form-group">
+ {{ __("I know my password and I want to %link%log in%/link%!", {
+ "link": '<a href="?_action=auth">',
+ "/link": '</a>',
+ }) }}
+ </div>
+ </form>
+</div>
+
+<div class="col-md-4"></div>
+
+{% endblock %}
diff --git a/src/application/templates/bootstrap-3/register.twig b/src/application/templates/bootstrap-3/register.twig
new file mode 100644
index 0000000..1e4cd92
--- /dev/null
+++ b/src/application/templates/bootstrap-3/register.twig
@@ -0,0 +1,89 @@
+{% set title = __("Register") %}
+{% set formId = "register" %}
+{% set formError = getAndClearFormError(formId) %}
+
+{% extends "base.twig" %}
+
+{% block content %}
+
+<div class="page-header margin-top-0">
+ <h1>{{ __("Register") }}</h1>
+</div>
+
+<div class="col-md-4"></div>
+
+<div class="well col-md-4">
+ {% if formError %}
+ {% include "components/alert_error.twig" with { message: formError } %}
+ {% endif %}
+ <form action="{{ g.server.REQUEST_URI }}" method="post">
+ <input type="hidden" name="form_id" value="{{ formId }}">
+
+ <div class="form-group" id="group0">
+ <label for="i_username">{{ __("Username:") }}</label>
+ <input class="form-control" id="i_username" type="text" name="username" value="" required>
+ </div>
+
+ <div class="form-group" id="group1">
+ <label for="i_username">{{ __("Username:") }}</label>
+ <input class="form-control" id="i_username" type="text" name="df82a9bc21" value="{{ lastFormField(formId, "df82a9bc21") }}" required autofocus>
+ </div>
+
+ <div class="form-group" id="group2">
+ <label for="i_display_name">{{ __("Display name:") }}</label>
+ <input class="form-control" id="i_display_name" type="text" name="display_name" value="{{ lastFormField(formId, "display_name") }}" required>
+ </div>
+
+ <div class="form-group" id="group3">
+ <label for="i_password">{{ __("Choose password:") }}</label>
+ <input class="form-control" id="i_password" type="password" name="password" required>
+ </div>
+
+ <div class="form-group" id="group4">
+ <label for="i_password_retype">{{ __("Repeat password:") }}</label>
+ <input class="form-control" id="i_password_retype" type="password" name="password_retype" required>
+ </div>
+
+ <div class="form-group" id="group5">
+ <label for="i_email">{{ __("Email address:") }}</label>
+ <input class="form-control" id="i_email" type="email" name="email" value="{{ lastFormField(formId, "email") }}" required>
+ </div>
+
+ <div class="form-group" id="group6">
+ <label for="i_email">{{ __("CAPTCHA:") }}</label>
+ <div class="text-center margin-bottom">
+ <img src="?_action=captcha&amp;t={{ "now"|date("Uv") }}" alt="CAPTCHA" width="192" height="48" id="captcha-img">
+ </div>
+ <div class="input-group">
+ <input type="text" name="captcha" id="i_captcha" class="form-control" required>
+ <div class="input-group-btn">
+ <button class="btn btn-default" type="button" id="btn-refresh-captcha"><span class="fa fa-refresh" aria-hidden="true"></span><span class="sr-only">{{ __("New CAPTCHA") }}</span></button>
+ </div>
+ </div>
+ </div>
+
+ <div class="form-group">
+ <button class="btn btn-primary" type="submit">{{ __("Register now") }}</button>
+ </div>
+
+ <div class="form-group">
+ {{ __("Already have an account? %link%Sign in now%/link%", {
+ "link": '<a href="?_action=auth">',
+ "/link": '</a>',
+ }) }}
+ </div>
+ </form>
+</div>
+
+<div class="col-md-4"></div>
+
+<script>
+$(function() {
+ $("#btn-refresh-captcha").click(function() {
+ $("#captcha-img").attr("src", "?_action=captcha&t=" + new Date().getTime().toString());
+ });
+ $("#i_username").prop("disabled", true).prop("required", false);
+});
+</script>
+
+{% endblock %}
diff --git a/src/application/templates/bootstrap-3/search.twig b/src/application/templates/bootstrap-3/search.twig
new file mode 100644
index 0000000..c8f6315
--- /dev/null
+++ b/src/application/templates/bootstrap-3/search.twig
@@ -0,0 +1,63 @@
+{% set title = __("Search") %}
+{% set formId = "search" %}
+{% set formError = getAndClearFormError(formId) %}
+
+{% extends "base.twig" %}
+
+{% block content %}
+
+<div class="page-header margin-top-0">
+ <h1>{{ __("Search") }}</h1>
+</div>
+
+{% if formError %}
+ {% include "components/alert_error.twig" with { message: formError } %}
+{% endif %}
+
+<form action="." method="get">
+ <input type="hidden" name="form_id" value="{{ formId }}">
+ <input type="hidden" name="_action" value="search">
+ <div class="form-group">
+ <div class="input-group">
+ <input class="form-control" type="search" id="i_query" name="query" value="{{ lastFormField(formId, "query")|default(g.get.query)|default("") }}" required autofocus placeholder="{{ __("Enter your search query...") }}">
+ <div class="input-group-btn">
+ <button class="btn btn-primary" type="submit">{{ __("Search") }}</button>
+ </div>
+ </div>
+ </div>
+</form>
+
+{% if g.get.query is defined and g.get.query is not null and g.get.query != "" %}
+ {% if ctx.posts|length > 0 %}
+ <p>{{ __("%result_count% result(s) in %search_duration% second(s)", {
+ "result_count": ctx.posts|length,
+ "search_duration": ctx.search_duration|number_format(2, __(".", context: "Number formatting"), __(",", context: "Number formatting")),
+ }) }}</p>
+ <div class="list-group margin-top">
+ {% for post in ctx.posts|filter(p => not p.deleted) %}
+ {% set hasAttachments = ctx.attachments[post.id]|length > 0 %}
+ {% set postAuthor = ctx.users[post.authorId] %}
+ <a href="?_action=viewtopic&amp;topic={{ post.topicId|url_encode }}#post-{{ post.id|url_encode }}" class="list-group-item">
+ {% if hasAttachments %}
+ <span class="badge"><span class="fa fa-paperclip"></span></span>
+ {% endif %}
+ {{ renderPostSummary(post.content) }}<br>
+ <span class="text-muted">{{ __("posted by %author% on %post_date% in %topic%", {
+ "author": '<em>' ~ (postAuthor ? postAuthor.displayName : __("unknown"))|e("html") ~ '</em>',
+ "post_date": '<span class="_time">' ~ post.postDate.format("c")|e("html") ~ '</span>',
+ "topic": '<em>'
+ ~ (topics[post.topicId].isLocked ? '<span class="fa fa-lock text-muted" aria-hidden="true"></span> ' : '')
+ ~ (topics[post.topicId] ? topics[post.topicId].title : null)|default("unknown")|e("html") ~ '</em>',
+ }) }}</span>
+ </a>
+ {% endfor %}
+ </div>
+ {% else %}
+ <div class="well icon-well text-info margin-top margin-bottom">
+ <span class="fa fa-info-circle text-info" aria-hidden="true"></span>
+ <em>{{ __("No results for this search") }}</em>
+ </div>
+ {% endif %}
+{% endif %}
+
+{% endblock %}
diff --git a/src/application/templates/bootstrap-3/view_topic.twig b/src/application/templates/bootstrap-3/view_topic.twig
new file mode 100644
index 0000000..008c336
--- /dev/null
+++ b/src/application/templates/bootstrap-3/view_topic.twig
@@ -0,0 +1,353 @@
+{% set canReply =
+ not ctx.topic.isLocked
+ and currentUser is not null
+ and currentUser.hasPermission(permission("CREATE_OWN_POST")) %}
+
+{% set canEdit =
+ currentUser is not null and (
+ (
+ ctx.topicAuthor is not null
+ and currentUser.id == ctx.topicAuthor.id
+ and ctx.topicAuthor.hasPermission(permission("EDIT_OWN_TOPIC"))
+ )
+ or currentUser.hasPermission(permission("EDIT_OTHER_TOPIC"))
+ ) %}
+
+{% set canDelete =
+ currentUser is not null and (
+ (
+ ctx.topicAuthor is not null
+ and currentUser.id == ctx.topicAuthor.id
+ and ctx.topicAuthor.hasPermission(permission("DELETE_OWN_TOPIC"))
+ )
+ or currentUser.hasPermission(permission("DELETE_OTHER_TOPIC"))
+ ) %}
+
+{% set title = ctx.topic.title %}
+{% extends "base.twig" %}
+
+{% block content %}
+
+{% if canEdit %}
+ <div class="modal fade" tabindex="-1" role="dialog" id="diag-edit-post">
+ <form class="modal-dialog" role="document" action="?_action=updatepost" method="post">
+ <input type="hidden" id="i_edit_post" name="post">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+ <h4 class="modal-title">{{ __("Edit post") }}</h4>
+ </div>
+ <div class="modal-body">
+ <label class="sr-only" for="i_edit_message">{{ __("Message:") }}</label>
+ <div class="panel panel-default">
+ <div class="panel-heading" style="padding:4px">
+ <div class="btn-toolbar" role="toolbar">
+ <div class="btn-group" role="group">
+ <button data-area="#i_edit_message" title="{{ __("Bold") }}" data-editor-command="bold" type="button" class="btn btn-default btn-xs"><i class="fa fa-fw fa-bold"></i></button>
+ <button data-area="#i_edit_message" title="{{ __("Italic") }}" data-editor-command="italic" type="button" class="btn btn-default btn-xs"><i class="fa fa-fw fa-italic"></i></button>
+ <button data-area="#i_edit_message" title="{{ __("Underlined") }}" data-editor-command="underline" type="button" class="btn btn-default btn-xs"><i class="fa fa-fw fa-underline"></i></button>
+ <button data-area="#i_edit_message" title="{{ __("Strikethrough") }}" data-editor-command="strikethrough" type="button" class="btn btn-default btn-xs"><i class="fa fa-fw fa-strikethrough"></i></button>
+ </div>
+ <div class="btn-group" role="group">
+ <button data-area="#i_edit_message" title="{{ __("Superscript") }}" data-editor-command="sup" type="button" class="btn btn-default btn-xs"><i class="fa fa-fw fa-superscript"></i></button>
+ <button data-area="#i_edit_message" title="{{ __("Subscript") }}" data-editor-command="sub" type="button" class="btn btn-default btn-xs"><i class="fa fa-fw fa-subscript"></i></button>
+ </div>
+ <div class="btn-group" role="group">
+ <button data-area="#i_edit_message" title="{{ __("Quote") }}" data-editor-command="quote" type="button" class="btn btn-default btn-xs"><i class="fa fa-fw fa-quote-left"></i></button>
+ <button data-area="#i_edit_message" title="{{ __("Spoiler") }}" data-editor-command="spoiler" type="button" class="btn btn-default btn-xs"><i class="fa fa-fw fa-eye"></i></button>
+ </div>
+ </div>
+ </div>
+ <div class="panel-body" style="padding:0">
+ <textarea class="form-control" id="i_edit_message" name="message" required rows="12" cols="60" style="resize:vertical;max-height:500px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;margin:-1px;width:calc(100% + 2px)"></textarea>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-default" data-dismiss="modal"><span class="fa fa-remove" aria-hidden="true"></span> {{ __("Cancel") }}</button>
+ <button type="submit" class="btn btn-success"><span class="fa fa-save" aria-hidden="true"></span> {{ __("Save changes") }}</button>
+ </div>
+ </div>
+ </form>
+ </div>
+{% endif %}
+{% if currentUser is null %}
+ <div class="modal fade" tabindex="-1" role="dialog" id="diag-cant-view-attachment">
+ <div class="modal-dialog modal-danger" role="document">
+ <div class="modal-content panel-danger">
+ <div class="modal-header panel-heading">
+ <h4 class="modal-title"><span class="fa fa-exclamation-circle" aria-hidden="true"></span> {{ __("Permission denied") }}</h4>
+ </div>
+ <div class="modal-body">
+ {{ __("You must be logged in to view attachments") }}
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-default" data-dismiss="modal"><span class="fa fa-close" aria-hidden="true"></span> {{ __("Close") }}</button>
+ <a href="?_action=auth&amp;next={{ g.server.REQUEST_URI|url_encode }}" class="btn btn-success"><span class="fa fa-user" aria-hidden="true"></span> {{ __("Log in") }}</a>
+ </div>
+ </div>
+ </div>
+ </div>
+ <script>
+ $(function() {
+ $(".attachment").click(function(e) {
+ e.preventDefault();
+ $("#diag-cant-view-attachment").modal();
+ });
+ });
+ </script>
+{% else %}
+ <div class="modal fade" tabindex="-1" role="dialog" id="diag-image-attachment">
+ <div class="modal-dialog" role="document">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+ <h4 class="modal-title">{{ __("Attachment") }}</h4>
+ </div>
+ <div class="modal-body">
+ <img class="image-attachment-view attachment-view" id="image-attachment-view" alt="">
+ </div>
+ <div class="modal-footer">
+ <a href="" download id="image-attachment-dl-btn" class="btn btn-default"><span class="fa fa-download" aria-hidden="true"></span> {{ __("Download") }}</a>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="modal fade" tabindex="-1" role="dialog" id="diag-video-attachment">
+ <div class="modal-dialog" role="document">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+ <h4 class="modal-title">{{ __("Attachment") }}</h4>
+ </div>
+ <div class="modal-body">
+ <video class="video-attachment-view attachment-view" id="video-attachment-view" controls></video>
+ </div>
+ <div class="modal-footer">
+ <a href="" download id="video-attachment-dl-btn" class="btn btn-default"><span class="fa fa-download" aria-hidden="true"></span> {{ __("Download") }}</a>
+ </div>
+ </div>
+ </div>
+ </div>
+ <script>
+ $(function() {
+ $(".image-attachment:not(.video-attachment)").click(function(e) {
+ e.preventDefault();
+ var attUrl = "?_action=attachment&attachment=" + encodeURIComponent($(this).attr("data-attachment-id"));
+ $("#image-attachment-view").attr("src", attUrl);
+ $("#image-attachment-dl-btn").attr("href", attUrl);
+ $("#diag-image-attachment").modal();
+ });
+ $(".image-attachment.video-attachment").click(function(e) {
+ e.preventDefault();
+ var attUrl = "?_action=attachment&attachment=" + encodeURIComponent($(this).attr("data-attachment-id"));
+ $("#video-attachment-view").attr("src", attUrl);
+ $("#video-attachment-dl-btn").attr("href", attUrl);
+ $("#diag-video-attachment").modal();
+ });
+ $("#diag-video-attachment").on("hide.bs.modal", function() {
+ $("#video-attachment-view")[0].pause();
+ });
+ });
+ </script>
+{% endif %}
+
+{% set formError = getAndClearFormError("updateTopic") %}
+{% if formError %}
+ {% include "components/alert_error.twig" with { message: formError } %}
+{% endif %}
+{% set formError = getAndClearFormError("lockTopic") %}
+{% if formError %}
+ {% include "components/alert_error.twig" with { message: formError } %}
+{% endif %}
+{% set formError = null %}
+
+<div class="page-header margin-top-0 clearfix">
+ <div id="displayHeading">
+ <div role="heading" class="h1 seamless-inline">
+ {% if ctx.topic.isLocked %}
+ <span class="fa fa-lock text-muted" aria-hidden="true"></span>
+ {% endif %}
+ {{ ctx.topic.title }}
+ <div class="pull-right text-normal">
+ {% if canEdit and not ctx.topic.isLocked %}
+ <button id="btn-edit-title" class="btn btn-default js-only"><span class="fa fa-pencil" aria-hidden="true"></span> {{ __("Edit title") }}</button>
+ {% endif %}
+ {% if canReply %}
+ <button id="btn-reply" class="btn btn-default js-only"><span class="fa fa-comment" aria-hidden="true"></span> {{ __("Reply") }}</button>
+ {% endif %}
+ {% if canEdit %}
+ {% if ctx.topic.isLocked %}
+ <form action="?_action=locktopic" method="post" class="seamless-inline">
+ <input type="hidden" name="topic" value="{{ ctx.topic.id }}">
+ <input type="hidden" name="locked" value="false">
+ <button type="submit" class="btn btn-success"><span class="fa fa-unlock" aria-hidden="true"></span> {{ __("Unlock topic") }}</button>
+ </form>
+ {% else %}
+ <form action="?_action=locktopic" method="post" class="seamless-inline">
+ <input type="hidden" name="topic" value="{{ ctx.topic.id }}">
+ <input type="hidden" name="locked" value="true">
+ <button type="submit" class="btn btn-warning"><span class="fa fa-lock" aria-hidden="true"></span> {{ __("Lock topic") }}</button>
+ </form>
+ {% endif %}
+ {% endif %}
+ {% if canDelete %}
+ <form action="?_action=deletetopic" method="post" class="seamless-inline">
+ <input type="hidden" name="topic" value="{{ ctx.topic.id }}">
+ <button type="submit" class="btn btn-danger"><span class="fa fa-trash" aria-hidden="true"></span> {{ __("Delete topic") }}</button>
+ </form>
+ {% endif %}
+ </div>
+ </div><br>
+ {{ __("Started by %user% on %date%", {
+ "user": (ctx.topicAuthor is not null) ? ('<a href="?_action=viewuser&amp;user=' ~ ctx.topicAuthor.id|url_encode|e("html") ~ '">' ~ ctx.topicAuthor.displayName|e("html") ~ '</a>') : __("(deleted)"),
+ "date": '<span class="_time">' ~ ctx.topic.creationDate.format("c")|e("html") ~ '</span>',
+ }) }}
+ </div>
+ {% if canEdit %}
+ <form action="?_action=updatetopic" method="post" id="editHeading" style="display: none;" class="form-inline seamless-inline">
+ <input type="hidden" name="topic" value="{{ ctx.topic.id }}">
+ <div class="row">
+ <div class="col-md-8">
+ <input type="text" class="form-control" name="title" id="i_edit_title" data-original-value="{{ ctx.topic.title }}" value="{{ ctx.topic.title }}" style="box-sizing: border-box; width: 100%; font-size: 36px; height: 56px">
+ </div>
+ <div class="col-md-4 text-right">
+ <button type="button" id="topicTitleEditCancel" class="btn btn-default"><span class="fa fa-close" aria-hidden="true"></span> {{ __("Cancel") }}</button>
+ <button type="submit" class="btn btn-success"><span class="fa fa-save" aria-hidden="true"></span> {{ __("Save changes") }}</button>
+ </div>
+ </div>
+ </form>
+ {% endif %}
+</div>
+<script>
+{% if canEdit %}
+$(function() {
+ $("#btn-edit-title").click(function() {
+ $("#displayHeading").hide();
+ $("#editHeading").show();
+ $("#i_edit_title").val($("#i_edit_title").attr("data-original-value")).focus();
+ });
+ $("#topicTitleEditCancel").click(function() {
+ $("#displayHeading").show();
+ $("#editHeading").hide();
+ });
+ $("._edit-post").click(function() {
+ var $post = $("#post-" + $(this).attr("data-post-id"));
+ var $postContent = $post.find(".post-content");
+ $("#i_edit_message").css("height", "").val($post.attr("data-text"));
+ $("#i_edit_post").val($(this).attr("data-post-id"));
+ $("#diag-edit-post").modal();
+ });
+ $("#diag-edit-post").on("shown.bs.modal", function() {
+ $("#i_edit_message").focus();
+ });
+});
+{% endif %}
+{% if canReply %}
+$(function() {
+ function focusReplyBox() {
+ var msgInput = $("#i_message");
+ msgInput[0].scrollIntoView();
+ msgInput.focus();
+ }
+ $("#btn-reply").click(function() {
+ focusReplyBox();
+ });
+ $("._reply-post").click(function() {
+ var text = $("#post-" + $(this).attr("data-post-id")).attr("data-text");
+ var val = $("#i_message").val();
+ var lines = text.split("\n");
+ for (var i = 0; i < lines.length; ++i)
+ val += "\n> " + lines[i];
+ val += "\n\n";
+ $("#i_message").val(val.replace(/^\n+/, ""));
+ focusReplyBox();
+ });
+});
+{% endif %}
+</script>
+
+{% for item in ctx.allItems %}
+ {% if item.type == "post" %}
+ {% include "components/post.twig" with {
+ post: item.post,
+ postAuthor: item.postAuthor,
+ topic: item.topic,
+ attachments: item.attachments,
+ hide_actions: false,
+ hide_pfp: false,
+ } %}
+ {% elseif item.type == "logMessage" %}
+ {% include "components/topic_log.twig" with {
+ type: item.type,
+ logMessage: item.logMessage,
+ postAuthor: item.postAuthor,
+ topicAuthor: item.topicAuthor,
+ topic: item.topic,
+ hide_actions: false,
+ hide_pfp: false,
+ } %}
+ {% endif %}
+{% endfor %}
+
+{% if ctx.topic.isLocked %}
+ <div class="well icon-well text-warning margin-top-4x">
+ <span class="fa fa-lock text-warning" aria-hidden="true"></span>
+ <em>{{ __("This topic has been locked") }}</em>
+ </div>
+{% elseif currentUser is not null %}
+ {% set formId = "addpost" %}
+ <h3 id="form">{{ __("Reply to this topic") }}</h3>
+ {% set formError = getAndClearFormError(formId) %}
+ {% if formError %}
+ {% include "components/alert_error.twig" with { message: formError } %}
+ {% endif %}
+ <form action="{{ g.server.REQUEST_URI }}#form" method="post" enctype="multipart/form-data">
+ <input type="hidden" name="form_id" value="{{ formId }}">
+ <div class="form-group">
+ <label for="i_message">{{ __("Message:") }}</label>
+ <div class="panel panel-default">
+ <div class="panel-heading" style="padding:4px">
+ <div class="btn-toolbar" role="toolbar">
+ <div class="btn-group" role="group">
+ <button data-area="#i_message" title="{{ __("Bold") }}" data-editor-command="bold" type="button" class="btn btn-default btn-xs"><i class="fa fa-fw fa-bold"></i></button>
+ <button data-area="#i_message" title="{{ __("Italic") }}" data-editor-command="italic" type="button" class="btn btn-default btn-xs"><i class="fa fa-fw fa-italic"></i></button>
+ <button data-area="#i_message" title="{{ __("Underlined") }}" data-editor-command="underline" type="button" class="btn btn-default btn-xs"><i class="fa fa-fw fa-underline"></i></button>
+ <button data-area="#i_message" title="{{ __("Strikethrough") }}" data-editor-command="strikethrough" type="button" class="btn btn-default btn-xs"><i class="fa fa-fw fa-strikethrough"></i></button>
+ </div>
+ <div class="btn-group" role="group">
+ <button data-area="#i_message" title="{{ __("Superscript") }}" data-editor-command="sup" type="button" class="btn btn-default btn-xs"><i class="fa fa-fw fa-superscript"></i></button>
+ <button data-area="#i_message" title="{{ __("Subscript") }}" data-editor-command="sub" type="button" class="btn btn-default btn-xs"><i class="fa fa-fw fa-subscript"></i></button>
+ </div>
+ <div class="btn-group" role="group">
+ <button data-area="#i_message" title="{{ __("Quote") }}" data-editor-command="quote" type="button" class="btn btn-default btn-xs"><i class="fa fa-fw fa-quote-left"></i></button>
+ <button data-area="#i_message" title="{{ __("Spoiler") }}" data-editor-command="spoiler" type="button" class="btn btn-default btn-xs"><i class="fa fa-fw fa-eye"></i></button>
+ </div>
+ </div>
+ </div>
+ <div class="panel-body" style="padding:0">
+ <textarea class="form-control" id="i_message" name="message" required rows="12" cols="60" style="resize:vertical;max-height:499px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;margin:-1px;width:calc(100% + 2px)"></textarea>
+ </div>
+ </div>
+ </div>
+ <div class="form-group">
+ <label for="i_files">{{ __("Attachments: <small>(max. %max_attachment_count% files, max. %max_attachment_size% MiB each)</small>", {
+ "max_attachment_count": constant("MAX_ATTACHMENT_COUNT"),
+ "max_attachment_size": constant("MAX_ATTACHMENT_SIZE") // (2**20),
+ }) }}</label>
+ <input type="file" name="files[]" id="i_files" multiple accept="*/*">
+ </div>
+ <button type="submit" class="btn btn-success">{{ __("Post reply") }} <span class="fa fa-send" aria-hidden="true"></span></button>
+ </form>
+{% else %}
+ <div class="well margin-top-4x text-center">
+ <div class="h3 margin-top-0">{{ __("Log in to reply to this topic") }}</div>
+ <a href="?_action=auth&amp;next={{ g.server.REQUEST_URI|url_encode }}" class="btn btn-success">
+ <span class="fa fa-user" aria-hidden="true"></span>
+ {{ __("Log in") }}
+ </a>
+ </div>
+{% endif %}
+
+
+{% endblock %}
diff --git a/src/application/templates/bootstrap-3/view_topics.twig b/src/application/templates/bootstrap-3/view_topics.twig
new file mode 100644
index 0000000..899ebe7
--- /dev/null
+++ b/src/application/templates/bootstrap-3/view_topics.twig
@@ -0,0 +1,25 @@
+{% extends "base.twig" %}
+
+{% block content %}
+
+{% if currentUser.hasPermission(permission("CREATE_OWN_TOPIC")) %}
+ <p class="text-right">
+ <a href="?_action=newtopic" class="btn btn-success"><span class="fa fa-plus" aria-hidden="true"></span> {{ __("New topic") }}</a>
+ </p>
+{% endif %}
+
+<div class="list-group">
+ {% for topic in ctx.topics %}
+ <a class="list-group-item" href="?_action=viewtopic&amp;topic={{ topic.id|url_encode }}">
+ <h4 class="list-group-item-heading">
+ {% if topic.isLocked %}
+ <span class="fa fa-lock text-muted" aria-hidden="true"></span>
+ {% endif %}
+ {{ topic.title }}
+ </h4>
+ <p class="list-group-item-text _time">{{ topic.creationDate.format("c") }}</p>
+ </a>
+ {% endfor %}
+</div>
+
+{% endblock %}
diff --git a/src/application/templates/bootstrap-3/view_user.twig b/src/application/templates/bootstrap-3/view_user.twig
new file mode 100644
index 0000000..b4c936c
--- /dev/null
+++ b/src/application/templates/bootstrap-3/view_user.twig
@@ -0,0 +1,201 @@
+{% set canEdit =
+ currentUser is not null
+ and (
+ (
+ ctx.user.id == currentUser.id
+ and currentUser.hasPermission(permission("EDIT_OWN_USER"))
+ )
+ or currentUser.hasPermission(permission("EDIT_OTHER_USER"))
+ ) %}
+
+{% set isOwnProfile =
+ currentUser is not null
+ and currentUser.id == ctx.user.id %}
+
+{% set sUserPossessive = isOwnProfile ? "Your Posts" : "%display_name%'s posts" %}
+
+{% set emailPending = isOwnProfile and ctx.user.pendingEmail is not null %}
+
+{% set title = ctx.user.displayName %}
+
+{% extends "base.twig" %}
+
+{% block content %}
+
+<div class="clearfix page-header margin-top-0">
+ <img class="pull-left margin-right" src="?_action=profilepicture&amp;user={{ ctx.user.id|url_encode }}" alt="{{ __("Profile picture") }}" width="64" height="64">
+ <span class="h1">{{ ctx.user.displayName }}</span>
+ {% if isOwnProfile %}
+ <span class="label label-primary">{{ __("You") }}</span>
+ {% endif %}<br>
+ @{{ ctx.user.name }} &bull; <span class="text-muted">{{ __("Member since %join_date%", {
+ "join_date": '<span class="_date">' ~ (ctx.dateJoined.format("c")|e("html")) ~ '</span>',
+ }) }}</span>
+</div>
+
+{% if canEdit %}
+<div class="row">
+<div class="col-md-9">
+{% endif %}
+
+<h3>{{ __(sUserPossessive, {
+ "display_name": ctx.user.displayName|e("html"),
+}) }}</h3>
+
+{% if ctx.posts|length > 0 %}
+
+ <div class="post-container">
+ <div class="post-container-posts">
+ <div class="list-group margin-top">
+ {% for post in ctx.posts %}
+ <a href="?_action=viewtopic&amp;topic={{ post.topicId|url_encode }}#post-{{ post.id|url_encode }}" class="list-group-item">
+ {% if hasAttachments %}
+ <span class="badge"><span class="fa fa-paperclip"></span></span>
+ {% endif %}
+ {{ renderPostSummary(post.content) }}<br>
+ <span class="text-muted">{{ __("posted on %post_date% in %topic%", {
+ "post_date": '<span class="_time">' ~ post.postDate.format("c")|e("html") ~ '</span>',
+ "topic": '<em>' ~
+ (ctx.topics[post.topicId] is not null and ctx.topics[post.topicId].isLocked ? '<span class="fa fa-lock text-muted" aria-hidden="true"></span> ' : '') ~
+ (ctx.topics[post.topicId] is not null ? ctx.topics[post.topicId].title : __("unknown"))|e("html") ~ '</em>',
+ }) }}</span>
+ </a>
+ {% endfor %}
+ </div>
+ </div>
+ <div class="post-container-controls">
+ <button class="btn btn-default">{{ __("Show all posts") }}</button>
+ </div>
+ </div>
+{% else %}
+ <div class="well icon-well text-info margin-top margin-bottom">
+ <span class="fa fa-info-circle text-info" aria-hidden="true"></span>
+ <em>{{ __("This user has not posted anything yet") }}</em>
+ </div>
+{% endif %}
+
+{% if canEdit %}
+</div> <!-- .col-md-9 -->
+
+<div class="col-md-3">
+ <h3>{{ __("Edit profile") }}</h3>
+ {% set formId = "update_profile" %}
+ {% set formError = getAndClearFormError(formId) %}
+ {% if formError %}
+ {% include "components/alert_error.twig" with { message: formError } %}
+ {% endif %}
+ <form action="{{ g.server.REQUEST_URI }}" method="post" enctype="multipart/form-data">
+ <input type="hidden" name="form_id" value="{{ formId }}">
+ <div class="form-group">
+ <label for="i_display_name">{{ __("Display name:") }}</label>
+ <input required class="form-control" type="text" name="display_name" id="i_display_name" value="{{ ctx.user.displayName }}">
+ </div>
+ <div class="form-group">
+ <label for="i_name">{{ __("Username:") }}</label>
+ {% if ctx.lastNameChangeTooRecent %}
+ <input class="form-control" type="text" id="i_name" value="{{ ctx.user.name }}" disabled>
+ <small class="text-danger"><strong>{{ __("You can only change your username every 30 days!") }}</strong></small>
+ {% else %}
+ <input required class="form-control" type="text" name="name" id="i_name" value="{{ ctx.user.name }}">
+ {% endif %}
+ </div>
+ <div class="form-group">
+ <label for="i_email">{{ __("Email address:") }}</label>
+ {% if emailPending %}
+ <input class="form-control" type="email" id="i_email" value="{{ ctx.user.email }}" disabled>
+ {% else %}
+ <input required class="form-control" type="email" id="i_email" name="email" value="{{ ctx.user.email }}">
+ {% endif %}
+ </div>
+ <div class="form-group">
+ <label>{{ __("Profile picture:") }}</label>
+ <div class="radio margin-top-0 {{ ctx.user.profilePicture is empty ? " disabled text-muted" }}">
+ <label>
+ <input type="radio" name="pfp_action" id="pfp_action_1" value="keep"{{ ctx.user.profilePicture is not empty ? ' checked' : ' disabled' }}>
+ {{ __("Keep current profile picture") }}
+ </label>
+ </div>
+ <div class="radio">
+ <label>
+ <input type="radio" name="pfp_action" id="pfp_action_2" value="remove"{{ ctx.user.profilePicture is empty ? ' checked' : '' }}>
+ {% if ctx.user.profilePicture is empty %}
+ {{ __("No profile picture") }}
+ {% else %}
+ {{ __("Remove profile picture") }}
+ {% endif %}
+ </label>
+ </div>
+ <div class="radio">
+ <label>
+ <input type="radio" name="pfp_action" value="replace" id="pfp_action_3">
+ {{ __("Upload new profile picture") }}
+ </label>
+ </div>
+ <input type="file" name="pfp" id="i_pfp" accept="image/png,image/jpeg" class="margin-left-3x small">
+ </div>
+ <div class="form-group">
+ <button type="submit" class="btn btn-success">{{ __("Save changes") }}</button>
+ </div>
+ </form>
+ {% if isOwnProfile %}
+ <h3>{{ __("Change password") }}</h3>
+ {% set formId = "update_password" %}
+ {% set formError = getAndClearFormError(formId) %}
+ {% if formError %}
+ {% include "components/alert_error.twig" with { message: formError } %}
+ {% endif %}
+ <form action="{{ g.server.REQUEST_URI }}" method="post">
+ <input type="hidden" name="form_id" value="{{ formId }}">
+ <div class="form-group">
+ <label for="i_current_password">{{ __("Current password:") }}</label>
+ <input autocomplete="current-password" required class="form-control" type="password" name="current_password" id="i_current_password" required>
+ </div>
+ <div class="form-group">
+ <label for="i_new_password">{{ __("New password:") }}</label>
+ <input autocomplete="new-password" required class="form-control" type="password" name="new_password" id="i_new_password" required>
+ </div>
+ <div class="form-group">
+ <label for="i_retype_password">{{ __("Retype password:") }}</label>
+ <input autocomplete="new-password" required class="form-control" type="password" name="retype_password" id="i_retype_password" required>
+ </div>
+ <div class="form-group">
+ <button type="submit" class="btn btn-success">{{ __("Change password") }}</button>
+ </div>
+ </form>
+ {% endif %}
+</div> <!-- .col-md-3 -->
+</div> <!-- .row -->
+{% endif %}
+
+<script>
+$(function() {
+ $(".post-container").each(function(i, e) {
+ if ($(e).height() > 900) { // more than 800 so it doesn't collapse just a few pixels
+ $(e).addClass("collapsed");
+ }
+ $(e).find(".post-container-controls button").click(function() {
+ $(e).removeClass("collapsed");
+ });
+ });
+});
+{% if canEdit %}
+$(function() {
+ function _hide() {
+ $("#i_pfp").hide().prop("disabled", true).prop("required", false);
+ $("#i_pfp + .file-input-group").hide().find("button").prop("disabled", true);
+ }
+ _hide();
+ setTimeout(_hide, 10);
+ $("[name='pfp_action']").on("change input check click", function() {
+ if ($("#pfp_action_3").is(":checked")) {
+ $("#i_pfp").show().prop("disabled", false).prop("required", true);
+ $("#i_pfp + .file-input-group").show().find("button").prop("disabled", false);
+ } else {
+ _hide();
+ }
+ })
+});
+{% endif %}
+</script>
+
+{% endblock %}