Multitag frontend update, bug fixes

* Checking the box to reset a post's UpdatedAt to CreatedAt now actually
  does so (removes the "updated at" label from front-end)
* Tagged Posts index now has better header formatting when multi-tags
  are in use.
  * No longer "Tagged as: hello,world,-mars"
  * Now is titled "Tagged Posts" and includes the lists of Tags and Not
    formatted nicely with clickable links.
* Fixes the "Read more..." link always appearing in HTML-type blog
  posts even when they didn't use the `<snip>` tag.
master
Noah 2020-02-26 12:49:39 -08:00
parent b57dfa9e64
commit 211b7d8318
4 changed files with 71 additions and 14 deletions

View File

@ -7,7 +7,6 @@ import (
"net/http"
"strconv"
"strings"
"time"
"git.kirsle.net/apps/gophertype/pkg/authentication"
"git.kirsle.net/apps/gophertype/pkg/glue"
@ -91,6 +90,11 @@ func BlogIndex(privacy string, tagged bool) http.HandlerFunc {
var (
v = responses.NewTemplateVars(w, r)
tagName string
// Multitag values.
isMultitag bool
include []string
exclude []string
)
// Tagged view?
@ -102,7 +106,17 @@ func BlogIndex(privacy string, tagged bool) http.HandlerFunc {
// Page title to use.
var title = "Blog"
if tagged {
// Check for use of multi-tags.
if strings.Contains(tagName, ",") {
title = "Tagged Posts"
if inc, exc, err := models.ParseMultitag(tagName); err == nil {
isMultitag = true
include = inc
exclude = exc
}
} else {
title = "Tagged as: " + tagName
}
} else if privacy == models.Draft {
title = "Drafts"
} else if privacy == models.Unlisted {
@ -113,6 +127,15 @@ func BlogIndex(privacy string, tagged bool) http.HandlerFunc {
v.V["title"] = title
v.V["tag"] = tagName
v.V["Multitag"] = struct {
Is bool
Include []string
Exclude []string
}{
isMultitag,
include,
exclude,
}
v.V["privacy"] = privacy
responses.RenderTemplate(w, r, "_builtin/blog/index.gohtml", v)
}
@ -257,13 +280,13 @@ func EditPost(w http.ResponseWriter, r *http.Request) {
post.AuthorID = author.ID
// When editing, allow to not touch the Last Updated time.
if !isNew && form.GetBool("no-update") == true {
post.UpdatedAt = post.CreatedAt
} else {
post.UpdatedAt = time.Now().UTC()
}
var resetUpdated bool = !isNew && form.GetBool("no-update") == true
err := post.Save()
if resetUpdated {
post.SetUpdated(post.CreatedAt)
}
if err != nil {
v.Error = err
} else {

View File

@ -81,7 +81,7 @@ func (m commentMan) LoadByDeleteToken(token string) (Comment, error) {
// GetIndex returns the index page of blog posts.
func (m commentMan) GetThread(thread string) ([]Comment, error) {
var coms []Comment
r := DB.Debug().Preload("User").
r := DB.Preload("User").
Where("thread = ?", thread).
Order("created_at asc").
Find(&coms)
@ -102,7 +102,7 @@ func (m commentMan) GetRecent(page int, perPage int) (PagedComments, error) {
pc.PerPage = 20
}
query := DB.Debug().Preload("User").Preload("Post").
query := DB.Preload("User").Preload("Post").
Order("created_at desc")
// Count the total number of rows.
@ -148,12 +148,12 @@ func (m commentMan) GetSubscribers(thread string) ([]string, error) {
func (m commentMan) UnsubscribeThread(thread string, email string) error {
// Verify the thread is valid.
var count int
DB.Debug().Model(&Comment{}).Where("thread=?", thread).Count(&count)
DB.Model(&Comment{}).Where("thread=?", thread).Count(&count)
if count == 0 {
return errors.New("invalid comment thread")
}
r := DB.Debug().Table("comments").Where("thread=? AND subscribe=?", thread, true).Updates(map[string]interface{}{
r := DB.Table("comments").Where("thread=? AND subscribe=?", thread, true).Updates(map[string]interface{}{
"subscribe": false,
})
return r.Error
@ -161,7 +161,7 @@ func (m commentMan) UnsubscribeThread(thread string, email string) error {
// UnsubscribeFromAll remove's an email subscription for ALL comment threads.
func (m commentMan) UnsubscribeFromAll(email string) error {
r := DB.Debug().Table("comments").Where("email=?", email).Updates(map[string]interface{}{
r := DB.Table("comments").Where("email=?", email).Updates(map[string]interface{}{
"subscribe": false,
})
return r.Error

View File

@ -90,7 +90,7 @@ func (m postMan) LoadFragment(fragment string) (Post, error) {
func (m postMan) LoadRandom(privacy string) (Post, error) {
// Find all the post IDs.
var pp []Post
r := DB.Debug().Select("id").Where("privacy = ?", privacy).Find(&pp)
r := DB.Select("id").Where("privacy = ?", privacy).Find(&pp)
if r.Error != nil || len(pp) == 0 {
return Post{}, r.Error
}
@ -386,7 +386,9 @@ func (p Post) PreviewHTML() template.HTML {
return template.HTML(markdown.RenderTrustedMarkdown(body))
}
if hasMore {
body += fmt.Sprintf(`<p><a href="/%s">Read more...</a></p>`, p.Fragment)
}
return template.HTML(body)
}
@ -466,6 +468,14 @@ func (p *Post) Save() error {
return DB.Save(&p).Error
}
// SetUpdated force sets the updated time of a post, i.e. to reset it to original.
func (p Post) SetUpdated(dt time.Time) error {
r := DB.Table("posts").Where("id = ?", p.ID).Updates(map[string]interface{}{
"updated_at": dt,
})
return r.Error
}
// ParseForm populates a Post from an HTTP form.
func (p *Post) ParseForm(form *forms.Data) {
p.Title = form.Get("title")

View File

@ -3,6 +3,30 @@
<h1>{{ or .V.title "Blog" }}</h1>
{{ if .V.Multitag.Is }}
<div class="alert alert-secondary">
<small><em>
{{ if .V.Multitag.Include }}
<span class="mr-2">
<strong>Tags:</strong>
{{ range $tag := .V.Multitag.Include }}
<a href="/tagged/{{ $tag }}" class="ml-2">#{{ $tag }}</a>
{{ end }}
</span>
{{ end }}
{{ if .V.Multitag.Exclude }}
<span class="mr-2">
<strong>Not:</strong>
{{ range $tag := .V.Multitag.Exclude }}
<a href="/tagged/{{ $tag }}" class="ml-2">#{{ $tag }}</a>
{{ end }}
</span>
{{ end }}
</em></small>
</div>
{{ end }}
{{ BlogIndex .Request .V.tag .V.privacy }}
{{ end }}