2019-06-03 09:32:43 +00:00
|
|
|
package arn
|
|
|
|
|
|
|
|
import (
|
|
|
|
"sort"
|
|
|
|
|
|
|
|
"github.com/aerogo/markdown"
|
|
|
|
"github.com/aerogo/nano"
|
|
|
|
)
|
|
|
|
|
2019-11-18 05:01:13 +00:00
|
|
|
// ThreadID represents a thread ID.
|
|
|
|
type ThreadID = ID
|
|
|
|
|
2019-06-03 09:32:43 +00:00
|
|
|
// Thread is a forum thread.
|
|
|
|
type Thread struct {
|
|
|
|
Title string `json:"title" editable:"true"`
|
|
|
|
Sticky int `json:"sticky" editable:"true"`
|
|
|
|
Tags []string `json:"tags" editable:"true"`
|
|
|
|
Edited string `json:"edited"`
|
|
|
|
|
|
|
|
hasID
|
|
|
|
hasText
|
|
|
|
hasPosts
|
|
|
|
hasCreator
|
|
|
|
hasLikes
|
|
|
|
hasLocked
|
|
|
|
|
|
|
|
html string
|
|
|
|
}
|
|
|
|
|
|
|
|
// Link returns the relative URL of the thread.
|
|
|
|
func (thread *Thread) Link() string {
|
|
|
|
return "/thread/" + thread.ID
|
|
|
|
}
|
|
|
|
|
|
|
|
// HTML returns the HTML representation of the thread.
|
|
|
|
func (thread *Thread) HTML() string {
|
|
|
|
if thread.html != "" {
|
|
|
|
return thread.html
|
|
|
|
}
|
|
|
|
|
|
|
|
thread.html = markdown.Render(thread.Text)
|
|
|
|
return thread.html
|
|
|
|
}
|
|
|
|
|
|
|
|
// String implements the default string serialization.
|
|
|
|
func (thread *Thread) String() string {
|
|
|
|
return thread.Title
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parent always returns nil for threads.
|
|
|
|
func (thread *Thread) Parent() PostParent {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetParentID always returns an empty string for threads.
|
|
|
|
func (thread *Thread) GetParentID() string {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2019-08-27 06:03:29 +00:00
|
|
|
// GetParentType always returns an empty string for threads.
|
|
|
|
func (thread *Thread) GetParentType() string {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2019-06-03 09:32:43 +00:00
|
|
|
// TypeName returns the type name.
|
|
|
|
func (thread *Thread) TypeName() string {
|
|
|
|
return "Thread"
|
|
|
|
}
|
|
|
|
|
|
|
|
// Self returns the object itself.
|
|
|
|
func (thread *Thread) Self() Loggable {
|
|
|
|
return thread
|
|
|
|
}
|
|
|
|
|
|
|
|
// OnLike is called when the thread receives a like.
|
|
|
|
func (thread *Thread) OnLike(likedBy *User) {
|
|
|
|
if !thread.Creator().Settings().Notification.ForumLikes {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
thread.Creator().SendNotification(&PushNotification{
|
|
|
|
Title: likedBy.Nick + " liked your thread",
|
|
|
|
Message: likedBy.Nick + " liked your thread \"" + thread.Title + "\".",
|
|
|
|
Icon: "https:" + likedBy.AvatarLink("large"),
|
|
|
|
Link: "https://notify.moe" + likedBy.Link(),
|
|
|
|
Type: NotificationTypeLike,
|
|
|
|
})
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
// OnLock is called when the thread is locked.
|
|
|
|
func (thread *Thread) OnLock(user *User) {
|
|
|
|
logEntry := NewEditLogEntry(user.ID, "edit", "Thread", thread.ID, "Locked", "false", "true")
|
|
|
|
logEntry.Save()
|
|
|
|
}
|
|
|
|
|
|
|
|
// OnUnlock is called when the thread is unlocked.
|
|
|
|
func (thread *Thread) OnUnlock(user *User) {
|
|
|
|
logEntry := NewEditLogEntry(user.ID, "edit", "Thread", thread.ID, "Locked", "true", "false")
|
|
|
|
logEntry.Save()
|
|
|
|
}
|
|
|
|
|
|
|
|
// TitleByUser returns the title of the thread,
|
|
|
|
// regardless of the user language settings
|
|
|
|
// because threads are bound to one language.
|
|
|
|
func (thread *Thread) TitleByUser(user *User) string {
|
|
|
|
return thread.Title
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetThread ...
|
2019-11-18 06:13:51 +00:00
|
|
|
func GetThread(id ThreadID) (*Thread, error) {
|
2019-06-03 09:32:43 +00:00
|
|
|
obj, err := DB.Get("Thread", id)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return obj.(*Thread), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetThreadsByTag ...
|
|
|
|
func GetThreadsByTag(tag string) []*Thread {
|
|
|
|
var threads []*Thread
|
|
|
|
allTags := (tag == "" || tag == "<nil>")
|
|
|
|
|
|
|
|
for thread := range StreamThreads() {
|
|
|
|
if (allTags && !Contains(thread.Tags, "update")) || Contains(thread.Tags, tag) {
|
|
|
|
threads = append(threads, thread)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return threads
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetThreadsByUser ...
|
|
|
|
func GetThreadsByUser(user *User) []*Thread {
|
|
|
|
var threads []*Thread
|
|
|
|
|
|
|
|
for thread := range StreamThreads() {
|
|
|
|
if thread.CreatedBy == user.ID {
|
|
|
|
threads = append(threads, thread)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return threads
|
|
|
|
}
|
|
|
|
|
|
|
|
// StreamThreads ...
|
|
|
|
func StreamThreads() <-chan *Thread {
|
|
|
|
channel := make(chan *Thread, nano.ChannelBufferSize)
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
for obj := range DB.All("Thread") {
|
|
|
|
channel <- obj.(*Thread)
|
|
|
|
}
|
|
|
|
|
|
|
|
close(channel)
|
|
|
|
}()
|
|
|
|
|
|
|
|
return channel
|
|
|
|
}
|
|
|
|
|
|
|
|
// AllThreads ...
|
|
|
|
func AllThreads() []*Thread {
|
|
|
|
all := make([]*Thread, 0, DB.Collection("Thread").Count())
|
|
|
|
|
|
|
|
for obj := range StreamThreads() {
|
|
|
|
all = append(all, obj)
|
|
|
|
}
|
|
|
|
|
|
|
|
return all
|
|
|
|
}
|
|
|
|
|
|
|
|
// SortThreads sorts a slice of threads for the forum view (stickies first).
|
|
|
|
func SortThreads(threads []*Thread) {
|
|
|
|
sort.Slice(threads, func(i, j int) bool {
|
|
|
|
a := threads[i]
|
|
|
|
b := threads[j]
|
|
|
|
|
|
|
|
if a.Sticky != b.Sticky {
|
|
|
|
return a.Sticky > b.Sticky
|
|
|
|
}
|
|
|
|
|
|
|
|
return a.Created > b.Created
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// SortThreadsLatestFirst sorts a slice of threads by creation date.
|
|
|
|
func SortThreadsLatestFirst(threads []*Thread) {
|
|
|
|
sort.Slice(threads, func(i, j int) bool {
|
|
|
|
return threads[i].Created > threads[j].Created
|
|
|
|
})
|
|
|
|
}
|