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"
}
// Content
as.Content = string(a.absolutePostHTML(p))
as.Content = string(a.postHtml(p, true))
// Attachments
if images := p.Parameters[a.cfg.Micropub.PhotoParam]; len(images) > 0 {
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) {
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 {
return nil, err
}

View File

@ -17,10 +17,6 @@ const (
rssFeed feedType = "rss"
atomFeed feedType = "atom"
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) {
@ -47,23 +43,14 @@ func (a *goBlog) generateFeed(blog string, f feedType, w http.ResponseWriter, r
for _, p := range posts {
created, _ := dateparse.ParseLocal(p.Published)
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{
Title: p.Title(),
Link: &feeds.Link{Href: a.fullPostURL(p)},
Description: a.postSummary(p),
Id: p.Path,
Content: string(a.absolutePostHTML(p)),
Content: string(a.postHtml(p, true)),
Created: created,
Updated: updated,
Enclosure: enc,
})
}
var err error

View File

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

View File

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

View File

@ -311,7 +311,7 @@ func buildPostsQuery(c *postsRequestConfig, selection string) (query string, arg
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{}
// Parameter filter
paramFilter := ""
@ -327,21 +327,44 @@ func (d *database) getPostParameters(path string, parameters ...string) (params
}
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
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 {
return nil, err
return err
}
// Result
var name, value string
params = map[string][]string{}
var path, name, value string
params := map[string]map[string][]string{}
for rows.Next() {
if err = rows.Scan(&name, &value); err != nil {
return nil, err
if err = rows.Scan(&path, &name, &value); err != nil {
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) {
@ -369,13 +392,14 @@ func (d *database) getPosts(config *postsRequestConfig) (posts []*post, err erro
Status: postStatus(status),
Priority: priority,
}
if !config.withoutParameters {
if p.Parameters, err = d.getPostParameters(path, config.withOnlyParameters...); err != nil {
return nil, err
}
}
posts = append(posts, p)
}
if !config.withoutParameters {
err = d.loadPostParameters(posts, config.withOnlyParameters...)
if err != nil {
return nil, err
}
}
return posts, nil
}

View File

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

View File

@ -54,10 +54,6 @@ h1 a, h2 a {
text-decoration: none;
}
img {
width: 100%;
}
button, .button, input, textarea, select {
border: 1px solid #000;
border: 1px solid var(--primary, #000);
@ -163,7 +159,7 @@ footer * {
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%;
}

View File

@ -14,13 +14,7 @@
{{ if .Data.Content }}
{{ include "oldcontentwarning" . }}
<div class=e-content>
{{ with p .Data "audio" }}
<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 }}
{{ content .Data false }}
</div>
{{ end }}
{{ include "posttax" . }}

View File

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

View File

@ -24,7 +24,7 @@ func (a *goBlog) sendWebmentions(p *post) error {
return nil
}
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 {
return err
}