summaryrefslogtreecommitdiff
path: root/src/application/templates/modern/view_topic.twig
diff options
context:
space:
mode:
Diffstat (limited to 'src/application/templates/modern/view_topic.twig')
-rw-r--r--src/application/templates/modern/view_topic.twig367
1 files changed, 367 insertions, 0 deletions
diff --git a/src/application/templates/modern/view_topic.twig b/src/application/templates/modern/view_topic.twig
new file mode 100644
index 0000000..733ce1b
--- /dev/null
+++ b/src/application/templates/modern/view_topic.twig
@@ -0,0 +1,367 @@
+{% 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 couldEditPost =
+ currentUser is not null
+ and (
+ currentUser.hasPermission(permission("EDIT_OWN_POST"))
+ or currentUser.hasPermission(permission("EDIT_OTHER_POST"))
+ ) %}
+
+{% 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 couldEditPost %}
+ <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="btn-danger close btn-iconic" data-dismiss="modal" aria-label="Close">
+ <svg viewBox="0 0 24 24" class="icon"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
+ </button>
+ <h4 class="modal-title">{{ __("Edit post") }}</h4>
+ </div>
+ <div class="modal-body edit-post-wrapper">
+ <label class="sr-only" for="i_edit_message">{{ __("Message:") }}</label>
+ {% include "components/richtext_editor.twig" with { id: "i_edit_message", name: "message" } %}
+ </div>
+ <div class="modal-footer">
+ <button type="button" data-dismiss="modal">
+ <svg viewBox="0 0 24 24" class="icon"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
+ <span>{{ __("Cancel") }}</span>
+ </button>
+ <button type="submit" class="btn btn-success">
+ <svg viewBox="0 0 24 24" class="icon"><path d="M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z"/><path d="M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7"/><path d="M7 3v4a1 1 0 0 0 1 1h7"/></svg>
+ <span>{{ __("Save changes") }}</span>
+ </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">
+ <div class="modal-header">
+ <h4 class="modal-title"><svg viewBox="0 0 24 24" class="icon icon-in-text"><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg> {{ __("Permission denied") }}</h4>
+ </div>
+ <div class="modal-body">
+ {{ __("You must be logged in to view attachments") }}
+ </div>
+ <div class="modal-footer">
+ <button type="button" data-dismiss="modal">
+ <svg viewBox="0 0 24 24" class="icon"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
+ <span>{{ __("Close") }}</span>
+ </button>
+ <a href="?_action=auth&amp;next={{ g.server.REQUEST_URI|url_encode }}" class="btn btn-success">
+ <svg viewBox="0 0 24 24" class="icon"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
+ <span>{{ __("Log in") }}</span>
+ </a>
+ </div>
+ </div>
+ </div>
+ </div>
+ <script>
+ document.addEventListener("DOMContentLoaded", function() {
+ document.querySelectorAll(".attachment").forEach(e => e.addEventListener("click", function(ev) {
+ ev.preventDefault();
+ document.querySelector("#diag-cant-view-attachment").classList.remove("fade");
+ }));
+ });
+ </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="btn-danger close btn-iconic" data-dismiss="modal" aria-label="Close">
+ <svg viewBox="0 0 24 24" class="icon"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
+ </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">
+ <svg viewBox="0 0 24 24" class="icon"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" x2="12" y1="15" y2="3"/></svg>
+ <span>{{ __("Download") }}</span>
+ </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="btn-danger close btn-iconic" data-dismiss="modal" aria-label="Close">
+ <svg viewBox="0 0 24 24" class="icon"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
+ </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">
+ <svg viewBox="0 0 24 24" class="icon"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" x2="12" y1="15" y2="3"/></svg>
+ <span>{{ __("Download") }}</span>
+ </a>
+ </div>
+ </div>
+ </div>
+ </div>
+ <script>
+ document.addEventListener("DOMContentLoaded", function() {
+ document.querySelectorAll(".image-attachment:not(.video-attachment)").forEach(e => e.addEventListener("click", function(ev) {
+ ev.preventDefault();
+ const attUrl = "?_action=attachment&attachment=" + encodeURIComponent(e.dataset.attachmentId);
+ document.querySelector("#image-attachment-view").src = attUrl;
+ document.querySelector("#image-attachment-dl-btn").href = attUrl;
+ document.querySelector("#diag-image-attachment").classList.remove("fade");
+ }));
+ document.querySelectorAll(".image-attachment.video-attachment").forEach(e => e.addEventListener("click", function(ev) {
+ ev.preventDefault();
+ const attUrl = "?_action=attachment&attachment=" + encodeURIComponent(e.dataset.attachmentId);
+ document.querySelector("#video-attachment-view").src = attUrl;
+ document.querySelector("#video-attachment-dl-btn").href = attUrl;
+ document.querySelector("#diag-video-attachment").classList.remove("fade");
+ }));
+ document.querySelector("#diag-video-attachment").addEventListener("hidemodal", function() {
+ document.querySelector("#video-attachment-view").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">
+ <div id="displayHeading">
+ <div role="heading" class="h1 seamless-inline">
+ {% if ctx.topic.isLocked %}
+ <svg viewBox="0 0 24 24" class="icon text-muted"><rect width="18" height="11" x="3" y="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
+ {% endif %}
+ {{ ctx.topic.title }}
+ <div class="title-controls pull-right text-normal">
+ {% if canEdit and not ctx.topic.isLocked %}
+ <button id="btn-edit-title" class="btn js-only btn-iconic" title="{{ __("Edit title") }}">
+ <svg viewBox="0 0 24 24" class="icon"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"/><path d="m15 5 4 4"/></svg>
+ </button>
+ {% endif %}
+ {% if canReply %}
+ <button id="btn-reply" class="btn js-only btn-iconic" title="{{ __("Reply") }}">
+ <svg viewBox="0 0 24 24" class="icon"><polyline points="9 17 4 12 9 7"/><path d="M20 18v-2a4 4 0 0 0-4-4H4"/></svg>
+ </button>
+ {% endif %}
+ {% if canEdit %}
+ {% if ctx.topic.isLocked %}
+ <form action="?_action=locktopic" method="post">
+ <input type="hidden" name="topic" value="{{ ctx.topic.id }}">
+ <input type="hidden" name="locked" value="false">
+ <button type="submit" class="btn btn-success btn-iconic" title="{{ __("Unlock topic") }}">
+ <svg viewBox="0 0 24 24" class="icon"><rect width="18" height="11" x="3" y="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 9.9-1"/></svg>
+ </button>
+ </form>
+ {% else %}
+ <form action="?_action=locktopic" method="post">
+ <input type="hidden" name="topic" value="{{ ctx.topic.id }}">
+ <input type="hidden" name="locked" value="true">
+ <button type="submit" class="btn btn-warning btn-iconic" title="{{ __("Lock topic") }}">
+ <svg viewBox="0 0 24 24" class="icon"><rect width="18" height="11" x="3" y="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
+ </button>
+ </form>
+ {% endif %}
+ {% endif %}
+ {% if canDelete %}
+ <form action="?_action=deletetopic" method="post">
+ <input type="hidden" name="topic" value="{{ ctx.topic.id }}">
+ <button type="submit" class="btn btn-danger btn-iconic" title="{{ __("Delete topic") }}">
+ <svg viewBox="0 0 24 24" class="icon"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/><line x1="10" x2="10" y1="11" y2="17"/><line x1="14" x2="14" y1="11" y2="17"/></svg>
+ </button>
+ </form>
+ {% endif %}
+ </div>
+ </div>
+ {{ __("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" hidden>
+ <input type="hidden" name="topic" value="{{ ctx.topic.id }}">
+ <div class="spring-row">
+ <div class="spring-fill h1">
+ <input type="text" name="title" id="i_edit_title" data-original-value="{{ ctx.topic.title }}" value="{{ ctx.topic.title }}">
+ </div>
+ <div class="spring-fit controls">
+ <button type="button" class="btn-iconic" id="topicTitleEditCancel" title="{{ __("Cancel") }}">
+ <svg viewBox="0 0 24 24" class="icon"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
+ </button>
+ <button type="submit" class="btn-iconic btn-success" title="{{ __("Save changes") }}">
+ <svg viewBox="0 0 24 24" class="icon"><path d="M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z"/><path d="M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7"/><path d="M7 3v4a1 1 0 0 0 1 1h7"/></svg>
+ </button>
+ </div>
+ </div>
+ </form>
+ {% endif %}
+</div>
+{% if canEdit %}
+<script>
+document.addEventListener("DOMContentLoaded", function() {
+ document.querySelector("#btn-edit-title").addEventListener("click", function() {
+ document.querySelector("#displayHeading").hidden = true;
+ document.querySelector("#editHeading").hidden = false;
+ const $title = document.querySelector("#i_edit_title")
+ $title.value = $title.dataset.originalValue;
+ $title.focus();
+ $title.selectionStart = $title.selectionEnd = $title.value.length;
+ });
+ document.querySelector("#topicTitleEditCancel").addEventListener("click", function() {
+ document.querySelector("#displayHeading").hidden = false;
+ document.querySelector("#editHeading").hidden = true;
+ });
+});
+</script>
+{% endif %}
+{% if couldEditPost %}
+<script>
+document.addEventListener("DOMContentLoaded", function() {
+ document.querySelectorAll("._edit-post").forEach(e => e.addEventListener("click", function() {
+ const $post = document.querySelector("[data-post-id='" + e.dataset.postId + "']");
+ const $edit = document.querySelector("#i_edit_message");
+ $edit.style.removeProperty("height");
+ $edit.value = $post.dataset.text;
+ document.querySelector("#i_edit_post").value = e.dataset.postId;
+ document.querySelector("#diag-edit-post").classList.remove("fade");
+ }));
+});
+</script>
+{% endif %}
+<script>
+{% if canReply %}
+document.addEventListener("DOMContentLoaded", function() {
+ function focusReplyBox() {
+ const msgInput = document.querySelector("#i_message");
+ msgInput.scrollIntoView();
+ msgInput.focus();
+ }
+ document.querySelector("#btn-reply").addEventListener("click", function() {
+ focusReplyBox();
+ });
+ document.querySelectorAll("._reply-post").forEach(e => e.addEventListener("click", function() {
+ const text = document.querySelector("#post-" + e.dataset.postId).dataset.text;
+ const lines = text.split("\n");
+ let val = document.querySelector("#i_message").value;
+ for (let i = 0; i < lines.length; ++i)
+ val += "\n> " + lines[i];
+ val += "\n\n";
+ document.querySelector("#i_message").value = 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,
+ topicAuthor: item.topicAuthor,
+ 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="topic-info-box warning topic-locked">
+ <svg viewBox="0 0 24 24" class="icon"><rect width="18" height="11" x="3" y="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
+ <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" class="post-reply">
+ <input type="hidden" name="form_id" value="{{ formId }}">
+ <div class="form-group">
+ <label for="i_message" class="sr-only">{{ __("Message:") }}</label>
+ {% include "components/richtext_editor.twig" with { id: "i_message", name: "message" } %}
+ </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">
+ <span>{{ __("Post reply") }}</span>
+ <svg viewBox="0 0 24 24" class="icon"><path d="M3.714 3.048a.498.498 0 0 0-.683.627l2.843 7.627a2 2 0 0 1 0 1.396l-2.842 7.627a.498.498 0 0 0 .682.627l18-8.5a.5.5 0 0 0 0-.904z"/><path d="M6 12h16"/></svg>
+ </button>
+ </form>
+{% else %}
+ <div class="topic-info-box success">
+ <h3>{{ __("Log in to reply to this topic") }}</h3>
+ <a href="?_action=auth&amp;next={{ g.server.REQUEST_URI|url_encode }}" class="btn btn-success">
+ <svg viewBox="0 0 24 24" class="icon"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
+ <span>{{ __("Log in") }}</span>
+ </a>
+ </div>
+{% endif %}
+
+
+{% endblock %}