2792 lines
93 KiB
JavaScript
2792 lines
93 KiB
JavaScript
angular.module("FICApp", ["ngRoute", "ngResource", "ngSanitize"])
|
|
.config(function($routeProvider, $locationProvider) {
|
|
$routeProvider
|
|
.when("/themes", {
|
|
controller: "ThemesListController",
|
|
templateUrl: "views/theme-list.html"
|
|
})
|
|
.when("/themes/:themeId", {
|
|
controller: "ThemeController",
|
|
templateUrl: "views/theme.html"
|
|
})
|
|
.when("/themes/:themeId/exercices/:exerciceId", {
|
|
controller: "ExerciceController",
|
|
templateUrl: "views/exercice.html"
|
|
})
|
|
.when("/repositories", {
|
|
controller: "RepositoriesController",
|
|
templateUrl: "views/repositories.html"
|
|
})
|
|
.when("/sync", {
|
|
controller: "SyncController",
|
|
templateUrl: "views/sync.html"
|
|
})
|
|
.when("/settings", {
|
|
controller: "SettingsController",
|
|
templateUrl: "views/settings.html"
|
|
})
|
|
.when("/pki", {
|
|
controller: "PKIController",
|
|
templateUrl: "views/pki.html"
|
|
})
|
|
.when("/exercices", {
|
|
controller: "AllExercicesListController",
|
|
templateUrl: "views/exercice-list.html"
|
|
})
|
|
.when("/exercices/:exerciceId", {
|
|
controller: "ExerciceController",
|
|
templateUrl: "views/exercice.html"
|
|
})
|
|
.when("/exercices/:exerciceId/flags", {
|
|
controller: "ExerciceController",
|
|
templateUrl: "views/exercice-flags.html"
|
|
})
|
|
.when("/exercices/:exerciceId/resolution", {
|
|
controller: "ExerciceController",
|
|
templateUrl: "views/exercice-resolution.html"
|
|
})
|
|
.when("/tags", {
|
|
controller: "TagsListController",
|
|
templateUrl: "views/tags.html"
|
|
})
|
|
.when("/teams", {
|
|
controller: "TeamsListController",
|
|
templateUrl: "views/team-list.html"
|
|
})
|
|
.when("/teams/print", {
|
|
controller: "TeamsListController",
|
|
templateUrl: "views/team-print.html"
|
|
})
|
|
.when("/teams/export", {
|
|
controller: "TeamsListController",
|
|
templateUrl: "views/team-export.html"
|
|
})
|
|
.when("/teams/:teamId", {
|
|
controller: "TeamController",
|
|
templateUrl: "views/team-edit.html"
|
|
})
|
|
.when("/teams/:teamId/stats", {
|
|
controller: "TeamController",
|
|
templateUrl: "views/team-stats.html"
|
|
})
|
|
.when("/teams/:teamId/score", {
|
|
controller: "TeamController",
|
|
templateUrl: "views/team-score.html"
|
|
})
|
|
.when("/public/:screenId", {
|
|
controller: "PublicController",
|
|
templateUrl: "views/public.html"
|
|
})
|
|
.when("/files", {
|
|
controller: "FilesListController",
|
|
templateUrl: "views/file-list.html"
|
|
})
|
|
.when("/events", {
|
|
controller: "EventsListController",
|
|
templateUrl: "views/event-list.html"
|
|
})
|
|
.when("/events/:eventId", {
|
|
controller: "EventController",
|
|
templateUrl: "views/event.html"
|
|
})
|
|
.when("/claims", {
|
|
controller: "ClaimsListController",
|
|
templateUrl: "views/claim-list.html"
|
|
})
|
|
.when("/claims/:claimId", {
|
|
controller: "ClaimController",
|
|
templateUrl: "views/claim.html"
|
|
})
|
|
.when("/", {
|
|
templateUrl: "views/home.html"
|
|
});
|
|
$locationProvider.html5Mode(true);
|
|
});
|
|
|
|
function setCookie(name, value, days) {
|
|
var expires;
|
|
|
|
if (days) {
|
|
var date = new Date();
|
|
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
|
expires = "; expires=" + date.toGMTString();
|
|
} else {
|
|
expires = "";
|
|
}
|
|
document.cookie = encodeURIComponent(name) + "=" + encodeURIComponent(value) + expires + "; path=/";
|
|
}
|
|
|
|
function getCookie(name) {
|
|
var nameEQ = encodeURIComponent(name) + "=";
|
|
var ca = document.cookie.split(';');
|
|
for (var i = 0; i < ca.length; i++) {
|
|
var c = ca[i];
|
|
while (c.charAt(0) === ' ')
|
|
c = c.substring(1, c.length);
|
|
if (c.indexOf(nameEQ) === 0)
|
|
return decodeURIComponent(c.substring(nameEQ.length, c.length));
|
|
}
|
|
return null;
|
|
}
|
|
|
|
angular.module("FICApp")
|
|
.directive('autofocus', ['$timeout', function($timeout) {
|
|
return {
|
|
restrict: 'A',
|
|
link : function($scope, $element) {
|
|
$timeout(function() {
|
|
$element[0].focus();
|
|
});
|
|
}
|
|
}
|
|
}])
|
|
|
|
.component('toast', {
|
|
bindings: {
|
|
date: '=',
|
|
msg: '=',
|
|
timeout: '=',
|
|
title: '=',
|
|
variant: '=',
|
|
yesNo: '=',
|
|
onyes: '=',
|
|
onno: '=',
|
|
},
|
|
controller: function($element) {
|
|
if (this.timeout === 0)
|
|
$element.children(0).toast({autohide: false});
|
|
else if (!this.timeout && this.timeout !== 0)
|
|
$element.children(0).toast({delay: 7000});
|
|
else
|
|
$element.children(0).toast({delay: this.timeout});
|
|
$element.children(0).toast('show');
|
|
$element.children(0).on('hidden.bs.toast', function() {
|
|
$element.children(0).toast('dispose');
|
|
});
|
|
this.yesFunc = function() {
|
|
$element.children(0).toast('hide');
|
|
if (this.onyes)
|
|
this.onyes();
|
|
}
|
|
this.noFunc = function() {
|
|
$element.children(0).toast('hide');
|
|
if (this.onno)
|
|
this.onno();
|
|
}
|
|
},
|
|
template: `<div class="toast mb-2" role="alert" aria-live="assertive" aria-atomic="true">
|
|
<div class="toast-header">
|
|
<span ng-if="$ctrl.variant" class="badge badge-pill badge-{{ $ctrl.variant }}" style="padding: .25em .66em"> </span>
|
|
<strong class="mr-auto" ng-bind="$ctrl.title"></strong>
|
|
<small class="text-muted" ng-bind="$ctrl.date">just now</small>
|
|
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
|
|
<span aria-hidden="true">×</span>
|
|
</button>
|
|
</div>
|
|
<div class="toast-body text-break" ng-bind-html="$ctrl.msg" ng-if="$ctrl.msg"></div>
|
|
<div class="d-flex justify-content-around mb-1" ng-if="$ctrl.yesNo">
|
|
<button type="button" class="ml-2 btn btn-sm btn-success" ng-click="$ctrl.yesFunc()">Yes</button>
|
|
<button type="button" class="btn btn-sm btn-danger" ng-click="$ctrl.noFunc()">No</button>
|
|
</div>
|
|
</div>`
|
|
});
|
|
|
|
angular.module("FICApp")
|
|
.factory("Version", function($resource) {
|
|
return $resource("api/version")
|
|
})
|
|
.factory("Timestamp", function($resource) {
|
|
return $resource("api/timestamps.json")
|
|
})
|
|
.factory("Health", function($resource) {
|
|
return $resource("api/health.json")
|
|
})
|
|
.factory("Monitor", function($resource) {
|
|
return $resource("api/monitor/:machineId", { machineId: '@id' })
|
|
})
|
|
.factory("Event", function($resource) {
|
|
return $resource("api/events/:eventId", { eventId: '@id' }, {
|
|
'update': {method: 'PUT'},
|
|
})
|
|
})
|
|
.factory("Claim", function($resource) {
|
|
return $resource("api/claims/:claimId", { claimId: '@id' }, {
|
|
'update': {method: 'PUT'},
|
|
})
|
|
})
|
|
.factory("ClaimAssignee", function($resource) {
|
|
return $resource("api/claims-assignees/:assigneeId", { assigneeId: '@id' }, {
|
|
'update': {method: 'PUT'},
|
|
})
|
|
})
|
|
.factory("Certificate", function($resource) {
|
|
return $resource("api/certs/:serial", { serial: '@id' }, {
|
|
'update': {method: 'PUT'},
|
|
})
|
|
})
|
|
.factory("CACertificate", function($resource) {
|
|
return $resource("api/ca/:serial", { serial: '@id' })
|
|
})
|
|
.factory("File", function($resource) {
|
|
return $resource("api/files/:fileId", { fileId: '@id' })
|
|
})
|
|
.factory("ROSettings", function($resource) {
|
|
return $resource("api/settings-ro.json")
|
|
})
|
|
.factory("Settings", function($resource) {
|
|
return $resource("api/settings.json", null, {
|
|
'update': {method: 'PUT'},
|
|
})
|
|
})
|
|
.factory("NextSettings", function($resource) {
|
|
return $resource("api/settings-next/:tsId", { tsId: '@id'}, {
|
|
'update': {method: 'PUT'},
|
|
})
|
|
})
|
|
.factory("SettingsChallenge", function($resource) {
|
|
return $resource("api/challenge.json", null, {
|
|
'update': {method: 'PUT'},
|
|
})
|
|
})
|
|
.factory("Scene", function($resource) {
|
|
return $resource("api/public/:screenId", { screenId: '@id' }, {
|
|
'update': {method: 'PUT'},
|
|
})
|
|
})
|
|
.factory("Team", function($resource) {
|
|
return $resource("api/teams/:teamId", { teamId: '@id' }, {
|
|
'update': {method: 'PUT'},
|
|
})
|
|
})
|
|
.factory("TeamCertificate", function($resource) {
|
|
return $resource("api/teams/:teamId/certificates", { teamId: '@id' })
|
|
})
|
|
.factory("TeamAssociation", function($resource) {
|
|
return $resource("api/teams/:teamId/associations/:assoc", { teamId: '@teamId', assoc: '@assoc' })
|
|
})
|
|
.factory("TeamMember", function($resource) {
|
|
return $resource("api/teams/:teamId/members", { teamId: '@id' }, {
|
|
'save': {method: 'PUT'},
|
|
})
|
|
})
|
|
.factory("TeamMy", function($resource) {
|
|
return $resource("api/teams/:teamId/my.json", { teamId: '@id' })
|
|
})
|
|
.factory("Teams", function($resource) {
|
|
return $resource("api/teams.json")
|
|
})
|
|
.factory("TeamHistory", function($resource) {
|
|
return $resource("api/teams/:teamId/history.json", { teamId: '@id' })
|
|
})
|
|
.factory("TeamScore", function($resource) {
|
|
return $resource("api/teams/:teamId/score-grid.json", { teamId: '@id' })
|
|
})
|
|
.factory("TeamStats", function($resource) {
|
|
return $resource("api/teams/:teamId/stats.json", { teamId: '@id' })
|
|
})
|
|
.factory("TeamPresence", function($resource) {
|
|
return $resource("api/teams/:teamId/tries", { teamId: '@id' })
|
|
})
|
|
.factory("Theme", function($resource) {
|
|
return $resource("api/themes/:themeId", { themeId: '@id' }, {
|
|
update: {method: 'PUT'}
|
|
});
|
|
})
|
|
.factory("Themes", function($resource) {
|
|
return $resource("api/themes.json", null, {
|
|
'get': {method: 'GET'},
|
|
})
|
|
})
|
|
.factory("ThemedExercice", function($resource) {
|
|
return $resource("api/themes/:themeId/exercices/:exerciceId", { themeId: '@id', exerciceId: '@idExercice' }, {
|
|
update: {method: 'PUT'}
|
|
})
|
|
})
|
|
.factory("Exercice", function($resource) {
|
|
return $resource("api/exercices/:exerciceId", { exerciceId: '@id' }, {
|
|
update: {method: 'PUT'},
|
|
patch: {method: 'PATCH'}
|
|
})
|
|
})
|
|
.factory("ExerciceClaims", function($resource) {
|
|
return $resource("api/exercices/:exerciceId/claims", { exerciceId: '@idExercice'})
|
|
})
|
|
.factory("ExerciceTags", function($resource) {
|
|
return $resource("api/exercices/:exerciceId/tags", { exerciceId: '@idExercice'}, {
|
|
update: {method: 'PUT'}
|
|
})
|
|
})
|
|
.factory("ExerciceHistory", function($resource) {
|
|
return $resource("api/exercices/:exerciceId/history.json", { exerciceId: '@id' })
|
|
})
|
|
.factory("ExercicesStats", function($resource) {
|
|
return $resource("api/exercices_stats.json", { themeId: '@id' })
|
|
})
|
|
.factory("ExerciceStats", function($resource) {
|
|
return $resource("api/exercices/:exerciceId/stats.json", { exerciceId: '@id' })
|
|
})
|
|
.factory("ExerciceFile", function($resource) {
|
|
return $resource("api/exercices/:exerciceId/files/:fileId", { exerciceId: '@idExercice', fileId: '@id' }, {
|
|
update: {method: 'PUT'}
|
|
})
|
|
})
|
|
.factory("ExerciceHint", function($resource) {
|
|
return $resource("api/exercices/:exerciceId/hints/:hintId", { exerciceId: '@idExercice', hintId: '@id' }, {
|
|
update: {method: 'PUT'}
|
|
})
|
|
})
|
|
.factory("ExerciceHintDeps", function($resource) {
|
|
return $resource("api/exercices/:exerciceId/hints/:hintId/dependancies", { exerciceId: '@idExercice', hintId: '@id' })
|
|
})
|
|
.factory("ExerciceFlag", function($resource) {
|
|
return $resource("api/exercices/:exerciceId/flags/:flagId", { exerciceId: '@idExercice', flagId: '@id' }, {
|
|
update: {method: 'PUT'}
|
|
})
|
|
})
|
|
.factory("ExerciceFlagChoices", function($resource) {
|
|
return $resource("api/exercices/:exerciceId/flags/:flagId/choices/:choiceId", { exerciceId: '@idExercice', flagId: '@idFlag', choiceId: '@id' }, {
|
|
'update': {method: 'PUT'},
|
|
})
|
|
})
|
|
.factory("ExerciceFlagDeps", function($resource) {
|
|
return $resource("api/exercices/:exerciceId/flags/:flagId/dependancies", { exerciceId: '@idExercice', flagId: '@id' })
|
|
})
|
|
.factory("ExerciceMCQFlag", function($resource) {
|
|
return $resource("api/exercices/:exerciceId/quiz/:mcqId", { exerciceId: '@idExercice', mcqId: '@id' }, {
|
|
update: {method: 'PUT'}
|
|
})
|
|
})
|
|
.factory("ExerciceMCQDeps", function($resource) {
|
|
return $resource("api/exercices/:exerciceId/quiz/:mcqId/dependancies", { exerciceId: '@idExercice', mcqId: '@id' })
|
|
});
|
|
|
|
angular.module("FICApp")
|
|
.filter("countHints", function() {
|
|
return function(input) {
|
|
if (input == undefined)
|
|
return 0;
|
|
return input.reduce(function(sum, n){ return sum + (n.content || n.file) ? 1 : 0; }, 0);
|
|
}
|
|
})
|
|
.filter("toColor", function() {
|
|
return function(num) {
|
|
num >>>= 0;
|
|
var b = num & 0xFF,
|
|
g = (num & 0xFF00) >>> 8,
|
|
r = (num & 0xFF0000) >>> 16,
|
|
a = ( (num & 0xFF000000) >>> 24 ) / 255 ;
|
|
return "#" + r.toString(16) + g.toString(16) + b.toString(16);
|
|
}
|
|
})
|
|
.filter("cksum", function() {
|
|
return function(input) {
|
|
if (input == undefined)
|
|
return input;
|
|
var raw = atob(input).toString(16);
|
|
var hex = '';
|
|
for (var i = 0; i < raw.length; i++ ) {
|
|
var _hex = raw.charCodeAt(i).toString(16)
|
|
hex += (_hex.length == 2 ? _hex : '0' + _hex);
|
|
}
|
|
return hex
|
|
}
|
|
})
|
|
|
|
.component('dependancy', {
|
|
bindings: {
|
|
dep: '=',
|
|
deleteDep: '=',
|
|
},
|
|
controller: function() {},
|
|
template: `
|
|
<li>
|
|
<span ng-if="$ctrl.dep.label">Flag {{ $ctrl.dep.label }}</span>
|
|
<span ng-if="$ctrl.dep.title">QCM {{ $ctrl.dep.title }}</span>
|
|
<button type="button" class="btn btn-sm btn-danger" ng-if="$ctrl.deleteDep" ng-click="$ctrl.deleteDep()"><span class="glyphicon glyphicon-trash"></span></button>
|
|
</li>
|
|
`
|
|
})
|
|
|
|
.directive('color', function() {
|
|
return {
|
|
require: 'ngModel',
|
|
link: function(scope, ele, attr, ctrl){
|
|
ctrl.$formatters.unshift(function(num){
|
|
num >>>= 0;
|
|
var b = num & 0xFF,
|
|
g = (num & 0xFF00) >>> 8,
|
|
r = (num & 0xFF0000) >>> 16,
|
|
a = ( (num & 0xFF000000) >>> 24 ) / 255 ;
|
|
return "#" + r.toString(16) + g.toString(16) + b.toString(16);
|
|
});
|
|
ctrl.$parsers.unshift(function(viewValue){
|
|
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(viewValue);
|
|
return result ? (
|
|
parseInt(result[1], 16) * 256 * 256 +
|
|
parseInt(result[2], 16) * 256 +
|
|
parseInt(result[3], 16)
|
|
|
|
) : 0;
|
|
});
|
|
}
|
|
};
|
|
})
|
|
|
|
.directive('integer', function() {
|
|
return {
|
|
require: 'ngModel',
|
|
link: function(scope, ele, attr, ctrl){
|
|
ctrl.$parsers.unshift(function(viewValue){
|
|
return parseInt(viewValue, 10);
|
|
});
|
|
}
|
|
};
|
|
})
|
|
|
|
.directive('float', function() {
|
|
return {
|
|
require: 'ngModel',
|
|
link: function(scope, ele, attr, ctrl){
|
|
ctrl.$parsers.unshift(function(viewValue){
|
|
return parseFloat(viewValue, 10);
|
|
});
|
|
}
|
|
};
|
|
})
|
|
|
|
.run(function($rootScope, $http, $interval, $timeout, Settings, $location) {
|
|
$rootScope.$location = $location;
|
|
$rootScope.Utils = {
|
|
keys : Object.keys
|
|
};
|
|
$rootScope.refreshSettings = function() {
|
|
$http.get("api/settings.json").then(function(response) {
|
|
response.data.start = new Date(response.data.start);
|
|
response.data.end = new Date(response.data.end);
|
|
response.data.generation = new Date(response.data.generation);
|
|
if ($rootScope.settings && $rootScope.settings.activateTime instanceof Date)
|
|
response.data.activateTime = $rootScope.settings.activateTime;
|
|
$rootScope.settings = response.data;
|
|
$rootScope.recvTime(response);
|
|
})
|
|
}
|
|
$rootScope.refreshSettings();
|
|
$interval($rootScope.refreshSettings, 10000);
|
|
|
|
$rootScope.toasts = [];
|
|
$rootScope.addToast = function(kind, title, msg, yesFunc, noFunc, tmout) {
|
|
$rootScope.toasts.unshift({
|
|
variant: kind,
|
|
title: title,
|
|
msg: msg,
|
|
timeout: tmout,
|
|
yesFunc: yesFunc,
|
|
noFunc: noFunc,
|
|
});
|
|
}
|
|
|
|
$rootScope.staticFilesNeedUpdate = 0;
|
|
$rootScope.staticRegenerationInProgress = false;
|
|
$rootScope.regenerateStaticFiles = function() {
|
|
$rootScope.staticRegenerationInProgress = true;
|
|
$http.post("api/full-generation").then(function(response) {
|
|
$rootScope.staticFilesNeedUpdate = 0;
|
|
$rootScope.staticRegenerationInProgress = false;
|
|
$rootScope.addToast('success', 'Regeneration ended');
|
|
}, function (response) {
|
|
$rootScope.staticRegenerationInProgress = false;
|
|
$rootScope.addToast('error', 'An error occurs when saving settings:', response.data.errmsg);
|
|
})
|
|
}
|
|
|
|
$rootScope.$on('$locationChangeStart', function(event, next, current) {
|
|
if($rootScope.staticFilesNeedUpdate) {
|
|
$timeout(function () {
|
|
document.getElementById("circle1").classList.add("play");
|
|
}, 10);
|
|
$timeout(function () {
|
|
document.getElementById("circle1").classList.remove("play");
|
|
}, 710);
|
|
|
|
$timeout(function () {
|
|
document.getElementById("circle2").classList.add("play");
|
|
}, 50);
|
|
$timeout(function () {
|
|
document.getElementById("circle2").classList.remove("play");
|
|
}, 750);
|
|
}
|
|
});
|
|
|
|
$rootScope.logged = parseInt(getCookie("myassignee")) > 0;
|
|
})
|
|
|
|
.controller("VersionController", function($scope, Version) {
|
|
$scope.v = Version.get();
|
|
})
|
|
|
|
.controller("TimestampController", function($scope, $interval, Timestamp) {
|
|
$scope.t = Timestamp.get();
|
|
var refresh = function() {
|
|
$scope.t = Timestamp.get();
|
|
}
|
|
var myinterval = $interval(refresh, 2500);
|
|
$scope.$on('$destroy', function () { $interval.cancel(myinterval); });
|
|
})
|
|
|
|
.controller("HealthController", function($scope, $interval, Health, $http) {
|
|
var refresh = function() {
|
|
$scope.health = Health.query();
|
|
}
|
|
refresh();
|
|
var myinterval = $interval(refresh, 2500);
|
|
$scope.drop_submission = function(path) {
|
|
$scope.addToast('info', 'Delete submission', 'Ensure this submission is not interesting. Continue?',
|
|
function() {
|
|
$http.delete("api/submissions" + path).then(function(response) {
|
|
refresh();
|
|
}, function(response) {
|
|
$scope.addToast('danger', 'An error occurs when deleting submission:', response.data.errmsg);
|
|
});
|
|
}
|
|
);
|
|
}
|
|
$scope.$on('$destroy', function () { $interval.cancel(myinterval); });
|
|
})
|
|
|
|
.controller("MonitorController", function($scope, Monitor) {
|
|
$scope.monitor = Monitor.get();
|
|
})
|
|
|
|
.controller("AllTeamAssociationsController", function($scope, $http) {
|
|
$scope.allAssociations = [];
|
|
$http.get("api/teams-associations.json").then(function(response) {
|
|
$scope.allAssociations = response.data;
|
|
})
|
|
})
|
|
|
|
.controller("SettingsController", function($scope, $rootScope, NextSettings, Settings, SettingsChallenge, $location, $http, $interval) {
|
|
$scope.nextsettings = NextSettings.query();
|
|
$scope.erase = false;
|
|
$scope.editNextSettings = function(ns) {
|
|
$scope.activateTime = new Date(ns.date);
|
|
$rootScope.settings.activateTime = $scope.activateTime;
|
|
$scope.erase = true;
|
|
Object.keys(ns.values).forEach(function(k) {
|
|
$scope.config[k] = ns.values[k];
|
|
});
|
|
$scope.config.enableExerciceDepend = $scope.config.unlockedChallengeDepth >= 0;
|
|
$scope.config.disabledsubmitbutton = $scope.config.disablesubmitbutton && $scope.config.disablesubmitbutton.length > 0;
|
|
}
|
|
$scope.deleteNextSettings = function(ns) {
|
|
ns.$delete().then(function() {
|
|
$scope.nextsettings = NextSettings.query();
|
|
})
|
|
}
|
|
|
|
$scope.displayDangerousActions = false;
|
|
$scope.config = Settings.get();
|
|
$scope.dist_config = {};
|
|
$scope.config.$promise.then(function(response) {
|
|
response.start = new Date(response.start);
|
|
if (response.end) response.end = new Date(response.end);
|
|
else response.end = null;
|
|
response.generation = new Date(response.generation);
|
|
response.activateTime = new Date(response.activateTime);
|
|
$scope.dist_config = Object.assign({}, response);
|
|
|
|
response.enableExerciceDepend = response.unlockedChallengeDepth >= 0;
|
|
response.disabledsubmitbutton = response.disablesubmitbutton && response.disablesubmitbutton.length > 0;
|
|
})
|
|
$scope.challenge = SettingsChallenge.get();
|
|
$scope.duration = 360;
|
|
$scope.activateTime = "";
|
|
$scope.challenge.$promise.then(function(c) {
|
|
if (c.duration)
|
|
$scope.duration = c.duration;
|
|
});
|
|
|
|
$scope.exerciceDependChange = function() {
|
|
if ($scope.config.enableExerciceDepend)
|
|
$scope.config.unlockedChallengeDepth = 0;
|
|
else
|
|
$scope.config.unlockedChallengeDepth = -1;
|
|
};
|
|
|
|
$scope.submitButtonStateChange = function() {
|
|
if ($scope.config.disabledsubmitbutton)
|
|
$scope.config.disablesubmitbutton = "Mise à jour en cours...";
|
|
else
|
|
$scope.config.disablesubmitbutton = "";
|
|
};
|
|
|
|
$scope.newdqa = "";
|
|
$scope.addDelegatedQA = function() {
|
|
if ($scope.newdqa.length) {
|
|
if (!$scope.config.delegated_qa)
|
|
$scope.config.delegated_qa = [];
|
|
|
|
$scope.config.delegated_qa.push($scope.newdqa);
|
|
$scope.saveSettings();
|
|
$scope.newdqa = "";
|
|
}
|
|
}
|
|
$scope.dropDelegatedQA = function(member) {
|
|
if ($scope.config.delegated_qa) {
|
|
$scope.config.delegated_qa = [];
|
|
|
|
angular.forEach($scope.config.delegated_qa, function(m, k) {
|
|
if (member == m)
|
|
$scope.config.delegated_qa.splice(k, 1);
|
|
});
|
|
$scope.saveSettings();
|
|
}
|
|
}
|
|
|
|
$scope.saveChallengeInfo = function() {
|
|
this.challenge.duration = $scope.duration;
|
|
this.challenge.$update(function(response) {
|
|
$scope.addToast('success', 'Infos du challenge mises à jour avec succès !');
|
|
}, function(response) {
|
|
$scope.addToast('danger', 'An error occurs when saving challenge info:', response.data.errmsg);
|
|
});
|
|
}
|
|
$scope.saveSettings = function(msg) {
|
|
if (msg === undefined) { msg = 'New settings saved!'; }
|
|
if (this.config.end == "") this.config.end = null;
|
|
|
|
var nStart = this.config.start;
|
|
var nEnd = this.config.end;
|
|
var nGen = this.config.generation;
|
|
var state = this.config.enableExerciceDepend;
|
|
this.config.unlockedChallengeDepth = (this.config.enableExerciceDepend?this.config.unlockedChallengeDepth:-1)
|
|
this.config.disablesubmitbutton = (this.config.disabledsubmitbutton?this.config.disablesubmitbutton:'')
|
|
var updateQuery = {};
|
|
if (this.activateTime && this.activateTime != '') {
|
|
updateQuery['t'] = this.activateTime;
|
|
this.activateTime = null;
|
|
}
|
|
if (this.erase) {
|
|
updateQuery['erase'] = true;
|
|
this.erase = false;
|
|
}
|
|
this.config.$update(updateQuery, function(response) {
|
|
$scope.dist_config = Object.assign({}, response);
|
|
$scope.addToast('success', msg);
|
|
$scope.nextsettings = NextSettings.query();
|
|
response.enableExerciceDepend = response.unlockedChallengeDepth >= 0;
|
|
response.disabledsubmitbutton = response.disablesubmitbutton && response.disablesubmitbutton.length > 0;
|
|
$rootScope.settings.start = new Date(nStart);
|
|
if (nEnd) {
|
|
$rootScope.settings.end = new Date(nEnd);
|
|
} else {
|
|
$rootScope.settings.end = null;
|
|
}
|
|
$rootScope.settings.generation = new Date(nGen);
|
|
$scope.updateActivateTime();
|
|
}, function(response) {
|
|
$scope.addToast('danger', 'An error occurs when saving settings:', response.data.errmsg);
|
|
});
|
|
}
|
|
$scope.launchChallenge = function() {
|
|
var ts = $rootScope.getSrvTime().getTime() - $rootScope.getSrvTime().getTime() % 60000;
|
|
this.config.start = new Date(ts + 120000);
|
|
this.config.end = new Date(ts + 120000 + this.duration * 60000);
|
|
|
|
$scope.addToast('info', 'Challenge ready to start,', 'propagate the changes?',
|
|
function() {
|
|
$scope.saveSettings();
|
|
});
|
|
}
|
|
$scope.updateActivateTime = function() {
|
|
$rootScope.settings.activateTime = this.activateTime;
|
|
}
|
|
$scope.updActivateTime = function(modulo) {
|
|
if (modulo) {
|
|
var ts = Math.floor((new Date(this.config.end) - $rootScope.getSrvTime().getTime() - (60000 * modulo / 2)) / (60000 * modulo)) * (60000 * modulo);
|
|
var d = new Date(this.config.end) - ts;
|
|
this.activateTime = new Date(d);
|
|
this.updateActivateTime();
|
|
} else {
|
|
this.activateTime = null;
|
|
this.updateActivateTime();
|
|
}
|
|
}
|
|
$scope.reset = function(type) {
|
|
var txts = {
|
|
"settings": "En validant, vous remettrez les paramètres de cette page à leur valeur initiale, y compris la date de début du challenge.",
|
|
"challengeInfo": "En validant, vous effacerez les informations descriptives du challenge.",
|
|
"challenges": "En validant, vous retirerez toutes les données statiques des challenges.",
|
|
"teams": "En validant, vous supprimerez l'ensemble des équipes enregistreées.",
|
|
"game": "En validant, vous supprimerez toutes les tentatives, les validations, ... faites par les équipes.",
|
|
}
|
|
$scope.addToast('warning', txts[type], 'Êtes-vous sûr de vouloir continuer ?',
|
|
function() {
|
|
if (confirm("Êtes-vous vraiment sûr ?\n" + txts[type])) {
|
|
$http.post("api/reset", {"type": type}).then(function(time) {
|
|
$scope.addToast('success', type + 'reseted');
|
|
$location.url("/");
|
|
}, function(response) {
|
|
$scope.addToast('danger', 'An error occurs when reseting ' + type + ':', response.data.errmsg);
|
|
});
|
|
|
|
}
|
|
});
|
|
};
|
|
$scope.switchToProd = function() {
|
|
$scope.addToast('warning', "Activer le mode challenge ?", "L'activation du mode challenge est temporaire (vous devriez plutôt relancer le daemon avec l'option `-4real`). Ce mode permet d'éviter les mauvaises manipulations et désactive le hook git de synchronisation automatique. Êtes-vous sûr de vouloir continuer ?",
|
|
function() {
|
|
$http.put("api/prod", true).then(function(time) {
|
|
$rootScope.refreshSettings()
|
|
$scope.addToast('success', 'Mode challenge activé');
|
|
}, function(response) {
|
|
$scope.addToast('danger', 'An error occurs when activating challenge mode:', response.data.errmsg);
|
|
});
|
|
});
|
|
};
|
|
})
|
|
|
|
.controller("RepositoriesController", function($scope, $http) {
|
|
$http.get("api/repositories").then(function(response) {
|
|
$scope.repositories = response.data.repositories;
|
|
});
|
|
})
|
|
.component('repositoryUptodate', {
|
|
bindings: {
|
|
repository: '<',
|
|
},
|
|
controller: function($http) {
|
|
var ctrl = this;
|
|
|
|
ctrl.status = {};
|
|
ctrl.color = "badge-secondary";
|
|
|
|
ctrl.$onInit = function() {
|
|
$http.post("api/repositories/" + ctrl.repository.path).then(function(response) {
|
|
ctrl.status = response.data;
|
|
|
|
if (ctrl.repository.hash.startsWith(ctrl.status.hash)) {
|
|
ctrl.color = "badge-success";
|
|
} else {
|
|
ctrl.color = "badge-danger";
|
|
}
|
|
});
|
|
};
|
|
},
|
|
template: `<span class="badge {{ $ctrl.color }}">{{ $ctrl.status.hash }}</span> <small>{{ $ctrl.status.text }}</small>`
|
|
})
|
|
|
|
.controller("SyncController", function($scope, $rootScope, ROSettings, $location, $http, $interval) {
|
|
$scope.displayDangerousActions = false;
|
|
$scope.configro = ROSettings.get();
|
|
|
|
var needRefreshSyncReportWhenReady = false;
|
|
var refreshSyncReport = function() {
|
|
needRefreshSyncReportWhenReady = false;
|
|
$http.get("full_import_report.json").then(function(response) {
|
|
$scope.syncReport = response.data;
|
|
})
|
|
};
|
|
refreshSyncReport()
|
|
|
|
var progressInterval = $interval(function() {
|
|
$http.get("api/sync/deep").then(function(response) {
|
|
if (response.data.progress && response.data.progress != 255)
|
|
needRefreshSyncReportWhenReady = true;
|
|
else if (needRefreshSyncReportWhenReady)
|
|
refreshSyncReport();
|
|
if (response.data && response.data.progress) {
|
|
$scope.syncPercent = Math.floor(response.data.progress * 100 / 255);
|
|
$scope.syncProgress = Math.floor(response.data.progress * 100 / 255) + " %";
|
|
} else {
|
|
$scope.syncProgress = response.data;
|
|
$scope.syncPercent = 0;
|
|
}
|
|
}, function(response) {
|
|
$scope.syncPercent = 0;
|
|
if (response.data && response.data.errmsg)
|
|
$scope.syncProgress = response.data.errmsg;
|
|
else
|
|
$scope.syncProgress = response.data;
|
|
})
|
|
}, 1500);
|
|
$scope.$on('$destroy', function () { $interval.cancel(progressInterval); });
|
|
|
|
$scope.deepSyncInProgress = false;
|
|
$scope.deepSync = function(theme) {
|
|
if (theme) {
|
|
question = 'Faire une synchronisation intégrale du thème ' + theme.name + ' ?'
|
|
url = "api/sync/deep/" + theme.id
|
|
} else {
|
|
question = 'Faire une synchronisation intégrale ?'
|
|
url = "api/sync/deep"
|
|
}
|
|
$scope.addToast('warning', question, '',
|
|
function() {
|
|
$scope.deepSyncInProgress = true;
|
|
$http.post(url).then(function() {
|
|
$scope.deepSyncInProgress = false;
|
|
$scope.addToast('success', 'Synchronisation intégrale terminée.', '<a href="check_import.html" target="_self">Voir le rapport</a>.', null, null, 15000);
|
|
}, function(response) {
|
|
$scope.deepSyncInProgress = false;
|
|
$scope.addToast('warning', 'Synchronisation intégrale terminée.', '<a href="check_import.html" target="_self">Voir le rapport</a>.', null, null, 15000);
|
|
});
|
|
});
|
|
};
|
|
$scope.speedyDeepSync = function() {
|
|
$scope.addToast('warning', 'Faire une synchronisation profonde rapide, sans s\'occuper des fichiers ?', '',
|
|
function() {
|
|
$scope.deepSyncInProgress = true;
|
|
$http.post("api/sync/speed").then(function() {
|
|
$scope.deepSyncInProgress = false;
|
|
$scope.addToast('success', 'Synchronisation profonde rapide terminée.', '<a href="check_import.html" target="_self">Voir le rapport</a>.', null, null, 15000);
|
|
}, function(response) {
|
|
$scope.deepSyncInProgress = false;
|
|
$scope.addToast('warning', 'Synchronisation profinde rapide terminée.', '<a href="check_import.html" target="_self">Voir le rapport</a>.', null, null, 15000);
|
|
});
|
|
});
|
|
};
|
|
$scope.baseSync = function() {
|
|
$scope.addToast('warning', 'Tirer les mises à jour du dépôt ?', '',
|
|
function() {
|
|
$scope.deepSyncInProgress = true;
|
|
$http.post("api/sync/base").then(function() {
|
|
$scope.deepSyncInProgress = false;
|
|
$scope.addToast('success', 'Mise à jour terminée.');
|
|
}, function(response) {
|
|
$scope.deepSyncInProgress = false;
|
|
$scope.addToast('danger', 'Mise à jour terminée.', response.data.errmsg);
|
|
});
|
|
});
|
|
};
|
|
$scope.syncVideos = function() {
|
|
$scope.addToast('warning', 'Synchroniser les vidéos de résolution ?', 'ATTENTION il ne faut pas lancer cette synchronisation durant le challenge. Seulement une fois le challenge terminé, cela permet de rendre les vidéos accessibles dans l\'interface joueurs.',
|
|
function() {
|
|
$scope.deepSyncInProgress = true;
|
|
$http.post("api/sync/videos").then(function() {
|
|
$scope.deepSyncInProgress = false;
|
|
$scope.addToast('success', 'Import des vidéos terminé.');
|
|
}, function(response) {
|
|
$scope.deepSyncInProgress = false;
|
|
$scope.addToast('danger', 'Import des vidéos terminé.', response.data.errmsg);
|
|
});
|
|
});
|
|
};
|
|
$scope.dropSoluces = function() {
|
|
$scope.addToast('warning', 'Effacer les solutions', 'Ceci va retirer les textes de résolution de la base de données ainsi que les liens vers les vidéos.',
|
|
function() {
|
|
$scope.deepSyncInProgress = true;
|
|
$http.post("api/sync/drop_soluces").then(function() {
|
|
$scope.deepSyncInProgress = false;
|
|
$scope.addToast('success', 'Effacement des solutions terminé.');
|
|
}, function(response) {
|
|
$scope.deepSyncInProgress = false;
|
|
$scope.addToast('danger', 'Effacement des solutions terminé avec des erreurs.', response.data.errmsg);
|
|
});
|
|
});
|
|
};
|
|
})
|
|
|
|
.controller("PKIController", function($scope, $rootScope, Certificate, CACertificate, Team, $location, $http) {
|
|
var ts = Date.now() - Date.now() % 86400000;
|
|
var d = new Date(ts);
|
|
var f = new Date(ts + 3 * 86400000);
|
|
$scope.newca = {
|
|
notAfter: f.toISOString(),
|
|
notBefore: d.toISOString(),
|
|
};
|
|
|
|
$scope.teams = Team.query();
|
|
$scope.certificates = Certificate.query();
|
|
$scope.ca = CACertificate.get();
|
|
|
|
$scope.revoke = function() {
|
|
var targetserial = $("#revokeModal").data("certificate");
|
|
if (targetserial) {
|
|
Certificate.delete({ serial: targetserial }).$promise.then(
|
|
function() {
|
|
$('#revokeModal').modal('hide');
|
|
$scope.certificates = Certificate.query().$promise.then(function(certificates) {
|
|
certificates.forEach(function(certificate, cid) {
|
|
certificate.serial = parseInt(certificate.id).toString(16);
|
|
});
|
|
});
|
|
}, function(response) {
|
|
$scope.addToast('danger', 'An error occurs when trying to associate certificate:', response.data.errmsg);
|
|
}
|
|
);
|
|
}
|
|
};
|
|
|
|
$scope.validateSearch = function(keyEvent) {
|
|
if (keyEvent.which === 13) {
|
|
var myCertificate = null;
|
|
$scope.certificates.forEach(function(certificate) {
|
|
if (String(certificate.id).indexOf($scope.query.toUpperCase()) >= 0) {
|
|
if (myCertificate === null)
|
|
myCertificate = certificate;
|
|
else
|
|
myCertificate = false;
|
|
}
|
|
});
|
|
if (myCertificate && myCertificate.id_team == null) {
|
|
$('#associationModal').data('certificate', myCertificate.id)
|
|
$('#associationModal').modal()
|
|
}
|
|
}
|
|
};
|
|
$scope.validatePKIForm = function(keyEvent) {
|
|
if (keyEvent.which === 13)
|
|
$scope.associate()
|
|
};
|
|
$scope.associate = function() {
|
|
var targetserial = $("#associationModal").data("certificate");
|
|
if (!targetserial) return;
|
|
Certificate.update({ serial: targetserial }, { id_team: $scope.selectedTeam }).$promise.then(
|
|
function() {
|
|
$('#associationModal').modal('hide');
|
|
$scope.certificates = Certificate.query();
|
|
$scope.selectedTeam = null;
|
|
}, function(response) {
|
|
$scope.addToast('danger', 'An error occurs when trying to associate certificate:', response.data.errmsg);
|
|
}
|
|
);
|
|
};
|
|
|
|
$scope.generateCA = function() {
|
|
$http.post("api/ca/new", $scope.newca).then(function() {
|
|
$scope.ca = CACertificate.get();
|
|
}, function(response) {
|
|
$scope.addToast('danger', 'An error occurs when generating CA:', response.data.errmsg);
|
|
});
|
|
};
|
|
$scope.renewCA = function() {
|
|
$scope.ca = {};
|
|
};
|
|
|
|
$scope.generateCert = function() {
|
|
$http.post("api/certs").then(function() {
|
|
$scope.certificates = Certificate.query();
|
|
}, function(response) {
|
|
$scope.addToast('danger', 'An error occurs when generating certificate:', response.data.errmsg);
|
|
});
|
|
};
|
|
$scope.generateHtpasswd = function() {
|
|
$http.post("api/htpasswd").then(function() {
|
|
$scope.addToast('success', 'Fichier htpasswd généré avec succès');
|
|
}, function(response) {
|
|
$scope.addToast('danger', 'An error occurs when generating htpasswd file:', response.data.errmsg);
|
|
});
|
|
};
|
|
$scope.removeHtpasswd = function() {
|
|
$http.delete("api/htpasswd").then(function() {
|
|
$scope.addToast('success', 'Fichier htpasswd supprimé avec succès');
|
|
}, function(response) {
|
|
$scope.addToast('danger', 'An error occurs when deleting htpasswd file:', response.data.errmsg);
|
|
});
|
|
};
|
|
})
|
|
|
|
.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();
|
|
|
|
$scope.chScreen = function(sid) {
|
|
if ($scope.screenid)
|
|
$location.url("/public/" + $scope.screenid);
|
|
else
|
|
$location.url("/public/");
|
|
}
|
|
|
|
$scope.types = {
|
|
"welcome": "Messages de bienvenue",
|
|
"countdown": "Compte à rebours",
|
|
"message": "Message",
|
|
"panel": "Boîte",
|
|
"carousel": "Carousel",
|
|
"exercice": "Exercice",
|
|
"table": "Tableau",
|
|
"rank": "Classement",
|
|
"graph": "Graphique",
|
|
};
|
|
$scope.typeside = {
|
|
"welcome": "Messages de bienvenue",
|
|
"themes": "Présentation des thèmes",
|
|
"exercice_follow": "Dernier exercice des événements",
|
|
"exercice": "Exercice",
|
|
"rank": "Classement",
|
|
"graph": "Graphique",
|
|
"message": "Message",
|
|
"panel": "Boîte",
|
|
};
|
|
$scope.welcome_types = {
|
|
"teams": "Accueil des équipes",
|
|
"public": "Accueil du public",
|
|
};
|
|
$scope.carousel_types = {
|
|
"exercices": "Exercices",
|
|
"teams": "Équipes",
|
|
"themes": "Thèmes",
|
|
"ranking": "Classement",
|
|
};
|
|
$scope.colors = {
|
|
"primary": "Primaire",
|
|
"secondary": "Secondaire",
|
|
"info": "Info",
|
|
"success": "Success",
|
|
"warning": "Warning",
|
|
"danger": "Danger",
|
|
"light": "Clair",
|
|
"dark": "Foncé",
|
|
};
|
|
$scope.rank_types = {
|
|
"general": "Classement général",
|
|
"final": "Classement final",
|
|
};
|
|
$scope.rank_types_side = {
|
|
"carousel": "Classement général carousel",
|
|
"general": "Classement général",
|
|
};
|
|
$scope.table_types = {
|
|
"levels": "Niveaux d'exercices",
|
|
"teams": "Équipes",
|
|
};
|
|
$scope.exercices = Exercice.query();
|
|
|
|
$scope.clearScene = function() {
|
|
$scope.someUpdt = true;
|
|
$scope.display.scenes = [];
|
|
};
|
|
$scope.presetScene = function(scene) {
|
|
$scope.someUpdt = true;
|
|
if (scene == "registration") {
|
|
$scope.display.scenes = [
|
|
{
|
|
type: "welcome",
|
|
params: { kind: "teams", url: "https://fic.srs.epita.fr/" },
|
|
},
|
|
{
|
|
type: "welcome",
|
|
params: { kind: "public", notitle: true },
|
|
},
|
|
];
|
|
$scope.display.side = [
|
|
{
|
|
type: "themes",
|
|
params: { },
|
|
},
|
|
];
|
|
}
|
|
else if (scene == "welcome") {
|
|
$scope.display.scenes = [
|
|
{
|
|
type: "carousel",
|
|
params: { color: "info", kind: "themes", title: "Présentation des entreprises ciblées"},
|
|
},
|
|
];
|
|
$scope.display.side = [
|
|
{
|
|
type: "welcome",
|
|
params: { kind: "public" },
|
|
},
|
|
];
|
|
}
|
|
else if (scene == "start") {
|
|
$scope.display.scenes = [
|
|
{
|
|
type: "welcome",
|
|
params: { kind: "public" },
|
|
},
|
|
{
|
|
type: "countdown",
|
|
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 == "end") {
|
|
$scope.display.scenes = [
|
|
{
|
|
type: "rank",
|
|
params: { which: "final" },
|
|
},
|
|
{
|
|
type: "table",
|
|
params: { kind: "teams", themes: $scope.themes.map(function(z, i) { return z.id; }), total: true, teams: [] },
|
|
},
|
|
];
|
|
angular.forEach($scope.teams, function(team, tid) {
|
|
if (team.rank >= 1 && team.rank <= 4)
|
|
$scope.display.scenes[1].params.teams.push(tid)
|
|
});
|
|
$scope.display.side = [
|
|
{
|
|
type: "rank",
|
|
params: { which: "carousel" },
|
|
},
|
|
{
|
|
type: "graph",
|
|
params: { teams: [], height: 400, legend: false, hide: true },
|
|
},
|
|
{
|
|
type: "message",
|
|
params: { html: '<div class="row"><div class="col"><a href="http://www.epita.fr/" class="align-middle text-center"><img class="mt-4" src="/img/epita.png" alt="Epita"></a></div><div class="col mr-3"> <a href="http://www.epita.fr/"><img src="/img/comcyber.png" alt="Réserves de cyberdéfense" style="height: 100px;"></a></div></div>', hide: true },
|
|
},
|
|
];
|
|
angular.forEach($scope.teams, function(team, tid) {
|
|
if (team.rank >= 1 && team.rank <= 9)
|
|
$scope.display.side[1].params.teams.push(tid)
|
|
});
|
|
$scope.display.hideEvents = true;
|
|
$scope.display.hideCountdown = true;
|
|
}
|
|
else if (scene == "summary") {
|
|
$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 },
|
|
},
|
|
{
|
|
type: "graph",
|
|
params: { teams: [], height: 337, legend: true },
|
|
},
|
|
];
|
|
angular.forEach($scope.teams, function(team, tid) {
|
|
if (team.rank >= 1 && team.rank <= 9)
|
|
$scope.display.scenes[1].params.teams.push(tid)
|
|
});
|
|
$scope.display.side = [
|
|
{
|
|
type: "exercice_follow",
|
|
params: { },
|
|
},
|
|
];
|
|
$scope.display.hideEvents = false;
|
|
$scope.display.hideCountdown = false;
|
|
}
|
|
else if (scene == "summary2") {
|
|
$scope.display.scenes = [
|
|
{
|
|
type: "graph",
|
|
params: { teams: [], height: 400, legend: false },
|
|
},
|
|
{
|
|
type: "rank",
|
|
params: { limit: 10, which: "general", legend: true },
|
|
},
|
|
];
|
|
angular.forEach($scope.teams, function(team, tid) {
|
|
if (team.rank >= 1 && team.rank <= 9)
|
|
$scope.display.scenes[0].params.teams.push(tid)
|
|
});
|
|
$scope.display.side = [
|
|
{
|
|
type: "exercice_follow",
|
|
params: { },
|
|
},
|
|
];
|
|
$scope.display.hideEvents = false;
|
|
$scope.display.hideCountdown = false;
|
|
}
|
|
else if (scene == "summary3") {
|
|
$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 },
|
|
},
|
|
{
|
|
type: "rank",
|
|
params: { limit: 10, which: "general", legend: false },
|
|
},
|
|
];
|
|
$scope.display.side = [
|
|
{
|
|
type: "exercice_follow",
|
|
params: { },
|
|
},
|
|
];
|
|
$scope.display.hideEvents = false;
|
|
$scope.display.hideCountdown = false;
|
|
}
|
|
else if (scene == "happyhour") {
|
|
$scope.display.customCountdown = {
|
|
show: true,
|
|
shadow: "#E8CF5C",
|
|
end: new Date($rootScope.getSrvTime().getTime() + 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($rootScope.getSrvTime().getTime() + 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($rootScope.getSrvTime().getTime() + duration)).toISOString();
|
|
}
|
|
$scope.genCustomCountdownDate = function(duration) {
|
|
if (duration == null) {
|
|
$scope.display.customCountdown.end = $rootScope.settings.activateTime;
|
|
} else {
|
|
$scope.display.customCountdown.end = (new Date($rootScope.getSrvTime().getTime() + duration)).toISOString();
|
|
}
|
|
}
|
|
|
|
$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);
|
|
prms.$promise.then(function() {
|
|
$scope.addToast('success', 'Scene successfully published!');
|
|
}, function(response) {
|
|
$scope.addToast('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.display.scenes.push({params: {}});
|
|
};
|
|
$scope.delScene = function(s) {
|
|
$scope.someUpdt = true;
|
|
angular.forEach($scope.display.scenes, function(scene, k) {
|
|
if (scene == s)
|
|
$scope.display.scenes.splice(k, 1);
|
|
});
|
|
};
|
|
$scope.upScene = function(s) {
|
|
$scope.someUpdt = true;
|
|
angular.forEach($scope.display.scenes, function(scene, k) {
|
|
if (scene == s && k > 0) {
|
|
$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.display.scenes, function(scene, k) {
|
|
if (move && scene == s) {
|
|
$scope.display.scenes.splice(k, 1);
|
|
$scope.display.scenes.splice(k + 1, 0, scene);
|
|
move = false;
|
|
}
|
|
});
|
|
};
|
|
})
|
|
|
|
.controller("FilesListController", function($scope, File, $location, $http, $rootScope) {
|
|
$scope.files = File.query();
|
|
$scope.errfnd = null;
|
|
$scope.errzip = null;
|
|
$scope.fields = ["id", "path", "name", "size"];
|
|
|
|
$scope.clearFiles = function(id) {
|
|
File.delete(function() {
|
|
$rootScope.staticFilesNeedUpdate++;
|
|
$scope.files = [];
|
|
});
|
|
};
|
|
$scope.gunzipFile = function(f) {
|
|
f.gunzipWIP = true;
|
|
$http({
|
|
url: "api/files/" + f.id + "/gunzip",
|
|
method: "POST"
|
|
}).then(function(response) {
|
|
f.gunzipWIP = false;
|
|
f.err = true;
|
|
}, function(response) {
|
|
f.gunzipWIP = false;
|
|
$scope.inSync = false;
|
|
$scope.errzip += 1;
|
|
f.err = response.data.errmsg;
|
|
})
|
|
};
|
|
$scope.checksum = function(f) {
|
|
f.checkWIP = true;
|
|
$http({
|
|
url: "api/files/" + f.id + "/check",
|
|
method: "POST"
|
|
}).then(function(response) {
|
|
f.checkWIP = false;
|
|
f.err = true;
|
|
}, function(response) {
|
|
f.checkWIP = false;
|
|
$scope.inSync = false;
|
|
$scope.errfnd += 1;
|
|
f.err = response.data.errmsg;
|
|
})
|
|
};
|
|
$scope.checksumAll = function() {
|
|
$scope.errfnd = null;
|
|
angular.forEach($scope.files, function(file) {
|
|
$scope.checksum(file);
|
|
});
|
|
if ($scope.errfnd === null) $scope.errfnd = 0;
|
|
};
|
|
$scope.gunzipFiles = function() {
|
|
$scope.errzip = null;
|
|
angular.forEach($scope.files, function(file) {
|
|
$scope.gunzipFile(file);
|
|
});
|
|
if ($scope.errzip === null) $scope.errzip = 0;
|
|
};
|
|
$scope.show = function(f) {
|
|
$location.url("/exercices/" + f.idExercice);
|
|
};
|
|
})
|
|
|
|
.controller("EventsListController", function($scope, Event, $location) {
|
|
$scope.events = Event.query();
|
|
$scope.fields = ["id", "kind", "txt", "time"];
|
|
|
|
$scope.clearEvents = function(id) {
|
|
Event.delete(function() {
|
|
$scope.events = [];
|
|
});
|
|
};
|
|
$scope.show = function(id) {
|
|
$location.url("/events/" + id);
|
|
};
|
|
})
|
|
.controller("EventController", function($scope, Event, $routeParams, $location) {
|
|
$scope.event = Event.get({ eventId: $routeParams.eventId });
|
|
$scope.fields = ["kind", "txt", "time"];
|
|
$scope.kinds = {
|
|
"secondary": "Par défaut",
|
|
"primary": "Mise en valeur",
|
|
"info": "Info",
|
|
"warning": "Warning",
|
|
"success": "Success",
|
|
"danger": "Danger",
|
|
"light": "Clair",
|
|
"dark": "Foncé",
|
|
};
|
|
|
|
$scope.saveEvent = function() {
|
|
if (this.event.id) {
|
|
this.event.$update();
|
|
} else {
|
|
this.event.$save(function() {
|
|
$location.url("/events/" + $scope.event.id);
|
|
});
|
|
}
|
|
}
|
|
$scope.deleteEvent = function() {
|
|
this.event.$remove(function() { $location.url("/events/");});
|
|
}
|
|
})
|
|
|
|
.controller("AssigneesListController", function($scope, ClaimAssignee, $location) {
|
|
$scope.assignees = ClaimAssignee.query();
|
|
|
|
$scope.setMyAId = function(aid) {
|
|
setCookie("myassignee", aid, 5);
|
|
$location.url("/claims/");
|
|
}
|
|
$scope.whoami = getCookie("myassignee");
|
|
$scope.newAssignee = function() {
|
|
$scope.assignees.push(new ClaimAssignee());
|
|
}
|
|
$scope.edit = function(a) {
|
|
a.edit = true;
|
|
}
|
|
$scope.updateAssignee = function(a) {
|
|
if (a.id) {
|
|
a.$update(function() { $location.url("/claims/");});
|
|
} else {
|
|
a.$save()
|
|
}
|
|
}
|
|
$scope.removeAssignee = function(a) {
|
|
a.$remove(function() { $location.url("/claims/");});
|
|
}
|
|
})
|
|
.controller("ClaimsTinyListController", function($scope, Claim, ClaimAssignee, $interval) {
|
|
$scope.whoami = getCookie("myassignee");
|
|
|
|
var priorities = {
|
|
"low": 1,
|
|
"medium": 2,
|
|
"high": 3,
|
|
"critical": 4,
|
|
};
|
|
$scope.priorities = [
|
|
"secondary",
|
|
"light",
|
|
"info",
|
|
"warning",
|
|
"danger",
|
|
];
|
|
|
|
var refresh = function() {
|
|
Claim.query().$promise.then(function(claims) {
|
|
$scope.newClaims = 0;
|
|
$scope.newClaimsMaxLevel = 0;
|
|
$scope.myClaims = 0;
|
|
$scope.myClaimsMaxLevel = 0;
|
|
|
|
claims.forEach(function(claim, cid) {
|
|
if ($scope.whoami && !claim.id_assignee && claim.state == 'new') {
|
|
$scope.newClaims++;
|
|
if (priorities[claim.priority] > $scope.newClaimsMaxLevel)
|
|
$scope.newClaimsMaxLevel = priorities[claim.priority];
|
|
}
|
|
else if ($scope.whoami && claim.id_assignee == $scope.whoami && claim.state != 'closed' && claim.state != 'invalid') {
|
|
$scope.myClaims++;
|
|
if (claim.state == 'new' && priorities[claim.priority] > $scope.myClaimsMaxLevel)
|
|
$scope.myClaimsMaxLevel = priorities[claim.priority];
|
|
}
|
|
})
|
|
});
|
|
};
|
|
refresh();
|
|
$interval(refresh, 10000);
|
|
})
|
|
.controller("ClaimsListController", function($scope, Claim, ClaimAssignee, Teams, $interval, $location) {
|
|
var refresh = function() {
|
|
$scope.claims = Claim.query();
|
|
$scope.assignees = ClaimAssignee.query();
|
|
}
|
|
refresh();
|
|
var myinterval = $interval(refresh, 10000);
|
|
$scope.$on('$destroy', function () { $interval.cancel(myinterval); });
|
|
|
|
$scope.whoami = getCookie("myassignee");
|
|
$scope.teams = Teams.get();
|
|
$scope.fields = ["subject", "id_team", "state", "id_assignee", "last_update", "id"];
|
|
|
|
$scope.order = "priority";
|
|
$scope.chOrder = function(no) {
|
|
$scope.order = no;
|
|
};
|
|
|
|
$scope.clearClaims = function(id) {
|
|
Claim.delete(function() {
|
|
$scope.claims = [];
|
|
});
|
|
};
|
|
$scope.show = function(id) {
|
|
$location.url("/claims/" + id);
|
|
};
|
|
})
|
|
.controller("ClaimLastUpdateController", function($scope, $http) {
|
|
$scope.init = function(claim) {
|
|
$http.get("api/claims/" + claim.id + "/last_update").then(function(response) {
|
|
if (response.data)
|
|
$scope.last_update = response.data;
|
|
else
|
|
$scope.last_update = claim.creation;
|
|
claim.last_update = $scope.last_update;
|
|
}, function(response) {
|
|
$scope.last_update = claim.creation;
|
|
})
|
|
}
|
|
})
|
|
.controller("ClaimController", function($scope, Claim, ClaimAssignee, Teams, Exercice, $routeParams, $location, $http, $rootScope) {
|
|
$scope.claim = Claim.get({ claimId: $routeParams.claimId }, function(v) {
|
|
v.id_team = "" + v.id_team;
|
|
if (!v.priority)
|
|
v.priority = "medium";
|
|
});
|
|
if ($routeParams.claimId == "new")
|
|
$scope.fields = ["id_team", "id_exercice", "subject", "priority", "id_assignee"];
|
|
else
|
|
$scope.fields = ["subject", "priority", "id_exercice", "id_assignee", "id_team", "creation", "state"];
|
|
$scope.assignees = ClaimAssignee.query();
|
|
$scope.comm = { ndescription: ""};
|
|
$scope.whoami = Math.floor(getCookie("myassignee"));
|
|
$scope.teams = Teams.get();
|
|
$scope.exercices = Exercice.query();
|
|
$scope.namedFields = {
|
|
"subject": "Objet",
|
|
"id_assignee": "Assigné à",
|
|
"state": "État",
|
|
"id_team": "Équipe",
|
|
"id_exercice": "Challenge",
|
|
"creation": "Création",
|
|
"priority": "Priorité",
|
|
"description": "Description",
|
|
};
|
|
$scope.states = {
|
|
"new": "Nouveau",
|
|
"need-info": "Besoin d'infos",
|
|
"confirmed": "Confirmé",
|
|
"in-progress": "En cours",
|
|
"need-review": "Fait",
|
|
"closed": "Clos",
|
|
"invalid": "Invalide",
|
|
};
|
|
$scope.priorities = {
|
|
"low": "Basse",
|
|
"medium": "Moyenne",
|
|
"high": "Haute",
|
|
"critical": "Critique",
|
|
};
|
|
|
|
$scope.changeState = function(state) {
|
|
this.claim.state = state;
|
|
if ((state == "in-progress" || state == "invalid") && this.claim.id_assignee)
|
|
this.claim.id_assignee = $scope.whoami;
|
|
if (this.claim.id)
|
|
this.saveClaim(state == "invalid" || state == "closed");
|
|
}
|
|
$scope.assignToMe = function() {
|
|
this.claim.id_assignee = $scope.whoami;
|
|
if (this.claim.id)
|
|
this.saveClaim(false);
|
|
}
|
|
$scope.updateDescription = function(description) {
|
|
$http({
|
|
url: "api/claims/" + $scope.claim.id + "/descriptions",
|
|
method: "PUT",
|
|
data: description
|
|
}).then(function(response) {
|
|
$scope.claim = Claim.get({ claimId: $routeParams.claimId }, function(v) {
|
|
v.id_team = "" + v.id_team;
|
|
if (!v.priority)
|
|
v.priority = "medium";
|
|
});
|
|
});
|
|
}
|
|
$scope.saveDescription = function() {
|
|
$http({
|
|
url: "api/claims/" + $scope.claim.id,
|
|
method: "POST",
|
|
data: {
|
|
"id_assignee": $scope.whoami,
|
|
"content": $scope.comm.ndescription
|
|
}
|
|
}).then(function(response) {
|
|
$location.url("/claims/" + $scope.claim.id + "/");
|
|
});
|
|
}
|
|
$scope.saveClaim = function(backToList) {
|
|
this.claim.whoami = $scope.whoami;
|
|
if (this.claim.id_team) {
|
|
this.claim.id_team = parseInt(this.claim.id_team, 10);
|
|
} else {
|
|
this.claim.id_team = null;
|
|
}
|
|
if (this.claim.id) {
|
|
this.claim.$update(function(v) {
|
|
v.id_team = "" + v.id_team;
|
|
if ($scope.comm.ndescription)
|
|
$scope.saveDescription();
|
|
else if (backToList)
|
|
$location.url("/claims/");
|
|
else
|
|
$scope.claim = Claim.get({ claimId: $routeParams.claimId }, function(v) {
|
|
v.id_team = "" + v.id_team;
|
|
if (!v.priority)
|
|
v.priority = "medium";
|
|
});
|
|
});
|
|
} else {
|
|
this.claim.$save(function() {
|
|
if (!$scope.comm.ndescription)
|
|
$scope.comm.ndescription = "Création de la tâche";
|
|
$scope.saveDescription();
|
|
}, function(response) {
|
|
$scope.addToast('danger', 'An error occurs when trying to save claim:', response.data.errmsg);
|
|
});
|
|
}
|
|
}
|
|
$scope.deleteClaim = function() {
|
|
this.claim.$remove(function() { $location.url("/claims/");});
|
|
}
|
|
})
|
|
|
|
.controller("ThemesListController", function($scope, Theme, $location, $rootScope, $http) {
|
|
$scope.themes = Theme.query();
|
|
$scope.fields = ["name", "authors", "headline", "path"];
|
|
|
|
$scope.validateSearch = function(keyEvent) {
|
|
if (keyEvent.which === 13) {
|
|
var myTheme = null;
|
|
$scope.themes.forEach(function(theme) {
|
|
if (String(theme.name.toLowerCase()).indexOf($scope.query.toLowerCase()) >= 0) {
|
|
if (myTheme === null)
|
|
myTheme = theme;
|
|
else
|
|
myTheme = false;
|
|
}
|
|
});
|
|
if (myTheme)
|
|
$location.url("themes/" + myTheme.id);
|
|
}
|
|
};
|
|
|
|
$scope.show = function(id) {
|
|
$location.url("/themes/" + id);
|
|
};
|
|
$scope.inSync = false;
|
|
$scope.sync = function() {
|
|
$scope.inSync = true;
|
|
$http({
|
|
url: "api/sync/themes",
|
|
method: "POST"
|
|
}).then(function(response) {
|
|
$scope.inSync = false;
|
|
$scope.themes = Theme.query();
|
|
$rootScope.staticFilesNeedUpdate++;
|
|
if (response.data)
|
|
$scope.addToast('danger', 'An error occurs when synchronizing theme list:', response.data);
|
|
else
|
|
$scope.addToast('success', 'Synchronisation de la liste des thèmes terminée avec succès.');
|
|
}, function(response) {
|
|
$scope.inSync = false;
|
|
$scope.addToast('danger', 'An error occurs when synchronizing theme list:', response.data.errmsg);
|
|
});
|
|
};
|
|
})
|
|
.controller("ThemeController", function($scope, Theme, $routeParams, $location, $rootScope, $http) {
|
|
$scope.theme = Theme.get({ themeId: $routeParams.themeId });
|
|
$scope.fields = ["name", "urlid", "locked", "authors", "headline", "intro", "image", "partner_txt", "partner_href", "partner_img"];
|
|
|
|
$scope.saveTheme = function() {
|
|
if (this.theme.id) {
|
|
this.theme.$update();
|
|
} else {
|
|
this.theme.$save(function() {
|
|
$location.url("/themes/" + $scope.theme.id);
|
|
});
|
|
}
|
|
$rootScope.staticFilesNeedUpdate++;
|
|
}
|
|
$scope.deleteTheme = function() {
|
|
this.theme.$remove(function() {
|
|
$rootScope.staticFilesNeedUpdate++;
|
|
$location.url("/themes/");
|
|
}, function(response) {
|
|
$scope.addToast('danger', 'An error occurs when trying to delete theme:', response.data.errmsg);
|
|
});
|
|
}
|
|
})
|
|
|
|
.controller("TagsListController", function($scope, $http) {
|
|
$scope.tags = [];
|
|
$http({
|
|
url: "api/tags",
|
|
method: "GET"
|
|
}).then(function(response) {
|
|
$scope.tags = response.data
|
|
});
|
|
})
|
|
|
|
.controller("AllExercicesListController", function($scope, Exercice, Theme, $routeParams, $location, $rootScope, $http, $filter) {
|
|
$http({
|
|
url: "api/themes.json",
|
|
method: "GET"
|
|
}).then(function(response) {
|
|
$scope.themes = response.data
|
|
});
|
|
|
|
$scope.exercices = Exercice.query();
|
|
$scope.exercice = {}; // Array used to save fields to updates in selected exercices
|
|
$scope.fields = ["title", "headline"];
|
|
|
|
$scope.validateSearch = function(keyEvent) {
|
|
if (keyEvent.which === 13) {
|
|
var myExercice = null;
|
|
$scope.exercices.forEach(function(exercice) {
|
|
if (String(exercice.title.toLowerCase()).indexOf($scope.query.toLowerCase()) >= 0) {
|
|
if (myExercice === null)
|
|
myExercice = exercice;
|
|
else
|
|
myExercice = false;
|
|
}
|
|
});
|
|
if (myExercice)
|
|
$location.url("exercices/" + myExercice.id);
|
|
}
|
|
};
|
|
|
|
$scope.toggleSelectAll = function() {
|
|
angular.forEach($filter('filter')($scope.exercices, $scope.query), function(ex) {
|
|
ex.selected = !$scope.selectall
|
|
})
|
|
}
|
|
|
|
$scope.updateExercices = function() {
|
|
angular.forEach($scope.exercices, function(ex) {
|
|
if (ex.selected) {
|
|
Exercice.patch({exerciceId: ex.id}, $scope.exercice);
|
|
}
|
|
})
|
|
$scope.exercice = {};
|
|
$rootScope.staticFilesNeedUpdate++;
|
|
$scope.addToast('success', 'Édition de masse terminée avec succès');
|
|
}
|
|
|
|
$scope.show = function(id) {
|
|
$location.url("/exercices/" + id);
|
|
};
|
|
$scope.inSync = false;
|
|
$scope.syncFull = function() {
|
|
$scope.inSync = true;
|
|
$scope.done = -1;
|
|
$scope.total = 0;
|
|
var work = [];
|
|
var go = function() {
|
|
if (!work.length) {
|
|
$scope.addToast('info', "Synchronisation des exercices terminée.");
|
|
$scope.inSync = false;
|
|
return;
|
|
}
|
|
var u = work.pop();
|
|
|
|
$http({
|
|
url: u,
|
|
method: "GET"
|
|
}).then(function(response) {
|
|
$rootScope.staticFilesNeedUpdate++;
|
|
$scope.done += 1;
|
|
go();
|
|
}, function(response) {
|
|
$scope.done += 1;
|
|
go();
|
|
});
|
|
};
|
|
|
|
angular.forEach($scope.exercices, function(ex) {
|
|
if ($scope.syncFiles)
|
|
work.push("api/sync/exercices/" + ex.id + "/files");
|
|
if ($scope.syncHints)
|
|
work.push("api/sync/exercices/" + ex.id + "/hints");
|
|
if ($scope.syncFlags)
|
|
work.push("api/sync/exercices/" + ex.id + "/flags");
|
|
});
|
|
$scope.total = work.length;
|
|
go();
|
|
|
|
};
|
|
$scope.syncFiles = true;
|
|
$scope.syncHints = true;
|
|
$scope.syncFlags = true;
|
|
})
|
|
.controller("ExercicesListController", function($scope, ThemedExercice, $location, $rootScope, $http) {
|
|
$scope.exercices = ThemedExercice.query({ themeId: $scope.theme.id });
|
|
$scope.fields = ["title", "headline", "issue"];
|
|
|
|
$scope.show = function(id) {
|
|
$location.url("/themes/" + $scope.theme.id + "/exercices/" + id);
|
|
};
|
|
|
|
$scope.inSync = false;
|
|
$scope.syncExo = function() {
|
|
$scope.inSync = true;
|
|
$http({
|
|
url: "api/sync/themes/" + $scope.theme.id + "/exercices",
|
|
method: "POST"
|
|
}).then(function(response) {
|
|
$scope.inSync = false;
|
|
$scope.exercices = ThemedExercice.query({ themeId: $scope.theme.id });
|
|
$rootScope.staticFilesNeedUpdate++;
|
|
if (response.data)
|
|
$scope.addToast('warning', 'An error occurs when synchrinizing exercices:', response.data);
|
|
else
|
|
$scope.addToast('success', 'Synchronisation de la liste des exercices terminée avec succès.');
|
|
}, function(response) {
|
|
$scope.inSync = false;
|
|
$scope.addToast('danger', 'An error occurs when synchrinizing exercices:', response.data.errmsg);
|
|
});
|
|
};
|
|
})
|
|
.controller("ExerciceController", function($scope, $rootScope, Exercice, ThemedExercice, $routeParams, $location, $http) {
|
|
if ($routeParams.themeId && $routeParams.exerciceId == "new") {
|
|
$scope.exercice = new ThemedExercice();
|
|
} else {
|
|
$scope.exercice = Exercice.get({ exerciceId: $routeParams.exerciceId });
|
|
}
|
|
|
|
$scope.my_ex_num = {};
|
|
|
|
$http({
|
|
url: "api/themes.json",
|
|
method: "GET"
|
|
}).then(function(response) {
|
|
$scope.themes = response.data;
|
|
if ($scope.exercice.id_theme) {
|
|
for (var k in $scope.themes[$scope.exercice.id_theme].exercices) {
|
|
var exercice = $scope.themes[$scope.exercice.id_theme].exercices[k];
|
|
$scope.my_ex_num[exercice.id] = k;
|
|
}
|
|
} else {
|
|
for (var k in $scope.themes["0"].exercices) {
|
|
var exercice = $scope.themes["0"].exercices[k];
|
|
$scope.my_ex_num[exercice.id] = k;
|
|
}
|
|
}
|
|
});
|
|
$scope.exercices = Exercice.query();
|
|
$scope.fields = ["title", "urlid", "authors", "disabled", "statement", "headline", "overview", "finished", "depend", "gain", "coefficient", "videoURI", "image", "resolution", "issue", "issuekind", "wip"];
|
|
|
|
$scope.inSync = false;
|
|
$scope.syncExo = function() {
|
|
$scope.inSync = true;
|
|
$http({
|
|
url: $scope.exercice.id_theme?("api/sync/themes/" + $scope.exercice.id_theme + "/exercices/" + $routeParams.exerciceId):("api/sync/exercices/" + $routeParams.exerciceId),
|
|
method: "POST"
|
|
}).then(function(response) {
|
|
$scope.inSync = false;
|
|
$scope.exercice = Exercice.get({ exerciceId: $routeParams.exerciceId });
|
|
$rootScope.staticFilesNeedUpdate++;
|
|
if (response.data)
|
|
$scope.addToast('danger', 'An error occurs when synchronizing exercice:', response.data);
|
|
else
|
|
$scope.addToast('success', "Synchronisation de l'exercice terminée avec succès.");
|
|
}, function(response) {
|
|
$scope.inSync = false;
|
|
$scope.addToast('danger', 'An error occurs when synchronizing exercice:', response.data.errmsg);
|
|
});
|
|
};
|
|
|
|
$scope.deleteExercice = function() {
|
|
var tid = $scope.exercice.id_theme;
|
|
this.exercice.$remove(function() {
|
|
$rootScope.staticFilesNeedUpdate++;
|
|
$location.url("/themes/" + tid);
|
|
}, function(response) {
|
|
$scope.addToast('danger', 'An error occurs when trying to delete exercice:', response.data.errmsg);
|
|
});
|
|
}
|
|
$scope.saveExercice = function() {
|
|
if (this.exercice.id) {
|
|
this.exercice.$update();
|
|
$rootScope.staticFilesNeedUpdate++;
|
|
} else if ($routeParams.themeId) {
|
|
this.exercice.$save({ themeId: $routeParams.themeId }, function() {
|
|
$rootScope.staticFilesNeedUpdate++;
|
|
$location.url("/themes/" + $scope.exercice.idTheme + "/exercices/" + $scope.exercice.id);
|
|
}, function(response) {
|
|
$scope.addToast('danger', 'An error occurs when trying to save exercice:', response.data.errmsg);
|
|
});
|
|
}
|
|
}
|
|
$scope.selectedTeam = "";
|
|
$scope.validateForTeam = function() {
|
|
var flagid = $("#validationModal").data("flagid");
|
|
if (!flagid) return;
|
|
var target = {
|
|
team_id: parseInt($("#tteam").val().replace(/number:/, '')),
|
|
kind: $("#validationModal").data("kind"),
|
|
secondary: flagid,
|
|
};
|
|
$http({
|
|
url: "api/exercices/" + $scope.exercice.id + "/history.json",
|
|
method: "PUT",
|
|
data: target
|
|
}).then(function(response) {
|
|
$rootScope.staticFilesNeedUpdate++;
|
|
$("#validationModal").modal('hide');
|
|
$scope.addToast('success', 'Flag validé avec succès');
|
|
}, function(response) {
|
|
$scope.addToast('danger', 'An error occurs when trying to validate flag for team:', response.data.errmsg);
|
|
});
|
|
}
|
|
$scope.historyAppend = function() {
|
|
var secondary = null;
|
|
if ($("#historyEvent").val() == "hint")
|
|
secondary = parseInt($("#historySecondaryHint").val().replace(/number:/, ''));
|
|
else if ($("#historyEvent").val() == "wchoices" || $("#historyEvent").val() == "flag_found")
|
|
secondary = parseInt($("#historySecondaryFlag").val().replace(/number:/, ''));
|
|
else if ($("#historyEvent").val() == "mcq_found")
|
|
secondary = parseInt($("#historySecondaryQuiz").val().replace(/number:/, ''));
|
|
|
|
var target = {
|
|
team_id: parseInt($("#tteam").val().replace(/number:/, '')),
|
|
kind: $("#historyEvent").val(),
|
|
secondary: secondary,
|
|
};
|
|
$http({
|
|
url: "api/exercices/" + $scope.exercice.id + "/history.json",
|
|
method: "PUT",
|
|
data: target
|
|
}).then(function(response) {
|
|
$rootScope.staticFilesNeedUpdate++;
|
|
$("#appendHistoryModal").modal('hide');
|
|
$scope.addToast('success', 'Événement ajouté avec succès');
|
|
$scope.refreshHistory();
|
|
}, function(response) {
|
|
$scope.addToast('danger', 'An error occurs when trying to add event in history:', response.data.errmsg);
|
|
});
|
|
}
|
|
})
|
|
|
|
.controller("SubmissionsStatsController", function($scope, $http, $interval) {
|
|
var refresh = function() {
|
|
$http({
|
|
url: "api/submissions-stats.json",
|
|
}).then(function(response) {
|
|
$scope.submissionsstats = response.data;
|
|
});
|
|
}
|
|
var myinterval = $interval(refresh, 15000);
|
|
refresh();
|
|
$scope.$on('$destroy', function () { $interval.cancel(myinterval); });
|
|
})
|
|
|
|
.controller("ValidationsStatsController", function($scope, $http, $interval) {
|
|
var refresh = function() {
|
|
$http({
|
|
url: "api/validations-stats.json",
|
|
}).then(function(response) {
|
|
$scope.validationsstats = response.data;
|
|
});
|
|
}
|
|
var myinterval = $interval(refresh, 15000);
|
|
refresh();
|
|
$scope.$on('$destroy', function () { $interval.cancel(myinterval); });
|
|
})
|
|
|
|
.controller("ExercicesStatsController", function($scope, Themes, ExercicesStats) {
|
|
$scope.themes = Themes.get();
|
|
$scope.exercices = {};
|
|
ExercicesStats.query().$promise.then(function (exs) {
|
|
exs.forEach(function (ex) {
|
|
$scope.exercices[ex.id_exercice] = ex;
|
|
})
|
|
});
|
|
$scope.lenExoArray = [];
|
|
$scope.themes.$promise.then(function(themes) {
|
|
if (themes['0'] && themes['0'].exercices.length) {
|
|
var j = 0;
|
|
for (var i = themes['0'].exercices.length / 10; i >= 0; i--) {
|
|
$scope.lenExoArray.push(j++);
|
|
}
|
|
}
|
|
});
|
|
})
|
|
|
|
.controller("ExerciceStatsController", function($scope, ExerciceStats, $routeParams) {
|
|
$scope.stats = ExerciceStats.get({ exerciceId: $routeParams.exerciceId });
|
|
})
|
|
|
|
.controller("ExerciceClaimsController", function($scope, ExerciceClaims, Team, ClaimAssignee, $routeParams, $location) {
|
|
$scope.claims = ExerciceClaims.query({ exerciceId: $routeParams.exerciceId });
|
|
$scope.assignees = ClaimAssignee.query();
|
|
|
|
$scope.claims.$promise.then(function(claims) {
|
|
claims.forEach(function(claim, cid) {
|
|
$scope.claims[cid].team = Team.get({ teamId: claim.id_team });
|
|
})
|
|
});
|
|
|
|
$scope.showClosed = false;
|
|
$scope.show = function(id) {
|
|
$location.url("/claims/" + id);
|
|
};
|
|
})
|
|
|
|
.controller("ExerciceTagsController", function($scope, ExerciceTags, $routeParams, $rootScope) {
|
|
$scope.tags = ExerciceTags.query({ exerciceId: $routeParams.exerciceId });
|
|
|
|
$scope.addTag = function() {
|
|
$scope.tags.push("");
|
|
}
|
|
$scope.deleteTag = function() {
|
|
$scope.tags.splice($scope.tags.indexOf(this.tag), 1);
|
|
return $scope.saveTags();
|
|
}
|
|
$scope.saveTags = function() {
|
|
ExerciceTags.update({ exerciceId: $routeParams.exerciceId }, this.tags);
|
|
$rootScope.staticFilesNeedUpdate++;
|
|
}
|
|
})
|
|
|
|
.controller("ExerciceHistoryController", function($scope, ExerciceHistory, $routeParams, $http, $rootScope) {
|
|
$scope.history = [];
|
|
$scope.refreshHistory = function() {
|
|
$scope.history = ExerciceHistory.query({ exerciceId: $routeParams.exerciceId });
|
|
}
|
|
$scope.$parent.refreshHistory = $scope.refreshHistory;
|
|
$scope.refreshHistory();
|
|
$scope.updHistory = function() {
|
|
var target = {
|
|
team_id: $("#updHistory").data("idteam"),
|
|
kind: $("#updHistory").data("kind"),
|
|
time: $("#updHistory").data("time"),
|
|
secondary: $("#updHistory").data("secondary") != "" ? $("#updHistory").data("secondary") : null,
|
|
coeff: parseFloat($('#historycoeff').val()),
|
|
};
|
|
if (target) {
|
|
$http({
|
|
url: "api/exercices/" + $routeParams.exerciceId + "/history.json",
|
|
method: "PATCH",
|
|
data: target
|
|
}).then(function(response) {
|
|
$rootScope.staticFilesNeedUpdate++;
|
|
$scope.history = ExerciceHistory.query({ exerciceId: $routeParams.exerciceId });
|
|
$('#updHistory').modal('hide');
|
|
}, function(response) {
|
|
$scope.addToast('danger', 'An error occurs when updating history item: ', response.data.errmsg);
|
|
});
|
|
}
|
|
}
|
|
$scope.delHistory = function(row) {
|
|
$http({
|
|
url: "api/exercices/" + $routeParams.exerciceId + "/history.json",
|
|
method: "DELETE",
|
|
data: row
|
|
}).then(function(response) {
|
|
$rootScope.staticFilesNeedUpdate++;
|
|
$scope.history = ExerciceHistory.query({ exerciceId: $routeParams.exerciceId });
|
|
}, function(response) {
|
|
$scope.addToast('danger', 'An error occurs when removing history item: ', response.data.errmsg);
|
|
});
|
|
}
|
|
})
|
|
|
|
.controller("ExerciceFilesController", function($scope, ExerciceFile, $routeParams, $rootScope, $http) {
|
|
$scope.files = ExerciceFile.query({ exerciceId: $routeParams.exerciceId });
|
|
|
|
$scope.deleteFile = function() {
|
|
this.file.$delete(function() {
|
|
$scope.files = ExerciceFile.query({ exerciceId: $routeParams.exerciceId });
|
|
});
|
|
$rootScope.staticFilesNeedUpdate++;
|
|
return false;
|
|
}
|
|
$scope.deleteFileDep = function() {
|
|
$http({
|
|
url: "api//files/" + this.file.id + "/dependancies/" + this.dep.id,
|
|
method: "DELETE"
|
|
}).then(function(response) {
|
|
$scope.files = ExerciceFile.query({ exerciceId: $routeParams.exerciceId });
|
|
}, function(response) {
|
|
$scope.addToast('danger', 'An error occurs when removing file dep:', response.data.errmsg);
|
|
});
|
|
$rootScope.staticFilesNeedUpdate++;
|
|
return false;
|
|
}
|
|
$scope.saveFile = function() {
|
|
this.file.$update();
|
|
$rootScope.staticFilesNeedUpdate++;
|
|
}
|
|
$scope.inSync = false;
|
|
$scope.syncFiles = function() {
|
|
$scope.inSync = true;
|
|
$http({
|
|
url: "api/sync/exercices/" + $routeParams.exerciceId + "/files",
|
|
method: "POST"
|
|
}).then(function(response) {
|
|
$scope.inSync = false;
|
|
$rootScope.staticFilesNeedUpdate++;
|
|
$scope.files = ExerciceFile.query({ exerciceId: $routeParams.exerciceId });
|
|
if (response.data)
|
|
$scope.addToast('danger', 'An error occurs when synchronizing flags list:', response.data);
|
|
else
|
|
$scope.addToast('success', "Synchronisation de la liste de fichiers terminée avec succès.");
|
|
}, function(response) {
|
|
$scope.inSync = false;
|
|
$scope.addToast('danger', 'An error occurs when synchronizing flags list:', response.data.errmsg);
|
|
});
|
|
};
|
|
})
|
|
|
|
.controller("ExerciceHintsController", function($scope, ExerciceHint, $routeParams, $rootScope, $http) {
|
|
$scope.hints = ExerciceHint.query({ exerciceId: $routeParams.exerciceId });
|
|
|
|
$scope.addHint = function() {
|
|
$scope.hints.push(new ExerciceHint());
|
|
}
|
|
$scope.deleteHint = function() {
|
|
this.hint.$delete(function() {
|
|
$scope.hints = ExerciceHint.query({ exerciceId: $routeParams.exerciceId });
|
|
$rootScope.staticFilesNeedUpdate++;
|
|
}, function(response) {
|
|
$scope.addToast('danger', 'An error occurs when trying to delete hint:', response.data.errmsg);
|
|
});
|
|
}
|
|
$scope.saveHint = function() {
|
|
if (this.hint.id) {
|
|
this.hint.$update();
|
|
} else {
|
|
this.hint.$save({ exerciceId: $routeParams.exerciceId });
|
|
}
|
|
$rootScope.staticFilesNeedUpdate++;
|
|
}
|
|
$scope.inSync = false;
|
|
$scope.syncHints = function() {
|
|
$scope.inSync = true;
|
|
$http({
|
|
url: "api/sync/exercices/" + $routeParams.exerciceId + "/hints",
|
|
method: "POST"
|
|
}).then(function(response) {
|
|
$scope.inSync = false;
|
|
$scope.hints = ExerciceHint.query({ exerciceId: $routeParams.exerciceId });
|
|
$rootScope.staticFilesNeedUpdate++;
|
|
if (response.data)
|
|
$scope.addToast('danger', 'An error occurs when synchronizing hints list:', response.data);
|
|
else
|
|
$scope.addToast('success', "Synchronisation de la liste d'indices terminée avec succès.");
|
|
}, function(response) {
|
|
$scope.inSync = false;
|
|
$scope.addToast('danger', 'An error occurs when synchronizing hints list:', response.data.errmsg);
|
|
});
|
|
};
|
|
})
|
|
|
|
.controller("ExerciceHintDepsController", function($scope, $routeParams, ExerciceHintDeps) {
|
|
$scope.init = function(hint) {
|
|
$scope.deps = ExerciceHintDeps.query({ exerciceId: $routeParams.exerciceId, hintId: hint.id });
|
|
}
|
|
})
|
|
|
|
.controller("ExerciceFlagsController", function($scope, ExerciceFlag, $routeParams, $rootScope, $http) {
|
|
$scope.flags = ExerciceFlag.query({ exerciceId: $routeParams.exerciceId });
|
|
|
|
$scope.flags.$promise.then(function(flags) {
|
|
flags.forEach(function(flag, fid) {
|
|
flags[fid].values = [''];
|
|
});
|
|
});
|
|
|
|
$scope.changeValue = function(flag) {
|
|
flag.value = undefined;
|
|
flag.show_raw = true;
|
|
}
|
|
$scope.addFlag = function() {
|
|
$scope.flags.push(new ExerciceFlag());
|
|
}
|
|
$scope.deleteFlag = function() {
|
|
this.flag.$delete(function() {
|
|
$scope.flags = ExerciceFlag.query({ exerciceId: $routeParams.exerciceId });
|
|
$rootScope.staticFilesNeedUpdate++;
|
|
}, function(response) {
|
|
$scope.addToast('danger', 'An error occurs when trying to delete flag:', response.data.errmsg);
|
|
});
|
|
}
|
|
$scope.saveFlag = function() {
|
|
if (this.flag.id) {
|
|
this.flag.$update();
|
|
} else {
|
|
this.flag.$save({ exerciceId: $routeParams.exerciceId });
|
|
}
|
|
$rootScope.staticFilesNeedUpdate++;
|
|
}
|
|
$scope.testFlag = function(flag) {
|
|
if (flag.values) {
|
|
var val = flag.value;
|
|
treatFlagKey(flag);
|
|
flag.test_str = flag.value;
|
|
flag.value = val;
|
|
}
|
|
|
|
if (flag.test_str) {
|
|
$http({
|
|
url: "api/exercices/" + $routeParams.exerciceId + "/flags/" + flag.id + "/try",
|
|
data: {"flag": flag.test_str},
|
|
method: "POST"
|
|
}).then(function(response) {
|
|
flag.test_str = "";
|
|
$scope.addToast('success', "Flag Ok !");
|
|
}, function(response) {
|
|
flag.test_str = "";
|
|
$scope.addToast('danger', 'An error occurs: ', response.data.errmsg);
|
|
});
|
|
}
|
|
}
|
|
$scope.inSync = false;
|
|
$scope.syncFlags = function() {
|
|
$scope.inSync = true;
|
|
$http({
|
|
url: "api/sync/exercices/" + $routeParams.exerciceId + "/flags",
|
|
method: "POST"
|
|
}).then(function(response) {
|
|
$scope.inSync = false;
|
|
$scope.flags = ExerciceFlag.query({ exerciceId: $routeParams.exerciceId });
|
|
$rootScope.staticFilesNeedUpdate++;
|
|
if (response.data)
|
|
$scope.addToast('danger', 'An error occurs when synchronizing flags list:', response.data);
|
|
else
|
|
$scope.addToast('success', "Synchronisation de la liste de drapeaux terminée avec succès.");
|
|
}, function(response) {
|
|
$scope.inSync = false;
|
|
$scope.addToast('danger', 'An error occurs when synchronizing flags list:', response.data.errmsg);
|
|
});
|
|
};
|
|
})
|
|
|
|
.controller("ExerciceFlagChoicesController", function($scope, ExerciceFlagChoices, $routeParams) {
|
|
$scope.choices = ExerciceFlagChoices.query({ exerciceId: $routeParams.exerciceId, flagId: $scope.flag.id })
|
|
|
|
$scope.flag.wantchoices = function() {
|
|
$scope.flag.choices = {};
|
|
$scope.choices.forEach(function(choice) {
|
|
$scope.flag.choices[choice.value] = choice.label
|
|
})
|
|
}
|
|
$scope.choices.$promise.then(function(choices) {
|
|
if ($scope.flag.choices_cost == 0 && choices.length > 0) {
|
|
$scope.flag.wantchoices()
|
|
}
|
|
})
|
|
|
|
|
|
$scope.addChoice = function() {
|
|
$scope.choices.push(new ExerciceFlagChoices())
|
|
}
|
|
|
|
$scope.saveChoice = function() {
|
|
if (this.choice.id)
|
|
this.choice.$update({exerciceId: $routeParams.exerciceId, flagId: this.flag.id})
|
|
else
|
|
this.choice.$save({exerciceId: $routeParams.exerciceId, flagId: this.flag.id})
|
|
}
|
|
|
|
$scope.deleteChoice = function() {
|
|
if (this.choice.id)
|
|
this.choice.$delete({exerciceId: $routeParams.exerciceId, flagId: this.flag.id}, function() {
|
|
$scope.choices = ExerciceFlagChoices.query({ exerciceId: $routeParams.exerciceId, flagId: $scope.flag.id })
|
|
})
|
|
}
|
|
})
|
|
|
|
.controller("ExerciceFlagDepsController", function($scope, $routeParams, ExerciceFlagDeps) {
|
|
$scope.init = function(flag) {
|
|
$scope.deps = ExerciceFlagDeps.query({ exerciceId: $routeParams.exerciceId, flagId: flag.id });
|
|
}
|
|
})
|
|
|
|
.controller("ExerciceMCQFlagsController", function($scope, ExerciceMCQFlag, $routeParams, $rootScope) {
|
|
$scope.quiz = ExerciceMCQFlag.query({ exerciceId: $routeParams.exerciceId });
|
|
|
|
$scope.addQuiz = function() {
|
|
$scope.quiz.push(new ExerciceMCQFlag());
|
|
}
|
|
$scope.deleteQuiz = function() {
|
|
this.q.$delete(function() {
|
|
$scope.quiz = ExerciceMCQFlag.query({ exerciceId: $routeParams.exerciceId });
|
|
$rootScope.staticFilesNeedUpdate++;
|
|
}, function(response) {
|
|
$scope.addToast('danger', 'An error occurs when trying to delete flag:', response.data.errmsg);
|
|
});
|
|
}
|
|
$scope.saveQuiz = function() {
|
|
if (this.q.id) {
|
|
this.q.$update();
|
|
} else {
|
|
this.q.$save({ exerciceId: $routeParams.exerciceId });
|
|
}
|
|
$rootScope.staticFilesNeedUpdate++;
|
|
}
|
|
|
|
$scope.addChoice = function() {
|
|
this.quiz[this.qk].entries.push({label: "", response: false})
|
|
}
|
|
$scope.deleteChoice = function() {
|
|
this.quiz[this.qk].entries.splice(this.quiz[this.qk].entries.indexOf(this.choice), 1);
|
|
}
|
|
})
|
|
|
|
.controller("ExerciceMCQDepsController", function($scope, $routeParams, ExerciceMCQDeps) {
|
|
$scope.init = function(flag) {
|
|
$scope.deps = ExerciceMCQDeps.query({ exerciceId: $routeParams.exerciceId, mcqId: flag.id });
|
|
}
|
|
})
|
|
|
|
.controller("TeamsListController", function($scope, $rootScope, Team, $location, $http) {
|
|
$scope.teams = Team.query();
|
|
$scope.fields = ["id", "name"];
|
|
|
|
$scope.validateSearch = function(keyEvent) {
|
|
if (keyEvent.which === 13) {
|
|
var myTeam = null;
|
|
$scope.teams.forEach(function(team) {
|
|
if (String(team.name.toLowerCase()).indexOf($scope.query.toLowerCase()) >= 0) {
|
|
if (myTeam === null)
|
|
myTeam = team;
|
|
else
|
|
myTeam = false;
|
|
}
|
|
});
|
|
if (myTeam)
|
|
$location.url("teams/" + myTeam.id);
|
|
}
|
|
};
|
|
|
|
$scope.genDexCfg = function() {
|
|
$http.post("api/dex.yaml").then(function() {
|
|
$http.post("api/dex-password.tpl").then(function() {
|
|
$scope.addToast('success', 'Dex config refreshed.', "Don't forget to reload/reboot frontend host.");
|
|
}, function(response) {
|
|
$scope.addToast('danger', 'An error occurs when generating dex password tpl:', response.data.errmsg);
|
|
});
|
|
}, function(response) {
|
|
$scope.addToast('danger', 'An error occurs when generating dex config:', response.data.errmsg);
|
|
});
|
|
}
|
|
$scope.desactiveTeams = function() {
|
|
$http.post("api/disableinactiveteams").then(function() {
|
|
$scope.teams = Team.query();
|
|
}, function(response) {
|
|
$scope.addToast('danger', 'An error occurs when disabling inactive teams:', response.data.errmsg);
|
|
});
|
|
}
|
|
$scope.show = function(id) {
|
|
$location.url("/teams/" + id);
|
|
};
|
|
})
|
|
.controller("TeamMembersController", function($scope, TeamMember) {
|
|
$scope.fields = ["firstname", "lastname", "nickname", "company"];
|
|
if ($scope.team != null) {
|
|
$scope.members = TeamMember.query({ teamId: $scope.team.id });
|
|
|
|
$scope.newMember = function() {
|
|
$scope.members.push(new TeamMember());
|
|
}
|
|
$scope.saveTeamMembers = function() {
|
|
if (this.team.id) {
|
|
TeamMember.save({ teamId: this.team.id }, $scope.members);
|
|
}
|
|
}
|
|
$scope.removeMember = function(member) {
|
|
angular.forEach($scope.members, function(m, k) {
|
|
if (member == m)
|
|
$scope.members.splice(k, 1);
|
|
});
|
|
}
|
|
}
|
|
})
|
|
.controller("TeamController", function($scope, $rootScope, $location, Team, TeamMember, $routeParams, $http) {
|
|
if ($scope.team && $scope.team.id)
|
|
$routeParams.teamId = $scope.team.id;
|
|
$scope.team = Team.get({ teamId: $routeParams.teamId });
|
|
$scope.fields = ["name", "color", "external_id"];
|
|
|
|
$scope.saveTeam = function() {
|
|
if (this.team.id) {
|
|
this.team.$update();
|
|
$rootScope.staticFilesNeedUpdate++;
|
|
} else {
|
|
this.team.$save(function() {
|
|
$rootScope.staticFilesNeedUpdate++;
|
|
$location.url("/teams/" + $scope.team.id);
|
|
});
|
|
}
|
|
}
|
|
$scope.resetPasswd = function(team) {
|
|
$http({
|
|
url: "api/password",
|
|
method: "POST"
|
|
}).then(function(response) {
|
|
team.password = response.data.password;
|
|
});
|
|
}
|
|
$scope.deleteTeam = function() {
|
|
backName = this.team.name;
|
|
this.team.$remove(function() { $scope.addToast('success', 'Team ' + backName + ' successfully removed.'); $location.url("/teams/"); $rootScope.staticFilesNeedUpdate++; },
|
|
function(response) { $scope.addToast('danger', 'An error occurs during suppression of the team:', response.data.errmsg); });
|
|
}
|
|
$scope.showStats = function() {
|
|
$location.url("/teams/" + $scope.team.id + "/stats");
|
|
}
|
|
$scope.showScore = function() {
|
|
$location.url("/teams/" + $scope.team.id + "/score");
|
|
}
|
|
})
|
|
.controller("TeamCertificatesController", function($scope, $rootScope, TeamCertificate, $routeParams, $http) {
|
|
$scope.certificates = TeamCertificate.query({ teamId: $routeParams.teamId });
|
|
$scope.certificates.$promise.then(function(certificates) {
|
|
certificates.forEach(function(certificate, cid) {
|
|
certificate.serial = parseInt(certificate.id).toString(16);
|
|
});
|
|
});
|
|
|
|
$scope.dissociateCertificate = function(certificate) {
|
|
$http({
|
|
url: "api/certs/" + certificate.id,
|
|
method: "PUT",
|
|
data: {
|
|
id_team: null
|
|
}
|
|
}).then(function(response) {
|
|
$scope.certificates = TeamCertificate.query({ teamId: $routeParams.teamId }).$promise.then(function(certificates) {
|
|
certificates.forEach(function(certificate, cid) {
|
|
certificate.serial = parseInt(certificate.id).toString(16);
|
|
});
|
|
});
|
|
$scope.addToast('success', 'Certificate successfully dissociated!');
|
|
}, function(response) {
|
|
$scope.addToast('danger', 'An error occurs when dissociating certiticate:', response.data.errmsg);
|
|
});
|
|
}
|
|
})
|
|
.controller("TeamAssociationsController", function($scope, $rootScope, TeamAssociation, $routeParams, $http) {
|
|
$scope.associations = TeamAssociation.query({ teamId: $routeParams.teamId });
|
|
$scope.form = { "newassoc": "" };
|
|
|
|
$scope.addAssociation = function() {
|
|
if ($scope.form.newassoc) {
|
|
TeamAssociation.save({ teamId: $scope.team.id, assoc: $scope.form.newassoc }).$promise.then(
|
|
function() {
|
|
$scope.form.newassoc = "";
|
|
$scope.associations = TeamAssociation.query({ teamId: $routeParams.teamId });
|
|
}, function(response) {
|
|
if (response.data)
|
|
$scope.addToast('danger', 'An error occurs when creating user association: ', response.data.errmsg);
|
|
});
|
|
}
|
|
}
|
|
|
|
$scope.dropAssociation = function(assoc) {
|
|
TeamAssociation.delete({ teamId: $routeParams.teamId, assoc: assoc }).$promise.then(
|
|
function() {
|
|
$scope.associations = TeamAssociation.query({ teamId: $routeParams.teamId });
|
|
}, function(response) {
|
|
$scope.addToast('danger', 'An error occurs when removing user association: ', response.data.errmsg);
|
|
});
|
|
}
|
|
})
|
|
.controller("TeamHistoryController", function($scope, TeamHistory, $routeParams, $http, $rootScope) {
|
|
$scope.history = TeamHistory.query({ teamId: $routeParams.teamId });
|
|
$scope.updHistory = function() {
|
|
var target = {
|
|
team_id: parseInt($routeParams.teamId),
|
|
kind: $("#updHistory").data("kind"),
|
|
time: $("#updHistory").data("time"),
|
|
secondary: $("#updHistory").data("secondary") != "" ? $("#updHistory").data("secondary") : null,
|
|
coeff: parseFloat($('#historycoeff').val()),
|
|
};
|
|
if (target) {
|
|
$http({
|
|
url: "api/exercices/" + $("#updHistory").data("primary") + "/history.json",
|
|
method: "PATCH",
|
|
data: target
|
|
}).then(function(response) {
|
|
$rootScope.staticFilesNeedUpdate++;
|
|
$scope.history = TeamHistory.query({ teamId: $routeParams.teamId });
|
|
$('#updHistory').modal('hide');
|
|
}, function(response) {
|
|
$scope.addToast('danger', 'An error occurs when updating history item: ', response.data.errmsg);
|
|
});
|
|
}
|
|
}
|
|
$scope.delHistory = function(row) {
|
|
$http({
|
|
url: "api/teams/" + $routeParams.teamId + "/history.json",
|
|
method: "DELETE",
|
|
data: row
|
|
}).then(function(response) {
|
|
$rootScope.staticFilesNeedUpdate++;
|
|
$scope.history = TeamHistory.query({ teamId: $routeParams.teamId });
|
|
}, function(response) {
|
|
$scope.addToast('danger', 'An error occurs when removing history item: ', response.data.errmsg);
|
|
});
|
|
}
|
|
})
|
|
.controller("TeamScoreController", function($scope, TeamScore, TeamMy, Exercice, $routeParams) {
|
|
$scope.scores = TeamScore.query({ teamId: $routeParams.teamId });
|
|
$scope.my = TeamMy.get({ teamId: $routeParams.teamId });
|
|
$scope.exercices = Exercice.query();
|
|
})
|
|
.controller("TeamStatsController", function($scope, TeamStats, $routeParams) {
|
|
$scope.teamstats = TeamStats.get({ teamId: $routeParams.teamId });
|
|
$scope.teamstats.$promise.then(function(res) {
|
|
solvedByLevelPie("#pieLevels", res.levels);
|
|
var themes = [];
|
|
angular.forEach(res.themes, function(theme, tid) {
|
|
themes.push(theme);
|
|
})
|
|
solvedByThemesPie("#pieThemes", themes);
|
|
});
|
|
})
|
|
.controller("TeamsJSONController", function($scope, Teams) {
|
|
$scope.teams = Teams.get();
|
|
$scope.teams.$promise.then(function(teams) {
|
|
$scope.rank = [];
|
|
Object.keys(teams).forEach(function(team) {
|
|
$scope.teams[team].id = team;
|
|
$scope.rank.push($scope.teams[team]);
|
|
})
|
|
});
|
|
})
|
|
.controller("TeamExercicesController", function($scope, Teams, Themes, TeamMy, Exercice, $routeParams) {
|
|
$scope.teams = Teams.get();
|
|
$scope.themes = Themes.get();
|
|
$scope.exercices = Exercice.query();
|
|
$scope.my = TeamMy.get({ teamId: $routeParams.teamId });
|
|
|
|
$scope.teams.$promise.then(function(res){
|
|
$scope.nb_teams = 0;
|
|
$scope.nb_reg_teams = Object.keys(res).length;
|
|
angular.forEach(res, function(team, tid) {
|
|
if (team.rank)
|
|
$scope.nb_teams += 1;
|
|
}, 0);
|
|
});
|
|
|
|
$scope.my.$promise.then(function(res){
|
|
$scope.solved_exercices = 0;
|
|
angular.forEach(res.exercices, function(exercice, eid) {
|
|
if (exercice.solved_rank) {
|
|
$scope.solved_exercices += 1;
|
|
}
|
|
}, 0);
|
|
});
|
|
|
|
})
|
|
|
|
.controller("PresenceController", function($scope, TeamPresence, $routeParams) {
|
|
$scope.presence = TeamPresence.query({ teamId: $routeParams.teamId });
|
|
$scope.presence.$promise.then(function(res) {
|
|
presenceCal($scope, "#presenceCal", res);
|
|
});
|
|
});
|
|
|
|
function solvedByLevelPie(location, data) {
|
|
var width = d3.select(location).node().getBoundingClientRect().width - parseInt(d3.select(location).style("padding-right")) - parseInt(d3.select(location).style("padding-left")),
|
|
height = d3.select(location).node().getBoundingClientRect().width - 10,
|
|
radius = Math.min(width, height) / 2,
|
|
innerRadius = 0.1 * radius;
|
|
|
|
var color = d3.scale.ordinal()
|
|
.range(["#9E0041", "#C32F4B", "#E1514B", "#F47245", "#FB9F59", "#FEC574", "#FAE38C", "#EAD195", "#C7E89E", "#9CD6A4", "#6CC4A4", "#4D9DB4", "#4776B4", "#5E4EA1"]);
|
|
|
|
var pie = d3.layout.pie()
|
|
.sort(null)
|
|
.value(function(d) { return d.width; });
|
|
|
|
var arc = d3.svg.arc()
|
|
.innerRadius(innerRadius)
|
|
.outerRadius(function (d) {
|
|
return (radius - innerRadius) * (d.data.score / 100.0) + innerRadius;
|
|
});
|
|
|
|
var outlineArc = d3.svg.arc()
|
|
.innerRadius(innerRadius)
|
|
.outerRadius(radius);
|
|
|
|
var svg = d3.select(location).append("svg")
|
|
.attr("width", width)
|
|
.attr("height", height)
|
|
.append("g")
|
|
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
|
|
|
|
data.forEach(function(d) {
|
|
d.score = d.solved * 100 / d.total;
|
|
d.width = d.tries + 1;
|
|
});
|
|
|
|
var path = svg.selectAll(".solidArc")
|
|
.data(pie(data))
|
|
.enter().append("path")
|
|
.attr("fill", function(d) { return color(d.data.tip); })
|
|
.attr("class", "solidArc")
|
|
.attr("stroke", "gray")
|
|
.attr("d", arc);
|
|
|
|
var outerPath = svg.selectAll(".outlineArc")
|
|
.data(pie(data))
|
|
.enter().append("path")
|
|
.attr("fill", "none")
|
|
.attr("stroke", "gray")
|
|
.attr("class", "outlineArc")
|
|
.attr("d", outlineArc);
|
|
|
|
var labelArc = d3.svg.arc()
|
|
.outerRadius(0.8 * radius)
|
|
.innerRadius(0.8 * radius);
|
|
|
|
svg.selectAll(".labelArc")
|
|
.data(pie(data))
|
|
.enter().append("text")
|
|
.attr("transform", function(d) { return "translate(" + labelArc.centroid(d) + ")"; })
|
|
.attr("dy", ".35em")
|
|
.attr("text-anchor", "middle")
|
|
.text(function(d) { return d.data.tip + ": " + d.data.solved + "/" + d.data.total; });
|
|
|
|
svg.selectAll(".label2Arc")
|
|
.data(pie(data))
|
|
.enter().append("text")
|
|
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
|
|
.attr("dy", ".35em")
|
|
.attr("text-anchor", "middle")
|
|
.text(function(d) { return d.data.tries; });
|
|
}
|
|
|
|
function solvedByThemesPie(location, data) {
|
|
var width = d3.select(location).node().getBoundingClientRect().width - parseInt(d3.select(location).style("padding-right")) - parseInt(d3.select(location).style("padding-left")),
|
|
height = d3.select(location).node().getBoundingClientRect().width,
|
|
radius = Math.min(width, height) / 2,
|
|
innerRadius = 0.1 * radius;
|
|
|
|
var color = d3.scale.ordinal()
|
|
.range(["#9E0041", "#C32F4B", "#E1514B", "#F47245", "#FB9F59", "#FEC574", "#FAE38C", "#EAD195", "#C7E89E", "#9CD6A4", "#6CC4A4", "#4D9DB4", "#4776B4", "#5E4EA1"]);
|
|
|
|
var pie = d3.layout.pie()
|
|
.sort(null)
|
|
.value(function(d) { return d.width; });
|
|
|
|
var arc = d3.svg.arc()
|
|
.innerRadius(innerRadius)
|
|
.outerRadius(function (d) {
|
|
return (radius - innerRadius) * (d.data.score / 100.0) + innerRadius;
|
|
});
|
|
|
|
var outlineArc = d3.svg.arc()
|
|
.innerRadius(innerRadius)
|
|
.outerRadius(radius);
|
|
|
|
var svg = d3.select(location).append("svg")
|
|
.attr("width", width)
|
|
.attr("height", height)
|
|
.append("g")
|
|
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
|
|
|
|
data.forEach(function(d) {
|
|
d.score = d.solved * 100 / d.total;
|
|
d.width = d.tries + 0.5;
|
|
});
|
|
|
|
var path = svg.selectAll(".solidArc")
|
|
.data(pie(data))
|
|
.enter().append("path")
|
|
.attr("fill", function(d) { return color(d.data.tip); })
|
|
.attr("class", "solidArc")
|
|
.attr("stroke", "gray")
|
|
.attr("d", arc);
|
|
|
|
var outerPath = svg.selectAll(".outlineArc")
|
|
.data(pie(data))
|
|
.enter().append("path")
|
|
.attr("fill", "none")
|
|
.attr("stroke", "gray")
|
|
.attr("class", "outlineArc")
|
|
.attr("d", outlineArc);
|
|
|
|
svg.selectAll(".label2Arc")
|
|
.data(pie(data))
|
|
.enter().append("text")
|
|
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
|
|
.attr("dy", ".35em")
|
|
.attr("text-anchor", "middle")
|
|
.text(function(d) { return d.data.solved; });
|
|
|
|
var labelArc = d3.svg.arc()
|
|
.outerRadius(0.8 * radius)
|
|
.innerRadius(0.8 * radius);
|
|
|
|
svg.selectAll(".labelArc")
|
|
.data(pie(data))
|
|
.enter().append("text")
|
|
.attr("transform", function(d) { return "translate(" + labelArc.centroid(d) + ")"; })
|
|
.attr("dy", ".35em")
|
|
.attr("text-anchor", "middle")
|
|
.text(function(d) { return d.data.tip + ": " + d.data.tries; });
|
|
}
|
|
|
|
function presenceCal(scope, location, data) {
|
|
var width = d3.select(location).node().getBoundingClientRect().width,
|
|
height = 80,
|
|
cellSize = 17; // cell size
|
|
|
|
var percent = d3.format(".1%"),
|
|
format = d3.time.format("%H:%M");
|
|
|
|
var color = d3.scale.quantize()
|
|
.domain([0, 16])
|
|
.range(d3.range(8).map(function(d) { return "q" + d + "-8"; }));
|
|
|
|
var svg = d3.select(location).selectAll("svg")
|
|
.data(d3.range(scope.settings.start, scope.time.start + (scope.settings.start % 86400000 + scope.settings.end - scope.settings.start), 86400000).map(function(t) { return new Date(t); }))
|
|
.enter().append("svg")
|
|
.attr("width", width)
|
|
.attr("height", height)
|
|
.attr("class", "RdYlGn")
|
|
.append("g")
|
|
.attr("transform", "translate(" + ((width - cellSize * 24) / 2) + "," + (height - cellSize * 4 - 1) + ")");
|
|
|
|
svg.append("text")
|
|
.attr("transform", "translate(-6," + cellSize * 2.6 + ")rotate(-90)")
|
|
.style("text-anchor", "middle")
|
|
.text(function(d) { return d.getDate() + "-" + (d.getMonth() + 1); });
|
|
|
|
var rect = svg.selectAll(".quarter")
|
|
.data(function(d) { return d3.time.minutes(new Date(d.getFullYear(), d.getMonth(), d.getDate(), 0), new Date(d.getFullYear(), d.getMonth(), d.getDate(), 23), 15); })
|
|
.enter().append("rect")
|
|
.attr("width", cellSize)
|
|
.attr("height", cellSize)
|
|
.attr("transform", function(d) { return "translate(" + (d.getHours() * cellSize) + "," + (d.getMinutes() / 15 * cellSize) + ")"; })
|
|
.attr("class", function(d) { if (d >= scope.settings.start && d < scope.settings.start + scope.settings.end - scope.settings.start) return color(data.reduce(function(prev, cur){
|
|
cur = new Date(cur).getTime();
|
|
dv = d.getTime();
|
|
return prev + ((dv <= cur && cur < dv+15*60000)?1:0);
|
|
}, 0)); });
|
|
}
|