diff --git a/app/http/endpoints/api/panelcreate.go b/app/http/endpoints/api/panelcreate.go index 483111a..0b8c56d 100644 --- a/app/http/endpoints/api/panelcreate.go +++ b/app/http/endpoints/api/panelcreate.go @@ -228,11 +228,11 @@ func (p *panel) verifyContent() bool { return len(p.Content) > 0 && len(p.Content) < 1025 } -func (p *panel) getEmoji() (string, bool) { +func (p *panel) getEmoji() (emoji string, ok bool) { p.Emote = strings.Replace(p.Emote, ":", "", -1) - emoji := utils.GetEmojiByName(p.Emote) - return emoji, emoji != "" + emoji, ok = utils.GetEmoji(p.Emote) + return } func (p *panel) verifyChannel(channels []channel.Channel) bool { diff --git a/app/http/endpoints/api/panelupdate.go b/app/http/endpoints/api/panelupdate.go new file mode 100644 index 0000000..1a1f580 --- /dev/null +++ b/app/http/endpoints/api/panelupdate.go @@ -0,0 +1,206 @@ +package api + +import ( + "github.com/TicketsBot/GoPanel/botcontext" + dbclient "github.com/TicketsBot/GoPanel/database" + "github.com/TicketsBot/GoPanel/rpc" + "github.com/TicketsBot/common/premium" + "github.com/TicketsBot/database" + "github.com/gin-gonic/gin" + "github.com/rxdn/gdl/rest" + "github.com/rxdn/gdl/rest/request" + "strconv" +) + +func UpdatePanel(ctx *gin.Context) { + guildId := ctx.Keys["guildid"].(uint64) + + botContext, err := botcontext.ContextForGuild(guildId) + if err != nil { + ctx.AbortWithStatusJSON(500, gin.H{ + "success": false, + "error": err.Error(), + }) + return + } + + var data panel + + if err := ctx.BindJSON(&data); err != nil { + ctx.AbortWithStatusJSON(400, gin.H{ + "success": false, + "error": err.Error(), + }) + return + } + + messageId, err := strconv.ParseUint(ctx.Param("message"), 10, 64) + if err != nil { + ctx.AbortWithStatusJSON(400, gin.H{ + "success": false, + "error": err.Error(), + }) + return + } + + data.MessageId = messageId + + // get existing + existing, err := dbclient.Client.Panel.Get(data.MessageId) + if err != nil { + ctx.AbortWithStatusJSON(500, gin.H{ + "success": false, + "error": err.Error(), + }) + return + } + + // check guild ID matches + if existing.GuildId != guildId { + ctx.AbortWithStatusJSON(400, gin.H{ + "success": false, + "error": "Guild ID does not match", + }) + return + } + + if !data.doValidations(ctx, guildId) { + return + } + + // check if we need to update the message + shouldUpdateMessage := uint32(existing.Colour) != data.Colour || + existing.ChannelId != data.ChannelId || + existing.Content != data.Content || + existing.Title != data.Title || + existing.ReactionEmote != data.Emote + + emoji, _ := data.getEmoji() // already validated + newMessageId := messageId + + if shouldUpdateMessage { + // delete old message + if err := rest.DeleteMessage(botContext.Token, botContext.RateLimiter, existing.ChannelId, existing.MessageId); err != nil { + ctx.AbortWithStatusJSON(500, gin.H{ + "success": false, + "error": err.Error(), + }) + return + } + + premiumTier := rpc.PremiumClient.GetTierByGuildId(guildId, true, botContext.Token, botContext.RateLimiter) + newMessageId, err = data.sendEmbed(&botContext, premiumTier > premium.None) + if err != nil { + if err == request.ErrForbidden { + ctx.AbortWithStatusJSON(500, gin.H{ + "success": false, + "error": "I do not have permission to send messages in the specified channel", + }) + } else { + // TODO: Most appropriate error? + ctx.AbortWithStatusJSON(500, gin.H{ + "success": false, + "error": err.Error(), + }) + } + + return + } + + // Add reaction + if err = rest.CreateReaction(botContext.Token, botContext.RateLimiter, data.ChannelId, newMessageId, emoji); err != nil { + if err == request.ErrForbidden { + ctx.AbortWithStatusJSON(500, gin.H{ + "success": false, + "error": "I do not have permission to add reactions in the specified channel", + }) + } else { + // TODO: Most appropriate error? + ctx.AbortWithStatusJSON(500, gin.H{ + "success": false, + "error": err.Error(), + }) + } + + return + } + } + + // Store in DB + panel := database.Panel{ + MessageId: newMessageId, + ChannelId: data.ChannelId, + GuildId: guildId, + Title: data.Title, + Content: data.Content, + Colour: int32(data.Colour), + TargetCategory: data.CategoryId, + ReactionEmote: emoji, + WelcomeMessage: data.WelcomeMessage, + } + + if err = dbclient.Client.Panel.Update(messageId, panel); err != nil { + ctx.AbortWithStatusJSON(500, gin.H{ + "success": false, + "error": err.Error(), + }) + return + } + + // insert role mention data + // delete old data + if err = dbclient.Client.PanelRoleMentions.DeleteAll(newMessageId); err != nil { + ctx.AbortWithStatusJSON(500, gin.H{ + "success": false, + "error": err.Error(), + }) + return + } + + // TODO: Reduce to 1 query + if err = dbclient.Client.PanelUserMention.Set(newMessageId, false); err != nil { + ctx.AbortWithStatusJSON(500, gin.H{ + "success": false, + "error": err.Error(), + }) + return + } + + // string is role ID or "user" to mention the ticket opener + for _, mention := range data.Mentions { + if mention == "user" { + if err = dbclient.Client.PanelUserMention.Set(newMessageId, true); err != nil { + ctx.AbortWithStatusJSON(500, gin.H{ + "success": false, + "error": err.Error(), + }) + return + } + } else { + roleId, err := strconv.ParseUint(mention, 10, 64) + if err != nil { + ctx.AbortWithStatusJSON(500, gin.H{ + "success": false, + "error": err.Error(), + }) + return + } + + // should we check the role is a valid role in the guild? + // not too much of an issue if it isnt + + if err = dbclient.Client.PanelRoleMentions.Add(newMessageId, roleId); err != nil { + ctx.AbortWithStatusJSON(500, gin.H{ + "success": false, + "error": err.Error(), + }) + return + } + } + } + + ctx.JSON(200, gin.H{ + "success": true, + "message_id": strconv.FormatUint(newMessageId, 10), + }) +} diff --git a/app/http/server.go b/app/http/server.go index 0cee33e..4081ed0 100644 --- a/app/http/server.go +++ b/app/http/server.go @@ -89,6 +89,7 @@ func StartServer() { guildAuthApi.GET("/panels", api.ListPanels) guildAuthApi.PUT("/panels", api.CreatePanel) + guildAuthApi.PUT("/panels/:message", api.UpdatePanel) guildAuthApi.DELETE("/panels/:message", api.DeletePanel) guildAuthApi.GET("/logs/", api.GetLogs) @@ -142,36 +143,43 @@ func createRenderer() multitemplate.Renderer { r = addManageTemplate(r, "blacklist") r = addManageTemplate(r, "logs") r = addManageTemplate(r, "modmaillogs") - r = addManageTemplate(r, "settings") + r = addManageTemplate(r, "settings", "./public/templates/includes/substitutionmodal.tmpl") r = addManageTemplate(r, "ticketlist") r = addManageTemplate(r, "ticketview") - r = addManageTemplate(r, "panels") + r = addManageTemplate(r, "panels", "./public/templates/includes/substitutionmodal.tmpl", "./public/templates/includes/paneleditmodal.tmpl") r = addManageTemplate(r, "tags") return r } -func addMainTemplate(renderer multitemplate.Renderer, name string) multitemplate.Renderer { - renderer.AddFromFiles(fmt.Sprintf("main/%s", name), +func addMainTemplate(renderer multitemplate.Renderer, name string, extra ...string) multitemplate.Renderer { + files := []string{ "./public/templates/layouts/main.tmpl", "./public/templates/includes/head.tmpl", "./public/templates/includes/sidebar.tmpl", "./public/templates/includes/loadingscreen.tmpl", fmt.Sprintf("./public/templates/views/%s.tmpl", name), - ) + } + + files = append(files, extra...) + + renderer.AddFromFiles(fmt.Sprintf("main/%s", name), files...) return renderer } -func addManageTemplate(renderer multitemplate.Renderer, name string) multitemplate.Renderer { - renderer.AddFromFiles(fmt.Sprintf("manage/%s", name), +func addManageTemplate(renderer multitemplate.Renderer, name string, extra ...string) multitemplate.Renderer { + files := []string{ "./public/templates/layouts/manage.tmpl", "./public/templates/includes/head.tmpl", "./public/templates/includes/sidebar.tmpl", "./public/templates/includes/navbar.tmpl", - "./public/templates/includes/substitutionmodal.tmpl", "./public/templates/includes/loadingscreen.tmpl", fmt.Sprintf("./public/templates/views/%s.tmpl", name), - ) + } + + files = append(files, extra...) + + renderer.AddFromFiles(fmt.Sprintf("manage/%s", name), files...) return renderer } diff --git a/go.mod b/go.mod index 0a313b0..f069d82 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/BurntSushi/toml v0.3.1 github.com/TicketsBot/archiverclient v0.0.0-20200425115930-0ca198cc8306 github.com/TicketsBot/common v0.0.0-20200529141045-7426ad13f1a4 - github.com/TicketsBot/database v0.0.0-20200619194554-a6db672a94cf + github.com/TicketsBot/database v0.0.0-20200620140717-f747a0bb4238 github.com/apex/log v1.1.2 github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect github.com/dgrijalva/jwt-go v3.2.0+incompatible diff --git a/public/static/css/style.css b/public/static/css/style.css index 83baeae..2af8984 100644 --- a/public/static/css/style.css +++ b/public/static/css/style.css @@ -195,3 +195,7 @@ html > ::-webkit-scrollbar { .bootstrap-select .bs-ok-default:after { color: #2ECC71 !important; } + +.wrapper { + z-index: 1000 !important; +} \ No newline at end of file diff --git a/public/static/js/modalbackdrop.js b/public/static/js/modalbackdrop.js new file mode 100644 index 0000000..84e93d5 --- /dev/null +++ b/public/static/js/modalbackdrop.js @@ -0,0 +1,23 @@ +function clear(...elements) { + for (const elementId of elements) { + document.getElementById(elementId).value = ''; + } +} + +function hideBackdrop() { + for (const backdrop of document.getElementsByClassName('modal-backdrop fade show')) { + backdrop.remove(); + } +} + +function registerHideListener(elementId) { + $(`#${elementId}`).on('hidden.bs.modal', hideBackdrop); +} + +function showBackdrop() { + hideBackdrop(); + + const backdrop = document.createElement('div'); + backdrop.classList.add('modal-backdrop', 'fade', 'show'); + document.getElementsByClassName('main-panel')[0].appendChild(backdrop); +} diff --git a/public/templates/includes/paneleditmodal.tmpl b/public/templates/includes/paneleditmodal.tmpl new file mode 100644 index 0000000..fa76cc1 --- /dev/null +++ b/public/templates/includes/paneleditmodal.tmpl @@ -0,0 +1,222 @@ +{{define "paneleditmodal"}} +
+ + +{{end}} \ No newline at end of file diff --git a/public/templates/includes/substitutionmodal.tmpl b/public/templates/includes/substitutionmodal.tmpl index fbf3ec4..7214a35 100644 --- a/public/templates/includes/substitutionmodal.tmpl +++ b/public/templates/includes/substitutionmodal.tmpl @@ -31,4 +31,13 @@ + + {{end}} \ No newline at end of file diff --git a/public/templates/layouts/manage.tmpl b/public/templates/layouts/manage.tmpl index a14492b..62c8a79 100644 --- a/public/templates/layouts/manage.tmpl +++ b/public/templates/layouts/manage.tmpl @@ -5,7 +5,6 @@