admin: Handle dashboard later publication through evdist
This commit is contained in:
parent
5592fabefa
commit
79afaa8fb2
@ -7,7 +7,9 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@ -15,6 +17,7 @@ import (
|
||||
var DashboardDir string
|
||||
|
||||
func declarePublicRoutes(router *gin.RouterGroup) {
|
||||
router.GET("/public/", listPublic)
|
||||
router.GET("/public/:sid", getPublic)
|
||||
router.DELETE("/public/:sid", deletePublic)
|
||||
router.PUT("/public/:sid", savePublic)
|
||||
@ -32,6 +35,11 @@ type FICPublicDisplay struct {
|
||||
HideEvents bool `json:"hideEvents"`
|
||||
HideCountdown bool `json:"hideCountdown"`
|
||||
HideCarousel bool `json:"hideCarousel"`
|
||||
PropagationTime *time.Time `json:"propagationTime,omitempty"`
|
||||
}
|
||||
|
||||
func InitDashboardPresets(dir string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func readPublic(path string) (FICPublicDisplay, error) {
|
||||
@ -65,14 +73,62 @@ func savePublicTo(path string, s FICPublicDisplay) error {
|
||||
}
|
||||
}
|
||||
|
||||
type DashboardFiles struct {
|
||||
Presets []string `json:"presets"`
|
||||
Nexts []*NextDashboardFile `json:"nexts"`
|
||||
}
|
||||
|
||||
type NextDashboardFile struct {
|
||||
Name string `json:"name"`
|
||||
Screen int `json:"screen"`
|
||||
Date time.Time `json:"date"`
|
||||
}
|
||||
|
||||
func listPublic(c *gin.Context) {
|
||||
files, err := os.ReadDir(DashboardDir)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
var ret DashboardFiles
|
||||
for _, file := range files {
|
||||
if strings.HasPrefix(file.Name(), "preset-") {
|
||||
ret.Presets = append(ret.Presets, strings.TrimSuffix(strings.TrimPrefix(file.Name(), "preset-"), ".json"))
|
||||
continue
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(file.Name(), "public") || len(file.Name()) < 18 {
|
||||
continue
|
||||
}
|
||||
|
||||
ts, err := strconv.ParseInt(file.Name()[8:18], 10, 64)
|
||||
if err == nil {
|
||||
s, _ := strconv.Atoi(file.Name()[6:7])
|
||||
ret.Nexts = append(ret.Nexts, &NextDashboardFile{
|
||||
Name: file.Name()[6:18],
|
||||
Screen: s,
|
||||
Date: time.Unix(ts, 0),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, ret)
|
||||
}
|
||||
|
||||
func getPublic(c *gin.Context) {
|
||||
if strings.Contains(c.Params.ByName("sid"), "/") {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "sid cannot contains /"})
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := os.Stat(path.Join(DashboardDir, fmt.Sprintf("public%s.json", c.Params.ByName("sid")))); !os.IsNotExist(err) {
|
||||
p, err := readPublic(path.Join(DashboardDir, fmt.Sprintf("public%s.json", c.Params.ByName("sid"))))
|
||||
filename := fmt.Sprintf("public%s.json", c.Params.ByName("sid"))
|
||||
if strings.HasPrefix(c.Params.ByName("sid"), "preset-") {
|
||||
filename = fmt.Sprintf("%s.json", c.Params.ByName("sid"))
|
||||
}
|
||||
|
||||
if _, err := os.Stat(path.Join(DashboardDir, filename)); !os.IsNotExist(err) {
|
||||
p, err := readPublic(path.Join(DashboardDir, filename))
|
||||
if err != nil {
|
||||
log.Println("Unable to readPublic in getPublic:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during scene retrieval."})
|
||||
@ -92,11 +148,24 @@ func deletePublic(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := savePublicTo(path.Join(DashboardDir, fmt.Sprintf("public%s.json", c.Params.ByName("sid"))), FICPublicDisplay{}); err != nil {
|
||||
filename := fmt.Sprintf("public%s.json", c.Params.ByName("sid"))
|
||||
if strings.HasPrefix(c.Params.ByName("sid"), "preset-") {
|
||||
filename = fmt.Sprintf("%s.json", c.Params.ByName("sid"))
|
||||
}
|
||||
|
||||
if len(filename) == 12 {
|
||||
if err := savePublicTo(path.Join(DashboardDir, filename), FICPublicDisplay{}); err != nil {
|
||||
log.Println("Unable to deletePublic:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during scene deletion."})
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if err := os.Remove(path.Join(DashboardDir, filename)); err != nil {
|
||||
log.Println("Unable to deletePublic:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during scene deletion."})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, FICPublicDisplay{Scenes: []FICPublicScene{}, Side: []FICPublicScene{}})
|
||||
}
|
||||
@ -114,7 +183,20 @@ func savePublic(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := savePublicTo(path.Join(DashboardDir, fmt.Sprintf("public%s.json", c.Params.ByName("sid"))), scenes); err != nil {
|
||||
filename := fmt.Sprintf("public%s.json", c.Params.ByName("sid"))
|
||||
if c.Request.URL.Query().Has("t") {
|
||||
t, err := time.Parse(time.RFC3339, c.Request.URL.Query().Get("t"))
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
filename = fmt.Sprintf("public%s-%d.json", c.Params.ByName("sid"), t.Unix())
|
||||
} else if c.Request.URL.Query().Has("p") {
|
||||
filename = fmt.Sprintf("preset-%s.json", c.Request.URL.Query().Get("p"))
|
||||
}
|
||||
|
||||
if err := savePublicTo(path.Join(DashboardDir, filename), scenes); err != nil {
|
||||
log.Println("Unable to savePublicTo:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during scene saving."})
|
||||
return
|
||||
|
@ -247,6 +247,11 @@ func main() {
|
||||
api.ApplySettings(config)
|
||||
}
|
||||
|
||||
// Initialize dashboard presets
|
||||
if err = api.InitDashboardPresets(api.DashboardDir); err != nil {
|
||||
log.Println("Unable to initialize dashboards presets:", err)
|
||||
}
|
||||
|
||||
// Database connection
|
||||
log.Println("Opening database...")
|
||||
if err = fic.DBInit(*dsn); err != nil {
|
||||
|
@ -962,9 +962,12 @@ angular.module("FICApp")
|
||||
})
|
||||
|
||||
.controller("PublicController", function($scope, $rootScope, $routeParams, $location, Scene, Theme, Teams, Exercice) {
|
||||
$scope.propagationtime = null;
|
||||
$scope.presetName = "";
|
||||
$scope.screens = [0,1,2,3,4,5,6,7,8,9];
|
||||
$scope.screenid = $routeParams.screenId;
|
||||
$scope.display = Scene.get({ screenId: $routeParams.screenId });
|
||||
$scope.listScenes = Scene.get();
|
||||
$scope.themes = Theme.query();
|
||||
$scope.teams = Teams.get();
|
||||
|
||||
@ -1221,6 +1224,32 @@ angular.module("FICApp")
|
||||
}
|
||||
}
|
||||
|
||||
$scope.loadFile = function(fname) {
|
||||
$scope.display = Scene.get({ screenId: fname });
|
||||
$scope.someUpdt = true;
|
||||
};
|
||||
$scope.deleteFile = function(fname) {
|
||||
Scene.delete({ screenId: fname });
|
||||
$scope.listScenes = Scene.get();
|
||||
};
|
||||
$scope.latePropagation = function() {
|
||||
$scope.someUpdt = false;
|
||||
var prms = Scene.update({ screenId: $scope.screenid, t: $scope.propagationTime }, $scope.display);
|
||||
prms.$promise.then(function() {
|
||||
$scope.addToast('success', 'Scene successfully planned!');
|
||||
}, function(response) {
|
||||
$scope.addToast('danger', 'An error occurs when planning scene:', response.data.errmsg);
|
||||
});
|
||||
};
|
||||
$scope.savePreset = function() {
|
||||
$scope.someUpdt = false;
|
||||
var prms = Scene.update({ screenId: $scope.screenid, p: $scope.presetName }, $scope.display);
|
||||
prms.$promise.then(function() {
|
||||
$scope.addToast('success', 'Preset successfully saved!');
|
||||
}, function(response) {
|
||||
$scope.addToast('danger', 'An error occurs when saving preset:', response.data.errmsg);
|
||||
});
|
||||
};
|
||||
$scope.saveScenes = function() {
|
||||
$scope.someUpdt = false;
|
||||
var prms = Scene.update({ screenId: $scope.screenid }, $scope.display);
|
||||
|
@ -4,7 +4,7 @@
|
||||
<select class="custom-select" style="width: auto" id="screenid" ng-model="screenid" ng-options="k as 'Écran ' + v for (k, v) in screens" ng-change="chScreen()"></select>
|
||||
<div class="float-right dropdown">
|
||||
<button type="button" class="btn btn-secondary dropdown-toggle mr-sm-2" type="button" data-toggle="dropdown">
|
||||
Charger scène
|
||||
Scène prédéfinie
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item" ng-click="presetScene('registration')">Enregistrement équipes</a>
|
||||
@ -19,12 +19,44 @@
|
||||
<a class="dropdown-item" ng-click="presetScene('freehintquarter')">Free Hint Quarter</a>
|
||||
<a class="dropdown-item" ng-click="presetScene('happyhour')">Happy Hour</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a href="#" class="dropdown-item" ng-repeat="preset in listScenes.presets" ng-click="loadFile('preset-' + preset)">
|
||||
{{ preset }}
|
||||
<button class="btn btn-sm btn-outline-danger" ng-click="deleteFile('preset-' + preset); $event.preventDefault();"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span></button>
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" ng-click="clearScene()">Scène vide</a>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" ng-click="addSide()" class="float-right btn btn-primary mr-sm-2"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span> élm. côté</button>
|
||||
<button type="button" ng-click="addScene()" class="float-right btn btn-primary mr-sm-2"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span> élm. central</button>
|
||||
<button type="submit" class="float-right btn mr-sm-2" ng-class="{'btn-success': someUpdt,'btn-secondary': !someUpdt}"><span class="glyphicon glyphicon-save" aria-hidden="true"></span> Publier cette scène</button>
|
||||
<div class="float-right btn-group mr-sm-2">
|
||||
<button type="submit" class="btn" ng-class="{'btn-success': someUpdt,'btn-secondary': !someUpdt}"><span class="glyphicon glyphicon-blackboard" aria-hidden="true"></span> Publier cette scène</button>
|
||||
<button type="button" class="btn dropdown-toggle dropdown-toggle-split" ng-class="{'btn-success': someUpdt,'btn-secondary': !someUpdt}" data-toggle="dropdown" aria-expanded="false">
|
||||
<span class="sr-only">Toggle Dropdown</span>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<div class="px-3 py-1">
|
||||
<div class="form-group">
|
||||
<label for="public-propagation-time">Publier à :</label>
|
||||
<input type="datetime-local" id="public-propagation-time" class="form-control" ng-model="propagationTime">
|
||||
</div>
|
||||
<button type="button" ng-disabled="!propagationTime" ng-click="latePropagation()" class="btn btn-primary w-100"><span class="glyphicon glyphicon-time" aria-hidden="true"></span> Publier plus tard</button>
|
||||
</div>
|
||||
<div class="dropdown-divider"></div>
|
||||
<div class="px-3 py-1">
|
||||
<div class="form-group">
|
||||
<label for="public-preset-name">Sauvegarder comme :</label>
|
||||
<input id="public-preset-name" class="form-control" placeholder="Scène de départ" ng-model="presetName">
|
||||
</div>
|
||||
<button type="button" ng-disabled="!presetName" ng-click="savePreset()" class="btn btn-primary w-100"><span class="glyphicon glyphicon-download-alt" aria-hidden="true"></span> Sauvegarder</button>
|
||||
</div>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a href="#" class="dropdown-item" ng-repeat="next in listScenes.nexts" ng-if="next.screen == screenid" ng-click="loadFile(next.name)">
|
||||
{{ next.date | date:'dd/MM/yy HH:mm:ss' }}
|
||||
<button class="btn btn-sm btn-outline-danger" ng-click="deleteFile(next.name); $event.preventDefault();"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span></button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</h2>
|
||||
|
||||
<div class="row">
|
||||
|
Loading…
x
Reference in New Issue
Block a user