use shared cache

This commit is contained in:
Dot-Rar 2020-04-02 20:53:11 +01:00
parent 770d8925bf
commit 26172fbd89
86 changed files with 9138 additions and 9136 deletions

View File

@ -1,19 +1,19 @@
# Golang CircleCI 2.0 configuration file # Golang CircleCI 2.0 configuration file
# #
# Check https://circleci.com/docs/2.0/language-go/ for more details # Check https://circleci.com/docs/2.0/language-go/ for more details
version: 2 version: 2
jobs: jobs:
build: build:
docker: docker:
- image: circleci/golang:1.12 - image: circleci/golang:1.12
working_directory: /go/src/github.com/{{ORG_NAME}}/{{REPO_NAME}} working_directory: /go/src/github.com/{{ORG_NAME}}/{{REPO_NAME}}
steps: steps:
- checkout - checkout
# specify any bash command here prefixed with `run: ` # specify any bash command here prefixed with `run: `
- run: go get -v ./cmd/panel/ - run: go get -v ./cmd/panel/
- run: go build ./cmd/panel/main.go - run: go build ./cmd/panel/main.go
- store_artifacts: - store_artifacts:
path: /go/src/github.com/{{ORG_NAME}}/{{REPO_NAME}}/main path: /go/src/github.com/{{ORG_NAME}}/{{REPO_NAME}}/main

18
.gitattributes vendored
View File

@ -1,9 +1,9 @@
public/static/css/animate.min.css linguist-vendored public/static/css/animate.min.css linguist-vendored
public/static/css/bootstrap.min.css linguist-vendored public/static/css/bootstrap.min.css linguist-vendored
public/static/css/light-bootstrap-dashboard.css linguist-vendored public/static/css/light-bootstrap-dashboard.css linguist-vendored
public/static/js/bootstrap-notify.js linguist-vendored public/static/js/bootstrap-notify.js linguist-vendored
public/static/js/bootstrap-select.js linguist-vendored public/static/js/bootstrap-select.js linguist-vendored
public/static/js/bootstrap.min.js linguist-vendored public/static/js/bootstrap.min.js linguist-vendored
public/static/js/chartist.min.js linguist-vendored public/static/js/chartist.min.js linguist-vendored
public/static/js/jquery.3.2.1.min.js linguist-vendored public/static/js/jquery.3.2.1.min.js linguist-vendored
public/static/js/light-bootstrap-dashboard.js linguist-vendored public/static/js/light-bootstrap-dashboard.js linguist-vendored

6
.gitignore vendored
View File

@ -1,3 +1,3 @@
config.toml config.toml
*.iml *.iml
.idea .idea

View File

@ -1 +1 @@
# GoPanel # GoPanel

View File

@ -114,17 +114,20 @@ func UpdateSettingsHandler(ctx *gin.Context) {
// Archive channel // Archive channel
// Create a list of IDs // Create a list of IDs
var channelIds []string channelsChan := make(chan []table.Channel)
for _, c := range guild.Channels { go table.GetCachedChannelsByGuild(guildId, channelsChan)
channelIds = append(channelIds, c.Id) channels := <-channelsChan
var channelIds []int64
for _, channel := range channels {
channelIds = append(channelIds, channel.ChannelId)
} }
// Update or archive channel // Update or archive channel
archiveChannelStr := ctx.PostForm("archivechannel") archiveChannelStr := ctx.PostForm("archivechannel")
if utils.Contains(channelIds, archiveChannelStr) { archiveChannelId, err := strconv.ParseInt(archiveChannelStr, 10, 64)
// Error is impossible, as we check it's a valid channel already if err == nil && utils.Contains(channelIds, archiveChannelId) {
parsed, _ := strconv.ParseInt(archiveChannelStr, 10, 64) table.UpdateArchiveChannel(guildId, archiveChannelId)
table.UpdateArchiveChannel(guildId, parsed)
} }
// Users can close // Users can close

View File

@ -1,25 +1,25 @@
package root package root
import ( import (
"fmt" "fmt"
"github.com/TicketsBot/GoPanel/config" "github.com/TicketsBot/GoPanel/config"
"github.com/TicketsBot/GoPanel/utils" "github.com/TicketsBot/GoPanel/utils"
"github.com/gin-gonic/contrib/sessions" "github.com/gin-gonic/contrib/sessions"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"net/url" "net/url"
) )
func LoginHandler(ctx *gin.Context) { func LoginHandler(ctx *gin.Context) {
store := sessions.Default(ctx) store := sessions.Default(ctx)
if store == nil { if store == nil {
return return
} }
defer store.Save() defer store.Save()
if utils.IsLoggedIn(store) { if utils.IsLoggedIn(store) {
ctx.Redirect(302, config.Conf.Server.BaseUrl) ctx.Redirect(302, config.Conf.Server.BaseUrl)
} else { } else {
redirect := url.QueryEscape(config.Conf.Oauth.RedirectUri) redirect := url.QueryEscape(config.Conf.Oauth.RedirectUri)
ctx.Redirect(302, fmt.Sprintf("https://discordapp.com/oauth2/authorize?response_type=code&redirect_uri=%s&scope=identify+guilds&client_id=%d", redirect, config.Conf.Oauth.Id)) ctx.Redirect(302, fmt.Sprintf("https://discordapp.com/oauth2/authorize?response_type=code&redirect_uri=%s&scope=identify+guilds&client_id=%d", redirect, config.Conf.Oauth.Id))
} }
} }

View File

@ -1,17 +1,17 @@
package root package root
import ( import (
"github.com/gin-gonic/contrib/sessions" "github.com/gin-gonic/contrib/sessions"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
func LogoutHandler(ctx *gin.Context) { func LogoutHandler(ctx *gin.Context) {
store := sessions.Default(ctx) store := sessions.Default(ctx)
if store == nil { if store == nil {
return return
} }
defer store.Save() defer store.Save()
store.Clear() store.Clear()
ctx.Redirect(302, "https://ticketsbot.net") ctx.Redirect(302, "https://ticketsbot.net")
} }

50
cache/redis.go vendored
View File

@ -1,25 +1,25 @@
package cache package cache
import ( import (
"fmt" "fmt"
"github.com/TicketsBot/GoPanel/config" "github.com/TicketsBot/GoPanel/config"
"github.com/go-redis/redis" "github.com/go-redis/redis"
) )
type RedisClient struct { type RedisClient struct {
*redis.Client *redis.Client
} }
var Client RedisClient var Client RedisClient
func NewRedisClient() RedisClient { func NewRedisClient() RedisClient {
client := redis.NewClient(&redis.Options{ client := redis.NewClient(&redis.Options{
Addr: fmt.Sprintf("%s:%d", config.Conf.Redis.Host, config.Conf.Redis.Port), Addr: fmt.Sprintf("%s:%d", config.Conf.Redis.Host, config.Conf.Redis.Port),
Password: config.Conf.Redis.Password, Password: config.Conf.Redis.Password,
PoolSize: config.Conf.Redis.Threads, PoolSize: config.Conf.Redis.Threads,
}) })
return RedisClient{ return RedisClient{
client, client,
} }
} }

42
cache/uriparser.go vendored
View File

@ -1,21 +1,21 @@
package cache package cache
import "net/url" import "net/url"
type RedisURI struct { type RedisURI struct {
Addr string Addr string
Password string Password string
} }
func ParseURI(raw string) RedisURI { func ParseURI(raw string) RedisURI {
parsed, err := url.Parse(raw); if err != nil { parsed, err := url.Parse(raw); if err != nil {
panic(err) panic(err)
} }
passwd, _ := parsed.User.Password() passwd, _ := parsed.User.Password()
return RedisURI{ return RedisURI{
Addr: parsed.Host, Addr: parsed.Host,
Password: passwd, Password: passwd,
} }
} }

View File

@ -1,35 +1,35 @@
admins=["217617036749176833"] admins=["217617036749176833"]
[server] [server]
host="0.0.0.0:3000" host="0.0.0.0:3000"
baseUrl="http://localhost:3000" baseUrl="http://localhost:3000"
mainSite="https://ticketsbot.net" mainSite="https://ticketsbot.net"
[server.ratelimit] [server.ratelimit]
window=10 window=10
max=600 max=600
[server.session] [server.session]
threads=10 threads=10
secret="secret" secret="secret"
[oauth] [oauth]
id= id=
secret="" secret=""
redirectUri="" redirectUri=""
[mariadb] [mariadb]
host="127.0.0.1" host="127.0.0.1"
username="ryan" username="ryan"
password="ryan" password="ryan"
database="tickets" database="tickets"
threads=5 threads=5
[bot] [bot]
token="" token=""
premium-lookup-proxy-url="http://localhost:3000" premium-lookup-proxy-url="http://localhost:3000"
premium-lookup-proxy-key="" premium-lookup-proxy-key=""
[redis] [redis]
host="127.0.0.1" host="127.0.0.1"
port=6379 port=6379
password="" password=""
threads=5 threads=5

View File

@ -1,78 +1,78 @@
package config package config
import ( import (
"github.com/BurntSushi/toml" "github.com/BurntSushi/toml"
"io/ioutil" "io/ioutil"
) )
type ( type (
Config struct { Config struct {
Admins []string Admins []string
Server Server Server Server
Oauth Oauth Oauth Oauth
MariaDB MariaDB MariaDB MariaDB
Bot Bot Bot Bot
Redis Redis Redis Redis
} }
Server struct { Server struct {
Host string Host string
BaseUrl string BaseUrl string
MainSite string MainSite string
Ratelimit Ratelimit Ratelimit Ratelimit
Session Session Session Session
} }
Ratelimit struct { Ratelimit struct {
Window int Window int
Max int Max int
} }
Session struct { Session struct {
Threads int Threads int
Secret string Secret string
} }
Oauth struct { Oauth struct {
Id int64 Id int64
Secret string Secret string
RedirectUri string RedirectUri string
} }
MariaDB struct { MariaDB struct {
Host string Host string
Username string Username string
Password string Password string
Database string Database string
Threads int Threads int
} }
Bot struct { Bot struct {
Token string Token string
PremiumLookupProxyUrl string `toml:"premium-lookup-proxy-url"` PremiumLookupProxyUrl string `toml:"premium-lookup-proxy-url"`
PremiumLookupProxyKey string `toml:"premium-lookup-proxy-key"` PremiumLookupProxyKey string `toml:"premium-lookup-proxy-key"`
} }
Redis struct { Redis struct {
Host string Host string
Port int Port int
Password string Password string
Threads int Threads int
} }
) )
var ( var (
Conf Config Conf Config
) )
func LoadConfig() { func LoadConfig() {
raw, err := ioutil.ReadFile("config.toml") raw, err := ioutil.ReadFile("config.toml")
if err != nil { if err != nil {
panic(err) panic(err)
} }
_, err = toml.Decode(string(raw), &Conf) _, err = toml.Decode(string(raw), &Conf)
if err != nil { if err != nil {
panic(err) panic(err)
} }
} }

View File

@ -1,35 +1,35 @@
package database package database
import ( import (
"fmt" "fmt"
"github.com/TicketsBot/GoPanel/config" "github.com/TicketsBot/GoPanel/config"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"github.com/jinzhu/gorm" "github.com/jinzhu/gorm"
) )
var ( var (
Database gorm.DB Database gorm.DB
) )
func ConnectToDatabase() { func ConnectToDatabase() {
uri := fmt.Sprintf( uri := fmt.Sprintf(
"%s:%s@tcp(%s:3306)/%s?charset=utf8mb4&parseTime=True&loc=Local", "%s:%s@tcp(%s:3306)/%s?charset=utf8mb4&parseTime=True&loc=Local",
config.Conf.MariaDB.Username, config.Conf.MariaDB.Username,
config.Conf.MariaDB.Password, config.Conf.MariaDB.Password,
config.Conf.MariaDB.Host, config.Conf.MariaDB.Host,
config.Conf.MariaDB.Database, config.Conf.MariaDB.Database,
) )
db, err := gorm.Open("mysql", uri) db, err := gorm.Open("mysql", uri)
if err != nil { if err != nil {
panic(err) panic(err)
} }
db.DB().SetMaxOpenConns(config.Conf.MariaDB.Threads) db.DB().SetMaxOpenConns(config.Conf.MariaDB.Threads)
db.DB().SetMaxIdleConns(0) db.DB().SetMaxIdleConns(0)
db.Set("gorm:table_options", "charset=utf8mb4") db.Set("gorm:table_options", "charset=utf8mb4")
db.BlockGlobalUpdate(true) db.BlockGlobalUpdate(true)
Database = *db Database = *db
} }

View File

@ -5,7 +5,7 @@ import (
) )
type ArchiveChannel struct { type ArchiveChannel struct {
Guild int64 `gorm:"column:GUILDID"` Guild int64 `gorm:"column:GUILDID"`
Channel int64 `gorm:"column:CHANNELID"` Channel int64 `gorm:"column:CHANNELID"`
} }
@ -24,4 +24,3 @@ func GetArchiveChannel(guildId int64) int64 {
return channel.Channel return channel.Channel
} }

View File

@ -1,37 +1,37 @@
package table package table
import ( import (
"github.com/TicketsBot/GoPanel/database" "github.com/TicketsBot/GoPanel/database"
) )
type BlacklistNode struct { type BlacklistNode struct {
Assoc int `gorm:"column:ASSOCID;type:int;primary_key;auto_increment"` Assoc int `gorm:"column:ASSOCID;type:int;primary_key;auto_increment"`
Guild int64 `gorm:"column:GUILDID"` Guild int64 `gorm:"column:GUILDID"`
User int64 `gorm:"column:USERID"` User int64 `gorm:"column:USERID"`
} }
func (BlacklistNode) TableName() string { func (BlacklistNode) TableName() string {
return "blacklist" return "blacklist"
} }
func IsBlacklisted(guildId, userId int64) bool { func IsBlacklisted(guildId, userId int64) bool {
var count int var count int
database.Database.Table("blacklist").Where(&BlacklistNode{Guild: guildId, User: userId}).Count(&count) database.Database.Table("blacklist").Where(&BlacklistNode{Guild: guildId, User: userId}).Count(&count)
return count > 0 return count > 0
} }
func AddBlacklist(guildId, userId int64) { func AddBlacklist(guildId, userId int64) {
database.Database.Create(&BlacklistNode{Guild: guildId, User: userId}) database.Database.Create(&BlacklistNode{Guild: guildId, User: userId})
} }
func RemoveBlacklist(guildId, userId int64) { func RemoveBlacklist(guildId, userId int64) {
var node BlacklistNode var node BlacklistNode
database.Database.Where(BlacklistNode{Guild: guildId, User: userId}).Take(&node) database.Database.Where(BlacklistNode{Guild: guildId, User: userId}).Take(&node)
database.Database.Delete(&node) database.Database.Delete(&node)
} }
func GetBlacklistNodes(guildId int64) []BlacklistNode { func GetBlacklistNodes(guildId int64) []BlacklistNode {
var nodes []BlacklistNode var nodes []BlacklistNode
database.Database.Where(&BlacklistNode{Guild: guildId}).Find(&nodes) database.Database.Where(&BlacklistNode{Guild: guildId}).Find(&nodes)
return nodes return nodes
} }

View File

@ -1,25 +1,25 @@
package table package table
import ( import (
"github.com/TicketsBot/GoPanel/database" "github.com/TicketsBot/GoPanel/database"
) )
type ChannelCategory struct { type ChannelCategory struct {
GuildId int64 `gorm:"column:GUILDID"` GuildId int64 `gorm:"column:GUILDID"`
Category int64 `gorm:"column:CATEGORYID"` Category int64 `gorm:"column:CATEGORYID"`
} }
func (ChannelCategory) TableName() string { func (ChannelCategory) TableName() string {
return "channelcategory" return "channelcategory"
} }
func UpdateChannelCategory(guildId int64, categoryId int64) { func UpdateChannelCategory(guildId int64, categoryId int64) {
database.Database.Where(&ChannelCategory{GuildId: guildId}).Assign(&ChannelCategory{Category: categoryId}).FirstOrCreate(&ChannelCategory{}) database.Database.Where(&ChannelCategory{GuildId: guildId}).Assign(&ChannelCategory{Category: categoryId}).FirstOrCreate(&ChannelCategory{})
} }
func GetChannelCategory(guildId int64) int64 { func GetChannelCategory(guildId int64) int64 {
var category ChannelCategory var category ChannelCategory
database.Database.Where(&ChannelCategory{GuildId: guildId}).First(&category) database.Database.Where(&ChannelCategory{GuildId: guildId}).First(&category)
return category.Category return category.Category
} }

View File

@ -1,38 +1,38 @@
package table package table
import ( import (
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"github.com/TicketsBot/GoPanel/database" "github.com/TicketsBot/GoPanel/database"
"github.com/TicketsBot/GoPanel/utils/discord/objects" "github.com/TicketsBot/GoPanel/utils/discord/objects"
) )
type GuildCache struct { type GuildCache struct {
UserId string `gorm:"column:USERID;type:varchar(20)"` // Apparently I made this a VARCHAR in the JS version UserId string `gorm:"column:USERID;type:varchar(20)"` // Apparently I made this a VARCHAR in the JS version
Guilds string `gorm:"column:guilds;type:mediumtext"` Guilds string `gorm:"column:guilds;type:mediumtext"`
} }
func (GuildCache) TableName() string { func (GuildCache) TableName() string {
return "guildscache" return "guildscache"
} }
func UpdateGuilds(userId string, guilds string) { func UpdateGuilds(userId string, guilds string) {
var cache GuildCache var cache GuildCache
database.Database.Where(&GuildCache{UserId: userId}).Assign(&GuildCache{Guilds: guilds}).FirstOrCreate(&cache) database.Database.Where(&GuildCache{UserId: userId}).Assign(&GuildCache{Guilds: guilds}).FirstOrCreate(&cache)
} }
func GetGuilds(userId string) []objects.Guild { func GetGuilds(userId string) []objects.Guild {
var cache GuildCache var cache GuildCache
database.Database.Where(&GuildCache{UserId: userId}).First(&cache) database.Database.Where(&GuildCache{UserId: userId}).First(&cache)
decoded, err := base64.StdEncoding.DecodeString(cache.Guilds) decoded, err := base64.StdEncoding.DecodeString(cache.Guilds)
if err != nil { if err != nil {
return make([]objects.Guild, 0) return make([]objects.Guild, 0)
} }
var guilds []objects.Guild var guilds []objects.Guild
if err := json.Unmarshal(decoded, &guilds); err != nil { if err := json.Unmarshal(decoded, &guilds); err != nil {
return make([]objects.Guild, 0) return make([]objects.Guild, 0)
} }
return guilds return guilds
} }

View File

@ -1,44 +1,44 @@
package table package table
import "github.com/TicketsBot/GoPanel/database" import "github.com/TicketsBot/GoPanel/database"
type PermissionNode struct { type PermissionNode struct {
GuildId int64 `gorm:"column:GUILDID"` GuildId int64 `gorm:"column:GUILDID"`
UserId int64 `gorm:"column:USERID"` UserId int64 `gorm:"column:USERID"`
IsSupport bool `gorm:"column:ISSUPPORT"` IsSupport bool `gorm:"column:ISSUPPORT"`
IsAdmin bool `gorm:"column:ISADMIN"` IsAdmin bool `gorm:"column:ISADMIN"`
} }
func (PermissionNode) TableName() string { func (PermissionNode) TableName() string {
return "permissions" return "permissions"
} }
func GetAdminGuilds(userId int64) []int64 { func GetAdminGuilds(userId int64) []int64 {
var nodes []PermissionNode var nodes []PermissionNode
database.Database.Where(&PermissionNode{UserId: userId}).Find(&nodes) database.Database.Where(&PermissionNode{UserId: userId}).Find(&nodes)
ids := make([]int64, 0) ids := make([]int64, 0)
for _, node := range nodes { for _, node := range nodes {
ids = append(ids, node.GuildId) ids = append(ids, node.GuildId)
} }
return ids return ids
} }
func IsSupport(guildId int64, userId int64) bool { func IsSupport(guildId int64, userId int64) bool {
var node PermissionNode var node PermissionNode
database.Database.Where(&PermissionNode{GuildId: guildId, UserId: userId}).Take(&node) database.Database.Where(&PermissionNode{GuildId: guildId, UserId: userId}).Take(&node)
return node.IsSupport return node.IsSupport
} }
func IsAdmin(guildId int64, userId int64) bool { func IsAdmin(guildId int64, userId int64) bool {
var node PermissionNode var node PermissionNode
database.Database.Where(&PermissionNode{GuildId: guildId, UserId: userId}).Take(&node) database.Database.Where(&PermissionNode{GuildId: guildId, UserId: userId}).Take(&node)
return node.IsAdmin return node.IsAdmin
} }
func IsStaff(guildId int64, userId int64) bool { func IsStaff(guildId int64, userId int64) bool {
var node PermissionNode var node PermissionNode
database.Database.Where(&PermissionNode{GuildId: guildId, UserId: userId}).Take(&node) database.Database.Where(&PermissionNode{GuildId: guildId, UserId: userId}).Take(&node)
return node.IsAdmin || node.IsSupport return node.IsAdmin || node.IsSupport
} }

View File

@ -1,37 +1,37 @@
package table package table
import ( import (
"github.com/TicketsBot/GoPanel/database" "github.com/TicketsBot/GoPanel/database"
) )
type PingEveryone struct { type PingEveryone struct {
GuildId int64 `gorm:"column:GUILDID"` GuildId int64 `gorm:"column:GUILDID"`
PingEveryone bool `gorm:"column:PINGEVERYONE;type:TINYINT"` PingEveryone bool `gorm:"column:PINGEVERYONE;type:TINYINT"`
} }
func (PingEveryone) TableName() string { func (PingEveryone) TableName() string {
return "pingeveryone" return "pingeveryone"
} }
// tldr I hate gorm // tldr I hate gorm
func UpdatePingEveryone(guildId int64, pingEveryone bool) { func UpdatePingEveryone(guildId int64, pingEveryone bool) {
var settings []PingEveryone var settings []PingEveryone
database.Database.Where(&PingEveryone{GuildId: guildId}).Find(&settings) database.Database.Where(&PingEveryone{GuildId: guildId}).Find(&settings)
updated := PingEveryone{guildId, pingEveryone} updated := PingEveryone{guildId, pingEveryone}
if len(settings) == 0 { if len(settings) == 0 {
database.Database.Create(&updated) database.Database.Create(&updated)
} else { } else {
database.Database.Table("pingeveryone").Where("GUILDID = ?", guildId).Update("PINGEVERYONE", pingEveryone) database.Database.Table("pingeveryone").Where("GUILDID = ?", guildId).Update("PINGEVERYONE", pingEveryone)
} }
//database.Database.Where(&PingEveryone{GuildId: guildId}).Assign(&updated).FirstOrCreate(&PingEveryone{}) //database.Database.Where(&PingEveryone{GuildId: guildId}).Assign(&updated).FirstOrCreate(&PingEveryone{})
} }
func GetPingEveryone(guildId int64) bool { func GetPingEveryone(guildId int64) bool {
pingEveryone := PingEveryone{PingEveryone: true} pingEveryone := PingEveryone{PingEveryone: true}
database.Database.Where(&PingEveryone{GuildId: guildId}).First(&pingEveryone) database.Database.Where(&PingEveryone{GuildId: guildId}).First(&pingEveryone)
return pingEveryone.PingEveryone return pingEveryone.PingEveryone
} }

View File

@ -1,25 +1,25 @@
package table package table
import ( import (
"github.com/TicketsBot/GoPanel/database" "github.com/TicketsBot/GoPanel/database"
) )
type Prefix struct { type Prefix struct {
GuildId int64 `gorm:"column:GUILDID"` GuildId int64 `gorm:"column:GUILDID"`
Prefix string `gorm:"column:PREFIX;type:varchar(8)"` Prefix string `gorm:"column:PREFIX;type:varchar(8)"`
} }
func (Prefix) TableName() string { func (Prefix) TableName() string {
return "prefix" return "prefix"
} }
func UpdatePrefix(guildId int64, prefix string) { func UpdatePrefix(guildId int64, prefix string) {
database.Database.Where(&Prefix{GuildId: guildId}).Assign(&Prefix{Prefix: prefix}).FirstOrCreate(&Prefix{}) database.Database.Where(&Prefix{GuildId: guildId}).Assign(&Prefix{Prefix: prefix}).FirstOrCreate(&Prefix{})
} }
func GetPrefix(guildId int64) string { func GetPrefix(guildId int64) string {
prefix := Prefix{Prefix: "t!"} prefix := Prefix{Prefix: "t!"}
database.Database.Where(&Prefix{GuildId: guildId}).First(&prefix) database.Database.Where(&Prefix{GuildId: guildId}).First(&prefix)
return prefix.Prefix return prefix.Prefix
} }

View File

@ -1,50 +1,50 @@
package table package table
import ( import (
"github.com/TicketsBot/GoPanel/database" "github.com/TicketsBot/GoPanel/database"
) )
type TicketArchive struct { type TicketArchive struct {
Uuid string `gorm:"column:UUID;type:varchar(36)"` Uuid string `gorm:"column:UUID;type:varchar(36)"`
Guild int64 `gorm:"column:GUILDID"` Guild int64 `gorm:"column:GUILDID"`
User int64 `gorm:"column:USERID"` User int64 `gorm:"column:USERID"`
Username string `gorm:"column:USERNAME;type:varchar(32)"` Username string `gorm:"column:USERNAME;type:varchar(32)"`
TicketId int `gorm:"column:TICKETID"` TicketId int `gorm:"column:TICKETID"`
CdnUrl string `gorm:"column:CDNURL;type:varchar(100)"` CdnUrl string `gorm:"column:CDNURL;type:varchar(100)"`
} }
func (TicketArchive) TableName() string { func (TicketArchive) TableName() string {
return "ticketarchive" return "ticketarchive"
} }
func GetTicketArchives(guildId int64) []TicketArchive { func GetTicketArchives(guildId int64) []TicketArchive {
var archives []TicketArchive var archives []TicketArchive
database.Database.Where(&TicketArchive{Guild: guildId}).Order("TICKETID desc").Find(&archives) database.Database.Where(&TicketArchive{Guild: guildId}).Order("TICKETID desc").Find(&archives)
return archives return archives
} }
func GetFilteredTicketArchives(guildId int64, userId int64, username string, ticketId int) []TicketArchive { func GetFilteredTicketArchives(guildId int64, userId int64, username string, ticketId int) []TicketArchive {
var archives []TicketArchive var archives []TicketArchive
query := database.Database.Where(&TicketArchive{Guild: guildId}) query := database.Database.Where(&TicketArchive{Guild: guildId})
if userId != 0 { if userId != 0 {
query = query.Where(&TicketArchive{User: userId}) query = query.Where(&TicketArchive{User: userId})
} }
if username != "" { if username != "" {
query = query.Where(&TicketArchive{Username: username}) query = query.Where(&TicketArchive{Username: username})
} }
if ticketId != 0 { if ticketId != 0 {
query = query.Where(&TicketArchive{TicketId: ticketId}) query = query.Where(&TicketArchive{TicketId: ticketId})
} }
query.Order("TICKETID desc").Find(&archives) query.Order("TICKETID desc").Find(&archives)
return archives return archives
} }
func GetCdnUrl(guildId int64, uuid string) string { func GetCdnUrl(guildId int64, uuid string) string {
var archive TicketArchive var archive TicketArchive
database.Database.Where(&TicketArchive{Guild: guildId, Uuid: uuid}).First(&archive) database.Database.Where(&TicketArchive{Guild: guildId, Uuid: uuid}).First(&archive)
return archive.CdnUrl return archive.CdnUrl
} }

View File

@ -1,25 +1,25 @@
package table package table
import ( import (
"github.com/TicketsBot/GoPanel/database" "github.com/TicketsBot/GoPanel/database"
) )
type TicketLimit struct { type TicketLimit struct {
GuildId int64 `gorm:"column:GUILDID"` GuildId int64 `gorm:"column:GUILDID"`
Limit int `gorm:"column:TICKETLIMIT"` Limit int `gorm:"column:TICKETLIMIT"`
} }
func (TicketLimit) TableName() string { func (TicketLimit) TableName() string {
return "ticketlimit" return "ticketlimit"
} }
func UpdateTicketLimit(guildId int64, limit int) { func UpdateTicketLimit(guildId int64, limit int) {
database.Database.Where(&TicketLimit{GuildId: guildId}).Assign(&TicketLimit{Limit: limit}).FirstOrCreate(&TicketLimit{}) database.Database.Where(&TicketLimit{GuildId: guildId}).Assign(&TicketLimit{Limit: limit}).FirstOrCreate(&TicketLimit{})
} }
func GetTicketLimit(guildId int64) int { func GetTicketLimit(guildId int64) int {
limit := TicketLimit{Limit: 5} limit := TicketLimit{Limit: 5}
database.Database.Where(&TicketLimit{GuildId: guildId}).First(&limit) database.Database.Where(&TicketLimit{GuildId: guildId}).First(&limit)
return limit.Limit return limit.Limit
} }

View File

@ -1,42 +1,42 @@
package table package table
import "github.com/TicketsBot/GoPanel/database" import "github.com/TicketsBot/GoPanel/database"
type Ticket struct { type Ticket struct {
Uuid string `gorm:"column:UUID;type:varchar(36);primary_key"` Uuid string `gorm:"column:UUID;type:varchar(36);primary_key"`
TicketId int `gorm:"column:ID"` TicketId int `gorm:"column:ID"`
Guild int64 `gorm:"column:GUILDID"` Guild int64 `gorm:"column:GUILDID"`
Channel int64 `gorm:"column:CHANNELID"` Channel int64 `gorm:"column:CHANNELID"`
Owner int64 `gorm:"column:OWNERID"` Owner int64 `gorm:"column:OWNERID"`
Members string `gorm:"column:MEMBERS;type:text"` Members string `gorm:"column:MEMBERS;type:text"`
IsOpen bool `gorm:"column:OPEN"` IsOpen bool `gorm:"column:OPEN"`
OpenTime int64 `gorm:"column:OPENTIME"` OpenTime int64 `gorm:"column:OPENTIME"`
} }
func (Ticket) TableName() string { func (Ticket) TableName() string {
return "tickets" return "tickets"
} }
func GetTickets(guild int64) []Ticket { func GetTickets(guild int64) []Ticket {
var tickets []Ticket var tickets []Ticket
database.Database.Where(&Ticket{Guild: guild}).Order("ID asc").Find(&tickets) database.Database.Where(&Ticket{Guild: guild}).Order("ID asc").Find(&tickets)
return tickets return tickets
} }
func GetOpenTickets(guild int64) []Ticket { func GetOpenTickets(guild int64) []Ticket {
var tickets []Ticket var tickets []Ticket
database.Database.Where(&Ticket{Guild: guild, IsOpen: true}).Order("ID asc").Find(&tickets) database.Database.Where(&Ticket{Guild: guild, IsOpen: true}).Order("ID asc").Find(&tickets)
return tickets return tickets
} }
func GetTicket(uuid string, ch chan Ticket) { func GetTicket(uuid string, ch chan Ticket) {
var ticket Ticket var ticket Ticket
database.Database.Where(&Ticket{Uuid: uuid}).First(&ticket) database.Database.Where(&Ticket{Uuid: uuid}).First(&ticket)
ch <- ticket ch <- ticket
} }
func GetTicketById(guild int64, id int, ch chan Ticket) { func GetTicketById(guild int64, id int, ch chan Ticket) {
var ticket Ticket var ticket Ticket
database.Database.Where(&Ticket{Guild: guild, TicketId: id}).First(&ticket) database.Database.Where(&Ticket{Guild: guild, TicketId: id}).First(&ticket)
ch <- ticket ch <- ticket
} }

View File

@ -1,34 +1,34 @@
package table package table
import ( import (
"github.com/TicketsBot/GoPanel/database" "github.com/TicketsBot/GoPanel/database"
) )
type UsernameNode struct { type UsernameNode struct {
Id int64 `gorm:"column:USERID;primary_key"` Id int64 `gorm:"column:USERID;primary_key"`
Name string `gorm:"column:USERNAME;type:text"` // Base 64 encoded Name string `gorm:"column:USERNAME;type:text"` // Base 64 encoded
Discriminator string `gorm:"column:DISCRIM;type:varchar(4)"` Discriminator string `gorm:"column:DISCRIM;type:varchar(4)"`
Avatar string `gorm:"column:AVATARHASH;type:varchar(100)"` Avatar string `gorm:"column:AVATARHASH;type:varchar(100)"`
} }
func (UsernameNode) TableName() string { func (UsernameNode) TableName() string {
return "usernames" return "usernames"
} }
func GetUsername(id int64, ch chan string) { func GetUsername(id int64, ch chan string) {
node := UsernameNode{Name: "Unknown"} node := UsernameNode{Name: "Unknown"}
database.Database.Where(&UsernameNode{Id: id}).First(&node) database.Database.Where(&UsernameNode{Id: id}).First(&node)
ch <- node.Name ch <- node.Name
} }
func GetUserNodes(ids []int64) []UsernameNode { func GetUserNodes(ids []int64) []UsernameNode {
var nodes []UsernameNode var nodes []UsernameNode
database.Database.Where(ids).Find(&nodes) database.Database.Where(ids).Find(&nodes)
return nodes return nodes
} }
func GetUserId(name, discrim string) int64 { func GetUserId(name, discrim string) int64 {
var node UsernameNode var node UsernameNode
database.Database.Where(&UsernameNode{Name: name, Discriminator: discrim}).First(&node) database.Database.Where(&UsernameNode{Name: name, Discriminator: discrim}).First(&node)
return node.Id return node.Id
} }

View File

@ -1,27 +1,27 @@
package table package table
import "github.com/TicketsBot/GoPanel/database" import "github.com/TicketsBot/GoPanel/database"
type UserCanClose struct { type UserCanClose struct {
Guild int64 `gorm:"column:GUILDID;unique;primary_key"` Guild int64 `gorm:"column:GUILDID;unique;primary_key"`
CanClose *bool `gorm:"column:CANCLOSE"` CanClose *bool `gorm:"column:CANCLOSE"`
} }
func (UserCanClose) TableName() string { func (UserCanClose) TableName() string {
return "usercanclose" return "usercanclose"
} }
func IsUserCanClose(guild int64, ch chan bool) { func IsUserCanClose(guild int64, ch chan bool) {
var node UserCanClose var node UserCanClose
database.Database.Where(UserCanClose{Guild: guild}).First(&node) database.Database.Where(UserCanClose{Guild: guild}).First(&node)
if node.CanClose == nil { if node.CanClose == nil {
ch <- true ch <- true
} else { } else {
ch <- *node.CanClose ch <- *node.CanClose
} }
} }
func SetUserCanClose(guild int64, value bool) { func SetUserCanClose(guild int64, value bool) {
database.Database.Where(&UserCanClose{Guild: guild}).Assign(&UserCanClose{CanClose: &value}).FirstOrCreate(&UserCanClose{}) database.Database.Where(&UserCanClose{Guild: guild}).Assign(&UserCanClose{CanClose: &value}).FirstOrCreate(&UserCanClose{})
} }

View File

@ -1,22 +1,22 @@
package table package table
import ( import (
"github.com/TicketsBot/GoPanel/database" "github.com/TicketsBot/GoPanel/database"
"time" "time"
) )
type Votes struct { type Votes struct {
Id int64 `gorm:"type:bigint;unique_index;primary_key"` Id int64 `gorm:"type:bigint;unique_index;primary_key"`
VoteTime time.Time VoteTime time.Time
} }
func (Votes) TableName() string { func (Votes) TableName() string {
return "votes" return "votes"
} }
func HasVoted(owner int64, ch chan bool) { func HasVoted(owner int64, ch chan bool) {
var node Votes var node Votes
database.Database.Where(Votes{Id: owner}).First(&node) database.Database.Where(Votes{Id: owner}).First(&node)
ch <- time.Now().Sub(node.VoteTime) < 24 * time.Hour ch <- time.Now().Sub(node.VoteTime) < 24 * time.Hour
} }

View File

@ -1,25 +1,25 @@
package table package table
import ( import (
"github.com/TicketsBot/GoPanel/database" "github.com/TicketsBot/GoPanel/database"
) )
type WelcomeMessage struct { type WelcomeMessage struct {
GuildId int64 `gorm:"column:GUILDID"` GuildId int64 `gorm:"column:GUILDID"`
Message string `gorm:"column:MESSAGE;type:text"` Message string `gorm:"column:MESSAGE;type:text"`
} }
func (WelcomeMessage) TableName() string { func (WelcomeMessage) TableName() string {
return "welcomemessages" return "welcomemessages"
} }
func UpdateWelcomeMessage(guildId int64, message string) { func UpdateWelcomeMessage(guildId int64, message string) {
database.Database.Where(&WelcomeMessage{GuildId: guildId}).Assign(&WelcomeMessage{Message: message}).FirstOrCreate(&WelcomeMessage{}) database.Database.Where(&WelcomeMessage{GuildId: guildId}).Assign(&WelcomeMessage{Message: message}).FirstOrCreate(&WelcomeMessage{})
} }
func GetWelcomeMessage(guildId int64) string { func GetWelcomeMessage(guildId int64) string {
message := WelcomeMessage{Message: "No message specified"} message := WelcomeMessage{Message: "No message specified"}
database.Database.Where(&WelcomeMessage{GuildId: guildId}).First(&message) database.Database.Where(&WelcomeMessage{GuildId: guildId}).First(&message)
return message.Message return message.Message
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,69 +1,69 @@
@font-face{font-family:Whitney;font-style:light;font-weight:300;src:url('https://discordapp.com/assets/6c6374bad0b0b6d204d8d6dc4a18d820.woff') format('woff')} @font-face{font-family:Whitney;font-style:light;font-weight:300;src:url('https://discordapp.com/assets/6c6374bad0b0b6d204d8d6dc4a18d820.woff') format('woff')}
@font-face{font-family:Whitney;font-style:normal;font-weight:500;src:url('https://discordapp.com/assets/e8acd7d9bf6207f99350ca9f9e23b168.woff') format('woff')} @font-face{font-family:Whitney;font-style:normal;font-weight:500;src:url('https://discordapp.com/assets/e8acd7d9bf6207f99350ca9f9e23b168.woff') format('woff')}
@font-face{font-family:Whitney;font-style:medium;font-weight:600;src:url('https://discordapp.com/assets/3bdef1251a424500c1b3a78dea9b7e57.woff') format('woff')} @font-face{font-family:Whitney;font-style:medium;font-weight:600;src:url('https://discordapp.com/assets/3bdef1251a424500c1b3a78dea9b7e57.woff') format('woff')}
@font-face{font-family:WhitneyMedium;font-style:medium;font-weight:600;src:url('https://discordapp.com/assets/be0060dafb7a0e31d2a1ca17c0708636.woff') format('woff')} @font-face{font-family:WhitneyMedium;font-style:medium;font-weight:600;src:url('https://discordapp.com/assets/be0060dafb7a0e31d2a1ca17c0708636.woff') format('woff')}
@font-face{font-family:Whitney;font-style:bold;font-weight:700;src:url('https://discordapp.com/assets/8e12fb4f14d9c4592eb8ec9f22337b04.woff') format('woff')} @font-face{font-family:Whitney;font-style:bold;font-weight:700;src:url('https://discordapp.com/assets/8e12fb4f14d9c4592eb8ec9f22337b04.woff') format('woff')}
.discord-container { .discord-container {
background-color: #2e3136; background-color: #2e3136;
border-radius: 4px; border-radius: 4px;
height: 80vh; height: 80vh;
max-height: 100vh; max-height: 100vh;
margin: 0; margin: 0;
padding: 0; padding: 0;
font-family: 'Whitney', sans-serif !important; font-family: 'Whitney', sans-serif !important;
} }
.channel-header { .channel-header {
background-color: #1e2124; background-color: #1e2124;
height: 5vh; height: 5vh;
width: 100%; width: 100%;
border-radius: 4px 4px 0 0; border-radius: 4px 4px 0 0;
position: relative; position: relative;
text-align: center; text-align: center;
display: flex; display: flex;
align-items: center; align-items: center;
} }
.channel-name { .channel-name {
color: white; color: white;
padding-left: 20px; padding-left: 20px;
} }
#message-container { #message-container {
height: 70vh; height: 70vh;
max-height: 70vh; max-height: 70vh;
position: relative; position: relative;
overflow: scroll; overflow: scroll;
overflow-x: hidden; overflow-x: hidden;
} }
.message { .message {
color: white !important; color: white !important;
padding-left: 20px; padding-left: 20px;
} }
.message-input { .message-input {
padding-top: 20px !important; padding-top: 20px !important;
border-color: #2e3136 !important; border-color: #2e3136 !important;
padding-left: 5px !important; padding-left: 5px !important;
padding-right: 5px !important; padding-right: 5px !important;
background-color: #2e3136 !important; background-color: #2e3136 !important;
color: white !important; color: white !important;
height: 100%; height: 100%;
max-height: 100%; max-height: 100%;
min-height: 100%; min-height: 100%;
} }
.form-control:focus { .form-control:focus {
border-color: #2e3136 !important; border-color: #2e3136 !important;
background-color: #2e3136 !important; background-color: #2e3136 !important;
color: white !important; color: white !important;
} }
.message-input:focus { .message-input:focus {
border-color: #2e3136 !important; border-color: #2e3136 !important;
background-color: #2e3136 !important; background-color: #2e3136 !important;
color: white !important; color: white !important;
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,39 +1,39 @@
body { body {
font-family: 'Open Sans',sans-serif !important; font-family: 'Open Sans',sans-serif !important;
font-size: 1rem; font-size: 1rem;
font-weight: 400; font-weight: 400;
line-height: 1.5; line-height: 1.5;
} }
.sidebar { .sidebar {
background: url("/assets/img/sidebar-2.jpg"); background: url("/assets/img/sidebar-2.jpg");
background-size: cover; background-size: cover;
overflow-x: hidden !important; overflow-x: hidden !important;
} }
.sidebar-bottom { .sidebar-bottom {
position: absolute !important; position: absolute !important;
width: 100%; width: 100%;
} }
.nav-link { .nav-link {
color: white !important; color: white !important;
} }
.filterCard { .filterCard {
cursor: pointer; cursor: pointer;
} }
.table td, .table th { .table td, .table th {
text-align: center; text-align: center;
} }
.close-container { .close-container {
text-align: right; text-align: right;
padding-bottom: 10px; padding-bottom: 10px;
} }
.input-fill { .input-fill {
margin: 0 !important; margin: 0 !important;
padding: 0 !important; padding: 0 !important;
} }

View File

@ -1,14 +1,14 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32" fill="white"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32" fill="white">
<circle transform="translate(8 0)" cx="0" cy="16" r="0"> <circle transform="translate(8 0)" cx="0" cy="16" r="0">
<animate attributeName="r" values="0; 4; 0; 0" dur="1.2s" repeatCount="indefinite" begin="0" <animate attributeName="r" values="0; 4; 0; 0" dur="1.2s" repeatCount="indefinite" begin="0"
keytimes="0;0.2;0.7;1" keySplines="0.2 0.2 0.4 0.8;0.2 0.6 0.4 0.8;0.2 0.6 0.4 0.8" calcMode="spline" /> keytimes="0;0.2;0.7;1" keySplines="0.2 0.2 0.4 0.8;0.2 0.6 0.4 0.8;0.2 0.6 0.4 0.8" calcMode="spline" />
</circle> </circle>
<circle transform="translate(16 0)" cx="0" cy="16" r="0"> <circle transform="translate(16 0)" cx="0" cy="16" r="0">
<animate attributeName="r" values="0; 4; 0; 0" dur="1.2s" repeatCount="indefinite" begin="0.3" <animate attributeName="r" values="0; 4; 0; 0" dur="1.2s" repeatCount="indefinite" begin="0.3"
keytimes="0;0.2;0.7;1" keySplines="0.2 0.2 0.4 0.8;0.2 0.6 0.4 0.8;0.2 0.6 0.4 0.8" calcMode="spline" /> keytimes="0;0.2;0.7;1" keySplines="0.2 0.2 0.4 0.8;0.2 0.6 0.4 0.8;0.2 0.6 0.4 0.8" calcMode="spline" />
</circle> </circle>
<circle transform="translate(24 0)" cx="0" cy="16" r="0"> <circle transform="translate(24 0)" cx="0" cy="16" r="0">
<animate attributeName="r" values="0; 4; 0; 0" dur="1.2s" repeatCount="indefinite" begin="0.6" <animate attributeName="r" values="0; 4; 0; 0" dur="1.2s" repeatCount="indefinite" begin="0.6"
keytimes="0;0.2;0.7;1" keySplines="0.2 0.2 0.4 0.8;0.2 0.6 0.4 0.8;0.2 0.6 0.4 0.8" calcMode="spline" /> keytimes="0;0.2;0.7;1" keySplines="0.2 0.2 0.4 0.8;0.2 0.6 0.4 0.8;0.2 0.6 0.4 0.8" calcMode="spline" />
</circle> </circle>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 950 B

After

Width:  |  Height:  |  Size: 964 B

View File

@ -1,404 +1,404 @@
/* /*
Creative Tim Modifications Creative Tim Modifications
Lines: 239, 240 was changed from top: 5px to top: 50% and we added margin-top: -13px. In this way the close button will be aligned vertically Lines: 239, 240 was changed from top: 5px to top: 50% and we added margin-top: -13px. In this way the close button will be aligned vertically
Line:242 - modified when the icon is set, we add the class "alert-with-icon", so there will be enough space for the icon. Line:242 - modified when the icon is set, we add the class "alert-with-icon", so there will be enough space for the icon.
*/ */
/* /*
* Project: Bootstrap Notify = v3.1.5 * Project: Bootstrap Notify = v3.1.5
* Description: Turns standard Bootstrap alerts into "Growl-like" notifications. * Description: Turns standard Bootstrap alerts into "Growl-like" notifications.
* Author: Mouse0270 aka Robert McIntosh * Author: Mouse0270 aka Robert McIntosh
* License: MIT License * License: MIT License
* Website: https://github.com/mouse0270/bootstrap-growl * Website: https://github.com/mouse0270/bootstrap-growl
*/ */
/* global define:false, require: false, jQuery:false */ /* global define:false, require: false, jQuery:false */
(function (factory) { (function (factory) {
if (typeof define === 'function' && define.amd) { if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module. // AMD. Register as an anonymous module.
define(['jquery'], factory); define(['jquery'], factory);
} else if (typeof exports === 'object') { } else if (typeof exports === 'object') {
// Node/CommonJS // Node/CommonJS
factory(require('jquery')); factory(require('jquery'));
} else { } else {
// Browser globals // Browser globals
factory(jQuery); factory(jQuery);
} }
}(function ($) { }(function ($) {
// Create the defaults once // Create the defaults once
var defaults = { var defaults = {
element: 'body', element: 'body',
position: null, position: null,
type: "info", type: "info",
allow_dismiss: true, allow_dismiss: true,
allow_duplicates: true, allow_duplicates: true,
newest_on_top: false, newest_on_top: false,
showProgressbar: false, showProgressbar: false,
placement: { placement: {
from: "top", from: "top",
align: "right" align: "right"
}, },
offset: 20, offset: 20,
spacing: 10, spacing: 10,
z_index: 1031, z_index: 1031,
delay: 5000, delay: 5000,
timer: 1000, timer: 1000,
url_target: '_blank', url_target: '_blank',
mouse_over: null, mouse_over: null,
animate: { animate: {
enter: 'animated fadeInDown', enter: 'animated fadeInDown',
exit: 'animated fadeOutUp' exit: 'animated fadeOutUp'
}, },
onShow: null, onShow: null,
onShown: null, onShown: null,
onClose: null, onClose: null,
onClosed: null, onClosed: null,
icon_type: 'class', icon_type: 'class',
template: '<div data-notify="container" class="col-xs-11 col-sm-4 alert alert-{0}" role="alert"><button type="button" aria-hidden="true" class="close" data-notify="dismiss">&times;</button><span data-notify="icon"></span> <span data-notify="title">{1}</span> <span data-notify="message">{2}</span><div class="progress" data-notify="progressbar"><div class="progress-bar progress-bar-{0}" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%;"></div></div><a href="{3}" target="{4}" data-notify="url"></a></div>' template: '<div data-notify="container" class="col-xs-11 col-sm-4 alert alert-{0}" role="alert"><button type="button" aria-hidden="true" class="close" data-notify="dismiss">&times;</button><span data-notify="icon"></span> <span data-notify="title">{1}</span> <span data-notify="message">{2}</span><div class="progress" data-notify="progressbar"><div class="progress-bar progress-bar-{0}" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%;"></div></div><a href="{3}" target="{4}" data-notify="url"></a></div>'
}; };
String.format = function () { String.format = function () {
var str = arguments[0]; var str = arguments[0];
for (var i = 1; i < arguments.length; i++) { for (var i = 1; i < arguments.length; i++) {
str = str.replace(RegExp("\\{" + (i - 1) + "\\}", "gm"), arguments[i]); str = str.replace(RegExp("\\{" + (i - 1) + "\\}", "gm"), arguments[i]);
} }
return str; return str;
}; };
function isDuplicateNotification(notification) { function isDuplicateNotification(notification) {
var isDupe = false; var isDupe = false;
$('[data-notify="container"]').each(function (i, el) { $('[data-notify="container"]').each(function (i, el) {
var $el = $(el); var $el = $(el);
var title = $el.find('[data-notify="title"]').text().trim(); var title = $el.find('[data-notify="title"]').text().trim();
var message = $el.find('[data-notify="message"]').html().trim(); var message = $el.find('[data-notify="message"]').html().trim();
// The input string might be different than the actual parsed HTML string! // The input string might be different than the actual parsed HTML string!
// (<br> vs <br /> for example) // (<br> vs <br /> for example)
// So we have to force-parse this as HTML here! // So we have to force-parse this as HTML here!
var isSameTitle = title === $("<div>" + notification.settings.content.title + "</div>").html().trim(); var isSameTitle = title === $("<div>" + notification.settings.content.title + "</div>").html().trim();
var isSameMsg = message === $("<div>" + notification.settings.content.message + "</div>").html().trim(); var isSameMsg = message === $("<div>" + notification.settings.content.message + "</div>").html().trim();
var isSameType = $el.hasClass('alert-' + notification.settings.type); var isSameType = $el.hasClass('alert-' + notification.settings.type);
if (isSameTitle && isSameMsg && isSameType) { if (isSameTitle && isSameMsg && isSameType) {
//we found the dupe. Set the var and stop checking. //we found the dupe. Set the var and stop checking.
isDupe = true; isDupe = true;
} }
return !isDupe; return !isDupe;
}); });
return isDupe; return isDupe;
} }
function Notify(element, content, options) { function Notify(element, content, options) {
// Setup Content of Notify // Setup Content of Notify
var contentObj = { var contentObj = {
content: { content: {
message: typeof content === 'object' ? content.message : content, message: typeof content === 'object' ? content.message : content,
title: content.title ? content.title : '', title: content.title ? content.title : '',
icon: content.icon ? content.icon : '', icon: content.icon ? content.icon : '',
url: content.url ? content.url : '#', url: content.url ? content.url : '#',
target: content.target ? content.target : '-' target: content.target ? content.target : '-'
} }
}; };
options = $.extend(true, {}, contentObj, options); options = $.extend(true, {}, contentObj, options);
this.settings = $.extend(true, {}, defaults, options); this.settings = $.extend(true, {}, defaults, options);
this._defaults = defaults; this._defaults = defaults;
if (this.settings.content.target === "-") { if (this.settings.content.target === "-") {
this.settings.content.target = this.settings.url_target; this.settings.content.target = this.settings.url_target;
} }
this.animations = { this.animations = {
start: 'webkitAnimationStart oanimationstart MSAnimationStart animationstart', start: 'webkitAnimationStart oanimationstart MSAnimationStart animationstart',
end: 'webkitAnimationEnd oanimationend MSAnimationEnd animationend' end: 'webkitAnimationEnd oanimationend MSAnimationEnd animationend'
}; };
if (typeof this.settings.offset === 'number') { if (typeof this.settings.offset === 'number') {
this.settings.offset = { this.settings.offset = {
x: this.settings.offset, x: this.settings.offset,
y: this.settings.offset y: this.settings.offset
}; };
} }
//if duplicate messages are not allowed, then only continue if this new message is not a duplicate of one that it already showing //if duplicate messages are not allowed, then only continue if this new message is not a duplicate of one that it already showing
if (this.settings.allow_duplicates || (!this.settings.allow_duplicates && !isDuplicateNotification(this))) { if (this.settings.allow_duplicates || (!this.settings.allow_duplicates && !isDuplicateNotification(this))) {
this.init(); this.init();
} }
} }
$.extend(Notify.prototype, { $.extend(Notify.prototype, {
init: function () { init: function () {
var self = this; var self = this;
this.buildNotify(); this.buildNotify();
if (this.settings.content.icon) { if (this.settings.content.icon) {
this.setIcon(); this.setIcon();
} }
if (this.settings.content.url != "#") { if (this.settings.content.url != "#") {
this.styleURL(); this.styleURL();
} }
this.styleDismiss(); this.styleDismiss();
this.placement(); this.placement();
this.bind(); this.bind();
this.notify = { this.notify = {
$ele: this.$ele, $ele: this.$ele,
update: function (command, update) { update: function (command, update) {
var commands = {}; var commands = {};
if (typeof command === "string") { if (typeof command === "string") {
commands[command] = update; commands[command] = update;
} else { } else {
commands = command; commands = command;
} }
for (var cmd in commands) { for (var cmd in commands) {
switch (cmd) { switch (cmd) {
case "type": case "type":
this.$ele.removeClass('alert-' + self.settings.type); this.$ele.removeClass('alert-' + self.settings.type);
this.$ele.find('[data-notify="progressbar"] > .progress-bar').removeClass('progress-bar-' + self.settings.type); this.$ele.find('[data-notify="progressbar"] > .progress-bar').removeClass('progress-bar-' + self.settings.type);
self.settings.type = commands[cmd]; self.settings.type = commands[cmd];
this.$ele.addClass('alert-' + commands[cmd]).find('[data-notify="progressbar"] > .progress-bar').addClass('progress-bar-' + commands[cmd]); this.$ele.addClass('alert-' + commands[cmd]).find('[data-notify="progressbar"] > .progress-bar').addClass('progress-bar-' + commands[cmd]);
break; break;
case "icon": case "icon":
var $icon = this.$ele.find('[data-notify="icon"]'); var $icon = this.$ele.find('[data-notify="icon"]');
if (self.settings.icon_type.toLowerCase() === 'class') { if (self.settings.icon_type.toLowerCase() === 'class') {
$icon.removeClass(self.settings.content.icon).addClass(commands[cmd]); $icon.removeClass(self.settings.content.icon).addClass(commands[cmd]);
} else { } else {
if (!$icon.is('img')) { if (!$icon.is('img')) {
$icon.find('img'); $icon.find('img');
} }
$icon.attr('src', commands[cmd]); $icon.attr('src', commands[cmd]);
} }
break; break;
case "progress": case "progress":
var newDelay = self.settings.delay - (self.settings.delay * (commands[cmd] / 100)); var newDelay = self.settings.delay - (self.settings.delay * (commands[cmd] / 100));
this.$ele.data('notify-delay', newDelay); this.$ele.data('notify-delay', newDelay);
this.$ele.find('[data-notify="progressbar"] > div').attr('aria-valuenow', commands[cmd]).css('width', commands[cmd] + '%'); this.$ele.find('[data-notify="progressbar"] > div').attr('aria-valuenow', commands[cmd]).css('width', commands[cmd] + '%');
break; break;
case "url": case "url":
this.$ele.find('[data-notify="url"]').attr('href', commands[cmd]); this.$ele.find('[data-notify="url"]').attr('href', commands[cmd]);
break; break;
case "target": case "target":
this.$ele.find('[data-notify="url"]').attr('target', commands[cmd]); this.$ele.find('[data-notify="url"]').attr('target', commands[cmd]);
break; break;
default: default:
this.$ele.find('[data-notify="' + cmd + '"]').html(commands[cmd]); this.$ele.find('[data-notify="' + cmd + '"]').html(commands[cmd]);
} }
} }
var posX = this.$ele.outerHeight() + parseInt(self.settings.spacing) + parseInt(self.settings.offset.y); var posX = this.$ele.outerHeight() + parseInt(self.settings.spacing) + parseInt(self.settings.offset.y);
self.reposition(posX); self.reposition(posX);
}, },
close: function () { close: function () {
self.close(); self.close();
} }
}; };
}, },
buildNotify: function () { buildNotify: function () {
var content = this.settings.content; var content = this.settings.content;
this.$ele = $(String.format(this.settings.template, this.settings.type, content.title, content.message, content.url, content.target)); this.$ele = $(String.format(this.settings.template, this.settings.type, content.title, content.message, content.url, content.target));
this.$ele.attr('data-notify-position', this.settings.placement.from + '-' + this.settings.placement.align); this.$ele.attr('data-notify-position', this.settings.placement.from + '-' + this.settings.placement.align);
if (!this.settings.allow_dismiss) { if (!this.settings.allow_dismiss) {
this.$ele.find('[data-notify="dismiss"]').css('display', 'none'); this.$ele.find('[data-notify="dismiss"]').css('display', 'none');
} }
if ((this.settings.delay <= 0 && !this.settings.showProgressbar) || !this.settings.showProgressbar) { if ((this.settings.delay <= 0 && !this.settings.showProgressbar) || !this.settings.showProgressbar) {
this.$ele.find('[data-notify="progressbar"]').remove(); this.$ele.find('[data-notify="progressbar"]').remove();
} }
}, },
setIcon: function () { setIcon: function () {
this.$ele.addClass('alert-with-icon'); this.$ele.addClass('alert-with-icon');
if (this.settings.icon_type.toLowerCase() === 'class') { if (this.settings.icon_type.toLowerCase() === 'class') {
this.$ele.find('[data-notify="icon"]').addClass(this.settings.content.icon); this.$ele.find('[data-notify="icon"]').addClass(this.settings.content.icon);
} else { } else {
if (this.$ele.find('[data-notify="icon"]').is('img')) { if (this.$ele.find('[data-notify="icon"]').is('img')) {
this.$ele.find('[data-notify="icon"]').attr('src', this.settings.content.icon); this.$ele.find('[data-notify="icon"]').attr('src', this.settings.content.icon);
} else { } else {
this.$ele.find('[data-notify="icon"]').append('<img src="' + this.settings.content.icon + '" alt="Notify Icon" />'); this.$ele.find('[data-notify="icon"]').append('<img src="' + this.settings.content.icon + '" alt="Notify Icon" />');
} }
} }
}, },
styleDismiss: function () { styleDismiss: function () {
this.$ele.find('[data-notify="dismiss"]').css({ this.$ele.find('[data-notify="dismiss"]').css({
position: 'absolute', position: 'absolute',
right: '10px', right: '10px',
top: '50%', top: '50%',
marginTop: '-13px', marginTop: '-13px',
zIndex: this.settings.z_index + 2 zIndex: this.settings.z_index + 2
}); });
}, },
styleURL: function () { styleURL: function () {
this.$ele.find('[data-notify="url"]').css({ this.$ele.find('[data-notify="url"]').css({
backgroundImage: 'url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)', backgroundImage: 'url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)',
height: '100%', height: '100%',
left: 0, left: 0,
position: 'absolute', position: 'absolute',
top: 0, top: 0,
width: '100%', width: '100%',
zIndex: this.settings.z_index + 1 zIndex: this.settings.z_index + 1
}); });
}, },
placement: function () { placement: function () {
var self = this, var self = this,
offsetAmt = this.settings.offset.y, offsetAmt = this.settings.offset.y,
css = { css = {
display: 'inline-block', display: 'inline-block',
margin: '0px auto', margin: '0px auto',
position: this.settings.position ? this.settings.position : (this.settings.element === 'body' ? 'fixed' : 'absolute'), position: this.settings.position ? this.settings.position : (this.settings.element === 'body' ? 'fixed' : 'absolute'),
transition: 'all .5s ease-in-out', transition: 'all .5s ease-in-out',
zIndex: this.settings.z_index zIndex: this.settings.z_index
}, },
hasAnimation = false, hasAnimation = false,
settings = this.settings; settings = this.settings;
$('[data-notify-position="' + this.settings.placement.from + '-' + this.settings.placement.align + '"]:not([data-closing="true"])').each(function () { $('[data-notify-position="' + this.settings.placement.from + '-' + this.settings.placement.align + '"]:not([data-closing="true"])').each(function () {
offsetAmt = Math.max(offsetAmt, parseInt($(this).css(settings.placement.from)) + parseInt($(this).outerHeight()) + parseInt(settings.spacing)); offsetAmt = Math.max(offsetAmt, parseInt($(this).css(settings.placement.from)) + parseInt($(this).outerHeight()) + parseInt(settings.spacing));
}); });
if (this.settings.newest_on_top === true) { if (this.settings.newest_on_top === true) {
offsetAmt = this.settings.offset.y; offsetAmt = this.settings.offset.y;
} }
css[this.settings.placement.from] = offsetAmt + 'px'; css[this.settings.placement.from] = offsetAmt + 'px';
switch (this.settings.placement.align) { switch (this.settings.placement.align) {
case "left": case "left":
case "right": case "right":
css[this.settings.placement.align] = this.settings.offset.x + 'px'; css[this.settings.placement.align] = this.settings.offset.x + 'px';
break; break;
case "center": case "center":
css.left = 0; css.left = 0;
css.right = 0; css.right = 0;
break; break;
} }
this.$ele.css(css).addClass(this.settings.animate.enter); this.$ele.css(css).addClass(this.settings.animate.enter);
$.each(Array('webkit-', 'moz-', 'o-', 'ms-', ''), function (index, prefix) { $.each(Array('webkit-', 'moz-', 'o-', 'ms-', ''), function (index, prefix) {
self.$ele[0].style[prefix + 'AnimationIterationCount'] = 1; self.$ele[0].style[prefix + 'AnimationIterationCount'] = 1;
}); });
$(this.settings.element).append(this.$ele); $(this.settings.element).append(this.$ele);
if (this.settings.newest_on_top === true) { if (this.settings.newest_on_top === true) {
offsetAmt = (parseInt(offsetAmt) + parseInt(this.settings.spacing)) + this.$ele.outerHeight(); offsetAmt = (parseInt(offsetAmt) + parseInt(this.settings.spacing)) + this.$ele.outerHeight();
this.reposition(offsetAmt); this.reposition(offsetAmt);
} }
if ($.isFunction(self.settings.onShow)) { if ($.isFunction(self.settings.onShow)) {
self.settings.onShow.call(this.$ele); self.settings.onShow.call(this.$ele);
} }
this.$ele.one(this.animations.start, function () { this.$ele.one(this.animations.start, function () {
hasAnimation = true; hasAnimation = true;
}).one(this.animations.end, function () { }).one(this.animations.end, function () {
if ($.isFunction(self.settings.onShown)) { if ($.isFunction(self.settings.onShown)) {
self.settings.onShown.call(this); self.settings.onShown.call(this);
} }
}); });
setTimeout(function () { setTimeout(function () {
if (!hasAnimation) { if (!hasAnimation) {
if ($.isFunction(self.settings.onShown)) { if ($.isFunction(self.settings.onShown)) {
self.settings.onShown.call(this); self.settings.onShown.call(this);
} }
} }
}, 600); }, 600);
}, },
bind: function () { bind: function () {
var self = this; var self = this;
this.$ele.find('[data-notify="dismiss"]').on('click', function () { this.$ele.find('[data-notify="dismiss"]').on('click', function () {
self.close(); self.close();
}); });
this.$ele.mouseover(function () { this.$ele.mouseover(function () {
$(this).data('data-hover', "true"); $(this).data('data-hover', "true");
}).mouseout(function () { }).mouseout(function () {
$(this).data('data-hover', "false"); $(this).data('data-hover', "false");
}); });
this.$ele.data('data-hover', "false"); this.$ele.data('data-hover', "false");
if (this.settings.delay > 0) { if (this.settings.delay > 0) {
self.$ele.data('notify-delay', self.settings.delay); self.$ele.data('notify-delay', self.settings.delay);
var timer = setInterval(function () { var timer = setInterval(function () {
var delay = parseInt(self.$ele.data('notify-delay')) - self.settings.timer; var delay = parseInt(self.$ele.data('notify-delay')) - self.settings.timer;
if ((self.$ele.data('data-hover') === 'false' && self.settings.mouse_over === "pause") || self.settings.mouse_over != "pause") { if ((self.$ele.data('data-hover') === 'false' && self.settings.mouse_over === "pause") || self.settings.mouse_over != "pause") {
var percent = ((self.settings.delay - delay) / self.settings.delay) * 100; var percent = ((self.settings.delay - delay) / self.settings.delay) * 100;
self.$ele.data('notify-delay', delay); self.$ele.data('notify-delay', delay);
self.$ele.find('[data-notify="progressbar"] > div').attr('aria-valuenow', percent).css('width', percent + '%'); self.$ele.find('[data-notify="progressbar"] > div').attr('aria-valuenow', percent).css('width', percent + '%');
} }
if (delay <= -(self.settings.timer)) { if (delay <= -(self.settings.timer)) {
clearInterval(timer); clearInterval(timer);
self.close(); self.close();
} }
}, self.settings.timer); }, self.settings.timer);
} }
}, },
close: function () { close: function () {
var self = this, var self = this,
posX = parseInt(this.$ele.css(this.settings.placement.from)), posX = parseInt(this.$ele.css(this.settings.placement.from)),
hasAnimation = false; hasAnimation = false;
this.$ele.data('closing', 'true').addClass(this.settings.animate.exit); this.$ele.data('closing', 'true').addClass(this.settings.animate.exit);
self.reposition(posX); self.reposition(posX);
if ($.isFunction(self.settings.onClose)) { if ($.isFunction(self.settings.onClose)) {
self.settings.onClose.call(this.$ele); self.settings.onClose.call(this.$ele);
} }
this.$ele.one(this.animations.start, function () { this.$ele.one(this.animations.start, function () {
hasAnimation = true; hasAnimation = true;
}).one(this.animations.end, function () { }).one(this.animations.end, function () {
$(this).remove(); $(this).remove();
if ($.isFunction(self.settings.onClosed)) { if ($.isFunction(self.settings.onClosed)) {
self.settings.onClosed.call(this); self.settings.onClosed.call(this);
} }
}); });
setTimeout(function () { setTimeout(function () {
if (!hasAnimation) { if (!hasAnimation) {
self.$ele.remove(); self.$ele.remove();
if (self.settings.onClosed) { if (self.settings.onClosed) {
self.settings.onClosed(self.$ele); self.settings.onClosed(self.$ele);
} }
} }
}, 600); }, 600);
}, },
reposition: function (posX) { reposition: function (posX) {
var self = this, var self = this,
notifies = '[data-notify-position="' + this.settings.placement.from + '-' + this.settings.placement.align + '"]:not([data-closing="true"])', notifies = '[data-notify-position="' + this.settings.placement.from + '-' + this.settings.placement.align + '"]:not([data-closing="true"])',
$elements = this.$ele.nextAll(notifies); $elements = this.$ele.nextAll(notifies);
if (this.settings.newest_on_top === true) { if (this.settings.newest_on_top === true) {
$elements = this.$ele.prevAll(notifies); $elements = this.$ele.prevAll(notifies);
} }
$elements.each(function () { $elements.each(function () {
$(this).css(self.settings.placement.from, posX); $(this).css(self.settings.placement.from, posX);
posX = (parseInt(posX) + parseInt(self.settings.spacing)) + $(this).outerHeight(); posX = (parseInt(posX) + parseInt(self.settings.spacing)) + $(this).outerHeight();
}); });
} }
}); });
$.notify = function (content, options) { $.notify = function (content, options) {
var plugin = new Notify(this, content, options); var plugin = new Notify(this, content, options);
return plugin.notify; return plugin.notify;
}; };
$.notifyDefaults = function (options) { $.notifyDefaults = function (options) {
defaults = $.extend(true, {}, defaults, options); defaults = $.extend(true, {}, defaults, options);
return defaults; return defaults;
}; };
$.notifyClose = function (command) { $.notifyClose = function (command) {
if (typeof command === "undefined" || command === "all") { if (typeof command === "undefined" || command === "all") {
$('[data-notify]').find('[data-notify="dismiss"]').trigger('click'); $('[data-notify]').find('[data-notify="dismiss"]').trigger('click');
} else { } else {
$('[data-notify-position="' + command + '"]').find('[data-notify="dismiss"]').trigger('click'); $('[data-notify-position="' + command + '"]').find('[data-notify="dismiss"]').trigger('click');
} }
}; };
})); }));

View File

@ -1,438 +1,438 @@
!function($) { !function($) {
var Selectpicker = function(element, options, e) { var Selectpicker = function(element, options, e) {
if (e ) { if (e ) {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
} }
this.$element = $(element); this.$element = $(element);
this.$newElement = null; this.$newElement = null;
this.button = null; this.button = null;
//Merge defaults, options and data-attributes to make our options //Merge defaults, options and data-attributes to make our options
this.options = $.extend({}, $.fn.selectpicker.defaults, this.$element.data(), typeof options == 'object' && options); this.options = $.extend({}, $.fn.selectpicker.defaults, this.$element.data(), typeof options == 'object' && options);
//If we have no title yet, check the attribute 'title' (this is missed by jq as its not a data-attribute //If we have no title yet, check the attribute 'title' (this is missed by jq as its not a data-attribute
if(this.options.title==null) if(this.options.title==null)
this.options.title = this.$element.attr('title'); this.options.title = this.$element.attr('title');
//Expose public methods //Expose public methods
this.val = Selectpicker.prototype.val; this.val = Selectpicker.prototype.val;
this.render = Selectpicker.prototype.render; this.render = Selectpicker.prototype.render;
this.init(); this.init();
}; };
Selectpicker.prototype = { Selectpicker.prototype = {
constructor: Selectpicker, constructor: Selectpicker,
init: function (e) { init: function (e) {
var _this = this; var _this = this;
this.$element.hide(); this.$element.hide();
this.multiple = this.$element.prop('multiple'); this.multiple = this.$element.prop('multiple');
var classList = this.$element.attr('class') !== undefined ? this.$element.attr('class').split(/\s+/) : ''; var classList = this.$element.attr('class') !== undefined ? this.$element.attr('class').split(/\s+/) : '';
var id = this.$element.attr('id'); var id = this.$element.attr('id');
this.$element.after( this.createView() ); this.$element.after( this.createView() );
this.$newElement = this.$element.next('.select'); this.$newElement = this.$element.next('.select');
var select = this.$newElement; var select = this.$newElement;
var menu = this.$newElement.find('.dropdown-menu'); var menu = this.$newElement.find('.dropdown-menu');
var menuArrow = this.$newElement.find('.dropdown-arrow'); var menuArrow = this.$newElement.find('.dropdown-arrow');
var menuA = menu.find('li > a'); var menuA = menu.find('li > a');
var liHeight = select.addClass('open').find('.dropdown-menu li > a').outerHeight(); var liHeight = select.addClass('open').find('.dropdown-menu li > a').outerHeight();
select.removeClass('open'); select.removeClass('open');
var divHeight = menu.find('li .divider').outerHeight(true); var divHeight = menu.find('li .divider').outerHeight(true);
var selectOffset_top = this.$newElement.offset().top; var selectOffset_top = this.$newElement.offset().top;
var size = 0; var size = 0;
var menuHeight = 0; var menuHeight = 0;
var selectHeight = this.$newElement.outerHeight(); var selectHeight = this.$newElement.outerHeight();
this.button = this.$newElement.find('> button'); this.button = this.$newElement.find('> button');
if (id !== undefined) { if (id !== undefined) {
this.button.attr('id', id); this.button.attr('id', id);
$('label[for="' + id + '"]').click(function(){ select.find('button#'+id).focus(); }) $('label[for="' + id + '"]').click(function(){ select.find('button#'+id).focus(); })
} }
for (var i = 0; i < classList.length; i++) { for (var i = 0; i < classList.length; i++) {
if(classList[i] != 'selectpicker') { if(classList[i] != 'selectpicker') {
this.$newElement.addClass(classList[i]); this.$newElement.addClass(classList[i]);
} }
} }
//If we are multiple, then add the show-tick class by default //If we are multiple, then add the show-tick class by default
if(this.multiple) { if(this.multiple) {
this.$newElement.addClass('select-multiple'); this.$newElement.addClass('select-multiple');
} }
this.button.addClass(this.options.style); this.button.addClass(this.options.style);
menu.addClass(this.options.menuStyle); menu.addClass(this.options.menuStyle);
menuArrow.addClass(function() { menuArrow.addClass(function() {
if (_this.options.menuStyle) { if (_this.options.menuStyle) {
return _this.options.menuStyle.replace('dropdown-', 'dropdown-arrow-'); return _this.options.menuStyle.replace('dropdown-', 'dropdown-arrow-');
} }
}); });
this.checkDisabled(); this.checkDisabled();
this.checkTabIndex(); this.checkTabIndex();
this.clickListener(); this.clickListener();
var menuPadding = parseInt(menu.css('padding-top')) + parseInt(menu.css('padding-bottom')) + parseInt(menu.css('border-top-width')) + parseInt(menu.css('border-bottom-width')); var menuPadding = parseInt(menu.css('padding-top')) + parseInt(menu.css('padding-bottom')) + parseInt(menu.css('border-top-width')) + parseInt(menu.css('border-bottom-width'));
if (this.options.size == 'auto') { if (this.options.size == 'auto') {
// Creative Tim Changes: We changed the regular function made in bootstrap-select with this function so the getSize() will not be triggered one million times per second while you scroll. // Creative Tim Changes: We changed the regular function made in bootstrap-select with this function so the getSize() will not be triggered one million times per second while you scroll.
var getSize = debounce(function() { var getSize = debounce(function() {
var selectOffset_top_scroll = selectOffset_top - $(window).scrollTop(); var selectOffset_top_scroll = selectOffset_top - $(window).scrollTop();
var windowHeight = $(window).innerHeight(); var windowHeight = $(window).innerHeight();
var menuExtras = menuPadding + parseInt(menu.css('margin-top')) + parseInt(menu.css('margin-bottom')) + 2; var menuExtras = menuPadding + parseInt(menu.css('margin-top')) + parseInt(menu.css('margin-bottom')) + 2;
var selectOffset_bot = windowHeight - selectOffset_top_scroll - selectHeight - menuExtras; var selectOffset_bot = windowHeight - selectOffset_top_scroll - selectHeight - menuExtras;
menuHeight = selectOffset_bot; menuHeight = selectOffset_bot;
if (select.hasClass('dropup')) { if (select.hasClass('dropup')) {
menuHeight = selectOffset_top_scroll - menuExtras; menuHeight = selectOffset_top_scroll - menuExtras;
} }
//limit menuHeight to 300px to have a smooth transition with cubic bezier on dropdown //limit menuHeight to 300px to have a smooth transition with cubic bezier on dropdown
if(menuHeight >= 300){ if(menuHeight >= 300){
menuHeight = 300; menuHeight = 300;
} }
menu.css({'max-height' : menuHeight + 'px', 'overflow-y' : 'auto', 'min-height' : liHeight * 3 + 'px'}); menu.css({'max-height' : menuHeight + 'px', 'overflow-y' : 'auto', 'min-height' : liHeight * 3 + 'px'});
}, 50); }, 50);
getSize; getSize;
$(window).on('scroll', getSize); $(window).on('scroll', getSize);
$(window).on('resize', getSize); $(window).on('resize', getSize);
if (window.MutationObserver) { if (window.MutationObserver) {
new MutationObserver(getSize).observe(this.$element.get(0), { new MutationObserver(getSize).observe(this.$element.get(0), {
childList: true childList: true
}); });
} else { } else {
this.$element.bind('DOMNodeInserted', getSize); this.$element.bind('DOMNodeInserted', getSize);
} }
} else if (this.options.size && this.options.size != 'auto' && menu.find('li').length > this.options.size) { } else if (this.options.size && this.options.size != 'auto' && menu.find('li').length > this.options.size) {
var optIndex = menu.find("li > *").filter(':not(.divider)').slice(0,this.options.size).last().parent().index(); var optIndex = menu.find("li > *").filter(':not(.divider)').slice(0,this.options.size).last().parent().index();
var divLength = menu.find("li").slice(0,optIndex + 1).find('.divider').length; var divLength = menu.find("li").slice(0,optIndex + 1).find('.divider').length;
menuHeight = liHeight*this.options.size + divLength*divHeight + menuPadding; menuHeight = liHeight*this.options.size + divLength*divHeight + menuPadding;
menu.css({'max-height' : menuHeight + 'px', 'overflow-y' : 'scroll'}); menu.css({'max-height' : menuHeight + 'px', 'overflow-y' : 'scroll'});
//console.log('sunt in if'); //console.log('sunt in if');
} }
// Listen for updates to the DOM and re render... (Use Mutation Observer when availiable) // Listen for updates to the DOM and re render... (Use Mutation Observer when availiable)
if (window.MutationObserver) { if (window.MutationObserver) {
new MutationObserver($.proxy(this.reloadLi, this)).observe(this.$element.get(0), { new MutationObserver($.proxy(this.reloadLi, this)).observe(this.$element.get(0), {
childList: true childList: true
}); });
} else { } else {
this.$element.bind('DOMNodeInserted', $.proxy(this.reloadLi, this)); this.$element.bind('DOMNodeInserted', $.proxy(this.reloadLi, this));
} }
this.render(); this.render();
}, },
createDropdown: function() { createDropdown: function() {
var drop = var drop =
"<div class='btn-group select'>" + "<div class='btn-group select'>" +
"<button class='btn dropdown-toggle clearfix' data-toggle='dropdown'>" + "<button class='btn dropdown-toggle clearfix' data-toggle='dropdown'>" +
"<span class='filter-option'></span>&nbsp;" + "<span class='filter-option'></span>&nbsp;" +
"<span class='caret'></span>" + "<span class='caret'></span>" +
"</button>" + "</button>" +
"<span class='dropdown-arrow'></span>" + "<span class='dropdown-arrow'></span>" +
"<ul class='dropdown-menu' role='menu'>" + "<ul class='dropdown-menu' role='menu'>" +
"</ul>" + "</ul>" +
"</div>"; "</div>";
return $(drop); return $(drop);
}, },
createView: function() { createView: function() {
var $drop = this.createDropdown(); var $drop = this.createDropdown();
var $li = this.createLi(); var $li = this.createLi();
$drop.find('ul').append($li); $drop.find('ul').append($li);
return $drop; return $drop;
}, },
reloadLi: function() { reloadLi: function() {
//Remove all children. //Remove all children.
this.destroyLi(); this.destroyLi();
//Re build //Re build
$li = this.createLi(); $li = this.createLi();
this.$newElement.find('ul').append( $li ); this.$newElement.find('ul').append( $li );
//render view //render view
this.render(); this.render();
}, },
destroyLi:function() { destroyLi:function() {
this.$newElement.find('li').remove(); this.$newElement.find('li').remove();
}, },
createLi: function() { createLi: function() {
var _this = this; var _this = this;
var _li = []; var _li = [];
var _liA = []; var _liA = [];
var _liHtml = ''; var _liHtml = '';
this.$element.find('option').each(function(){ this.$element.find('option').each(function(){
_li.push($(this).text()); _li.push($(this).text());
}); });
this.$element.find('option').each(function(index) { this.$element.find('option').each(function(index) {
//Get the class and text for the option //Get the class and text for the option
var optionClass = $(this).attr("class") !== undefined ? $(this).attr("class") : ''; var optionClass = $(this).attr("class") !== undefined ? $(this).attr("class") : '';
var text = $(this).text(); var text = $(this).text();
var subtext = $(this).data('subtext') !== undefined ? '<small class="muted">'+$(this).data('subtext')+'</small>' : ''; var subtext = $(this).data('subtext') !== undefined ? '<small class="muted">'+$(this).data('subtext')+'</small>' : '';
//Append any subtext to the main text. //Append any subtext to the main text.
text+=subtext; text+=subtext;
if ($(this).parent().is('optgroup') && $(this).data('divider') != true) { if ($(this).parent().is('optgroup') && $(this).data('divider') != true) {
if ($(this).index() == 0) { if ($(this).index() == 0) {
//Get the opt group label //Get the opt group label
var label = $(this).parent().attr('label'); var label = $(this).parent().attr('label');
var labelSubtext = $(this).parent().data('subtext') !== undefined ? '<small class="muted">'+$(this).parent().data('subtext')+'</small>' : ''; var labelSubtext = $(this).parent().data('subtext') !== undefined ? '<small class="muted">'+$(this).parent().data('subtext')+'</small>' : '';
label += labelSubtext; label += labelSubtext;
if ($(this)[0].index != 0) { if ($(this)[0].index != 0) {
_liA.push( _liA.push(
'<div class="divider"></div>'+ '<div class="divider"></div>'+
'<dt>'+label+'</dt>'+ '<dt>'+label+'</dt>'+
_this.createA(text, "opt " + optionClass ) _this.createA(text, "opt " + optionClass )
); );
} else { } else {
_liA.push( _liA.push(
'<dt>'+label+'</dt>'+ '<dt>'+label+'</dt>'+
_this.createA(text, "opt " + optionClass )); _this.createA(text, "opt " + optionClass ));
} }
} else { } else {
_liA.push( _this.createA(text, "opt " + optionClass ) ); _liA.push( _this.createA(text, "opt " + optionClass ) );
} }
} else if ($(this).data('divider') == true) { } else if ($(this).data('divider') == true) {
_liA.push('<div class="divider"></div>'); _liA.push('<div class="divider"></div>');
} else if ($(this).data('hidden') == true) { } else if ($(this).data('hidden') == true) {
_liA.push(''); _liA.push('');
} else { } else {
_liA.push( _this.createA(text, optionClass ) ); _liA.push( _this.createA(text, optionClass ) );
} }
}); });
if (_li.length > 0) { if (_li.length > 0) {
for (var i = 0; i < _li.length; i++) { for (var i = 0; i < _li.length; i++) {
var $option = this.$element.find('option').eq(i); var $option = this.$element.find('option').eq(i);
_liHtml += "<li rel=" + i + ">" + _liA[i] + "</li>"; _liHtml += "<li rel=" + i + ">" + _liA[i] + "</li>";
} }
} }
//If we dont have a selected item, and we dont have a title, select the first element so something is set in the button //If we dont have a selected item, and we dont have a title, select the first element so something is set in the button
if(this.$element.find('option:selected').length==0 && !_this.options.title) { if(this.$element.find('option:selected').length==0 && !_this.options.title) {
this.$element.find('option').eq(0).prop('selected', true).attr('selected', 'selected'); this.$element.find('option').eq(0).prop('selected', true).attr('selected', 'selected');
} }
return $(_liHtml); return $(_liHtml);
}, },
createA:function(test, classes) { createA:function(test, classes) {
return '<a tabindex="-1" href="#" class="'+classes+'">' + return '<a tabindex="-1" href="#" class="'+classes+'">' +
'<span class="">' + test + '</span>' + '<span class="">' + test + '</span>' +
'</a>'; '</a>';
}, },
render:function() { render:function() {
var _this = this; var _this = this;
//Set width of select //Set width of select
if (this.options.width == 'auto') { if (this.options.width == 'auto') {
var ulWidth = this.$newElement.find('.dropdown-menu').css('width'); var ulWidth = this.$newElement.find('.dropdown-menu').css('width');
this.$newElement.css('width',ulWidth); this.$newElement.css('width',ulWidth);
} else if (this.options.width && this.options.width != 'auto') { } else if (this.options.width && this.options.width != 'auto') {
this.$newElement.css('width',this.options.width); this.$newElement.css('width',this.options.width);
} }
//Update the LI to match the SELECT //Update the LI to match the SELECT
this.$element.find('option').each(function(index) { this.$element.find('option').each(function(index) {
_this.setDisabled(index, $(this).is(':disabled') || $(this).parent().is(':disabled') ); _this.setDisabled(index, $(this).is(':disabled') || $(this).parent().is(':disabled') );
_this.setSelected(index, $(this).is(':selected') ); _this.setSelected(index, $(this).is(':selected') );
}); });
var selectedItems = this.$element.find('option:selected').map(function(index,value) { var selectedItems = this.$element.find('option:selected').map(function(index,value) {
if($(this).attr('title')!=undefined) { if($(this).attr('title')!=undefined) {
return $(this).attr('title'); return $(this).attr('title');
} else { } else {
return $(this).text(); return $(this).text();
} }
}).toArray(); }).toArray();
//Convert all the values into a comma delimited string //Convert all the values into a comma delimited string
var title = selectedItems.join(", "); var title = selectedItems.join(", ");
//If this is multi select, and the selectText type is count, the show 1 of 2 selected etc.. //If this is multi select, and the selectText type is count, the show 1 of 2 selected etc..
if(_this.multiple && _this.options.selectedTextFormat.indexOf('count') > -1) { if(_this.multiple && _this.options.selectedTextFormat.indexOf('count') > -1) {
var max = _this.options.selectedTextFormat.split(">"); var max = _this.options.selectedTextFormat.split(">");
if( (max.length>1 && selectedItems.length > max[1]) || (max.length==1 && selectedItems.length>=2)) { if( (max.length>1 && selectedItems.length > max[1]) || (max.length==1 && selectedItems.length>=2)) {
title = selectedItems.length +' of ' + this.$element.find('option').length + ' selected'; title = selectedItems.length +' of ' + this.$element.find('option').length + ' selected';
} }
} }
//If we dont have a title, then use the default, or if nothing is set at all, use the not selected text //If we dont have a title, then use the default, or if nothing is set at all, use the not selected text
if(!title) { if(!title) {
title = _this.options.title != undefined ? _this.options.title : _this.options.noneSelectedText; title = _this.options.title != undefined ? _this.options.title : _this.options.noneSelectedText;
} }
this.$element.next('.select').find('.filter-option').html( title ); this.$element.next('.select').find('.filter-option').html( title );
}, },
setSelected:function(index, selected) { setSelected:function(index, selected) {
if(selected) { if(selected) {
this.$newElement.find('li').eq(index).addClass('selected'); this.$newElement.find('li').eq(index).addClass('selected');
} else { } else {
this.$newElement.find('li').eq(index).removeClass('selected'); this.$newElement.find('li').eq(index).removeClass('selected');
} }
}, },
setDisabled:function(index, disabled) { setDisabled:function(index, disabled) {
if(disabled) { if(disabled) {
this.$newElement.find('li').eq(index).addClass('disabled'); this.$newElement.find('li').eq(index).addClass('disabled');
} else { } else {
this.$newElement.find('li').eq(index).removeClass('disabled'); this.$newElement.find('li').eq(index).removeClass('disabled');
} }
}, },
checkDisabled: function() { checkDisabled: function() {
if (this.$element.is(':disabled')) { if (this.$element.is(':disabled')) {
this.button.addClass('disabled'); this.button.addClass('disabled');
this.button.click(function(e) { this.button.click(function(e) {
e.preventDefault(); e.preventDefault();
}); });
} }
}, },
checkTabIndex: function() { checkTabIndex: function() {
if (this.$element.is('[tabindex]')) { if (this.$element.is('[tabindex]')) {
var tabindex = this.$element.attr("tabindex"); var tabindex = this.$element.attr("tabindex");
this.button.attr('tabindex', tabindex); this.button.attr('tabindex', tabindex);
} }
}, },
clickListener: function() { clickListener: function() {
var _this = this; var _this = this;
$('body').on('touchstart.dropdown', '.dropdown-menu', function (e) { e.stopPropagation(); }); $('body').on('touchstart.dropdown', '.dropdown-menu', function (e) { e.stopPropagation(); });
this.$newElement.on('click', 'li a', function(e){ this.$newElement.on('click', 'li a', function(e){
var clickedIndex = $(this).parent().index(), var clickedIndex = $(this).parent().index(),
$this = $(this).parent(), $this = $(this).parent(),
$select = $this.parents('.select'); $select = $this.parents('.select');
//Dont close on multi choice menu //Dont close on multi choice menu
if(_this.multiple) { if(_this.multiple) {
e.stopPropagation(); e.stopPropagation();
} }
e.preventDefault(); e.preventDefault();
//Dont run if we have been disabled //Dont run if we have been disabled
if ($select.prev('select').not(':disabled') && !$(this).parent().hasClass('disabled')){ if ($select.prev('select').not(':disabled') && !$(this).parent().hasClass('disabled')){
//Deselect all others if not multi select box //Deselect all others if not multi select box
if (!_this.multiple) { if (!_this.multiple) {
$select.prev('select').find('option').removeAttr('selected'); $select.prev('select').find('option').removeAttr('selected');
$select.prev('select').find('option').eq(clickedIndex).prop('selected', true).attr('selected', 'selected'); $select.prev('select').find('option').eq(clickedIndex).prop('selected', true).attr('selected', 'selected');
} }
//Else toggle the one we have chosen if we are multi selet. //Else toggle the one we have chosen if we are multi selet.
else { else {
var selected = $select.prev('select').find('option').eq(clickedIndex).prop('selected'); var selected = $select.prev('select').find('option').eq(clickedIndex).prop('selected');
if(selected) { if(selected) {
$select.prev('select').find('option').eq(clickedIndex).removeAttr('selected'); $select.prev('select').find('option').eq(clickedIndex).removeAttr('selected');
} else { } else {
$select.prev('select').find('option').eq(clickedIndex).prop('selected', true).attr('selected', 'selected'); $select.prev('select').find('option').eq(clickedIndex).prop('selected', true).attr('selected', 'selected');
} }
} }
$select.find('.filter-option').html($this.text()); $select.find('.filter-option').html($this.text());
$select.find('button').focus(); $select.find('button').focus();
// Trigger select 'change' // Trigger select 'change'
$select.prev('select').trigger('change'); $select.prev('select').trigger('change');
} }
}); });
this.$newElement.on('click', 'li.disabled a, li dt, li .divider', function(e) { this.$newElement.on('click', 'li.disabled a, li dt, li .divider', function(e) {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
$select = $(this).parent().parents('.select'); $select = $(this).parent().parents('.select');
$select.find('button').focus(); $select.find('button').focus();
}); });
this.$element.on('change', function(e) { this.$element.on('change', function(e) {
_this.render(); _this.render();
}); });
}, },
val:function(value) { val:function(value) {
if(value!=undefined) { if(value!=undefined) {
this.$element.val( value ); this.$element.val( value );
this.$element.trigger('change'); this.$element.trigger('change');
return this.$element; return this.$element;
} else { } else {
return this.$element.val(); return this.$element.val();
} }
} }
}; };
$.fn.selectpicker = function(option, event) { $.fn.selectpicker = function(option, event) {
//get the args of the outer function.. //get the args of the outer function..
var args = arguments; var args = arguments;
var value; var value;
var chain = this.each(function () { var chain = this.each(function () {
var $this = $(this), var $this = $(this),
data = $this.data('selectpicker'), data = $this.data('selectpicker'),
options = typeof option == 'object' && option; options = typeof option == 'object' && option;
if (!data) { if (!data) {
$this.data('selectpicker', (data = new Selectpicker(this, options, event))); $this.data('selectpicker', (data = new Selectpicker(this, options, event)));
} else { } else {
for(var i in option) { for(var i in option) {
data[i]=option[i]; data[i]=option[i];
} }
} }
if (typeof option == 'string') { if (typeof option == 'string') {
//Copy the value of option, as once we shift the arguments //Copy the value of option, as once we shift the arguments
//it also shifts the value of option. //it also shifts the value of option.
property = option; property = option;
if(data[property] instanceof Function) { if(data[property] instanceof Function) {
[].shift.apply(args); [].shift.apply(args);
value = data[property].apply(data, args); value = data[property].apply(data, args);
} else { } else {
value = data[property]; value = data[property];
} }
} }
}); });
if(value!=undefined) { if(value!=undefined) {
return value; return value;
} else { } else {
return chain; return chain;
} }
}; };
$.fn.selectpicker.defaults = { $.fn.selectpicker.defaults = {
style: null, style: null,
size: 'auto', size: 'auto',
title: null, title: null,
selectedTextFormat : 'values', selectedTextFormat : 'values',
noneSelectedText : 'Nothing selected', noneSelectedText : 'Nothing selected',
width: null, width: null,
menuStyle: null, menuStyle: null,
toggleSize: null toggleSize: null
} }
}(window.jQuery); }(window.jQuery);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,154 +1,154 @@
/*! /*!
========================================================= =========================================================
* Light Bootstrap Dashboard - v1.4.0 * Light Bootstrap Dashboard - v1.4.0
========================================================= =========================================================
* Product Page: http://www.creative-tim.com/product/light-bootstrap-dashboard * Product Page: http://www.creative-tim.com/product/light-bootstrap-dashboard
* Copyright 2017 Creative Tim (http://www.creative-tim.com) * Copyright 2017 Creative Tim (http://www.creative-tim.com)
* Licensed under MIT (https://github.com/creativetimofficial/light-bootstrap-dashboard/blob/master/LICENSE.md) * Licensed under MIT (https://github.com/creativetimofficial/light-bootstrap-dashboard/blob/master/LICENSE.md)
========================================================= =========================================================
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/ */
var searchVisible = 0; var searchVisible = 0;
var transparent = true; var transparent = true;
var transparentDemo = true; var transparentDemo = true;
var fixedTop = false; var fixedTop = false;
var navbar_initialized = false; var navbar_initialized = false;
$(document).ready(function(){ $(document).ready(function(){
window_width = $(window).width(); window_width = $(window).width();
// check if there is an image set for the sidebar's background // check if there is an image set for the sidebar's background
lbd.checkSidebarImage(); lbd.checkSidebarImage();
// Init navigation toggle for small screens // Init navigation toggle for small screens
lbd.initRightMenu(); lbd.initRightMenu();
// Activate the tooltips // Activate the tooltips
$('[rel="tooltip"]').tooltip(); $('[rel="tooltip"]').tooltip();
$('.form-control').on("focus", function(){ $('.form-control').on("focus", function(){
$(this).parent('.input-group').addClass("input-group-focus"); $(this).parent('.input-group').addClass("input-group-focus");
}).on("blur", function(){ }).on("blur", function(){
$(this).parent(".input-group").removeClass("input-group-focus"); $(this).parent(".input-group").removeClass("input-group-focus");
}); });
// Fixes sub-nav not working as expected on IOS // Fixes sub-nav not working as expected on IOS
$('body').on('touchstart.dropdown', '.dropdown-menu', function (e) { e.stopPropagation(); }); $('body').on('touchstart.dropdown', '.dropdown-menu', function (e) { e.stopPropagation(); });
}); });
$(document).on('click', '.navbar-toggle', function(){ $(document).on('click', '.navbar-toggle', function(){
$toggle = $(this); $toggle = $(this);
if(lbd.misc.navbar_menu_visible == 1) { if(lbd.misc.navbar_menu_visible == 1) {
$('html').removeClass('nav-open'); $('html').removeClass('nav-open');
lbd.misc.navbar_menu_visible = 0; lbd.misc.navbar_menu_visible = 0;
$('#bodyClick').remove(); $('#bodyClick').remove();
setTimeout(function(){ setTimeout(function(){
$toggle.removeClass('toggled'); $toggle.removeClass('toggled');
}, 550); }, 550);
} else { } else {
setTimeout(function(){ setTimeout(function(){
$toggle.addClass('toggled'); $toggle.addClass('toggled');
}, 580); }, 580);
div = '<div id="bodyClick"></div>'; div = '<div id="bodyClick"></div>';
$(div).appendTo('body').click(function() { $(div).appendTo('body').click(function() {
$('html').removeClass('nav-open'); $('html').removeClass('nav-open');
lbd.misc.navbar_menu_visible = 0; lbd.misc.navbar_menu_visible = 0;
setTimeout(function(){ setTimeout(function(){
$toggle.removeClass('toggled'); $toggle.removeClass('toggled');
$('#bodyClick').remove(); $('#bodyClick').remove();
}, 550); }, 550);
}); });
$('html').addClass('nav-open'); $('html').addClass('nav-open');
lbd.misc.navbar_menu_visible = 1; lbd.misc.navbar_menu_visible = 1;
} }
}); });
$(window).on('resize', function(){ $(window).on('resize', function(){
if(navbar_initialized){ if(navbar_initialized){
lbd.initRightMenu(); lbd.initRightMenu();
navbar_initialized = true; navbar_initialized = true;
} }
}); });
lbd = { lbd = {
misc:{ misc:{
navbar_menu_visible: 0 navbar_menu_visible: 0
}, },
checkSidebarImage: function(){ checkSidebarImage: function(){
$sidebar = $('.sidebar'); $sidebar = $('.sidebar');
image_src = $sidebar.data('image'); image_src = $sidebar.data('image');
if(image_src !== undefined){ if(image_src !== undefined){
sidebar_container = '<div class="sidebar-background" style="background-image: url(' + image_src + ') "/>' sidebar_container = '<div class="sidebar-background" style="background-image: url(' + image_src + ') "/>'
$sidebar.append(sidebar_container); $sidebar.append(sidebar_container);
} }
}, },
initRightMenu: debounce(function(){ initRightMenu: debounce(function(){
if(!navbar_initialized){ if(!navbar_initialized){
$sidebar_wrapper = $('.sidebar-wrapper'); $sidebar_wrapper = $('.sidebar-wrapper');
$navbar = $('nav').find('.navbar-collapse').html(); $navbar = $('nav').find('.navbar-collapse').html();
mobile_menu_content = ''; mobile_menu_content = '';
nav_content = $navbar; nav_content = $navbar;
nav_content = '<ul class="nav nav-mobile-menu">' + nav_content + '</ul>'; nav_content = '<ul class="nav nav-mobile-menu">' + nav_content + '</ul>';
// navbar_form = $('nav').find('.navbar-form').get(0).outerHTML; // navbar_form = $('nav').find('.navbar-form').get(0).outerHTML;
$sidebar_nav = $sidebar_wrapper.find(' > .nav'); $sidebar_nav = $sidebar_wrapper.find(' > .nav');
// insert the navbar form before the sidebar list // insert the navbar form before the sidebar list
$nav_content = $(nav_content); $nav_content = $(nav_content);
// $navbar_form = $(navbar_form); // $navbar_form = $(navbar_form);
$nav_content.insertBefore($sidebar_nav); $nav_content.insertBefore($sidebar_nav);
// $navbar_form.insertBefore($nav_content); // $navbar_form.insertBefore($nav_content);
$(".sidebar-wrapper .dropdown .dropdown-menu > li > a").click(function(event) { $(".sidebar-wrapper .dropdown .dropdown-menu > li > a").click(function(event) {
event.stopPropagation(); event.stopPropagation();
}); });
mobile_menu_initialized = true; mobile_menu_initialized = true;
} else { } else {
if($(window).width() > 991){ if($(window).width() > 991){
// reset all the additions that we made for the sidebar wrapper only if the screen is bigger than 991px // reset all the additions that we made for the sidebar wrapper only if the screen is bigger than 991px
// $sidebar_wrapper.find('.navbar-form').remove(); // $sidebar_wrapper.find('.navbar-form').remove();
$sidebar_wrapper.find('.nav-mobile-menu').remove(); $sidebar_wrapper.find('.nav-mobile-menu').remove();
mobile_menu_initialized = false; mobile_menu_initialized = false;
} }
} }
},200) },200)
} }
// Returns a function, that, as long as it continues to be invoked, will not // Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for // be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the // N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing. // leading edge, instead of the trailing.
function debounce(func, wait, immediate) { function debounce(func, wait, immediate) {
var timeout; var timeout;
return function() { return function() {
var context = this, args = arguments; var context = this, args = arguments;
clearTimeout(timeout); clearTimeout(timeout);
timeout = setTimeout(function() { timeout = setTimeout(function() {
timeout = null; timeout = null;
if (!immediate) func.apply(context, args); if (!immediate) func.apply(context, args);
}, wait); }, wait);
if (immediate && !timeout) func.apply(context, args); if (immediate && !timeout) func.apply(context, args);
}; };
}; };

View File

@ -1,56 +1,56 @@
{{define "head"}} {{define "head"}}
<title>Tickets | A Discord Support Manager Bot</title> <title>Tickets | A Discord Support Manager Bot</title>
<!-- Meta --> <!-- Meta -->
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="Management panel for the Discord Tickets bot"> <meta name="description" content="Management panel for the Discord Tickets bot">
<link rel="shortcut icon" href="/assets/img/favicon.ico" type="image/x-icon"> <link rel="shortcut icon" href="/assets/img/favicon.ico" type="image/x-icon">
<link rel="icon" href="/assets/img/favicon.ico" type="image/x-icon"> <link rel="icon" href="/assets/img/favicon.ico" type="image/x-icon">
<!-- Custom CSS --> <!-- Custom CSS -->
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700" rel="stylesheet"> <link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700" rel="stylesheet">
<link href="/assets/css/style.css" rel="stylesheet"> <link href="/assets/css/style.css" rel="stylesheet">
<style> <style>
.avatar { .avatar {
background: url("{{.avatar}}?size=32"); background: url("{{.avatar}}?size=32");
width: 28px; width: 28px;
height: 29px; height: 29px;
display: block; display: block;
background-size: cover; background-size: cover;
border-radius: 50%; border-radius: 50%;
} }
</style> </style>
<!-- Bootstrap --> <!-- Bootstrap -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<link href="/assets/css/light-bootstrap-dashboard.css?v=1.4.0" rel="stylesheet"/> <link href="/assets/css/light-bootstrap-dashboard.css?v=1.4.0" rel="stylesheet"/>
<link href="/assets/css/animate.min.css" rel="stylesheet"/> <link href="/assets/css/animate.min.css" rel="stylesheet"/>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
crossorigin="anonymous"></script> crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js" <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js"
integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut"
crossorigin="anonymous"></script> crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js" <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js"
integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k"
crossorigin="anonymous"></script> crossorigin="anonymous"></script>
<!-- Discord theme --> <!-- Discord theme -->
<link href="/assets/css/discordmock.css" rel="stylesheet"/> <link href="/assets/css/discordmock.css" rel="stylesheet"/>
<!-- Icons --> <!-- Icons -->
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css" <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css"
integrity="sha384-oS3vJWv+0UjzBfQzYUhtDYW+Pj2yciDJxpsK1OYPAYjqT085Qq/1cq5FLXAZQ7Ay" crossorigin="anonymous"> integrity="sha384-oS3vJWv+0UjzBfQzYUhtDYW+Pj2yciDJxpsK1OYPAYjqT085Qq/1cq5FLXAZQ7Ay" crossorigin="anonymous">
<!-- GA --> <!-- GA -->
<script type="80be96f83bbfbba3d4097e23-text/javascript"> <script type="80be96f83bbfbba3d4097e23-text/javascript">
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date; window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
ga('create', 'UA-161945537-1', 'auto'); ga('create', 'UA-161945537-1', 'auto');
ga('send', 'pageview'); ga('send', 'pageview');
</script> </script>
<script async src='https://www.google-analytics.com/analytics.js' type="80be96f83bbfbba3d4097e23-text/javascript"></script> <script async src='https://www.google-analytics.com/analytics.js' type="80be96f83bbfbba3d4097e23-text/javascript"></script>
{{end}} {{end}}

View File

@ -1,23 +1,23 @@
{{define "navbar"}} {{define "navbar"}}
<nav class="navbar navbar-expand-lg navbar-dark bg-dark"> <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="collapse navbar-collapse" id="navbarSupportedContent"> <div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto"> <ul class="navbar-nav mr-auto">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/manage/{{.guildId}}/settings">Settings</a> <a class="nav-link" href="/manage/{{.guildId}}/settings">Settings</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/manage/{{.guildId}}/logs/page/1">Logs</a> <a class="nav-link" href="/manage/{{.guildId}}/logs/page/1">Logs</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/manage/{{.guildId}}/blacklist">Blacklist</a> <a class="nav-link" href="/manage/{{.guildId}}/blacklist">Blacklist</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/manage/{{.guildId}}/tickets">Ticket List</a> <a class="nav-link" href="/manage/{{.guildId}}/tickets">Ticket List</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/manage/{{.guildId}}/panels">Reaction Panels</a> <a class="nav-link" href="/manage/{{.guildId}}/panels">Reaction Panels</a>
</li> </li>
</ul> </ul>
</div> </div>
</nav> </nav>
{{end}} {{end}}

View File

@ -1,33 +1,33 @@
{{define "sidebar"}} {{define "sidebar"}}
<div class="sidebar" data-color="orange" data-image="/assets/img/sidebar-2.jpg"> <div class="sidebar" data-color="orange" data-image="/assets/img/sidebar-2.jpg">
<div class="sidebar-wrapper"> <div class="sidebar-wrapper">
<div class="logo"> <div class="logo">
<a href="https://ticketsbot.net" class="simple-text"> <a href="https://ticketsbot.net" class="simple-text">
TicketsBot TicketsBot
</a> </a>
</div> </div>
<ul class="nav"> <ul class="nav">
<li class="nav-item sidebar-bottom"> <li class="nav-item sidebar-bottom">
<a href="/"> <a href="/">
<i class="fas fa-server"></i> <i class="fas fa-server"></i>
<p>Servers</p> <p>Servers</p>
</a> </a>
</li> </li>
<li class="nav-item sidebar-bottom" style="bottom: 60px"> <li class="nav-item sidebar-bottom" style="bottom: 60px">
<a href="/logout"> <a href="/logout">
<i class="fas fa-sign-out-alt"></i> <i class="fas fa-sign-out-alt"></i>
<p>Logout</p> <p>Logout</p>
</a> </a>
</li> </li>
<li class="nav-item sidebar-bottom" style="bottom: 10px"> <li class="nav-item sidebar-bottom" style="bottom: 10px">
<a href="#"> <a href="#">
<i class="avatar"></i> <i class="avatar"></i>
<p>{{.name}}</p> <p>{{.name}}</p>
</a> </a>
</li> </li>
</ul> </ul>
</div> </div>
</div> </div>
{{end}} {{end}}

View File

@ -1,14 +1,14 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
{{template "head" .}} {{template "head" .}}
</head> </head>
<body> <body>
<div class="wrapper"> <div class="wrapper">
{{template "sidebar" .}} {{template "sidebar" .}}
<div class="main-panel"> <div class="main-panel">
{{template "content" .}} {{template "content" .}}
</div> </div>
</div> </div>
</body> </body>
</html> </html>

View File

@ -1,16 +1,16 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
{{template "head" .}} {{template "head" .}}
</head> </head>
<body> <body>
<div class="wrapper"> <div class="wrapper">
{{template "sidebar" .}} {{template "sidebar" .}}
<div class="main-panel"> <div class="main-panel">
{{template "navbar" .}} {{template "navbar" .}}
{{template "content" .}} {{template "content" .}}
</div> </div>
</div> </div>
</body> </body>
</html> </html>

View File

@ -1,115 +1,115 @@
{{define "content"}} {{define "content"}}
<div class="content"> <div class="content">
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<h4 class="card-title">Blacklisted Users</h4> <h4 class="card-title">Blacklisted Users</h4>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="card-body table-responsive"> <div class="card-body table-responsive">
<div id="accordion"> <div id="accordion">
<div class="card"> <div class="card">
<div class="card-header collapsed filterCard" id="addBlacklistHeader" data-toggle="collapse" data-target="#addBlacklist" aria-expanded="false" aria-controls="addBlacklist"> <div class="card-header collapsed filterCard" id="addBlacklistHeader" data-toggle="collapse" data-target="#addBlacklist" aria-expanded="false" aria-controls="addBlacklist">
<span class="align-middle" data-toggle="collapse" data-target="#addBlacklist" aria-expanded="false" aria-controls="addBlacklist"> <span class="align-middle" data-toggle="collapse" data-target="#addBlacklist" aria-expanded="false" aria-controls="addBlacklist">
<i class="fas fa-plus"></i> Add New User <i class="fas fa-plus"></i> Add New User
</span> </span>
</div> </div>
<div id="addBlacklist" class="collapse" aria-labelledby="addBlacklistHeader" data-parent="#accordion"> <div id="addBlacklist" class="collapse" aria-labelledby="addBlacklistHeader" data-parent="#accordion">
<div class="card-body"> <div class="card-body">
<form> <form>
<div class="row"> <div class="row">
<div class="col-md-3 pr-1"> <div class="col-md-3 pr-1">
<div class="form-group"> <div class="form-group">
<label>Username</label> <label>Username</label>
<input name="username" type="text" class="form-control" placeholder="Username"> <input name="username" type="text" class="form-control" placeholder="Username">
</div> </div>
</div> </div>
<div class="col-md-1 px-1"> <div class="col-md-1 px-1">
<label>Discriminator</label> <label>Discriminator</label>
<div class="input-group mb-3"> <div class="input-group mb-3">
<div class="input-group-prepend"> <div class="input-group-prepend">
<div class="input-group-text">#</div> <div class="input-group-text">#</div>
</div> </div>
<input name="discrim" type="text" class="form-control" placeholder="0000"> <input name="discrim" type="text" class="form-control" placeholder="0000">
</div> </div>
</div> </div>
</div> </div>
<input name="csrf" type="hidden" value="{{.csrf}}"> <input name="csrf" type="hidden" value="{{.csrf}}">
<div class="row"> <div class="row">
<div class="col-md-2"> <div class="col-md-2">
<div class="form-group"> <div class="form-group">
<button type="submit" class="btn btn-primary mx-auto"><i class="fas fa-paper-plane"></i> Submit</button> <button type="submit" class="btn btn-primary mx-auto"><i class="fas fa-paper-plane"></i> Submit</button>
</div> </div>
</div> </div>
</div> </div>
</form> </form>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<table class="table table-hover table-striped"> <table class="table table-hover table-striped">
<thead> <thead>
<tr> <tr>
<th>User ID</th> <th>User ID</th>
<th>Username#Discrim</th> <th>Username#Discrim</th>
<th>Remove</th> <th>Remove</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{{range .blacklisted}} {{range .blacklisted}}
<tr> <tr>
<td>{{.userId}}</td> <td>{{.userId}}</td>
<td>{{.username}}#{{.discrim}}</td> <td>{{.username}}#{{.discrim}}</td>
<td><a href="/manage/{{$.guildId}}/blacklist/remove/{{.userId}}?c={{$.csrf}}">Remove</a></td> <td><a href="/manage/{{$.guildId}}/blacklist/remove/{{.userId}}?c={{$.csrf}}">Remove</a></td>
</tr> </tr>
{{end}} {{end}}
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div aria-live="polite" aria-atomic="true" style="position: relative; min-height: 200px;"> <div aria-live="polite" aria-atomic="true" style="position: relative; min-height: 200px;">
<div style="position: absolute; right: 10px; min-width: 300px"> <div style="position: absolute; right: 10px; min-width: 300px">
{{if .userNotFound}} {{if .userNotFound}}
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false"> <div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false">
<div class="toast-header"> <div class="toast-header">
<strong class="mr-auto">Warning</strong> <strong class="mr-auto">Warning</strong>
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close"> <button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="toast-body"> <div class="toast-body">
The user you specified couldn't be found The user you specified couldn't be found
</div> </div>
</div> </div>
{{end}} {{end}}
{{if .isStaff}} {{if .isStaff}}
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false"> <div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false">
<div class="toast-header"> <div class="toast-header">
<strong class="mr-auto">Warning</strong> <strong class="mr-auto">Warning</strong>
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close"> <button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="toast-body"> <div class="toast-body">
You cannot blacklist a staff member You cannot blacklist a staff member
</div> </div>
</div> </div>
{{end}} {{end}}
</div> </div>
</div> </div>
<script> <script>
$('.toast').toast('show'); $('.toast').toast('show');
</script> </script>
</div> </div>
{{end}} {{end}}

View File

@ -1,46 +1,46 @@
{{define "content"}} {{define "content"}}
<div class="content"> <div class="content">
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<h4 class="card-title">Servers</h4> <h4 class="card-title">Servers</h4>
{{if .empty}} {{if .empty}}
<p class="card-category">Select a server to manage below</p> <p class="card-category">Select a server to manage below</p>
{{end}} {{end}}
</div> </div>
<div class="card-body"> <div class="card-body">
{{if .empty}} {{if .empty}}
<p class="center-align" style="padding-top: 50px; font-size: 16px"> <p class="center-align" style="padding-top: 50px; font-size: 16px">
You are not the admin of any guilds that the bot is in. Click below to invite the bot: You are not the admin of any guilds that the bot is in. Click below to invite the bot:
<br/> <br/>
<a href="https://invite.ticketsbot.net">Invite</a> <a href="https://invite.ticketsbot.net">Invite</a>
</p> </p>
{{else}} {{else}}
<div class="card-body table-responsive"> <div class="card-body table-responsive">
<table class="table table-hover table-striped"> <table class="table table-hover table-striped">
<thead> <thead>
<th>Server Name</th> <th>Server Name</th>
</thead> </thead>
<tbody> <tbody>
{{range .servers}} {{range .servers}}
<tr> <tr>
<td> <td>
<a href="/manage/{{.Id}}/settings"> <a href="/manage/{{.Id}}/settings">
{{.Name}} {{.Name}}
</a> </a>
</td> </td>
</tr> </tr>
{{end}} {{end}}
</tbody> </tbody>
</table> </table>
</div> </div>
{{end}} {{end}}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{{end}} {{end}}

View File

@ -1,100 +1,100 @@
{{define "content"}} {{define "content"}}
<div class="content"> <div class="content">
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<div id="accordion"> <div id="accordion">
<div class="card"> <div class="card">
<div class="card-header collapsed filterCard" id="filterHeader" data-toggle="collapse" data-target="#filterLogs" aria-expanded="false" aria-controls="filterLogs"> <div class="card-header collapsed filterCard" id="filterHeader" data-toggle="collapse" data-target="#filterLogs" aria-expanded="false" aria-controls="filterLogs">
<span class="align-middle" data-toggle="collapse" data-target="#filterLogs" aria-expanded="false" aria-controls="filterLogs"> <span class="align-middle" data-toggle="collapse" data-target="#filterLogs" aria-expanded="false" aria-controls="filterLogs">
<i class="fas fa-search"></i> Filter Logs <i class="fas fa-search"></i> Filter Logs
</span> </span>
</div> </div>
<div id="filterLogs" class="collapse" aria-labelledby="filterHeader" data-parent="#accordion"> <div id="filterLogs" class="collapse" aria-labelledby="filterHeader" data-parent="#accordion">
<div class="card-body"> <div class="card-body">
<form action="/manage/{{.guildId}}/logs/page/1"> <form action="/manage/{{.guildId}}/logs/page/1">
<div class="row"> <div class="row">
<div class="col-md-4 pr-1"> <div class="col-md-4 pr-1">
<div class="form-group"> <div class="form-group">
<label>Ticket ID</label> <label>Ticket ID</label>
<input name="ticketid" type="text" class="form-control" placeholder="Ticket ID"> <input name="ticketid" type="text" class="form-control" placeholder="Ticket ID">
</div> </div>
</div> </div>
<div class="col-md-4 px-1"> <div class="col-md-4 px-1">
<div class="form-group"> <div class="form-group">
<label>Username</label> <label>Username</label>
<input name="username" type="text" class="form-control" placeholder="Username"> <input name="username" type="text" class="form-control" placeholder="Username">
</div> </div>
</div> </div>
<div class="col-md-4 px-1"> <div class="col-md-4 px-1">
<div class="form-group"> <div class="form-group">
<label>User ID</label> <label>User ID</label>
<input name="userid" type="text" class="form-control" placeholder="User ID"> <input name="userid" type="text" class="form-control" placeholder="User ID">
</div> </div>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-2"> <div class="col-md-2">
<div class="form-group"> <div class="form-group">
<button type="submit" class="btn btn-primary mx-auto"><i class="fas fa-paper-plane"></i> Filter</button> <button type="submit" class="btn btn-primary mx-auto"><i class="fas fa-paper-plane"></i> Filter</button>
</div> </div>
</div> </div>
</div> </div>
</form> </form>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<h4 class="card-title">Logs</h4> <h4 class="card-title">Logs</h4>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="card-body table-responsive"> <div class="card-body table-responsive">
<table class="table table-hover table-striped"> <table class="table table-hover table-striped">
<thead> <thead>
<tr> <tr>
<th>Ticket ID</th> <th>Ticket ID</th>
<th>Username</th> <th>Username</th>
<th>User ID</th> <th>User ID</th>
<th>Log URL</th> <th>Log URL</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{{range .logs}} {{range .logs}}
<tr> <tr>
<td>{{.ticketid}}</td> <td>{{.ticketid}}</td>
<td>{{.username}}</td> <td>{{.username}}</td>
<td>{{.userid}}</td> <td>{{.userid}}</td>
<td><a href="/manage/{{$.guildId}}/logs/view/{{.uuid}}">{{.uuid}}</a></td> <td><a href="/manage/{{$.guildId}}/logs/view/{{.uuid}}">{{.uuid}}</a></td>
</tr> </tr>
{{end}} {{end}}
</tbody> </tbody>
</table> </table>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<ul class="pagination justify-content-center"> <ul class="pagination justify-content-center">
{{if .isPageOne}} {{if .isPageOne}}
<li class="disabled"><a href="#"><i class="fas fa-chevron-left"></i></a></li> <li class="disabled"><a href="#"><i class="fas fa-chevron-left"></i></a></li>
{{else}} {{else}}
<li class="waves-effect"><a href="/manage/{{.guildId}}/logs/page/{{.previousPage}}"><i class="fas fa-chevron-left"></i></a></li> <li class="waves-effect"><a href="/manage/{{.guildId}}/logs/page/{{.previousPage}}"><i class="fas fa-chevron-left"></i></a></li>
{{end}} {{end}}
<p class="center-align" style="padding-left: 10px; padding-right: 10px;">Page {{.page}}</p> <p class="center-align" style="padding-left: 10px; padding-right: 10px;">Page {{.page}}</p>
<li class="waves-effect"><a href="/manage/{{.guildId}}/logs/page/{{.nextPage}}"><i class="fas fa-chevron-right"></i></a></li> <li class="waves-effect"><a href="/manage/{{.guildId}}/logs/page/{{.nextPage}}"><i class="fas fa-chevron-right"></i></a></li>
</ul> </ul>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{{end}} {{end}}

View File

@ -1,235 +1,235 @@
{{define "content"}} {{define "content"}}
<div class="content"> <div class="content">
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<h4 class="card-title">Reaction Panels</h4> <h4 class="card-title">Reaction Panels</h4>
</div> </div>
<div class="card-body"> <div class="card-body">
<p>Your panel quota: <b>{{.panelcount}} / {{if .premium}}∞{{else}}1{{end}}</b></p> <p>Your panel quota: <b>{{.panelcount}} / {{if .premium}}∞{{else}}1{{end}}</b></p>
{{if not .premium}} {{if not .premium}}
<p>Note: You can expand your panel quote by purchasing premium</p> <p>Note: You can expand your panel quote by purchasing premium</p>
{{end}} {{end}}
<table class="table table-hover table-striped"> <table class="table table-hover table-striped">
<thead> <thead>
<tr> <tr>
<th>Channel</th> <th>Channel</th>
<th>Panel Title</th> <th>Panel Title</th>
<th>Panel Content</th> <th>Panel Content</th>
<th>Ticket Channel Category</th> <th>Ticket Channel Category</th>
<th>Delete</th> <th>Delete</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{{range .panels}} {{range .panels}}
<tr> <tr>
<td>#{{.ChannelName}}</td> <td>#{{.ChannelName}}</td>
<td>{{.Title}}</td> <td>{{.Title}}</td>
<td>{{.Content}}</td> <td>{{.Content}}</td>
<td>{{.CategoryName}}</td> <td>{{.CategoryName}}</td>
<td><a href="/manage/{{$.guildId}}/panels/delete/{{.MessageId}}?csrf={{$.csrf}}">Delete</a></td> <td><a href="/manage/{{$.guildId}}/panels/delete/{{.MessageId}}?csrf={{$.csrf}}">Delete</a></td>
</tr> </tr>
{{end}} {{end}}
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<h4 class="card-title">Create A Panel</h4> <h4 class="card-title">Create A Panel</h4>
</div> </div>
<div class="card-body"> <div class="card-body">
<form method="post" action="/manage/{{.guildId}}/panels/create"> <form method="post" action="/manage/{{.guildId}}/panels/create">
<div class="row"> <div class="row">
<div class="col-md-4 pr-1"> <div class="col-md-4 pr-1">
<div class="form-group"> <div class="form-group">
<label class="black">Panel Title</label> <label class="black">Panel Title</label>
<input name="title" type="text" class="form-control" placeholder="Open a ticket!"> <input name="title" type="text" class="form-control" placeholder="Open a ticket!">
</div> </div>
</div> </div>
<div class="col-md-8 pr-1"> <div class="col-md-8 pr-1">
<div class="form-group"> <div class="form-group">
<label class="black">Panel Content</label> <label class="black">Panel Content</label>
<textarea name="content" type="text" class="form-control" <textarea name="content" type="text" class="form-control"
placeholder="By reacting to this ticket, a ticket will be opened for you."></textarea> placeholder="By reacting to this ticket, a ticket will be opened for you."></textarea>
</div> </div>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-3 pr-1"> <div class="col-md-3 pr-1">
<label class="black">Panel Colour</label> <label class="black">Panel Colour</label>
<div class="input-group mb-3"> <div class="input-group mb-3">
<input name="colour" type="color" class="form-control input-fill" value="#23A31A"> <input name="colour" type="color" class="form-control input-fill" value="#23A31A">
</div> </div>
</div> </div>
<div class="col-md-3 pr-1"> <div class="col-md-3 pr-1">
<label class="black">Panel Channel</label> <label class="black">Panel Channel</label>
<div class="input-group mb-3"> <div class="input-group mb-3">
<div class="input-group-prepend"> <div class="input-group-prepend">
<div class="input-group-text">#</div> <div class="input-group-text">#</div>
</div> </div>
<select class="form-control" name="channel"> <select class="form-control" name="channel">
{{range $id, $name := .channels}} {{range $id, $name := .channels}}
<option value="{{$id}}">{{$name}}</option> <option value="{{$id}}">{{$name}}</option>
{{end}} {{end}}
</select> </select>
</div> </div>
</div> </div>
<div class="col-md-3 pr-1"> <div class="col-md-3 pr-1">
<label class="black">Ticket Channel Category</label> <label class="black">Ticket Channel Category</label>
<div class="input-group mb-3"> <div class="input-group mb-3">
<div class="input-group-prepend"> <div class="input-group-prepend">
<div class="input-group-text">#</div> <div class="input-group-text">#</div>
</div> </div>
<select class="form-control" name="categories"> <select class="form-control" name="categories">
{{range $id, $name := .categories}} {{range $id, $name := .categories}}
<option value="{{$id}}">{{$name}}</option> <option value="{{$id}}">{{$name}}</option>
{{end}} {{end}}
</select> </select>
</div> </div>
</div> </div>
<div class="col-md-3 pr-1"> <div class="col-md-3 pr-1">
<div class="form-group"> <div class="form-group">
<label class="black">Reaction Emote</label> <label class="black">Reaction Emote</label>
<input name="reaction" type="text" class="form-control" placeholder="envelope_with_arrow"> <input name="reaction" type="text" class="form-control" placeholder="envelope_with_arrow">
</div> </div>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<input name="csrf" type="hidden" value="{{.csrf}}"> <input name="csrf" type="hidden" value="{{.csrf}}">
<div class="col-md-2 pr-1 offset-md-5"> <div class="col-md-2 pr-1 offset-md-5">
<div class="form-group"> <div class="form-group">
<button type="submit" class="btn btn-primary"><i class="fas fa-paper-plane"></i> Submit</button> <button type="submit" class="btn btn-primary"><i class="fas fa-paper-plane"></i> Submit</button>
</div> </div>
</div> </div>
</div> </div>
</form> </form>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div aria-live="polite" aria-atomic="true" style="position: relative; min-height: 200px;"> <div aria-live="polite" aria-atomic="true" style="position: relative; min-height: 200px;">
<div style="position: absolute; right: 10px; min-width: 300px"> <div style="position: absolute; right: 10px; min-width: 300px">
{{if not .validTitle}} {{if not .validTitle}}
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false"> <div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false">
<div class="toast-header"> <div class="toast-header">
<strong class="mr-auto">Warning</strong> <strong class="mr-auto">Warning</strong>
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close"> <button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="toast-body"> <div class="toast-body">
Panel titles must be between 1 and 255 characters long Panel titles must be between 1 and 255 characters long
</div> </div>
</div> </div>
{{end}} {{end}}
{{if not .validContent}} {{if not .validContent}}
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false"> <div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false">
<div class="toast-header"> <div class="toast-header">
<strong class="mr-auto">Warning</strong> <strong class="mr-auto">Warning</strong>
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close"> <button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="toast-body"> <div class="toast-body">
Panel content must be between 1 and 1024 characters long Panel content must be between 1 and 1024 characters long
</div> </div>
</div> </div>
{{end}} {{end}}
{{if not .validColour}} {{if not .validColour}}
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false"> <div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false">
<div class="toast-header"> <div class="toast-header">
<strong class="mr-auto">Warning</strong> <strong class="mr-auto">Warning</strong>
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close"> <button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="toast-body"> <div class="toast-body">
Invalid panel colour. You must use the hex value of the colour, which you can find <a href="https://www.google.co.uk/search?client=opera&q=html+colour+picker">here</a>. Invalid panel colour. You must use the hex value of the colour, which you can find <a href="https://www.google.co.uk/search?client=opera&q=html+colour+picker">here</a>.
<br />Colour has defaulted to green. <br />Colour has defaulted to green.
</div> </div>
</div> </div>
{{end}} {{end}}
{{if not .validChannel}} {{if not .validChannel}}
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false"> <div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false">
<div class="toast-header"> <div class="toast-header">
<strong class="mr-auto">Warning</strong> <strong class="mr-auto">Warning</strong>
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close"> <button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="toast-body"> <div class="toast-body">
Invalid channel - please try again Invalid channel - please try again
</div> </div>
</div> </div>
{{end}} {{end}}
{{if not .validCategory}} {{if not .validCategory}}
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false"> <div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false">
<div class="toast-header"> <div class="toast-header">
<strong class="mr-auto">Warning</strong> <strong class="mr-auto">Warning</strong>
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close"> <button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="toast-body"> <div class="toast-body">
Invalid category - please try again Invalid category - please try again
</div> </div>
</div> </div>
{{end}} {{end}}
{{if not .validReaction}} {{if not .validReaction}}
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false"> <div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false">
<div class="toast-header"> <div class="toast-header">
<strong class="mr-auto">Warning</strong> <strong class="mr-auto">Warning</strong>
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close"> <button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="toast-body"> <div class="toast-body">
Invalid reaction emote Invalid reaction emote
</div> </div>
</div> </div>
{{end}} {{end}}
{{if .created}} {{if .created}}
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false"> <div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false">
<div class="toast-header"> <div class="toast-header">
<strong class="mr-auto">Success</strong> <strong class="mr-auto">Success</strong>
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close"> <button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="toast-body"> <div class="toast-body">
Your panel has been created. You may need to refresh this page to see it displayed. Your panel has been created. You may need to refresh this page to see it displayed.
</div> </div>
</div> </div>
{{end}} {{end}}
{{if .metQuota}} {{if .metQuota}}
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false"> <div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false">
<div class="toast-header"> <div class="toast-header">
<strong class="mr-auto">Warning</strong> <strong class="mr-auto">Warning</strong>
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close"> <button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="toast-body"> <div class="toast-body">
You've hit your panel quota. Premium users can create <b>unlimited panels</b>. Click <a href="https://ticketsbot.net/premium">here</a> to learn more about premium. You've hit your panel quota. Premium users can create <b>unlimited panels</b>. Click <a href="https://ticketsbot.net/premium">here</a> to learn more about premium.
</div> </div>
</div> </div>
{{end}} {{end}}
</div> </div>
</div> </div>
<script> <script>
$('.toast').toast('show'); $('.toast').toast('show');
</script> </script>
</div> </div>
{{end}} {{end}}

View File

@ -1,159 +1,159 @@
{{define "content"}} {{define "content"}}
<div class="content"> <div class="content">
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<h4 class="card-title">Settings</h4> <h4 class="card-title">Settings</h4>
{{if .empty}} {{if .empty}}
<p class="card-category">Select a server to manage below</p> <p class="card-category">Select a server to manage below</p>
{{end}} {{end}}
</div> </div>
<div class="card-body"> <div class="card-body">
<form method="post"> <form method="post">
<div class="row"> <div class="row">
<div class="col-md-5 pr-1"> <div class="col-md-5 pr-1">
<div class="form-group"> <div class="form-group">
<label>Prefix (Max len. 8)</label> <label>Prefix (Max len. 8)</label>
<input name="prefix" type="text" class="form-control" placeholder="t!" value="{{.prefix}}"> <input name="prefix" type="text" class="form-control" placeholder="t!" value="{{.prefix}}">
</div> </div>
</div> </div>
<div class="col-md-5 px-1"> <div class="col-md-5 px-1">
<div class="form-group"> <div class="form-group">
<label>Ticket Limit (1-10)</label> <label>Ticket Limit (1-10)</label>
<input name="ticketlimit" type="text" class="form-control" placeholder="5" value="{{.ticketLimit}}"> <input name="ticketlimit" type="text" class="form-control" placeholder="5" value="{{.ticketLimit}}">
</div> </div>
</div> </div>
<div class="col-md-2 px-1"> <div class="col-md-2 px-1">
<div class="form-group"> <div class="form-group">
<label>Ping @everyone on ticket open</label> <label>Ping @everyone on ticket open</label>
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="checkbox" name="pingeveryone" value="on" {{if .pingEveryone}}checked{{end}} style="width:30px;height:30px;"> <input class="form-check-input" type="checkbox" name="pingeveryone" value="on" {{if .pingEveryone}}checked{{end}} style="width:30px;height:30px;">
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<label>Welcome Message (Max len. 1000)</label> <label>Welcome Message (Max len. 1000)</label>
<textarea name="welcomeMessage" class="form-control" rows="3" style="resize: none">{{.welcomeMessage}}</textarea> <textarea name="welcomeMessage" class="form-control" rows="3" style="resize: none">{{.welcomeMessage}}</textarea>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-5 pr-1"> <div class="col-md-5 pr-1">
<label>Archive Channel</label> <label>Archive Channel</label>
<div class="input-group mb-3"> <div class="input-group mb-3">
<div class="input-group-prepend"> <div class="input-group-prepend">
<div class="input-group-text">#</div> <div class="input-group-text">#</div>
</div> </div>
<select class="form-control" name="archivechannel"> <select class="form-control" name="archivechannel">
{{range .channels}} {{range .channels}}
<option {{if eq .ChannelId $.archivechannel }}selected{{end}} value="{{.ChannelId}}">{{.Name}}</option> <option {{if eq .ChannelId $.archivechannel }}selected{{end}} value="{{.ChannelId}}">{{.Name}}</option>
{{end}} {{end}}
</select> </select>
</div> </div>
</div> </div>
<div class="col-md-5 px-1"> <div class="col-md-5 px-1">
<div class="form-group"> <div class="form-group">
<label>Channel Category</label> <label>Channel Category</label>
<select class="form-control" name="category"> <select class="form-control" name="category">
{{range .categories}} {{range .categories}}
<option {{if eq $.activecategory .ChannelId}}selected{{end}} value="{{.ChannelId}}">{{.Name}}</option> <option {{if eq $.activecategory .ChannelId}}selected{{end}} value="{{.ChannelId}}">{{.Name}}</option>
{{end}} {{end}}
</select> </select>
</div> </div>
</div> </div>
<div class="col-md-2 px-1"> <div class="col-md-2 px-1">
<div class="form-group"> <div class="form-group">
<label>Allow users to close tickets</label> <label>Allow users to close tickets</label>
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="checkbox" name="userscanclose" value="on" {{if .usersCanClose}}checked{{end}} style="width:30px;height:30px;"> <input class="form-check-input" type="checkbox" name="userscanclose" value="on" {{if .usersCanClose}}checked{{end}} style="width:30px;height:30px;">
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<label>Ticket Naming Scheme</label> <label>Ticket Naming Scheme</label>
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="radio" name="namingscheme" id="naming-by-id" value="id" {{if eq .namingScheme "id"}}checked{{end}}> <input class="form-check-input" type="radio" name="namingscheme" id="naming-by-id" value="id" {{if eq .namingScheme "id"}}checked{{end}}>
<label class="form-check-label" for="naming-by-id"> <label class="form-check-label" for="naming-by-id">
Ticket ID Ticket ID
</label> </label>
</div> </div>
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="radio" name="namingscheme" id="naming-by-username" value="username" {{if eq .namingScheme "username"}}checked{{end}}> <input class="form-check-input" type="radio" name="namingscheme" id="naming-by-username" value="username" {{if eq .namingScheme "username"}}checked{{end}}>
<label class="form-check-label" for="naming-by-username"> <label class="form-check-label" for="naming-by-username">
Username Username
</label> </label>
</div> </div>
<input name="csrf" type="hidden" value="{{.csrf}}"> <input name="csrf" type="hidden" value="{{.csrf}}">
<div class="row"> <div class="row">
<div class="col-md-1 pr-1"> <div class="col-md-1 pr-1">
<div class="form-group"> <div class="form-group">
<button type="submit" class="btn btn-primary"><i class="fas fa-paper-plane"></i> Submit</button> <button type="submit" class="btn btn-primary"><i class="fas fa-paper-plane"></i> Submit</button>
</div> </div>
</div> </div>
</div> </div>
</form> </form>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div aria-live="polite" aria-atomic="true" style="position: relative; min-height: 200px;"> <div aria-live="polite" aria-atomic="true" style="position: relative; min-height: 200px;">
<div style="position: absolute; right: 10px; min-width: 300px"> <div style="position: absolute; right: 10px; min-width: 300px">
{{if .invalidPrefix}} {{if .invalidPrefix}}
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false"> <div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false">
<div class="toast-header"> <div class="toast-header">
<strong class="mr-auto">Warning</strong> <strong class="mr-auto">Warning</strong>
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close"> <button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="toast-body"> <div class="toast-body">
The prefix you specified was invalid The prefix you specified was invalid
</div> </div>
</div> </div>
{{end}} {{end}}
{{if .invalidWelcomeMessage}} {{if .invalidWelcomeMessage}}
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false"> <div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false">
<div class="toast-header"> <div class="toast-header">
<strong class="mr-auto">Warning</strong> <strong class="mr-auto">Warning</strong>
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close"> <button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="toast-body"> <div class="toast-body">
The welcome message you specified was invalid The welcome message you specified was invalid
</div> </div>
</div> </div>
{{end}} {{end}}
{{if .invalidTicketLimit}} {{if .invalidTicketLimit}}
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false"> <div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false">
<div class="toast-header"> <div class="toast-header">
<strong class="mr-auto">Warning</strong> <strong class="mr-auto">Warning</strong>
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close"> <button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="toast-body"> <div class="toast-body">
The ticket limit you specified was invalid The ticket limit you specified was invalid
</div> </div>
</div> </div>
{{end}} {{end}}
</div> </div>
</div> </div>
<script> <script>
$('.toast').toast('show'); $('.toast').toast('show');
$('#pingeveryone').prop('indeterminate', {{.pingEveryone}}); $('#pingeveryone').prop('indeterminate', {{.pingEveryone}});
</script> </script>
</div> </div>
{{end}} {{end}}

View File

@ -1,43 +1,43 @@
{{define "content"}} {{define "content"}}
<div class="content"> <div class="content">
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<h4 class="card-title">Ticket List</h4> <h4 class="card-title">Ticket List</h4>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="card-body table-responsive"> <div class="card-body table-responsive">
<table class="table table-hover table-striped"> <table class="table table-hover table-striped">
<thead> <thead>
<tr> <tr>
<th>Ticket ID</th> <th>Ticket ID</th>
<th>User</th> <th>User</th>
<th>Additional Members</th> <th>Additional Members</th>
<th>View</th> <th>View</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{{range .tickets}} {{range .tickets}}
<tr> <tr>
<td>{{.ticketId}}</td> <td>{{.ticketId}}</td>
<td>{{.username}}#{{.discrim}}</td> <td>{{.username}}#{{.discrim}}</td>
<td>{{range .members}}{{.username}}#{{.discrim}}{{.sep}}{{end}}</td> <td>{{range .members}}{{.username}}#{{.discrim}}{{.sep}}{{end}}</td>
<td><a class="btn btn-primary btn-sm" role="button" href="/manage/{{$.guildId}}/tickets/view/{{.uuid}}">View</a></td> <td><a class="btn btn-primary btn-sm" role="button" href="/manage/{{$.guildId}}/tickets/view/{{.uuid}}">View</a></td>
</tr> </tr>
{{end}} {{end}}
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<script> <script>
$('.toast').toast('show'); $('.toast').toast('show');
</script> </script>
</div> </div>
{{end}} {{end}}

View File

@ -1,122 +1,122 @@
{{define "content"}} {{define "content"}}
<div class="content"> <div class="content">
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h4 class="card-title">Close Ticket</h4> <h4 class="card-title">Close Ticket</h4>
<div class="close-container"> <div class="close-container">
<form class="form-inline" action="/manage/{{.guildId}}/tickets/view/{{.uuid}}/close" method="post"> <form class="form-inline" action="/manage/{{.guildId}}/tickets/view/{{.uuid}}/close" method="post">
<input type="text" class="form-control" id="reason" name="reason" placeholder="No reason specified" style="width: 80%"> <input type="text" class="form-control" id="reason" name="reason" placeholder="No reason specified" style="width: 80%">
<input name="csrf" type="hidden" value="{{.csrf}}"> <input name="csrf" type="hidden" value="{{.csrf}}">
<div style="padding-left: 10px"> <div style="padding-left: 10px">
<button type="submit" class="btn btn-primary">Close Ticket</button> <button type="submit" class="btn btn-primary">Close Ticket</button>
</div> </div>
</form> </form>
</div> </div>
<h4 class="card-title">View Ticket</h4> <h4 class="card-title">View Ticket</h4>
<div class="discord-container"> <div class="discord-container">
<div class="channel-header"> <div class="channel-header">
<span class="channel-name">#ticket-{{.ticketId}}</span> <span class="channel-name">#ticket-{{.ticketId}}</span>
</div> </div>
<div id="message-container"> <div id="message-container">
{{range .messages}} {{range .messages}}
<div class="message"> <div class="message">
<b>{{.username}}</b> <b>{{.username}}</b>
{{.content}} {{.content}}
</div> </div>
{{end}} {{end}}
</div> </div>
<div class="input-container"> <div class="input-container">
<form action="javascript:sendMessage()"> <form action="javascript:sendMessage()">
{{if .premium}} {{if .premium}}
<input type="text" class="form-control message-input" id="message" name="message" <input type="text" class="form-control message-input" id="message" name="message"
placeholder="Message #ticket-{{.ticketId}}"> placeholder="Message #ticket-{{.ticketId}}">
{{else}} {{else}}
<input type="text" class="form-control message-input" id="message" name="message" <input type="text" class="form-control message-input" id="message" name="message"
placeholder="Premium users get live messages and can respond through webchat" disabled> placeholder="Premium users get live messages and can respond through webchat" disabled>
{{end}} {{end}}
</form> </form>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div aria-live="polite" aria-atomic="true" style="position: relative; min-height: 200px;"> <div aria-live="polite" aria-atomic="true" style="position: relative; min-height: 200px;">
<div style="position: absolute; right: 10px; min-width: 300px"> <div style="position: absolute; right: 10px; min-width: 300px">
{{if .isError}} {{if .isError}}
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false"> <div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false">
<div class="toast-header"> <div class="toast-header">
<strong class="mr-auto">Error</strong> <strong class="mr-auto">Error</strong>
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close"> <button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="toast-body"> <div class="toast-body">
{{.error}} {{.error}}
</div> </div>
</div> </div>
{{end}} {{end}}
</div> </div>
</div> </div>
<script> <script>
$('.toast').toast('show'); $('.toast').toast('show');
</script> </script>
</div> </div>
</div> </div>
<script> <script>
// Scroll to bottom // Scroll to bottom
let container = document.getElementById("message-container"); let container = document.getElementById("message-container");
container.scrollTop = container.scrollHeight; container.scrollTop = container.scrollHeight;
</script> </script>
{{if .premium}} {{if .premium}}
<script> <script>
let ws = new WebSocket("wss://panel.ticketsbot.net/webchat"); let ws = new WebSocket("wss://panel.ticketsbot.net/webchat");
ws.onopen = () => { ws.onopen = () => {
ws.send(JSON.stringify({ ws.send(JSON.stringify({
"type": "auth", "type": "auth",
"data": { "data": {
"guild": "{{.guildId}}", "guild": "{{.guildId}}",
"ticket": "{{.ticketId}}" "ticket": "{{.ticketId}}"
} }
})); }));
}; };
ws.onmessage = (evt) => { ws.onmessage = (evt) => {
let data = JSON.parse(evt.data); let data = JSON.parse(evt.data);
let container = document.getElementById("message-container"); let container = document.getElementById("message-container");
let element = document.createElement("div"); let element = document.createElement("div");
element.className = "message"; element.className = "message";
element.innerHTML = ` element.innerHTML = `
<b>${data.username}</b> <b>${data.username}</b>
${data.content} ${data.content}
`; `;
container.appendChild(element); container.appendChild(element);
// Scroll to bottom // Scroll to bottom
container.scrollTop = container.scrollHeight; container.scrollTop = container.scrollHeight;
}; };
function sendMessage() { function sendMessage() {
let msg = document.getElementById("message").value; let msg = document.getElementById("message").value;
document.getElementById("message").value = ""; document.getElementById("message").value = "";
ws.send(JSON.stringify({ ws.send(JSON.stringify({
"type": "send", "type": "send",
"data": msg "data": msg
})) }))
} }
</script> </script>
{{end}} {{end}}
{{end}} {{end}}

View File

@ -1,119 +1,119 @@
package discord package discord
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"github.com/TicketsBot/GoPanel/config" "github.com/TicketsBot/GoPanel/config"
"github.com/pasztorpisti/qs" "github.com/pasztorpisti/qs"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"strconv" "strconv"
"time" "time"
) )
type ( type (
TokenData struct { TokenData struct {
ClientId string `qs:"client_id"` ClientId string `qs:"client_id"`
ClientSecret string `qs:"client_secret"` ClientSecret string `qs:"client_secret"`
GrantType string `qs:"grant_type"` GrantType string `qs:"grant_type"`
Code string `qs:"code"` Code string `qs:"code"`
RedirectUri string `qs:"redirect_uri"` RedirectUri string `qs:"redirect_uri"`
Scope string `qs:"scope"` Scope string `qs:"scope"`
} }
RefreshData struct { RefreshData struct {
ClientId string `qs:"client_id"` ClientId string `qs:"client_id"`
ClientSecret string `qs:"client_secret"` ClientSecret string `qs:"client_secret"`
GrantType string `qs:"grant_type"` GrantType string `qs:"grant_type"`
RefreshToken string `qs:"refresh_token"` RefreshToken string `qs:"refresh_token"`
RedirectUri string `qs:"redirect_uri"` RedirectUri string `qs:"redirect_uri"`
Scope string `qs:"scope"` Scope string `qs:"scope"`
} }
TokenResponse struct { TokenResponse struct {
AccessToken string `json:"access_token"` AccessToken string `json:"access_token"`
TokenType string `json:"token_type"` TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in"` ExpiresIn int `json:"expires_in"`
RefreshToken string `json:"refresh_token"` RefreshToken string `json:"refresh_token"`
Scope string `json:"scope"` Scope string `json:"scope"`
} }
) )
const TokenEndpoint = "https://discordapp.com/api/oauth2/token" const TokenEndpoint = "https://discordapp.com/api/oauth2/token"
func AccessToken(code string) (TokenResponse, error) { func AccessToken(code string) (TokenResponse, error) {
data := TokenData{ data := TokenData{
ClientId: strconv.Itoa(int(config.Conf.Oauth.Id)), ClientId: strconv.Itoa(int(config.Conf.Oauth.Id)),
ClientSecret: config.Conf.Oauth.Secret, ClientSecret: config.Conf.Oauth.Secret,
GrantType: "authorization_code", GrantType: "authorization_code",
Code: code, Code: code,
RedirectUri: config.Conf.Oauth.RedirectUri, RedirectUri: config.Conf.Oauth.RedirectUri,
Scope: "identify guilds", Scope: "identify guilds",
} }
res, err := tokenPost(data) res, err := tokenPost(data)
if err != nil { if err != nil {
return TokenResponse{}, err return TokenResponse{}, err
} }
var unmarshalled TokenResponse var unmarshalled TokenResponse
if err = json.Unmarshal(res, &unmarshalled); err != nil { if err = json.Unmarshal(res, &unmarshalled); err != nil {
return TokenResponse{}, err return TokenResponse{}, err
} }
return unmarshalled, nil return unmarshalled, nil
} }
func RefreshToken(refreshToken string) (TokenResponse, error) { func RefreshToken(refreshToken string) (TokenResponse, error) {
data := RefreshData{ data := RefreshData{
ClientId: strconv.Itoa(int(config.Conf.Oauth.Id)), ClientId: strconv.Itoa(int(config.Conf.Oauth.Id)),
ClientSecret: config.Conf.Oauth.Secret, ClientSecret: config.Conf.Oauth.Secret,
GrantType: "refresh_token", GrantType: "refresh_token",
RefreshToken: refreshToken, RefreshToken: refreshToken,
RedirectUri: config.Conf.Oauth.RedirectUri, RedirectUri: config.Conf.Oauth.RedirectUri,
Scope: "identify guilds", Scope: "identify guilds",
} }
res, err := tokenPost(data) res, err := tokenPost(data)
if err != nil { if err != nil {
return TokenResponse{}, err return TokenResponse{}, err
} }
var unmarshalled TokenResponse var unmarshalled TokenResponse
if err = json.Unmarshal(res, &unmarshalled); err != nil { if err = json.Unmarshal(res, &unmarshalled); err != nil {
return TokenResponse{}, err return TokenResponse{}, err
} }
return unmarshalled, nil return unmarshalled, nil
} }
func tokenPost(body ...interface{}) ([]byte, error) { func tokenPost(body ...interface{}) ([]byte, error) {
str, err := qs.Marshal(body[0]) str, err := qs.Marshal(body[0])
if err != nil { if err != nil {
return nil, err return nil, err
} }
encoded := []byte(str) encoded := []byte(str)
req, err := http.NewRequest("POST", TokenEndpoint, bytes.NewBuffer([]byte(encoded))) req, err := http.NewRequest("POST", TokenEndpoint, bytes.NewBuffer([]byte(encoded)))
if err != nil { if err != nil {
return nil, err return nil, err
} }
req.Header.Set("Content-Type", string(ApplicationFormUrlEncoded)) req.Header.Set("Content-Type", string(ApplicationFormUrlEncoded))
client := &http.Client{} client := &http.Client{}
client.Timeout = 3 * time.Second client.Timeout = 3 * time.Second
res, err := client.Do(req) res, err := client.Do(req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer res.Body.Close() defer res.Body.Close()
content, err := ioutil.ReadAll(res.Body) content, err := ioutil.ReadAll(res.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return content, nil return content, nil
} }

View File

@ -1,14 +1,14 @@
package channel package channel
import ( import (
"fmt" "fmt"
"github.com/TicketsBot/GoPanel/utils/discord" "github.com/TicketsBot/GoPanel/utils/discord"
) )
func GetChannelMessages(id int) discord.Endpoint { func GetChannelMessages(id int) discord.Endpoint {
return discord.Endpoint{ return discord.Endpoint{
RequestType: discord.GET, RequestType: discord.GET,
AuthorizationType: discord.BOT, AuthorizationType: discord.BOT,
Endpoint: fmt.Sprintf("/channels/%d/messages", id), Endpoint: fmt.Sprintf("/channels/%d/messages", id),
} }
} }

View File

@ -1,15 +1,15 @@
package guild package guild
import ( import (
"fmt" "fmt"
"github.com/TicketsBot/GoPanel/utils/discord" "github.com/TicketsBot/GoPanel/utils/discord"
"strconv" "strconv"
) )
func GetGuild(id int) discord.Endpoint { func GetGuild(id int) discord.Endpoint {
return discord.Endpoint{ return discord.Endpoint{
RequestType: discord.GET, RequestType: discord.GET,
AuthorizationType: discord.BOT, AuthorizationType: discord.BOT,
Endpoint: fmt.Sprintf("/guilds/%s", strconv.Itoa(id)), Endpoint: fmt.Sprintf("/guilds/%s", strconv.Itoa(id)),
} }
} }

View File

@ -1,14 +1,14 @@
package guild package guild
import ( import (
"fmt" "fmt"
"github.com/TicketsBot/GoPanel/utils/discord" "github.com/TicketsBot/GoPanel/utils/discord"
) )
func GetGuildChannels(id int) discord.Endpoint { func GetGuildChannels(id int) discord.Endpoint {
return discord.Endpoint{ return discord.Endpoint{
RequestType: discord.GET, RequestType: discord.GET,
AuthorizationType: discord.BOT, AuthorizationType: discord.BOT,
Endpoint: fmt.Sprintf("/guilds/%d/channels", id), Endpoint: fmt.Sprintf("/guilds/%d/channels", id),
} }
} }

View File

@ -1,9 +1,9 @@
package user package user
import "github.com/TicketsBot/GoPanel/utils/discord" import "github.com/TicketsBot/GoPanel/utils/discord"
var CurrentUser = discord.Endpoint{ var CurrentUser = discord.Endpoint{
RequestType: discord.GET, RequestType: discord.GET,
AuthorizationType: discord.BEARER, AuthorizationType: discord.BEARER,
Endpoint: "/users/@me", Endpoint: "/users/@me",
} }

View File

@ -1,9 +1,9 @@
package user package user
import "github.com/TicketsBot/GoPanel/utils/discord" import "github.com/TicketsBot/GoPanel/utils/discord"
var CurrentUserGuilds = discord.Endpoint{ var CurrentUserGuilds = discord.Endpoint{
RequestType: discord.GET, RequestType: discord.GET,
AuthorizationType: discord.BEARER, AuthorizationType: discord.BEARER,
Endpoint: "/users/@me/guilds", Endpoint: "/users/@me/guilds",
} }

View File

@ -1,16 +1,16 @@
package objects package objects
type Activity struct { type Activity struct {
Name string Name string
Type int Type int
Url string Url string
Timestamps Timestamp Timestamps Timestamp
ApplicationId string ApplicationId string
Details string Details string
State string State string
Party Party Party Party
Assets Asset Assets Asset
Secrets Secret Secrets Secret
Instance bool Instance bool
Flags int Flags int
} }

View File

@ -1,8 +1,8 @@
package objects package objects
type Asset struct { type Asset struct {
LargeImage string LargeImage string
LargeText string LargeText string
SmallImage string SmallImage string
SmallText string SmallText string
} }

View File

@ -1,11 +1,11 @@
package objects package objects
type Attachment struct { type Attachment struct {
Id string Id string
Filename string Filename string
Size int Size int
url string url string
ProxyUrl string ProxyUrl string
height int height int
Width int Width int
} }

View File

@ -1,22 +1,22 @@
package objects package objects
type Channel struct { type Channel struct {
Id string Id string
Type int Type int
GuildId string GuildId string
Position int Position int
PermissionsOverwrites []Overwrite PermissionsOverwrites []Overwrite
Name string Name string
Topic string Topic string
Nsfw bool Nsfw bool
LastMessageId string LastMessageId string
Bitrate int Bitrate int
userLimit int userLimit int
RateLimitPerUser int RateLimitPerUser int
Recipients []User Recipients []User
Icon string Icon string
Ownerid string Ownerid string
ApplicationId string ApplicationId string
ParentId string ParentId string
LastPinTimestamp string LastPinTimestamp string
} }

View File

@ -1,7 +1,7 @@
package objects package objects
type ClientStatus struct { type ClientStatus struct {
Desktop string Desktop string
Mobile string Mobile string
Web string Web string
} }

View File

@ -1,17 +1,17 @@
package objects package objects
type Embed struct { type Embed struct {
Title string Title string
Type string Type string
Description string Description string
Url string Url string
Timestamp string Timestamp string
Color int Color int
Footer EmbedField Footer EmbedField
Image EmbedImage Image EmbedImage
Thumbnail EmbedThumbnail Thumbnail EmbedThumbnail
Video EmbedVideo Video EmbedVideo
Provider EmbedProvider Provider EmbedProvider
Author EmbedAuthor Author EmbedAuthor
Fields []EmbedField Fields []EmbedField
} }

View File

@ -1,8 +1,8 @@
package objects package objects
type EmbedAuthor struct { type EmbedAuthor struct {
Name string Name string
Url string Url string
IconUrl string IconUrl string
ProxyIconUrl string ProxyIconUrl string
} }

View File

@ -1,7 +1,7 @@
package objects package objects
type EmbedField struct { type EmbedField struct {
Name string Name string
Value string Value string
Inline bool Inline bool
} }

View File

@ -1,7 +1,7 @@
package objects package objects
type EmbedFooter struct { type EmbedFooter struct {
Text string Text string
IconUrl string IconUrl string
ProxyIconUrl string ProxyIconUrl string
} }

View File

@ -1,8 +1,8 @@
package objects package objects
type EmbedImage struct { type EmbedImage struct {
Url string Url string
ProxyUrl string ProxyUrl string
Height int Height int
Width int Width int
} }

View File

@ -1,6 +1,6 @@
package objects package objects
type EmbedProvider struct { type EmbedProvider struct {
Name string Name string
Url string Url string
} }

View File

@ -1,8 +1,8 @@
package objects package objects
type EmbedThumbnail struct { type EmbedThumbnail struct {
Url string Url string
ProxyUrl string ProxyUrl string
Height int Height int
Width int Width int
} }

View File

@ -1,7 +1,7 @@
package objects package objects
type EmbedVideo struct { type EmbedVideo struct {
Url string Url string
Height int Height int
Width int Width int
} }

View File

@ -1,11 +1,11 @@
package objects package objects
type Emoji struct { type Emoji struct {
Id string Id string
Name string Name string
Roles []string Roles []string
User User User User
RequireColons bool RequireColons bool
Managed bool Managed bool
Animated bool Animated bool
} }

View File

@ -1,25 +1,25 @@
package objects package objects
type Message struct { type Message struct {
Id string Id string
ChannelId string ChannelId string
GuildId string GuildId string
Author User Author User
Member Member Member Member
Content string Content string
Timestamp string Timestamp string
EditedTimestamp string EditedTimestamp string
Tts bool Tts bool
MentionEveryone bool MentionEveryone bool
Mentions []interface{} Mentions []interface{}
MentionsRoles []int64 MentionsRoles []int64
Attachments []Attachment Attachments []Attachment
Embeds []Embed Embeds []Embed
Reactions []Reaction Reactions []Reaction
Nonce string Nonce string
Pinned bool Pinned bool
WebhookId string WebhookId string
Type int Type int
Activity MessageActivity Activity MessageActivity
Application MessageApplication Application MessageApplication
} }

View File

@ -1,6 +1,6 @@
package objects package objects
type MessageActivity struct { type MessageActivity struct {
Type int Type int
PartyId string PartyId string
} }

View File

@ -1,9 +1,9 @@
package objects package objects
type MessageApplication struct { type MessageApplication struct {
Id string Id string
CoverImage string CoverImage string
Description string Description string
Icon string Icon string
Name string Name string
} }

View File

@ -1,8 +1,8 @@
package objects package objects
type Overwrite struct { type Overwrite struct {
Id string Id string
Type string Type string
Allow int Allow int
Deny int Deny int
} }

View File

@ -1,6 +1,6 @@
package objects package objects
type Party struct { type Party struct {
Id string Id string
Size []int Size []int
} }

View File

@ -1,11 +1,11 @@
package objects package objects
type Presence struct { type Presence struct {
User User User User
Roles []string Roles []string
Game Activity Game Activity
GuildId string GuildId string
Status string Status string
Activities []Activity Activities []Activity
ClientStatus ClientStatus ClientStatus ClientStatus
} }

View File

@ -1,7 +1,7 @@
package objects package objects
type Reaction struct { type Reaction struct {
Count int Count int
Me bool Me bool
Emoji Emoji Emoji Emoji
} }

View File

@ -1,12 +1,12 @@
package objects package objects
type Role struct { type Role struct {
Id string Id string
Name string Name string
Color int Color int
Hoist bool Hoist bool
Position int Position int
Permissions int Permissions int
Managed bool Managed bool
Mentionable bool Mentionable bool
} }

View File

@ -1,7 +1,7 @@
package objects package objects
type Secret struct { type Secret struct {
Join string Join string
Spectate string Spectate string
Match string Match string
} }

View File

@ -1,6 +1,6 @@
package objects package objects
type Timestamp struct { type Timestamp struct {
Start int Start int
End int End int
} }

View File

@ -1,12 +1,12 @@
package objects package objects
type User struct { type User struct {
Id string Id string
Username string Username string
Discriminator string Discriminator string
Avatar string Avatar string
Verified bool Verified bool
Email string Email string
Flags int Flags int
PremiumType int PremiumType int
} }

View File

@ -1,14 +1,14 @@
package objects package objects
type VoiceState struct { type VoiceState struct {
GuildId string GuildId string
ChannelId string ChannelId string
UserId string UserId string
Member Member Member Member
SessionId string SessionId string
Deaf bool Deaf bool
Mute bool Mute bool
SelfDeaf bool SelfDeaf bool
SelfMute bool SelfMute bool
Suppress bool Suppress bool
} }

View File

@ -1,12 +1,12 @@
package utils package utils
import "io/ioutil" import "io/ioutil"
func ReadFile(path string) (string, error) { func ReadFile(path string) (string, error) {
content, err := ioutil.ReadFile(path) content, err := ioutil.ReadFile(path)
if err != nil { if err != nil {
return "", err return "", err
} }
return string(content), nil return string(content), nil
} }

View File

@ -1,20 +1,20 @@
package utils package utils
import ( import (
"github.com/gin-gonic/contrib/sessions" "github.com/gin-gonic/contrib/sessions"
"strconv" "strconv"
) )
func IsLoggedIn(store sessions.Session) bool { func IsLoggedIn(store sessions.Session) bool {
return store.Get("access_token") != nil && return store.Get("access_token") != nil &&
store.Get("expiry") != nil && store.Get("expiry") != nil &&
store.Get("refresh_token") != nil && store.Get("refresh_token") != nil &&
store.Get("userid") != nil && store.Get("userid") != nil &&
store.Get("name") != nil && store.Get("name") != nil &&
store.Get("avatar") != nil && store.Get("avatar") != nil &&
store.Get("csrf") != nil store.Get("csrf") != nil
} }
func GetUserId(store sessions.Session) (int64, error) { func GetUserId(store sessions.Session) (int64, error) {
return strconv.ParseInt(store.Get("userid").(string), 10, 64) return strconv.ParseInt(store.Get("userid").(string), 10, 64)
} }

View File

@ -1,42 +1,42 @@
package utils package utils
import ( import (
"github.com/TicketsBot/GoPanel/utils/discord/objects" "github.com/TicketsBot/GoPanel/utils/discord/objects"
"reflect" "reflect"
) )
func Contains(s interface{}, elem interface{}) bool { func Contains(s interface{}, elem interface{}) bool {
arrV := reflect.ValueOf(s) arrV := reflect.ValueOf(s)
if arrV.Kind() == reflect.Slice { if arrV.Kind() == reflect.Slice {
for i := 0; i < arrV.Len(); i++ { for i := 0; i < arrV.Len(); i++ {
// XXX - panics if slice element points to an unexported struct field // XXX - panics if slice element points to an unexported struct field
// see https://golang.org/pkg/reflect/#Value.Interface // see https://golang.org/pkg/reflect/#Value.Interface
if arrV.Index(i).Interface() == elem { if arrV.Index(i).Interface() == elem {
return true return true
} }
} }
} }
return false return false
} }
func Insert(slice []objects.Guild, index int, value objects.Guild) []objects.Guild { func Insert(slice []objects.Guild, index int, value objects.Guild) []objects.Guild {
// Grow the slice by one element. // Grow the slice by one element.
slice = slice[0 : len(slice)+1] slice = slice[0 : len(slice)+1]
// Use copy to move the upper part of the slice out of the way and open a hole. // Use copy to move the upper part of the slice out of the way and open a hole.
copy(slice[index+1:], slice[index:]) copy(slice[index+1:], slice[index:])
// Store the new value. // Store the new value.
slice[index] = value slice[index] = value
// Return the result. // Return the result.
return slice return slice
} }
func Reverse(slice []objects.Message) []objects.Message { func Reverse(slice []objects.Message) []objects.Message {
for i := len(slice)/2-1; i >= 0; i-- { for i := len(slice)/2-1; i >= 0; i-- {
opp := len(slice)-1-i opp := len(slice)-1-i
slice[i], slice[opp] = slice[opp], slice[i] slice[i], slice[opp] = slice[opp], slice[i]
} }
return slice return slice
} }