diff options
author | FChannel <> | 2021-07-23 22:45:44 -0700 |
---|---|---|
committer | FChannel <> | 2021-07-23 22:45:44 -0700 |
commit | 8f7386f2906716d40099fb50f029d48796dd1bbd (patch) | |
tree | 790b290434220a94e384eb8970d26dea1967e6f9 | |
parent | c5eff11c39d0a07f5cb7401835d04ba4df9edcbf (diff) |
added cross post support. could blow up if referencing a link that is not local to the database or cache.
-rw-r--r-- | client.go | 209 | ||||
-rw-r--r-- | database.go | 12 | ||||
-rw-r--r-- | main.go | 61 | ||||
-rw-r--r-- | outboxPost.go | 2 | ||||
-rw-r--r-- | static/posts.html | 4 |
5 files changed, 205 insertions, 83 deletions
@@ -638,78 +638,6 @@ func MediaProxy(url string) string { 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], "<a class=\"reply\" style=\"" + style + "\" title=\"" + quoteTitle + "\" href=\"/" + board.Name + "/" + shortURL(board.Outbox, op) + "#" + shortURL(board.Outbox, link) + "\"> >>" + shortURL(board.Outbox, link) + isOP + "</a>", -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], "<span class=\"quote\">" + quote + "</span>") - 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<span class=\"quote\">></span>") - - nContent = strings.ReplaceAll(nContent, `/\<`, ">") - - return template.HTML(nContent) -} - func ParseAttachment(obj ObjectBase, catalog bool) template.HTML { if len(obj.Attachment) < 1 { @@ -784,3 +712,140 @@ func ParseAttachment(obj ObjectBase, catalog bool) template.HTML { return template.HTML(media) } + +func ParseContent(db *sql.DB, board Actor, op string, content string, thread ObjectBase) template.HTML { + + nContent := ParseLinkComments(db, board, op, content, thread) + + nContent = ParseCommentQuotes(nContent) + + nContent = strings.ReplaceAll(nContent, `/\<`, ">") + + return template.HTML(nContent) +}; + +func ParseLinkComments(db *sql.DB, board Actor, op string, content string, thread ObjectBase) string { + re := regexp.MustCompile(`(>>(https?://[A-Za-z0-9_.:\-~]+\/[A-Za-z0-9_.\-~]+\/)(f[A-Za-z0-9_.\-~]+-)?([A-Za-z0-9_.\-~]+)?#?([A-Za-z0-9_.\-~]+)?)`) + match := re.FindAllStringSubmatch(content, -1) + + //add url to each matched reply + for i, _ := range match { + link := strings.Replace(match[i][0], ">>", "", 1) + isOP := "" + + domain := match[i][2] + + if link == op { + isOP = " (OP)" + } + + parsedLink := ConvertHashLink(domain, link) + + //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 == parsedLink { + quoteTitle = ParseLinkTitle(board.Outbox, op, e.Content) + break + } + } + + if quoteTitle == "" { + obj := GetObjectFromDBFromID(db, parsedLink) + if len(obj.OrderedItems) > 0 { + quoteTitle = ParseLinkTitle(board.Outbox, op, obj.OrderedItems[0].Content) + } else { + quoteTitle = ParseLinkTitle(board.Outbox, op, parsedLink) + } + } + } + + var style string + if board.Restricted { + style = "color: #af0a0f;" + } + + //replace link with quote format + replyID, isReply := IsReplyToOP(db, op, parsedLink) + if isReply { + id := shortURL(board.Outbox, replyID) + + content = strings.Replace(content, match[i][0], "<a class=\"reply\" style=\"" + style + "\" title=\"" + quoteTitle + "\" href=\"/" + board.Name + "/" + shortURL(board.Outbox, op) + "#" + id + "\">>>" + id + "" + isOP + "</a>", -1) + + } else { + + //this is a cross post + parsedOP := GetReplyOP(db, parsedLink) + + if parsedOP != "" { + link = parsedOP + "#" + shortURL(parsedOP, parsedLink) + } + + content = strings.Replace(content, match[i][0], "<a class=\"reply\" style=\"" + style + "\" title=\"" + quoteTitle + "\" href=\"" + link + "\">>>" + shortURL(board.Outbox, parsedLink) + isOP + " →</a>", -1) + } + } + + return content +} + +func ParseLinkTitle(actorName string, op string, content string) string { + re := regexp.MustCompile(`(>>(https?://[A-Za-z0-9_.:\-~]+\/[A-Za-z0-9_.\-~]+\/)\w+(#.+)?)`) + match := re.FindAllStringSubmatch(content, -1) + + for i, _ := range match { + link := strings.Replace(match[i][0], ">>", "", 1) + isOP := "" + + domain := match[i][2] + + if link == op { + isOP = " (OP)" + } + + link = ConvertHashLink(domain, link) + content = strings.Replace(content, match[i][0], ">>" + shortURL(actorName, link) + isOP , 1) + } + + content = strings.ReplaceAll(content, "'", "") + content = strings.ReplaceAll(content, "\"", "") + content = strings.ReplaceAll(content, ">", `/\<`) + + return content +} + +func ParseCommentQuotes(content string) string { + // replace quotes + re := regexp.MustCompile(`((\r\n|\r|\n|^)>(.+)?[^\r\n])`) + match := re.FindAllStringSubmatch(content, -1) + + for i, _ := range match { + quote := strings.Replace(match[i][0], ">", ">", 1) + line := re.ReplaceAllString(match[i][0], "<span class=\"quote\">" + quote + "</span>") + content = strings.Replace(content, match[i][0], line, 1) + } + + //replace isolated greater than symboles + re = regexp.MustCompile(`(\r\n|\n|\r)>`) + + return re.ReplaceAllString(content, "\r\n<span class=\"quote\">></span>") +} + +func ConvertHashLink(domain string, link string) string { + re := regexp.MustCompile(`(#.+)`) + parsedLink := re.FindString(link) + + if parsedLink != "" { + parsedLink = domain + "" + strings.Replace(parsedLink, "#", "", 1) + parsedLink = strings.Replace(parsedLink, "\r", "", -1) + } else { + parsedLink = link + } + + return parsedLink +} diff --git a/database.go b/database.go index 3a20cb4..3d3f9ff 100644 --- a/database.go +++ b/database.go @@ -6,6 +6,7 @@ import ( "os" "sort" "strings" + "regexp" "time" "html/template" @@ -558,7 +559,16 @@ 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` + 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 like $1 and type='Note' union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where id like $1 and type='Note') as x order by x.updated` + + re := regexp.MustCompile(`f(\w+)\-`) + match := re.FindStringSubmatch(id) + + if len(match) > 0 { + re := regexp.MustCompile(`(.+)\-`) + id = re.ReplaceAllString(id, "") + id = "%" + match[1] + "/" + id + } rows, err := db.Query(query, id) @@ -1730,7 +1730,7 @@ func CreateAttachmentObject(file multipart.File, header *multipart.FileHeader) ( return nAttachment, tempFile } -func ParseCommentForReplies(comment string) []ObjectBase { +func ParseCommentForReplies(db *sql.DB, comment string, op string) []ObjectBase { re := regexp.MustCompile(`(>>https?://[A-Za-z0-9_.\-~]+\/[A-Za-z0-9_.\-~]+\/\w+)`) match := re.FindAllStringSubmatch(comment, -1) @@ -1743,7 +1743,8 @@ func ParseCommentForReplies(comment string) []ObjectBase { str = strings.Replace(str, "http://", "", 1) str = strings.Replace(str, "https://", "", 1) str = TP + "" + str - if !IsInStringArray(links, str) { + _ , isReply := IsReplyToOP(db, op, str) + if !IsInStringArray(links, str) && isReply { links = append(links, str) } } @@ -1864,7 +1865,6 @@ func GetActorCollection(collection string) Collection { return nCollection } - defer resp.Body.Close() if resp.StatusCode == 200 { @@ -2405,7 +2405,7 @@ func ParseCommentForReply(comment string) string { re := regexp.MustCompile("(>>)(https://|http://)?(www\\.)?.+\\/\\w+") match := re.FindAllStringSubmatch(comment, -1) - + var links []string for i:= 0; i < len(match); i++ { @@ -2414,7 +2414,7 @@ func ParseCommentForReply(comment string) string { } if(len(links) > 0){ - _, isValid := CheckValidActivity(links[0]) + _, isValid := CheckValidActivity(strings.ReplaceAll(links[0], ">", "")) if(isValid) { return links[0] @@ -2843,3 +2843,54 @@ func HasValidation(w http.ResponseWriter, r *http.Request, actor Actor) bool { return true } + +func IsReplyToOP(db *sql.DB, op string, link string) (string, bool) { + + if op == link { + return link, true + } + + re := regexp.MustCompile(`f(\w+)\-`) + match := re.FindStringSubmatch(link) + + if len(match) > 0 { + re := regexp.MustCompile(`(.+)\-`) + link = re.ReplaceAllString(link, "") + link = "%" + match[1] + "/" + link + } + + query := `select id from replies where id like $1 and inreplyto=$2` + + rows, err := db.Query(query, link, op) + + CheckError(err, "error selecting in reply to op from db") + + var id string + defer rows.Close() + rows.Next() + rows.Scan(&id) + + if id != "" { + + return id, true + } + + return "", false +} + +func GetReplyOP(db *sql.DB, link string) string { + + query := `select id from replies where id in (select inreplyto from replies where id=$1) and inreplyto=''` + + rows, err := db.Query(query, link) + + CheckError(err, "could not get reply OP from db ") + + var id string + + defer rows.Close() + rows.Next() + rows.Scan(&id) + + return id +} diff --git a/outboxPost.go b/outboxPost.go index b3a8baf..1d658ea 100644 --- a/outboxPost.go +++ b/outboxPost.go @@ -381,7 +381,7 @@ func ObjectFromForm(r *http.Request, db *sql.DB, obj ObjectBase) ObjectBase { } } - replyingTo := ParseCommentForReplies(r.FormValue("comment")) + replyingTo := ParseCommentForReplies(db, r.FormValue("comment"), originalPost.Id) for _, e := range replyingTo { diff --git a/static/posts.html b/static/posts.html index d7f46b9..5810348 100644 --- a/static/posts.html +++ b/static/posts.html @@ -101,10 +101,6 @@ {{ if .Replies.OrderedItems }} {{ range .Replies.OrderedItems }} <span id="{{$parentId}}-replyto-{{.Id}}"></span> - <script> - var content = convertContentNoLink('{{$board.Actor.Id}}', '{{ .Content }}', '{{ $opId }}') - document.getElementById("{{ $parentId }}-replyto-{{.Id}}").innerHTML = "<a title='" + content +"' href='/{{ $board.Name }}/" + shortURL("{{ $board.Actor.Id }}", "{{ $opId }}") + "#" + shortURL("{{ $board.Actor.Id }}", "{{ .Id }}") + "'>>>" + shortURL("{{ $board.Actor.Id }}", "{{ .Id }}") + "</a>"; - </script> {{ end }} {{ end }} <p id="{{ .Id }}-content" style="white-space: pre-wrap; margin: 10px 30px 10px 30px;">{{.ContentHTML}}</p> |