Several Changes

Added Team Self-Management pages (Site must not be 'voting' mode)
Process screenshots on upload, create thumbnail
Change inline screenshots to thumbnails (screenshots can get big)
This commit is contained in:
Brian Buller 2017-07-14 12:27:42 -05:00
parent 1c08032627
commit c2959b3974
9 changed files with 10777 additions and 10507 deletions

View File

@ -1,14 +1,18 @@
package main package main
import ( import (
"bytes"
"encoding/base64"
"errors"
"fmt" "fmt"
"io/ioutil" "image"
"image/gif"
"image/jpeg"
"net/http" "net/http"
"strings" "strings"
"encoding/base64"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/nfnt/resize"
) )
func handleAdminGames(w http.ResponseWriter, req *http.Request, page *pageData) { func handleAdminGames(w http.ResponseWriter, req *http.Request, page *pageData) {
@ -65,10 +69,29 @@ func saveScreenshots(teamId string, req *http.Request) error {
if len(hdr.Filename) > extIdx { if len(hdr.Filename) > extIdx {
fltp = hdr.Filename[extIdx+1:] fltp = hdr.Filename[extIdx+1:]
} }
data, _ := ioutil.ReadAll(file) m, _, err := image.Decode(file)
str := base64.StdEncoding.EncodeToString(data) buf := new(bytes.Buffer)
// We convert everything to jpg
if err = jpeg.Encode(buf, m, nil); err != nil {
return errors.New("Unable to encode image")
}
thm := resize.Resize(200, 0, m, resize.Lanczos3)
thmBuf := new(bytes.Buffer)
var thmString string
if fltp == "gif" {
if err = gif.Encode(thmBuf, thm, nil); err != nil {
return errors.New("Unable to encode image")
}
} else {
if err = jpeg.Encode(thmBuf, thm, nil); err != nil {
return errors.New("Unable to encode image")
}
}
thmString = base64.StdEncoding.EncodeToString(thmBuf.Bytes())
return dbSaveTeamGameScreenshot(teamId, &Screenshot{ return dbSaveTeamGameScreenshot(teamId, &Screenshot{
Image: str, Image: base64.StdEncoding.EncodeToString(buf.Bytes()),
Thumbnail: thmString,
Filetype: fltp, Filetype: fltp,
}) })
} }

View File

@ -3,6 +3,10 @@ package main
import ( import (
"net/http" "net/http"
_ "image/gif"
_ "image/jpeg"
_ "image/png"
"github.com/gorilla/mux" "github.com/gorilla/mux"
) )

20950
assets.go

File diff suppressed because it is too large Load Diff

View File

@ -106,6 +106,9 @@ func main() {
pub.HandleFunc("/vote", handlePublicSaveVote) pub.HandleFunc("/vote", handlePublicSaveVote)
pub.HandleFunc("/image/{teamid}/{imageid}", handleImageRequest) pub.HandleFunc("/image/{teamid}/{imageid}", handleImageRequest)
pub.HandleFunc("/thumbnail/{teamid}/{imageid}", handleThumbnailRequest) pub.HandleFunc("/thumbnail/{teamid}/{imageid}", handleThumbnailRequest)
pub.HandleFunc("/team/{id}", handleTeamMgmtRequest)
pub.HandleFunc("/team/{id}/{function}", handleTeamMgmtRequest)
pub.HandleFunc("/team/{id}/{function}/{subid}", handleTeamMgmtRequest)
// API Subrouter // API Subrouter
//api := r.PathPrefix("/api").Subtrouter() //api := r.PathPrefix("/api").Subtrouter()

View File

@ -108,3 +108,71 @@ func handleImageRequest(w http.ResponseWriter, req *http.Request) {
} }
w.Write(dat) w.Write(dat)
} }
func handleTeamMgmtRequest(w http.ResponseWriter, req *http.Request) {
if dbGetPublicSiteMode() == SiteModeVoting {
redirect("/", w, req)
}
page := initPublicPage(w, req)
vars := mux.Vars(req)
page.SubTitle = "Team Details"
teamId := vars["id"]
if teamId != "" {
// Team self-management functions
if !dbIsValidTeam(teamId) {
http.Error(w, "Page Not Found", 404)
return
}
switch vars["function"] {
case "":
page.SubTitle = "Team Management"
t := dbGetTeam(teamId)
page.TemplateData = t
page.show("public-teammgmt.html", w)
case "savemember":
mbrName := req.FormValue("newmembername")
mbrSlack := req.FormValue("newmemberslackid")
mbrTwitter := req.FormValue("newmembertwitter")
mbrEmail := req.FormValue("newmemberemail")
if err := dbAddTeamMember(teamId, mbrName, mbrEmail, mbrSlack, mbrTwitter); err != nil {
page.session.setFlashMessage("Error adding team member: "+err.Error(), "error")
} else {
page.session.setFlashMessage(mbrName+" added to team!", "success")
}
refreshTeamsInMemory()
redirect("/team/"+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(m.Name+" deleted from team", "success")
}
refreshTeamsInMemory()
redirect("/team/"+teamId, w, req)
case "savegame":
name := req.FormValue("gamename")
desc := req.FormValue("gamedesc")
if dbIsValidTeam(teamId) {
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("/team/"+teamId, w, req)
}
case "screenshotupload":
if err := saveScreenshots(teamId, req); err != nil {
page.session.setFlashMessage("Error updating game: "+err.Error(), "error")
}
redirect("/team/"+teamId, w, req)
case "screenshotdelete":
ssid := vars["subid"]
if err := dbDeleteTeamGameScreenshot(teamId, ssid); err != nil {
page.session.setFlashMessage("Error deleting screenshot: "+err.Error(), "error")
}
redirect("/team/"+teamId, w, req)
}
}
}

View File

@ -20,9 +20,9 @@
<hr /> <hr />
</div> </div>
<div class="left big-space">
<form class="pure-form pure-form-aligned" action="/admin/games/{{ $uuid }}/save" method="POST"> <form class="pure-form pure-form-aligned" action="/admin/games/{{ $uuid }}/save" method="POST">
<fieldset> <fieldset>
<div class="left big-space">
<a name="game" /> <a name="game" />
<h3>Team Game</h3> <h3>Team Game</h3>
<div class="pure-control-group"> <div class="pure-control-group">
@ -33,6 +33,12 @@
<label class="control-label" for="gamedesc">Description</label> <label class="control-label" for="gamedesc">Description</label>
<textarea id="gamedesc" name="gamedesc" placeholder="Description...">{{ .TemplateData.Game.Description }}</textarea> <textarea id="gamedesc" name="gamedesc" placeholder="Description...">{{ .TemplateData.Game.Description }}</textarea>
</div> </div>
<div class="pure-control-group reset-pull">
<a href="/admin/teams/{{ $uuid }}" class="pull-left space pure-button pure-button-plain">Cancel</a>
<button type="submit" class="pull-right space pure-button pure-button-primary">Update Game</button>
</div>
</fieldset>
</form>
<div class="pure-control-group"> <div class="pure-control-group">
<label class="control-label">Screenshots</label> <label class="control-label">Screenshots</label>
<div class="center-all horizontal-scroll thumbnail-container" id="thumbnail-container"> <div class="center-all horizontal-scroll thumbnail-container" id="thumbnail-container">
@ -51,12 +57,6 @@
{{ end }} {{ end }}
</div> </div>
</div> </div>
<div class="pure-control-group reset-pull">
<a href="/admin/teams/{{ $uuid }}" class="pull-left space pure-button pure-button-plain">Cancel</a>
<button type="submit" class="pull-right space pure-button pure-button-primary">Update Game</button>
</div>
</fieldset>
</form>
<hr /> <hr />
<div class="left"> <div class="left">
@ -68,7 +68,6 @@
<th>Slack ID</th> <th>Slack ID</th>
<th>Twitter</th> <th>Twitter</th>
<th>Email</th> <th>Email</th>
<th>Edit</th>
<th>Remove</th> <th>Remove</th>
</tr> </tr>
</thead> </thead>
@ -79,9 +78,6 @@
<td>{{ $v.SlackId }}</td> <td>{{ $v.SlackId }}</td>
<td>{{ $v.Twitter }}</td> <td>{{ $v.Twitter }}</td>
<td>{{ $v.Email }}</td> <td>{{ $v.Email }}</td>
<td>
<a href="/admin/teams/{{ $v.UUID }}/edit" class="pure-button pure-button-plain"><i class="fa fa-pencil"></i></a>
</td>
<td> <td>
<form action="/admin/teams/{{ $uuid }}/deletemember" method="POST"> <form action="/admin/teams/{{ $uuid }}/deletemember" method="POST">
<input type="hidden" name="memberid" value="{{ $v.UUID }}"/> <input type="hidden" name="memberid" value="{{ $v.UUID }}"/>

View File

@ -8,7 +8,7 @@
<thead> <thead>
<tr> <tr>
<th>Name</th> <th>Name</th>
<th>Key</th> <th>Management Link</th>
<th>Members</th> <th>Members</th>
<th>Game</th> <th>Game</th>
<th></th> <th></th>
@ -18,7 +18,7 @@
{{ range $i, $v := .TemplateData.Teams }} {{ range $i, $v := .TemplateData.Teams }}
<tr> <tr>
<td>{{ $v.Name }}</td> <td>{{ $v.Name }}</td>
<td>{{ $v.UUID }}</td> <td><a href="/team/{{ $v.UUID }}">{{ $v.UUID }}</a></td>
<td>{{ len $v.Members }}</td> <td>{{ len $v.Members }}</td>
<td>{{ $v.Game.Name }}</td> <td>{{ $v.Game.Name }}</td>
<td> <td>

View File

@ -0,0 +1,159 @@
{{ $uuid := .TemplateData.UUID }}
<div class="center">
<div class="left">
<h3>{{.TemplateData.Name}}</h3>
<hr />
</div>
<div class="left big-space">
<form class="pure-form pure-form-aligned" action="/team/{{ $uuid }}/savegame" method="POST">
<fieldset>
<a name="game" />
<h3>Team Game</h3>
<div class="pure-control-group">
<label class="control-label" for="gamename">Game Name</label>
<input id="gamename" name="gamename" value="{{ .TemplateData.Game.Name }}" placeholder="Game Name">
</div>
<div class="pure-control-group">
<label class="control-label" for="gamedesc">Description</label>
<textarea id="gamedesc" name="gamedesc" placeholder="Description...">{{ .TemplateData.Game.Description }}</textarea>
</div>
<div class="pure-control-group reset-pull">
<a href="/team/{{ $uuid }}" class="pull-left space pure-button pure-button-plain">Cancel</a>
<button type="submit" class="pull-right space pure-button pure-button-primary">Update Game</button>
</div>
</fieldset>
</form>
<div class="pure-control-group">
<label class="control-label">Screenshots</label>
<div class="center-all horizontal-scroll thumbnail-container" id="thumbnail-container">
{{ if not .TemplateData.Game.Screenshots }}
<a style="margin-top:40px;" class="center-all pure-button pure-button-primary" href="javascript:toggleUploadSSForm();">Upload Screenshot</a>
{{ else }}
{{ range $i, $v := .TemplateData.Game.Screenshots }}
<img data-teamid="{{ $uuid }}" data-ssid="{{ $v.UUID }}" class="thumbnail" alt="{{ $v.Description }}" src="data:image/{{$v.Filetype}};base64,{{ $v.Thumbnail }}" />
{{ end }}
{{ end }}
</div>
{{ if .TemplateData.Game.Screenshots }}
<div class="right">
<a id="toggleUploadSSFormBtn" class="pure-button pure-button-primary" href="javascript:toggleUploadSSForm();">Upload Screenshot</a>
</div>
{{ end }}
</div>
</div>
<hr />
<div class="left">
<h3>Team Members</h3>
<table class="center padding hide">
<thead>
<tr>
<th>Name</th>
<th>Slack ID</th>
<th>Twitter</th>
<th>Email</th>
<th>Remove</th>
</tr>
</thead>
<tbody>
{{ range $i, $v := .TemplateData.Members }}
<tr>
<td>{{ $v.Name }}</td>
<td>{{ $v.SlackId }}</td>
<td>{{ $v.Twitter }}</td>
<td>{{ $v.Email }}</td>
<td>
<form action="/team/{{ $uuid }}/deletemember" method="POST">
<input type="hidden" name="memberid" value="{{ $v.UUID }}"/>
<button type="submit" class="pure-button pure-button-error"><i class="fa fa-trash"></i></button>
</form>
</td>
</tr>
{{ end }}
<tr>
<td colspan="6">Add a new member</td>
</tr>
<tr>
<td colspan="6" class="padding">
<form class="pure-form" action="/team/{{ $uuid }}/savemember" method="POST">
<div class="pure-control-group">
<input id="newmembername" name="newmembername" value="" placeholder="Member Name" autofocus />
<input id="newmemberslackid" name="newmemberslackid" value="" placeholder="@SlackID" />
<input id="newmembertwitter" name="newmembertwitter" value="" placeholder="@Twitter" />
<input id="newmemberemail" name="newmemberemail" value="" placeholder="user@email.com" />
<button type="submit" class="pull-right space-sides pure-button pure-button-primary">Add</button>
</div>
</form>
</td>
</tr>
</tbody>
</table>
</div>
<div class="pure-control-group reset-pull">
<a href="/team/{{ $uuid }}" class="pull-left space pure-button pure-button-plain">Cancel</a>
</div>
</div>
<div id="uploadscreenshotform" style="display:none;">
<h3>Upload Screenshot</h3>
<form class="pure-form pure-form-aligned" action="/team/{{ $uuid }}/screenshotupload" method="POST" enctype="multipart/form-data">
<div class="pure-control-group" style="margin-bottom:50px;">
<input class="file" type="file" name="newssfile" multiple>
</div>
<a href="javascript:hideModal();" class="pull-left space-sides pure-button">Cancel</a>
<button type="submit" class="pull-right space-sides pure-button pure-button-primary">Add</button>
</form>
</div>
<div id="editscreenshotform" style="display:none;">
<div id="editss-container" class="pure-control-group" style="margin-bottom:50px;">
</div>
</div>
<script>
snack.listener(
{
node:document.getElementById('thumbnail-container'),
event:'click',
delegate: function(node) {
return node.getElementsByTagName('img');
}
},
function() {
showEditScreenShotModal(snack.wrap(this)[0]);
}
);
function showEditScreenShotModal(img) {
var newImg = img.cloneNode();
var editSSForm = document.getElementById('editscreenshotform');
var cont = document.getElementById('editss-container');
while(cont.hasChildNodes()) {
cont.removeChild(cont.lastChild);
}
cont.appendChild(newImg);
showModal({
title: 'Edit Screenshot',
bodyNode: editSSForm,
buttons: [
{ title: 'Delete', class: 'pure-button-error', position: 'right',
click: function() {
window.location = "/team/{{ $uuid }}/screenshotdelete/"+img.dataset.ssid;
}
},
{ title: 'Cancel', class: 'pure-button', position: 'right', click: hideModal }
]
});
editSSForm.style.display="block";
editSSForm.style.height="200px";
}
function toggleUploadSSForm() {
var uploadForm = document.getElementById('uploadscreenshotform');
showModal({
title: 'Upload Screenshot',
subtitle: '({{ .TemplateData.Name }})',
bodyNode: uploadForm
});
uploadForm.style.display="block";
document.getElementById('modal-body').style.height='165px';
}
</script>

View File

@ -104,15 +104,16 @@ function embiggenScreenshot(img) {
return; return;
} }
var container = document.getElementById('embiggenedScreenShot'); var container = document.getElementById('embiggenedScreenShot');
var clone = ss.cloneNode(true);
clone.classList.remove('thumbnail');
while(container.hasChildNodes()) { while(container.hasChildNodes()) {
container.removeChild(container.firstChild); container.removeChild(container.firstChild);
} }
var clickToCloseMsg = document.createElement('div'); var clickToCloseMsg = document.createElement('div');
clickToCloseMsg.innerText = "Click Image to Close"; clickToCloseMsg.innerText = "Click Image to Close";
var oImg = document.createElement("img");
oImg.setAttribute('src', '/image/'+ss.dataset.teamid+'/'+ss.dataset.ssid);
oImg.setAttribute('alt', ss.getAttribute('alt'));
container.appendChild(clickToCloseMsg); container.appendChild(clickToCloseMsg);
container.appendChild(clone); container.appendChild(oImg);
container.classList.remove('hidden'); container.classList.remove('hidden');
} }