{{.Content}}
+{{.ContentHTML}}
From 3b2c8a4c78ade9e819b61aa055039ae00e517a3e Mon Sep 17 00:00:00 2001 From: FChannel <> Date: Thu, 22 Jul 2021 13:50:11 -0700 Subject: fixed quote bug and moved comment parsing server side without javascript --- activityPubStruct.go | 11 +++-- client.go | 126 ++++++++++++++++++++++++++++++++++++++++++++++----- database.go | 42 +++++++++++++++++ main.go | 4 +- static/main.html | 9 +--- static/ncatalog.html | 5 +- static/posts.html | 35 +++----------- 7 files changed, 174 insertions(+), 58 deletions(-) diff --git a/activityPubStruct.go b/activityPubStruct.go index e790573..62fabe1 100644 --- a/activityPubStruct.go +++ b/activityPubStruct.go @@ -1,6 +1,9 @@ package main -import "encoding/json" +import ( + "encoding/json" + "html/template" +) type AtContextRaw struct { Context json.RawMessage `json:"@context,omitempty"` @@ -111,7 +114,8 @@ type ObjectBase struct { TripCode string `json:"tripcode,omitempty"` Actor string `json:"actor,omitempty"` Audience string `json:"audience,omitempty"` - Content string `json:"content,omitempty"` + ContentHTML template.HTML `json:"contenthtml,omitempty"` + Content string `json:"content,omitempty"` EndTime string `json:"endTime,omitempty"` Generator string `json:"generator,omitempty"` Icon string `json:"icon,omitempty"` @@ -156,7 +160,8 @@ type NestedObjectBase struct { TripCode string `json:"tripcode,omitempty"` Actor string `json:"actor,omitempty"` Audience string `json:"audience,omitempty"` - Content string `json:"content,omitempty"` + ContentHTML template.HTML `json:"contenthtml,omitempty"` + Content string `json:"content,omitempty"` EndTime string `json:"endTime,omitempty"` Generator string `json:"generator,omitempty"` Icon string `json:"icon,omitempty"` diff --git a/client.go b/client.go index af83a28..12e6147 100644 --- a/client.go +++ b/client.go @@ -1,14 +1,17 @@ package main -import "net/http" -import "html/template" -import "database/sql" -import _ "github.com/lib/pq" -import "strings" -import "strconv" -import "sort" -import "regexp" -import "time" +import ( + "net/http" + "html/template" + "database/sql" + _ "github.com/lib/pq" + "strings" + "strconv" + "sort" + "regexp" + "time" + "fmt" +) var Key *string = new(string) @@ -116,6 +119,7 @@ func IndexGet(w http.ResponseWriter, r *http.Request, db *sql.DB) { if(len(data.BoardRemainer) == 3){ data.BoardRemainer = make([]int, 0) } + data.InstanceIndex = GetCollectionFromReq("https://fchan.xyz/followers").Items data.NewsItems = getNewsFromDB(db, 3) @@ -184,6 +188,9 @@ func OutboxGet(w http.ResponseWriter, r *http.Request, db *sql.DB, collection Co "proxy": func(url string) string { return MediaProxy(url) }, + "short": func(actorName string, url string) string { + return shortURL(actorName, url) + }, "sub": func (i, j int) int { return i - j }}).ParseFiles("./static/main.html", "./static/nposts.html", "./static/top.html", "./static/bottom.html", "./static/posts.html")) @@ -219,6 +226,14 @@ func OutboxGet(w http.ResponseWriter, r *http.Request, db *sql.DB, collection Co returnData.Boards = Boards returnData.Posts = collection.OrderedItems + for i, e := range returnData.Posts { + returnData.Posts[i].ContentHTML = ParseContent(db, returnData.Board.Actor, e.Id, e.Content, e) + + for j, k := range e.Replies.OrderedItems { + returnData.Posts[i].Replies.OrderedItems[j].ContentHTML = ParseContent(db, returnData.Board.Actor, e.Id, k.Content, e) + } + } + var offset = 8 var pages []int pageLimit := (float64(collection.TotalItems) / float64(offset)) @@ -236,7 +251,10 @@ func CatalogGet(w http.ResponseWriter, r *http.Request, db *sql.DB, collection C t := template.Must(template.New("").Funcs(template.FuncMap{ "proxy": func(url string) string { return MediaProxy(url) - }, + }, + "short": func(actorName string, url string) string { + return shortURL(actorName, url) + }, "sub": func (i, j int) int { return i - j }}).ParseFiles("./static/main.html", "./static/ncatalog.html", "./static/top.html")) actor := collection.Actor @@ -273,7 +291,10 @@ func PostGet(w http.ResponseWriter, r *http.Request, db *sql.DB){ t := template.Must(template.New("").Funcs(template.FuncMap{ "proxy": func(url string) string { return MediaProxy(url) - }, + }, + "short": func(actorName string, url string) string { + return shortURL(actorName, url) + }, "sub": func (i, j int) int { return i - j }}).ParseFiles("./static/main.html", "./static/npost.html", "./static/top.html", "./static/bottom.html", "./static/posts.html")) path := r.URL.Path @@ -333,6 +354,14 @@ func PostGet(w http.ResponseWriter, r *http.Request, db *sql.DB){ if len(returnData.Posts) > 0 { returnData.PostId = shortURL(returnData.Board.To, returnData.Posts[0].Id) + + for i, e := range returnData.Posts { + returnData.Posts[i].ContentHTML = ParseContent(db, returnData.Board.Actor, e.Id, e.Content, e) + + for j, k := range e.Replies.OrderedItems { + returnData.Posts[i].Replies.OrderedItems[j].ContentHTML = ParseContent(db, returnData.Board.Actor, e.Id, k.Content, e) + } + } } t.ExecuteTemplate(w, "layout", returnData) @@ -596,7 +625,80 @@ func MediaProxy(url string) string { if re.MatchString(url) { return url } - + + fmt.Println("") MediaHashs[HashMedia(url)] = url return "/api/media?hash=" + HashMedia(url) } + +func ParseContent(db *sql.DB, board Actor, op string, content string, thread ObjectBase) template.HTML { + var nContent = content + + re := regexp.MustCompile(`(>>https?:\/\/[A-Za-z0-9_.-~]+\/[A-Za-z0-9_.-~]+\/\w+)`) + match := re.FindAllStringSubmatch(nContent, -1) + + //add url to each matched reply + for i, _ := range match { + link := strings.Replace(match[i][0], ">>", "", 1) + isOP := "" + + if link == op { + isOP = " (OP)" + } + + //formate the hover title text + var quoteTitle string + + // if the quoted content is local get it + // else get it from the database + if thread.Id == link { + quoteTitle = thread.Content + } else { + for _, e := range thread.Replies.OrderedItems { + if e.Id == link { + quoteTitle = e.Content + break + } + } + + if quoteTitle == "" { + obj := GetObjectFromDBFromID(db, link) + if len(obj.OrderedItems) > 0 { + quoteTitle = obj.OrderedItems[0].Content + } + } + } + + quoteTitle = strings.ReplaceAll(quoteTitle, ">", `/\<`) + quoteTitle = strings.ReplaceAll(quoteTitle, "\"", "") + quoteTitle = strings.ReplaceAll(quoteTitle, "'", "") + + + var style string + if board.Restricted { + style = "color: #af0a0f;" + } + + //replace link with quote format + nContent = strings.Replace(nContent, match[i][0], " >>" + shortURL(board.Outbox, link) + isOP + "", -1) + } + + // replace quotes + re = regexp.MustCompile(`((\r\n|^)>(.+)?[^\r\n])`) + match = re.FindAllStringSubmatch(nContent, -1) + + for i, _ := range match { + quote := strings.Replace(match[i][0], ">", ">", 1) + line := re.ReplaceAllString(match[i][0], "" + quote + "") + nContent = strings.Replace(nContent, match[i][0], line, 1) + } + + //replace isolated greater than symboles + re = regexp.MustCompile(`(\r\n)>`) + + nContent = re.ReplaceAllString(nContent, "\r\n>") + + nContent = strings.ReplaceAll(nContent, `/\<`, ">") + + return template.HTML(nContent) +} diff --git a/database.go b/database.go index 1b2ec3d..3a20cb4 100644 --- a/database.go +++ b/database.go @@ -554,6 +554,48 @@ func GetObjectFromDB(db *sql.DB, id string) Collection { return nColl } +func GetObjectFromDBFromID(db *sql.DB, id string) Collection { + var nColl Collection + var result []ObjectBase + + query := `select x.id, x.name, x.content, x.type, x.published, x.updated, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode, x.sensitive from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where id=$1 and type='Note' union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where id=$1 and type='Note') as x order by x.updated` + + rows, err := db.Query(query, id) + + CheckError(err, "error query object from db") + + defer rows.Close() + for rows.Next(){ + var post ObjectBase + var actor Actor + var attachID string + var previewID string + + err = rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode, &post.Sensitive) + + CheckError(err, "error scan object into post struct") + + post.Actor = actor.Id + + var postCnt int + var imgCnt int + post.Replies, postCnt, imgCnt = GetObjectRepliesDB(db, post) + + post.Replies.TotalItems = postCnt + post.Replies.TotalImgs = imgCnt + + post.Attachment = GetObjectAttachment(db, attachID) + + post.Preview = GetObjectPreview(db, previewID) + + result = append(result, post) + } + + nColl.OrderedItems = result + + return nColl +} + func GetObjectFromDBCatalog(db *sql.DB, id string) Collection { var nColl Collection var result []ObjectBase diff --git a/main.go b/main.go index 4327e44..601394f 100644 --- a/main.go +++ b/main.go @@ -1640,8 +1640,8 @@ func CreateAttachmentObject(file multipart.File, header *multipart.FileHeader) ( } func ParseCommentForReplies(comment string) []ObjectBase { - - re := regexp.MustCompile("(>>)(https://|http://)?(www\\.)?.+\\/\\w+") + + re := regexp.MustCompile(`(>>https?:\/\/[A-z.:0-9]+\/[A-z0-9]+\/\w+)`) match := re.FindAllStringSubmatch(comment, -1) var links []string diff --git a/static/main.html b/static/main.html index 3cb8555..9209ed9 100644 --- a/static/main.html +++ b/static/main.html @@ -53,14 +53,6 @@ color: #789922; } - .reply { - {{ if .Board.Restricted }} - color:#af0a0f; - {{ else }} - color:#000080; - {{ end }} - } - .post { {{ if .Board.Restricted }} background-color: #d5daf0; @@ -85,6 +77,7 @@
{{.Content}}
+ {{ .Name }}{{ if .AttributedTo }} {{.AttributedTo }} {{ else }} Anonymous {{ end }} {{ .TripCode }} {{ .Published }} No. {{ short $board.Actor.Outbox .Id }} {{ if ne .Type "Tombstone" }}[Report]{{ end }} +{{.ContentHTML}}
{{ if .Replies }} {{ $replies := .Replies }} {{ if gt $replies.TotalItems 5 }} @@ -94,7 +93,7 @@ {{ end }} {{ end }} {{ range $replies.OrderedItems }} -{{.Content}}
+{{.ContentHTML}}