From 1faf4b9aa1e4e6a5b0d5da51f2507f900258c181 Mon Sep 17 00:00:00 2001 From: Brian Buller Date: Thu, 15 Jun 2017 12:35:53 -0500 Subject: [PATCH] Working on editing team games --- admin_clients.go | 28 ++++++ admin_games.go | 68 ++++++++++--- admin_teams.go | 10 +- assets/css/gjvote.css | 104 +++++++++++++++++++- assets/js/gjvote.js | 48 +++++++++ main.go | 4 +- model_games.go | 143 ++++++++++++++++++++++++++- model_teams.go | 2 +- templates/admin-editteam.html | 178 +++++++++++++++++++++++----------- templates/admin-menu.html | 12 ++- templates/header.html | 8 +- 11 files changed, 518 insertions(+), 87 deletions(-) diff --git a/admin_clients.go b/admin_clients.go index 19f7504..666e6fe 100644 --- a/admin_clients.go +++ b/admin_clients.go @@ -55,3 +55,31 @@ func handleAdminClients(w http.ResponseWriter, req *http.Request, page *pageData } } } + +func clientIsAuthenticated(cid string, req *http.Request) bool { + return clientIsServer(req) || dbClientIsAuth(cid) +} + +func clientIsServer(req *http.Request) bool { + clientIp, _, _ := net.SplitHostPort(req.RemoteAddr) + ifaces, err := net.Interfaces() + if err == nil { + for _, i := range ifaces { + if addrs, err := i.Addrs(); err == nil { + for _, addr := range addrs { + var ip net.IP + switch v := addr.(type) { + case *net.IPNet: + ip = v.IP + case *net.IPAddr: + ip = v.IP + } + if clientIp == ip.String() { + return true + } + } + } + } + } + return false +} diff --git a/admin_games.go b/admin_games.go index 209a4ac..957e042 100644 --- a/admin_games.go +++ b/admin_games.go @@ -1,8 +1,12 @@ package main import ( + "fmt" + "io" + "mime/multipart" "net/http" - "strings" + "os" + "strconv" "github.com/gorilla/mux" ) @@ -10,23 +14,63 @@ import ( func handleAdminGames(w http.ResponseWriter, req *http.Request, page *pageData) { vars := mux.Vars(req) page.SubTitle = "Games" - gameId := vars["id"] - teamId := req.FormValue("teamid") - if strings.TrimSpace(teamId) != "" { - page.session.setStringValue("teamid", teamId) - page.TeamID = teamId - } - if gameId == "new" { + teamId := vars["id"] + if teamId == "" { + // Games List + type gamesPageData struct { + Games []Game + } + page.TemplateData = gamesPageData{Games: dbGetAllGames()} + page.SubTitle = "Games" + page.show("admin-games.html", w) + } else { switch vars["function"] { case "save": name := req.FormValue("gamename") + desc := req.FormValue("gamedesc") if dbIsValidTeam(teamId) { - if dbEditTeamGame(teamId, name) != nil { + if err := dbUpdateTeamGame(teamId, name, desc); err != nil { + page.session.setFlashMessage("Error updating game: "+err.Error(), "error") + } else { + page.session.setFlashMessage("Team game updated", "success") } + redirect("/admin/teams/"+teamId, w, req) } - default: - page.SubTitle = "Add New Game" - page.show("admin-addgame.html", w) + case "screenshotupload": + if err := saveScreenshots(teamId, req); err != nil { + page.session.setFlashMessage("Error updating game: "+err.Error(), "error") + } + redirect("/admin/teams/"+teamId, w, req) } } } + +func saveScreenshots(teamId string, req *http.Request) error { + err := req.ParseMultipartForm((1 << 10) * 24) + if err != nil { + return err + } + + for _, fheaders := range req.MultipartForm.File { + for _, hdr := range fheaders { + // open uploaded + var infile multipart.File + if infile, err = hdr.Open(); err != nil { + return err + } + + // open destination + var outfile *os.File + if outfile, err = os.Create("./uploaded/" + hdr.Filename); err != nil { + return err + } + // 32K buffer copy + var written int64 + if written, err = io.Copy(outfile, infile); err != nil { + return err + } + fmt.Println("uploaded file:" + hdr.Filename + ";length:" + strconv.Itoa(int(written))) + } + } + return nil +} diff --git a/admin_teams.go b/admin_teams.go index 536f4a2..c741509 100644 --- a/admin_teams.go +++ b/admin_teams.go @@ -11,6 +11,7 @@ func handleAdminTeams(w http.ResponseWriter, req *http.Request, page *pageData) page.SubTitle = "Teams" teamId := vars["id"] if teamId == "new" { + // Add a new team switch vars["function"] { case "save": name := req.FormValue("teamname") @@ -30,6 +31,7 @@ func handleAdminTeams(w http.ResponseWriter, req *http.Request, page *pageData) page.show("admin-addteam.html", w) } } else if teamId != "" { + // Functions for existing team if dbIsValidTeam(teamId) { switch vars["function"] { case "save": @@ -44,8 +46,11 @@ func handleAdminTeams(w http.ResponseWriter, req *http.Request, page *pageData) redirect("/admin/teams", w, req) case "delete": var err error + t := dbGetTeam(teamId) if err = dbDeleteTeam(teamId); err != nil { page.session.setFlashMessage("Error deleting team: "+err.Error(), "error") + } else { + page.session.setFlashMessage("Team "+t.Name+" Deleted", "success") } redirect("/admin/teams", w, req) case "savemember": @@ -61,10 +66,11 @@ func handleAdminTeams(w http.ResponseWriter, req *http.Request, page *pageData) redirect("/admin/teams/"+teamId, w, req) case "deletemember": mbrId := req.FormValue("memberid") + m, _ := dbGetTeamMember(teamId, mbrId) if err := dbDeleteTeamMember(teamId, mbrId); err != nil { page.session.setFlashMessage("Error deleting team member: "+err.Error(), "error") } else { - page.session.setFlashMessage("Member deleted from team", "success") + page.session.setFlashMessage(m.Name+" deleted from team", "success") } redirect("/admin/teams/"+teamId, w, req) default: @@ -78,10 +84,10 @@ func handleAdminTeams(w http.ResponseWriter, req *http.Request, page *pageData) redirect("/admin/teams", w, req) } } else { + // Team List type teamsPageData struct { Teams []Team } - page.TemplateData = teamsPageData{Teams: dbGetAllTeams()} page.SubTitle = "Teams" page.show("admin-teams.html", w) diff --git a/assets/css/gjvote.css b/assets/css/gjvote.css index 931f8d1..6b2e344 100644 --- a/assets/css/gjvote.css +++ b/assets/css/gjvote.css @@ -17,6 +17,14 @@ div.content { margin-left: 0; } +input.file { + padding: .5em .6em; + display: inline-block; + border: 1px solid #ccc; + box-shadow: inset 0 1px 3px #ddd; + border-radius: 4px; +} + #menu { width: 100%; height: 40px; @@ -29,6 +37,14 @@ div.content { display: inline-block; } +#menu .pure-menu-nonlink { + color: #777; + padding: .5em 1em; + display: block; + text-decoration: none; + white-space: nowrap; +} + #menu .menu-button { display: inline; position: absolute; @@ -57,8 +73,87 @@ div.content { background-color: #191818; } -input.uuid-field { - width: 360px; +div.horizontal-scroll { + overflow-x: scroll; +} + +img.thumbnail { + width: 100px; + height: 100px; +} + +.thumbnail-container { + display: block; + height: 120px; + background-color: #EEE; +} + +.padding { + padding: 5px; +} + +.space { + margin: 5px; +} + +.space-sides { + margin-left: 5px; + margin-right: 5px; +} + +.big-space { + margin: 10px; +} + +.left { + text-align: left; +} +.right { + text-align: right; +} +.center { + text-align: center; +} + +.center-all { + text-align: center; + vertical-align: center; + margin: auto; +} + +table.center td { + text-align: center; +} + +table.padding td { + padding: 5px; +} + +.pure-button-error { + background-color: #DD0000; + color: #FFFFFF; +} + +#modal-overlay { + visibility: hidden; + position: absolute; + left: 0px; + top: 0px; + width: 100%; + height: 100%; + text-align: center; + z-index: 1000; + background-color: rgba(0, 0, 0, 0.5); +} + +#modal-overlay>div { + width: 500px; + margin: 100px auto; + background-color: #FFF; + border: 1px solid #000; + border-radius: 10px; + padding: 15px; + text-align: center; } @media (min-width: 40em) { @@ -92,6 +187,11 @@ input.uuid-field { margin-left: 150px; } + aside.flash.success { + background-color: #229af9; + color: #FFFFFF; + } + .content { margin-left: 150px; } diff --git a/assets/js/gjvote.js b/assets/js/gjvote.js index 2797602..a1ddb6a 100644 --- a/assets/js/gjvote.js +++ b/assets/js/gjvote.js @@ -15,3 +15,51 @@ document.onkeydown = function(evt) { toggleAdminPanel(); } } + +function showModal(options) { + var modal = document.getElementById('modal-overlay'); + document.getElementById('modal-title').innerText = (options.title)?options.title:""; + document.getElementById('modal-subtitle').innerText = (options.subtitle)?options.subtitle:""; + if(options.body) { + document.getElementById('modal-body').innerText = options.body; + } else if(options.bodyNode) { + document.getElementById('modal-body').appendChild(options.bodyNode); + } + if(options.buttons) { + for(var i = 0; i < options.buttons.length; i++) { + var btn; + if(options.buttons[i].isSubmit) { + btn = document.createElement('submit'); + } else { + btn = document.createElement('a'); + } + options.buttons[i].title = (options.buttons[i].title==undefined)?'':options.buttons[i].title; + options.buttons[i].href = (options.buttons[i].href==undefined)?'#':options.buttons[i].href; + options.buttons[i].click = (options.buttons[i].click==undefined)?function(){}:options.buttons[i].click; + options.buttons[i].class = (options.buttons[i].class==undefined)?'':options.buttons[i].class; + options.buttons[i].position = (options.buttons[i].position==undefined)?'right':options.buttons[i].position; + + btn.innerHTML = options.buttons[i].title; + btn.title = options.buttons[i].title; + btn.href = options.buttons[i].href; + btn.className = 'space pure-button '+options.buttons[i].class+' '+options.buttons[i].position; + snack.listener( + {node:btn, event:'click'}, + options.buttons[i].click + ); + document.getElementById('modal-buttons').appendChild(btn); + } + } + modal.style.visibility = 'visible'; +} + +function hideModal() { + var modal = document.getElementById('modal-overlay'); + modal.style.visibility = 'hidden'; + document.getElementById('modal-title').innerHTML = ''; + document.getElementById('modal-body').innerHTML = ''; + var buttonsDiv = document.getElementById('modal-buttons') + while(buttonsDiv.firstChild) { + buttonsDiv.removeChild(buttonsDiv.firstChild); + } +} diff --git a/main.go b/main.go index 70b2ffb..062ece2 100644 --- a/main.go +++ b/main.go @@ -53,6 +53,7 @@ type pageData struct { CurrentJam string ClientID string ClientIsAuth bool + ClientIsServer bool TeamID string TemplateData interface{} @@ -237,7 +238,8 @@ func InitPageData(w http.ResponseWriter, req *http.Request) *pageData { } p.ClientID = p.session.getClientID() - p.ClientIsAuth = dbClientIsAuth(p.ClientID) + p.ClientIsAuth = clientIsAuthenticated(p.ClientID, req) + p.ClientIsServer = clientIsServer(req) p.TeamID, _ = p.session.getStringValue("teamid") return p diff --git a/model_games.go b/model_games.go index 50b3538..14a4cd4 100644 --- a/model_games.go +++ b/model_games.go @@ -1,12 +1,145 @@ package main -import "github.com/pborman/uuid" +import ( + "errors" + + "github.com/pborman/uuid" +) type Game struct { - UUID *uuid.UUID - Name string + Name string + TeamId string + Description string + Screenshots []Screenshot } -func dbIsValidGame(id string) bool { - return true +type Screenshot struct { + Description string + Image string +} + +func dbUpdateTeamGame(teamId, name, desc string) error { + var err error + if err = openDatabase(); err != nil { + return err + } + defer closeDatabase() + + // Make sure the team is valid + tm := dbGetTeam(teamId) + if tm == nil { + return errors.New("Invalid team") + } + gamePath := []string{"teams", teamId, "game"} + + if err := db.MkBucketPath(gamePath); err != nil { + return err + } + if name == "" { + name = tm.Name + "'s Game" + } + if err := db.SetValue(gamePath, "name", name); err != nil { + return err + } + if err := db.SetValue(gamePath, "description", desc); err != nil { + return err + } + if err := db.MkBucketPath(append(gamePath, "screenshots")); err != nil { + return err + } + + return err +} + +func dbGetAllGames() []Game { + var ret []Game + tms := dbGetAllTeams() + for i := range tms { + ret = append(ret, *dbGetTeamGame(tms[i].UUID)) + } + return ret +} + +func dbGetTeamGame(teamId string) *Game { + var err error + if err = openDatabase(); err != nil { + return nil + } + defer closeDatabase() + + gamePath := []string{"teams", teamId, "game"} + gm := new(Game) + if gm.Name, err = db.GetValue(gamePath, "name"); err != nil { + gm.Name = "" + } + gm.TeamId = teamId + if gm.Description, err = db.GetValue(gamePath, "description"); err != nil { + gm.Description = "" + } + gm.Screenshots = dbGetTeamGameScreenshots(teamId) + return gm +} + +// Screenshots are saved as base64 encoded pngs +func dbGetTeamGameScreenshots(teamId string) []Screenshot { + var ret []Screenshot + var err error + ssPath := []string{"teams", teamId, "game", "screenshots"} + var ssIds []string + if ssIds, err = db.GetBucketList(ssPath); err != nil { + return ret + } + for _, v := range ssIds { + if ss := dbGetTeamGameScreenshot(teamId, v); ss != nil { + ret = append(ret, *ss) + } + } + return ret +} + +func dbGetTeamGameScreenshot(teamId, ssId string) *Screenshot { + var err error + ssPath := []string{"teams", teamId, "game", "screenshots", ssId} + ret := new(Screenshot) + if ret.Description, err = db.GetValue(ssPath, "description"); err != nil { + return nil + } + if ret.Image, err = db.GetValue(ssPath, "image"); err != nil { + return nil + } + return ret +} + +func dbSaveTeamGameScreenshot(teamId string, ss *Screenshot) error { + var err error + if err = openDatabase(); err != nil { + return nil + } + defer closeDatabase() + + ssPath := []string{"teams", teamId, "game", "screenshots"} + // Generate a UUID for this screenshot + uuid := uuid.New() + ssPath = append(ssPath, uuid) + if err := db.MkBucketPath(ssPath); err != nil { + return err + } + if err := db.SetValue(ssPath, "description", ss.Description); err != nil { + return err + } + if err := db.SetValue(ssPath, "image", ss.Image); err != nil { + return err + } + return nil +} + +func dbDeleteTeamGameScreenshot(teamId, ssId string) error { + var err error + if err = openDatabase(); err != nil { + return nil + } + defer closeDatabase() + + ssPath := []string{"teams", teamId, "game", "screenshots"} + return db.DeleteBucket(ssPath, ssId) } diff --git a/model_teams.go b/model_teams.go index 3e8d812..1e89ad2 100644 --- a/model_teams.go +++ b/model_teams.go @@ -105,6 +105,7 @@ func dbGetTeam(id string) *Team { return nil } tm.Members, _ = dbGetTeamMembers(id) + tm.Game = dbGetTeamGame(id) return tm } @@ -213,7 +214,6 @@ func dbGetTeamMembers(teamid string) ([]TeamMember, error) { for _, v := range memberUuids { var mbr *TeamMember if mbr, err = dbGetTeamMember(teamid, v); err == nil { - fmt.Println("Finding Team Members", teamid, mbr.Name) ret = append(ret, *mbr) } } diff --git a/templates/admin-editteam.html b/templates/admin-editteam.html index f198a9d..e26cc8c 100644 --- a/templates/admin-editteam.html +++ b/templates/admin-editteam.html @@ -1,70 +1,126 @@ {{ $uuid := .TemplateData.UUID }}
-
+
+ +

Team Details

+
+
+
+ + +
+
+
+ Cancel + + +
+
+ +
+
+ +
-
- {{ .TemplateData.Name }} +
+

Team Game

+
+ + +
+
+ + +
+
+ +
+ {{ if not .TemplateData.Game.Screenshots }} + Upload Screenshot + {{ else }} + {{ range $i, $v := .TemplateData.Game.Screenshots }} + {{ $v.Description }} + {{ end }} + {{ end }} +
+ {{ if .TemplateData.Game.Screenshots }} + + {{ end }} +
- -
- - -
-
- Cancel - - + Cancel +
-

Members

- - - - - - - - - - - - - {{ range $i, $v := .TemplateData.Members }} - - - - - - - - - {{ end }} - - - - - + + +
NameSlack IDTwitterEmailEditRemove
{{ $v.Name }}{{ $v.SlackId }}{{ $v.Twitter }}{{ $v.Email }} - - -
- - -
-
Add a new member
-
- +
+
+

Team Members

+ + + + + + + + + + + + + {{ range $i, $v := .TemplateData.Members }} + + + + + + + + + {{ end }} + + + + + - - -
NameSlack IDTwitterEmailEditRemove
{{ $v.Name }}{{ $v.SlackId }}{{ $v.Twitter }}{{ $v.Email }} + + + + + + +
Add a new member
+
+
+ - - -
+ +
+ +
+
+
+ Cancel +
+ + diff --git a/templates/admin-menu.html b/templates/admin-menu.html index 771e76c..4bd1df9 100644 --- a/templates/admin-menu.html +++ b/templates/admin-menu.html @@ -12,11 +12,15 @@ {{ end }} - {{if .ClientIsAuth}} - DeAuth Client - {{else}} + {{ if .ClientIsAuth }} + {{ if .ClientIsServer }} + Server Mode + {{ else }} + DeAuth Client + {{ end }} + {{ else }} Auth Client - {{end}} + {{ end }}