Handles are saved in `remoteusers` while the links take you to an

intermediate page (WIP) that shows the user profile page url
This commit is contained in:
Michael Demetriou 2019-10-10 15:11:46 +03:00
parent e5bbd45b49
commit 99bb77153e
5 changed files with 82 additions and 17 deletions

View File

@ -26,7 +26,6 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/writeas/activity/streams" "github.com/writeas/activity/streams"
"github.com/writeas/activityserve"
"github.com/writeas/httpsig" "github.com/writeas/httpsig"
"github.com/writeas/impart" "github.com/writeas/impart"
"github.com/writeas/nerds/store" "github.com/writeas/nerds/store"
@ -45,6 +44,7 @@ type RemoteUser struct {
ActorID string ActorID string
Inbox string Inbox string
SharedInbox string SharedInbox string
Handle string
} }
func (ru *RemoteUser) AsPerson() *activitystreams.Person { func (ru *RemoteUser) AsPerson() *activitystreams.Person {
@ -133,7 +133,7 @@ func handleFetchCollectionOutbox(app *App, w http.ResponseWriter, r *http.Reques
posts, err := app.db.GetPosts(app.cfg, 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(app.cfg) o := pp.ActivityObject(app)
a := activitystreams.NewCreateActivity(o) a := activitystreams.NewCreateActivity(o)
ocp.OrderedItems = append(ocp.OrderedItems, *a) ocp.OrderedItems = append(ocp.OrderedItems, *a)
} }
@ -525,7 +525,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(app.cfg) na := p.ActivityObject(app)
// Add followers // Add followers
p.Collection.ID = collID p.Collection.ID = collID
@ -571,7 +571,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(app.cfg) na := p.ActivityObject(app)
// Add followers // Add followers
p.Collection.ID = collID p.Collection.ID = collID
@ -626,8 +626,7 @@ func federatePost(app *App, p *PublicPost, collID int64, isUpdate bool) error {
// the mentioned users. This might seem wasteful but the code is // the mentioned users. This might seem wasteful but the code is
// cleaner than adding the mentioned users to CC here instead of // cleaner than adding the mentioned users to CC here instead of
// in p.ActivityObject() // in p.ActivityObject()
na = p.ActivityObject(app.cfg) na = p.ActivityObject(app)
for _, tag := range na.Tag { for _, tag := range na.Tag {
if tag.Type == "Mention" { if tag.Type == "Mention" {
activity = activitystreams.NewCreateActivity(na) activity = activitystreams.NewCreateActivity(na)
@ -638,11 +637,11 @@ func federatePost(app *App, p *PublicPost, collID int64, isUpdate bool) error {
// much logic to catch this at the expense of the odd extra request. // much logic to catch this at the expense of the odd extra request.
// I don't believe we'd ever have too many mentions in a single post that this // I don't believe we'd ever have too many mentions in a single post that this
// could become a burden. // could become a burden.
remoteActor, err := activityserve.NewRemoteActor(tag.HRef)
if err != nil { fmt.Println(tag.HRef)
log.Error("Couldn't fetch remote actor", err) fmt.Println("aaa")
} remoteUser, err := getRemoteUser(app, tag.HRef)
err = makeActivityPost(app.cfg.App.Host, actor, remoteActor.GetInbox(), activity) err = makeActivityPost(app.cfg.App.Host, actor, remoteUser.Inbox, activity)
if err != nil { if err != nil {
log.Error("Couldn't post! %v", err) log.Error("Couldn't post! %v", err)
} }
@ -656,7 +655,7 @@ func federatePost(app *App, p *PublicPost, collID int64, isUpdate bool) error {
func getRemoteUser(app *App, actorID string) (*RemoteUser, error) { func getRemoteUser(app *App, actorID string) (*RemoteUser, error) {
u := RemoteUser{ActorID: actorID} u := RemoteUser{ActorID: actorID}
err := app.db.QueryRow("SELECT id, inbox, shared_inbox FROM remoteusers WHERE actor_id = ?", actorID).Scan(&u.ID, &u.Inbox, &u.SharedInbox) err := app.db.QueryRow("SELECT id, inbox, shared_inbox, handle FROM remoteusers WHERE actor_id = ?", actorID).Scan(&u.ID, &u.Inbox, &u.SharedInbox, &u.Handle)
switch { switch {
case err == sql.ErrNoRows: case err == sql.ErrNoRows:
return nil, impart.HTTPError{http.StatusNotFound, "No remote user with that ID."} return nil, impart.HTTPError{http.StatusNotFound, "No remote user with that ID."}
@ -668,6 +667,21 @@ func getRemoteUser(app *App, actorID string) (*RemoteUser, error) {
return &u, nil return &u, nil
} }
// getRemoteUserFromHandle retrieves the profile page of a remote user
// from the @user@server.tld handle
func getRemoteUserFromHandle(app *App, handle string) (*RemoteUser, error) {
u := RemoteUser{Handle: handle}
err := app.db.QueryRow("SELECT id, actor_id, inbox, shared_inbox FROM remoteusers WHERE handle = ?", handle).Scan(&u.ID, &u.ActorID, &u.Inbox, &u.SharedInbox)
switch {
case err == sql.ErrNoRows:
return nil, impart.HTTPError{http.StatusNotFound, "No remote user with that handle."}
case err != nil:
log.Error("Couldn't get remote user %s: %v", handle, err)
return nil, err
}
return &u, nil
}
func getActor(app *App, actorIRI string) (*activitystreams.Person, *RemoteUser, error) { func getActor(app *App, actorIRI string) (*activitystreams.Person, *RemoteUser, error) {
log.Info("Fetching actor %s locally", actorIRI) log.Info("Fetching actor %s locally", actorIRI)
actor := &activitystreams.Person{} actor := &activitystreams.Person{}

View File

@ -820,6 +820,21 @@ func handleViewCollection(app *App, w http.ResponseWriter, r *http.Request) erro
return err return err
} }
func handleViewMention(app *App, w http.ResponseWriter, r *http.Request) error {
vars := mux.Vars(r)
handle := vars["handle"]
remoteUser, err := getRemoteUserFromHandle(app, handle)
if err != nil {
log.Error("Couldn't find this user in our database "+handle, err)
return err
}
w.Write([]byte("go to " + remoteUser.ActorID))
return nil
}
func handleViewCollectionTag(app *App, w http.ResponseWriter, r *http.Request) error { func handleViewCollectionTag(app *App, w http.ResponseWriter, r *http.Request) error {
vars := mux.Vars(r) vars := mux.Vars(r)
tag := vars["tag"] tag := vars["tag"]

View File

@ -34,6 +34,7 @@ var (
titleElementReg = regexp.MustCompile("</?h[1-6]>") titleElementReg = regexp.MustCompile("</?h[1-6]>")
hashtagReg = regexp.MustCompile(`{{\[\[\|\|([^|]+)\|\|\]\]}}`) hashtagReg = regexp.MustCompile(`{{\[\[\|\|([^|]+)\|\|\]\]}}`)
markeddownReg = regexp.MustCompile("<p>(.+)</p>") markeddownReg = regexp.MustCompile("<p>(.+)</p>")
mentionReg = regexp.MustCompile(`@[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]+\b`)
) )
func (p *Post) formatContent(cfg *config.Config, c *Collection, isOwner bool) { func (p *Post) formatContent(cfg *config.Config, c *Collection, isOwner bool) {
@ -82,6 +83,8 @@ func applyMarkdownSpecial(data []byte, skipNoFollow bool, baseURL string, cfg *c
tagPrefix = "/read/t/" tagPrefix = "/read/t/"
} }
md = []byte(hashtagReg.ReplaceAll(md, []byte("<a href=\""+tagPrefix+"$1\" class=\"hashtag\"><span>#</span><span class=\"p-category\">$1</span></a>"))) md = []byte(hashtagReg.ReplaceAll(md, []byte("<a href=\""+tagPrefix+"$1\" class=\"hashtag\"><span>#</span><span class=\"p-category\">$1</span></a>")))
handlePrefix := baseURL + "mention:"
md = []byte(mentionReg.ReplaceAll(md, []byte("<a href=\""+handlePrefix+"$0\" class=\"mention\">$0</a>")))
} }
// Strip out bad HTML // Strip out bad HTML
policy := getSanitizationPolicy() policy := getSanitizationPolicy()

View File

@ -25,6 +25,7 @@ import (
"github.com/guregu/null/zero" "github.com/guregu/null/zero"
"github.com/kylemcc/twitter-text-go/extract" "github.com/kylemcc/twitter-text-go/extract"
"github.com/microcosm-cc/bluemonday" "github.com/microcosm-cc/bluemonday"
"github.com/writeas/activityserve"
stripmd "github.com/writeas/go-strip-markdown" stripmd "github.com/writeas/go-strip-markdown"
"github.com/writeas/impart" "github.com/writeas/impart"
"github.com/writeas/monday" "github.com/writeas/monday"
@ -35,7 +36,6 @@ 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"
) )
@ -1033,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(app.cfg) po := p.ActivityObject(app)
po.Context = []interface{}{activitystreams.Namespace} po.Context = []interface{}{activitystreams.Namespace}
return impart.RenderActivityJSON(w, po, http.StatusOK) return impart.RenderActivityJSON(w, po, http.StatusOK)
} }
@ -1068,7 +1068,8 @@ func (p *PublicPost) CanonicalURL() string {
return p.Collection.CanonicalURL() + p.Slug.String return p.Collection.CanonicalURL() + p.Slug.String
} }
func (p *PublicPost) ActivityObject(cfg *config.Config) *activitystreams.Object { func (p *PublicPost) ActivityObject(app *App) *activitystreams.Object {
cfg := app.cfg
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
@ -1117,7 +1118,38 @@ func (p *PublicPost) ActivityObject(cfg *config.Config) *activitystreams.Object
mentions := mentionRegex.FindAllString(content, -1) mentions := mentionRegex.FindAllString(content, -1)
for _, handle := range mentions { for _, handle := range mentions {
actorIRI := RemoteLookup(handle) var actorIRI string
remoteuser, errRemoteUser := getRemoteUserFromHandle(app, handle)
if errRemoteUser != nil {
// can't find using handle in the table but the table may already have this user without
// handle from a previous version
actorIRI = RemoteLookup(handle)
_, errRemoteUser := getRemoteUser(app, actorIRI)
// if it exists then we need to update the handle
if errRemoteUser == nil {
// query := "UPDATE remoteusers SET handle='" + handle + "' WHERE actor_id='" + iri + "';"
// log.Info(query)
_, err := app.db.Exec("UPDATE remoteusers SET handle=? WHERE actor_id=?;", handle, actorIRI)
if err != nil {
log.Error("Can't update handle (" + handle + ") in database for user " + actorIRI)
}
} else {
// this probably means we don't have the user in the table so let's try to insert it
// here we need to ask the server for the inboxes
remoteActor, err := activityserve.NewRemoteActor(actorIRI)
if err != nil {
log.Error("Couldn't fetch remote actor", err)
}
fmt.Println(actorIRI, remoteActor.GetInbox(), remoteActor.GetSharedInbox(), handle)
_, err = app.db.Exec("INSERT INTO remoteusers (actor_id, inbox, shared_inbox, handle) VALUES( ?, ?, ?, ?)", actorIRI, remoteActor.GetInbox(), remoteActor.GetSharedInbox(), handle)
if err != nil {
log.Error("Can't insert remote user in database", err)
return nil
}
}
} else {
actorIRI = remoteuser.ActorID
}
mentionedUsers[handle] = actorIRI mentionedUsers[handle] = actorIRI
} }
@ -1379,7 +1411,7 @@ Are you sure it was ever here?`,
return ErrCollectionPageNotFound return ErrCollectionPageNotFound
} }
p.extractData() p.extractData()
ap := p.ActivityObject(app.cfg) ap := p.ActivityObject(app)
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 {

View File

@ -184,6 +184,7 @@ func InitRoutes(apper Apper, r *mux.Router) *mux.Router {
func RouteCollections(handler *Handler, r *mux.Router) { func RouteCollections(handler *Handler, r *mux.Router) {
r.HandleFunc("/page/{page:[0-9]+}", handler.Web(handleViewCollection, UserLevelReader)) r.HandleFunc("/page/{page:[0-9]+}", handler.Web(handleViewCollection, UserLevelReader))
r.HandleFunc("/mention:{handle}", handler.Web(handleViewMention, UserLevelReader))
r.HandleFunc("/tag:{tag}", handler.Web(handleViewCollectionTag, UserLevelReader)) r.HandleFunc("/tag:{tag}", handler.Web(handleViewCollectionTag, UserLevelReader))
r.HandleFunc("/tag:{tag}/feed/", handler.Web(ViewFeed, UserLevelReader)) r.HandleFunc("/tag:{tag}/feed/", handler.Web(ViewFeed, UserLevelReader))
r.HandleFunc("/tags/{tag}", handler.Web(handleViewCollectionTag, UserLevelReader)) r.HandleFunc("/tags/{tag}", handler.Web(handleViewCollectionTag, UserLevelReader))