Link hashtags to Reader when Chorus mode enabled

instead of linking to posts only on a user's blog.

Ref T681
customize-reader
Matt Baer 5 years ago
parent 1a80cd3c02
commit ff7828c558
  1. 8
      activitypub.go
  2. 6
      app.go
  3. 6
      collections.go
  4. 13
      database.go
  5. 2
      export.go
  6. 6
      feed.go
  7. 23
      postrender.go
  8. 17
      posts.go
  9. 4
      read.go
  10. 2
      sitemap.go

@ -129,10 +129,10 @@ func handleFetchCollectionOutbox(app *App, w http.ResponseWriter, r *http.Reques
ocp := activitystreams.NewOrderedCollectionPage(accountRoot, "outbox", res.TotalPosts, p) ocp := activitystreams.NewOrderedCollectionPage(accountRoot, "outbox", res.TotalPosts, p)
ocp.OrderedItems = []interface{}{} ocp.OrderedItems = []interface{}{}
posts, err := app.db.GetPosts(c, p, false, true, false) posts, err := app.db.GetPosts(app.cfg, c, p, false, true, false)
for _, pp := range *posts { for _, pp := range *posts {
pp.Collection = res pp.Collection = res
o := pp.ActivityObject() o := pp.ActivityObject(app.cfg)
a := activitystreams.NewCreateActivity(o) a := activitystreams.NewCreateActivity(o)
ocp.OrderedItems = append(ocp.OrderedItems, *a) ocp.OrderedItems = append(ocp.OrderedItems, *a)
} }
@ -524,7 +524,7 @@ func deleteFederatedPost(app *App, p *PublicPost, collID int64) error {
} }
p.Collection.hostName = app.cfg.App.Host p.Collection.hostName = app.cfg.App.Host
actor := p.Collection.PersonObject(collID) actor := p.Collection.PersonObject(collID)
na := p.ActivityObject() na := p.ActivityObject(app.cfg)
// Add followers // Add followers
p.Collection.ID = collID p.Collection.ID = collID
@ -570,7 +570,7 @@ func federatePost(app *App, p *PublicPost, collID int64, isUpdate bool) error {
} }
} }
actor := p.Collection.PersonObject(collID) actor := p.Collection.PersonObject(collID)
na := p.ActivityObject() na := p.ActivityObject(app.cfg)
// Add followers // Add followers
p.Collection.ID = collID p.Collection.ID = collID

@ -224,14 +224,14 @@ func handleViewHome(app *App, w http.ResponseWriter, r *http.Request) error {
log.Error("unable to get landing banner: %v", err) log.Error("unable to get landing banner: %v", err)
return impart.HTTPError{http.StatusInternalServerError, fmt.Sprintf("Could not get banner: %v", err)} return impart.HTTPError{http.StatusInternalServerError, fmt.Sprintf("Could not get banner: %v", err)}
} }
p.Banner = template.HTML(applyMarkdown([]byte(banner.Content), "")) p.Banner = template.HTML(applyMarkdown([]byte(banner.Content), "", app.cfg))
content, err := getLandingBody(app) content, err := getLandingBody(app)
if err != nil { if err != nil {
log.Error("unable to get landing content: %v", err) log.Error("unable to get landing content: %v", err)
return impart.HTTPError{http.StatusInternalServerError, fmt.Sprintf("Could not get content: %v", err)} return impart.HTTPError{http.StatusInternalServerError, fmt.Sprintf("Could not get content: %v", err)}
} }
p.Content = template.HTML(applyMarkdown([]byte(content.Content), "")) p.Content = template.HTML(applyMarkdown([]byte(content.Content), "", app.cfg))
// Get error messages // Get error messages
session, err := app.sessionStore.Get(r, cookieName) session, err := app.sessionStore.Get(r, cookieName)
@ -279,7 +279,7 @@ func handleTemplatedPage(app *App, w http.ResponseWriter, r *http.Request, t *te
return err return err
} }
p.ContentTitle = c.Title.String p.ContentTitle = c.Title.String
p.Content = template.HTML(applyMarkdown([]byte(c.Content), "")) p.Content = template.HTML(applyMarkdown([]byte(c.Content), "", app.cfg))
p.PlainContent = shortPostDescription(stripmd.Strip(c.Content)) p.PlainContent = shortPostDescription(stripmd.Strip(c.Content))
if !c.Updated.IsZero() { if !c.Updated.IsZero() {
p.Updated = c.Updated.Format("January 2, 2006") p.Updated = c.Updated.Format("January 2, 2006")

@ -496,7 +496,7 @@ func fetchCollectionPosts(app *App, w http.ResponseWriter, r *http.Request) erro
} }
} }
posts, err := app.db.GetPosts(c, page, isCollOwner, false, false) posts, err := app.db.GetPosts(app.cfg, c, page, isCollOwner, false, false)
if err != nil { if err != nil {
return err return err
} }
@ -730,7 +730,7 @@ func handleViewCollection(app *App, w http.ResponseWriter, r *http.Request) erro
return impart.HTTPError{http.StatusFound, redirURL} return impart.HTTPError{http.StatusFound, redirURL}
} }
coll.Posts, _ = app.db.GetPosts(c, page, cr.isCollOwner, false, false) coll.Posts, _ = app.db.GetPosts(app.cfg, c, page, cr.isCollOwner, false, false)
// Serve collection // Serve collection
displayPage := CollectionPage{ displayPage := CollectionPage{
@ -825,7 +825,7 @@ func handleViewCollectionTag(app *App, w http.ResponseWriter, r *http.Request) e
coll := newDisplayCollection(c, cr, page) coll := newDisplayCollection(c, cr, page)
coll.Posts, _ = app.db.GetPostsTagged(c, tag, page, cr.isCollOwner) coll.Posts, _ = app.db.GetPostsTagged(app.cfg, c, tag, page, cr.isCollOwner)
if coll.Posts != nil && len(*coll.Posts) == 0 { if coll.Posts != nil && len(*coll.Posts) == 0 {
return ErrCollectionPageNotFound return ErrCollectionPageNotFound
} }

@ -29,6 +29,7 @@ import (
"github.com/writeas/web-core/log" "github.com/writeas/web-core/log"
"github.com/writeas/web-core/query" "github.com/writeas/web-core/query"
"github.com/writeas/writefreely/author" "github.com/writeas/writefreely/author"
"github.com/writeas/writefreely/config"
"github.com/writeas/writefreely/key" "github.com/writeas/writefreely/key"
) )
@ -105,8 +106,8 @@ type writestore interface {
ClaimPosts(userID int64, collAlias string, posts *[]ClaimPostRequest) (*[]ClaimPostResult, error) ClaimPosts(userID int64, collAlias string, posts *[]ClaimPostRequest) (*[]ClaimPostResult, error)
GetPostsCount(c *CollectionObj, includeFuture bool) GetPostsCount(c *CollectionObj, includeFuture bool)
GetPosts(c *Collection, page int, includeFuture, forceRecentFirst, includePinned bool) (*[]PublicPost, error) GetPosts(cfg *config.Config, c *Collection, page int, includeFuture, forceRecentFirst, includePinned bool) (*[]PublicPost, error)
GetPostsTagged(c *Collection, tag string, page int, includeFuture bool) (*[]PublicPost, error) GetPostsTagged(cfg *config.Config, c *Collection, tag string, page int, includeFuture bool) (*[]PublicPost, error)
GetAPFollowers(c *Collection) (*[]RemoteUser, error) GetAPFollowers(c *Collection) (*[]RemoteUser, error)
GetAPActorKeys(collectionID int64) ([]byte, []byte) GetAPActorKeys(collectionID int64) ([]byte, []byte)
@ -1067,7 +1068,7 @@ func (db *datastore) GetPostsCount(c *CollectionObj, includeFuture bool) {
// It will return future posts if `includeFuture` is true. // It will return future posts if `includeFuture` is true.
// It will include only standard (non-pinned) posts unless `includePinned` is true. // It will include only standard (non-pinned) posts unless `includePinned` is true.
// TODO: change includeFuture to isOwner, since that's how it's used // TODO: change includeFuture to isOwner, since that's how it's used
func (db *datastore) GetPosts(c *Collection, page int, includeFuture, forceRecentFirst, includePinned bool) (*[]PublicPost, error) { func (db *datastore) GetPosts(cfg *config.Config, c *Collection, page int, includeFuture, forceRecentFirst, includePinned bool) (*[]PublicPost, error) {
collID := c.ID collID := c.ID
cf := c.NewFormat() cf := c.NewFormat()
@ -1112,7 +1113,7 @@ func (db *datastore) GetPosts(c *Collection, page int, includeFuture, forceRecen
break break
} }
p.extractData() p.extractData()
p.formatContent(c, includeFuture) p.formatContent(cfg, c, includeFuture)
posts = append(posts, p.processPost()) posts = append(posts, p.processPost())
} }
@ -1128,7 +1129,7 @@ func (db *datastore) GetPosts(c *Collection, page int, includeFuture, forceRecen
// given tag. // given tag.
// It will return future posts if `includeFuture` is true. // It will return future posts if `includeFuture` is true.
// TODO: change includeFuture to isOwner, since that's how it's used // TODO: change includeFuture to isOwner, since that's how it's used
func (db *datastore) GetPostsTagged(c *Collection, tag string, page int, includeFuture bool) (*[]PublicPost, error) { func (db *datastore) GetPostsTagged(cfg *config.Config, c *Collection, tag string, page int, includeFuture bool) (*[]PublicPost, error) {
collID := c.ID collID := c.ID
cf := c.NewFormat() cf := c.NewFormat()
@ -1176,7 +1177,7 @@ func (db *datastore) GetPostsTagged(c *Collection, tag string, page int, include
break break
} }
p.extractData() p.extractData()
p.formatContent(c, includeFuture) p.formatContent(cfg, c, includeFuture)
posts = append(posts, p.processPost()) posts = append(posts, p.processPost())
} }

@ -118,7 +118,7 @@ func compileFullExport(app *App, u *User) *ExportUser {
var collObjs []CollectionObj var collObjs []CollectionObj
for _, c := range *colls { for _, c := range *colls {
co := &CollectionObj{Collection: c} co := &CollectionObj{Collection: c}
co.Posts, err = app.db.GetPosts(&c, 0, true, false, true) co.Posts, err = app.db.GetPosts(app.cfg, &c, 0, true, false, true)
if err != nil { if err != nil {
log.Error("unable to get collection posts: %v", err) log.Error("unable to get collection posts: %v", err)
} }

@ -55,9 +55,9 @@ func ViewFeed(app *App, w http.ResponseWriter, req *http.Request) error {
tag := mux.Vars(req)["tag"] tag := mux.Vars(req)["tag"]
if tag != "" { if tag != "" {
coll.Posts, _ = app.db.GetPostsTagged(c, tag, 1, false) coll.Posts, _ = app.db.GetPostsTagged(app.cfg, c, tag, 1, false)
} else { } else {
coll.Posts, _ = app.db.GetPosts(c, 1, false, true, false) coll.Posts, _ = app.db.GetPosts(app.cfg, c, 1, false, true, false)
} }
author := "" author := ""
@ -94,7 +94,7 @@ func ViewFeed(app *App, w http.ResponseWriter, req *http.Request) error {
Title: title, Title: title,
Link: &Link{Href: permalink}, Link: &Link{Href: permalink},
Description: "<![CDATA[" + stripmd.Strip(p.Content) + "]]>", Description: "<![CDATA[" + stripmd.Strip(p.Content) + "]]>",
Content: applyMarkdown([]byte(p.Content), ""), Content: applyMarkdown([]byte(p.Content), "", app.cfg),
Author: &Author{author, ""}, Author: &Author{author, ""},
Created: p.Created, Created: p.Created,
Updated: p.Updated, Updated: p.Updated,

@ -16,6 +16,7 @@ import (
stripmd "github.com/writeas/go-strip-markdown" stripmd "github.com/writeas/go-strip-markdown"
"github.com/writeas/saturday" "github.com/writeas/saturday"
"github.com/writeas/web-core/stringmanip" "github.com/writeas/web-core/stringmanip"
"github.com/writeas/writefreely/config"
"github.com/writeas/writefreely/parse" "github.com/writeas/writefreely/parse"
"html" "html"
"html/template" "html/template"
@ -34,27 +35,27 @@ var (
markeddownReg = regexp.MustCompile("<p>(.+)</p>") markeddownReg = regexp.MustCompile("<p>(.+)</p>")
) )
func (p *Post) formatContent(c *Collection, isOwner bool) { func (p *Post) formatContent(cfg *config.Config, c *Collection, isOwner bool) {
baseURL := c.CanonicalURL() baseURL := c.CanonicalURL()
if !isSingleUser { if !isSingleUser {
baseURL = "/" + c.Alias + "/" baseURL = "/" + c.Alias + "/"
} }
p.HTMLTitle = template.HTML(applyBasicMarkdown([]byte(p.Title.String))) p.HTMLTitle = template.HTML(applyBasicMarkdown([]byte(p.Title.String)))
p.HTMLContent = template.HTML(applyMarkdown([]byte(p.Content), baseURL)) p.HTMLContent = template.HTML(applyMarkdown([]byte(p.Content), baseURL, cfg))
if exc := strings.Index(string(p.Content), "<!--more-->"); exc > -1 { if exc := strings.Index(string(p.Content), "<!--more-->"); exc > -1 {
p.HTMLExcerpt = template.HTML(applyMarkdown([]byte(p.Content[:exc]), baseURL)) p.HTMLExcerpt = template.HTML(applyMarkdown([]byte(p.Content[:exc]), baseURL, cfg))
} }
} }
func (p *PublicPost) formatContent(isOwner bool) { func (p *PublicPost) formatContent(cfg *config.Config, isOwner bool) {
p.Post.formatContent(&p.Collection.Collection, isOwner) p.Post.formatContent(cfg, &p.Collection.Collection, isOwner)
} }
func applyMarkdown(data []byte, baseURL string) string { func applyMarkdown(data []byte, baseURL string, cfg *config.Config) string {
return applyMarkdownSpecial(data, false, baseURL) return applyMarkdownSpecial(data, false, baseURL, cfg)
} }
func applyMarkdownSpecial(data []byte, skipNoFollow bool, baseURL string) string { func applyMarkdownSpecial(data []byte, skipNoFollow bool, baseURL string, cfg *config.Config) string {
mdExtensions := 0 | mdExtensions := 0 |
blackfriday.EXTENSION_TABLES | blackfriday.EXTENSION_TABLES |
blackfriday.EXTENSION_FENCED_CODE | blackfriday.EXTENSION_FENCED_CODE |
@ -74,7 +75,11 @@ func applyMarkdownSpecial(data []byte, skipNoFollow bool, baseURL string) string
md := blackfriday.Markdown([]byte(data), blackfriday.HtmlRenderer(htmlFlags, "", ""), mdExtensions) md := blackfriday.Markdown([]byte(data), blackfriday.HtmlRenderer(htmlFlags, "", ""), mdExtensions)
if baseURL != "" { if baseURL != "" {
// Replace special text generated by Markdown parser // Replace special text generated by Markdown parser
md = []byte(hashtagReg.ReplaceAll(md, []byte("<a href=\""+baseURL+"tag:$1\" class=\"hashtag\"><span>#</span><span class=\"p-category\">$1</span></a>"))) tagPrefix := baseURL + "tag:"
if cfg.App.Chorus {
tagPrefix = "/read/t/"
}
md = []byte(hashtagReg.ReplaceAll(md, []byte("<a href=\""+tagPrefix+"$1\" class=\"hashtag\"><span>#</span><span class=\"p-category\">$1</span></a>")))
} }
// Strip out bad HTML // Strip out bad HTML
policy := getSanitizationPolicy() policy := getSanitizationPolicy()

@ -35,6 +35,7 @@ import (
"github.com/writeas/web-core/i18n" "github.com/writeas/web-core/i18n"
"github.com/writeas/web-core/log" "github.com/writeas/web-core/log"
"github.com/writeas/web-core/tags" "github.com/writeas/web-core/tags"
"github.com/writeas/writefreely/config"
"github.com/writeas/writefreely/page" "github.com/writeas/writefreely/page"
"github.com/writeas/writefreely/parse" "github.com/writeas/writefreely/parse"
) )
@ -376,7 +377,7 @@ func handleViewPost(app *App, w http.ResponseWriter, r *http.Request) error {
Direction: d, Direction: d,
} }
if !isRaw { if !isRaw {
post.HTMLContent = template.HTML(applyMarkdown([]byte(content), "")) post.HTMLContent = template.HTML(applyMarkdown([]byte(content), "", app.cfg))
} }
} }
@ -1032,7 +1033,7 @@ func fetchPost(app *App, w http.ResponseWriter, r *http.Request) error {
} }
p.Collection = &CollectionObj{Collection: *coll} p.Collection = &CollectionObj{Collection: *coll}
po := p.ActivityObject() po := p.ActivityObject(app.cfg)
po.Context = []interface{}{activitystreams.Namespace} po.Context = []interface{}{activitystreams.Namespace}
return impart.RenderActivityJSON(w, po, http.StatusOK) return impart.RenderActivityJSON(w, po, http.StatusOK)
} }
@ -1067,7 +1068,7 @@ func (p *PublicPost) CanonicalURL() string {
return p.Collection.CanonicalURL() + p.Slug.String return p.Collection.CanonicalURL() + p.Slug.String
} }
func (p *PublicPost) ActivityObject() *activitystreams.Object { func (p *PublicPost) ActivityObject(cfg *config.Config) *activitystreams.Object {
o := activitystreams.NewArticleObject() o := activitystreams.NewArticleObject()
o.ID = p.Collection.FederatedAPIBase() + "api/posts/" + p.ID o.ID = p.Collection.FederatedAPIBase() + "api/posts/" + p.ID
o.Published = p.Created o.Published = p.Created
@ -1078,7 +1079,7 @@ func (p *PublicPost) ActivityObject() *activitystreams.Object {
} }
o.Name = p.DisplayTitle() o.Name = p.DisplayTitle()
if p.HTMLContent == template.HTML("") { if p.HTMLContent == template.HTML("") {
p.formatContent(false) p.formatContent(cfg, false)
} }
o.Content = string(p.HTMLContent) o.Content = string(p.HTMLContent)
if p.Language.Valid { if p.Language.Valid {
@ -1092,9 +1093,13 @@ func (p *PublicPost) ActivityObject() *activitystreams.Object {
var tagBaseURL string var tagBaseURL string
if isSingleUser { if isSingleUser {
tagBaseURL = p.Collection.CanonicalURL() + "tag:" tagBaseURL = p.Collection.CanonicalURL() + "tag:"
} else {
if cfg.App.Chorus {
tagBaseURL = fmt.Sprintf("%s/read/t/", p.Collection.hostName)
} else { } else {
tagBaseURL = fmt.Sprintf("%s/%s/tag:", p.Collection.hostName, p.Collection.Alias) tagBaseURL = fmt.Sprintf("%s/%s/tag:", p.Collection.hostName, p.Collection.Alias)
} }
}
for _, t := range p.Tags { for _, t := range p.Tags {
o.Tag = append(o.Tag, activitystreams.Tag{ o.Tag = append(o.Tag, activitystreams.Tag{
Type: activitystreams.TagHashtag, Type: activitystreams.TagHashtag,
@ -1330,14 +1335,14 @@ func viewCollectionPost(app *App, w http.ResponseWriter, r *http.Request) error
fmt.Fprint(w, p.Content) fmt.Fprint(w, p.Content)
} else if strings.Contains(r.Header.Get("Accept"), "application/activity+json") { } else if strings.Contains(r.Header.Get("Accept"), "application/activity+json") {
p.extractData() p.extractData()
ap := p.ActivityObject() ap := p.ActivityObject(app.cfg)
ap.Context = []interface{}{activitystreams.Namespace} ap.Context = []interface{}{activitystreams.Namespace}
return impart.RenderActivityJSON(w, ap, http.StatusOK) return impart.RenderActivityJSON(w, ap, http.StatusOK)
} else { } else {
p.extractData() p.extractData()
p.Content = strings.Replace(p.Content, "<!--more-->", "", 1) p.Content = strings.Replace(p.Content, "<!--more-->", "", 1)
// TODO: move this to function // TODO: move this to function
p.formatContent(cr.isCollOwner) p.formatContent(app.cfg, cr.isCollOwner)
tp := struct { tp := struct {
*PublicPost *PublicPost
page.StaticPage page.StaticPage

@ -100,7 +100,7 @@ func (app *App) FetchPublicPosts() (interface{}, error) {
} }
p.extractData() p.extractData()
p.HTMLContent = template.HTML(applyMarkdown([]byte(p.Content), "")) p.HTMLContent = template.HTML(applyMarkdown([]byte(p.Content), "", app.cfg))
fp := p.processPost() fp := p.processPost()
if isCollectionPost { if isCollectionPost {
fp.Collection = &CollectionObj{Collection: *c} fp.Collection = &CollectionObj{Collection: *c}
@ -295,7 +295,7 @@ func viewLocalTimelineFeed(app *App, w http.ResponseWriter, req *http.Request) e
Title: title, Title: title,
Link: &Link{Href: permalink}, Link: &Link{Href: permalink},
Description: "<![CDATA[" + stripmd.Strip(p.Content) + "]]>", Description: "<![CDATA[" + stripmd.Strip(p.Content) + "]]>",
Content: applyMarkdown([]byte(p.Content), ""), Content: applyMarkdown([]byte(p.Content), "", app.cfg),
Author: &Author{author, ""}, Author: &Author{author, ""},
Created: p.Created, Created: p.Created,
Updated: p.Updated, Updated: p.Updated,

@ -66,7 +66,7 @@ func handleViewSitemap(app *App, w http.ResponseWriter, r *http.Request) error {
host = c.CanonicalURL() host = c.CanonicalURL()
sm := buildSitemap(host, pre) sm := buildSitemap(host, pre)
posts, err := app.db.GetPosts(c, 0, false, false, false) posts, err := app.db.GetPosts(app.cfg, c, 0, false, false, false)
if err != nil { if err != nil {
log.Error("Error getting posts: %v", err) log.Error("Error getting posts: %v", err)
return err return err

Loading…
Cancel
Save