aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBobby <[email protected]>2025-07-19 15:22:22 +0530
committerBobby <[email protected]>2025-07-19 15:22:22 +0530
commitd31111cf0133b223a8e665e6798b8ae09aa5c8a9 (patch)
tree3f4d98333a4c7da4854b7482cdab80802a1dde59
parent1d582861feab127bdd588430d4d8374cf1d54fd5 (diff)
downloadimageboard-d31111cf0133b223a8e665e6798b8ae09aa5c8a9.tar.xz
imageboard-d31111cf0133b223a8e665e6798b8ae09aa5c8a9.zip
post metadata update via edit
-rw-r--r--controllers/posts.go151
-rw-r--r--controllers/register.go8
-rw-r--r--database/images.go4
-rw-r--r--database/posts.go2
-rw-r--r--database/user.go17
-rw-r--r--models/user.go5
-rw-r--r--router/routes.go5
-rw-r--r--static/css/main.css92
-rw-r--r--templates/posts/edit.django207
-rw-r--r--templates/posts/single.django19
10 files changed, 488 insertions, 22 deletions
diff --git a/controllers/posts.go b/controllers/posts.go
index 038ca1f..57738b5 100644
--- a/controllers/posts.go
+++ b/controllers/posts.go
@@ -4,6 +4,7 @@ import (
"errors"
"imageboard/config"
"imageboard/database"
+ "imageboard/models"
"imageboard/utils/auth"
"imageboard/utils/format"
"imageboard/utils/handlers"
@@ -307,7 +308,7 @@ func PostsSinglePostPageController(ctx *fiber.Ctx) error {
})
}
-func PostsSinglePostFavouriteController(ctx *fiber.Ctx) error {
+func PostsSinglePostFavouritePostController(ctx *fiber.Ctx) error {
if !auth.IsAuthenticated(ctx) {
return ctx.Redirect(auth.GetLoginURLWithNextField(ctx), fiber.StatusFound)
}
@@ -339,10 +340,10 @@ func PostsSinglePostFavouriteController(ctx *fiber.Ctx) error {
return InternalServerErrorController(ctx, err)
}
- return ctx.Redirect("/posts/" + postID)
+ return ctx.Redirect(auth.GetRedirectURL(ctx), fiber.StatusSeeOther)
}
-func PostsSinglePostEditController(ctx *fiber.Ctx) error {
+func PostsSinglePostEditPageController(ctx *fiber.Ctx) error {
if !auth.IsAuthenticated(ctx) {
return ctx.Redirect(auth.GetLoginURLWithNextField(ctx), fiber.StatusFound)
}
@@ -365,13 +366,151 @@ func PostsSinglePostEditController(ctx *fiber.Ctx) error {
return InternalServerErrorController(ctx, err)
}
- if post.Uploader.Username != auth.GetCurrentUser(ctx).Username || !auth.GetCurrentUser(ctx).CanEditPosts() {
+ currentUser := auth.GetCurrentUser(ctx)
+ if post.Uploader.Username != currentUser.Username && !currentUser.CanEditPosts() {
return ForbiddenController(ctx, errors.New("You do not have permission to edit this post"))
}
+ users, err := database.ListAllUsers()
+ if err != nil {
+ return InternalServerErrorController(ctx, err)
+ }
+ approvers, err := database.ListAllApprovers()
+ if err != nil {
+ return InternalServerErrorController(ctx, err)
+ }
+
+ postTags := make([]map[string]models.Tag, 0, len(post.Tags))
+ for _, tag := range post.Tags {
+ switch tag.Type {
+ case config.TagTypeGeneral:
+ postTags = append(postTags, map[string]models.Tag{"general": tag})
+ case config.TagTypeArtist:
+ postTags = append(postTags, map[string]models.Tag{"artist": tag})
+ case config.TagTypeCharacter:
+ postTags = append(postTags, map[string]models.Tag{"character": tag})
+ case config.TagTypeCopyright:
+ postTags = append(postTags, map[string]models.Tag{"copyright": tag})
+ case config.TagTypeMeta:
+ postTags = append(postTags, map[string]models.Tag{"meta": tag})
+ default:
+ postTags = append(postTags, map[string]models.Tag{"general": tag})
+ }
+ }
+
ctx.Locals("Title", config.PT_POST_EDIT+" #"+format.Int64ToString(int64(post.ID)))
return shortcuts.Render(ctx, config.TEMPLATE_POST_EDIT, fiber.Map{
- "Post": post,
- "CDNURL": format.GetCDNURL(),
+ "Post": post,
+ "CDNURL": format.GetCDNURL(),
+ "Users": users,
+ "Approvers": approvers,
+ "PostTags": postTags,
})
}
+
+func PostsSinglePostEditPostController(ctx *fiber.Ctx) error {
+ if !auth.IsAuthenticated(ctx) {
+ return ctx.Redirect(auth.GetLoginURLWithNextField(ctx), fiber.StatusFound)
+ }
+
+ postID := ctx.Params("id")
+ if postID == "" {
+ return NotFoundController(ctx)
+ }
+
+ uintPostID, err := format.StringToUint(postID)
+ if err != nil {
+ return NotFoundController(ctx)
+ }
+
+ post, err := database.GetPostByID(uintPostID)
+ if err != nil {
+ if err.Error() == "record not found" {
+ return NotFoundController(ctx)
+ }
+ return InternalServerErrorController(ctx, err)
+ }
+
+ currentUser := auth.GetCurrentUser(ctx)
+ if post.Uploader.Username != currentUser.Username && !currentUser.CanEditPosts() {
+ return ForbiddenController(ctx, errors.New("You do not have permission to edit this post"))
+ }
+
+ title := ctx.FormValue("title")
+ description := ctx.FormValue("description")
+ sourceURL := ctx.FormValue("source_url")
+ rating := ctx.FormValue("rating")
+
+ updates := make(map[string]interface{})
+
+ if title != post.Title {
+ updates["title"] = title
+ }
+
+ if description != post.Description {
+ updates["description"] = description
+ }
+
+ if sourceURL != post.SourceURL {
+ updates["source_url"] = sourceURL
+ }
+
+ if rating != "" && rating != string(post.Rating) {
+ ratingEnum, err := transformers.ConvertStringRatingToType(rating)
+ if err != nil {
+ return fiber.NewError(fiber.StatusBadRequest, "Invalid rating value")
+ }
+ updates["rating"] = ratingEnum
+ }
+
+ if currentUser.CanApprovePosts() {
+ isApproved := ctx.FormValue("is_approved") == "1"
+ if isApproved != post.IsApproved {
+ updates["is_approved"] = isApproved
+ }
+ }
+
+ if currentUser.CanDeletePosts() {
+ isDeleted := ctx.FormValue("is_deleted") == "1"
+ if isDeleted != post.IsDeleted {
+ updates["is_deleted"] = isDeleted
+ }
+ }
+
+ if currentUser.IsAdmin() {
+ uploaderID := ctx.FormValue("uploader")
+ if uploaderID != "" {
+ uintUploaderID, err := format.StringToUint(uploaderID)
+ if err == nil && uintUploaderID != post.UploaderID {
+ updates["uploader_id"] = uintUploaderID
+ }
+ }
+
+ approverID := ctx.FormValue("approver")
+ if approverID != "" {
+ if approverID == "0" {
+ if post.ApproverID != nil {
+ updates["approver_id"] = nil
+ }
+ } else {
+ uintApproverID, err := format.StringToUint(approverID)
+ if err == nil && (post.ApproverID == nil || *post.ApproverID != uintApproverID) {
+ updates["approver_id"] = uintApproverID
+ }
+ }
+ }
+ }
+
+ if len(updates) > 0 {
+ if err := database.UpdateImage(post.ID, updates); err != nil {
+ return InternalServerErrorController(ctx, err)
+ }
+ }
+
+ nextURL := ctx.FormValue("next")
+ if nextURL == "" {
+ nextURL = "/posts/" + format.Int64ToString(int64(post.ID))
+ }
+
+ return ctx.Redirect(nextURL, fiber.StatusSeeOther)
+}
diff --git a/controllers/register.go b/controllers/register.go
index 8952c91..6d1383f 100644
--- a/controllers/register.go
+++ b/controllers/register.go
@@ -58,9 +58,11 @@ func RegisterPostController(ctx *fiber.Ctx) error {
}
user := &models.User{
- Username: form.Username,
- Email: form.Email,
- Password: form.Password,
+ Username: form.Username,
+ Email: form.Email,
+ Password: form.Password,
+ PostsRequireApproval: true,
+ Level: config.UserLevelMember,
}
if err := database.CreateUser(user); err != nil {
diff --git a/database/images.go b/database/images.go
index 95b5339..40b6390 100644
--- a/database/images.go
+++ b/database/images.go
@@ -80,3 +80,7 @@ func CreateImageSizeWithTx(tx *gorm.DB, imageID uint, sizeType config.ImageSizeT
return &imageSize, nil
}
+
+func UpdateImage(imageID uint, updates map[string]interface{}) error {
+ return DB.Model(&models.Image{}).Where("id = ?", imageID).Updates(updates).Error
+}
diff --git a/database/posts.go b/database/posts.go
index 1aeaecd..9736feb 100644
--- a/database/posts.go
+++ b/database/posts.go
@@ -31,7 +31,7 @@ func GetPosts(limit int, ratings []config.Rating, tags []string) ([]models.Image
func GetPostByID(postID uint) (*models.Image, error) {
var post models.Image
- if err := DB.Preload("Sizes").Preload("Uploader").Preload("Tags").First(&post, postID).Error; err != nil {
+ if err := DB.Preload("Sizes").Preload("Uploader").Preload("Approver").Preload("Tags").First(&post, postID).Error; err != nil {
return nil, err
}
return &post, nil
diff --git a/database/user.go b/database/user.go
index 4fe7e18..cae46ae 100644
--- a/database/user.go
+++ b/database/user.go
@@ -1,6 +1,9 @@
package database
-import "imageboard/models"
+import (
+ "imageboard/config"
+ "imageboard/models"
+)
func GetUserByUsername(username string) (*models.User, error) {
var user models.User
@@ -10,6 +13,18 @@ func GetUserByUsername(username string) (*models.User, error) {
return &user, nil
}
+func ListAllUsers() ([]models.User, error) {
+ var users []models.User
+ err := DB.Where("is_deleted = ?", false).Order("LOWER(username) ASC").Find(&users).Error
+ return users, err
+}
+
+func ListAllApprovers() ([]models.User, error) {
+ var users []models.User
+ err := DB.Where("is_deleted = ? AND level >= ?", false, config.UserLevelJanitor).Order("LOWER(username) ASC").Find(&users).Error
+ return users, err
+}
+
func GetUserByID(userID uint) (*models.User, error) {
var user models.User
if err := DB.Where("id = ?", userID).First(&user).Error; err != nil {
diff --git a/models/user.go b/models/user.go
index 6ed3270..0fab1e7 100644
--- a/models/user.go
+++ b/models/user.go
@@ -34,7 +34,7 @@ type User struct {
}
func (u *User) BeforeCreate(tx *gorm.DB) error {
- u.Username = strings.TrimSpace(u.Username)
+ u.Username = strings.TrimSpace(strings.ToLower(u.Username))
u.Email = strings.TrimSpace(strings.ToLower(u.Email))
if u.Username == "" {
@@ -51,7 +51,8 @@ func (u *User) BeforeCreate(tx *gorm.DB) error {
}
if userCount == 0 {
- u.Level = config.UserLevelSuperAdmin // First user becomes Super Admin
+ u.Level = config.UserLevelSuperAdmin
+ u.PostsRequireApproval = false
}
if len(u.Username) < 3 && u.Level < config.UserLevelSuperAdmin {
diff --git a/router/routes.go b/router/routes.go
index 9d8c4fd..fd1f898 100644
--- a/router/routes.go
+++ b/router/routes.go
@@ -18,8 +18,9 @@ func Initialize(router *fiber.App) {
posts.Post("/new", controllers.PostsUploadPostController)
posts.Get("/new/ilinkfetch", controllers.PostsUploadImageLinkProxyController)
posts.Get("/:id", controllers.PostsSinglePostPageController)
- posts.Post("/:id/favourite", controllers.PostsSinglePostFavouriteController)
- posts.Get("/:id/edit", controllers.PostsSinglePostEditController)
+ posts.Post("/:id/favourite", controllers.PostsSinglePostFavouritePostController)
+ posts.Get("/:id/edit", controllers.PostsSinglePostEditPageController)
+ posts.Post("/:id/edit", controllers.PostsSinglePostEditPostController)
login := router.Group("/login")
login.Get("/", controllers.LoginPageController)
diff --git a/static/css/main.css b/static/css/main.css
index cdd39e5..640303c 100644
--- a/static/css/main.css
+++ b/static/css/main.css
@@ -198,6 +198,7 @@ main {
.itext,
input[type="text"],
+textarea,
input[type="email"],
input[type="password"],
input[type="number"],
@@ -210,6 +211,7 @@ input[type="url"] {
.itext:focus,
input[type="text"]:focus,
+textarea:focus,
input[type="email"]:focus,
input[type="password"]:focus,
input[type="number"]:focus,
@@ -219,6 +221,30 @@ input[type="url"]:focus {
outline: none;
}
+select {
+ background-color: #1a0033;
+ border: 1px solid #9999ff;
+ color: #ccccff;
+ padding: 3px 5px;
+ width: 100%;
+}
+
+select:focus {
+ border-color: #ff99cc;
+ background-color: #260040;
+ outline: none;
+}
+
+option {
+ background-color: #1a0033;
+ color: #ccccff;
+}
+
+option:hover {
+ background-color: #260040;
+ color: #ff99cc;
+}
+
input[type="button"],
button[type="button"],
input[type="submit"] {
@@ -352,7 +378,15 @@ footer::before {
gap: 4px;
}
-.fg-sub small {
+.fg-sub-radio {
+ display: flex;
+ flex-direction: row;
+ gap: 8px;
+ align-items: center;
+}
+
+.fg-sub small,
+.fg-sub-radio small {
color: #ff99cc;
}
@@ -363,6 +397,7 @@ footer::before {
}
.fg-sub input,
+.fg-sub textarea,
.fg-sub.itext {
border-style: double;
border-width: 3px;
@@ -370,6 +405,13 @@ footer::before {
width: 100%;
}
+.fg-sub select {
+ border-style: double;
+ border-width: 3px;
+ border-color: #9999ff;
+ width: 100%;
+}
+
.fbtngrp {
margin: 8px 0 0 0;
}
@@ -400,6 +442,13 @@ footer::before {
text-align: center;
}
+.info {
+ background-color: #001a33;
+ border: 1px solid #0066cc;
+ padding: 8px;
+ margin-bottom: 16px;
+}
+
.upload-drag-box {
border: 2px dashed #9999ff;
background-color: #1a0033;
@@ -765,6 +814,10 @@ footer::before {
color: #ff6b6b;
}
+.post-rating>a {
+ color: inherit;
+}
+
.post-rating.Safe {
color: #4caf50;
}
@@ -886,7 +939,6 @@ footer::before {
.post-detail-label {
color: #cccccc;
- white-space: nowrap;
}
.post-favourite-actions {
@@ -920,4 +972,40 @@ footer::before {
width: 16px;
height: 16px;
fill: currentColor;
+}
+
+.edit-post {
+ display: flex;
+ flex-direction: row;
+ height: 100%;
+ align-items: stretch;
+}
+
+.edit-main {
+ flex: 1;
+ padding-right: 8px;
+ border-right: 1px solid #4d4d80;
+}
+
+.edit-sidebar {
+ width: 264px;
+ flex-shrink: 0;
+ padding-left: 8px;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.edit-sidebar>.post-detail-item {
+ align-items: flex-start;
+}
+
+.edit-sidebar>.post-detail-item>.post-detail-label {
+ width: 72px;
+ flex-shrink: 0;
+}
+
+.edit-sidebar>.post-detail-item>.post-detail-value {
+ word-break: break-all;
} \ No newline at end of file
diff --git a/templates/posts/edit.django b/templates/posts/edit.django
index b4f13a4..93d582d 100644
--- a/templates/posts/edit.django
+++ b/templates/posts/edit.django
@@ -1,4 +1,209 @@
{% extends 'layouts/main.django' %}
{% block content %}
- Edit Post Page
+ <div class="edit-post">
+ <div class="edit-main">
+ {% if Post.Title %}
+ <h1 class="post-title">{{ Post.Title }}</h1>
+ {% else %}
+ <h1 class="post-title">Post #{{ Post.ID }}</h1>
+ {% endif %}
+ <form action="/posts/{{ Post.ID }}/edit" method="post" class="ibform">
+ {% if Error %}
+ <div class="error">{{ Error }}</div>
+ {% endif %}
+ <div class="fgroup">
+ <div class="fg-main">
+ <label for="title">Title</label>
+ </div>
+ <div class="fg-sub">
+ <input type="text" class="itext" id="title" name="title" value="{{ Post.Title }}" placeholder="e.g., My Awesome Post" />
+ <small>Optional title for the post. If left empty, the post will be titled "Post #{{ Post.ID }}".</small>
+ </div>
+ </div>
+ <div class="fgroup">
+ <div class="fg-main">
+ <label for="description">Description</label>
+ </div>
+ <div class="fg-sub">
+ <textarea id="description" name="description" class="itextarea" placeholder="Describe your post here..." rows="6">{{ Post.Description }}</textarea>
+ <small>Optional description for the post. This can be used to provide more context or details about the content of the post. <a href="/help/syntax" target="_blank">Learn more about syntax</a>.</small>
+ </div>
+ </div>
+ <div class="fgroup">
+ <div class="fg-main">
+ <label for="source_url">SourceURL</label>
+ </div>
+ <div class="fg-sub">
+ <input type="url" class="itext" id="source_url" name="source_url" value="{{ Post.SourceURL }}" placeholder="https://example.com/source" />
+ </div>
+ </div>
+ <div class="fgroup">
+ <div class="fg-main">
+ <label>Rating</label>
+ </div>
+ <div class="fg-sub-radio">
+ {% if 'Safe' in Post.Rating %}
+ <input type="radio" id="rating-safe" name="rating" value="Safe" checked />
+ {% else %}
+ <input type="radio" id="rating-safe" name="rating" value="Safe" />
+ {% endif %}
+ <label for="rating-safe">Safe</label>
+ {% if 'Sensitive' in Post.Rating %}
+ <input type="radio" id="rating-sensitive" name="rating" value="Sensitive" checked />
+ {% else %}
+ <input type="radio" id="rating-sensitive" name="rating" value="Sensitive" />
+ {% endif %}
+ <label for="rating-sensitive">Sensitive</label>
+ {% if 'Questionable' in Post.Rating %}
+ <input type="radio" id="rating-questionable" name="rating" value="Questionable" checked />
+ {% else %}
+ <input type="radio" id="rating-questionable" name="rating" value="Questionable" />
+ {% endif %}
+ <label for="rating-questionable">Questionable</label>
+ {% if 'Explicit' in Post.Rating %}
+ <input type="radio" id="rating-explicit" name="rating" value="Explicit" checked />
+ {% else %}
+ <input type="radio" id="rating-explicit" name="rating" value="Explicit" />
+ {% endif %}
+ <label for="rating-explicit">Explicit</label>
+ </div>
+ </div>
+ {% if User.CanApprovePosts %}
+ <div class="fgroup">
+ <div class="fg-main">
+ <label for="is_approved">Is Approved</label>
+ </div>
+ <div class="fg-sub-radio">
+ {% if Post.IsApproved %}
+ <input type="checkbox" id="is_approved" name="is_approved" value="1" checked="checked" />
+ {% else %}
+ <input type="checkbox" id="is_approved" name="is_approved" value="1" />
+ {% endif %}
+ <label for="is_approved"><small>Mark this post as approved. Approved posts won't be deleted after 3 days.</small></label>
+ </div>
+ </div>
+ {% endif %}
+ {% if User.CanDeletePosts %}
+ <div class="fgroup">
+ <div class="fg-main">
+ <label for="is_deleted">Is Deleted</label>
+ </div>
+ <div class="fg-sub-radio">
+ {% if Post.IsDeleted %}
+ <input type="checkbox" id="is_deleted" name="is_deleted" value="1" checked="checked" />
+ {% else %}
+ <input type="checkbox" id="is_deleted" name="is_deleted" value="1" />
+ {% endif %}
+ <label for="is_deleted"><small>Mark this post as deleted. Deleted posts are hidden from public view.</small></label>
+ </div>
+ </div>
+ {% endif %}
+ {% if User.IsAdmin %}
+ <div class="fgroup">
+ <div class="fg-main">
+ <label for="uploader">Uploader</label>
+ </div>
+ <div class="fg-sub">
+ <select id="uploader" name="uploader">
+ {% for u in Users %}
+ {% if u.ID == Post.UploaderID %}
+ <option value="{{ u.ID }}" selected>{{ u.Username }}</option>
+ {% else %}
+ <option value="{{ u.ID }}">{{ u.Username }}</option>
+ {% endif %}
+ {% endfor %}
+ </select>
+ </div>
+ </div>
+ <div class="fgroup">
+ <div class="fg-main">
+ <label for="approver">Approver</label>
+ </div>
+ <div class="fg-sub">
+ <select id="approver" name="approver">
+ {% if not Post.ApproverID %}
+ <option value="0" selected>---</option>
+ {% else %}
+ <option value="0">---</option>
+ {% endif %}
+ {% for a in Approvers %}
+ {% if a.ID == Post.ApproverID %}
+ <option value="{{ a.ID }}" selected>{{ a.Username }}</option>
+ {% else %}
+ <option value="{{ a.ID }}">{{ a.Username }}</option>
+ {% endif %}
+ {% endfor %}
+ </select>
+ </div>
+ </div>
+ {% endif %}
+ <input type="hidden" name="next" value="{{ Request.Path }}" />
+ <input type="submit" value="Save Changes" style="margin-top: 8px;" />
+ </form>
+ <h1>Tags</h1>
+ <div class="tag-list">
+ <div class="post-detail-item">
+ <span class="post-detail-label">General Tags:</span>
+ <span class="post-detail-value">
+ {% if PostTags.general|length > 0 %}
+ {% for tag in PostTags.general %}
+ <a href="/tags/{{ tag.Name }}" style="color: {{ tag.Type.Color }};">{{ tag.Name }}</a>
+ {% endfor %}
+ {% else %}
+ <span class="no-tags">No general tags</span>
+ {% endif %}
+ </span>
+ </div>
+ <input type="text" id="general-tag-input" class="itext" placeholder="Add general tag" />
+ <button type="button" id="add-general-tag" class="ib-button">Add</button>
+ </div>
+ </div>
+ <div class="edit-sidebar">
+ <img src="{{ CDNURL }}/thumbnail/{{ Post.FileName }}" alt="{{ Post.Title }}" width="{{ Post.Sizes.1.Width }}" height="{{ Post.Sizes.1.Height }}" />
+ <div class="post-detail-item">
+ <span class="post-detail-label">ID:</span>
+ <span class="post-detail-value">{{ Post.ID }}</span>
+ </div>
+ <div class="post-detail-item">
+ <span class="post-detail-label">Uploader:</span>
+ <span class="post-detail-value"><a href="/u/{{ Post.Uploader.Username }}">{{ Post.Uploader.Username }}</a></span>
+ </div>
+ <div class="post-detail-item">
+ <span class="post-detail-label">Approver:</span>
+ {% if Post.Approver.ID %}
+ <span class="post-detail-value"><a href="/u/{{ Post.Approver.Username }}">{{ Post.Approver.Username }}</a></span>
+ {% else %}
+ {% if Post.IsApproved %}
+ <span class="post-detail-value">N/A</span>
+ {% else %}
+ <span class="post-detail-value">Not Approved</span>
+ {% endif %}
+ {% endif %}
+ </div>
+ <div class="post-detail-item">
+ <span class="post-detail-label">Filename:</span>
+ <span class="post-detail-value">{{ Post.FileName }}</span>
+ </div>
+ <div class="post-detail-item">
+ <span class="post-detail-label">Type:</span>
+ <span class="post-detail-value">{{ Post.ContentType }}</span>
+ </div>
+ <div class="post-detail-item">
+ <span class="post-detail-label">MD5:</span>
+ <span class="post-detail-value">{{ Post.MD5Hash }}</span>
+ </div>
+ <div class="post-detail-item">
+ <span class="post-detail-label">ViewCount:</span>
+ <span class="post-detail-value">{{ Post.ViewCount }}</span>
+ </div>
+ <div class="post-detail-item">
+ <span class="post-detail-label">Favourites:</span>
+ <span class="post-detail-value post-favourite-actions">{{ Post.FavouriteCount }}</span>
+ </div>
+ <div class="post-detail-item">
+ <span class="post-detail-label">Comments:</span>
+ <span class="post-detail-value">{{ Post.CommentCount }}</span>
+ </div>
+ </div>
+ </div>
{% endblock %}
diff --git a/templates/posts/single.django b/templates/posts/single.django
index ec788c3..56fc670 100644
--- a/templates/posts/single.django
+++ b/templates/posts/single.django
@@ -83,10 +83,15 @@
<a href="javascript:void(0);" onclick="switchSize('large');">Large</a>
<a href="javascript:void(0);" onclick="switchSize('original');">Original</a>
{% if User and Post.Uploader.Username == User.Username or User.CanEditTags %}
- | <a href="/posts/{{ Post.ID }}/edit#tags">Edit Post</a>
+ | <a href="/posts/{{ Post.ID }}/edit">Edit Post</a>
{% endif %}
</div>
</div>
+ {% if not Post.IsApproved %}
+ <div class="info" style="margin-bottom: 12px;">
+ This post is pending approval. See <a href="/help/mod_queue">mod queues</a>.
+ </div>
+ {% endif %}
<div class="post-image-container">
<img src="{{ CDNURL }}/medium/{{ Post.FileName }}" alt="{{ Post.Title }}" id="post-image" />
</div>
@@ -108,11 +113,11 @@
<span class="post-detail-value"><a href="{{ CDNURL }}/original/{{ Post.FileName }}" target="_blank">{{ Post.GetOriginalSize.Width }}x{{ Post.GetOriginalSize.Height }} ({{ Post.GetOriginalSize.GetFileSizeFormatted }})</a></span>
</div>
<div class="post-detail-item">
- <span class="post-detail-label">Favourites:</span>
+ <span class="post-detail-label" id="favourites">Favourites:</span>
<span class="post-detail-value post-favourite-actions">
{{ Post.FavouriteCount }}
<form action="/posts/{{ Post.ID }}/favourite" method="post">
- <input type="hidden" name="next" value="{{ Request.Path }}" />
+ <input type="hidden" name="next" value="{{ Request.Path }}#favourites" />
<button type="submit" class="icon-button" title="{{ IsUserFavourited|yesno:'Unfavourite this post,Favourite this post' }}">
{% if IsUserFavourited %}
<svg viewBox="0 0 24 24">
@@ -129,7 +134,7 @@
</div>
<div class="post-detail-item">
<span class="post-detail-label">Rating:</span>
- <span class="post-detail-value post-rating {{ Post.Rating }}">{{ Post.Rating }}</span>
+ <span class="post-detail-value post-rating {{ Post.Rating }}"><a href="/posts?rating={{ Post.Rating }}">{{ Post.Rating }}</a></span>
</div>
</div>
<div class="post-details">
@@ -158,6 +163,12 @@
</span>
</div>
</div>
+ <div class="post-details">
+ <div class="post-detail-item">
+ <span class="post-detail-label">Description:</span>
+ <span class="post-detail-value">{{ Post.Description|default:'No description provided.' }}</span>
+ </div>
+ </div>
</div>
{% endif %}
{% endblock %}