diff --git a/admin/static/js/app.js b/admin/static/js/app.js
index fd3912fd..83f356d0 100644
--- a/admin/static/js/app.js
+++ b/admin/static/js/app.js
@@ -1,180 +1,180 @@
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);
- });
+ .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;
+ 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=/";
+ 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;
+ 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();
- });
- }
- }
- }])
+ .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: `
+ .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: `
`
- });
-
-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).toString(16),
- g = ((num & 0xFF00) >>> 8).toString(16),
- r = ((num & 0xFF0000) >>> 16).toString(16),
- a = ( (num & 0xFF000000) >>> 24 ) / 255 ;
- if (r.length <= 1) r = "0" + r;
- if (g.length <= 1) g = "0" + g;
- if (b.length <= 1) b = "0" + b;
- return "#" + r + g + b;
- }
- })
- .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
- }
- })
+ .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' })
+ });
- .component('dependancy', {
- bindings: {
- dep: '=',
- deleteDep: '=',
- },
- controller: function() {},
- template: `
+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).toString(16),
+ g = ((num & 0xFF00) >>> 8).toString(16),
+ r = ((num & 0xFF0000) >>> 16).toString(16),
+ a = ((num & 0xFF000000) >>> 24) / 255;
+ if (r.length <= 1) r = "0" + r;
+ if (g.length <= 1) g = "0" + g;
+ if (b.length <= 1) b = "0" + b;
+ return "#" + r + g + b;
+ }
+ })
+ .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: `
Flag {{ $ctrl.dep.label }}
QCM {{ $ctrl.dep.title }}
`
- })
+ })
- .directive('color', function() {
- return {
- require: 'ngModel',
- link: function(scope, ele, attr, ctrl){
- ctrl.$formatters.unshift(function(num){
- num >>>= 0;
- var b = (num & 0xFF).toString(16),
- g = ((num & 0xFF00) >>> 8).toString(16),
- r = ((num & 0xFF0000) >>> 16).toString(16),
- a = ( (num & 0xFF000000) >>> 24 ) / 255 ;
- if (r.length <= 1) r = "0" + r;
- if (g.length <= 1) g = "0" + g;
- if (b.length <= 1) b = "0" + b;
- return "#" + r + g + b;
- });
- 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)
+ .directive('color', function () {
+ return {
+ require: 'ngModel',
+ link: function (scope, ele, attr, ctrl) {
+ ctrl.$formatters.unshift(function (num) {
+ num >>>= 0;
+ var b = (num & 0xFF).toString(16),
+ g = ((num & 0xFF00) >>> 8).toString(16),
+ r = ((num & 0xFF0000) >>> 16).toString(16),
+ a = ((num & 0xFF000000) >>> 24) / 255;
+ if (r.length <= 1) r = "0" + r;
+ if (g.length <= 1) g = "0" + g;
+ if (b.length <= 1) b = "0" + b;
+ return "#" + r + g + b;
+ });
+ 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);
- });
+ ) : 0;
+ });
}
- );
- }
- $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: `
{{ $ctrl.status.hash }} {{ $ctrl.status.text }}`
- })
-
- .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.', '
Voir le rapport.', null, null, 15000);
- }, function(response) {
- $scope.deepSyncInProgress = false;
- $scope.addToast('warning', 'Synchronisation intégrale terminée.', '
Voir le rapport.', 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.', '
Voir le rapport.', null, null, 15000);
- }, function(response) {
- $scope.deepSyncInProgress = false;
- $scope.addToast('warning', 'Synchronisation profinde rapide terminée.', '
Voir le rapport.', 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: '
', 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", "background_color", "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", "background_color", "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
+ .directive('integer', function () {
+ return {
+ require: 'ngModel',
+ link: function (scope, ele, attr, ctrl) {
+ ctrl.$parsers.unshift(function (viewValue) {
+ return parseInt(viewValue, 10);
+ });
+ }
+ };
})
- }
- $scope.choices.$promise.then(function(choices) {
- if ($scope.flag.choices_cost == 0 && choices.length > 0) {
- $scope.flag.wantchoices()
- }
- })
+ .directive('float', function () {
+ return {
+ require: 'ngModel',
+ link: function (scope, ele, attr, ctrl) {
+ ctrl.$parsers.unshift(function (viewValue) {
+ return parseFloat(viewValue, 10);
+ });
+ }
+ };
+ })
- $scope.addChoice = function() {
- $scope.choices.push(new ExerciceFlagChoices())
- }
+ .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);
- $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})
- }
+ $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,
+ });
+ }
- $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 })
- })
- }
- })
+ $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);
+ })
+ }
- .controller("ExerciceFlagDepsController", function($scope, $routeParams, ExerciceFlagDeps) {
- $scope.init = function(flag) {
- $scope.deps = ExerciceFlagDeps.query({ exerciceId: $routeParams.exerciceId, flagId: flag.id });
- }
- })
+ $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);
- .controller("ExerciceMCQFlagsController", function($scope, ExerciceMCQFlag, $routeParams, $rootScope) {
- $scope.quiz = ExerciceMCQFlag.query({ exerciceId: $routeParams.exerciceId });
+ $timeout(function () {
+ document.getElementById("circle2").classList.add("play");
+ }, 50);
+ $timeout(function () {
+ document.getElementById("circle2").classList.remove("play");
+ }, 750);
+ }
+ });
- $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++;
- }
+ $rootScope.logged = parseInt(getCookie("myassignee")) > 0;
+ })
- $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("VersionController", function ($scope, Version) {
+ $scope.v = Version.get();
+ })
- .controller("ExerciceMCQDepsController", function($scope, $routeParams, ExerciceMCQDeps) {
- $scope.init = function(flag) {
- $scope.deps = ExerciceMCQDeps.query({ exerciceId: $routeParams.exerciceId, mcqId: flag.id });
- }
- })
+ .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("TeamsListController", function($scope, $rootScope, Team, $location, $http) {
- $scope.teams = Team.query();
- $scope.fields = ["id", "name"];
+ .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); });
+ })
- $scope.order = [];
- $scope.teams.$promise.then(function(teams) {
- teams.forEach(function(team) {
- $scope.order.push(team.id);
- });
- });
- $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);
- }
- };
+ .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: `
{{ $ctrl.status.hash }} {{ $ctrl.status.text }}`
+ })
+
+ .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.', '
Voir le rapport.', null, null, 15000);
+ }, function (response) {
+ $scope.deepSyncInProgress = false;
+ $scope.addToast('warning', 'Synchronisation intégrale terminée.', '
Voir le rapport.', 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.', '
Voir le rapport.', null, null, 15000);
+ }, function (response) {
+ $scope.deepSyncInProgress = false;
+ $scope.addToast('warning', 'Synchronisation profinde rapide terminée.', '
Voir le rapport.', 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.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);
- });
- $http.post("api/vouch-proxy.yaml").then(function() {
- $scope.addToast('success', 'VouchProxy config refreshed.', "Don't forget to reload/reboot frontend host.");
- }, function(response) {
- $scope.addToast('danger', 'An error occurs when generating VouchProxy 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.certificates = Certificate.query();
+ $scope.ca = CACertificate.get();
- $scope.newMember = function() {
- $scope.members.push(new TeamMember());
- }
- $scope.saveTeamMembers = function() {
- if (this.team.id) {
- TeamMember.save({ teamId: this.team.id }, $scope.members);
+ $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.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.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.dissociateCertificate = function(certificate) {
- $http({
- url: "api/certs/" + certificate.id,
- method: "PUT",
- data: {
- id_team: null
+ $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: '
', 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();
+ }
}
- }).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.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;
+ }
+ });
+ };
+ })
- $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("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";
});
- }
- })
- .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) {
+ 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", "background_color", "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/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);
+ url: "api/tags",
+ method: "GET"
+ }).then(function (response) {
+ $scope.tags = response.data
});
- }
- }
- $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);
- });
+ .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.my.$promise.then(function(res){
- $scope.solved_exercices = 0;
- angular.forEach(res.exercices, function(exercice, eid) {
- if (exercice.solved_rank) {
- $scope.solved_exercices += 1;
+ $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
+ })
}
- }, 0);
- });
- })
+ $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');
+ }
- .controller("PresenceController", function($scope, TeamPresence, $routeParams) {
- $scope.presence = TeamPresence.query({ teamId: $routeParams.teamId });
- $scope.presence.$promise.then(function(res) {
- presenceCal($scope, "#presenceCal", res);
+ $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", "background_color", "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.order = [];
+ $scope.teams.$promise.then(function (teams) {
+ teams.forEach(function (team) {
+ $scope.order.push(team.id);
+ });
+ });
+ $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);
+ });
+ $http.post("api/vouch-proxy.yaml").then(function () {
+ $scope.addToast('success', 'VouchProxy config refreshed.', "Don't forget to reload/reboot frontend host.");
+ }, function (response) {
+ $scope.addToast('danger', 'An error occurs when generating VouchProxy 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 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 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 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 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 outlineArc = d3.svg.arc()
- .innerRadius(innerRadius)
- .outerRadius(radius);
+ 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 svg = d3.select(location).append("svg")
- .attr("width", width)
- .attr("height", height)
- .append("g")
- .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
+ var outerPath = svg.selectAll(".outlineArc")
+ .data(pie(data))
+ .enter().append("path")
+ .attr("fill", "none")
+ .attr("stroke", "gray")
+ .attr("class", "outlineArc")
+ .attr("d", outlineArc);
- data.forEach(function(d) {
- d.score = d.solved * 100 / d.total;
- d.width = d.tries + 1;
- });
+ var labelArc = d3.svg.arc()
+ .outerRadius(0.8 * radius)
+ .innerRadius(0.8 * radius);
- 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);
+ 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; });
- 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; });
+ 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 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 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 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 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 outlineArc = d3.svg.arc()
- .innerRadius(innerRadius)
- .outerRadius(radius);
+ 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 svg = d3.select(location).append("svg")
- .attr("width", width)
- .attr("height", height)
- .append("g")
- .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
+ var outerPath = svg.selectAll(".outlineArc")
+ .data(pie(data))
+ .enter().append("path")
+ .attr("fill", "none")
+ .attr("stroke", "gray")
+ .attr("class", "outlineArc")
+ .attr("d", outlineArc);
- data.forEach(function(d) {
- d.score = d.solved * 100 / d.total;
- d.width = d.tries + 0.5;
- });
+ 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 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 labelArc = d3.svg.arc()
+ .outerRadius(0.8 * radius)
+ .innerRadius(0.8 * radius);
- 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; });
+ 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 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 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 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) + ")");
+ 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); });
+ 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)); });
+ 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));
+ });
}