diff options
| author | Bobby <[email protected]> | 2025-07-19 15:22:22 +0530 |
|---|---|---|
| committer | Bobby <[email protected]> | 2025-07-19 15:22:22 +0530 |
| commit | d31111cf0133b223a8e665e6798b8ae09aa5c8a9 (patch) | |
| tree | 3f4d98333a4c7da4854b7482cdab80802a1dde59 | |
| parent | 1d582861feab127bdd588430d4d8374cf1d54fd5 (diff) | |
| download | imageboard-d31111cf0133b223a8e665e6798b8ae09aa5c8a9.tar.xz imageboard-d31111cf0133b223a8e665e6798b8ae09aa5c8a9.zip | |
post metadata update via edit
| -rw-r--r-- | controllers/posts.go | 151 | ||||
| -rw-r--r-- | controllers/register.go | 8 | ||||
| -rw-r--r-- | database/images.go | 4 | ||||
| -rw-r--r-- | database/posts.go | 2 | ||||
| -rw-r--r-- | database/user.go | 17 | ||||
| -rw-r--r-- | models/user.go | 5 | ||||
| -rw-r--r-- | router/routes.go | 5 | ||||
| -rw-r--r-- | static/css/main.css | 92 | ||||
| -rw-r--r-- | templates/posts/edit.django | 207 | ||||
| -rw-r--r-- | templates/posts/single.django | 19 |
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 %} |
