Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aff0d99842 | ||
|
|
00150118d2 | ||
|
|
332a1eb32b | ||
|
|
db4d46c368 | ||
|
|
a8c8610ca2 | ||
|
|
d09c17d30d | ||
|
|
e3e3ea07db | ||
|
|
22592439da | ||
|
|
dfa9fb4025 | ||
|
|
0e2a68a2d4 | ||
|
|
1eefa65527 | ||
|
|
f9b6af58c9 | ||
|
|
56ecf48b57 | ||
|
|
06fbfc90c5 | ||
|
|
39a4fef506 | ||
|
|
f9fe0bb5f8 | ||
|
|
d22ab387dd |
@@ -104,7 +104,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
chapter, err := chapters.Add(story, title, result.Author[5:], result.Source, publishDate, &fictionalDate)
|
chapter, err := chapters.Add(story, title, result.Author[5:], result.Source, publishDate, &fictionalDate, models.ChapterCommentModeDisabled)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,15 +8,13 @@ import (
|
|||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.aiterp.net/rpdata/api/models"
|
|
||||||
"git.aiterp.net/rpdata/api/models/changes"
|
|
||||||
|
|
||||||
"git.aiterp.net/rpdata/api/models/logs"
|
|
||||||
|
|
||||||
"git.aiterp.net/rpdata/api/graph2"
|
"git.aiterp.net/rpdata/api/graph2"
|
||||||
"git.aiterp.net/rpdata/api/internal/auth"
|
"git.aiterp.net/rpdata/api/internal/auth"
|
||||||
"git.aiterp.net/rpdata/api/internal/loader"
|
"git.aiterp.net/rpdata/api/internal/loader"
|
||||||
"git.aiterp.net/rpdata/api/internal/store"
|
"git.aiterp.net/rpdata/api/internal/store"
|
||||||
|
"git.aiterp.net/rpdata/api/models"
|
||||||
|
"git.aiterp.net/rpdata/api/models/changes"
|
||||||
|
"git.aiterp.net/rpdata/api/models/logs"
|
||||||
"github.com/99designs/gqlgen/handler"
|
"github.com/99designs/gqlgen/handler"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -64,6 +62,8 @@ func logListedChanges() {
|
|||||||
log.Printf("\tTag: (%s,%s)", object.Kind, object.Name)
|
log.Printf("\tTag: (%s,%s)", object.Kind, object.Name)
|
||||||
case models.Chapter:
|
case models.Chapter:
|
||||||
log.Printf("\tChapter: %s", object.ID)
|
log.Printf("\tChapter: %s", object.ID)
|
||||||
|
case models.Comment:
|
||||||
|
log.Printf("\tComment: %s", object.ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,6 +80,7 @@ func queryHandler() http.HandlerFunc {
|
|||||||
|
|
||||||
return fmt.Errorf("The server failed to serve your request due to an internal error, it has been logged")
|
return fmt.Errorf("The server failed to serve your request due to an internal error, it has been logged")
|
||||||
}),
|
}),
|
||||||
|
handler.ComplexityLimit(300),
|
||||||
)
|
)
|
||||||
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|||||||
@@ -0,0 +1,182 @@
|
|||||||
|
package graph2
|
||||||
|
|
||||||
|
import (
|
||||||
|
input "git.aiterp.net/rpdata/api/graph2/input"
|
||||||
|
changes "git.aiterp.net/rpdata/api/models/changes"
|
||||||
|
channels "git.aiterp.net/rpdata/api/models/channels"
|
||||||
|
characters "git.aiterp.net/rpdata/api/models/characters"
|
||||||
|
files "git.aiterp.net/rpdata/api/models/files"
|
||||||
|
logs "git.aiterp.net/rpdata/api/models/logs"
|
||||||
|
posts "git.aiterp.net/rpdata/api/models/posts"
|
||||||
|
stories "git.aiterp.net/rpdata/api/models/stories"
|
||||||
|
)
|
||||||
|
|
||||||
|
func complexity() (cr ComplexityRoot) {
|
||||||
|
subListComplexity := 25
|
||||||
|
bigSublistComplexity := 75
|
||||||
|
hugeSublistComplexity := 100
|
||||||
|
findComplexity := 100
|
||||||
|
listComplexity := 200
|
||||||
|
mutationComplexity := 100
|
||||||
|
|
||||||
|
cr.Query.Character = func(childComplexity int, id *string, nick *string) int {
|
||||||
|
return childComplexity + findComplexity
|
||||||
|
}
|
||||||
|
cr.Query.Characters = func(childComplexity int, filter *characters.Filter) int {
|
||||||
|
return childComplexity + listComplexity
|
||||||
|
}
|
||||||
|
cr.Query.Channel = func(childComplexity int, name string) int {
|
||||||
|
return childComplexity + findComplexity
|
||||||
|
}
|
||||||
|
cr.Query.Channels = func(childComplexity int, filter *channels.Filter) int {
|
||||||
|
return childComplexity + listComplexity
|
||||||
|
}
|
||||||
|
cr.Query.Post = func(childComplexity int, id string) int {
|
||||||
|
return childComplexity + findComplexity
|
||||||
|
}
|
||||||
|
cr.Query.Posts = func(childComplexity int, filter *posts.Filter) int {
|
||||||
|
return childComplexity + listComplexity
|
||||||
|
}
|
||||||
|
cr.Query.UnknownNicks = func(childComplexity int, filter *input.UnknownNicksFilter) int {
|
||||||
|
return childComplexity + listComplexity
|
||||||
|
}
|
||||||
|
cr.Query.Log = func(childComplexity int, id string) int {
|
||||||
|
return childComplexity + findComplexity
|
||||||
|
}
|
||||||
|
cr.Query.Logs = func(childComplexity int, filter *logs.Filter) int {
|
||||||
|
if filter != nil && filter.Open != nil && *filter.Open == true {
|
||||||
|
return childComplexity + findComplexity
|
||||||
|
}
|
||||||
|
|
||||||
|
return childComplexity + listComplexity
|
||||||
|
}
|
||||||
|
cr.Query.Chapter = func(childComplexity int, id string) int {
|
||||||
|
return childComplexity + findComplexity
|
||||||
|
}
|
||||||
|
cr.Query.Comment = func(childComplexity int, id string) int {
|
||||||
|
return childComplexity + findComplexity
|
||||||
|
}
|
||||||
|
cr.Query.Tags = func(childComplexity int) int {
|
||||||
|
return childComplexity + listComplexity
|
||||||
|
}
|
||||||
|
cr.Query.Story = func(childComplexity int, id string) int {
|
||||||
|
return childComplexity + findComplexity
|
||||||
|
}
|
||||||
|
cr.Query.Stories = func(childComplexity int, filter *stories.Filter) int {
|
||||||
|
return childComplexity + listComplexity
|
||||||
|
}
|
||||||
|
cr.Query.File = func(childComplexity int, id string) int {
|
||||||
|
return childComplexity + findComplexity
|
||||||
|
}
|
||||||
|
cr.Query.Files = func(childComplexity int, filter *files.Filter) int {
|
||||||
|
return childComplexity + listComplexity
|
||||||
|
}
|
||||||
|
cr.Query.Changes = func(childComplexity int, filter *changes.Filter) int {
|
||||||
|
return childComplexity + listComplexity
|
||||||
|
}
|
||||||
|
cr.Query.Token = func(childComplexity int) int {
|
||||||
|
return childComplexity + findComplexity
|
||||||
|
}
|
||||||
|
|
||||||
|
cr.Mutation.AddStory = func(childComplexity int, input input.StoryAddInput) int {
|
||||||
|
return childComplexity + mutationComplexity
|
||||||
|
}
|
||||||
|
cr.Mutation.AddStoryTag = func(childComplexity int, input input.StoryTagAddInput) int {
|
||||||
|
return childComplexity + mutationComplexity
|
||||||
|
}
|
||||||
|
cr.Mutation.RemoveStoryTag = func(childComplexity int, input input.StoryTagRemoveInput) int {
|
||||||
|
return childComplexity + mutationComplexity
|
||||||
|
}
|
||||||
|
cr.Mutation.EditStory = func(childComplexity int, input input.StoryEditInput) int {
|
||||||
|
return childComplexity + mutationComplexity
|
||||||
|
}
|
||||||
|
cr.Mutation.RemoveStory = func(childComplexity int, input input.StoryRemoveInput) int {
|
||||||
|
return childComplexity + mutationComplexity
|
||||||
|
}
|
||||||
|
cr.Mutation.AddChapter = func(childComplexity int, input input.ChapterAddInput) int {
|
||||||
|
return childComplexity + mutationComplexity
|
||||||
|
}
|
||||||
|
cr.Mutation.EditChapter = func(childComplexity int, input input.ChapterEditInput) int {
|
||||||
|
return childComplexity + mutationComplexity
|
||||||
|
}
|
||||||
|
cr.Mutation.MoveChapter = func(childComplexity int, input input.ChapterMoveInput) int {
|
||||||
|
return childComplexity + mutationComplexity
|
||||||
|
}
|
||||||
|
cr.Mutation.RemoveChapter = func(childComplexity int, input input.ChapterRemoveInput) int {
|
||||||
|
return childComplexity + mutationComplexity
|
||||||
|
}
|
||||||
|
cr.Mutation.AddComment = func(childComplexity int, input input.CommentAddInput) int {
|
||||||
|
return childComplexity + mutationComplexity
|
||||||
|
}
|
||||||
|
cr.Mutation.EditComment = func(childComplexity int, input input.CommentEditInput) int {
|
||||||
|
return childComplexity + mutationComplexity
|
||||||
|
}
|
||||||
|
cr.Mutation.RemoveComment = func(childComplexity int, input input.CommentRemoveInput) int {
|
||||||
|
return childComplexity + mutationComplexity
|
||||||
|
}
|
||||||
|
cr.Mutation.AddLog = func(childComplexity int, input input.LogAddInput) int {
|
||||||
|
return childComplexity + mutationComplexity
|
||||||
|
}
|
||||||
|
cr.Mutation.ImportLog = func(childComplexity int, input input.LogImportInput) int {
|
||||||
|
return childComplexity + mutationComplexity
|
||||||
|
}
|
||||||
|
cr.Mutation.EditLog = func(childComplexity int, input input.LogEditInput) int {
|
||||||
|
return childComplexity + mutationComplexity
|
||||||
|
}
|
||||||
|
cr.Mutation.RemoveLog = func(childComplexity int, input input.LogRemoveInput) int {
|
||||||
|
return childComplexity + mutationComplexity
|
||||||
|
}
|
||||||
|
cr.Mutation.AddPost = func(childComplexity int, input input.PostAddInput) int {
|
||||||
|
return childComplexity + mutationComplexity
|
||||||
|
}
|
||||||
|
cr.Mutation.EditPost = func(childComplexity int, input input.PostEditInput) int {
|
||||||
|
return childComplexity + mutationComplexity
|
||||||
|
}
|
||||||
|
cr.Mutation.MovePost = func(childComplexity int, input input.PostMoveInput) int {
|
||||||
|
return childComplexity + mutationComplexity
|
||||||
|
}
|
||||||
|
cr.Mutation.RemovePost = func(childComplexity int, input input.PostRemoveInput) int {
|
||||||
|
return childComplexity + mutationComplexity
|
||||||
|
}
|
||||||
|
cr.Mutation.AddCharacter = func(childComplexity int, input input.CharacterAddInput) int {
|
||||||
|
return childComplexity + mutationComplexity
|
||||||
|
}
|
||||||
|
cr.Mutation.AddCharacterNick = func(childComplexity int, input input.CharacterNickInput) int {
|
||||||
|
return childComplexity + mutationComplexity
|
||||||
|
}
|
||||||
|
cr.Mutation.RemoveCharacterNick = func(childComplexity int, input input.CharacterNickInput) int {
|
||||||
|
return childComplexity + mutationComplexity
|
||||||
|
}
|
||||||
|
cr.Mutation.EditCharacter = func(childComplexity int, input input.CharacterEditInput) int {
|
||||||
|
return childComplexity + mutationComplexity
|
||||||
|
}
|
||||||
|
cr.Mutation.RemoveCharacter = func(childComplexity int, input input.CharacterRemoveInput) int {
|
||||||
|
return childComplexity + mutationComplexity
|
||||||
|
}
|
||||||
|
cr.Mutation.AddChannel = func(childComplexity int, input input.ChannelAddInput) int {
|
||||||
|
return childComplexity + mutationComplexity
|
||||||
|
}
|
||||||
|
cr.Mutation.EditChannel = func(childComplexity int, input input.ChannelEditInput) int {
|
||||||
|
return childComplexity + mutationComplexity
|
||||||
|
}
|
||||||
|
|
||||||
|
cr.Log.Characters = func(childComplexity int) int {
|
||||||
|
return childComplexity + subListComplexity
|
||||||
|
}
|
||||||
|
cr.Log.Posts = func(childComplexity int, kinds []string) int {
|
||||||
|
return childComplexity + hugeSublistComplexity
|
||||||
|
}
|
||||||
|
|
||||||
|
cr.Story.Chapters = func(childComplexity int) int {
|
||||||
|
return childComplexity + bigSublistComplexity
|
||||||
|
}
|
||||||
|
cr.Story.Tags = func(childComplexity int) int {
|
||||||
|
return childComplexity + subListComplexity
|
||||||
|
}
|
||||||
|
|
||||||
|
cr.Chapter.Comments = func(childComplexity int, limit *int) int {
|
||||||
|
return childComplexity + subListComplexity
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -35,6 +35,13 @@ models:
|
|||||||
model: git.aiterp.net/rpdata/api/models/logs.Filter
|
model: git.aiterp.net/rpdata/api/models/logs.Filter
|
||||||
LogImporter:
|
LogImporter:
|
||||||
model: git.aiterp.net/rpdata/api/models.LogImporter
|
model: git.aiterp.net/rpdata/api/models.LogImporter
|
||||||
|
Comment:
|
||||||
|
model: git.aiterp.net/rpdata/api/models.Comment
|
||||||
|
fields:
|
||||||
|
fictionalDate:
|
||||||
|
resolver: true
|
||||||
|
ChapterCommentMode:
|
||||||
|
model: git.aiterp.net/rpdata/api/models.ChapterCommentMode
|
||||||
Chapter:
|
Chapter:
|
||||||
model: git.aiterp.net/rpdata/api/models.Chapter
|
model: git.aiterp.net/rpdata/api/models.Chapter
|
||||||
fields:
|
fields:
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
func New() graphql.ExecutableSchema {
|
func New() graphql.ExecutableSchema {
|
||||||
return NewExecutableSchema(Config{
|
return NewExecutableSchema(Config{
|
||||||
Resolvers: &rootResolver{},
|
Resolvers: &rootResolver{},
|
||||||
|
Complexity: complexity(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,6 +35,10 @@ func (r *rootResolver) Log() LogResolver {
|
|||||||
return &types.LogResolver
|
return &types.LogResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *rootResolver) Comment() CommentResolver {
|
||||||
|
return &types.CommentResolver
|
||||||
|
}
|
||||||
|
|
||||||
func (r *rootResolver) Chapter() ChapterResolver {
|
func (r *rootResolver) Chapter() ChapterResolver {
|
||||||
return &types.ChapterResolver
|
return &types.ChapterResolver
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"git.aiterp.net/rpdata/api/models/changekeys"
|
"git.aiterp.net/rpdata/api/models/changekeys"
|
||||||
"git.aiterp.net/rpdata/api/models/changes"
|
"git.aiterp.net/rpdata/api/models/changes"
|
||||||
|
"git.aiterp.net/rpdata/api/models/comments"
|
||||||
|
|
||||||
"git.aiterp.net/rpdata/api/internal/auth"
|
"git.aiterp.net/rpdata/api/internal/auth"
|
||||||
"git.aiterp.net/rpdata/api/models/stories"
|
"git.aiterp.net/rpdata/api/models/stories"
|
||||||
@@ -48,7 +49,12 @@ func (r *mutationResolver) AddChapter(ctx context.Context, input input.ChapterAd
|
|||||||
return models.Chapter{}, errors.New("Story is not open")
|
return models.Chapter{}, errors.New("Story is not open")
|
||||||
}
|
}
|
||||||
|
|
||||||
chapter, err := chapters.Add(story, input.Title, author, input.Source, time.Now(), input.FictionalDate)
|
commentMode := models.ChapterCommentModeDisabled
|
||||||
|
if input.CommentMode != nil {
|
||||||
|
commentMode = *input.CommentMode
|
||||||
|
}
|
||||||
|
|
||||||
|
chapter, err := chapters.Add(story, input.Title, author, input.Source, time.Now(), input.FictionalDate, commentMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return models.Chapter{}, errors.New("Failed to create chapter: " + err.Error())
|
return models.Chapter{}, errors.New("Failed to create chapter: " + err.Error())
|
||||||
}
|
}
|
||||||
@@ -117,7 +123,7 @@ func (r *mutationResolver) EditChapter(ctx context.Context, input input.ChapterE
|
|||||||
input.FictionalDate = &time.Time{}
|
input.FictionalDate = &time.Time{}
|
||||||
}
|
}
|
||||||
|
|
||||||
chapter, err = chapters.Edit(chapter, input.Title, input.Source, input.FictionalDate)
|
chapter, err = chapters.Edit(chapter, input.Title, input.Source, input.FictionalDate, input.CommentMode, input.CommentsLocked)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return models.Chapter{}, errors.New("Failed to edit chapter: " + err.Error())
|
return models.Chapter{}, errors.New("Failed to edit chapter: " + err.Error())
|
||||||
}
|
}
|
||||||
@@ -150,6 +156,11 @@ func (r *mutationResolver) RemoveChapter(ctx context.Context, input input.Chapte
|
|||||||
return models.Chapter{}, errors.New("Failed to remove chapter: " + err.Error())
|
return models.Chapter{}, errors.New("Failed to remove chapter: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = comments.RemoveChapter(chapter)
|
||||||
|
if err != nil {
|
||||||
|
return models.Chapter{}, errors.New("Chapter was removed, but comment removal failed: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
story, err := stories.FindID(chapter.StoryID)
|
story, err := stories.FindID(chapter.StoryID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -0,0 +1,175 @@
|
|||||||
|
package queries
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.aiterp.net/rpdata/api/models/stories"
|
||||||
|
|
||||||
|
"git.aiterp.net/rpdata/api/models/characters"
|
||||||
|
|
||||||
|
"git.aiterp.net/rpdata/api/models/changekeys"
|
||||||
|
|
||||||
|
"git.aiterp.net/rpdata/api/models/changes"
|
||||||
|
|
||||||
|
"git.aiterp.net/rpdata/api/graph2/input"
|
||||||
|
"git.aiterp.net/rpdata/api/internal/auth"
|
||||||
|
"git.aiterp.net/rpdata/api/models/chapters"
|
||||||
|
"git.aiterp.net/rpdata/api/models/comments"
|
||||||
|
|
||||||
|
"git.aiterp.net/rpdata/api/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Queries
|
||||||
|
|
||||||
|
func (r *resolver) Comment(ctx context.Context, id string) (models.Comment, error) {
|
||||||
|
return comments.Find(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mutations
|
||||||
|
|
||||||
|
func (r *mutationResolver) AddComment(ctx context.Context, input input.CommentAddInput) (models.Comment, error) {
|
||||||
|
chapter, err := chapters.FindID(input.ChapterID)
|
||||||
|
if err != nil {
|
||||||
|
return models.Comment{}, errors.New("Chapter not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
token := auth.TokenFromContext(ctx)
|
||||||
|
if !token.Permitted("member", "story.edit") {
|
||||||
|
return models.Comment{}, errors.New("Unauthorized")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !chapter.CanComment() {
|
||||||
|
return models.Comment{}, errors.New("Comments are disabled or locked")
|
||||||
|
}
|
||||||
|
|
||||||
|
var characterPtr *models.Character
|
||||||
|
if input.CharacterID != nil {
|
||||||
|
character, err := characters.FindID(*input.CharacterID)
|
||||||
|
if err != nil {
|
||||||
|
return models.Comment{}, errors.New("Character not found")
|
||||||
|
} else if character.Author != token.UserID {
|
||||||
|
return models.Comment{}, errors.New("That is not your character")
|
||||||
|
}
|
||||||
|
|
||||||
|
characterPtr = &character
|
||||||
|
}
|
||||||
|
|
||||||
|
fictionalDate := time.Time{}
|
||||||
|
if input.FictionalDate != nil {
|
||||||
|
fictionalDate = *input.FictionalDate
|
||||||
|
}
|
||||||
|
|
||||||
|
subject := ""
|
||||||
|
if input.Subject != nil {
|
||||||
|
subject = *input.Subject
|
||||||
|
}
|
||||||
|
|
||||||
|
comment, err := comments.Add(chapter, subject, token.UserID, input.Source, input.CharacterName, characterPtr, time.Now(), fictionalDate)
|
||||||
|
if err != nil {
|
||||||
|
return models.Comment{}, errors.New("Failed to add comment: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
story, err := stories.FindID(chapter.StoryID)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("WARNING: Couldn't log comment change:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
changes.Submit("Comment", "add", token.UserID, true, changekeys.Many(comment, chapter, models.Story{ID: chapter.StoryID}), comment, chapter, story)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return comment, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *mutationResolver) EditComment(ctx context.Context, input input.CommentEditInput) (models.Comment, error) {
|
||||||
|
comment, err := comments.Find(input.CommentID)
|
||||||
|
if err != nil {
|
||||||
|
return models.Comment{}, errors.New("Comment not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
token := auth.TokenFromContext(ctx)
|
||||||
|
if !token.PermittedUser(comment.Author, "member", "story.edit") {
|
||||||
|
return models.Comment{}, errors.New("You cannot edit this comment")
|
||||||
|
}
|
||||||
|
|
||||||
|
chapter, err := chapters.FindID(comment.ChapterID)
|
||||||
|
if err != nil {
|
||||||
|
return models.Comment{}, errors.New("Comment's chapter not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !chapter.CanComment() {
|
||||||
|
return models.Comment{}, errors.New("Comments are disabled or locked")
|
||||||
|
}
|
||||||
|
|
||||||
|
if input.ClearFictionalDate != nil && *input.ClearFictionalDate == true {
|
||||||
|
input.FictionalDate = &time.Time{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if input.CharacterID != nil && *input.CharacterID != "" {
|
||||||
|
character, err := characters.FindID(*input.CharacterID)
|
||||||
|
if err != nil {
|
||||||
|
return models.Comment{}, errors.New("Character not found")
|
||||||
|
} else if character.Author != token.UserID {
|
||||||
|
return models.Comment{}, errors.New("That is not your character")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
comment, err = comments.Edit(comment, input.Source, input.CharacterName, input.CharacterID, input.Subject, input.FictionalDate)
|
||||||
|
if err != nil {
|
||||||
|
return models.Comment{}, errors.New("Could not post comment: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
story, err := stories.FindID(chapter.StoryID)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("WARNING: Couldn't log comment change:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
changes.Submit("Comment", "edit", token.UserID, true, changekeys.Many(comment, chapter, models.Story{ID: chapter.StoryID}), comment, chapter, story)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return comment, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *mutationResolver) RemoveComment(ctx context.Context, input input.CommentRemoveInput) (models.Comment, error) {
|
||||||
|
comment, err := comments.Find(input.CommentID)
|
||||||
|
if err != nil {
|
||||||
|
return models.Comment{}, errors.New("Comment not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
token := auth.TokenFromContext(ctx)
|
||||||
|
if !token.PermittedUser(comment.Author, "member", "story.edit") {
|
||||||
|
return models.Comment{}, errors.New("You cannot remove this comment")
|
||||||
|
}
|
||||||
|
|
||||||
|
chapter, err := chapters.FindID(comment.ChapterID)
|
||||||
|
if err != nil {
|
||||||
|
return models.Comment{}, errors.New("Comment's chapter not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !chapter.CanComment() {
|
||||||
|
return models.Comment{}, errors.New("Comments are disabled or locked")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = comments.Remove(comment)
|
||||||
|
if err != nil {
|
||||||
|
return models.Comment{}, errors.New("Failed to remove comment: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
story, err := stories.FindID(chapter.StoryID)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("WARNING: Couldn't log comment change:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
changes.Submit("Comment", "remove", token.UserID, true, changekeys.Many(comment, chapter, models.Story{ID: chapter.StoryID}), comment, chapter, story)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return comment, nil
|
||||||
|
}
|
||||||
@@ -41,6 +41,10 @@ type Query {
|
|||||||
chapter(id: String!): Chapter!
|
chapter(id: String!): Chapter!
|
||||||
|
|
||||||
|
|
||||||
|
# Find comment by ID
|
||||||
|
comment(id: String!): Comment!
|
||||||
|
|
||||||
|
|
||||||
# Find all distinct tags used in stories
|
# Find all distinct tags used in stories
|
||||||
tags: [Tag!]!
|
tags: [Tag!]!
|
||||||
|
|
||||||
@@ -97,6 +101,16 @@ type Mutation {
|
|||||||
removeChapter(input: ChapterRemoveInput!): Chapter!
|
removeChapter(input: ChapterRemoveInput!): Chapter!
|
||||||
|
|
||||||
|
|
||||||
|
"Add a comment to a chapter."
|
||||||
|
addComment(input: CommentAddInput!): Comment!
|
||||||
|
|
||||||
|
"Edit a comment in a chapter."
|
||||||
|
editComment(input: CommentEditInput!): Comment!
|
||||||
|
|
||||||
|
"Remove a comemnt in a chapter."
|
||||||
|
removeComment(input: CommentRemoveInput!): Comment!
|
||||||
|
|
||||||
|
|
||||||
# Add a new log
|
# Add a new log
|
||||||
addLog(input: LogAddInput!): Log!
|
addLog(input: LogAddInput!): Log!
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ type Change {
|
|||||||
objects: [ChangeObject!]!
|
objects: [ChangeObject!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
union ChangeObject = Character | Channel | Log | Post | Story | Tag | Chapter
|
union ChangeObject = Character | Channel | Log | Post | Story | Tag | Chapter | Comment
|
||||||
|
|
||||||
"""
|
"""
|
||||||
A change key is a key associated with a change, used for filtering
|
A change key is a key associated with a change, used for filtering
|
||||||
|
|||||||
@@ -20,6 +20,35 @@ type Chapter {
|
|||||||
|
|
||||||
# The date of edit.
|
# The date of edit.
|
||||||
editedDate: Date!
|
editedDate: Date!
|
||||||
|
|
||||||
|
"The comment mode."
|
||||||
|
commentMode: ChapterCommentMode!
|
||||||
|
|
||||||
|
"Whether the author has locket comments."
|
||||||
|
commentsLocked: Boolean!
|
||||||
|
|
||||||
|
"A shorthand for checking whether a logged-in user can comment on this chapter."
|
||||||
|
canComment: Boolean!
|
||||||
|
|
||||||
|
"Get all chapter comments."
|
||||||
|
comments(limit: Int): [Comment!]!
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
The possibles modes of comments for a chapter.
|
||||||
|
"""
|
||||||
|
enum ChapterCommentMode {
|
||||||
|
"Comments are disabled and hidden."
|
||||||
|
Disabled
|
||||||
|
|
||||||
|
"Comments should be shown as typical news article comments."
|
||||||
|
Article
|
||||||
|
|
||||||
|
"Comments should be shown in a compact form suitable for many small messages."
|
||||||
|
Chat
|
||||||
|
|
||||||
|
"Comments are going to be shown as omni-tool messages."
|
||||||
|
Message
|
||||||
}
|
}
|
||||||
|
|
||||||
# Input for addChapter mutation
|
# Input for addChapter mutation
|
||||||
@@ -41,6 +70,9 @@ input ChapterAddInput {
|
|||||||
|
|
||||||
# Optionally, assign a fictional date for the chapter
|
# Optionally, assign a fictional date for the chapter
|
||||||
fictionalDate: Date
|
fictionalDate: Date
|
||||||
|
|
||||||
|
"The comment mode for the chapter."
|
||||||
|
commentMode: ChapterCommentMode
|
||||||
}
|
}
|
||||||
|
|
||||||
# Input for editChapter mutation
|
# Input for editChapter mutation
|
||||||
@@ -59,6 +91,12 @@ input ChapterEditInput {
|
|||||||
|
|
||||||
# Remove the fictional date for a chapter
|
# Remove the fictional date for a chapter
|
||||||
clearFictionalDate: Boolean
|
clearFictionalDate: Boolean
|
||||||
|
|
||||||
|
"Set the comment mode for the chapter"
|
||||||
|
commentMode: ChapterCommentMode
|
||||||
|
|
||||||
|
"Set whether the chapter comments are locked"
|
||||||
|
commentsLocked: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
# Input for moveChapter mutation
|
# Input for moveChapter mutation
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
"""
|
||||||
|
A Comment represents a comment to a story chapter.
|
||||||
|
"""
|
||||||
|
type Comment {
|
||||||
|
"A unique ID of the change."
|
||||||
|
id: String!
|
||||||
|
|
||||||
|
"subject"
|
||||||
|
subject: String!
|
||||||
|
|
||||||
|
"The chapter ID the comment belongs to."
|
||||||
|
chapterId: String!
|
||||||
|
|
||||||
|
"The comment's author."
|
||||||
|
author: String!
|
||||||
|
|
||||||
|
"The displayed name of the character. This may be the same as character.name, but it does not need to be."
|
||||||
|
characterName: String!
|
||||||
|
|
||||||
|
"The character associated with the comment."
|
||||||
|
character: Character
|
||||||
|
|
||||||
|
"The fictional (IC) date of the comment."
|
||||||
|
fictionalDate: Date
|
||||||
|
|
||||||
|
"The date of creation."
|
||||||
|
createdDate: Date!
|
||||||
|
|
||||||
|
"The date of the last edit."
|
||||||
|
editedDate: Date!
|
||||||
|
|
||||||
|
"The markdown source of the comment."
|
||||||
|
source: String!
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
Input for the addComment mutation
|
||||||
|
"""
|
||||||
|
input CommentAddInput {
|
||||||
|
"What chapter to comment on."
|
||||||
|
chapterId: String!
|
||||||
|
|
||||||
|
"The markdown source of the comment."
|
||||||
|
source: String!
|
||||||
|
|
||||||
|
"The name of the character associated with this comment."
|
||||||
|
characterName: String!
|
||||||
|
|
||||||
|
"Optioanlly, a character ID."
|
||||||
|
characterId: String
|
||||||
|
|
||||||
|
"Optional in-universe date of the comment."
|
||||||
|
fictionalDate: Date
|
||||||
|
|
||||||
|
"Optional subject to add to the comment, only shown in some modes."
|
||||||
|
subject: String
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
Input for the editComment mutation
|
||||||
|
"""
|
||||||
|
input CommentEditInput {
|
||||||
|
"What comment to edit."
|
||||||
|
commentId: String!
|
||||||
|
|
||||||
|
"Change the markdown source."
|
||||||
|
source: String
|
||||||
|
|
||||||
|
"Change the name of the character associated with this comment."
|
||||||
|
characterName: String
|
||||||
|
|
||||||
|
"Change the character assocaited with this comment."
|
||||||
|
characterId: String
|
||||||
|
|
||||||
|
"Change the optional in-universe date of the comment."
|
||||||
|
fictionalDate: Date
|
||||||
|
|
||||||
|
"Set to clear the fictional date."
|
||||||
|
clearFictionalDate: Boolean
|
||||||
|
|
||||||
|
"Change the optional subject for the comment, only shown in some modes."
|
||||||
|
subject: String
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
Input for the removeComment mutation
|
||||||
|
"""
|
||||||
|
input CommentRemoveInput {
|
||||||
|
"What comment to edit."
|
||||||
|
commentId: String!
|
||||||
|
}
|
||||||
@@ -2,9 +2,11 @@ package types
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.aiterp.net/rpdata/api/models"
|
"git.aiterp.net/rpdata/api/models"
|
||||||
|
"git.aiterp.net/rpdata/api/models/comments"
|
||||||
)
|
)
|
||||||
|
|
||||||
type chapterResolver struct{}
|
type chapterResolver struct{}
|
||||||
@@ -17,5 +19,22 @@ func (r *chapterResolver) FictionalDate(ctx context.Context, chapter *models.Cha
|
|||||||
return &chapter.FictionalDate, nil
|
return &chapter.FictionalDate, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *chapterResolver) Comments(ctx context.Context, chapter *models.Chapter, limit *int) ([]models.Comment, error) {
|
||||||
|
limitValue := 0
|
||||||
|
if limit != nil {
|
||||||
|
if *limit < 0 {
|
||||||
|
return nil, errors.New("Limit cannot be negative")
|
||||||
|
}
|
||||||
|
|
||||||
|
limitValue = *limit
|
||||||
|
}
|
||||||
|
|
||||||
|
if !chapter.CommentMode.IsEnabled() {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return comments.ListChapterID(chapter.ID, limitValue)
|
||||||
|
}
|
||||||
|
|
||||||
// ChapterResolver is a resolver
|
// ChapterResolver is a resolver
|
||||||
var ChapterResolver chapterResolver
|
var ChapterResolver chapterResolver
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.aiterp.net/rpdata/api/internal/loader"
|
||||||
|
"git.aiterp.net/rpdata/api/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type commentResolver struct{}
|
||||||
|
|
||||||
|
func (r *commentResolver) Character(ctx context.Context, obj *models.Comment) (*models.Character, error) {
|
||||||
|
if obj.CharacterID == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
loader := loader.FromContext(ctx)
|
||||||
|
if loader == nil {
|
||||||
|
return nil, errors.New("no loader")
|
||||||
|
}
|
||||||
|
|
||||||
|
character, err := loader.Character("id", obj.CharacterID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &character, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *commentResolver) FictionalDate(ctx context.Context, obj *models.Comment) (*time.Time, error) {
|
||||||
|
if obj.FictionalDate.IsZero() {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &obj.FictionalDate, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommentResolver is a resolver
|
||||||
|
var CommentResolver commentResolver
|
||||||
@@ -24,12 +24,14 @@ const (
|
|||||||
ChangeModelTag ChangeModel = "Tag"
|
ChangeModelTag ChangeModel = "Tag"
|
||||||
// ChangeModelChapter is a value of ChangeModel
|
// ChangeModelChapter is a value of ChangeModel
|
||||||
ChangeModelChapter ChangeModel = "Chapter"
|
ChangeModelChapter ChangeModel = "Chapter"
|
||||||
|
// ChangeModelComment is a value of ChangeModel
|
||||||
|
ChangeModelComment ChangeModel = "Comment"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IsValid returns true if the underlying string is one of the correct values.
|
// IsValid returns true if the underlying string is one of the correct values.
|
||||||
func (e ChangeModel) IsValid() bool {
|
func (e ChangeModel) IsValid() bool {
|
||||||
switch e {
|
switch e {
|
||||||
case ChangeModelCharacter, ChangeModelChannel, ChangeModelLog, ChangeModelPost, ChangeModelStory, ChangeModelTag, ChangeModelChapter:
|
case ChangeModelCharacter, ChangeModelChannel, ChangeModelLog, ChangeModelPost, ChangeModelStory, ChangeModelTag, ChangeModelChapter, ChangeModelComment:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|||||||
+5
-1
@@ -19,6 +19,7 @@ type Change struct {
|
|||||||
Stories []Story `bson:"stories"`
|
Stories []Story `bson:"stories"`
|
||||||
Tags []Tag `bson:"tags"`
|
Tags []Tag `bson:"tags"`
|
||||||
Chapters []Chapter `bson:"chapters"`
|
Chapters []Chapter `bson:"chapters"`
|
||||||
|
Comments []Comment `bson:"comments"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChangeKey is a key for a change that can be used when subscribing to them.
|
// ChangeKey is a key for a change that can be used when subscribing to them.
|
||||||
@@ -29,7 +30,7 @@ type ChangeKey struct {
|
|||||||
|
|
||||||
// Objects makes a combined, mixed array of all the models stored in this change.
|
// Objects makes a combined, mixed array of all the models stored in this change.
|
||||||
func (change *Change) Objects() []interface{} {
|
func (change *Change) Objects() []interface{} {
|
||||||
data := make([]interface{}, 0, len(change.Logs)+len(change.Characters)+len(change.Channels)+len(change.Posts)+len(change.Stories)+len(change.Tags)+len(change.Chapters))
|
data := make([]interface{}, 0, len(change.Logs)+len(change.Characters)+len(change.Channels)+len(change.Posts)+len(change.Stories)+len(change.Tags)+len(change.Chapters)+len(change.Comments))
|
||||||
|
|
||||||
for _, log := range change.Logs {
|
for _, log := range change.Logs {
|
||||||
data = append(data, log)
|
data = append(data, log)
|
||||||
@@ -52,6 +53,9 @@ func (change *Change) Objects() []interface{} {
|
|||||||
for _, chapter := range change.Chapters {
|
for _, chapter := range change.Chapters {
|
||||||
data = append(data, chapter)
|
data = append(data, chapter)
|
||||||
}
|
}
|
||||||
|
for _, comment := range change.Comments {
|
||||||
|
data = append(data, comment)
|
||||||
|
}
|
||||||
|
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ func TestMany(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
models.Post{ID: "P5"},
|
models.Post{ID: "P5"},
|
||||||
|
models.Comment{ID: "SCC1234"},
|
||||||
}
|
}
|
||||||
|
|
||||||
expectations := []models.ChangeKey{
|
expectations := []models.ChangeKey{
|
||||||
@@ -33,6 +34,7 @@ func TestMany(t *testing.T) {
|
|||||||
{Model: "Character", ID: "C17"},
|
{Model: "Character", ID: "C17"},
|
||||||
{Model: "Channel", ID: "#Stuff"},
|
{Model: "Channel", ID: "#Stuff"},
|
||||||
{Model: "Post", ID: "P5"},
|
{Model: "Post", ID: "P5"},
|
||||||
|
{Model: "Comment", ID: "SCC1234"},
|
||||||
}
|
}
|
||||||
|
|
||||||
results := changekeys.Many(data...)
|
results := changekeys.Many(data...)
|
||||||
|
|||||||
@@ -28,6 +28,11 @@ func TestOne(t *testing.T) {
|
|||||||
models.Log{ID: "2018-10-23_210303325_RedrockAgency", ShortID: "L807"},
|
models.Log{ID: "2018-10-23_210303325_RedrockAgency", ShortID: "L807"},
|
||||||
models.ChangeKey{Model: "Log", ID: "2018-10-23_210303325_RedrockAgency"},
|
models.ChangeKey{Model: "Log", ID: "2018-10-23_210303325_RedrockAgency"},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"Comment_SCC1234",
|
||||||
|
models.Comment{ID: "SCC1234"},
|
||||||
|
models.ChangeKey{Model: "Comment", ID: "SCC1234"},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, row := range table {
|
for _, row := range table {
|
||||||
|
|||||||
@@ -81,6 +81,10 @@ func Submit(model, op, author string, listed bool, keys []models.ChangeKey, obje
|
|||||||
change.Chapters = append(change.Chapters, object)
|
change.Chapters = append(change.Chapters, object)
|
||||||
case []models.Chapter:
|
case []models.Chapter:
|
||||||
change.Chapters = append(change.Chapters, object...)
|
change.Chapters = append(change.Chapters, object...)
|
||||||
|
case models.Comment:
|
||||||
|
change.Comments = append(change.Comments, object)
|
||||||
|
case []models.Comment:
|
||||||
|
change.Comments = append(change.Comments, object...)
|
||||||
default:
|
default:
|
||||||
log.Printf("Warning: unrecognized object in change: %#+v", object)
|
log.Printf("Warning: unrecognized object in change: %#+v", object)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ChapterCommentMode represents the kind of tags.
|
||||||
|
type ChapterCommentMode string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ChapterCommentModeDisabled is a chapter comment mode, see GraphQL documentation.
|
||||||
|
ChapterCommentModeDisabled ChapterCommentMode = "Disabled"
|
||||||
|
|
||||||
|
// ChapterCommentModeArticle is a chapter comment mode, see GraphQL documentation.
|
||||||
|
ChapterCommentModeArticle ChapterCommentMode = "Article"
|
||||||
|
|
||||||
|
// ChapterCommentModeChat is a chapter comment mode, see GraphQL documentation.
|
||||||
|
ChapterCommentModeChat ChapterCommentMode = "Chat"
|
||||||
|
|
||||||
|
// ChapterCommentModeMessage is a chapter comment mode, see GraphQL documentation.
|
||||||
|
ChapterCommentModeMessage ChapterCommentMode = "Message"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UnmarshalGQL unmarshals
|
||||||
|
func (e *ChapterCommentMode) UnmarshalGQL(v interface{}) error {
|
||||||
|
str, ok := v.(string)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("enums must be strings")
|
||||||
|
}
|
||||||
|
|
||||||
|
*e = ChapterCommentMode(str)
|
||||||
|
switch *e {
|
||||||
|
case ChapterCommentModeDisabled, ChapterCommentModeArticle, ChapterCommentModeChat, ChapterCommentModeMessage:
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("%s is not a valid ChapterCommentMode", str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEnabled returns true if comments are enabled.
|
||||||
|
func (e ChapterCommentMode) IsEnabled() bool {
|
||||||
|
return e != ChapterCommentModeDisabled && len(e) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalGQL turns it into a JSON string
|
||||||
|
func (e ChapterCommentMode) MarshalGQL(w io.Writer) {
|
||||||
|
// Backwards compatibility: Empty value means disabled.
|
||||||
|
if len(e) == 0 {
|
||||||
|
w.Write(disabledChapterCommentModeBytes)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprint(w, "\""+string(e)+"\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
var disabledChapterCommentModeBytes = []byte(`"Disabled"`)
|
||||||
@@ -12,4 +12,11 @@ type Chapter struct {
|
|||||||
CreatedDate time.Time `bson:"createdDate"`
|
CreatedDate time.Time `bson:"createdDate"`
|
||||||
FictionalDate time.Time `bson:"fictionalDate,omitempty"`
|
FictionalDate time.Time `bson:"fictionalDate,omitempty"`
|
||||||
EditedDate time.Time `bson:"editedDate"`
|
EditedDate time.Time `bson:"editedDate"`
|
||||||
|
CommentMode ChapterCommentMode `bson:"commentMode"`
|
||||||
|
CommentsLocked bool `bson:"commentsLocked"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanComment returns true if the chapter can be commented to.
|
||||||
|
func (chapter *Chapter) CanComment() bool {
|
||||||
|
return !chapter.CommentsLocked && chapter.CommentMode.IsEnabled()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Add adds a new chapter.
|
// Add adds a new chapter.
|
||||||
func Add(story models.Story, title, author, source string, createdDate time.Time, finctionalDate *time.Time) (models.Chapter, error) {
|
func Add(story models.Story, title, author, source string, createdDate time.Time, finctionalDate *time.Time, commentMode models.ChapterCommentMode) (models.Chapter, error) {
|
||||||
chapter := models.Chapter{
|
chapter := models.Chapter{
|
||||||
ID: makeChapterID(),
|
ID: makeChapterID(),
|
||||||
StoryID: story.ID,
|
StoryID: story.ID,
|
||||||
@@ -17,6 +17,8 @@ func Add(story models.Story, title, author, source string, createdDate time.Time
|
|||||||
Source: source,
|
Source: source,
|
||||||
CreatedDate: createdDate,
|
CreatedDate: createdDate,
|
||||||
EditedDate: createdDate,
|
EditedDate: createdDate,
|
||||||
|
CommentMode: commentMode,
|
||||||
|
CommentsLocked: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
if finctionalDate != nil {
|
if finctionalDate != nil {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
|
|
||||||
// Edit edits a chapter, and updates EditedDate. While many Edit functions cheat if there's nothing to
|
// Edit edits a chapter, and updates EditedDate. While many Edit functions cheat if there's nothing to
|
||||||
// change, this functill will due to EditedDate.
|
// change, this functill will due to EditedDate.
|
||||||
func Edit(chapter models.Chapter, title, source *string, fictionalDate *time.Time) (models.Chapter, error) {
|
func Edit(chapter models.Chapter, title, source *string, fictionalDate *time.Time, commentMode *models.ChapterCommentMode, commentsLocked *bool) (models.Chapter, error) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
changes := bson.M{"editedDate": now}
|
changes := bson.M{"editedDate": now}
|
||||||
|
|
||||||
@@ -28,6 +28,14 @@ func Edit(chapter models.Chapter, title, source *string, fictionalDate *time.Tim
|
|||||||
changes["fictionalDate"] = *fictionalDate
|
changes["fictionalDate"] = *fictionalDate
|
||||||
edited.FictionalDate = *fictionalDate
|
edited.FictionalDate = *fictionalDate
|
||||||
}
|
}
|
||||||
|
if commentMode != nil && *commentMode != chapter.CommentMode {
|
||||||
|
changes["commentMode"] = *commentMode
|
||||||
|
edited.CommentMode = *commentMode
|
||||||
|
}
|
||||||
|
if commentsLocked != nil && *commentsLocked != chapter.CommentsLocked {
|
||||||
|
changes["commentsLocked"] = *commentsLocked
|
||||||
|
edited.CommentsLocked = *commentsLocked
|
||||||
|
}
|
||||||
|
|
||||||
err := collection.UpdateId(chapter.ID, bson.M{"$set": changes})
|
err := collection.UpdateId(chapter.ID, bson.M{"$set": changes})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// A Comment is a comment on a chapter.
|
||||||
|
type Comment struct {
|
||||||
|
ID string `bson:"_id"`
|
||||||
|
ChapterID string `bson:"chapterId"`
|
||||||
|
Subject string `bson:"subject"`
|
||||||
|
Author string `bson:"author"`
|
||||||
|
CharacterName string `bson:"characterName"`
|
||||||
|
CharacterID string `bson:"characterId"`
|
||||||
|
FictionalDate time.Time `bson:"fictionalDate"`
|
||||||
|
CreatedDate time.Time `bson:"createdDate"`
|
||||||
|
EditedDate time.Time `bson:"editeddDate"`
|
||||||
|
Source string `bson:"source"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package comments
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.aiterp.net/rpdata/api/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Add adds a comment.
|
||||||
|
func Add(chapter models.Chapter, subject, author, source, characterName string, character *models.Character, createdDate time.Time, fictionalDate time.Time) (models.Comment, error) {
|
||||||
|
characterID := ""
|
||||||
|
if character != nil {
|
||||||
|
characterID = character.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
comment := models.Comment{
|
||||||
|
ID: makeCommentID(),
|
||||||
|
ChapterID: chapter.ID,
|
||||||
|
Subject: subject,
|
||||||
|
Author: author,
|
||||||
|
CharacterName: characterName,
|
||||||
|
CharacterID: characterID,
|
||||||
|
FictionalDate: fictionalDate,
|
||||||
|
CreatedDate: createdDate,
|
||||||
|
EditedDate: createdDate,
|
||||||
|
Source: source,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := collection.Insert(comment)
|
||||||
|
if err != nil {
|
||||||
|
return models.Comment{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return comment, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package comments
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/binary"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"git.aiterp.net/rpdata/api/internal/store"
|
||||||
|
"git.aiterp.net/rpdata/api/models"
|
||||||
|
"github.com/globalsign/mgo"
|
||||||
|
)
|
||||||
|
|
||||||
|
var collection *mgo.Collection
|
||||||
|
|
||||||
|
func find(query interface{}) (models.Comment, error) {
|
||||||
|
comment := models.Comment{}
|
||||||
|
err := collection.Find(query).One(&comment)
|
||||||
|
|
||||||
|
return comment, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func list(query interface{}, limit int) ([]models.Comment, error) {
|
||||||
|
allocSize := 32
|
||||||
|
if limit >= 0 {
|
||||||
|
allocSize = limit
|
||||||
|
} else {
|
||||||
|
limit = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
comments := make([]models.Comment, 0, allocSize)
|
||||||
|
err := collection.Find(query).Sort("createdDate").Limit(limit).All(&comments)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return comments, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeCommentID() string {
|
||||||
|
result := "SCC"
|
||||||
|
offset := 0
|
||||||
|
data := make([]byte, 48)
|
||||||
|
|
||||||
|
rand.Read(data)
|
||||||
|
for len(result) < 32 {
|
||||||
|
result += strconv.FormatUint(binary.LittleEndian.Uint64(data[offset:]), 36)
|
||||||
|
offset += 8
|
||||||
|
|
||||||
|
if offset >= 48 {
|
||||||
|
rand.Read(data)
|
||||||
|
offset = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result[:32]
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
store.HandleInit(func(db *mgo.Database) {
|
||||||
|
collection = db.C("story.comments")
|
||||||
|
|
||||||
|
collection.EnsureIndexKey("chapterId")
|
||||||
|
collection.EnsureIndexKey("author")
|
||||||
|
collection.EnsureIndexKey("createdDate")
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package comments
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.aiterp.net/rpdata/api/models"
|
||||||
|
"github.com/globalsign/mgo/bson"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Edit edits a comment.
|
||||||
|
func Edit(comment models.Comment, source, characterName, characterID, subject *string, fictionalDate *time.Time) (models.Comment, error) {
|
||||||
|
changes := make(bson.M, 6)
|
||||||
|
|
||||||
|
comment.EditedDate = time.Now()
|
||||||
|
changes["editedDate"] = comment.EditedDate
|
||||||
|
|
||||||
|
if source != nil {
|
||||||
|
comment.Source = *source
|
||||||
|
changes["source"] = *source
|
||||||
|
}
|
||||||
|
if characterName != nil {
|
||||||
|
comment.CharacterName = *characterName
|
||||||
|
changes["characterName"] = *characterName
|
||||||
|
}
|
||||||
|
if characterID != nil {
|
||||||
|
comment.CharacterID = *characterID
|
||||||
|
changes["characterId"] = *characterID
|
||||||
|
}
|
||||||
|
if subject != nil {
|
||||||
|
comment.Subject = *subject
|
||||||
|
changes["subject"] = *subject
|
||||||
|
}
|
||||||
|
if fictionalDate != nil {
|
||||||
|
comment.FictionalDate = *fictionalDate
|
||||||
|
changes["fictionalDate"] = *fictionalDate
|
||||||
|
}
|
||||||
|
|
||||||
|
err := collection.UpdateId(comment.ID, bson.M{"$set": changes})
|
||||||
|
if err != nil {
|
||||||
|
return models.Comment{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return comment, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package comments
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.aiterp.net/rpdata/api/models"
|
||||||
|
"github.com/globalsign/mgo/bson"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Find finds a comment by ID.
|
||||||
|
func Find(id string) (models.Comment, error) {
|
||||||
|
return find(bson.M{"_id": id})
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package comments
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.aiterp.net/rpdata/api/models"
|
||||||
|
"github.com/globalsign/mgo/bson"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListChapterID lists all comments by chapter-ID
|
||||||
|
func ListChapterID(chapterID string, limit int) ([]models.Comment, error) {
|
||||||
|
return list(bson.M{"chapterId": chapterID}, limit)
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package comments
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.aiterp.net/rpdata/api/models"
|
||||||
|
"github.com/globalsign/mgo/bson"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Remove removes one comment.
|
||||||
|
func Remove(comment models.Comment) error {
|
||||||
|
return collection.RemoveId(comment.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveChapter removes all comments for the given chapter.
|
||||||
|
func RemoveChapter(chapter models.Chapter) error {
|
||||||
|
_, err := collection.RemoveAll(bson.M{"chapterId": chapter.ID})
|
||||||
|
return err
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user