dashboard: can now change the sidebar
This commit is contained in:
parent
196f10dc9f
commit
a4e0a90adf
@ -22,8 +22,17 @@ type FICPublicScene struct {
|
||||
Params map[string]interface{} `json:"params"`
|
||||
}
|
||||
|
||||
func readPublic(path string) ([]FICPublicScene, error) {
|
||||
var s []FICPublicScene
|
||||
type FICPublicDisplay struct {
|
||||
Scenes []FICPublicScene `json:"scenes"`
|
||||
Side []FICPublicScene `json:"side"`
|
||||
CustomCountdown map[string]interface{} `json:"customCountdown"`
|
||||
HideEvents bool `json:"hideEvents"`
|
||||
HideCountdown bool `json:"hideCountdown"`
|
||||
HideCarousel bool `json:"hideCarousel"`
|
||||
}
|
||||
|
||||
func readPublic(path string) (FICPublicDisplay, error) {
|
||||
var s FICPublicDisplay
|
||||
if fd, err := os.Open(path); err != nil {
|
||||
return s, err
|
||||
} else {
|
||||
@ -38,7 +47,7 @@ func readPublic(path string) ([]FICPublicScene, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func savePublicTo(path string, s []FICPublicScene) error {
|
||||
func savePublicTo(path string, s FICPublicDisplay) error {
|
||||
if fd, err := os.Create(path); err != nil {
|
||||
return err
|
||||
} else {
|
||||
@ -57,20 +66,20 @@ func getPublic(ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
if _, err := os.Stat(path.Join(DashboardDir, fmt.Sprintf("public%s.json", ps.ByName("sid")))); !os.IsNotExist(err) {
|
||||
return readPublic(path.Join(DashboardDir, fmt.Sprintf("public%s.json", ps.ByName("sid"))))
|
||||
} else {
|
||||
return []FICPublicScene{}, nil
|
||||
return FICPublicDisplay{Scenes: []FICPublicScene{}, Side: []FICPublicScene{}}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func deletePublic(ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
if err := savePublicTo(path.Join(DashboardDir, fmt.Sprintf("public%s.json", ps.ByName("sid"))), []FICPublicScene{}); err != nil {
|
||||
if err := savePublicTo(path.Join(DashboardDir, fmt.Sprintf("public%s.json", ps.ByName("sid"))), FICPublicDisplay{}); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return []FICPublicScene{}, err
|
||||
return FICPublicDisplay{Scenes: []FICPublicScene{}, Side: []FICPublicScene{}}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func savePublic(ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
var scenes []FICPublicScene
|
||||
var scenes FICPublicDisplay
|
||||
if err := json.Unmarshal(body, &scenes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -165,7 +165,7 @@ angular.module("FICApp")
|
||||
})
|
||||
.factory("Scene", function($resource) {
|
||||
return $resource("/api/public/:screenId", { screenId: '@id' }, {
|
||||
'update': {method: 'PUT', isArray: true},
|
||||
'update': {method: 'PUT'},
|
||||
})
|
||||
})
|
||||
.factory("Team", function($resource) {
|
||||
@ -573,7 +573,7 @@ angular.module("FICApp")
|
||||
.controller("PublicController", function($scope, $rootScope, $routeParams, $location, Scene, Theme, Teams, Exercice) {
|
||||
$scope.screens = [0,1,2,3,4,5,6,7,8,9];
|
||||
$scope.screenid = $routeParams.screenId;
|
||||
$scope.scenes = Scene.query({ screenId: $routeParams.screenId });
|
||||
$scope.display = Scene.get({ screenId: $routeParams.screenId });
|
||||
$scope.themes = Theme.query();
|
||||
$scope.teams = Teams.get();
|
||||
|
||||
@ -594,6 +594,14 @@ angular.module("FICApp")
|
||||
"table": "Tableau",
|
||||
"rank": "Classement",
|
||||
};
|
||||
$scope.typeside = {
|
||||
"welcome": "Messages de bienvenue",
|
||||
"themes": "Présentation des thèmes",
|
||||
"exercice_follow": "Dernier exercice des événements",
|
||||
"exercice": "Exercice",
|
||||
"message": "Message",
|
||||
"panel": "Boîte",
|
||||
};
|
||||
$scope.welcome_types = {
|
||||
"teams": "Accueil des équipes",
|
||||
"public": "Accueil du public",
|
||||
@ -625,12 +633,12 @@ angular.module("FICApp")
|
||||
|
||||
$scope.clearScene = function() {
|
||||
$scope.someUpdt = true;
|
||||
$scope.scenes = [];
|
||||
$scope.display.scenes = [];
|
||||
};
|
||||
$scope.presetScene = function(scene) {
|
||||
$scope.someUpdt = true;
|
||||
if (scene == "registration")
|
||||
$scope.scenes = [
|
||||
if (scene == "registration") {
|
||||
$scope.display.scenes = [
|
||||
{
|
||||
type: "welcome",
|
||||
params: { kind: "teams" },
|
||||
@ -640,19 +648,29 @@ angular.module("FICApp")
|
||||
params: { kind: "public", notitle: true },
|
||||
},
|
||||
];
|
||||
else if (scene == "welcome")
|
||||
$scope.scenes = [
|
||||
$scope.display.side = [
|
||||
{
|
||||
type: "welcome",
|
||||
params: { kind: "public" },
|
||||
type: "themes",
|
||||
params: { },
|
||||
},
|
||||
];
|
||||
}
|
||||
else if (scene == "welcome") {
|
||||
$scope.display.scenes = [
|
||||
{
|
||||
type: "carousel",
|
||||
params: { color: "info", kind: "themes", title: "Présentation des entreprises ciblées"},
|
||||
},
|
||||
];
|
||||
else if (scene == "start")
|
||||
$scope.scenes = [
|
||||
$scope.display.side = [
|
||||
{
|
||||
type: "welcome",
|
||||
params: { kind: "public" },
|
||||
},
|
||||
];
|
||||
}
|
||||
else if (scene == "start") {
|
||||
$scope.display.scenes = [
|
||||
{
|
||||
type: "welcome",
|
||||
params: { kind: "public" },
|
||||
@ -662,8 +680,15 @@ angular.module("FICApp")
|
||||
params: { color: "success", end: null, lead: "Go, go, go !", title: "Le challenge forensic va bientôt commencer !" },
|
||||
},
|
||||
];
|
||||
$scope.display.side = [
|
||||
{
|
||||
type: "themes",
|
||||
params: { },
|
||||
},
|
||||
];
|
||||
}
|
||||
else if (scene == "summary") {
|
||||
$scope.scenes = [
|
||||
$scope.display.scenes = [
|
||||
{
|
||||
type: "table",
|
||||
params: { kind: "levels", levels: [1,2,3,4,5], themes: $scope.themes.map(function(z, i) { return z.id; }), total: true },
|
||||
@ -673,57 +698,107 @@ angular.module("FICApp")
|
||||
params: { limit: 10, which: "general" },
|
||||
},
|
||||
];
|
||||
}
|
||||
else if (scene == "happyhour") {
|
||||
$scope.scenes = [
|
||||
$scope.display.side = [
|
||||
{
|
||||
type: "countdown",
|
||||
params: { color: "warning", end: new Date(Date.now() + 3602000).toISOString(), lead: "Terminé !", title: "Heure joyeuse : chaque résolution compte double !" },
|
||||
},
|
||||
{
|
||||
type: "rank",
|
||||
params: { limit: 10, which: "general" },
|
||||
type: "exercice_follow",
|
||||
params: { },
|
||||
},
|
||||
];
|
||||
}
|
||||
else if (scene == "happyhour") {
|
||||
$scope.display.customCountdown = {
|
||||
show: true,
|
||||
shadow: "#E8CF5C",
|
||||
end: new Date(Date.now() + 1802000).toISOString(),
|
||||
before: "Heure joyeuse : chaque résolution compte double !",
|
||||
after: "Heure joyeuse terminée !",
|
||||
}
|
||||
}
|
||||
else if (scene == "freehintquarter") {
|
||||
$scope.display.customCountdown = {
|
||||
show: true,
|
||||
shadow: "#3DD28F",
|
||||
end: new Date(Date.now() + 902000).toISOString(),
|
||||
before: "Quart d'heure facile : indices dévoilés !",
|
||||
after: "Quart d'heure facile terminée !",
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$scope.genSceneCountdownDate = function(scene, duration) {
|
||||
scene.params.end = (new Date(Date.now() + duration)).toISOString();
|
||||
}
|
||||
$scope.genCustomCountdownDate = function(duration) {
|
||||
$scope.display.customCountdown.end = (new Date(Date.now() + duration)).toISOString();
|
||||
}
|
||||
|
||||
$scope.saveScenes = function() {
|
||||
$scope.someUpdt = false;
|
||||
var prms = Scene.update({ screenId: $scope.screenid }, $scope.scenes);
|
||||
var prms = Scene.update({ screenId: $scope.screenid }, $scope.display);
|
||||
prms.$promise.then(function() {
|
||||
$rootScope.newBox('success', 'Scene successfully published!');
|
||||
}, function(response) {
|
||||
$rootScope.newBox('danger', 'An error occurs when saving scene:', response.data.errmsg);
|
||||
});
|
||||
};
|
||||
$scope.addSide = function() {
|
||||
$scope.someUpdt = true;
|
||||
$scope.display.side.push({params: {}});
|
||||
};
|
||||
$scope.delSide = function(s) {
|
||||
$scope.someUpdt = true;
|
||||
angular.forEach($scope.display.side, function(scene, k) {
|
||||
if (scene == s)
|
||||
$scope.display.side.splice(k, 1);
|
||||
});
|
||||
};
|
||||
$scope.upSide = function(s) {
|
||||
$scope.someUpdt = true;
|
||||
angular.forEach($scope.display.side, function(scene, k) {
|
||||
if (scene == s && k > 0) {
|
||||
$scope.display.side.splice(k, 1);
|
||||
$scope.display.side.splice(k - 1, 0, scene);
|
||||
}
|
||||
});
|
||||
};
|
||||
$scope.downSide = function(s) {
|
||||
$scope.someUpdt = true;
|
||||
var move = true;
|
||||
angular.forEach($scope.display.side, function(scene, k) {
|
||||
if (move && scene == s) {
|
||||
$scope.display.side.splice(k, 1);
|
||||
$scope.display.side.splice(k + 1, 0, scene);
|
||||
move = false;
|
||||
}
|
||||
});
|
||||
};
|
||||
$scope.addScene = function() {
|
||||
$scope.someUpdt = true;
|
||||
$scope.scenes.push({params: {}});
|
||||
$scope.display.scenes.push({params: {}});
|
||||
};
|
||||
$scope.delScene = function(s) {
|
||||
$scope.someUpdt = true;
|
||||
angular.forEach($scope.scenes, function(scene, k) {
|
||||
angular.forEach($scope.display.scenes, function(scene, k) {
|
||||
if (scene == s)
|
||||
$scope.scenes.splice(k, 1);
|
||||
$scope.display.scenes.splice(k, 1);
|
||||
});
|
||||
};
|
||||
$scope.upScene = function(s) {
|
||||
$scope.someUpdt = true;
|
||||
angular.forEach($scope.scenes, function(scene, k) {
|
||||
angular.forEach($scope.display.scenes, function(scene, k) {
|
||||
if (scene == s && k > 0) {
|
||||
$scope.scenes.splice(k, 1);
|
||||
$scope.scenes.splice(k - 1, 0, scene);
|
||||
$scope.display.scenes.splice(k, 1);
|
||||
$scope.display.scenes.splice(k - 1, 0, scene);
|
||||
}
|
||||
});
|
||||
};
|
||||
$scope.downScene = function(s) {
|
||||
$scope.someUpdt = true;
|
||||
var move = true;
|
||||
angular.forEach($scope.scenes, function(scene, k) {
|
||||
angular.forEach($scope.display.scenes, function(scene, k) {
|
||||
if (move && scene == s) {
|
||||
$scope.scenes.splice(k, 1);
|
||||
$scope.scenes.splice(k + 1, 0, scene);
|
||||
$scope.display.scenes.splice(k, 1);
|
||||
$scope.display.scenes.splice(k + 1, 0, scene);
|
||||
move = false;
|
||||
}
|
||||
});
|
||||
|
@ -13,21 +13,25 @@
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" ng-click="presetScene('summary')">Résumé</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<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 class="dropdown-item" ng-click="clearScene()">Scène vide</a>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" ng-click="addScene()" class="float-right btn btn-primary mr-sm-2"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Ajouter un élément</button>
|
||||
<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>
|
||||
</h2>
|
||||
|
||||
<div class="alert alert-info" ng-if="!scenes.length">
|
||||
<div class="row">
|
||||
<div class="col-9">
|
||||
<div class="alert alert-info" ng-if="!display.scenes.length">
|
||||
<strong ng-if="!someUpdt">Aucun contenu n'est actuellement affiché.</strong>
|
||||
<strong ng-if="someUpdt">Aucun contenu à afficher.</strong>
|
||||
</div>
|
||||
|
||||
<div class="card mb-2" ng-repeat="scene in scenes" ng-class="{'bg-secondary': !scene.params.hide, 'bg-light': scene.params.hide}">
|
||||
<div class="card mb-2" ng-repeat="scene in display.scenes" ng-class="{'bg-secondary': !scene.params.hide, 'bg-light': scene.params.hide}">
|
||||
<div class="card-body">
|
||||
|
||||
<div class="form-group row">
|
||||
@ -44,7 +48,7 @@
|
||||
</div>
|
||||
<button type="button" ng-click="upScene(scene)" class="btn btn-sm btn-light mb-2 mr-sm-2 mb-sm-0"><span class="glyphicon glyphicon-chevron-up" aria-hidden="true"></span></button>
|
||||
<button type="button" ng-click="downScene(scene)" class="btn btn-sm btn-light mb-2 mr-sm-2 mb-sm-0"><span class="glyphicon glyphicon-chevron-down" aria-hidden="true"></span></button>
|
||||
<button type="button" ng-click="delScene(scene)" class="btn btn-sm btn-warning mb-2 mr-sm-2 mb-sm-0"><span class="glyphicon glyphicon-minus" aria-hidden="true"></span> Supprimer</button>
|
||||
<button type="button" ng-click="delScene(scene)" class="btn btn-sm btn-warning mb-2 mr-sm-2 mb-sm-0"><span class="glyphicon glyphicon-minus" aria-hidden="true"></span></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -96,7 +100,19 @@
|
||||
<div class="form-group row" ng-if="scene.type == 'countdown'">
|
||||
<label for="cend" class="col-sm-2 col-form-label col-form-label-sm">Date de fin</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" id="cend" ng-model="scene.params.end" class="form-control form-control-sm">
|
||||
<div class="input-group mb-3">
|
||||
<input type="text" id="cend" ng-model="scene.params.end" class="form-control form-control-sm">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-sm btn-light dropdown-toggle" type="button" data-toggle="dropdown"></button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item" ng-click="genSceneCountdownDate(scene, 310000)">5 minutes</a>
|
||||
<a class="dropdown-item" ng-click="genSceneCountdownDate(scene, 910000)">15 minutes</a>
|
||||
<a class="dropdown-item" ng-click="genSceneCountdownDate(scene, 1100000)">30 minutes</a>
|
||||
<a class="dropdown-item" ng-click="genSceneCountdownDate(scene, 3700000)">1 heure</a>
|
||||
<a class="dropdown-item" ng-click="genSceneCountdownDate(scene, 5500000)">1.5 heure</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -179,4 +195,129 @@
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<div class="card mb-2" ng-repeat="scene in display.side" ng-class="{'bg-secondary': !scene.params.hide, 'bg-light': scene.params.hide}">
|
||||
<div class="card-body">
|
||||
<div class="text-right mb-2">
|
||||
<div class="btn-group-toggle" data-toggle="buttons" style="display: inline-block;">
|
||||
<label class="btn btn-sm btn-secondary" ng-class="{'active':!scene.params.hide}">
|
||||
<input type="checkbox" ng-model="scene.params.hide" autocomplete="off"><i class="glyphicon" ng-class="{'glyphicon-eye-open':scene.params.hide,'glyphicon-eye-close':!scene.params.hide}"></i>
|
||||
</label>
|
||||
</div>
|
||||
<button type="button" ng-click="upSide(scene)" class="btn btn-sm btn-light mb-2 mr-sm-2 mb-sm-0"><span class="glyphicon glyphicon-chevron-up" aria-hidden="true"></span></button>
|
||||
<button type="button" ng-click="downSide(scene)" class="btn btn-sm btn-light mb-2 mr-sm-2 mb-sm-0"><span class="glyphicon glyphicon-chevron-down" aria-hidden="true"></span></button>
|
||||
<button type="button" ng-click="delSide(scene)" class="btn btn-sm btn-warning mb-2 mr-sm-2 mb-sm-0"><span class="glyphicon glyphicon-minus" aria-hidden="true"></span></button>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<select class="custom-select custom-select-sm" id="type" ng-model="scene.type" ng-options="k as v for (k, v) in typeside"></select>
|
||||
</div>
|
||||
|
||||
<div class="form-group row" ng-if="scene.type == 'panel' || scene.type == 'carousel' || scene.type == 'countdown'">
|
||||
<label for="ptype" class="col-sm-4 col-form-label col-form-label-sm">Couleur</label>
|
||||
<div class="col-sm-8">
|
||||
<select class="custom-select custom-select-sm" id="ptype" ng-model="scene.params.color" ng-options="k as v for (k, v) in colors"></select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row" ng-if="scene.type == 'message' || scene.type == 'panel' || scene.type == 'carousel' || scene.type == 'countdown'">
|
||||
<label for="mtitle" class="col-sm-4 col-form-label col-form-label-sm">Titre</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="text" id="mtitle" ng-model="scene.params.title" class="form-control form-control-sm">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row" ng-if="scene.type == 'message' || scene.type == 'panel' || scene.type == 'countdown'">
|
||||
<label for="mlead" class="col-sm-4 col-form-label col-form-label-sm" ng-if="scene.type != 'countdown'">Lead</label>
|
||||
<label for="mlead" class="col-sm-4 col-form-label col-form-label-sm" ng-if="scene.type == 'countdown'">Final</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="text" id="mlead" ng-model="scene.params.lead" class="form-control form-control-sm">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row" ng-if="scene.type == 'message' || scene.type == 'panel'">
|
||||
<label for="mcnt" class="col-sm-4 col-form-label col-form-label-sm">HTML</label>
|
||||
<div class="col-sm-8">
|
||||
<textarea class="form-control form-control-sm" id="mcnt" ng-model="scene.params.html"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-if="scene.type == 'exercice'">
|
||||
<select class="custom-select custom-select-sm" id="eex" ng-model="scene.params.exercice" ng-options="ex.id as ex.title group by ex.path.split('/')[0] for ex in exercices"></select>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<label class="custom-control custom-checkbox">
|
||||
<input class="custom-control-input" type="checkbox" ng-model="display.customCountdown.show">
|
||||
<span class="custom-control-label">Activer timer secondaire</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="card-body" ng-if="display.customCountdown && display.customCountdown.show">
|
||||
<div class="form-group row">
|
||||
<label for="timertxtbefore" class="col-sm-5 col-form-label col-form-label-sm">Titre avant</label>
|
||||
<div class="col-sm-7">
|
||||
<input type="text" id="timertxtbefore" ng-model="display.customCountdown.before" class="form-control form-control-sm">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="timertxtafter" class="col-sm-5 col-form-label col-form-label-sm">Titre après</label>
|
||||
<div class="col-sm-7">
|
||||
<input type="text" id="timertxtafter" ng-model="display.customCountdown.after" class="form-control form-control-sm">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="timercolor" class="col-sm-5 col-form-label col-form-label-sm">Couleur</label>
|
||||
<div class="col-sm-7">
|
||||
<input type="color" id="timercolor" ng-model="display.customCountdown.shadow" class="form-control form-control-sm">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="timerend" class="col-sm-5 col-form-label col-form-label-sm">Évent</label>
|
||||
<div class="col-sm-7">
|
||||
<div class="input-group mb-3">
|
||||
<input type="text" id="timerend" ng-model="display.customCountdown.end" class="form-control form-control-sm">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-sm btn-outline-secondary dropdown-toggle" type="button" data-toggle="dropdown"></button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item" ng-click="genCustomCountdownDate(310000)">5 minutes</a>
|
||||
<a class="dropdown-item" ng-click="genCustomCountdownDate(910000)">15 minutes</a>
|
||||
<a class="dropdown-item" ng-click="genCustomCountdownDate(1100000)">30 minutes</a>
|
||||
<a class="dropdown-item" ng-click="genCustomCountdownDate(3700000)">1 heure</a>
|
||||
<a class="dropdown-item" ng-click="genCustomCountdownDate(5500000)">1.5 heure</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-check">
|
||||
<label class="custom-control custom-checkbox">
|
||||
<input class="custom-control-input" type="checkbox" ng-model="display.hideEvents">
|
||||
<span class="custom-control-label">Cacher les événements</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-check">
|
||||
<label class="custom-control custom-checkbox">
|
||||
<input class="custom-control-input" type="checkbox" ng-model="display.hideCountdown">
|
||||
<span class="custom-control-label">Cacher le timer</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-check">
|
||||
<label class="custom-control custom-checkbox">
|
||||
<input class="custom-control-input" type="checkbox" ng-model="display.hideCarousel">
|
||||
<span class="custom-control-label">Cacher le carousel</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -51,6 +51,9 @@ func init() {
|
||||
w.Header().Set("Cache-Control", "no-cache")
|
||||
http.ServeFile(w, r, path.Join(TeamsDir, "public", "stats.json"))
|
||||
})
|
||||
api.Router().GET("/api/teams/:tid/stats.json", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
http.Redirect(w, r, "http://127.0.0.1:8081/api/teams/" + string(ps.ByName("tid")) + "/stats.json", 302)
|
||||
})
|
||||
api.Router().GET("/settings.json", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
w.Header().Set("X-FIC-Time", fmt.Sprintf("%f", float64(time.Now().UnixNano()/1000)/1000000))
|
||||
w.Header().Set("Cache-Control", "no-cache")
|
||||
|
@ -23,7 +23,7 @@
|
||||
</div>
|
||||
</noscript>
|
||||
|
||||
<div ng-repeat="(k,s) in scene" class="repeatedd-item" style="margin-bottom: 15px;" ng-cloak>
|
||||
<div ng-repeat="(k,s) in display.scenes" class="repeatedd-item" style="margin-bottom: 15px;" ng-cloak>
|
||||
|
||||
<div class="card niceborder bg-dark" ng-if="s.type == 'welcome' && !s.params.hide && s.params.kind == 'teams'">
|
||||
<div class="card-body text-light text-indent">
|
||||
@ -85,7 +85,7 @@
|
||||
<div class="card-header bg-{{ s.params.color }} text-white" ng-if="s.params.title">
|
||||
<h3 style="margin:0" ng-bind="s.params.title"></h3>
|
||||
</div>
|
||||
<div class="card-body text-light" ng-if="s.params.text || s.params.lead">
|
||||
<div class="card-body" ng-if="s.params.text || s.params.lead">
|
||||
<h4 ng-if="s.params.lead" class="card-title text-justify" ng-bind="s.params.lead"></h4>
|
||||
<p class="card-text" ng-bind="s.params.text"></p>
|
||||
</div>
|
||||
@ -98,7 +98,7 @@
|
||||
<strong>Challenge <em>{{ themes[my.exercices[s.params.exercice].theme_id].exercices[s.params.exercice].title }}</em> du thème {{ themes[my.exercices[s.params.exercice].theme_id].name }}</strong>
|
||||
<small class="authors" ng-if="themes[my.exercices[s.params.exercice].theme_id].authors">par {{ themes[my.exercices[s.params.exercice].theme_id].authors | stripHTML }}</small>
|
||||
</p>
|
||||
<p ng-bind-html="my.exercices[s.params.exercice].statement"></p>
|
||||
<p ng-bind-html="my.exercices[s.params.exercice].overview"></p>
|
||||
<ul class="list-inline text-secondary">
|
||||
<li>Rapporte <ng-pluralize count="themes[my.exercices[s.params.exercice].theme_id].exercices[s.params.exercice].gain" when="{'0': 'aucun point', 'one': '{} point', 'other': '{} points'}"></ng-pluralize></li>
|
||||
<li ng-if="my.exercices[s.params.exercice].files"><ng-pluralize count="my.exercices[s.params.exercice].files.length" when="{'0': 'Aucun fichier disponible', 'one': '{} fichier disponible', 'other': '{} fichiers disponibles'}"></ng-pluralize></li>
|
||||
@ -130,7 +130,7 @@
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody class="table-bordered" ng-if="s.params.kind == 'teams'">
|
||||
<tr ng-repeat="team in rank | orderBy: 'rank' | limitTo: s.params.limit: s.params.begin" ng-if="s.params.teams.indexOf(team.id-0) !== -1" ng-controller="TeamController">
|
||||
<tr ng-repeat="team in teams" ng-if="s.params.teams.indexOf(team.id) !== -1" ng-controller="TeamController">
|
||||
<th class="text-center">{{ team.rank }}<sup ng-if="team.rank == 1">er</sup><sup ng-if="team.rank > 1">e</sup><br><span style="text-overflow: ellipsis; display: inline-block; width: 82px;overflow: hidden;">{{ team.name }}</span></th>
|
||||
<td ng-repeat="(tid,th) in themes" class="text-center" ng-if="mystats && s.params.themes.indexOf(tid-0) !== -1">
|
||||
<span ng-class="{'text-success': mystats.themes[tid].solved}">{{ mystats.themes[tid].solved }}/{{ mystats.themes[tid].total }}</span>
|
||||
@ -249,7 +249,7 @@
|
||||
<br>
|
||||
<small class="authors" ng-if="themes[exercice.theme_id].authors">par {{ themes[exercice.theme_id].authors }}</small>
|
||||
</p>
|
||||
<p ng-bind-html="exercice.statement"></p>
|
||||
<p ng-bind-html="exercice.overview"></p>
|
||||
<ul class="list-inline text-secondary">
|
||||
<li>Rapporte <ng-pluralize count="themes[exercice.theme_id].exercices[eid].gain" when="{'0': 'aucun point', 'one': '{} point', 'other': '{} points'}"></ng-pluralize></li>
|
||||
<li ng-if="exercice.files"><ng-pluralize count="exercice.files.length" when="{'0': 'Aucun fichier disponible', 'one': '{} fichier disponible', 'other': '{} fichiers disponibles'}"></ng-pluralize></li>
|
||||
@ -271,8 +271,110 @@
|
||||
|
||||
<div class="col-4">
|
||||
|
||||
<div id="themesSummary" ng-cloak>
|
||||
|
||||
<div ng-repeat="(k,s) in display.side" class="repeatedd-item" style="margin-bottom: 7px;">
|
||||
<div class="card niceborder bg-dark" ng-if="s.type == 'welcome' && !s.params.hide">
|
||||
<div class="card-body text-light text-indent" style="padding: 0.4rem;">
|
||||
<h1 ng-if="!s.params.notitle" class="text-center niceborder">Le challenge forensic !</h1>
|
||||
<p class="lead text-justify">
|
||||
Durant ce challenge, les équipes doivent <strong>remonter des scénarii
|
||||
d'attaques</strong> auxquels nos systèmes d'information <strong>font face
|
||||
chaque jour</strong> : fuite de données, compromission d'un poste de
|
||||
travail, exploitation de vulnérabilités d'un site web, ...
|
||||
</p>
|
||||
<p class="lead text-justify">
|
||||
Pour valider un challenge, chaque participant va télécharger :
|
||||
soit des <strong>journaux d'évènements</strong>, des extraits de trafic réseau
|
||||
ou même des <strong>copies</strong> figées <strong>de la mémoire vive</strong> de machines
|
||||
malveillantes, pour <strong>essayer de comprendre</strong> comment l'attaquant a
|
||||
<strong>contourné la sécurité</strong> de la machine et quelles actions hostiles
|
||||
ont été effectuées.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card niceborder bg-dark" ng-if="s.type == 'themes' && !s.params.hide">
|
||||
<div class="text-light text-bold text-right" style="position: absolute; z-index: 10; width: 100%; padding: 1rem;">
|
||||
<span style="background: rgba(50,50,50,0.66); padding: 0.2rem 0.4rem; border-radius: 3px; border-bottom-width: 3px;">
|
||||
Les entreprises ciblées
|
||||
</span>
|
||||
</div>
|
||||
<div class="card-body bg-dark">
|
||||
<div class="carousel slide" data-interval="12000" style="padding-bottom: 0px" autocarousel>
|
||||
<div class="carousel-inner">
|
||||
<div class="carousel-item" ng-repeat="theme in themes" ng-class="{active: $first}">
|
||||
<div class="carousel-caption text-indent">
|
||||
<div class="card-img-top theme-card" style="background-image: url('{{theme.image}}')"></div>
|
||||
<h3 class="text-left" ng-bind="theme.name"></h3>
|
||||
<p class="text-justify" style="font-size: 111%" ng-bind-html="theme.headline"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card niceborder bg-dark" ng-if="s.type == 'exercice' && !s.params.hide">
|
||||
<div class="card-img-top theme-card" style="background-image: url('{{themes[my.exercices[s.params.exercice].theme_id].image}}')"></div>
|
||||
<div class="card-body text-light">
|
||||
<h3 style="font-size: 1.0rem; text-weight: bold; overflow: hidden; text-overflow: ellipsis; white-space: nowrap">Défi <em>{{ themes[my.exercices[s.params.exercice].theme_id].exercices[s.params.exercice].title }}</em> du thème <em>{{ themes[my.exercices[s.params.exercice].theme_id].name }}</em></h3>
|
||||
<p ng-bind-html="my.exercices[s.params.exercice].overview"></p>
|
||||
<ul class="list-inline text-muted mr-2 ml-2 mb-1" style="font-size: 0.9rem">
|
||||
<li><ng-pluralize count="themes[my.exercices[s.params.exercice].theme_id].exercices[s.params.exercice].gain" when="{'0': 'aucun point', 'one': '{} point', 'other': '{} points'}"></ng-pluralize></li>
|
||||
<li ng-if="my.exercices[s.params.exercice].files"><ng-pluralize count="my.exercices[s.params.exercice].files.length" when="{'0': 'Aucun fichier', 'one': '{} fichier', 'other': '{} fichiers'}"></ng-pluralize></li>
|
||||
<li ng-if="my.exercices[s.params.exercice].hints.length"><ng-pluralize count="my.exercices[s.params.exercice].hints.length" when="{'0': 'Aucun indice disponible', 'one': '{} indice disponible', 'other': '{} indices disponibles'}"></ng-pluralize></li>
|
||||
<li>Tenté par <ng-pluralize count="themes[my.exercices[s.params.exercice].theme_id].exercices[s.params.exercice].tried" when="{'0': 'aucune équipe', 'one': '{} équipe', 'other': '{} équipes'}"></ng-pluralize></li>
|
||||
<li ng-if="my.exercices[s.params.exercice].tries">Résolu par <ng-pluralize count="themes[my.exercices[s.params.exercice].theme_id].exercices[s.params.exercice].solved" when="{'0': 'aucune équipe', 'one': '{} équipe', 'other': '{} équipes'}"></ng-pluralize></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card niceborder bg-dark" ng-if="s.type == 'exercice_follow' && !s.params.hide">
|
||||
<div class="text-light text-bold text-right" style="position: absolute; z-index: 10; width: 100%; padding: 1rem;">
|
||||
<span style="background: rgba(50,50,50,0.66); padding: 0.2rem 0.4rem; border-radius: 3px; border-bottom-width: 3px;">
|
||||
Challenges à la une
|
||||
</span>
|
||||
</div>
|
||||
<div class="card-img-top theme-card" style="background-image: url('{{themes[my.exercices[lastExercice].theme_id].image}}')"></div>
|
||||
<div class="card-body text-light">
|
||||
<h3 style="font-size: 1.0rem; text-weight: bold; overflow: hidden; text-overflow: ellipsis; white-space: nowrap"><em>{{ themes[my.exercices[lastExercice].theme_id].exercices[lastExercice].title }}</em> du thème <em>{{ themes[my.exercices[lastExercice].theme_id].name }}</em></h3>
|
||||
<p ng-bind-html="my.exercices[lastExercice].overview"></p>
|
||||
<ul class="list-inline text-muted mr-2 ml-2 mb-1" style="font-size: 0.9rem">
|
||||
<li><ng-pluralize count="themes[my.exercices[lastExercice].theme_id].exercices[lastExercice].gain" when="{'0': 'aucun point', 'one': '{} point', 'other': '{} points'}"></ng-pluralize></li>
|
||||
<li ng-if="my.exercices[lastExercice].files"><ng-pluralize count="my.exercices[lastExercice].files.length" when="{'0': 'Aucun fichier', 'one': '{} fichier', 'other': '{} fichiers'}"></ng-pluralize></li>
|
||||
<li ng-if="my.exercices[lastExercice].hints.length"><ng-pluralize count="my.exercices[lastExercice].hints.length" when="{'0': 'Aucun indice disponible', 'one': '{} indice disponible', 'other': '{} indices disponibles'}"></ng-pluralize></li>
|
||||
<li>Tenté par <ng-pluralize count="themes[my.exercices[lastExercice].theme_id].exercices[lastExercice].tried" when="{'0': 'aucune équipe', 'one': '{} équipe', 'other': '{} équipes'}"></ng-pluralize></li>
|
||||
<li ng-if="my.exercices[lastExercice].tries">Résolu par <ng-pluralize count="themes[my.exercices[lastExercice].theme_id].exercices[lastExercice].solved" when="{'0': 'aucune équipe', 'one': '{} équipe', 'other': '{} équipes'}"></ng-pluralize></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card niceborder bg-dark" ng-if="s.type == 'message' && !s.params.hide">
|
||||
<div class="card-body text-light">
|
||||
<h2 class="text-center niceborder ml-1 mr-1" ng-if="s.params.title" ng-bind="s.params.title"></h2>
|
||||
<p ng-if="s.params.lead" class="lead text-justify" ng-bind="s.params.lead"></p>
|
||||
<p ng-bind-html="s.params.html" ng-if="s.params.html"></p>
|
||||
<p ng-if="s.params.text" ng-bind="s.params.text"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card border-{{ s.params.color }} niceborder" ng-if="s.type == 'panel' && !s.params.hide">
|
||||
<div class="card-header bg-{{ s.params.color }} text-white" style="padding: 0" ng-if="s.params.title">
|
||||
<h3 style="background: none; margin:0" class="text-center" ng-bind="s.params.title"></h3>
|
||||
</div>
|
||||
<div class="card-body" ng-if="s.params.text || s.params.lead">
|
||||
<h4 ng-if="s.params.lead" class="card-title text-justify" ng-bind="s.params.lead"></h4>
|
||||
<p class="card-text" ng-bind="s.params.text"></p>
|
||||
</div>
|
||||
<div class="card-body" ng-if="s.params.html" ng-bind-html="s.params.html"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-controller="EventsController" id="eventsList" style="position:fixed;padding-right:10px">
|
||||
<div ng-repeat="e in events track by e.id" class="swap-animation" ng-cloak>
|
||||
<div ng-repeat="e in events track by e.id" class="swap-animation" ng-if="!display.hideEvents" ng-cloak>
|
||||
<div class="card card-sm niceborder" ng-class="e.kind">
|
||||
<div class="card-header text-right">
|
||||
<small class="text-muted">{{ e.since | since }}</small>
|
||||
@ -283,8 +385,19 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-controller="CountdownController" style="position: fixed; bottom: 109px; right: 0; width: 33vw; overflow: hidden; padding-top: 25px;" ng-cloak>
|
||||
<div class="bg-light text-center text-dark" ng-if="time.hours === 0 || time.hours" style="margin-top: -5px; width: inherit; box-shadow: white 0px -10px 15px 0px;">
|
||||
<div ng-controller="TimerController" ng-init="display.customCountdown.end?init(display.customCountdown.end):initStart()" style="position: fixed; bottom: {{ display.hideCountdown?'109px':'195px' }}; right: 0; width: 33vw; overflow: hidden; padding-top: 25px;" ng-if="display.customCountdown && display.customCountdown.show" ng-cloak>
|
||||
<div class="bg-light text-center text-dark" style="margin-top: -5px; width: inherit; box-shadow: white 0px -10px 15px 0px;">
|
||||
<div class="text-center clock" style="font-size: 50px;" ng-if="duration > 0">{{ duration / 60 | time }} <span class="point">:</span> {{ duration % 60 | time }}</div>
|
||||
<div class="text-center clock" style="font-size: 50px;" ng-if="duration <= 0">00 : 00</div>
|
||||
<div style="font-size: 18px; margin-top: -15px; text-shadow: 0 0 6px {{ display.customCountdown.shadow}};">
|
||||
<span ng-if="duration <= 0" ng-bind="display.customCountdown.after"></span>
|
||||
<span ng-if="duration > 0" ng-bind="display.customCountdown.before"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-controller="CountdownController" style="position: fixed; bottom: {{ display.hideCarousel?'0':'109px' }}; right: 0; width: 33vw; overflow: hidden; padding-top: 25px;" ng-if="!display.hideCountdown" ng-cloak>
|
||||
<div class="bg-light text-center text-dark" ng-if="time.hours === 0 || time.hours" style="margin-top: -5px; width: inherit; {{ display.customCountdown?'':'box-shadow: white 0px -10px 15px 0px;' }}">
|
||||
<div class="clock" ng-class="{expired: time.expired, end: time.end}" style="font-size: 50px">
|
||||
<span id="hours">{{ time.hours | time }}</span>
|
||||
<span class="point">:</span>
|
||||
@ -303,7 +416,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="position: fixed; bottom: 0; right: 0; width: 33vw; height: 110px;" class="bg-dark">
|
||||
<div style="position: fixed; bottom: 0; right: 0; width: 33vw; height: 110px; {{ display.hideCountdown && !display.customCountdown.show?'box-shadow: white 0px -10px 15px 0px;':'' }}" class="bg-dark" ng-if="!display.hideCarousel">
|
||||
<div class="carousel slide" id="carousel-logos" data-ride="carousel" data-interval="10500" style="height: inherit">
|
||||
<div class="carousel-inner" style="height: inherit">
|
||||
<div class="carousel-item">
|
||||
|
@ -1,5 +1,5 @@
|
||||
angular.module("FICApp", ["ngSanitize", "ngAnimate"])
|
||||
.controller("EventsController", function($scope, $http, $interval) {
|
||||
.controller("EventsController", function($scope, $rootScope, $http, $interval) {
|
||||
$scope.events = [];
|
||||
var refreshEvents = function() {
|
||||
// Update times
|
||||
@ -14,12 +14,27 @@ angular.module("FICApp", ["ngSanitize", "ngAnimate"])
|
||||
return;
|
||||
$scope.lasteventsetag = response.headers()["last-modified"];
|
||||
|
||||
var lastExercice = undefined;
|
||||
|
||||
$scope.events = response.data;
|
||||
$scope.events.forEach(function(event) {
|
||||
if (!lastExercice && $scope.themes) {
|
||||
var res = event.txt.match(/<strong>(\d+)<sup>e<\/sup><\/strong> défi ([^&]+)/);
|
||||
if (res) {
|
||||
for (var tid in $scope.themes) {
|
||||
if ($scope.themes[tid].name == res[2]) {
|
||||
lastExercice = Object.keys($scope.themes[tid].exercices)[parseInt(res[1])-1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
event.time = Date.parse(event.time);
|
||||
event.since = now - event.time;
|
||||
event.kind = ["border-" + event.kind, "alert-" + event.kind];
|
||||
});
|
||||
|
||||
$rootScope.lastExercice = lastExercice;
|
||||
});
|
||||
}
|
||||
refreshEvents()
|
||||
@ -61,7 +76,7 @@ angular.module("FICApp", ["ngSanitize", "ngAnimate"])
|
||||
return;
|
||||
$scope.lastpublicetag = response.headers()["last-modified"];
|
||||
|
||||
$scope.scene = response.data;
|
||||
$scope.display = response.data;
|
||||
});
|
||||
}
|
||||
refreshScene();
|
||||
|
@ -297,9 +297,6 @@ img {
|
||||
padding-bottom:0;
|
||||
}
|
||||
|
||||
#themesSummary {
|
||||
margin-bottom: 7px;
|
||||
}
|
||||
#themesSummary .card-body {
|
||||
padding:0;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user