mirror of https://github.com/jlelse/GoBlog
Support bridgy
This commit is contained in:
parent
6e8aeef10d
commit
659d6dcea3
|
@ -0,0 +1 @@
|
||||||
|
alter table webmentions add url text not null default "";
|
|
@ -3,11 +3,11 @@
|
||||||
<ul>
|
<ul>
|
||||||
{{ range $i, $mention := .Data }}
|
{{ range $i, $mention := .Data }}
|
||||||
<li>
|
<li>
|
||||||
<a href="{{$mention.Source}}" target="_blank" rel="nofollow noopener noreferrer ugc">
|
<a href="{{ $mention.Url }}" target="_blank" rel="nofollow noopener noreferrer ugc">
|
||||||
{{ if $mention.Author }}
|
{{ if $mention.Author }}
|
||||||
{{ $mention.Author }}
|
{{ $mention.Author }}
|
||||||
{{ else }}
|
{{ else }}
|
||||||
{{ $mention.Source }}
|
{{ $mention.Url }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</a>
|
</a>
|
||||||
{{ with $mention.Title }} <b>{{.}}</b>{{ end }}
|
{{ with $mention.Title }} <b>{{.}}</b>{{ end }}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
<div id="mention-{{ $mention.ID }}" class="p">
|
<div id="mention-{{ $mention.ID }}" class="p">
|
||||||
<p>
|
<p>
|
||||||
From: <a href="{{ $mention.Source }}" target="_blank" rel="noopener noreferrer">{{ $mention.Source }}</a><br/>
|
From: <a href="{{ $mention.Source }}" target="_blank" rel="noopener noreferrer">{{ $mention.Source }}</a><br/>
|
||||||
|
{{ if not (eq $mention.Source $mention.Url ) }}u-url: <a href="{{ $mention.Url }}" target="_blank" rel="noopener noreferrer">{{ $mention.Url }}</a><br/>{{ end }}
|
||||||
To: <a href="{{ $mention.Target }}" target="_blank">{{ $mention.Target }}</a><br/>
|
To: <a href="{{ $mention.Target }}" target="_blank">{{ $mention.Target }}</a><br/>
|
||||||
Created: {{ unixtodate $mention.Created }}<br/><br/>
|
Created: {{ unixtodate $mention.Created }}<br/><br/>
|
||||||
{{ if $mention.Author }}{{ $mention.Author }}<br/>{{ end }}
|
{{ if $mention.Author }}{{ $mention.Author }}<br/>{{ end }}
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="refresh" content="0;url=https://example.net/notice/ADYb7HhxE6UzPpfFiK">
|
||||||
|
<title>comment test</title>
|
||||||
|
</head>
|
||||||
|
<article class="h-entry">
|
||||||
|
<span class="p-uid">tag:example.net,2013:ADYb7HhxE6UzPpfFiK</span>
|
||||||
|
|
||||||
|
<time class="dt-published" datetime="2021-11-19T11:16:10.000Z">2021-11-19T11:16:10.000Z</time>
|
||||||
|
|
||||||
|
<span class="p-author h-card">
|
||||||
|
<data class="p-uid" value="tag:example.net,2013:m4rk"></data>
|
||||||
|
<data class="p-numeric-id" value="ADK9CwrdBhAeIowb5c"></data>
|
||||||
|
<a class="p-name u-url" href="https://example.net/users/m4rk">m4rk</a>
|
||||||
|
<a class="u-url" href="https://example.org"></a>
|
||||||
|
<a class="u-url" href="https://example.net/@m4rk"></a>
|
||||||
|
<span class="p-nickname">m4rk</span>
|
||||||
|
<img class="u-photo" src="https://example.net/media/dfee423b-b56b-42bf-8c0b-795cfc2a4776/blob" alt="" />
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<a class="u-url" href="https://example.net/notice/ADYb7HhxE6UzPpfFiK">https://example.net/notice/ADYb7HhxE6UzPpfFiK</a>
|
||||||
|
<div class="e-content p-name">
|
||||||
|
|
||||||
|
comment test
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<span class="u-category h-card">
|
||||||
|
<data class="p-uid" value="tag:example.net,2013:ADK9CwrdBhAeIowb5c"></data>
|
||||||
|
<a class="p-name u-url" href="https://example.net/users/m4rk">m4rk</a>
|
||||||
|
|
||||||
|
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<a class="u-in-reply-to" href="https://example.org/s/3"></a>
|
||||||
|
<a class="u-in-reply-to" href="https://example.org/walks/2021/11/9k-local-run"></a>
|
||||||
|
<a class="u-in-reply-to" href="https://example.net/web/statuses/ADYapxNFZfmlQbiYKm"></a>
|
||||||
|
|
||||||
|
|
||||||
|
</article>
|
||||||
|
|
||||||
|
</html>
|
|
@ -26,12 +26,14 @@ type mention struct {
|
||||||
NewSource string
|
NewSource string
|
||||||
Target string
|
Target string
|
||||||
NewTarget string
|
NewTarget string
|
||||||
|
Url string
|
||||||
Created int64
|
Created int64
|
||||||
Title string
|
Title string
|
||||||
Content string
|
Content string
|
||||||
Author string
|
Author string
|
||||||
Status webmentionStatus
|
Status webmentionStatus
|
||||||
Submentions []*mention
|
Submentions []*mention
|
||||||
|
hasUrl bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *goBlog) initWebmention() {
|
func (a *goBlog) initWebmention() {
|
||||||
|
@ -127,11 +129,12 @@ func (a *goBlog) createWebmention(source, target string) (err error) {
|
||||||
func (db *database) insertWebmention(m *mention, status webmentionStatus) error {
|
func (db *database) insertWebmention(m *mention, status webmentionStatus) error {
|
||||||
_, err := db.exec(
|
_, err := db.exec(
|
||||||
`
|
`
|
||||||
insert into webmentions (source, target, created, status, title, content, author)
|
insert into webmentions (source, target, url, created, status, title, content, author)
|
||||||
values (@source, @target, @created, @status, @title, @content, @author)
|
values (@source, @target, @url, @created, @status, @title, @content, @author)
|
||||||
`,
|
`,
|
||||||
sql.Named("source", m.Source),
|
sql.Named("source", m.Source),
|
||||||
sql.Named("target", m.Target),
|
sql.Named("target", m.Target),
|
||||||
|
sql.Named("url", m.Url),
|
||||||
sql.Named("created", m.Created),
|
sql.Named("created", m.Created),
|
||||||
sql.Named("status", status),
|
sql.Named("status", status),
|
||||||
sql.Named("title", m.Title),
|
sql.Named("title", m.Title),
|
||||||
|
@ -147,6 +150,7 @@ func (db *database) updateWebmention(m *mention, newStatus webmentionStatus) err
|
||||||
set
|
set
|
||||||
source = @newsource,
|
source = @newsource,
|
||||||
target = @newtarget,
|
target = @newtarget,
|
||||||
|
url = @url,
|
||||||
status = @status,
|
status = @status,
|
||||||
title = @title,
|
title = @title,
|
||||||
content = @content,
|
content = @content,
|
||||||
|
@ -157,6 +161,7 @@ func (db *database) updateWebmention(m *mention, newStatus webmentionStatus) err
|
||||||
`,
|
`,
|
||||||
sql.Named("newsource", defaultIfEmpty(m.NewSource, m.Source)),
|
sql.Named("newsource", defaultIfEmpty(m.NewSource, m.Source)),
|
||||||
sql.Named("newtarget", defaultIfEmpty(m.NewTarget, m.Target)),
|
sql.Named("newtarget", defaultIfEmpty(m.NewTarget, m.Target)),
|
||||||
|
sql.Named("url", m.Url),
|
||||||
sql.Named("status", newStatus),
|
sql.Named("status", newStatus),
|
||||||
sql.Named("title", m.Title),
|
sql.Named("title", m.Title),
|
||||||
sql.Named("content", m.Content),
|
sql.Named("content", m.Content),
|
||||||
|
@ -216,7 +221,7 @@ type webmentionsRequestConfig struct {
|
||||||
|
|
||||||
func buildWebmentionsQuery(config *webmentionsRequestConfig) (query string, args []interface{}) {
|
func buildWebmentionsQuery(config *webmentionsRequestConfig) (query string, args []interface{}) {
|
||||||
var queryBuilder strings.Builder
|
var queryBuilder strings.Builder
|
||||||
queryBuilder.WriteString("select id, source, target, created, title, content, author, status from webmentions ")
|
queryBuilder.WriteString("select id, source, target, url, created, title, content, author, status from webmentions ")
|
||||||
if config != nil {
|
if config != nil {
|
||||||
queryBuilder.WriteString("where 1")
|
queryBuilder.WriteString("where 1")
|
||||||
if config.target != "" {
|
if config.target != "" {
|
||||||
|
@ -258,10 +263,13 @@ func (db *database) getWebmentions(config *webmentionsRequestConfig) ([]*mention
|
||||||
}
|
}
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
m := &mention{}
|
m := &mention{}
|
||||||
err = rows.Scan(&m.ID, &m.Source, &m.Target, &m.Created, &m.Title, &m.Content, &m.Author, &m.Status)
|
err = rows.Scan(&m.ID, &m.Source, &m.Target, &m.Url, &m.Created, &m.Title, &m.Content, &m.Author, &m.Status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if m.Url == "" {
|
||||||
|
m.Url = m.Source
|
||||||
|
}
|
||||||
if config.submentions {
|
if config.submentions {
|
||||||
m.Submentions, err = db.getWebmentions(&webmentionsRequestConfig{
|
m.Submentions, err = db.getWebmentions(&webmentionsRequestConfig{
|
||||||
target: m.Source,
|
target: m.Source,
|
||||||
|
|
|
@ -201,7 +201,12 @@ func (a *goBlog) verifyReader(m *mention, body io.Reader) error {
|
||||||
m.Title = ""
|
m.Title = ""
|
||||||
m.Content = ""
|
m.Content = ""
|
||||||
m.Author = ""
|
m.Author = ""
|
||||||
|
m.Url = ""
|
||||||
|
m.hasUrl = false
|
||||||
m.fillFromData(microformats.Parse(&mfBuffer, sourceURL))
|
m.fillFromData(microformats.Parse(&mfBuffer, sourceURL))
|
||||||
|
if m.Url == "" {
|
||||||
|
m.Url = m.Source
|
||||||
|
}
|
||||||
// Set title when content is empty as well
|
// Set title when content is empty as well
|
||||||
if m.Title == "" && m.Content == "" {
|
if m.Title == "" && m.Content == "" {
|
||||||
doc, err := goquery.NewDocumentFromReader(&gqBuffer)
|
doc, err := goquery.NewDocumentFromReader(&gqBuffer)
|
||||||
|
@ -216,8 +221,11 @@ func (a *goBlog) verifyReader(m *mention, body io.Reader) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mention) fillFromData(mf *microformats.Data) {
|
func (m *mention) fillFromData(mf *microformats.Data) {
|
||||||
|
// Fill data
|
||||||
for _, i := range mf.Items {
|
for _, i := range mf.Items {
|
||||||
m.fill(i)
|
if m.fill(i) {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,8 +234,22 @@ func (m *mention) fill(mf *microformats.Microformat) bool {
|
||||||
// Check URL
|
// Check URL
|
||||||
if url, ok := mf.Properties["url"]; ok && len(url) > 0 {
|
if url, ok := mf.Properties["url"]; ok && len(url) > 0 {
|
||||||
if url0, ok := url[0].(string); ok {
|
if url0, ok := url[0].(string); ok {
|
||||||
if !strings.EqualFold(url0, defaultIfEmpty(m.NewSource, m.Source)) {
|
if strings.EqualFold(url0, defaultIfEmpty(m.NewSource, m.Source)) {
|
||||||
// Not correct URL
|
// Is searched entry
|
||||||
|
m.hasUrl = true
|
||||||
|
m.Url = url0
|
||||||
|
// Reset attributes to refill
|
||||||
|
m.Author = ""
|
||||||
|
m.Title = ""
|
||||||
|
m.Content = ""
|
||||||
|
} else if m.hasUrl {
|
||||||
|
// Already found entry
|
||||||
|
return false
|
||||||
|
} else if m.Url == "" {
|
||||||
|
// Is the first entry
|
||||||
|
m.Url = url0
|
||||||
|
} else {
|
||||||
|
// Is not the first entry
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,7 @@ func Test_verifyMention(t *testing.T) {
|
||||||
|
|
||||||
require.Equal(t, "https://example.org/articles/micropub-syndication-targets-and-crossposting-to-mastodon", m.Target)
|
require.Equal(t, "https://example.org/articles/micropub-syndication-targets-and-crossposting-to-mastodon", m.Target)
|
||||||
require.Equal(t, "https://example.net/articles/micropub-crossposting-to-twitter-and-enabling-tweetstorms", m.Source)
|
require.Equal(t, "https://example.net/articles/micropub-crossposting-to-twitter-and-enabling-tweetstorms", m.Source)
|
||||||
|
require.Equal(t, "https://example.net/articles/micropub-crossposting-to-twitter-and-enabling-tweetstorms", m.Url)
|
||||||
require.Equal(t, "Micropub, Crossposting to Twitter, and Enabling “Tweetsto…", m.Title)
|
require.Equal(t, "Micropub, Crossposting to Twitter, and Enabling “Tweetsto…", m.Title)
|
||||||
require.Equal(t, "I’ve previously talked about how I crosspost from this blog to my Mastodon account without the need for a third-party service, and how I leverage WordPress’s hook system to even enable toot threading. In this post, I’m going to really quickly explain my (extremely similar) Twitter setup. (Note: I don’t actually syndicate this blog’s posts to Twitter, but I do use this very setup on another site of mine.) I liked the idea of a dead-simple Twitter plugin, so I forked my Mastodon plugin and twea…", m.Content)
|
require.Equal(t, "I’ve previously talked about how I crosspost from this blog to my Mastodon account without the need for a third-party service, and how I leverage WordPress’s hook system to even enable toot threading. In this post, I’m going to really quickly explain my (extremely similar) Twitter setup. (Note: I don’t actually syndicate this blog’s posts to Twitter, but I do use this very setup on another site of mine.) I liked the idea of a dead-simple Twitter plugin, so I forked my Mastodon plugin and twea…", m.Content)
|
||||||
require.Equal(t, "Test Blogger", m.Author)
|
require.Equal(t, "Test Blogger", m.Author)
|
||||||
|
@ -57,3 +58,46 @@ func Test_verifyMention(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_verifyMentionBidgy(t *testing.T) {
|
||||||
|
|
||||||
|
testHtmlBytes, err := os.ReadFile("testdata/bridgy.html")
|
||||||
|
require.NoError(t, err)
|
||||||
|
testHtml := string(testHtmlBytes)
|
||||||
|
|
||||||
|
mockClient := &fakeHttpClient{}
|
||||||
|
mockClient.setFakeResponse(http.StatusOK, testHtml)
|
||||||
|
|
||||||
|
app := &goBlog{
|
||||||
|
httpClient: mockClient,
|
||||||
|
cfg: &config{
|
||||||
|
Db: &configDb{
|
||||||
|
File: filepath.Join(t.TempDir(), "test.db"),
|
||||||
|
},
|
||||||
|
Server: &configServer{
|
||||||
|
PublicAddress: "https://example.org",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
d: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// do nothing
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = app.initDatabase(false)
|
||||||
|
app.initComponents(false)
|
||||||
|
|
||||||
|
m := &mention{
|
||||||
|
Source: "https://example.com/abc",
|
||||||
|
Target: "https://example.org/walks/2021/11/9k-local-run",
|
||||||
|
}
|
||||||
|
|
||||||
|
err = app.verifyMention(m)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, "https://example.org/walks/2021/11/9k-local-run", m.Target)
|
||||||
|
require.Equal(t, "https://example.com/abc", m.Source)
|
||||||
|
require.Equal(t, "https://example.net/notice/ADYb7HhxE6UzPpfFiK", m.Url)
|
||||||
|
require.Equal(t, "comment test", m.Title)
|
||||||
|
require.Equal(t, "comment test", m.Content)
|
||||||
|
require.Equal(t, "m4rk", m.Author)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue