diff --git a/admin/sync/README.md b/admin/sync/README.md index 50e767fe..cc598c99 100644 --- a/admin/sync/README.md +++ b/admin/sync/README.md @@ -20,7 +20,7 @@ Tous les textes doivent utiliser l'encodage UTF8. - `[[flag]]` : drapeau classique à valider pour résoudre le challenge : * `id = 42` : (facultatif) identifiant du flag au sein de l'exercice, pour définir des dépendances ; * `label = "Intitulé"` : (facultatif, par défaut : `Flag`) intitulé du drapeau ; - * `raw = 'MieH2athxuPhai6u'` : drapeau exact à trouver ; + * `raw = 'MieH2athxuPhai6u'` ou `raw = ['part1', 'part2']` : drapeau exact à trouver ; sous forme de tableau, le participant n'aura pas connaissaance du nombre d'éléments ; * `validator_regexp = "^(?:sudo +)?(.*)$"` : (facultatif) expression rationnelle dont les groupes capturés serviront comme chaîne à valider (notez que `?:` au début d'un groupe ne le capturera pas) ; * `ignorecase = true` : (facultatif, par défaut : `false`) ignore la case de ce drapeau ; * `help = "Indication"` : (facultatif) chaîne de caractères placée sous le champ du formulaire, idéale pour donner une indication de format ; diff --git a/admin/sync/exercice_defines.go b/admin/sync/exercice_defines.go index ce031da6..006bc5d6 100644 --- a/admin/sync/exercice_defines.go +++ b/admin/sync/exercice_defines.go @@ -29,7 +29,8 @@ type ExerciceUnlockFile struct { type ExerciceFlag struct { Id int64 Label string `toml:",omitempty"` - Raw string + Raw interface{} + Separator string `toml:",omitempty"` IgnoreCase bool `toml:",omitempty"` ValidatorRe string `toml:"validator_regexp,omitempty"` Help string `toml:",omitempty"` diff --git a/admin/sync/exercice_keys.go b/admin/sync/exercice_keys.go index 4501dab6..8458489c 100644 --- a/admin/sync/exercice_keys.go +++ b/admin/sync/exercice_keys.go @@ -4,6 +4,7 @@ import ( "fmt" "math/rand" "path" + "strings" "unicode" "srs.epita.fr/fic-server/libfic" @@ -43,16 +44,61 @@ func SyncExerciceFlags(i Importer, exercice fic.Exercice) (errs []string) { kmap := map[int64]fic.Flag{} // Import normal flags + flags: for nline, flag := range params.Flags { if len(flag.Label) == 0 { flag.Label = "Flag" } - if !isFullGraphic(flag.Raw) { + if flag.Label[0] == '`' { + errs = append(errs, fmt.Sprintf("%q: flag #%d: Label should not begin with `.", path.Base(exercice.Path), nline + 1)) + flag.Label = flag.Label[1:] + } + + // Concatenate array + var raw string + if f, ok := flag.Raw.([]interface{}); ok { + if len(flag.ValidatorRe) > 0 { + errs = append(errs, fmt.Sprintf("%q: flag #%d: ValidatorRe cannot be defined for this kind of flag.", path.Base(exercice.Path), nline + 1)) + flag.ValidatorRe = "" + } + + if len(flag.Separator) == 0 { + flag.Separator = "," + } else if len(flag.Separator) > 1 { + flag.Separator = string(flag.Separator[0]) + errs = append(errs, fmt.Sprintf("%q: flag #%d: separator truncated to %q", path.Base(exercice.Path), nline + 1, flag.Separator)) + } + + for i, v := range f { + if g, ok := v.(string); ok { + if strings.Index(g, flag.Separator) != -1 { + errs = append(errs, fmt.Sprintf("%q: flag #%d: flag items cannot contain %q character as it is used as separator. Change the separator attribute for this flag.", path.Base(exercice.Path), nline + 1, flag.Separator)) + continue flags + } else { + raw += g + flag.Separator + } + } else { + errs = append(errs, fmt.Sprintf("%q: flag #%d, item %d has an invalid type: can only be string, is %T.", path.Base(exercice.Path), nline + 1, i, v)) + continue flags + } + } + + flag.Label = "`" + flag.Separator + flag.Label + } else if f, ok := flag.Raw.(int64); ok { + raw = fmt.Sprintf("%d", f) + } else if f, ok := flag.Raw.(string); !ok { + errs = append(errs, fmt.Sprintf("%q: flag #%d has an invalid type: can only be []string or string, not %T", path.Base(exercice.Path), nline + 1, flag.Raw)) + continue + } else { + raw = f + } + + if !isFullGraphic(raw) { errs = append(errs, fmt.Sprintf("%q: WARNING flag #%d: non-printable characters in flag, is this really expected?", path.Base(exercice.Path), nline + 1)) } - if k, err := exercice.AddRawFlag(flag.Label, flag.Help, flag.IgnoreCase, validatorRegexp(flag.ValidatorRe), []byte(flag.Raw), 0); err != nil { + if k, err := exercice.AddRawFlag(flag.Label, flag.Help, flag.IgnoreCase, validatorRegexp(flag.ValidatorRe), []byte(raw), 0); err != nil { errs = append(errs, fmt.Sprintf("%q: error flag #%d: %s", path.Base(exercice.Path), nline + 1, err)) continue } else { diff --git a/frontend/static/js/challenge.js b/frontend/static/js/challenge.js index 0dff9a73..7affdcde 100644 --- a/frontend/static/js/challenge.js +++ b/frontend/static/js/challenge.js @@ -171,6 +171,10 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) angular.forEach(exercice.flags, function(flag, fid) { if ($scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].flags[fid] && $scope.my.exercices[eid].flags[fid].value !== undefined) data.exercices[eid].flags[fid].value = $scope.my.exercices[eid].flags[fid].value; + if ($scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].flags[fid] && $scope.my.exercices[eid].flags[fid].values !== undefined) + data.exercices[eid].flags[fid].values = $scope.my.exercices[eid].flags[fid].values; + else + data.exercices[eid].flags[fid].values = [""]; }); }); angular.forEach(data.exercices, function(exercice, eid) { @@ -270,6 +274,10 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) var cbs; var cbd; + $scope.additem = function(key) { + key.values.push(""); + }; + $scope.ssubmit = function() { var resp = {} var check = undefined @@ -279,6 +287,22 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) { resp["flags"] = {}; angular.forEach($scope.my.exercices[$rootScope.current_exercice].flags, function(flag,kid) { + if (flag.values !== undefined) { + if (flag.separator) { + for (var i = flag.values.length - 1; i >= 0; i--) { + if (!flag.values[i].length) + flag.values.splice(i, 1); + } + + flag.value = flag.values.join(flag.separator) + flag.separator; + + if (flag.values.length == 0) + flag.values = [""]; + } + else + flag.value = flag.values[0]; + } + if (flag.found == null) { if (flag.soluce !== undefined) { if (check === undefined) check = true; diff --git a/frontend/static/views/defi.html b/frontend/static/views/defi.html index 2f98682a..5db7b3bd 100644 --- a/frontend/static/views/defi.html +++ b/frontend/static/views/defi.html @@ -88,15 +88,20 @@
-
- - +
+ +
+
+ +
diff --git a/libfic/team_my.go b/libfic/team_my.go index bddc8417..30382c25 100644 --- a/libfic/team_my.go +++ b/libfic/team_my.go @@ -39,6 +39,7 @@ type myTeamMCQ struct { type myTeamFlag struct { Label string `json:"label"` Help string `json:"help,omitempty"` + Separator string `json:"separator,omitempty"` Solved *time.Time `json:"found,omitempty"` Soluce string `json:"soluce,omitempty"` Choices map[string]string `json:"choices,omitempty"` @@ -194,7 +195,12 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) { flag.Solved = t.HasPartiallySolved(k) } - flag.Label = k.Label + if k.Label[0] == '`' && len(k.Label) > 2 { + flag.Label = k.Label[2:] + flag.Separator = string(k.Label[1]) + } else { + flag.Label = k.Label + } if flag.Solved == nil { flag.Help = k.Help