aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFChannel <>2022-06-18 13:57:30 -0700
committerFChannel <>2022-06-19 12:53:29 -0700
commit25829d2d0e379c323b8f2ae6e7c2aad7548f0a30 (patch)
treec828eb5bb04ce04141c717c26b637a59b32ab979
parentf3d1683d6562afb30522afb98fb3a22456473275 (diff)
sticky and lock implemented
-rw-r--r--activitypub/actor.go89
-rw-r--r--activitypub/object.go99
-rw-r--r--activitypub/structs.go (renamed from activitypub/activityPubStruct.go)2
-rw-r--r--databaseschema.psql12
-rw-r--r--main.go2
-rw-r--r--route/routes/boardmgmt.go96
-rw-r--r--route/util.go6
-rw-r--r--static/locked.pngbin0 -> 717 bytes
-rw-r--r--static/pin.pngbin0 -> 6118 bytes
-rw-r--r--views/catalog.html2
-rw-r--r--views/partials/posts.html8
-rw-r--r--views/partials/top.html6
12 files changed, 314 insertions, 8 deletions
diff --git a/activitypub/actor.go b/activitypub/actor.go
index 9996abd..4142685 100644
--- a/activitypub/actor.go
+++ b/activitypub/actor.go
@@ -214,11 +214,18 @@ func (actor Actor) GetCatalogCollection() (Collection, error) {
var err error
var rows *sql.Rows
- 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 actor=$1 and id in (select id from replies where inreplyto='') and type='Note' union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type='Note' union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type='Note') as x order by x.updated desc limit 165`
+ 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 actor=$1 and id in (select id from replies where inreplyto='') and type='Note' and id not in (select activity_id from sticky where actor_id=$1) union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type='Note' and id not in (select activity_id from sticky where actor_id=$1) union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type='Note' and id not in (select activity_id from sticky where actor_id=$1)) as x order by x.updated desc limit 165`
+
if rows, err = config.DB.Query(query, actor.Id); err != nil {
return nColl, util.MakeError(err, "GetCatalogCollection")
}
+ stickies, _ := actor.GetStickies()
+
+ for _, e := range stickies.OrderedItems {
+ result = append(result, e)
+ }
+
defer rows.Close()
for rows.Next() {
var post ObjectBase
@@ -236,6 +243,7 @@ func (actor Actor) GetCatalogCollection() (Collection, error) {
return nColl, util.MakeError(err, "GetCatalogCollection")
}
+ post.Locked, _ = post.IsLocked()
post.Actor = actor.Id
var replies CollectionBase
@@ -277,9 +285,22 @@ func (actor Actor) GetCollectionPage(page int) (Collection, error) {
var err error
var rows *sql.Rows
- query := `select count (x.id) over(), 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 actor=$1 and id in (select id from replies where inreplyto='') and type='Note' union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type='Note' union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type='Note') as x order by x.updated desc limit 15 offset $2`
+ query := `select count (x.id) over(), 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 actor=$1 and id in (select id from replies where inreplyto='') and type='Note' and id not in (select activity_id from sticky where actor_id=$1) union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type='Note' and id not in (select activity_id from sticky where actor_id=$1) union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where id not in (select activity_id from sticky where actor_id=$1) and actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type='Note') as x order by x.updated desc limit $2 offset $3`
+
+ limit := 15
+
+ if page == 0 {
+ stickies, _ := actor.GetStickies()
+ limit = limit - stickies.TotalItems
+
+ for _, e := range stickies.OrderedItems {
+ result = append(result, e)
+ }
+ }
- if rows, err = config.DB.Query(query, actor.Id, page*15); err != nil {
+ offset := page * limit
+
+ if rows, err = config.DB.Query(query, actor.Id, limit, offset); err != nil {
return nColl, util.MakeError(err, "GetCollectionPage")
}
@@ -301,6 +322,7 @@ func (actor Actor) GetCollectionPage(page int) (Collection, error) {
return nColl, util.MakeError(err, "GetCollectionPage")
}
+ post.Locked, _ = post.IsLocked()
post.Actor = actor.Id
post.Replies, post.Replies.TotalItems, post.Replies.TotalImgs, err = post.GetRepliesLimit(5)
@@ -359,6 +381,9 @@ func (actor Actor) GetCollection() (Collection, error) {
return nColl, util.MakeError(err, "GetCollection")
}
+ post.Sticky, _ = post.IsSticky()
+ post.Locked, _ = post.IsLocked()
+
post.Actor = actor.Id
post.Replies, post.Replies.TotalItems, post.Replies.TotalImgs, err = post.GetReplies()
@@ -1181,6 +1206,10 @@ func (actor Actor) ProcessInboxCreate(activity Activity) error {
return util.MakeError(errors.New("Object does not exist"), "ActorInbox")
}
+ if locked, _ := activity.Object.InReplyTo[0].IsLocked(); locked {
+ return util.MakeError(errors.New("Object locked"), "ActorInbox")
+ }
+
if wantToCache, err := activity.Object.WantToCache(actor); !wantToCache {
return util.MakeError(err, "ActorInbox")
}
@@ -1197,3 +1226,57 @@ func (actor Actor) ProcessInboxCreate(activity Activity) error {
return nil
}
+
+func (actor Actor) GetStickies() (Collection, error) {
+ var nColl Collection
+ var result []ObjectBase
+
+ query := `select count (x.id) over(), 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 actor=$1 and id in (select id from replies where inreplyto='') and type='Note' and id in (select activity_id from sticky where actor_id=$1) union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type='Note' union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type='Note' and id in (select activity_id from sticky where actor_id=$1)) as x order by x.updated desc limit 15`
+
+ rows, err := config.DB.Query(query, actor.Id)
+
+ if err != nil {
+ return nColl, util.MakeError(err, "GetStickies")
+ }
+
+ var count int
+ defer rows.Close()
+ for rows.Next() {
+ var post ObjectBase
+ var actor Actor
+
+ var attch ObjectBase
+ post.Attachment = append(post.Attachment, attch)
+
+ var prev NestedObjectBase
+ post.Preview = &prev
+
+ err = rows.Scan(&count, &post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &post.Attachment[0].Id, &post.Preview.Id, &actor.Id, &post.TripCode, &post.Sensitive)
+
+ if err != nil {
+ return nColl, util.MakeError(err, "GetStickies")
+ }
+
+ post.Sticky = true
+ post.Locked, _ = post.IsLocked()
+ post.Actor = actor.Id
+
+ var postCnt int
+ var imgCnt int
+ post.Replies, postCnt, imgCnt, _ = post.GetRepliesLimit(5)
+
+ post.Replies.TotalItems = postCnt
+ post.Replies.TotalImgs = imgCnt
+
+ post.Attachment, _ = post.Attachment[0].GetAttachment()
+
+ post.Preview, _ = post.Preview.GetPreview()
+
+ result = append(result, post)
+ }
+
+ nColl.TotalItems = count
+ nColl.OrderedItems = result
+
+ return nColl, nil
+}
diff --git a/activitypub/object.go b/activitypub/object.go
index 5eb8e67..acbe4a1 100644
--- a/activitypub/object.go
+++ b/activitypub/object.go
@@ -367,6 +367,9 @@ func (obj ObjectBase) GetCollectionLocal() (Collection, error) {
return nColl, util.MakeError(err, "GetCollectionLocal")
}
+ post.Sticky, _ = post.IsSticky()
+ post.Locked, _ = post.IsLocked()
+
post.Actor = actor.Id
if post.InReplyTo, err = post.GetInReplyTo(); err != nil {
@@ -462,6 +465,9 @@ func (obj ObjectBase) GetCollectionFromPath() (Collection, error) {
return nColl, nil
}
+ post.Sticky, _ = post.IsSticky()
+ post.Locked, _ = post.IsLocked()
+
post.Actor = actor.Id
if post.InReplyTo, err = post.GetInReplyTo(); err != nil {
@@ -1361,3 +1367,96 @@ func (obj ObjectBase) WriteWithAttachment(attachment ObjectBase) {
panic(e)
}
}
+
+func (obj ObjectBase) MarkSticky(actorID string) error {
+ var count int
+
+ var query = `select count(id) from replies where inreplyto='' and id=$1`
+ if err := config.DB.QueryRow(query, obj.Id).Scan(&count); err != nil {
+ return util.MakeError(err, "MarkSticky")
+ }
+
+ if count == 1 {
+ var nCount int
+ query = `select count(activity_id) from sticky where activity_id=$1`
+ if err := config.DB.QueryRow(query, obj.Id).Scan(&nCount); err != nil {
+ return util.MakeError(err, "MarkSticky")
+ }
+
+ if nCount > 0 {
+ query = `delete from sticky where activity_id=$1`
+ if _, err := config.DB.Exec(query, obj.Id); err != nil {
+ return util.MakeError(err, "MarkSticky")
+ }
+ } else {
+ query = `insert into sticky (actor_id, activity_id) values ($1, $2)`
+ if _, err := config.DB.Exec(query, actorID, obj.Id); err != nil {
+ return util.MakeError(err, "MarkSticky")
+ }
+ }
+ }
+
+ return nil
+}
+
+func (obj ObjectBase) MarkLocked(actorID string) error {
+ var count int
+
+ var query = `select count(id) from replies where inreplyto='' and id=$1`
+ if err := config.DB.QueryRow(query, obj.Id).Scan(&count); err != nil {
+ return util.MakeError(err, "MarkLocked")
+ }
+
+ if count == 1 {
+ var nCount int
+
+ query = `select count(activity_id) from locked where activity_id=$1`
+ if err := config.DB.QueryRow(query, obj.Id).Scan(&nCount); err != nil {
+ return util.MakeError(err, "MarkLocked")
+ }
+
+ if nCount > 0 {
+ query = `delete from locked where activity_id=$1`
+ if _, err := config.DB.Exec(query, obj.Id); err != nil {
+ return util.MakeError(err, "MarkLocked")
+ }
+ } else {
+ query = `insert into locked (actor_id, activity_id) values ($1, $2)`
+ if _, err := config.DB.Exec(query, actorID, obj.Id); err != nil {
+ return util.MakeError(err, "MarkLocked")
+ }
+ }
+ }
+
+ return nil
+}
+
+func (obj ObjectBase) IsSticky() (bool, error) {
+ var count int
+
+ query := `select count(activity_id) from sticky where activity_id=$1 `
+ if err := config.DB.QueryRow(query, obj.Id).Scan(&count); err != nil {
+ return false, util.MakeError(err, "IsSticky")
+ }
+
+ if count != 0 {
+ return true, nil
+ }
+
+ return false, nil
+}
+
+func (obj ObjectBase) IsLocked() (bool, error) {
+ var count int
+
+ query := `select count(activity_id) from locked where activity_id=$1 `
+ if err := config.DB.QueryRow(query, obj.Id).Scan(&count); err != nil {
+ return false, util.MakeError(err, "IsSticky")
+ }
+
+ if count != 0 {
+ return true, nil
+ }
+
+ return false, nil
+}
diff --git a/activitypub/activityPubStruct.go b/activitypub/structs.go
index b8e3180..c42d175 100644
--- a/activitypub/activityPubStruct.go
+++ b/activitypub/structs.go
@@ -145,6 +145,8 @@ type ObjectBase struct {
Duration string `json:"duration,omitempty"`
Size int64 `json:"size,omitempty"`
Sensitive bool `json:"sensitive,omitempty"`
+ Sticky bool `json:"sticky,omitempty"`
+ Locked bool `json:"locked,omitempty"`
}
type CryptoCur struct {
diff --git a/databaseschema.psql b/databaseschema.psql
index f5671c2..d4b2616 100644
--- a/databaseschema.psql
+++ b/databaseschema.psql
@@ -244,4 +244,14 @@ instance varchar(100) primary key,
timestamp TIMESTAMP default NOW()
);
-ALTER TABLE boardaccess ADD COLUMN IF NOT EXISTS label varchar(50) default 'Anon'; \ No newline at end of file
+ALTER TABLE boardaccess ADD COLUMN IF NOT EXISTS label varchar(50) default 'Anon';
+
+CREATE TABLE IF NOT EXISTS sticky(
+actor_id varchar(100),
+activity_id varchar(100)
+);
+
+CREATE TABLE IF NOT EXISTS locked(
+actor_id varchar(100),
+activity_id varchar(100)
+); \ No newline at end of file
diff --git a/main.go b/main.go
index 837fec5..24d9753 100644
--- a/main.go
+++ b/main.go
@@ -89,6 +89,8 @@ func main() {
app.All("/blacklist", routes.BoardBlacklist)
app.All("/report", routes.ReportPost)
app.Get("/make-report", routes.ReportGet)
+ app.Get("/sticky", routes.Sticky)
+ app.Get("/lock", routes.Lock)
// Webfinger routes
app.Get("/.well-known/webfinger", routes.Webfinger)
diff --git a/route/routes/boardmgmt.go b/route/routes/boardmgmt.go
index 5f24cdd..7ecc885 100644
--- a/route/routes/boardmgmt.go
+++ b/route/routes/boardmgmt.go
@@ -563,3 +563,99 @@ func ReportGet(ctx *fiber.Ctx) error {
return ctx.Render("report", fiber.Map{"page": data}, "layouts/main")
}
+
+func Sticky(ctx *fiber.Ctx) error {
+ id := ctx.Query("id")
+ board := ctx.Query("board")
+
+ actor, _ := activitypub.GetActorByNameFromDB(board)
+
+ _, auth := util.GetPasswordFromSession(ctx)
+
+ if id == "" || auth == "" {
+ return util.MakeError(errors.New("no auth"), "Sticky")
+ }
+
+ var obj = activitypub.ObjectBase{Id: id}
+ col, _ := obj.GetCollectionFromPath()
+
+ if len(col.OrderedItems) < 1 {
+ if has, _ := util.HasAuth(auth, actor.Id); !has {
+ return util.MakeError(errors.New("no auth"), "Sticky")
+ }
+
+ obj.MarkSticky(actor.Id)
+
+ return ctx.Redirect("/"+board, http.StatusSeeOther)
+ }
+
+ actor.Id = col.OrderedItems[0].Actor
+
+ var OP string
+ if len(col.OrderedItems[0].InReplyTo) > 0 && col.OrderedItems[0].InReplyTo[0].Id != "" {
+ OP = col.OrderedItems[0].InReplyTo[0].Id
+ } else {
+ OP = id
+ }
+
+ if has, _ := util.HasAuth(auth, actor.Id); !has {
+ return util.MakeError(errors.New("no auth"), "Sticky")
+ }
+
+ obj.MarkSticky(actor.Id)
+
+ var op = activitypub.ObjectBase{Id: OP}
+ if local, _ := op.IsLocal(); !local {
+ return ctx.Redirect("/"+board+"/"+util.RemoteShort(OP), http.StatusSeeOther)
+ } else {
+ return ctx.Redirect(OP, http.StatusSeeOther)
+ }
+}
+
+func Lock(ctx *fiber.Ctx) error {
+ id := ctx.Query("id")
+ board := ctx.Query("board")
+
+ actor, _ := activitypub.GetActorByNameFromDB(board)
+
+ _, auth := util.GetPasswordFromSession(ctx)
+
+ if id == "" || auth == "" {
+ return util.MakeError(errors.New("no auth"), "Lock")
+ }
+
+ var obj = activitypub.ObjectBase{Id: id}
+ col, _ := obj.GetCollectionFromPath()
+
+ if len(col.OrderedItems) < 1 {
+ if has, _ := util.HasAuth(auth, actor.Id); !has {
+ return util.MakeError(errors.New("no auth"), "Lock")
+ }
+
+ obj.MarkLocked(actor.Id)
+
+ return ctx.Redirect("/"+board, http.StatusSeeOther)
+ }
+
+ actor.Id = col.OrderedItems[0].Actor
+
+ var OP string
+ if len(col.OrderedItems[0].InReplyTo) > 0 && col.OrderedItems[0].InReplyTo[0].Id != "" {
+ OP = col.OrderedItems[0].InReplyTo[0].Id
+ } else {
+ OP = id
+ }
+
+ if has, _ := util.HasAuth(auth, actor.Id); !has {
+ return util.MakeError(errors.New("no auth"), "Lock")
+ }
+
+ obj.MarkLocked(actor.Id)
+
+ var op = activitypub.ObjectBase{Id: OP}
+ if local, _ := op.IsLocal(); !local {
+ return ctx.Redirect("/"+board+"/"+util.RemoteShort(OP), http.StatusSeeOther)
+ } else {
+ return ctx.Redirect(OP, http.StatusSeeOther)
+ }
+}
diff --git a/route/util.go b/route/util.go
index 09c5429..5a7d57c 100644
--- a/route/util.go
+++ b/route/util.go
@@ -139,6 +139,12 @@ func ParseOutboxRequest(ctx *fiber.Ctx, actor activitypub.Actor) error {
nObj.Actor = config.Domain + "/" + actor.Name
+ if locked, _ := nObj.InReplyTo[0].IsLocked(); locked {
+ ctx.Response().Header.SetStatusCode(403)
+ _, err := ctx.Write([]byte("thread is locked"))
+ return util.MakeError(err, "ParseOutboxRequest")
+ }
+
nObj, err = nObj.Write()
if err != nil {
return util.MakeError(err, "ParseOutboxRequest")
diff --git a/static/locked.png b/static/locked.png
new file mode 100644
index 0000000..7792d16
--- /dev/null
+++ b/static/locked.png
Binary files differ
diff --git a/static/pin.png b/static/pin.png
new file mode 100644
index 0000000..6952601
--- /dev/null
+++ b/static/pin.png
Binary files differ
diff --git a/views/catalog.html b/views/catalog.html
index f19c489..4bd48aa 100644
--- a/views/catalog.html
+++ b/views/catalog.html
@@ -30,7 +30,7 @@
</div>
</div>
<a id="{{ .Id }}-anchor" href="/{{ $board.Name }}/{{ shortURL $board.Actor.Outbox .Id}}">
- <div id="media-{{ .Id }}" style="width:180px;"> {{ parseAttachment . true }}</div>
+ <div id="media-{{ .Id }}" style="width:180px;"><div id="status" style="position: absolute;">{{ if .Sticky }}<span id="sticky"><img src="/static/pin.png"></span>{{ end }}{{ if .Locked }}<span id="lock"><img src="/static/locked.png"></span>{{ end }}</div>{{ parseAttachment . true }}</div>
</a>
<script>
media = document.getElementById("media-{{ .Id }}")
diff --git a/views/partials/posts.html b/views/partials/posts.html
index 6fe2d2d..676ca81 100644
--- a/views/partials/posts.html
+++ b/views/partials/posts.html
@@ -17,6 +17,8 @@
[<a href="/banmedia?id={{ .Id }}&board={{ $board.Actor.Name }}">Ban Media</a>]
[<a href="/deleteattach?id={{ .Id }}&board={{ $board.Actor.Name }}">Delete Attachment</a>]
[<a href="/marksensitive?id={{ .Id }}&board={{ $board.Actor.Name }}">Mark Sensitive</a>]
+ [<a href="/sticky?id={{ .Id }}&board={{ $board.Actor.Name }}">Sticky</a>]
+ [<a href="/lock?id={{ .Id }}&board={{ $board.Actor.Name }}">Lock</a>]
{{ end }}
<span style="display: block;">File: <a id="{{ .Id }}-img" href="{{ proxy (index .Attachment 0).Href}}">{{ shortImg (index .Attachment 0).Name }}</a><span id="{{ .Id }}-size"> ({{ convertSize (index .Attachment 0).Size }})</span></span>
<div id="hide-{{ .Id }}" style="display: none;">[Hide]</div>
@@ -48,7 +50,7 @@
<span class="subject"><b>{{ .Name }}</b></span>
<span class="name"><b>{{ if .AttributedTo }} {{.AttributedTo }} {{ else }} Anonymous {{ end }}</b></span>
<span class="tripcode"> {{ .TripCode }} </span>
- <span class="timestamp" data-utc="{{.Published | timeToUnix}}">{{ .Published | timeToReadableLong }} <a id="{{ .Id }}-anchor" href="/{{ $board.Name }}/{{ shortURL $board.Actor.Outbox $opId }}">No.</a> <a id="{{ .Id }}-link" title="{{ .Id }}" {{ if eq .Type "Note" }} href="javascript:quote('{{ $board.Actor.Id }}', '{{ $opId }}', '{{ .Id }}')" {{ end }}>{{ shortURL $board.Actor.Outbox .Id }}</a> {{ if ne .Type "Tombstone" }}[<a href="/make-report?actor={{ $board.Actor.Id }}&post={{ .Id }}">Report</a>]{{ end }}</span>
+ <span class="timestamp" data-utc="{{.Published | timeToUnix}}">{{ .Published | timeToReadableLong }} <a id="{{ .Id }}-anchor" href="/{{ $board.Name }}/{{ shortURL $board.Actor.Outbox $opId }}#{{ shortURL $board.Actor.Outbox .Id }}">No.</a> <a id="{{ .Id }}-link" title="{{ .Id }}" {{ if eq .Locked false }} {{ if eq .Type "Note" }} href="javascript:quote('{{ $board.Actor.Id }}', '{{ $opId }}', '{{ .Id }}')" {{ end }} {{ end }}>{{ shortURL $board.Actor.Outbox .Id }}</a> <span id="status" style="margin-right: 5px;">{{ if .Sticky }}<span id="sticky"><img src="/static/pin.png"></span>{{ end }} {{ if .Locked }} <span id="lock"><img src="/static/locked.png"></span>{{ end }}</span>{{ if ne .Type "Tombstone" }}[<a href="/make-report?actor={{ $board.Actor.Id }}&post={{ .Id }}">Report</a>]{{ end }}</span>
<p id="{{ .Id }}-content" style="white-space: pre-wrap; margin: 10px 30px 10px 30px;">{{ parseContent $board.Actor $opId .Content $thread .Id $page.PostType }}</p>
{{ if .Replies }}
{{ $replies := .Replies }}
@@ -70,6 +72,8 @@
[<a href="/banmedia?id={{ .Id }}&board={{ $board.Actor.Name }}">Ban Media</a>]
[<a href="/deleteattach?id={{ .Id }}&board={{ $board.Actor.Name }}">Delete Attachment</a>]
[<a href="/marksensitive?id={{ .Id }}&board={{ $board.Actor.Name }}">Mark Sensitive</a>]
+ [<a href="/sticky?id={{ .Id }}&board={{ $board.Actor.Name }}">Sticky</a>]
+ [<a href="/lock?id={{ .Id }}&board={{ $board.Actor.Name }}">Lock</a>]
{{ end }}
<span style="display: block;">File <a id="{{ .Id }}-img" href="{{ proxy (index .Attachment 0).Href}}">{{ shortImg (index .Attachment 0).Name }}</a> <span id="{{ .Id }}-size">({{ convertSize (index .Attachment 0).Size }})</span></span>
<div id="hide-{{ .Id }}" style="display: none;">[Hide]</div>
@@ -103,7 +107,7 @@
<span class="subject"><b>{{ .Name }}</b></span>
<span class="name"><b>{{ if .AttributedTo }} {{.AttributedTo }} {{ else }} Anonymous {{ end }}</b></span>
<span class="tripcode"> {{ .TripCode }} </span>
- <span class="timestamp" data-utc="{{ .Published | timeToUnix }}">{{ .Published | timeToReadableLong }} <a id="{{ .Id }}-anchor" href="/{{ $board.Name }}/{{ shortURL $board.Actor.Outbox $opId }}#{{ shortURL $board.Actor.Outbox .Id }}">No. </a><a id="{{ .Id }}-link" title="{{ .Id }}" {{ if eq .Type "Note" }} href="javascript:quote('{{ $board.Actor.Id }}', '{{ $opId }}', '{{ .Id }}')" {{ end }}>{{ shortURL $board.Actor.Outbox .Id }}</a> {{ if ne .Type "Tombstone" }}[<a href="/make-report?actor={{ $board.Actor.Id }}&post={{ .Id }}">Report</a>]{{ end }}</span>
+ <span class="timestamp" data-utc="{{ .Published | timeToUnix }}">{{ .Published | timeToReadableLong }} <a id="{{ .Id }}-anchor" href="/{{ $board.Name }}/{{ shortURL $board.Actor.Outbox $opId }}#{{ shortURL $board.Actor.Outbox .Id }}">No. </a><a id="{{ .Id }}-link" title="{{ .Id }}" {{ if eq $thread.Locked false }} {{ if eq .Type "Note" }} href="javascript:quote('{{ $board.Actor.Id }}', '{{ $opId }}', '{{ .Id }}')" {{ end }} {{ end }}>{{ shortURL $board.Actor.Outbox .Id }}</a> {{ if ne .Type "Tombstone" }}[<a href="/make-report?actor={{ $board.Actor.Id }}&post={{ .Id }}">Report</a>]{{ end }}</span>
{{ $parentId := .Id }}
{{ if .Replies.OrderedItems }}
{{ range .Replies.OrderedItems }}
diff --git a/views/partials/top.html b/views/partials/top.html
index 73da840..2a5c16b 100644
--- a/views/partials/top.html
+++ b/views/partials/top.html
@@ -4,8 +4,10 @@
{{ $len := len .Posts }}
{{ if eq $len 0 }}
{{ if eq .PostType "reply" }}
+ {{ if eq (index .Posts 0).Locked false }}
<h3 id="newpostbtn" state="0" style="display: none; margin-bottom:100px;">[<a href="javascript:startNewPost()">Post a Reply</a>]</h3>
- {{ else if eq .PostType "new" }}
+ {{ end }}
+ {{ else if and (eq .PostType "new") }}
<h3 id="newpostbtn" state="0" style="display: none; margin-bottom:100px;">[<a href="javascript:startNewPost()">Start a New Thread</a>]</h3>
{{ end }} <!-- end if inreplyto-->
<div id="newpost">
@@ -60,7 +62,9 @@
{{ if eq (index .Posts 0).Type "Note" }}
{{ if .Board.InReplyTo }}
+ {{ if eq (index .Posts 0).Locked false }}
<h3 id="newpostbtn" state="0" style="text-align: center; margin-top: 80px; display: none; margin-bottom:100px;">[<a href="javascript:startNewPost()">Post a Reply</a>]</h3>
+ {{ end }}
{{ else }}
<h3 id="newpostbtn" state="0" style="text-align: center; margin-top: 80px; display: none; margin-bottom:100px;">[<a href="javascript:startNewPost()">Start a New Thread</a>]</h3>
{{ end }} <!-- end if inreplyto-->