diff options
-rw-r--r-- | client.go | 52 | ||||
-rw-r--r-- | database.go | 66 | ||||
-rw-r--r-- | databaseschema.psql | 8 | ||||
-rw-r--r-- | main.go | 26 | ||||
-rw-r--r-- | static/index.html | 40 | ||||
-rw-r--r-- | static/nadmin.html | 8 | ||||
-rw-r--r-- | static/news.html | 32 |
7 files changed, 219 insertions, 13 deletions
@@ -8,6 +8,7 @@ import "strings" import "strconv" import "sort" import "regexp" +import "time" var Key *string = new(string) @@ -35,8 +36,7 @@ type Board struct{ type PageData struct { Title string - Message string - MessageHTML template.HTML + PreferredUsername string Board Board Pages []int CurrentPage int @@ -48,6 +48,8 @@ type PageData struct { Instance Actor InstanceIndex []ObjectBase ReturnTo string + NewsItems []NewsItem + BoardRemainer []int } type AdminPage struct { @@ -74,15 +76,22 @@ type Removed struct { Board string } + +type NewsItem struct { + Title string + Content string + Time int +} + func IndexGet(w http.ResponseWriter, r *http.Request, db *sql.DB) { - t := template.Must(template.ParseFiles("./static/main.html", "./static/index.html")) + t := template.Must(template.New("").Funcs(template.FuncMap{"mod": func(i, j int) bool { return i%j == 0 }, "unixtoreadable": func(u int) string { return time.Unix(int64(u), 0).Format("Jan 02, 2006") }}).ParseFiles("./static/main.html", "./static/index.html")) actor := GetActorFromDB(db, Domain) var data PageData data.Title = "Welcome to " + actor.PreferredUsername - data.Message = actor.PreferredUsername + " is a federated image board based on activitypub. The current version of the code running the server is still a work in progress, expect a bumpy ride for the time being. Get the server code here https://github.com/FChannel0" - data.MessageHTML = template.HTML(actor.PreferredUsername + " is a federated image board based on activitypub. The current version of the code running the server is still a work in progress, expect a bumpy ride for the time being. Get the server code here <a href='https://github.com/FChannel0'>https://github.com/FChannel0</a>") + + data.PreferredUsername = actor.PreferredUsername data.Boards = Boards data.Board.Name = "" data.Key = *Key @@ -91,11 +100,42 @@ func IndexGet(w http.ResponseWriter, r *http.Request, db *sql.DB) { data.Board.Actor = actor data.Board.Post.Actor = actor.Id data.Board.Restricted = actor.Restricted + data.BoardRemainer = make([]int, (len(data.Boards) % 3)+1) data.InstanceIndex = GetCollectionFromReq("https://fchan.xyz/followers").Items + data.NewsItems = getNewsFromDB(db) + t.ExecuteTemplate(w, "layout", data) +} +func NewsGet(w http.ResponseWriter, r *http.Request, db *sql.DB, timestamp int) { + t := template.Must(template.New("").Funcs(template.FuncMap{"unixtoreadable": func(u int) string { return time.Unix(int64(u), 0).Format("Jan 02, 2006") }}).ParseFiles("./static/main.html", "./static/news.html")) + + actor := GetActorFromDB(db, Domain) + + var data PageData + data.PreferredUsername = actor.PreferredUsername + data.Boards = Boards + data.Board.Name = "" + data.Key = *Key + data.Board.Domain = Domain + data.Board.ModCred, _ = GetPasswordFromSession(r) + data.Board.Actor = actor + data.Board.Post.Actor = actor.Id + data.Board.Restricted = actor.Restricted + data.NewsItems = []NewsItem{NewsItem{}} + + var err error + data.NewsItems[0], err = getNewsItemFromDB(db, timestamp) - t.ExecuteTemplate(w, "layout", data) + if err != nil { + w.WriteHeader(http.StatusForbidden) + w.Write([]byte("404 no path")) + return + } + + data.Title = actor.PreferredUsername + ": " + data.NewsItems[0].Title + + t.ExecuteTemplate(w, "layout", data) } func OutboxGet(w http.ResponseWriter, r *http.Request, db *sql.DB, collection Collection){ diff --git a/database.go b/database.go index 9d5d721..037fde2 100644 --- a/database.go +++ b/database.go @@ -7,11 +7,12 @@ import "time" import "os" import "strings" import "sort" +import "container/list" func GetActorFromDB(db *sql.DB, id string) Actor { - var nActor Actor + var nActor Actor - query :=`select type, id, name, preferedusername, inbox, outbox, following, followers, restricted, summary, publickeypem from actor where id=$1` + query :=`select type, id, name, preferedusername, inbox, outbox, following, followers, restricted, summary, publickeypem from actor where id=$1` rows, err := db.Query(query, id) @@ -1488,3 +1489,64 @@ func MarkObjectSensitive(db *sql.DB, id string, sensitive bool) { CheckError(err, "error updating sensitive object in cacheactivitystream") } + +func getNewsFromDB(db *sql.DB) []NewsItem { + news := list.New() + query :=`select title, content, time from newsItem order by time desc` + + rows, err := db.Query(query) + + if CheckError(err, "could not get news from db query") != nil { + return make([]NewsItem, 0) + } + + defer rows.Close() + for rows.Next() { + n := NewsItem{} + err = rows.Scan(&n.Title, &n.Content, &n.Time) + if CheckError(err, "error scanning news from db") != nil { + return make([]NewsItem, 0) + } + news.PushBack(n) + } + + anews := make([]NewsItem, news.Len()) + + i:=0 + for e := news.Front(); e != nil; e = e.Next() { + assert := e.Value.(NewsItem) + anews[i] = assert + i++ + } + + return anews +} + +func getNewsItemFromDB(db *sql.DB, timestamp int) (NewsItem, error) { + var news NewsItem + query := `select title, content, time from newsItem where time=$1 limit 1` + + rows, err := db.Query(query, timestamp) + + if err != nil { + return news, err + } + + defer rows.Close() + rows.Next() + err = rows.Scan(&news.Title, &news.Content, &news.Time) + + if err != nil { + return news, err + } + + return news, nil +} + +func WriteNewsToDB(db *sql.DB, news NewsItem) { + query := `insert into newsItem (title, content, time) values ($1, $2, $3)` + + _, err := db.Exec(query, news.Title, news.Content, time.Now().Unix()) + + CheckError(err, "error writing news item") +} diff --git a/databaseschema.psql b/databaseschema.psql index e12813e..43767f3 100644 --- a/databaseschema.psql +++ b/databaseschema.psql @@ -216,7 +216,13 @@ owner varchar(100), file varchar(100) ); +CREATE TABLE IF NOT EXISTS newsItem( +title text, +content text, +time bigint +); + ALTER TABLE actor ADD COLUMN IF NOT EXISTS publicKeyPem varchar(100) default ''; ALTER TABLE activitystream ADD COLUMN IF NOT EXISTS sensitive boolean default false; -ALTER TABLE cacheactivitystream ADD COLUMN IF NOT EXISTS sensitive boolean default false;
\ No newline at end of file +ALTER TABLE cacheactivitystream ADD COLUMN IF NOT EXISTS sensitive boolean default false; @@ -290,6 +290,21 @@ func main() { w.WriteHeader(http.StatusForbidden) w.Write([]byte("404 no path")) }) + + http.HandleFunc("/news/", func(w http.ResponseWriter, r *http.Request){ + timestamp := r.URL.Path[6:] + if timestamp[len(timestamp)-1:] == "/" { + timestamp = timestamp[:len(timestamp)-1] + } + + ts, err := strconv.Atoi(timestamp) + if err != nil { + w.WriteHeader(http.StatusForbidden) + w.Write([]byte("404 no path")) + } else { + NewsGet(w, r, db, ts) + } + }) http.HandleFunc("/post", func(w http.ResponseWriter, r *http.Request){ @@ -602,6 +617,17 @@ func main() { MakeActivityRequestOutbox(db, newActorActivity) http.Redirect(w, r, "/" + *Key, http.StatusSeeOther) }) + + http.HandleFunc("/" + *Key + "/postnews", func(w http.ResponseWriter, r *http.Request) { + var newsitem NewsItem + + newsitem.Title = r.FormValue("title") + newsitem.Content = r.FormValue("summary") + + WriteNewsToDB(db, newsitem) + + http.Redirect(w, r, "/", http.StatusSeeOther) + }) http.HandleFunc("/verify", func(w http.ResponseWriter, r *http.Request){ if(r.Method == "POST") { diff --git a/static/index.html b/static/index.html index ab0bea0..518c012 100644 --- a/static/index.html +++ b/static/index.html @@ -1,6 +1,6 @@ {{ define "header" }} <title>{{ .Title }}</title> -<meta name="description" content="{{ .Message }}"> +<meta name="description" content="{{ .PreferredUsername }} is a federated image board based on activitypub. The current version of the code running the server is still a work in progress, expect a bumpy ride for the time being. Get the server code here: https://github.com/FChannel0."> <meta property="og:locale" content="en_US" /> <meta property="og:type" content="website" /> @@ -8,10 +8,10 @@ <meta property="og:site_name" content="{{ .Board.Actor.PreferredUsername }}" /> <meta property="og:title" content="{{ .Title }}"> -<meta property="og:description" content="{{ .Message }}"> +<meta property="og:description" content="{{ .PreferredUsername }} is a federated image board based on activitypub. The current version of the code running the server is still a work in progress, expect a bumpy ride for the time being. Get the server code here: https://github.com/FChannel0."> <meta name="twitter:title" content="{{ .Title }}"> -<meta name="twitter:description" content="{{ .Message }}"> +<meta name="twitter:description" content="{{ .PreferredUsername }} is a federated image board based on activitypub. The current version of the code running the server is still a work in progress, expect a bumpy ride for the time being. Get the server code here: https://github.com/FChannel0."> <meta name="twitter:card" content="summary_large_image"> {{ end }} @@ -20,7 +20,39 @@ {{ define "content" }} <div style="text-align: center; max-width: 800px; margin: 0 auto;"> <h1>{{ .Title }}</h1> - <p style="text-align: justify">{{.MessageHTML}}</p> + <p style="text-align: justify">{{ .PreferredUsername }} is a federated image board based on activitypub. The current version of the code running the server is still a work in progress, expect a bumpy ride for the time being. Get the server code <a href="https://github.com/FChannel0">here</a>.</p> + + <div style="margin-top:50px;"> + + <div style="display: grid;border-right: 2px solid #820404"> + <div style="display: inline-grid;grid-column: 1 / 4;border-bottom: 2px solid #820404;border-left: 2px solid #820404;border-top: 2px solid #820404;"><span style="font-size: 1.5em;font-weight: bold;">Local boards</span></div> + {{ range .Boards }} + <div style="whitespace: nowrap;display: inline-grid;text-align: left;padding: 5px;border-bottom: 2px solid #820404;border-left: 2px solid #820404;"><a href="{{.Location}}">{{.Name}} - {{.PrefName}}</a></div> + {{ end }} + {{ range .BoardRemainer }} + <div style="whitespace: nowrap;display: inline-grid;text-align: left;padding: 5px;border-bottom: 2px solid #820404;border-left: 2px solid #820404;"></div> + {{ end }} + </div> + + </div> + + <div style="margin-top:50px;"> + <table align="center" style="text-align: left;"> + <th> + <tr>{{ .PreferredUsername }} news</tr> + </th> + + {{ range $i, $e := .NewsItems }} + <tr> + <td><a href="/news/{{.Time}}">{{unixtoreadable $e.Time}} - {{$e.Title}}</a> + {{ if eq $i 0 }} + <br><p>{{$e.Content}}</p> + {{ end }} + </td> + </tr> + {{ end }} + </table> + </div> <div style="margin-top:50px;"> <table align="center" style="text-align: left;"> diff --git a/static/nadmin.html b/static/nadmin.html index 984eb76..877e1e5 100644 --- a/static/nadmin.html +++ b/static/nadmin.html @@ -17,6 +17,14 @@ <option value="False">False</option> </select> </form> + + <h3>Post News</h3> + <form id="news" action="/{{ .Key }}/postnews" method="post" enctype="application/x-www-form-urlencoded"> + <label>Title:</label><br> + <input type="text" name="title" placeholder="{{.Actor}} was created!" required><input type="submit" value="Post"><br> + <label>Content:</label><br> + <textarea name="summary" rows="8" cols="50"></textarea><br> + </form> <ul style="display: inline-block; padding: 0;"> <li style="display: inline-block;"><a href="#following">Subscribed</a></li> diff --git a/static/news.html b/static/news.html new file mode 100644 index 0000000..757c420 --- /dev/null +++ b/static/news.html @@ -0,0 +1,32 @@ +{{ define "header" }} +<title>{{ .Title }}</title> +<meta name="description" content="{{ .PreferredUsername }} is a federated image board based on activitypub. The current version of the code running the server is still a work in progress, expect a bumpy ride for the time being. Get the server code here: https://github.com/FChannel0."> + +<meta property="og:locale" content="en_US" /> +<meta property="og:type" content="website" /> +<meta property="og:url" content="{{ .Board.Domain }}"> +<meta property="og:site_name" content="{{ .Board.Actor.PreferredUsername }}" /> + +<meta property="og:title" content="{{ .Title }}"> +<meta property="og:description" content="{{ .PreferredUsername }} is a federated image board based on activitypub. The current version of the code running the server is still a work in progress, expect a bumpy ride for the time being. Get the server code here: https://github.com/FChannel0."> + +<meta name="twitter:title" content="{{ .Title }}"> +<meta name="twitter:description" content="{{ .PreferredUsername }} is a federated image board based on activitypub. The current version of the code running the server is still a work in progress, expect a bumpy ride for the time being. Get the server code here: https://github.com/FChannel0."> +<meta name="twitter:card" content="summary_large_image"> + +{{ end }} + +{{ define "top" }}{{ end }} +{{ define "content" }} +<div style="text-align: left; max-width: 800px; margin: 0 auto;"> + + {{ range .NewsItems }} + <p><h1>{{unixtoreadable .Time}} - {{.Title}}</h1><br>{{.Content}}</p> + {{ end }} + +</div> +{{ end }} +{{ define "bottom" }}{{ end }} + +{{ define "script" }} +{{ end }} |