Audio(s) and link(s) in feed content, improved parameter loading

This commit is contained in:
Jan-Lukas Else 2021-07-19 16:32:45 +02:00
parent 9db6ca5b42
commit 10bc7e7e7d
11 changed files with 79 additions and 68 deletions

View File

@ -113,7 +113,7 @@ func (a *goBlog) toASNote(p *post) *asNote {
as.Type = "Note" as.Type = "Note"
} }
// Content // Content
as.Content = string(a.absolutePostHTML(p)) as.Content = string(a.postHtml(p, true))
// Attachments // Attachments
if images := p.Parameters[a.cfg.Micropub.PhotoParam]; len(images) > 0 { if images := p.Parameters[a.cfg.Micropub.PhotoParam]; len(images) > 0 {
for _, image := range images { for _, image := range images {

View File

@ -127,7 +127,7 @@ func (a *goBlog) checkLinks(w io.Writer, posts ...*post) error {
func (a *goBlog) allLinks(posts ...*post) (allLinks []*stringPair, err error) { func (a *goBlog) allLinks(posts ...*post) (allLinks []*stringPair, err error) {
for _, p := range posts { for _, p := range posts {
links, err := allLinksFromHTMLString(string(a.absolutePostHTML(p)), a.fullPostURL(p)) links, err := allLinksFromHTMLString(string(a.postHtml(p, true)), a.fullPostURL(p))
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -17,10 +17,6 @@ const (
rssFeed feedType = "rss" rssFeed feedType = "rss"
atomFeed feedType = "atom" atomFeed feedType = "atom"
jsonFeed feedType = "json" jsonFeed feedType = "json"
feedAudioURL = "audio"
feedAudioType = "audiomime"
feedAudioLength = "audiolength"
) )
func (a *goBlog) generateFeed(blog string, f feedType, w http.ResponseWriter, r *http.Request, posts []*post, title string, description string) { func (a *goBlog) generateFeed(blog string, f feedType, w http.ResponseWriter, r *http.Request, posts []*post, title string, description string) {
@ -47,23 +43,14 @@ func (a *goBlog) generateFeed(blog string, f feedType, w http.ResponseWriter, r
for _, p := range posts { for _, p := range posts {
created, _ := dateparse.ParseLocal(p.Published) created, _ := dateparse.ParseLocal(p.Published)
updated, _ := dateparse.ParseLocal(p.Updated) updated, _ := dateparse.ParseLocal(p.Updated)
var enc *feeds.Enclosure
if p.firstParameter(feedAudioURL) != "" {
enc = &feeds.Enclosure{
Url: p.firstParameter(feedAudioURL),
Type: p.firstParameter(feedAudioType),
Length: p.firstParameter(feedAudioLength),
}
}
feed.Add(&feeds.Item{ feed.Add(&feeds.Item{
Title: p.Title(), Title: p.Title(),
Link: &feeds.Link{Href: a.fullPostURL(p)}, Link: &feeds.Link{Href: a.fullPostURL(p)},
Description: a.postSummary(p), Description: a.postSummary(p),
Id: p.Path, Id: p.Path,
Content: string(a.absolutePostHTML(p)), Content: string(a.postHtml(p, true)),
Created: created, Created: created,
Updated: updated, Updated: updated,
Enclosure: enc,
}) })
} }
var err error var err error

View File

@ -78,8 +78,8 @@ h1 a, h2 a {
text-decoration: none; text-decoration: none;
} }
img { img, audio {
width: 100%; @extend .fw;
} }
button, input, textarea, select { button, input, textarea, select {

View File

@ -4,11 +4,11 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"html/template"
"net/http" "net/http"
"reflect" "reflect"
"strconv" "strconv"
"strings" "strings"
"sync"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/microcosm-cc/bluemonday" "github.com/microcosm-cc/bluemonday"
@ -29,9 +29,9 @@ type post struct {
Status postStatus Status postStatus
Priority int Priority int
// Not persisted // Not persisted
Slug string Slug string
rendered template.HTML renderCache sync.Map
absoluteRendered template.HTML renderMutex sync.Mutex
} }
type postStatus string type postStatus string

View File

@ -311,7 +311,7 @@ func buildPostsQuery(c *postsRequestConfig, selection string) (query string, arg
return query, args return query, args
} }
func (d *database) getPostParameters(path string, parameters ...string) (params map[string][]string, err error) { func (d *database) loadPostParameters(posts []*post, parameters ...string) (err error) {
var sqlArgs []interface{} var sqlArgs []interface{}
// Parameter filter // Parameter filter
paramFilter := "" paramFilter := ""
@ -327,21 +327,44 @@ func (d *database) getPostParameters(path string, parameters ...string) (params
} }
paramFilter += ")" paramFilter += ")"
} }
// Path filter
pathFilter := ""
if len(posts) > 0 {
pathFilter = " and path in ("
for i, p := range posts {
if i > 0 {
pathFilter += ", "
}
named := fmt.Sprintf("path%v", i)
pathFilter += "@" + named
sqlArgs = append(sqlArgs, sql.Named(named, p.Path))
}
pathFilter += ")"
}
// Query // Query
rows, err := d.query("select parameter, value from post_parameters where path = @path"+paramFilter+" order by id", append(sqlArgs, sql.Named("path", path))...) rows, err := d.query("select path, parameter, value from post_parameters where 1 = 1"+paramFilter+pathFilter+" order by id", sqlArgs...)
if err != nil { if err != nil {
return nil, err return err
} }
// Result // Result
var name, value string var path, name, value string
params = map[string][]string{} params := map[string]map[string][]string{}
for rows.Next() { for rows.Next() {
if err = rows.Scan(&name, &value); err != nil { if err = rows.Scan(&path, &name, &value); err != nil {
return nil, err return err
} }
params[name] = append(params[name], value) m, ok := params[path]
if !ok {
m = map[string][]string{}
}
m[name] = append(m[name], value)
params[path] = m
} }
return params, nil // Add to posts
for _, p := range posts {
p.Parameters = params[p.Path]
}
return nil
} }
func (d *database) getPosts(config *postsRequestConfig) (posts []*post, err error) { func (d *database) getPosts(config *postsRequestConfig) (posts []*post, err error) {
@ -369,13 +392,14 @@ func (d *database) getPosts(config *postsRequestConfig) (posts []*post, err erro
Status: postStatus(status), Status: postStatus(status),
Priority: priority, Priority: priority,
} }
if !config.withoutParameters {
if p.Parameters, err = d.getPostParameters(path, config.withOnlyParameters...); err != nil {
return nil, err
}
}
posts = append(posts, p) posts = append(posts, p)
} }
if !config.withoutParameters {
err = d.loadPostParameters(posts, config.withOnlyParameters...)
if err != nil {
return nil, err
}
}
return posts, nil return posts, nil
} }

View File

@ -47,30 +47,40 @@ func firstPostParameter(p *post, parameter string) string {
return p.firstParameter(parameter) return p.firstParameter(parameter)
} }
func (a *goBlog) postHtml(p *post) template.HTML { func (a *goBlog) postHtml(p *post, absolute bool) template.HTML {
if p.rendered != "" { p.renderMutex.Lock()
return p.rendered defer p.renderMutex.Unlock()
// Check cache
if r, ok := p.renderCache.Load(absolute); ok && r != nil {
return r.(template.HTML)
} }
htmlContent, err := a.renderMarkdown(p.Content, false) // Render markdown
htmlContent, err := a.renderMarkdown(p.Content, absolute)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
return "" return ""
} }
p.rendered = template.HTML(htmlContent) htmlContentStr := string(htmlContent)
return p.rendered // Add audio to the top
} if audio, ok := p.Parameters["audio"]; ok && len(audio) > 0 {
audios := ""
func (a *goBlog) absolutePostHTML(p *post) template.HTML { for _, a := range audio {
if p.absoluteRendered != "" { audios += fmt.Sprintf(`<audio controls preload=none><source src="%s"/></audio>`, a)
return p.absoluteRendered }
htmlContentStr = audios + htmlContentStr
} }
htmlContent, err := a.renderMarkdown(p.Content, true) // Add links to the bottom
if err != nil { if link, ok := p.Parameters["link"]; ok && len(link) > 0 {
log.Fatal(err) links := ""
return "" for _, l := range link {
links += fmt.Sprintf(`<p><a class=u-bookmark-of href="%s" target=_blank rel=noopener>%s</a></p>`, l, l)
}
htmlContentStr += links
} }
p.absoluteRendered = template.HTML(htmlContent) // Cache
return p.absoluteRendered html := template.HTML(htmlContentStr)
p.renderCache.Store(absolute, html)
return html
} }
const summaryDivider = "<!--more-->" const summaryDivider = "<!--more-->"
@ -80,7 +90,7 @@ func (a *goBlog) postSummary(p *post) (summary string) {
if summary != "" { if summary != "" {
return return
} }
html := string(a.postHtml(p)) html := string(a.postHtml(p, false))
if splitted := strings.Split(html, summaryDivider); len(splitted) > 1 { if splitted := strings.Split(html, summaryDivider); len(splitted) > 1 {
doc, _ := goquery.NewDocumentFromReader(strings.NewReader(splitted[0])) doc, _ := goquery.NewDocumentFromReader(strings.NewReader(splitted[0]))
summary = doc.Text() summary = doc.Text()

View File

@ -54,10 +54,6 @@ h1 a, h2 a {
text-decoration: none; text-decoration: none;
} }
img {
width: 100%;
}
button, .button, input, textarea, select { button, .button, input, textarea, select {
border: 1px solid #000; border: 1px solid #000;
border: 1px solid var(--primary, #000); border: 1px solid var(--primary, #000);
@ -163,7 +159,7 @@ footer * {
display: inline; display: inline;
} }
.fw, .fw-form, .fw-form input:not([type]), .fw-form input[type=submit], .fw-form input[type=button], .fw-form input[type=text], .fw-form input[type=email], .fw-form input[type=url], .fw-form input[type=password], .fw-form input[type=file], .fw-form textarea, .fw-form select { .fw, img, audio, .fw-form, .fw-form input:not([type]), .fw-form input[type=submit], .fw-form input[type=button], .fw-form input[type=text], .fw-form input[type=email], .fw-form input[type=url], .fw-form input[type=password], .fw-form input[type=file], .fw-form textarea, .fw-form select {
width: 100%; width: 100%;
} }

View File

@ -14,13 +14,7 @@
{{ if .Data.Content }} {{ if .Data.Content }}
{{ include "oldcontentwarning" . }} {{ include "oldcontentwarning" . }}
<div class=e-content> <div class=e-content>
{{ with p .Data "audio" }} {{ content .Data false }}
<audio controls preload="none" class="fw"><source src="{{ . }}"/></audio>
{{ end }}
{{ content .Data }}
{{ with p .Data "link" }}
<p><a class="u-bookmark-of" href="{{ . }}" target="_blank" rel="noopener">{{ . }}</a></p>
{{ end }}
</div> </div>
{{ end }} {{ end }}
{{ include "posttax" . }} {{ include "posttax" . }}

View File

@ -9,7 +9,7 @@
<data class="u-url hide" value="{{ absolute .Data.Path }}"></data> <data class="u-url hide" value="{{ absolute .Data.Path }}"></data>
{{ if .Data.Content }} {{ if .Data.Content }}
<div class=e-content> <div class=e-content>
{{ content .Data }} {{ content .Data false }}
</div> </div>
{{ end }} {{ end }}
</article> </article>

View File

@ -24,7 +24,7 @@ func (a *goBlog) sendWebmentions(p *post) error {
return nil return nil
} }
links := []string{} links := []string{}
contentLinks, err := allLinksFromHTML(strings.NewReader(string(a.postHtml(p))), a.fullPostURL(p)) contentLinks, err := allLinksFromHTML(strings.NewReader(string(a.postHtml(p, false))), a.fullPostURL(p))
if err != nil { if err != nil {
return err return err
} }