Extract Blog Thumbnails for Archive Page
This commit is contained in:
parent
c556f862e5
commit
7376947e8a
|
@ -17,6 +17,7 @@ func UpdateIndex(p *Post) error {
|
|||
type Index struct {
|
||||
Posts map[int]Post `json:"posts"`
|
||||
Fragments map[string]int `json:"fragments"`
|
||||
Thumbnails map[int]string `json:"thumbnails"`
|
||||
}
|
||||
|
||||
// GetIndex loads the index, or rebuilds it first if it doesn't exist.
|
||||
|
@ -35,6 +36,7 @@ func RebuildIndex() (*Index, error) {
|
|||
idx := &Index{
|
||||
Posts: map[int]Post{},
|
||||
Fragments: map[string]int{},
|
||||
Thumbnails: map[int]string{},
|
||||
}
|
||||
entries, _ := DB.List("blog/posts")
|
||||
for _, doc := range entries {
|
||||
|
@ -65,6 +67,12 @@ func (idx *Index) Update(p *Post) error {
|
|||
Updated: p.Updated,
|
||||
}
|
||||
idx.Fragments[p.Fragment] = p.ID
|
||||
|
||||
// Find a thumbnail image if possible.
|
||||
if thumb, ok := p.ExtractThumbnail(); ok {
|
||||
idx.Thumbnails[p.ID] = thumb
|
||||
}
|
||||
|
||||
err := DB.Commit("blog/index", idx)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -18,6 +18,12 @@ var DB *jsondb.DB
|
|||
|
||||
var log *golog.Logger
|
||||
|
||||
// Regexp used to parse a thumbnail image from a blog post. Looks for the first
|
||||
// URI component ending with an image extension.
|
||||
var (
|
||||
ThumbnailImageRegexp = regexp.MustCompile(`['"(]([a-zA-Z0-9-_:/?.=&]+\.(?:jpe?g|png|gif))['")]`)
|
||||
)
|
||||
|
||||
func init() {
|
||||
log = golog.GetLogger("blog")
|
||||
}
|
||||
|
@ -191,6 +197,16 @@ func (p *Post) Delete() error {
|
|||
return idx.Delete(p)
|
||||
}
|
||||
|
||||
// ExtractThumbnail searches and returns a thumbnail image to represent the
|
||||
// post. This will be the first image embedded in the post, or nothing.
|
||||
func (p *Post) ExtractThumbnail() (string, bool) {
|
||||
result := ThumbnailImageRegexp.FindStringSubmatch(p.Body)
|
||||
if len(result) < 2 {
|
||||
return "", false
|
||||
}
|
||||
return result[1], true
|
||||
}
|
||||
|
||||
// getNextID gets the next blog post ID.
|
||||
func (p *Post) nextID() int {
|
||||
// Highest ID seen so far.
|
||||
|
|
63
models/posts/thumbnail_test.go
Normal file
63
models/posts/thumbnail_test.go
Normal file
|
@ -0,0 +1,63 @@
|
|||
package posts_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/kirsle/blog/models/posts"
|
||||
)
|
||||
|
||||
func TestThumbnailRegexp(t *testing.T) {
|
||||
type testCase struct {
|
||||
Text string
|
||||
Expect string
|
||||
ExpectFail bool
|
||||
}
|
||||
|
||||
var tests = []testCase{
|
||||
{
|
||||
Text: "Hello world",
|
||||
ExpectFail: true,
|
||||
},
|
||||
{
|
||||
Text: "Some text.\n\n![An image](/static/photos/Image-1.jpg)\n" +
|
||||
"![Another image](/static/photos/Image-2.jpg)",
|
||||
Expect: "/static/photos/Image-1.jpg",
|
||||
},
|
||||
{
|
||||
Text: `<a href="/static/photos/12Abc456.jpg" target="_blank">` +
|
||||
`<img src="/static/photos/34Xyz123.jpg"></a>`,
|
||||
Expect: "/static/photos/12Abc456.jpg",
|
||||
},
|
||||
{
|
||||
Text: `A markdown image: ![With text](/test1.gif) and an HTML ` +
|
||||
`image: <img src="/test2.png">`,
|
||||
Expect: "/test1.gif",
|
||||
},
|
||||
{
|
||||
Text: `<a href="https://google.com/"><img src="https://example.com/logo.gif?query=string.jpg"></a>`,
|
||||
Expect: "https://example.com/logo.gif?query=string.jpg",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
p := &posts.Post{
|
||||
Body: test.Text,
|
||||
}
|
||||
|
||||
result, ok := p.ExtractThumbnail()
|
||||
if !ok && !test.ExpectFail {
|
||||
t.Errorf("Text: %s\nExpected to fail, but did not!\nGot: %s",
|
||||
test.Text,
|
||||
result,
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
if result != test.Expect {
|
||||
t.Errorf("Text: %s\nExpect: %s\nGot: %s",
|
||||
test.Text,
|
||||
test.Expect,
|
||||
result,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,22 +3,40 @@
|
|||
|
||||
<h1>Archive</h1>
|
||||
|
||||
{{ range .Data.Archive }}
|
||||
<h3>{{ .Date.Format "January, 2006" }}</h3>
|
||||
{{ $thumbs := .Data.Thumbnails }}
|
||||
|
||||
<ul class="list-unstyled">
|
||||
{{ range .Data.Archive }}
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h3>{{ .Date.Format "January, 2006" }}</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<div class="row">
|
||||
{{ range .Posts }}
|
||||
<li class="list-item">
|
||||
<a href="/{{ .Fragment }}">{{ .Title }}</a>
|
||||
{{ $thumb := index $thumbs .ID }}
|
||||
<div class="col-12 col-sm-6 col-md-4 col-lg-3 mb-4">
|
||||
<div class="card bg-secondary"
|
||||
style="height: auto; min-height: 150px;
|
||||
{{ if $thumb }}background-image: url({{ $thumb }}); background-size: cover{{ end }}
|
||||
"
|
||||
title="Tags: {{ range .Tags }}#{{ . }} {{ end }}">
|
||||
<span class="p-1" style="background-color: RGBA(255, 255, 255, 0.8)">
|
||||
<a href="/{{ .Fragment }}">{{ .Title }}</a><br>
|
||||
<small class="blog-meta">
|
||||
{{ .Created.Format "Jan 02 2006" }}
|
||||
{{ if ne .Privacy "public" }}
|
||||
<span class="blog-{{ .Privacy }}">[{{ .Privacy }}]</span>
|
||||
{{ end }}
|
||||
</small>
|
||||
</li>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
||||
|
|
|
@ -5,8 +5,8 @@ import (
|
|||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/kirsle/blog/src/middleware/auth"
|
||||
"github.com/kirsle/blog/models/posts"
|
||||
"github.com/kirsle/blog/src/middleware/auth"
|
||||
"github.com/kirsle/blog/src/render"
|
||||
"github.com/kirsle/blog/src/responses"
|
||||
"github.com/kirsle/blog/src/types"
|
||||
|
@ -55,6 +55,7 @@ func archiveHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
v := map[string]interface{}{
|
||||
"Archive": result,
|
||||
"Thumbnails": idx.Thumbnails,
|
||||
}
|
||||
render.Template(w, r, "blog/archive", v)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user