diff options
-rw-r--r-- | activityPubStruct.go | 11 | ||||
-rw-r--r-- | client.go | 126 | ||||
-rw-r--r-- | database.go | 42 | ||||
-rw-r--r-- | main.go | 4 | ||||
-rw-r--r-- | static/main.html | 9 | ||||
-rw-r--r-- | static/ncatalog.html | 5 | ||||
-rw-r--r-- | 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"` @@ -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], "<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) +} 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 @@ -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 @@ <body> <ul style="display: inline; padding:0;"> {{ $l := len .Boards }} + <li style="display: inline;">[<a href="/">Home</a>]</li> {{range $i, $e := .Boards}} {{ if eq (sub $l 1) 0 }} <li style="display: inline;">[ <a href="{{.Location}}">{{$e.Name}} </a>]</li> diff --git a/static/ncatalog.html b/static/ncatalog.html index 68bb01a..d007d62 100644 --- a/static/ncatalog.html +++ b/static/ncatalog.html @@ -105,7 +105,7 @@ document.getElementById("{{ .Id }}-anchor").href = "/{{ $board.Name }}/" + shortURL("{{$board.Actor.Id}}", "{{ .Id }}") </script> {{ end }} - <a id="{{ .Id }}-link" href="/{{ $board.Name }}/"> + <a id="{{ .Id }}-link" href="/{{ $board.Name }}/{{ short $board.Actor.Outbox .Id }}"> <div> {{ $replies := .Replies }} {{ if $replies }} @@ -121,9 +121,6 @@ </div> </a> </div> - <script> - document.getElementById("{{ .Id }}-link").href = "/{{ $board.Name }}/" + shortURL("{{$board.Actor.Id}}", "{{ .Id }}") - </script> {{ end }} </div> <hr> diff --git a/static/posts.html b/static/posts.html index d16f780..7552188 100644 --- a/static/posts.html +++ b/static/posts.html @@ -7,7 +7,7 @@ <hr> {{ end }} <div style="overflow: auto;"> - <div id="{{ .Id }}" style="overflow: visible; margin-bottom: 12px;"> + <div id="{{ short $board.Actor.Outbox .Id }}" style="overflow: visible; margin-bottom: 12px;"> {{ if eq $board.ModCred $board.Domain $board.Actor.Id }} <a href="/delete?id={{ .Id }}&board={{ $board.Actor.Name }}">[Delete Post]</a> {{ end }} @@ -22,7 +22,6 @@ <div id="media-{{ .Id }}"></div> <script> media = document.getElementById("media-{{ .Id }}") - if(({{ .Sensitive }} && {{ $board.Actor.Restricted }}) || (isOnion("{{ .Id }}") && !isOnion("{{ $board.Domain }}"))){ sensitive = document.getElementById("sensitive-{{ .Id }}") hide = document.getElementById("hide-{{ .Id }}") @@ -84,8 +83,8 @@ } </script> {{ end }} - <span style="color: #0f0c5d;"><b>{{ .Name }}</b></span><span style="color: #117743;"><b>{{ if .AttributedTo }} {{.AttributedTo }} {{ else }} Anonymous {{ end }}</b></span><span class="tripcode"> {{ .TripCode }} </span><span>{{ .Published }} <a id="{{ .Id }}-anchor" href="/{{ $board.Name }}/">No.</a> <a id="{{ .Id }}-link" title="{{ .Id }}" href="javascript:quote('{{ $board.Actor.Id }}', '{{ $opId }}', '{{ .Id }}')">{{ .Id }}</a> {{ if ne .Type "Tombstone" }}<a href="javascript:report('{{ $board.Actor.Id }}', '{{ .Id }}')">[Report]</a>{{ end }}</span> - <p id="{{ .Id }}-content" style="white-space: pre-wrap; margin: 10px 30px 10px 30px;">{{.Content}}</p> + <span style="color: #0f0c5d;"><b>{{ .Name }}</b></span><span style="color: #117743;"><b>{{ if .AttributedTo }} {{.AttributedTo }} {{ else }} Anonymous {{ end }}</b></span><span class="tripcode"> {{ .TripCode }} </span><span>{{ .Published }} <a id="{{ .Id }}-anchor" href="/{{ $board.Name }}/{{ short $board.Actor.Outbox $opId }}#{{ short $board.Actor.Outbox .Id }}">No.</a> <a id="{{ .Id }}-link" title="{{ .Id }}" href="javascript:quote('{{ $board.Actor.Id }}', '{{ $opId }}', '{{ .Id }}')">{{ short $board.Actor.Outbox .Id }}</a> {{ if ne .Type "Tombstone" }}<a href="javascript:report('{{ $board.Actor.Id }}', '{{ .Id }}')">[Report]</a>{{ end }}</span> + <p id="{{ .Id }}-content" style="white-space: pre-wrap; margin: 10px 30px 10px 30px;">{{.ContentHTML}}</p> {{ if .Replies }} {{ $replies := .Replies }} {{ if gt $replies.TotalItems 5 }} @@ -94,7 +93,7 @@ {{ end }} {{ end }} {{ range $replies.OrderedItems }} - <div id="{{ .Id }}"> + <div id="{{ short $board.Actor.Outbox .Id }}"> <div style="display: inline-block; overflow: auto;"> <div style="float: left; display: block; margin-right: 5px;">>></div> <div class="post" style="overflow: auto; padding: 5px; margin-bottom: 2px;"> @@ -176,7 +175,7 @@ } </script> {{ end }} - <span style="color: #0f0c5d;"><b>{{ .Name }}</b></span><span style="color: #117743;"><b>{{ if .AttributedTo }} {{.AttributedTo }} {{ else }} Anonymous {{ end }}</b></span><span class="tripcode"> {{ .TripCode }} </span><span>{{ .Published }} <a id="{{ .Id }}-anchor" href="/{{ $board.Name }}/post/{{ $opId }}#{{ .Id }}">No. </a><a id="{{ .Id }}-link" title="{{ .Id }}" href="javascript:quote('{{ $board.Actor.Id }}', '{{ $opId }}', '{{ .Id }}')">{{ .Id }}</a> {{ if ne .Type "Tombstone" }}<a href="javascript:report('{{ $board.Actor.Id }}', '{{ .Id }}')">[Report]</a>{{ end }}</span> + <span style="color: #0f0c5d;"><b>{{ .Name }}</b></span><span style="color: #117743;"><b>{{ if .AttributedTo }} {{.AttributedTo }} {{ else }} Anonymous {{ end }}</b></span><span class="tripcode"> {{ .TripCode }} </span><span>{{ .Published }} <a id="{{ .Id }}-anchor" href="/{{ $board.Name }}/{{ short $board.Actor.Outbox $opId }}#{{ short $board.Actor.Outbox .Id }}">No. </a><a id="{{ .Id }}-link" title="{{ .Id }}" href="javascript:quote('{{ $board.Actor.Id }}', '{{ $opId }}', '{{ .Id }}')">{{ short $board.Actor.Outbox .Id }}</a> {{ if ne .Type "Tombstone" }}<a href="javascript:report('{{ $board.Actor.Id }}', '{{ .Id }}')">[Report]</a>{{ end }}</span> {{ $parentId := .Id }} {{ if .Replies.OrderedItems }} {{ range .Replies.OrderedItems }} @@ -187,7 +186,7 @@ </script> {{ end }} {{ end }} - <p id="{{ .Id }}-content" style="white-space: pre-wrap; margin: 10px 30px 10px 30px;">{{.Content}}</p> + <p id="{{ .Id }}-content" style="white-space: pre-wrap; margin: 10px 30px 10px 30px;">{{.ContentHTML}}</p> </div> </div> </div> @@ -196,17 +195,6 @@ document.getElementById("{{ .Id }}-size").innerText = " (" + convertSize({{ (index .Attachment 0).Size }}) + ")"; document.getElementById("{{ .Id }}-img").innerText = shortImg("{{ (index .Attachment 0).Name }}"); {{ end }} - - document.getElementById("{{ .Id }}-link").innerText = shortURL("{{ $board.Actor.Id }}", "{{ .Id }}"); - - document.getElementById("{{ .Id }}-anchor").href = "/{{ $board.Name }}/" + shortURL("{{$board.Actor.Id}}", "{{ $opId }}") + - "#" + shortURL("{{$board.Actor.Id}}", "{{ .Id }}"); - document.getElementById("{{ .Id }}").setAttribute("id", shortURL("{{$board.Actor.Id}}", "{{ .Id }}")); - - var content = document.getElementById("{{ .Id }}-content"); - - content.innerHTML = convertContent('{{$board.Actor.Id}}', content.innerText, '{{ $opId }}') - </script> {{ end }} {{ end }} @@ -217,17 +205,6 @@ document.getElementById("{{ .Id }}-size").innerText = " (" + convertSize({{ (index .Attachment 0).Size }}) + ")"; document.getElementById("{{ .Id }}-img").innerText = shortImg("{{ (index .Attachment 0).Name }}"); {{ end }} - - document.getElementById("{{ .Id }}-link").innerText = shortURL("{{ $board.Actor.Id }}", "{{ .Id }}"); - - document.getElementById("{{ .Id }}").setAttribute("id", shortURL("{{ $board.Actor.Id }}", "{{ .Id }}")); - - document.getElementById("{{ .Id }}-anchor").href = "/{{ $board.Name }}/" + shortURL("{{$board.Actor.Id}}", "{{ $opId }}") + - "#" + shortURL("{{$board.Actor.Id}}", "{{ .Id }}"); - - var content = document.getElementById("{{ .Id }}-content"); - - content.innerHTML = convertContent('{{$board.Actor.Id}}', content.innerText, '{{ $opId }}') </script> {{ end }} {{ end }} |