diff --git a/.gitignore b/.gitignore index 2eacb04..a51d231 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,11 @@ # Ignore the binaries -gjvote -gjvote.darwin64 -gjvote.linux386 -gjvote.linux64 -gjvote.linuxarm -gjvote.win386.exe -gjvote.win64.exe +ictgj-voting +ictgj-voting.darwin64 +ictgj-voting.linux386 +ictgj-voting.linux64 +ictgj-voting.linuxarm +ictgj-voting.win386.exe +ictgj-voting.win64.exe # Ignore the DBs *.db diff --git a/admin_archive.go b/admin_archive.go index 0a49456..e127b30 100644 --- a/admin_archive.go +++ b/admin_archive.go @@ -15,6 +15,6 @@ func handleAdminArchive(w http.ResponseWriter, req *http.Request, page *pageData type archivePageData struct { Gamejams []Gamejam } - apd := new(archivePageData) + //apd := new(archivePageData) } } diff --git a/admin_games.go b/admin_games.go index 12cd918..c9729ef 100644 --- a/admin_games.go +++ b/admin_games.go @@ -30,11 +30,15 @@ func handleAdminGames(w http.ResponseWriter, req *http.Request, page *pageData) page.SubTitle = "Games" page.show("admin-games.html", w) } else { - tm := m.jam.GetTeamById(teamId) + tm, _ := m.jam.GetTeamById(teamId) if tm != nil { switch vars["function"] { case "save": - gm := NewGame(tm.UUID) + var err error + var gm *Game + if gm, err = NewGame(tm.UUID); err != nil { + page.session.setFlashMessage("Error updating game: "+err.Error(), "error") + } gm.Name = req.FormValue("gamename") gm.Link = req.FormValue("gamelink") gm.Description = req.FormValue("gamedesc") @@ -50,8 +54,13 @@ func handleAdminGames(w http.ResponseWriter, req *http.Request, page *pageData) } redirect("/admin/teams/"+tm.UUID+"#game", w, req) case "screenshotdelete": + var ss *Screenshot + var err error ssid := vars["subid"] - if err := tm.deleteScreenshot(ssid); err != nil { + if ss, err = NewScreenshot(tm.UUID, ssid); err != nil { + page.session.setFlashMessage("Error deleting screenshot: "+err.Error(), "error") + } + if err = m.jam.DeleteScreenshot(ss); err != nil { page.session.setFlashMessage("Error deleting screenshot: "+err.Error(), "error") } redirect("/admin/teams/"+tm.UUID+"#game", w, req) @@ -65,6 +74,8 @@ func handleAdminGames(w http.ResponseWriter, req *http.Request, page *pageData) func saveScreenshots(tm *Team, req *http.Request) error { var err error + var ss *Screenshot + file, hdr, err := req.FormFile("newssfile") if err != nil { return err @@ -74,13 +85,13 @@ func saveScreenshots(tm *Team, req *http.Request) error { if len(hdr.Filename) > extIdx { fltp = hdr.Filename[extIdx+1:] } - m, _, err := image.Decode(file) + mI, _, err := image.Decode(file) buf := new(bytes.Buffer) // We convert everything to jpg - if err = jpeg.Encode(buf, m, nil); err != nil { + if err = jpeg.Encode(buf, mI, nil); err != nil { return errors.New("Unable to encode image") } - thm := resize.Resize(200, 0, m, resize.Lanczos3) + thm := resize.Resize(200, 0, mI, resize.Lanczos3) thmBuf := new(bytes.Buffer) var thmString string if fltp == "gif" { @@ -94,9 +105,13 @@ func saveScreenshots(tm *Team, req *http.Request) error { } thmString = base64.StdEncoding.EncodeToString(thmBuf.Bytes()) - return tm.saveScreenshot(&Screenshot{ - Image: base64.StdEncoding.EncodeToString(buf.Bytes()), - Thumbnail: thmString, - Filetype: fltp, - }) + if ss, err = NewScreenshot(tm.UUID, ""); err != nil { + return err + } + + ss.Image = base64.StdEncoding.EncodeToString(buf.Bytes()) + ss.Thumbnail = thmString + ss.Filetype = fltp + + return m.jam.SaveScreenshot(ss) } diff --git a/admin_teams.go b/admin_teams.go index d2000f0..7de428c 100644 --- a/admin_teams.go +++ b/admin_teams.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "net/http" _ "image/gif" @@ -10,10 +11,6 @@ import ( "github.com/gorilla/mux" ) -func refreshTeamsInMemory() { - site.Teams = db.getAllTeams() -} - func handleAdminTeams(w http.ResponseWriter, req *http.Request, page *pageData) { vars := mux.Vars(req) page.SubTitle = "Teams" @@ -23,17 +20,11 @@ func handleAdminTeams(w http.ResponseWriter, req *http.Request, page *pageData) switch vars["function"] { case "save": name := req.FormValue("teamname") - if db.getTeamByName(name) != nil { - // A team with that name already exists - page.session.setFlashMessage("A team with the name "+name+" already exists!", "error") - } else { - if err := db.newTeam(name); err != nil { - page.session.setFlashMessage(err.Error(), "error") - } else { - page.session.setFlashMessage("Team "+name+" created!", "success") - } + tm := NewTeam("") + tm.Name = name + if err := m.jam.AddTeam(tm); err != nil { + page.session.setFlashMessage("Error adding team: "+err.Error(), "error") } - refreshTeamsInMemory() redirect("/admin/teams", w, req) default: page.SubTitle = "Add New Team" @@ -41,57 +32,57 @@ func handleAdminTeams(w http.ResponseWriter, req *http.Request, page *pageData) } } else if teamId != "" { // Functions for existing team - tm := db.getTeam(teamId) + tm, _ := m.jam.GetTeamById(teamId) if tm != nil { switch vars["function"] { case "save": - tm.UUID = teamId tm.Name = req.FormValue("teamname") - if err := tm.save(); err != nil { - page.session.setFlashMessage("Error updating team: "+err.Error(), "error") - } else { - page.session.setFlashMessage("Team Updated!", "success") - } - refreshTeamsInMemory() + page.session.setFlashMessage("Team Updated!", "success") redirect("/admin/teams", w, req) case "delete": var err error - if err = tm.delete(); err != nil { - page.session.setFlashMessage("Error deleting team: "+err.Error(), "error") + if err = m.jam.RemoveTeamById(teamId); err != nil { + page.session.setFlashMessage("Error removing team: "+err.Error(), "error") } else { - page.session.setFlashMessage("Team "+tm.Name+" Deleted", "success") + page.session.setFlashMessage("Team "+tm.Name+" Removed", "success") } - refreshTeamsInMemory() redirect("/admin/teams", w, req) case "savemember": mbrName := req.FormValue("newmembername") - mbr := newTeamMember(mbrName) - mbr.SlackId = req.FormValue("newmemberslackid") - mbr.Twitter = req.FormValue("newmembertwitter") - mbr.Email = req.FormValue("newmemberemail") - if err := tm.updateTeamMember(mbr); err != nil { + mbr, err := NewTeamMember(tm.UUID, "") + if err == nil { + mbr.SlackId = req.FormValue("newmemberslackid") + mbr.Twitter = req.FormValue("newmembertwitter") + mbr.Email = req.FormValue("newmemberemail") + } + if err := tm.AddTeamMember(mbr); err != nil { page.session.setFlashMessage("Error adding team member: "+err.Error(), "error") } else { page.session.setFlashMessage(mbrName+" added to team!", "success") } - refreshTeamsInMemory() redirect("/admin/teams/"+teamId+"#members", w, req) case "deletemember": - m := tm.getTeamMember(req.FormValue("memberid")) - if m != nil { - if err := tm.deleteTeamMember(m); err != nil { - page.session.setFlashMessage("Error deleting team member: "+err.Error(), "error") - } else { - page.session.setFlashMessage(m.Name+" deleted from team", "success") - } - refreshTeamsInMemory() + var err error + var mbr *TeamMember + if mbr, err = tm.GetTeamMemberById(req.FormValue("memberid")); err != nil { + fmt.Println("Error removing team member: " + err.Error()) + page.session.setFlashMessage("Error deleting team member", "error") + redirect("/admin/teams/"+teamId+"#members", w, req) + } + if err = tm.RemoveTeamMemberById(mbr.UUID); err != nil { + fmt.Println("Error removing team member: " + err.Error()) + page.session.setFlashMessage("Error deleting team member", "error") } else { - page.session.setFlashMessage("Couldn't find team member to delete", "error") + page.session.setFlashMessage(mbr.Name+" deleted from team", "success") } redirect("/admin/teams/"+teamId+"#members", w, req) default: page.SubTitle = "Edit Team" - t := db.getTeam(teamId) + t, err := m.jam.GetTeamById(teamId) + if err != nil { + page.session.setFlashMessage("Error loading team: "+err.Error(), "error") + redirect("/admin/teams", w, req) + } page.TemplateData = t page.show("admin-editteam.html", w) } @@ -104,7 +95,7 @@ func handleAdminTeams(w http.ResponseWriter, req *http.Request, page *pageData) type teamsPageData struct { Teams []Team } - page.TemplateData = teamsPageData{Teams: db.getAllTeams()} + page.TemplateData = m.jam page.SubTitle = "Teams" page.show("admin-teams.html", w) } diff --git a/admin_users.go b/admin_users.go index f4bdcea..1768f62 100644 --- a/admin_users.go +++ b/admin_users.go @@ -29,7 +29,7 @@ func handleAdminDoLogin(w http.ResponseWriter, req *http.Request) { // If it can't, it returns an error func doLogin(email, password string) error { if strings.TrimSpace(email) != "" && strings.TrimSpace(password) != "" { - return db.checkCredentials(email, password) + return m.checkCredentials(email, password) } return errors.New("Invalid Credentials") } @@ -53,12 +53,12 @@ func handleAdminUsers(w http.ResponseWriter, req *http.Request, page *pageData) switch vars["function"] { case "save": email = req.FormValue("email") - if db.isValidUserEmail(email) { + if m.isValidUserEmail(email) { // User already exists page.session.setFlashMessage("A user with email address "+email+" already exists!", "error") } else { password := req.FormValue("password") - if err := db.updateUserPassword(email, string(password)); err != nil { + if err := m.updateUserPassword(email, string(password)); err != nil { page.session.setFlashMessage(err.Error(), "error") } else { page.session.setFlashMessage("User "+email+" created!", "success") @@ -73,10 +73,10 @@ func handleAdminUsers(w http.ResponseWriter, req *http.Request, page *pageData) switch vars["function"] { case "save": var err error - if db.isValidUserEmail(email) { + if m.isValidUserEmail(email) { password := req.FormValue("password") if password != "" { - if err = db.updateUserPassword(email, password); err != nil { + if err = m.updateUserPassword(email, password); err != nil { page.session.setFlashMessage(err.Error(), "error") } else { page.session.setFlashMessage("User "+email+" created!", "success") @@ -86,8 +86,8 @@ func handleAdminUsers(w http.ResponseWriter, req *http.Request, page *pageData) } case "delete": var err error - if db.isValidUserEmail(email) { - if err = db.deleteUser(email); err != nil { + if m.isValidUserEmail(email) { + if err = m.deleteUser(email); err != nil { page.session.setFlashMessage(err.Error(), "error") } else { page.session.setFlashMessage("User "+email+" deleted!", "success") @@ -96,7 +96,7 @@ func handleAdminUsers(w http.ResponseWriter, req *http.Request, page *pageData) redirect("/admin/users", w, req) default: page.SubTitle = "Edit Admin User" - if !db.isValidUserEmail(email) { + if !m.isValidUserEmail(email) { page.session.setFlashMessage("Couldn't find the requested user, please try again.", "error") redirect("/admin/users", w, req) } @@ -107,7 +107,7 @@ func handleAdminUsers(w http.ResponseWriter, req *http.Request, page *pageData) type usersPageData struct { Users []string } - page.TemplateData = usersPageData{Users: db.getAllUsers()} + page.TemplateData = usersPageData{Users: m.getAllUsers()} page.SubTitle = "Admin Users" page.show("admin-users.html", w) diff --git a/admin_votes.go b/admin_votes.go index 63ef05f..582ba50 100644 --- a/admin_votes.go +++ b/admin_votes.go @@ -23,22 +23,22 @@ func getCondorcetResult() []Ranking { } var allPairs []teamPair var ret []Ranking - for i := 0; i < len(site.Teams); i++ { - for j := i + 1; j < len(site.Teams); j++ { + for i := 0; i < len(m.jam.Teams); i++ { + for j := i + 1; j < len(m.jam.Teams); j++ { // For each pairing find a winner - winner, pct, _ := findWinnerBetweenTeams(&site.Teams[i], &site.Teams[j]) + winner, pct, _ := findWinnerBetweenTeams(&m.jam.Teams[i], &m.jam.Teams[j]) newPair := new(teamPair) if winner != nil { newPair.winner = winner - if winner.UUID == site.Teams[i].UUID { - newPair.loser = &site.Teams[j] + if winner.UUID == m.jam.Teams[i].UUID { + newPair.loser = &m.jam.Teams[j] } else { - newPair.loser = &site.Teams[i] + newPair.loser = &m.jam.Teams[i] } newPair.majority = pct } else { - newPair.winner = &site.Teams[i] - newPair.loser = &site.Teams[j] + newPair.winner = &m.jam.Teams[i] + newPair.loser = &m.jam.Teams[j] newPair.majority = 50 } allPairs = append(allPairs, *newPair) @@ -46,8 +46,8 @@ func getCondorcetResult() []Ranking { } // initialize map of team wins teamWins := make(map[string]int) - for i := range site.Teams { - teamWins[site.Teams[i].UUID] = 0 + for i := range m.jam.Teams { + teamWins[m.jam.Teams[i].UUID] = 0 } // Figure out how many wins each team has for i := range allPairs { @@ -72,7 +72,10 @@ func getCondorcetResult() []Ranking { nR := new(Ranking) nR.Rank = currRank for i := range rankedWins[topWins] { - nR.Teams = append(nR.Teams, *site.getTeamByUUID(rankedWins[topWins][i])) + tm, _ := m.jam.GetTeamById(rankedWins[topWins][i]) + if tm != nil { + nR.Teams = append(nR.Teams, *tm) + } } ret = append(ret, *nR) delete(rankedWins, topWins) @@ -99,7 +102,7 @@ func uuidIsInRankingSlice(uuid string, sl []Ranking) bool { func findWinnerBetweenTeams(tm1, tm2 *Team) (*Team, float32, error) { // tally gets incremented for a tm1 win, decremented for a tm2 win var tm1votes, tm2votes float32 - for _, v := range site.Votes { + for _, v := range m.jam.Votes { for _, chc := range v.Choices { if chc.Team == tm1.UUID { tm1votes++ @@ -138,12 +141,12 @@ func handleAdminVotes(w http.ResponseWriter, req *http.Request, page *pageData) Results []Ranking } vpd := new(votePageData) - for i := range site.Votes { + for i := range m.jam.Votes { v := new(vpdVote) - v.Timestamp = site.Votes[i].Timestamp.Format(time.RFC3339) - v.ClientId = site.Votes[i].ClientId - for _, choice := range site.Votes[i].Choices { - for _, fndTm := range site.Teams { + v.Timestamp = m.jam.Votes[i].Timestamp.Format(time.RFC3339) + v.ClientId = m.jam.Votes[i].ClientId + for _, choice := range m.jam.Votes[i].Choices { + for _, fndTm := range m.jam.Teams { if fndTm.UUID == choice.Team { v.Choices = append(v.Choices, fndTm) break diff --git a/main.go b/main.go index be09458..652e691 100644 --- a/main.go +++ b/main.go @@ -69,7 +69,9 @@ func main() { } loadConfig() - site.save() + if err = site.SaveToDB(); err != nil { + errorExit("Unable to save site config to DB: " + err.Error()) + } initialize() r = mux.NewRouter() @@ -184,13 +186,11 @@ func initialize() { fmt.Print("GameJam Name: ") gjName, _ := reader.ReadString('\n') gjName = strings.TrimSpace(gjName) - if db.setJamName(gjName) != nil { - fmt.Println("Error saving Current Jam") - } + m.jam.Name = gjName } if m.jam.Name != "" { - fmt.Println("Current Jam Name: " + jmNm) + fmt.Println("Current Jam Name: " + m.jam.Name) } else { fmt.Println("No Jam Name Specified") } @@ -220,7 +220,7 @@ func InitPageData(w http.ResponseWriter, req *http.Request) *pageData { // First check if we're logged in userEmail, _ := p.session.getStringValue("email") // With a valid account - p.LoggedIn = db.isValidUserEmail(userEmail) + p.LoggedIn = m.isValidUserEmail(userEmail) p.Site = site p.SubTitle = "GameJam Voting" @@ -257,20 +257,15 @@ func InitPageData(w http.ResponseWriter, req *http.Request) *pageData { } p.HideAdminMenu = true - if p.CurrentJam = db.getJamName(); p.CurrentJam != "" { - p.FlashMessage = "Error Loading Current GameJam: " + err.Error() - p.FlashClass = "error" - } - p.ClientId = p.session.getClientId() - cl := db.getClient(p.ClientId) + cl := m.GetClient(p.ClientId) p.ClientIsAuth = cl.Auth p.ClientIsServer = clientIsServer(req) // Public Mode - p.PublicMode = db.getPublicSiteMode() + p.PublicMode = m.site.GetPublicMode() // Authentication Mode - p.AuthMode = db.site.getAuthMode() + p.AuthMode = m.site.GetAuthMode() return p } @@ -317,8 +312,8 @@ func resetToDefaults() { conf, _ := reader.ReadString('\n') conf = strings.ToUpper(strings.TrimSpace(conf)) if strings.HasPrefix(conf, "Y") { - if def.save() != nil { - errorExit("Error resetting to defaults") + if err := def.SaveToDB(); err != nil { + errorExit("Error resetting to defaults: " + err.Error()) } fmt.Println("Reset to defaults") } diff --git a/model.go b/model.go index 2e038ad..8f02588 100644 --- a/model.go +++ b/model.go @@ -15,6 +15,8 @@ type model struct { site *siteData // Configuration data for the site jam *Gamejam // The currently active gamejam clients []Client // Web clients that have connected to the server + + clientsUpdated bool } // Update Flags: Which parts of the model need to be updated @@ -39,20 +41,27 @@ func NewModel() (*model, error) { } // Load the site data - m.site = m.LoadSiteData() + m.site = NewSiteData(m) + if err = m.site.LoadFromDB(); err != nil { + // Error loading from the DB, set to defaults + def := NewSiteData(m) + m.site = def + } // Load the jam data - m.jam = m.LoadCurrentJam() + if m.jam, err = m.LoadCurrentJam(); err != nil { + return nil, errors.New("Unable to load current jam: " + err.Error()) + } // Load web clients m.clients = m.LoadAllClients() - return &m, nil + return m, nil } func (m *model) openDB() error { m.dbOpened += 1 - if db.dbOpened == 1 { + if m.dbOpened == 1 { var err error m.bolt, err = boltease.Create(m.dbFileName, 0600, nil) if err != nil { @@ -105,20 +114,22 @@ func (m *model) saveChanges() error { } defer m.closeDB() - if m.site.needsSave() { - if err = m.site.saveToDB(); err != nil { + if m.site.NeedsSave() { + if err = m.site.SaveToDB(); err != nil { return err } } - if m.jam.needsSave() { - if err = m.jam.saveToDB(); err != nil { + if m.jam.IsChanged { + if err = m.jam.SaveToDB(); err != nil { return err } + m.jam.IsChanged = false } if m.clientsUpdated { if err = m.SaveAllClients(); err != nil { return err } + m.clientsUpdated = false } return nil } diff --git a/model_clients.go b/model_clients.go index c7d5ee4..b525cd3 100644 --- a/model_clients.go +++ b/model_clients.go @@ -1,5 +1,11 @@ package main +import ( + "errors" + + "github.com/pborman/uuid" +) + /** * Client * A client is a system that is connecting to the web server @@ -14,12 +20,32 @@ type Client struct { } func NewClient(id string) *Client { + if id == "" { + id = uuid.New() + } return &Client{ UUID: id, mPath: []string{"clients", id}, } } +func (m *model) AddClient(cl *Client) error { + for i := range m.clients { + if m.clients[i].UUID == cl.UUID { + return errors.New("A client with that ID already exists") + } + if m.clients[i].IP == cl.IP { + return errors.New("A client with that IP already exists") + } + if m.clients[i].Name == cl.Name { + return errors.New("A client with that Name already exists") + } + } + m.clients = append(m.clients, *cl) + m.clientsUpdated = true + return nil +} + /** * DB Functions * These are generally just called when the app starts up, or when the periodic 'save' runs @@ -71,7 +97,7 @@ func (m *model) SaveAllClients() error { defer m.closeDB() for _, v := range m.clients { - if err = m.SaveClient(v); err != nil { + if err = m.SaveClient(&v); err != nil { return err } } @@ -86,13 +112,13 @@ func (m *model) SaveClient(cl *Client) error { } defer m.closeDB() - if err = db.bolt.SetBool(cl.mPath, "auth", c.Auth); err != nil { + if err = m.bolt.SetBool(cl.mPath, "auth", cl.Auth); err != nil { return err } - if err = db.bolt.SetValue(cl.mPath, "name", c.Name); err != nil { + if err = m.bolt.SetValue(cl.mPath, "name", cl.Name); err != nil { return err } - return db.bolt.SetValue(cl.mPath, "ip", c.IP) + return m.bolt.SetValue(cl.mPath, "ip", cl.IP) } /** @@ -132,7 +158,7 @@ func (m *model) UpdateClient(cl *Client) { } } if !found { - m.clients = append(m.clients, cl) + m.clients = append(m.clients, *cl) } m.clientsUpdated = true } diff --git a/model_gamejam.go b/model_gamejam.go index 1114178..7f27303 100644 --- a/model_gamejam.go +++ b/model_gamejam.go @@ -1,6 +1,10 @@ package main -import "time" +import ( + "errors" + "strings" + "time" +) /** * Gamejam @@ -13,9 +17,10 @@ type Gamejam struct { Teams []Team Votes []Vote - m *model // The model that holds this gamejam's data - mPath []string // The path in the db to this gamejam - changed bool // Flag to tell if we need to update the db + m *model // The model that holds this gamejam's data + mPath []string // The path in the db to this gamejam + + IsChanged bool // Flag to tell if we need to update the db } func NewGamejam(m *model) *Gamejam { @@ -30,13 +35,12 @@ func NewGamejam(m *model) *Gamejam { * These are generally just called when the app starts up, or when the periodic 'save' runs */ -func (m *model) LoadCurrentJam() *Gamejam { +func (m *model) LoadCurrentJam() (*Gamejam, error) { if err := m.openDB(); err != nil { - return err + return nil, err } defer m.closeDB() - var err error gj := NewGamejam(m) gj.Name, _ = m.bolt.GetValue(gj.mPath, "name") @@ -46,52 +50,37 @@ func (m *model) LoadCurrentJam() *Gamejam { // Load all votes gj.Votes = gj.LoadAllVotes() - return gj + return gj, nil } // Save everything to the DB whether it's flagged as changed or not -func (gj *Gamejam) saveToDB() error { +func (gj *Gamejam) SaveToDB() error { if err := gj.m.openDB(); err != nil { return err } defer gj.m.closeDB() -} - -/** - * In Memory functions - * This is generally how the app accesses client data - */ -func (gj *Gamejam) getTeamByUUID(uuid string) *Team { - for i := range gj.Teams { - if gj.Teams[i].UUID == uuid { - return &gj.Teams[i] + var errs []error + // Save all Teams + for _, tm := range gj.Teams { + if err := gj.SaveTeam(&tm); err != nil { + errs = append(errs, err) } } + + // Save all Votes + for _, vt := range gj.Votes { + if err := gj.SaveVote(&vt); err != nil { + errs = append(errs, err) + } + } + if len(errs) > 0 { + var errTxt string + for i := range errs { + errTxt = errTxt + errs[i].Error() + "\n" + } + errTxt = strings.TrimSpace(errTxt) + return errors.New("Error(s) saving to DB: " + errTxt) + } return nil } - -// Check if pth is already in updates, if not, add it -func (gj *Gamejam) NeedsUpdate(pth []string) { - var found bool - for _, v := range gj.updates { - if !(len(v) == len(pth)) { - continue - } - // The lengths are the same, do all elements match? - var nxt bool - for i := range pth { - if v[i] != pth[i] { - nxt = true - } - } - if !nxt { - // This pth is already in the 'updates' list - found = true - break - } - } - if !found { - gj.updates = append(gj.updates, pth) - } -} diff --git a/model_games.go b/model_games.go index 92290d3..788b3b3 100644 --- a/model_games.go +++ b/model_games.go @@ -1,6 +1,10 @@ package main -import "errors" +import ( + "errors" + + "github.com/pborman/uuid" +) /** * Game @@ -17,19 +21,37 @@ type Game struct { } // Create a new game object -func NewGame(tmId string) *Game { +func NewGame(tmId string) (*Game, error) { + if tmId == "" { + return nil, errors.New("Team ID is required") + } return &Game{ TeamId: tmId, mPath: []string{"jam", "teams", tmId, "game"}, - } + }, nil } -func (gm *Game) GetScreenshot(ssId string) *Screenshot { +func (gm *Game) GetScreenshot(ssId string) (*Screenshot, error) { for _, ss := range gm.Screenshots { if ss.UUID == ssId { - return ss + return &ss, nil } } + return nil, errors.New("Invalid Id") +} + +func (gm *Game) RemoveScreenshot(ssId string) error { + idx := -1 + for i, ss := range gm.Screenshots { + if ss.UUID == ssId { + idx = i + return nil + } + } + if idx < 0 { + return errors.New("Invalid Id") + } + gm.Screenshots = append(gm.Screenshots[:idx], gm.Screenshots[idx+1:]...) return nil } @@ -44,11 +66,18 @@ type Screenshot struct { } // Create a Screenshot Object -func NewScreenshot(tmId, ssId string) *Screenshot { +func NewScreenshot(tmId, ssId string) (*Screenshot, error) { + if tmId == "" { + return nil, errors.New("Team ID is required") + } + if ssId == "" { + // Generate a new UUID + ssId = uuid.New() + } return &Screenshot{ UUID: ssId, mPath: []string{"jam", "teams", tmId, "game", "screenshots", ssId}, - } + }, nil } /** @@ -57,14 +86,17 @@ func NewScreenshot(tmId, ssId string) *Screenshot { */ // Load a team's game from the DB and return it -func (gj *Gamejam) LoadTeamGame(tmId string) *Game { +func (gj *Gamejam) LoadTeamGame(tmId string) (*Game, error) { var err error if err = gj.m.openDB(); err != nil { - return nil + return nil, err } defer gj.m.closeDB() - gm := NewGame(tmId) + gm, err := NewGame(tmId) + if err != nil { + return nil, err + } if gm.Name, err = gj.m.bolt.GetValue(gm.mPath, "name"); err != nil { gm.Name = "" } @@ -74,10 +106,11 @@ func (gj *Gamejam) LoadTeamGame(tmId string) *Game { if gm.Link, err = gj.m.bolt.GetValue(gm.mPath, "link"); err != nil { gm.Link = "" } + // Now get the game screenshots gm.Screenshots = gj.LoadTeamGameScreenshots(tmId) - return &gm + return gm, nil } // Load a games screenshots from the DB @@ -89,44 +122,50 @@ func (gj *Gamejam) LoadTeamGameScreenshots(tmId string) []Screenshot { defer gj.m.closeDB() var ret []Screenshot - gm := NewGame(tmId) + gm, err := NewGame(tmId) + if err != nil { + return ret + } ssBktPath := append(gm.mPath, "screenshots") var ssIds []string ssIds, _ = gj.m.bolt.GetBucketList(ssBktPath) for _, v := range ssIds { - ssLd := gj.LoadTeamGameScreenshot(tmId, v) + ssLd, _ := gj.LoadTeamGameScreenshot(tmId, v) if ssLd != nil { - ret = append(ret, ssLd) + ret = append(ret, *ssLd) } } return ret } // Load a screenshot from the DB -func (gj *Gamejam) LoadTeamGameScreenshot(tmId, ssId string) *Screenshot { +func (gj *Gamejam) LoadTeamGameScreenshot(tmId, ssId string) (*Screenshot, error) { var err error if err = gj.m.openDB(); err != nil { - return nil + return nil, err } defer gj.m.closeDB() - ret := NewScreenshot(tmId, ssId) + ret, err := NewScreenshot(tmId, ssId) + if err != nil { + return nil, err + } if ret.Description, err = gj.m.bolt.GetValue(ret.mPath, "description"); err != nil { - return nil + return nil, err } if ret.Image, err = gj.m.bolt.GetValue(ret.mPath, "image"); err != nil { - return nil + return nil, err } if ret.Thumbnail, err = gj.m.bolt.GetValue(ret.mPath, "thumbnail"); err != nil { - return nil + return nil, err } if ret.Thumbnail == "" { ret.Thumbnail = ret.Image } if ret.Filetype, err = gj.m.bolt.GetValue(ret.mPath, "filetype"); err != nil { - return nil + return nil, err } - return ret + return ret, err } // Save a game to the DB @@ -141,6 +180,10 @@ func (gj *Gamejam) SaveGame(gm *Game) error { return err } + var tm *Team + if tm, err = gj.GetTeamById(gm.TeamId); err != nil { + return err + } if gm.Name == "" { gm.Name = tm.Name + "'s Game" } @@ -169,26 +212,35 @@ func (gj *Gamejam) SaveScreenshots(gm *Game) error { defer gj.m.closeDB() for _, ss := range gm.Screenshots { - if err = gj.SaveScreenshot(gm.TeamId, ss); err != nil { + if err = gj.SaveScreenshot(&ss); err != nil { return err } } // Now remove unused screenshots ssPath := append(gm.mPath, "screenshots") + var ssIds []string if ssIds, err = gj.m.bolt.GetBucketList(ssPath); err != nil { return err } for i := range ssIds { - if gm.GetScreenshot(ssIds[i]) == nil { - if err = gj.DeleteScreenshot(NewScreenshot(tm.TeamId, ssIds[i])); err != nil { - return err - } + ss, _ := gm.GetScreenshot(ssIds[i]) + if ss != nil { + // A valid screenshot, next + continue + } + if ss, err = NewScreenshot(gm.TeamId, ssIds[i]); err != nil { + // Error building screenshot to delete... + continue + } + if err = gj.DeleteScreenshot(ss); err != nil { + return err } } + return nil } // Save a screenshot -func (gj *Gamejam) SaveScreenshot(tmId string, ss *Screenshot) error { +func (gj *Gamejam) SaveScreenshot(ss *Screenshot) error { var err error if err = gj.m.openDB(); err != nil { return err @@ -229,12 +281,11 @@ func (gj *Gamejam) DeleteScreenshot(ss *Screenshot) error { // Set the given team's game to gm func (gj *Gamejam) UpdateGame(tmId string, gm *Game) error { - var found bool - tm := gj.GetTeamById(tmId) - if tm == nil { - return errors.New("Invalid team ID: " + gm.TeamId) + tm, err := gj.GetTeamById(tmId) + if err != nil { + return errors.New("Error getting team: " + err.Error()) } tm.Game = gm - gj.NeedsUpdate([]string{"team", tmId, "game"}) + gj.IsChanged = true return nil } diff --git a/model_sitedata.go b/model_sitedata.go index b9f2dbf..2ca86c3 100644 --- a/model_sitedata.go +++ b/model_sitedata.go @@ -1,16 +1,19 @@ package main -import "strconv" +import ( + "errors" + "strconv" +) /** * SiteData * Contains configuration for the website */ type siteData struct { - title string - port int - sessionName string - serverDir string + Title string + Port int + SessionName string + ServerDir string authMode int publicMode int @@ -62,10 +65,10 @@ func (s *siteData) LoadFromDB() error { if port, err := s.m.bolt.GetInt(s.mPath, "port"); err == nil { s.Port = port } - if sessionName, err = s.m.bolt.GetValue(s.mPath, "session-name"); err == nil { + if sessionName, err := s.m.bolt.GetValue(s.mPath, "session-name"); err == nil { s.SessionName = sessionName } - if serverDir, err = s.m.bolt.GetValue(s.mPath, "server-dir"); err == nil { + if serverDir, err := s.m.bolt.GetValue(s.mPath, "server-dir"); err == nil { s.ServerDir = serverDir } s.changed = false @@ -79,7 +82,8 @@ func (s *siteData) NeedsSave() bool { // Save the site data into the DB func (s *siteData) SaveToDB() error { - if err := s.m.openDB(); err != nil { + var err error + if err = s.m.openDB(); err != nil { return err } defer s.m.closeDB() @@ -108,12 +112,13 @@ func (s *siteData) GetAuthMode() int { // Set the auth mode func (s *siteData) SetAuthMode(mode int) error { if mode < AuthModeAuthentication || mode >= AuthModeError { - return errors.Error("Invalid Authentication Mode: " + strconv.Itoa(mode)) + return errors.New("Invalid Authentication Mode: " + strconv.Itoa(mode)) } if mode != s.authMode { s.authMode = mode s.changed = true } + return nil } // Return the public site mode @@ -124,10 +129,11 @@ func (s *siteData) GetPublicMode() int { // Set the public site mode func (s *siteData) SetPublicMode(mode int) error { if mode < SiteModeWaiting || mode >= SiteModeError { - return errors.Error("Invalid Public Mode: " + strconv.Itoa(mode)) + return errors.New("Invalid Public Mode: " + strconv.Itoa(mode)) } if mode != s.publicMode { s.publicMode = mode s.changed = true } + return nil } diff --git a/model_teams.go b/model_teams.go index 9f90f0c..f2c6202 100644 --- a/model_teams.go +++ b/model_teams.go @@ -1,6 +1,10 @@ package main -import "errors" +import ( + "errors" + + "github.com/pborman/uuid" +) /** * Team @@ -16,12 +20,24 @@ type Team struct { // Create a team func NewTeam(id string) *Team { + if id == "" { + id = uuid.New() + } return &Team{ UUID: id, mPath: []string{"jam", "teams", id}, } } +func (gj *Gamejam) GetTeamById(id string) (*Team, error) { + for i := range gj.Teams { + if gj.Teams[i].UUID == id { + return &gj.Teams[i], nil + } + } + return nil, errors.New("Invalid Team Id given") +} + type TeamMember struct { UUID string Name string @@ -33,11 +49,53 @@ type TeamMember struct { } // Create a new team member -func NewTeamMember(tmId, uId string) *TeamMember { +func NewTeamMember(tmId, uId string) (*TeamMember, error) { + if tmId == "" { + return nil, errors.New("Team ID is required") + } + if uId == "" { + uId = uuid.New() + } return &TeamMember{ UUID: uId, mPath: []string{"jam", "teams", tmId, "members", uId}, + }, nil +} + +// AddTeamMember adds a new team member +func (tm *Team) AddTeamMember(mbr *TeamMember) error { + lkup, _ := tm.GetTeamMemberById(mbr.UUID) + if lkup != nil { + return errors.New("A Team Member with that Id already exists") } + tm.Members = append(tm.Members, *mbr) + return nil +} + +// GetTeamMemberById returns a member with the given uuid +// or an error if it couldn't find it +func (tm *Team) GetTeamMemberById(uuid string) (*TeamMember, error) { + for i := range tm.Members { + if tm.Members[i].UUID == uuid { + return &tm.Members[i], nil + } + } + return nil, errors.New("Invalid Team Member Id given") +} + +func (tm *Team) RemoveTeamMemberById(id string) error { + idx := -1 + for i := range tm.Members { + if tm.Members[i].UUID == id { + idx = i + break + } + } + if idx < 0 { + return errors.New("Invalid Team Member ID given") + } + tm.Members = append(tm.Members[:idx], tm.Members[idx+1:]...) + return nil } /** @@ -50,43 +108,47 @@ func (gj *Gamejam) LoadAllTeams() []Team { var err error var ret []Team if err = gj.m.openDB(); err != nil { - return err + return ret } defer gj.m.closeDB() - if tmUUIDs, err = m.bolt.GetBucketList(mbrsPath); err != nil { + var tmUUIDs []string + tmsPath := append(gj.mPath, "teams") + if tmUUIDs, err = m.bolt.GetBucketList(tmsPath); err != nil { return ret } for _, v := range tmUUIDs { - tm := gj.LoadTeam(v) + tm, _ := gj.LoadTeam(v) if tm != nil { - ret = append(ret, tm) + ret = append(ret, *tm) } } return ret } // Load a team out of the database -func (gj *Gamejam) LoadTeam(uuid string) *Team { +func (gj *Gamejam) LoadTeam(uuid string) (*Team, error) { var err error if err = gj.m.openDB(); err != nil { - return err + return nil, err } defer gj.m.closeDB() // Team Data tm := NewTeam(uuid) if tm.Name, err = gj.m.bolt.GetValue(tm.mPath, "name"); err != nil { - return nil + return nil, errors.New("Error loading team: " + err.Error()) } // Team Members tm.Members = gj.LoadTeamMembers(uuid) // Team Game - tm.Game = gj.LoadTeamGame(uuid) + if tm.Game, err = gj.LoadTeamGame(uuid); err != nil { + return nil, errors.New("Error loading team game: " + err.Error()) + } - return tm + return tm, nil } // Load the members of a team from the DB and return them @@ -104,9 +166,9 @@ func (gj *Gamejam) LoadTeamMembers(tmId string) []TeamMember { mbrsPath := append(tm.mPath, "members") if memberUuids, err = gj.m.bolt.GetBucketList(mbrsPath); err == nil { for _, v := range memberUuids { - mbr := gj.LoadTeamMember(tmId, v) + mbr, _ := gj.LoadTeamMember(tmId, v) if mbr != nil { - ret = append(ret, mbr) + ret = append(ret, *mbr) } } } @@ -114,17 +176,20 @@ func (gj *Gamejam) LoadTeamMembers(tmId string) []TeamMember { } // Load a team member from the DB and return it -func (gj *Gamejam) LoadTeamMember(tmId, mbrId string) *TeamMember { +func (gj *Gamejam) LoadTeamMember(tmId, mbrId string) (*TeamMember, error) { var err error if err = gj.m.openDB(); err != nil { - return nil + return nil, err } defer gj.m.closeDB() - mbr := NewTeamMember(tmId, mbrId) + mbr, err := NewTeamMember(tmId, mbrId) + if err != nil { + return nil, errors.New("Error loading team member: " + err.Error()) + } // Name is the only required field if mbr.Name, err = gj.m.bolt.GetValue(mbr.mPath, "name"); err != nil { - return nil + return nil, errors.New("Error loading team member: " + err.Error()) } if mbr.SlackId, err = gj.m.bolt.GetValue(mbr.mPath, "slackid"); err != nil { mbr.SlackId = "" @@ -135,7 +200,7 @@ func (gj *Gamejam) LoadTeamMember(tmId, mbrId string) *TeamMember { if mbr.Email, err = gj.m.bolt.GetValue(mbr.mPath, "email"); err != nil { mbr.Email = "" } - return mbr + return mbr, nil } func (gj *Gamejam) SaveTeam(tm *Team) error { @@ -146,7 +211,7 @@ func (gj *Gamejam) SaveTeam(tm *Team) error { defer gj.m.closeDB() // Save team data - if err = gj.m.bolt.SetValue(tm.mPath, "name"); err != nil { + if err = gj.m.bolt.SetValue(tm.mPath, "name", tm.Name); err != nil { return err } @@ -167,10 +232,12 @@ func (gj *Gamejam) SaveTeam(tm *Team) error { } // Save team game - return gj.SaveGame(gm) + return gj.SaveGame(tm.Game) } // Delete the team tm +// TODO: Deletes should be done all at once when syncing memory to the DB +/* func (gj *Gamejam) DeleteTeam(tm *Team) error { var err error if err = gj.m.openDB(); err != nil { @@ -183,8 +250,11 @@ func (gj *Gamejam) DeleteTeam(tm *Team) error { } return gj.m.bolt.DeleteBucket(tm.mPath[:len(tm.mPath)-1], tm.UUID) } +*/ // Delete the TeamMember mbr from Team tm +// TODO: Deletes should be done all at once when syncing memory to the DB +/* func (gj *Gamejam) DeleteTeamMember(tm *Team, mbr *TeamMember) error { var err error if err = gj.m.openDB(); err != nil { @@ -197,28 +267,47 @@ func (gj *Gamejam) DeleteTeamMember(tm *Team, mbr *TeamMember) error { } return gj.m.bolt.DeleteBucket(mbr.mPath[:len(mbr.mPath)-1], mbr.UUID) } +*/ /** * In Memory functions * This is generally how the app accesses data */ -// Find a team by it's ID -func (gj *Gamejam) GetTeamById(id string) *Team { - for i := range gj.Teams { - if gj.Teams[i].UUID == id { - return gj.Teams[i] - } +// Add a team +func (gj *Gamejam) AddTeam(tm *Team) error { + if _, err := gj.GetTeamById(tm.UUID); err != nil { + return errors.New("A team with that ID already exists") } + if _, err := gj.GetTeamByName(tm.Name); err != nil { + return errors.New("A team with that Name already exists") + } + gj.Teams = append(gj.Teams, *tm) return nil } // Find a team by name -func (gj *Gamejam) GetTeamByName(nm string) *Team { +func (gj *Gamejam) GetTeamByName(nm string) (*Team, error) { for i := range gj.Teams { if gj.Teams[i].Name == nm { - return gj.Teams[i] + return &gj.Teams[i], nil } } + return nil, errors.New("Invalid team name given") +} + +// Remove a team by id +func (gj *Gamejam) RemoveTeamById(id string) error { + idx := -1 + for i := range gj.Teams { + if gj.Teams[i].UUID == id { + idx = i + break + } + } + if idx == -1 { + return errors.New("Invalid Team ID given") + } + gj.Teams = append(gj.Teams[:idx], gj.Teams[idx+1:]...) return nil } diff --git a/model_users.go b/model_users.go index f179600..17df447 100644 --- a/model_users.go +++ b/model_users.go @@ -3,6 +3,9 @@ package main import "golang.org/x/crypto/bcrypt" // These are all model functions that have to do with users +// Unlike gamejam functions, we manipulate the DB directly +// We want to make sure that we always use the most up-to-date user +// information. // Returns true if there are any users in the database func (m *model) hasUser() bool { diff --git a/model_votes.go b/model_votes.go index cbc1c19..cccad93 100644 --- a/model_votes.go +++ b/model_votes.go @@ -1,6 +1,7 @@ package main import ( + "errors" "strconv" "time" ) @@ -16,30 +17,87 @@ type Vote struct { Timestamp time.Time ClientId string // UUID of client Choices []GameChoice + + mPath []string // The path in the DB to this team } +func NewVote(clId string, tm time.Time) (*Vote, error) { + if clId == "" { + return nil, errors.New("Client ID is required") + } + if tm.IsZero() { + tm = time.Now() + } + + vt := new(Vote) + vt.mPath = []string{"jam", "votes", clId, tm.Format(time.RFC3339)} + return vt, nil +} + +func (vt *Vote) SetChoices(ch []string) error { + // Clear any previous choices from this vote + vt.Choices = []GameChoice{} + for i, v := range ch { + vt.Choices = append(vt.Choices, GameChoice{Rank: i, Team: v}) + } + return nil +} + +func (gj *Gamejam) GetVoteWithTimeString(clId, ts string) (*Vote, error) { + timestamp, err := time.Parse(time.RFC3339, ts) + if err != nil { + return nil, err + } + return gj.GetVote(clId, timestamp) +} + +func (gj *Gamejam) GetVote(clId string, ts time.Time) (*Vote, error) { + for _, v := range gj.Votes { + if v.ClientId == clId && v.Timestamp == ts { + return &v, nil + } + } + return nil, errors.New("Couldn't find requested vote") +} + +func (gj *Gamejam) AddVote(vt *Vote) error { + // Make sure that this isn't a duplicate + if _, err := gj.GetVote(vt.ClientId, vt.Timestamp); err == nil { + return errors.New("Duplicate Vote") + } + gj.Votes = append(gj.Votes, *vt) + return nil +} + +/** + * DB Functions + * These are generally just called when the app starts up or when the periodic 'save' runs + */ + // LoadAllVotes loads all votes for the jam out of the database func (gj *Gamejam) LoadAllVotes() []Vote { + var err error var ret []Vote - if err := gj.m.openDB(); err != nil { - return err + if err = gj.m.openDB(); err != nil { + return ret } defer gj.m.closeDB() votesPath := []string{"jam", "votes"} + var cliUUIDs []string if cliUUIDs, err = m.bolt.GetBucketList(votesPath); err != nil { return ret } for _, cId := range cliUUIDs { vtsPth := append(votesPath, cId) - if times, err := m.bolt.GetBucketList(vtsPth); err != nil { + var times []string + if times, err = m.bolt.GetBucketList(vtsPth); err != nil { // Error reading this bucket, move on to the next continue } for _, t := range times { - vt := gj.LoadVote(cId, t) - if vt != nil { - ret = append(ret, vt) + if vt, err := gj.LoadVote(cId, t); err == nil { + ret = append(ret, *vt) } } } @@ -47,27 +105,41 @@ func (gj *Gamejam) LoadAllVotes() []Vote { } // Load a vote from the DB and return it -func (gj *Gamejam) LoadVote(clientId, tm string) *Vote { +func (gj *Gamejam) LoadVote(clientId, t string) (*Vote, error) { var tm time.Time + var err error if tm, err = time.Parse(time.RFC3339, t); err != nil { - return nil + return nil, errors.New("Error loading vote: " + err.Error()) + } + vt, err := NewVote(clientId, tm) + if err != nil { + return nil, errors.New("Error creating vote: " + err.Error()) } - vt := new(Vote) - vt.Timestamp = tm - vt.ClientId = cId - vtPth := append(vtsPth, t) var choices []string - if choices, err = m.bolt.GetKeyList(vtPth); err != nil { - return nil + if choices, err = m.bolt.GetKeyList(vt.mPath); err != nil { + return nil, errors.New("Error creating vote: " + err.Error()) } for _, v := range choices { - ch := new(GameChoices) + ch := new(GameChoice) var rank int if rank, err = strconv.Atoi(v); err == nil { ch.Rank = rank - ch.Team, _ = m.bolt.GetValue(vtPth, v) + ch.Team, _ = m.bolt.GetValue(vt.mPath, v) vt.Choices = append(vt.Choices, *ch) } } - return &vt + return vt, nil +} + +func (gj *Gamejam) SaveVote(vt *Vote) error { + var err error + if err = gj.m.openDB(); err != nil { + return err + } + defer gj.m.closeDB() + + for _, v := range vt.Choices { + m.bolt.SetValue(vt.mPath, strconv.Itoa(v.Rank), v.Team) + } + return nil } diff --git a/page_session.go b/page_session.go index 99c99ff..ba3e04d 100644 --- a/page_session.go +++ b/page_session.go @@ -43,7 +43,7 @@ func (p *pageSession) getClientId() string { fmt.Println(" Client IP:" + clientIp) if clientIp != "127.0.0.1" { fmt.Println(" Pulling data by IP") - cli = db.getClientByIp(clientIp) + cli = m.GetClientByIp(clientIp) } if cli != nil { clientId = cli.UUID diff --git a/public_endpoints.go b/public_endpoints.go index 47de0d8..2173726 100644 --- a/public_endpoints.go +++ b/public_endpoints.go @@ -2,6 +2,7 @@ package main import ( "encoding/base64" + "fmt" "math/rand" "net/http" "strings" @@ -17,7 +18,7 @@ func initPublicPage(w http.ResponseWriter, req *http.Request) *pageData { func handleMain(w http.ResponseWriter, req *http.Request) { page := initPublicPage(w, req) - if db.getPublicSiteMode() == SiteModeWaiting { + if m.site.GetPublicMode() == SiteModeWaiting { page.SubTitle = "" page.show("public-waiting.html", w) } else { @@ -28,7 +29,7 @@ func handleMain(w http.ResponseWriter, req *http.Request) { func loadVotingPage(w http.ResponseWriter, req *http.Request) { page := initPublicPage(w, req) // Client authentication required - if (db.site.getAuthMode() == AuthModeAuthentication) && !page.ClientIsAuth { + if (m.site.GetAuthMode() == AuthModeAuthentication) && !page.ClientIsAuth { page.show("unauthorized.html", w) return } @@ -37,7 +38,7 @@ func loadVotingPage(w http.ResponseWriter, req *http.Request) { Timestamp string } vpd := new(votingPageData) - tms := db.getAllTeams() + tms := m.jam.Teams // Randomize the team list rand.Seed(time.Now().Unix()) @@ -55,7 +56,7 @@ func loadVotingPage(w http.ResponseWriter, req *http.Request) { func handlePublicSaveVote(w http.ResponseWriter, req *http.Request) { page := initPublicPage(w, req) // Client authentication required - if (db.site.getAuthMode() == AuthModeAuthentication) && !page.ClientIsAuth { + if (m.site.GetAuthMode() == AuthModeAuthentication) && !page.ClientIsAuth { page.show("unauthorized.html", w) return } @@ -66,23 +67,37 @@ func handlePublicSaveVote(w http.ResponseWriter, req *http.Request) { ts := req.FormValue("timestamp") timestamp, err := time.Parse(time.RFC3339, ts) if err != nil { - page.session.setFlashMessage("Error parsing timestamp: "+ts, "error") + page.session.setFlashMessage("Error creating vote", "error") + fmt.Println("Error parsing timestamp: " + ts) redirect("/", w, req) } - client := db.getClient(page.ClientId) - if _, err := client.getVote(timestamp); err == nil { + client := m.GetClient(page.ClientId) + + // voteSlice is an ordered string slice of the voters preferences + voteCSV := req.FormValue("uservote") + voteSlice := strings.Split(voteCSV, ",") + + if _, err = m.jam.GetVote(client.UUID, timestamp); err == nil { // Duplicate vote... Cancel it. page.session.setFlashMessage("Duplicate vote!", "error") redirect("/", w, req) } - // voteSlice is an ordered string slice of the voters preferences - voteCSV := req.FormValue("uservote") - voteSlice := strings.Split(voteCSV, ",") - if err := client.saveVote(timestamp, voteSlice); err != nil { - page.session.setFlashMessage("Error Saving Vote: "+err.Error(), "error") + + var vt *Vote + if vt, err = NewVote(client.UUID, timestamp); err != nil { + fmt.Println("Error creating vote: " + err.Error()) + page.session.setFlashMessage("Error creating vote", "error") + redirect("/", w, req) } - if newVote, err := client.getVote(timestamp); err == nil { - site.Votes = append(site.Votes, *newVote) + if err = vt.SetChoices(voteSlice); err != nil { + fmt.Println("Error creating vote: " + err.Error()) + page.session.setFlashMessage("Error creating vote", "error") + redirect("/", w, req) + } + if err := m.jam.AddVote(vt); err != nil { + fmt.Println("Error adding vote: " + err.Error()) + page.session.setFlashMessage("Error creating vote", "error") + redirect("/", w, req) } page.session.setFlashMessage("Vote Saved!", "success large fading") redirect("/", w, req) @@ -91,19 +106,22 @@ func handlePublicSaveVote(w http.ResponseWriter, req *http.Request) { func handleThumbnailRequest(w http.ResponseWriter, req *http.Request) { // Thumbnail requests are open even without client authentication vars := mux.Vars(req) - tm := db.getTeam(vars["teamid"]) - if tm == nil { + tm, err := m.jam.GetTeamById(vars["teamid"]) + if err != nil { + fmt.Println("handleThumbnailRequest: " + err.Error()) http.Error(w, "Couldn't find image", 404) return } - ss := tm.getScreenshot(vars["imageid"]) - if ss == nil { + ss, err := tm.Game.GetScreenshot(vars["imageid"]) + if err != nil { + fmt.Println("handleThumbnailRequest: " + err.Error()) http.Error(w, "Couldn't find image", 404) return } w.Header().Set("Content-Type", "image/"+ss.Filetype) dat, err := base64.StdEncoding.DecodeString(ss.Thumbnail) if err != nil { + fmt.Println("handleThumbnailRequest: " + err.Error()) http.Error(w, "Couldn't find image", 404) return } @@ -113,19 +131,22 @@ func handleThumbnailRequest(w http.ResponseWriter, req *http.Request) { func handleImageRequest(w http.ResponseWriter, req *http.Request) { // Image requests are open even without client authentication vars := mux.Vars(req) - tm := db.getTeam(vars["teamid"]) - if tm == nil { + tm, err := m.jam.GetTeamById(vars["teamid"]) + if err != nil { + fmt.Println("handleImageRequest: " + err.Error()) http.Error(w, "Couldn't find image", 404) return } - ss := tm.getScreenshot(vars["imageid"]) - if ss == nil { + ss, err := tm.Game.GetScreenshot(vars["imageid"]) + if err != nil { + fmt.Println("handleImageRequest: " + err.Error()) http.Error(w, "Couldn't find image", 404) return } w.Header().Set("Content-Type", "image/"+ss.Filetype) dat, err := base64.StdEncoding.DecodeString(ss.Image) if err != nil { + fmt.Println("handleImageRequest: " + err.Error()) http.Error(w, "Couldn't find image", 404) return } @@ -134,15 +155,15 @@ func handleImageRequest(w http.ResponseWriter, req *http.Request) { func handleTeamMgmtRequest(w http.ResponseWriter, req *http.Request) { // Team Management pages are open even without client authentication - if db.getPublicSiteMode() == SiteModeVoting { + if m.site.GetPublicMode() == SiteModeVoting { redirect("/", w, req) } page := initPublicPage(w, req) vars := mux.Vars(req) page.SubTitle = "Team Details" teamId := vars["id"] - tm := db.getTeam(teamId) - if tm != nil { + tm, err := m.jam.GetTeamById(teamId) + if err == nil { // Team self-management functions switch vars["function"] { case "": @@ -150,41 +171,35 @@ func handleTeamMgmtRequest(w http.ResponseWriter, req *http.Request) { page.TemplateData = tm page.show("public-teammgmt.html", w) case "savemember": - m := newTeamMember(req.FormValue("newmembername")) + m, err := NewTeamMember(tm.UUID, "") + if err != nil { + page.session.setFlashMessage("Error adding team member: "+err.Error(), "error") + redirect("/team/"+tm.UUID+"#members", w, req) + } + m.Name = req.FormValue("newmembername") m.SlackId = req.FormValue("newmemberslackid") m.Twitter = req.FormValue("newmembertwitter") m.Email = req.FormValue("newmemberemail") - if err := tm.updateTeamMember(m); err != nil { + if err := tm.AddTeamMember(m); err != nil { page.session.setFlashMessage("Error adding team member: "+err.Error(), "error") } else { page.session.setFlashMessage(m.Name+" added to team!", "success") } - refreshTeamsInMemory() redirect("/team/"+tm.UUID+"#members", w, req) case "deletemember": mbrId := req.FormValue("memberid") - m := tm.getTeamMember(mbrId) - if m != nil { - if err := tm.deleteTeamMember(m); err != nil { - page.session.setFlashMessage("Error deleting team member: "+err.Error(), "error") - } else { - page.session.setFlashMessage(m.Name+" deleted from team", "success") - } + err := tm.RemoveTeamMemberById(mbrId) + if err != nil { + page.session.setFlashMessage("Error deleting team member: "+err.Error(), "error") } else { - page.session.setFlashMessage("Couldn't find member to delete", "error") + page.session.setFlashMessage("Team member removed", "success") } - refreshTeamsInMemory() redirect("/team/"+tm.UUID, w, req) case "savegame": - gm := newGame(tm.UUID) - gm.Name = req.FormValue("gamename") - gm.Link = req.FormValue("gamelink") - gm.Description = req.FormValue("gamedesc") - if err := gm.save(); err != nil { - page.session.setFlashMessage("Error updating game: "+err.Error(), "error") - } else { - page.session.setFlashMessage("Team game updated", "success") - } + tm.Game.Name = req.FormValue("gamename") + tm.Game.Link = req.FormValue("gamelink") + tm.Game.Description = req.FormValue("gamedesc") + page.session.setFlashMessage("Team game updated", "success") redirect("/team/"+tm.UUID, w, req) case "screenshotupload": if err := saveScreenshots(tm, req); err != nil { @@ -193,7 +208,7 @@ func handleTeamMgmtRequest(w http.ResponseWriter, req *http.Request) { redirect("/team/"+tm.UUID, w, req) case "screenshotdelete": ssid := vars["subid"] - if err := tm.deleteScreenshot(ssid); err != nil { + if err := tm.Game.RemoveScreenshot(ssid); err != nil { page.session.setFlashMessage("Error deleting screenshot: "+err.Error(), "error") } redirect("/team/"+tm.UUID, w, req)