aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--client.go78
-rw-r--r--database.go75
-rw-r--r--databaseschema.psql8
-rw-r--r--main.go47
-rw-r--r--static/admin.html2
-rw-r--r--static/anews.html46
-rw-r--r--static/bottom.html4
-rw-r--r--static/index.html41
-rw-r--r--static/js/footerscript.js24
-rw-r--r--static/js/posts.js273
-rw-r--r--static/js/timer.js38
-rw-r--r--static/main.html2
-rw-r--r--static/nadmin.html8
-rw-r--r--static/news.html32
-rw-r--r--static/npost.html2
15 files changed, 573 insertions, 107 deletions
diff --git a/client.go b/client.go
index 6e185cc..f71fb7d 100644
--- a/client.go
+++ b/client.go
@@ -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,68 @@ 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
+ //almost certainly there is a better algorithm for this but the old one was wrong
+ //and I suck at math. This works at least.
+ data.BoardRemainer = make([]int, 3-(len(data.Boards) % 3))
+ if(len(data.BoardRemainer) == 3){
+ data.BoardRemainer = make([]int, 0)
+ }
data.InstanceIndex = GetCollectionFromReq("https://fchan.xyz/followers").Items
+ data.NewsItems = getNewsFromDB(db, 3)
+
+ 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)
+
+ 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 AllNewsGet(w http.ResponseWriter, r *http.Request, db *sql.DB) {
+ 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/anews.html"))
- t.ExecuteTemplate(w, "layout", data)
+ actor := GetActorFromDB(db, Domain)
+
+ var data PageData
+ data.PreferredUsername = actor.PreferredUsername
+ data.Title = actor.PreferredUsername + " News"
+ 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 = getNewsFromDB(db, 0)
+
+ 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 70b7572..baae16a 100644
--- a/database.go
+++ b/database.go
@@ -12,9 +12,9 @@ import (
)
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)
@@ -1518,3 +1518,74 @@ func MarkObjectSensitive(db *sql.DB, id string, sensitive bool) {
CheckError(err, "error updating sensitive object in cacheactivitystream")
}
+
+//if limit less than 1 return all news items
+func getNewsFromDB(db *sql.DB, limit int) []NewsItem {
+ var news []NewsItem
+
+ var query string
+ if(limit > 0) {
+ query =`select title, content, time from newsItem order by time desc limit $1`
+ } else {
+ query =`select title, content, time from newsItem order by time desc`
+ }
+
+ var rows *sql.Rows
+ var err error
+ if(limit > 0) {
+ rows, err = db.Query(query, limit)
+ } else {
+ rows, err = db.Query(query)
+ }
+
+
+ if CheckError(err, "could not get news from db query") != nil {
+ return news
+ }
+
+ 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 news
+ }
+ news = append(news, n)
+ }
+
+ return news
+}
+
+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 deleteNewsItemFromDB(db *sql.DB, timestamp int) {
+ query := `delete from newsItem where time=$1`
+ db.Exec(query, timestamp)
+}
+
+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;
diff --git a/main.go b/main.go
index b7c880b..7a30721 100644
--- a/main.go
+++ b/main.go
@@ -290,6 +290,27 @@ 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(len(timestamp) < 2) {
+ AllNewsGet(w, r, db)
+ return
+ }
+
+ 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 +623,32 @@ 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("/" + *Key + "/newsdelete/", func(w http.ResponseWriter, r *http.Request){
+ timestamp := r.URL.Path[13+len(*Key):]
+
+ tsint, err := strconv.Atoi(timestamp)
+
+ if(err != nil){
+ w.WriteHeader(http.StatusForbidden)
+ w.Write([]byte("404 no path"))
+ return
+ } else {
+ deleteNewsItemFromDB(db, tsint)
+ http.Redirect(w, r, "/news/", http.StatusSeeOther)
+ }
+ })
http.HandleFunc("/verify", func(w http.ResponseWriter, r *http.Request){
if(r.Method == "POST") {
diff --git a/static/admin.html b/static/admin.html
index a4c61c1..f238ec0 100644
--- a/static/admin.html
+++ b/static/admin.html
@@ -6,7 +6,7 @@
<body>
<div style="margin: 0 auto; width: 400px;">
<h3>Add Board</h3>
- <form id="new-post" action="/{{ .Key }}/addboard" method="post" enctype="application/x-www-form-urlencoded">
+ <form id="new-board" action="/{{ .Key }}/addboard" method="post" enctype="application/x-www-form-urlencoded">
<label>Name:</label><br>
<input type="text" name="name" placeholder="g" required><br>
<label>Prefered Name:</label><br>
diff --git a/static/anews.html b/static/anews.html
new file mode 100644
index 0000000..127760a
--- /dev/null
+++ b/static/anews.html
@@ -0,0 +1,46 @@
+{{ 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: center; max-width: 800px; margin: 0 auto;">
+ <h1>{{ .Title }}</h1>
+
+ <div style="margin-top:50px;">
+ <table align="center" style="text-align: left;">
+
+ {{ range $i, $e := .NewsItems }}
+ <tr>
+ <td>
+ {{ if $.Board.ModCred }}<a href="/{{ $.Key }}/newsdelete/{{ $e.Time }}">[Delete] </a>{{end}}
+ <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>
+{{ end }}
+{{ define "bottom" }}{{ end }}
+
+{{ define "script" }}
+{{ end }}
diff --git a/static/bottom.html b/static/bottom.html
index db9606d..994ad89 100644
--- a/static/bottom.html
+++ b/static/bottom.html
@@ -4,7 +4,7 @@
<form id="reply-post" action="/post" method="post" enctype="multipart/form-data">
<input id="reply-name" name="name" size="43" type="text" placeholder="Name" maxlength="100">
<input id="reply-options" name="options" size="43" type="text" placeholder="Options" maxlength="100">
- <textarea id="reply-comment" name="comment" rows="12" cols="54" style="width: 396px;" maxlength="2000"></textarea>
+ <textarea id="reply-comment" name="comment" rows="12" cols="54" style="width: 396px;" maxlength="2000" oninput="sessionStorage.setItem('element-reply-comment', document.getElementById('reply-comment').value)"></textarea>
<input id="reply-file" name="file" type="file">
<input id="reply-submit" type="submit" value="Reply" style="float: right;"><br><br>
<input type="hidden" id="inReplyTo-box" name="inReplyTo" value="{{ .Board.InReplyTo }}">
@@ -27,7 +27,7 @@
<div id="report-header" style="text-align: center; display: inline-block; width: 370px; z-index: 0; cursor: move;"></div><div id="report-close" style="display: inline-block; float: right;"><a href="javascript:closeReport()">[X]</a></div>
<form id="report-post" action="/report" method="post">
<label for="comment">Reason:</label>
- <textarea id="report-comment" name="comment" rows="12" cols="54" style="width: 396px;" maxlength="100"></textarea>
+ <textarea id="report-comment" name="comment" rows="12" cols="54" style="width: 396px;" maxlength="100" oninput="sessionStorage.setItem('element-report-comment', document.getElementById('report-comment').value)"></textarea>
<input id="report-submit" type="submit" value="Report" style="float: right;">
<input type="hidden" id="report-inReplyTo-box" name="id" value="{{ .Board.InReplyTo }}">
<input type="hidden" id="sendTo" name="sendTo" value="{{ .Board.To }}">
diff --git a/static/index.html b/static/index.html
index ab0bea0..61aeab8 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,40 @@
{{ 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><a href="/news">{{ .PreferredUsername }} news</a></tr>
+ </th>
+
+ {{ range $i, $e := .NewsItems }}
+ <tr>
+ <td>{{ if $.Board.ModCred }}<a href="/{{ $.Key }}/newsdelete/{{ $e.Time }}">[Delete] </a>{{end}}
+ <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/js/footerscript.js b/static/js/footerscript.js
index a63f422..69e56e7 100644
--- a/static/js/footerscript.js
+++ b/static/js/footerscript.js
@@ -3,34 +3,34 @@ var imgArray = [].slice.call(imgs);
imgArray.forEach(function(img, i){
img.addEventListener("click", function(e){
- var id = img.getAttribute("id")
- var media = document.getElementById("media-" + id)
- var sensitive = document.getElementById("sensitive-" + id)
+ var id = img.getAttribute("id");
+ var media = document.getElementById("media-" + id);
+ var sensitive = document.getElementById("sensitive-" + id);
if(img.getAttribute("enlarge") == "0")
{
- var attachment = img.getAttribute("attachment")
+ var attachment = img.getAttribute("attachment");
img.setAttribute("enlarge", "1");
img.setAttribute("style", "float: left; margin-right: 10px; cursor: pointer;");
- img.src = attachment
+ img.src = attachment;
}
else
{
- var preview = img.getAttribute("preview")
+ var preview = img.getAttribute("preview");
img.setAttribute("enlarge", "0");
if(img.getAttribute("main") == 1)
{
img.setAttribute("style", "float: left; margin-right: 10px; max-width: 250px; max-height: 250px; cursor: pointer;");
- img.src = preview
+ img.src = preview;
}
else
{
img.setAttribute("style", "float: left; margin-right: 10px; max-width: 125px; max-height: 125px; cursor: pointer;");
- img.src = preview
+ img.src = preview;
}
}
});
-})
+});
function viewLink(board, actor) {
@@ -38,7 +38,7 @@ function viewLink(board, actor) {
var postsArray = [].slice.call(posts);
postsArray.forEach(function(p, i){
- var id = p.getAttribute("post")
- p.href = "/" + board + "/" + shortURL(actor, id)
- })
+ var id = p.getAttribute("post");
+ p.href = "/" + board + "/" + shortURL(actor, id);
+ });
}
diff --git a/static/js/posts.js b/static/js/posts.js
index 79fb7c4..d91fadd 100644
--- a/static/js/posts.js
+++ b/static/js/posts.js
@@ -1,33 +1,44 @@
+function startNewPost(){
+ var el = document.getElementById("newpostbtn");
+ el.style="display:none;";
+ el.setAttribute("state", "1");
+ document.getElementById("newpost").style = "display: block;";
+}
+
+function stopNewPost(){
+ var el = document.getElementById("newpostbtn");
+ el.style="display:block;";
+ el.setAttribute("state", "0");
+ document.getElementById("newpost").style = "display: hidden;";
+}
+
function newpost()
{
- var el = document.getElementById("newpostbtn")
- var state = el.getAttribute("state")
- if(state = "0")
+ var state = document.getElementById("newpostbtn").getAttribute("state");
+ if(state === "0")
{
- el.style="display:none;"
- el.setAttribute("state", "1")
- document.getElementById("newpost").style = "display: block;";
+ startNewPost();
+ sessionStorage.setItem("newpostState", true);
}
else
{
- el.style="display:block;"
- el.setAttribute("state", "0")
- document.getElementById("newpost").style = "display: hidden;";
+ stopNewPost();
+ sessionStorage.setItem("newpostState", false);
}
}
function getMIMEType(type)
{
- re = /\/.+/g
- return type.replace(re, "")
+ re = /\/.+/g;
+ return type.replace(re, "");
}
function shortURL(actorName, url)
{
re = /.+\//g;
- temp = re.exec(url)
+ temp = re.exec(url);
- var output
+ var output;
if(stripTransferProtocol(temp[0]) == stripTransferProtocol(actorName) + "/")
{
@@ -55,18 +66,18 @@ function shortURL(actorName, url)
u = re.exec(short);
- str = short.replace(/\/+/g, " ")
+ str = short.replace(/\/+/g, " ");
- str = str.replace(u, " ").trim()
+ str = str.replace(u, " ").trim();
re = /(\w|[!@#$%^&*<>])+$/;
- v = re.exec(str)
+ v = re.exec(str);
output = "f" + v[0] + "-" + u
}
- return output
+ return output;
}
function shortImg(url)
@@ -92,22 +103,22 @@ function convertSize(size)
var convert = size / 1024.0;
if(convert > 1024)
{
- convert = convert / 1024.0
- convert = convert.toFixed(2) + " MB"
+ convert = convert / 1024.0;
+ convert = convert.toFixed(2) + " MB";
}
else
{
- convert = convert.toFixed(2) + " KB"
+ convert = convert.toFixed(2) + " KB";
}
- return convert
+ return convert;
}
function getBoardId(url)
{
- var re = /\/([^/\n]+)(.+)?/gm
+ var re = /\/([^/\n]+)(.+)?/gm;
var matches = re.exec(url);
- return matches[1]
+ return matches[1];
}
function convertContent(actorName, content, opid)
@@ -118,24 +129,24 @@ function convertContent(actorName, content, opid)
if(match)
{
match.forEach(function(quote, i){
- var link = quote.replace('>>', '')
- var isOP = ""
+ var link = quote.replace('>>', '');
+ var isOP = "";
if(link == opid)
{
isOP = " (OP)";
}
- var q = link
+ var q = link;
if(document.getElementById(link + "-content") != null) {
q = document.getElementById(link + "-content").innerText;
- q = q.replaceAll('>', '/\>')
- q = q.replaceAll('"', '')
- q = q.replaceAll("'", "")
+ q = q.replaceAll('>', '/\>');
+ q = q.replaceAll('"', '');
+ q = q.replaceAll("'", "");
}
newContent = newContent.replace(quote, '<a class="reply" title="' + q + '" href="'+ (actorName) + "/" + shortURL(actorName, opid) + '#' + shortURL(actorName, link) + '";">>>' + shortURL(actorName, link) + isOP + '</a>');
- })
+ });
}
re = /^(\s+)?>.+/gm;
@@ -146,10 +157,10 @@ function convertContent(actorName, content, opid)
match.forEach(function(quote, i) {
newContent = newContent.replace(quote, '<span class="quote">' + quote + '</span>');
- })
+ });
}
- return newContent.replaceAll('/\>', '>')
+ return newContent.replaceAll('/\>', '>');
}
function convertContentNoLink(actorName, content, opid)
@@ -160,36 +171,40 @@ function convertContentNoLink(actorName, content, opid)
if(match)
{
match.forEach(function(quote, i){
- var link = quote.replace('>>', '')
- var isOP = ""
+ var link = quote.replace('>>', '');
+ var isOP = "";
if(link == opid)
{
isOP = " (OP)";
}
- var q = link
+ var q = link;
if(document.getElementById(link + "-content") != null) {
q = document.getElementById(link + "-content").innerText;
}
newContent = newContent.replace(quote, '>>' + shortURL(actorName, link) + isOP);
- })
+ });
}
- newContent = newContent.replaceAll("'", "")
- return newContent.replaceAll('"', '')
+ newContent = newContent.replaceAll("'", "");
+ return newContent.replaceAll('"', '');
}
function closeReply()
{
document.getElementById("reply-box").style.display = "none";
- document.getElementById("reply-comment").value = "";
+ document.getElementById("reply-comment").value = "";
+
+ sessionStorage.setItem("element-closed-reply", true);
}
function closeReport()
{
document.getElementById("report-box").style.display = "none";
document.getElementById("report-comment").value = "";
+
+ sessionStorage.setItem("element-closed-report", true);
}
@@ -211,10 +226,11 @@ function next(actorName, totalPage, page)
function quote(actorName, opid, id)
{
+ sessionStorage.setItem("element-closed-reply", false);
var box = document.getElementById("reply-box");
var header = document.getElementById("reply-header");
var comment = document.getElementById("reply-comment");
- var inReplyTo = document.getElementById("inReplyTo-box");
+ var inReplyTo = document.getElementById("inReplyTo-box");
var w = window.innerWidth / 2 - 200;
if(id == "reply") {
@@ -223,8 +239,11 @@ function quote(actorName, opid, id)
var h = document.getElementById(id + "-content").offsetTop - 348;
}
-
- box.setAttribute("style", "display: block; position: absolute; width: 400px; height: 600px; z-index: 9; top: " + h + "px; left: " + w + "px; padding: 5px;");
+ const boxStyle = "display: block; position: absolute; width: 400px; height: 600px; z-index: 9; top: " + h + "px; left: " + w + "px; padding: 5px;";
+ box.setAttribute("style", boxStyle);
+ sessionStorage.setItem("element-reply-style", boxStyle);
+ sessionStorage.setItem("reply-top", h);
+ sessionStorage.setItem("reply-left", w);
if (inReplyTo.value != opid)
@@ -232,9 +251,12 @@ function quote(actorName, opid, id)
header.innerText = "Replying to Thread No. " + shortURL(actorName, opid);
inReplyTo.value = opid;
+ sessionStorage.setItem("element-reply-actor", actorName);
+ sessionStorage.setItem("element-reply-id", inReplyTo.value);
if(id != "reply")
comment.value += ">>" + id + "\n";
+ sessionStorage.setItem("element-reply-comment", comment.value);
dragElement(header);
@@ -242,66 +264,161 @@ function quote(actorName, opid, id)
function report(actorName, id)
{
+ sessionStorage.setItem("element-closed-report", false);
var box = document.getElementById("report-box");
var header = document.getElementById("report-header");
var comment = document.getElementById("report-comment");
- var inReplyTo = document.getElementById("report-inReplyTo-box");
+ var inReplyTo = document.getElementById("report-inReplyTo-box");
var w = window.innerWidth / 2 - 200;
var h = document.getElementById(id + "-content").offsetTop - 348;
- box.setAttribute("style", "display: block; position: absolute; width: 400px; height: 480px; z-index: 9; top: " + h + "px; left: " + w + "px; padding: 5px;");
+ const boxStyle = "display: block; position: absolute; width: 400px; height: 480px; z-index: 9; top: " + h + "px; left: " + w + "px; padding: 5px;";
+ box.setAttribute("style", boxStyle);
+ sessionStorage.setItem("element-report-style", boxStyle);
+ sessionStorage.setItem("report-top", h);
+ sessionStorage.setItem("report-left", w);
header.innerText = "Report Post No. " + shortURL(actorName, id);
inReplyTo.value = id;
+ sessionStorage.setItem("element-report-actor", actorName);
+ sessionStorage.setItem("element-report-id", id);
dragElement(header);
}
+var pos1, pos2, pos3, pos4;
+var elmnt;
+
+function closeDragElement(e) {
+ // stop moving when mouse button is released:
+ document.onmouseup = null;
+ document.onmousemove = null;
+ sessionStorage.setItem("eventhandler", false);
+}
+
+function elementDrag(e) {
+ e = e || window.event;
+ e.preventDefault();
+ // calculate the new cursor position:
+ pos1 = pos3 - e.clientX;
+ pos2 = pos4 - e.clientY;
+ pos3 = e.clientX;
+ pos4 = e.clientY;
+ sessionStorage.setItem("pos1", pos1);
+ sessionStorage.setItem("pos2", pos2);
+ sessionStorage.setItem("pos3", pos3);
+ sessionStorage.setItem("pos4", pos4);
+
+ // set the element's new position:
+ elmnt.parentElement.style.top = (elmnt.parentElement.offsetTop - pos2) + "px";
+ elmnt.parentElement.style.left = (elmnt.parentElement.offsetLeft - pos1) + "px";
+ if(elmnt.id.startsWith("report")){
+ sessionStorage.setItem("report-top", elmnt.parentElement.style.top);
+ sessionStorage.setItem("report-left", elmnt.parentElement.style.left);
+ }else if(elmnt.id.startsWith("reply")){
+ sessionStorage.setItem("reply-top", elmnt.parentElement.style.top);
+ sessionStorage.setItem("reply-left", elmnt.parentElement.style.left);
+ }
+}
+
+function dragMouseDown(e) {
+ e = e || window.event;
+ e.preventDefault();
+
+ // get the mouse cursor position at startup:
+ pos3 = e.clientX;
+ pos4 = e.clientY;
+ sessionStorage.setItem("pos3", pos3);
+ sessionStorage.setItem("pos4", pos4);
+
+ elmnt = e.currentTarget;
+
+ // call a function whenever the cursor moves:
+ document.onmouseup = closeDragElement;
+ document.onmousemove = elementDrag;
+ sessionStorage.setItem("eventhandler", true);
+
+}
+
function dragElement(elmnt) {
- var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
-
elmnt.onmousedown = dragMouseDown;
+}
- function dragMouseDown(e) {
- e = e || window.event;
- e.preventDefault();
- // get the mouse cursor position at startup:
- pos3 = e.clientX;
- pos4 = e.clientY;
- document.onmouseup = closeDragElement;
- // call a function whenever the cursor moves:
- document.onmousemove = elementDrag;
- }
+const stateLoadHandler = function(event){
+ pos1 = parseInt(sessionStorage.getItem("pos1"));
+ pos2 = parseInt(sessionStorage.getItem("pos2"));
+ pos3 = parseInt(sessionStorage.getItem("pos3"));
+ pos4 = parseInt(sessionStorage.getItem("pos4"));
- function elementDrag(e) {
- e = e || window.event;
- e.preventDefault();
- // calculate the new cursor position:
- pos1 = pos3 - e.clientX;
- pos2 = pos4 - e.clientY;
- pos3 = e.clientX;
- pos4 = e.clientY;
- // set the element's new position:
- elmnt.parentElement.style.top = (elmnt.parentElement.offsetTop - pos2) + "px";
- elmnt.parentElement.style.left = (elmnt.parentElement.offsetLeft - pos1) + "px";
- }
+ if(sessionStorage.getItem("element-closed-report") === "false"){
+ var box = document.getElementById("report-box");
+ var header = document.getElementById("report-header");
+ var comment = document.getElementById("report-comment");
+ var inReplyTo = document.getElementById("report-inReplyTo-box");
- function closeDragElement() {
- // stop moving when mouse button is released:
- document.onmouseup = null;
- document.onmousemove = null;
+ header.onmousedown = dragMouseDown;
+ inReplyTo.value = parseInt(sessionStorage.getItem("element-report-id"));
+ header.innerText = "Report Post No. " + shortURL(sessionStorage.getItem("element-report-actor"), sessionStorage.getItem("element-report-id"));
+ comment.value = sessionStorage.getItem("element-report-comment");
+
+ box.setAttribute("style", sessionStorage.getItem("element-report-style"));
+
+ box.style.top = sessionStorage.getItem("report-top");
+ box.style.left = sessionStorage.getItem("report-left");
+
+ if(sessionStorage.getItem("eventhandler") === "true"){
+ elmnt = header;
+ document.onmouseup = closeDragElement;
+ document.onmousemove = elementDrag;
+ }else{
+ document.onmouseup = null;
+ document.onmousemove = null;
+ }
}
-}
+ if(sessionStorage.getItem("element-closed-reply") === "false"){
+ var box = document.getElementById("reply-box");
+ var header = document.getElementById("reply-header");
+ var comment = document.getElementById("reply-comment");
+ var inReplyTo = document.getElementById("inReplyTo-box");
+
+ header.onmousedown = dragMouseDown;
+ inReplyTo.value = parseInt(sessionStorage.getItem("element-reply-id"));
+ header.innerText = "Replying to Thread No. " + shortURL(sessionStorage.getItem("element-reply-actor"), sessionStorage.getItem("element-reply-id"));
+ comment.value = sessionStorage.getItem("element-reply-comment");
+
+ pos1 = parseInt(sessionStorage.getItem("pos1"));
+ pos2 = parseInt(sessionStorage.getItem("pos2"));
+ pos3 = parseInt(sessionStorage.getItem("pos3"));
+ pos4 = parseInt(sessionStorage.getItem("pos4"));
+
+ box.setAttribute("style", sessionStorage.getItem("element-reply-style"));
+
+ box.style.top = sessionStorage.getItem("reply-top");
+ box.style.left = sessionStorage.getItem("reply-left");
+
+ if(sessionStorage.getItem("eventhandler") === "true"){
+ elmnt = header;
+ document.onmouseup = closeDragElement;
+ document.onmousemove = elementDrag;
+ }else{
+ document.onmouseup = null;
+ document.onmousemove = null;
+ }
+ }
+};
+
+document.addEventListener("DOMContentLoaded", stateLoadHandler, false);
function stripTransferProtocol(value){
- var re = /(https:\/\/|http:\/\/)?(www.)?/
- return value.replace(re, "")
+ var re = /(https:\/\/|http:\/\/)?(www.)?/;
+ return value.replace(re, "");
}
function isOnion(value){
- var re = /\.onion/
+ var re = /\.onion/;
if(value.match(re) != null)
- return true
- return false
+ return true;
+ return false;
}
+
diff --git a/static/js/timer.js b/static/js/timer.js
new file mode 100644
index 0000000..8f6516c
--- /dev/null
+++ b/static/js/timer.js
@@ -0,0 +1,38 @@
+var timerCount;
+var timerToggle = false;
+var timer;
+const contentLoadHandler = function(event){
+ timerToggle = !!document.getElementById("autoreload-checkbox").checked;
+ if(timerToggle){
+ timerCount = 5;
+ document.getElementById("autoreload-countdown").innerHTML = "5";
+ document.getElementById("autoreload-countdown").style.visibility = "visible";
+ timer = setInterval(timerFunction, 1000);
+ document.removeEventListener("DOMContentLoaded", contentLoadHandler, false);
+ }
+};
+
+document.addEventListener("DOMContentLoaded", contentLoadHandler, false);
+
+function timerFunction(){
+ timerCount--;
+ document.getElementById("autoreload-countdown").innerHTML = timerCount;
+ if(timerCount <= 0){
+ document.getElementById("autoreload-countdown").innerHTML = "Refreshing...";
+ clearInterval(timer);
+ location.reload();
+ }
+}
+
+function autoTimer(){
+ timerToggle = !timerToggle;
+ if(timerToggle === true){
+ timerCount = 5;
+ document.getElementById("autoreload-countdown").innerHTML = "5";
+ document.getElementById("autoreload-countdown").style.visibility = "visible";
+ timer = setInterval(timerFunction, 1000);
+ }else{
+ clearInterval(timer);
+ document.getElementById("autoreload-countdown").style.visibility = "hidden";
+ }
+} \ No newline at end of file
diff --git a/static/main.html b/static/main.html
index 72ed321..70e3c23 100644
--- a/static/main.html
+++ b/static/main.html
@@ -7,7 +7,7 @@
<meta name="keywords" content="Federated Imageboard based on Activtypub">
<meta property="og:locale" content="en_US" />
<meta property="og:type" content="website" />
- <link rel="icon" type="image/png" href="/static/favicon.png">
+ <link rel="icon" type="image/png" href="/static/favicon.png">
<style>
a, a:link, a:visited, a:hover, a:active {
text-decoration: none
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 }}
diff --git a/static/npost.html b/static/npost.html
index 740018b..3df4090 100644
--- a/static/npost.html
+++ b/static/npost.html
@@ -17,6 +17,7 @@
{{ end }}
<script src="/static/js/posts.js"></script>
+<script src="/static/js/timer.js"></script>
{{ end }}
{{ define "content" }}
@@ -39,6 +40,7 @@
<li style="display: inline"><a href="/{{ $board.Name }}/catalog">[Catalog]</a></li>
<li style="display: inline"><a id="bottom" href="#top">[Top]</a></li>
<li style="display: inline"><a href="javascript:location.reload()">[Refresh]</a></li>
+ <li style="display: inline"><input id="autoreload-checkbox" type="checkbox" onclick="autoTimer()"> Auto refresh <span id="autoreload-countdown" style="visibility: hidden;">0</span></li>
</ul>
{{ $replies := (index .Posts 0).Replies }}
<span style="float: right;">{{ $replies.TotalItems }} / {{ $replies.TotalImgs }}</span>