217 lines
5.1 KiB
Go
217 lines
5.1 KiB
Go
package arn
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"reflect"
|
|
|
|
"github.com/aerogo/aero"
|
|
"github.com/aerogo/aero/event"
|
|
"github.com/aerogo/api"
|
|
"github.com/aerogo/markdown"
|
|
"github.com/animenotifier/notify.moe/arn/autocorrect"
|
|
)
|
|
|
|
// Force interface implementations
|
|
var (
|
|
_ Postable = (*Thread)(nil)
|
|
_ Likeable = (*Thread)(nil)
|
|
_ LikeEventReceiver = (*Thread)(nil)
|
|
_ Lockable = (*Thread)(nil)
|
|
_ LockEventReceiver = (*Thread)(nil)
|
|
_ PostParent = (*Thread)(nil)
|
|
_ fmt.Stringer = (*Thread)(nil)
|
|
_ api.Newable = (*Thread)(nil)
|
|
_ api.Editable = (*Thread)(nil)
|
|
_ api.Actionable = (*Thread)(nil)
|
|
_ api.Deletable = (*Thread)(nil)
|
|
)
|
|
|
|
// Actions
|
|
func init() {
|
|
API.RegisterActions("Thread", []*api.Action{
|
|
// Like thread
|
|
LikeAction(),
|
|
|
|
// Unlike thread
|
|
UnlikeAction(),
|
|
|
|
// Lock thread
|
|
LockAction(),
|
|
|
|
// Unlock thread
|
|
UnlockAction(),
|
|
})
|
|
}
|
|
|
|
// Authorize returns an error if the given API POST request is not authorized.
|
|
func (thread *Thread) Authorize(ctx aero.Context, action string) error {
|
|
if !ctx.HasSession() {
|
|
return errors.New("Neither logged in nor in session")
|
|
}
|
|
|
|
if action == "edit" {
|
|
user := GetUserFromContext(ctx)
|
|
|
|
if thread.CreatedBy != user.ID && user.Role != "admin" {
|
|
return errors.New("Can't edit the threads of other users")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Create sets the data for a new thread with data we received from the API request.
|
|
func (thread *Thread) Create(ctx aero.Context) error {
|
|
data, err := ctx.Request().Body().JSONObject()
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
userID, ok := ctx.Session().Get("userId").(string)
|
|
|
|
if !ok || userID == "" {
|
|
return errors.New("Not logged in")
|
|
}
|
|
|
|
user, err := GetUser(userID)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
thread.ID = GenerateID("Thread")
|
|
thread.Title, _ = data["title"].(string)
|
|
thread.Text, _ = data["text"].(string)
|
|
thread.CreatedBy = user.ID
|
|
thread.Sticky, _ = data["sticky"].(int)
|
|
thread.Created = DateTimeUTC()
|
|
thread.Edited = ""
|
|
|
|
// Post-process text
|
|
thread.Title = autocorrect.ThreadTitle(thread.Title)
|
|
thread.Text = autocorrect.PostText(thread.Text)
|
|
|
|
// Tags
|
|
tags, _ := data["tags"].([]interface{})
|
|
thread.Tags = make([]string, len(tags))
|
|
|
|
for i := range thread.Tags {
|
|
thread.Tags[i] = tags[i].(string)
|
|
}
|
|
|
|
if len(tags) < 1 {
|
|
return errors.New("Need to specify at least one tag")
|
|
}
|
|
|
|
if len(thread.Title) < 10 {
|
|
return errors.New("Title too short: Should be at least 10 characters")
|
|
}
|
|
|
|
if len(thread.Text) < 10 {
|
|
return errors.New("Text too short: Should be at least 10 characters")
|
|
}
|
|
|
|
// Write log entry
|
|
logEntry := NewEditLogEntry(user.ID, "create", "Thread", thread.ID, "", "", "")
|
|
logEntry.Save()
|
|
|
|
// Create activity
|
|
activity := NewActivityCreate("Thread", thread.ID, user.ID)
|
|
activity.Save()
|
|
|
|
// Broadcast event to all users so they can reload the activity page if needed
|
|
for receiver := range StreamUsers() {
|
|
activityEvent := event.New("post activity", receiver.IsFollowing(user.ID))
|
|
receiver.BroadcastEvent(activityEvent)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Edit creates an edit log entry.
|
|
func (thread *Thread) Edit(ctx aero.Context, key string, value reflect.Value, newValue reflect.Value) (consumed bool, err error) {
|
|
switch key {
|
|
case "Tags":
|
|
newTags := newValue.Interface().([]string)
|
|
|
|
if len(newTags) < 1 {
|
|
return true, errors.New("Need to specify at least one tag")
|
|
}
|
|
|
|
case "Title":
|
|
newTitle := newValue.String()
|
|
|
|
if len(newTitle) < 10 {
|
|
return true, errors.New("Title too short: Should be at least 10 characters")
|
|
}
|
|
case "Text":
|
|
newText := newValue.String()
|
|
|
|
if len(newText) < 10 {
|
|
return true, errors.New("Text too short: Should be at least 10 characters")
|
|
}
|
|
}
|
|
|
|
return edit(thread, ctx, key, value, newValue)
|
|
}
|
|
|
|
// OnAppend saves a log entry.
|
|
func (thread *Thread) OnAppend(ctx aero.Context, key string, index int, obj interface{}) {
|
|
onAppend(thread, ctx, key, index, obj)
|
|
}
|
|
|
|
// OnRemove saves a log entry.
|
|
func (thread *Thread) OnRemove(ctx aero.Context, key string, index int, obj interface{}) {
|
|
onRemove(thread, ctx, key, index, obj)
|
|
}
|
|
|
|
// AfterEdit sets the edited date on the thread object.
|
|
func (thread *Thread) AfterEdit(ctx aero.Context) error {
|
|
thread.Edited = DateTimeUTC()
|
|
thread.html = markdown.Render(thread.Text)
|
|
return nil
|
|
}
|
|
|
|
// Save saves the thread object in the database.
|
|
func (thread *Thread) Save() {
|
|
DB.Set("Thread", thread.ID, thread)
|
|
}
|
|
|
|
// DeleteInContext deletes the thread in the given context.
|
|
func (thread *Thread) DeleteInContext(ctx aero.Context) error {
|
|
user := GetUserFromContext(ctx)
|
|
|
|
// Write log entry
|
|
logEntry := NewEditLogEntry(user.ID, "delete", "Thread", thread.ID, "", fmt.Sprint(thread), "")
|
|
logEntry.Save()
|
|
|
|
return thread.Delete()
|
|
}
|
|
|
|
// Delete deletes the thread and its posts from the database.
|
|
func (thread *Thread) Delete() error {
|
|
for _, post := range thread.Posts() {
|
|
err := post.Delete()
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Remove activities
|
|
for activity := range StreamActivityCreates() {
|
|
if activity.ObjectID == thread.ID && activity.ObjectType == "Thread" {
|
|
err := activity.Delete()
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
DB.Delete("Thread", thread.ID)
|
|
return nil
|
|
}
|