mirror of https://github.com/jlelse/GoBlog
Audio(s) and link(s) in feed content, improved parameter loading
This commit is contained in:
parent
9db6ca5b42
commit
10bc7e7e7d
|
@ -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 {
|
||||||
|
|
2
check.go
2
check.go
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
15
feeds.go
15
feeds.go
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
8
posts.go
8
posts.go
|
@ -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
|
||||||
|
|
52
postsDb.go
52
postsDb.go
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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" . }}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue