+
-
diff --git a/admin/sync/README.md b/admin/sync/README.md
index 846cf8e9..428a9e6b 100644
--- a/admin/sync/README.md
+++ b/admin/sync/README.md
@@ -11,6 +11,7 @@ Tous les textes doivent utiliser l'encodage UTF8.
+ `statement.txt` contenant le scénario du challenge, tel qu'il sera affiché sur le site, à destination des participants
+ `challenge.txt` définitions des paramètres de votre challenge (au format [toml](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md)) :
- `gain = 42` : nombre de points que rapporte cet exercice ;
+ - `tags = ["Android", "RAT", "ROM"]` : mots-clefs de l'exercice ;
- `[[depend]]` : dépendance à un autre exercice :
* `id = CHID` : identifiant du challenge ;
* `theme = "NomDuTheme"` : (facultatif) nom du thème dans lequel aller chercher l'identifiant (par défaut, on prend le thème courant) ;
diff --git a/admin/sync/exercice_defines.go b/admin/sync/exercice_defines.go
index 8947cf5f..641984e5 100644
--- a/admin/sync/exercice_defines.go
+++ b/admin/sync/exercice_defines.go
@@ -69,6 +69,7 @@ type ExerciceFlagUCQ struct {
// ExerciceParams contains values parsed from defines.txt.
type ExerciceParams struct {
Gain int64
+ Tags []string
Hints []ExerciceHintParams `toml:"hint"`
Dependencies []ExerciceDependency `toml:"depend"`
Flags []ExerciceFlag `toml:"flag"`
diff --git a/admin/sync/exercices.go b/admin/sync/exercices.go
index 0e6b5258..b32873c8 100644
--- a/admin/sync/exercices.go
+++ b/admin/sync/exercices.go
@@ -69,6 +69,7 @@ func SyncExercices(i Importer, theme fic.Theme) []string {
// Handle score gain
var gain int64
var depend *fic.Exercice
+ var tags []string
if p, err := parseExerciceParams(i, path.Join(theme.Path, edir)); err != nil {
errs = append(errs, fmt.Sprintf("%q: challenge.txt: %s", edir, err))
continue
@@ -76,6 +77,7 @@ func SyncExercices(i Importer, theme fic.Theme) []string {
errs = append(errs, fmt.Sprintf("%q: challenge.txt: Undefined gain for challenge", edir))
} else {
gain = p.Gain
+ tags = p.Tags
// Handle dependency
if len(p.Dependencies) > 0 {
@@ -110,12 +112,11 @@ func SyncExercices(i Importer, theme fic.Theme) []string {
statement = string(blackfriday.Run([]byte(statement)))
overview = string(blackfriday.Run([]byte(overview)))
- if e, err := theme.GetExerciceByTitle(ename); err != nil {
- if ex, err := theme.AddExercice(ename, fic.ToURLid(ename), path.Join(theme.Path, edir), statement, overview, depend, gain, videoURI); err != nil {
+ e, err := theme.GetExerciceByTitle(ename)
+ if err != nil {
+ if e, err = theme.AddExercice(ename, fic.ToURLid(ename), path.Join(theme.Path, edir), statement, overview, depend, gain, videoURI); err != nil {
errs = append(errs, fmt.Sprintf("%q: error on exercice add: %s", edir, err))
continue
- } else {
- dmap[int64(eid)] = ex
}
} else if e.Title != ename || e.URLId == "" || e.Statement != statement || e.Overview != overview || e.Gain != gain || e.VideoURI != videoURI {
e.Title = ename
@@ -127,11 +128,18 @@ func SyncExercices(i Importer, theme fic.Theme) []string {
if _, err := e.Update(); err != nil {
errs = append(errs, fmt.Sprintf("%q: error on exercice update: %s", edir, err))
continue
- } else {
- dmap[int64(eid)] = e
}
- } else {
- dmap[int64(eid)] = e
+ }
+ dmap[int64(eid)] = e
+
+ if _, err := e.WipeTags(); err != nil {
+ errs = append(errs, fmt.Sprintf("%q: Unable to wipe tags: %s", edir, err))
+ }
+ for _, tag := range tags {
+ if _, err := e.AddTag(tag); err != nil {
+ errs = append(errs, fmt.Sprintf("%q: Unable to add tag: %s", edir, err))
+ continue
+ }
}
}
diff --git a/libfic/db.go b/libfic/db.go
index 0fbd4fa4..31d66816 100644
--- a/libfic/db.go
+++ b/libfic/db.go
@@ -246,6 +246,15 @@ CREATE TABLE IF NOT EXISTS exercice_tries(
FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice),
FOREIGN KEY(id_team) REFERENCES teams(id_team)
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
+`); err != nil {
+ return err
+ }
+ if _, err := db.Exec(`
+CREATE TABLE IF NOT EXISTS exercice_tags(
+ id_exercice INTEGER NOT NULL,
+ tag VARCHAR(255) NOT NULL,
+ FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice)
+) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
`); err != nil {
return err
}
diff --git a/libfic/exercice_tag.go b/libfic/exercice_tag.go
new file mode 100644
index 00000000..22c4088b
--- /dev/null
+++ b/libfic/exercice_tag.go
@@ -0,0 +1,52 @@
+package fic
+
+// GetTags returns tags associated with this exercice.
+func (e Exercice) GetTags() (tags []string, err error) {
+ if rows, errr := DBQuery("SELECT tag FROM exercice_tags WHERE id_exercice = ?", e.Id); errr != nil {
+ return nil, errr
+ } else {
+ defer rows.Close()
+
+ tags = make([]string, 0)
+ for rows.Next() {
+ var t string
+ if err = rows.Scan(&t); err != nil {
+ return
+ }
+ tags = append(tags, t)
+ }
+ err = rows.Err()
+ return
+ }
+}
+
+// AddTag assign a new tag to the exercice and registers it into the database.
+func (e Exercice) AddTag(tag string) (string, error) {
+ if _, err := DBExec("INSERT INTO exercice_tags (id_exercice, tag) VALUES (?, ?)", e.Id, tag); err != nil {
+ return "", err
+ } else {
+ return tag, nil
+ }
+}
+
+// DeleteTag delete a tag assigned to the current exercice from the database.
+func (e Exercice) DeleteTag(tag string) (int64, error) {
+ if res, err := DBExec("DELETE FROM exercice_tags WHERE id_exercice = ? AND tag = ?", e.Id, tag); err != nil {
+ return 0, err
+ } else if nb, err := res.RowsAffected(); err != nil {
+ return 0, err
+ } else {
+ return nb, err
+ }
+}
+
+// WipeTags delete all tag assigned to the current exercice from the database.
+func (e Exercice) WipeTags() (int64, error) {
+ if res, err := DBExec("DELETE FROM exercice_tags WHERE id_exercice = ?", e.Id); err != nil {
+ return 0, err
+ } else if nb, err := res.RowsAffected(); err != nil {
+ return 0, err
+ } else {
+ return nb, err
+ }
+}
diff --git a/libfic/reset.go b/libfic/reset.go
index 0c0f03b5..a738e077 100644
--- a/libfic/reset.go
+++ b/libfic/reset.go
@@ -34,7 +34,7 @@ func ResetGame() (error) {
// ResetExercices wipes out all challenges (both attempts and statements).
func ResetExercices() (error) {
- return truncateTable("team_hints", "exercice_files_deps", "exercice_files", "flag_found", "exercice_flags", "exercice_solved", "exercice_tries", "exercice_hints", "mcq_found", "mcq_entries", "exercice_mcq", "exercices", "themes")
+ return truncateTable("team_hints", "exercice_files_deps", "exercice_files", "flag_found", "exercice_flags", "exercice_solved", "exercice_tries", "exercice_hints", "mcq_found", "mcq_entries", "exercice_mcq", "exercice_tags", "exercices", "themes")
}
// ResetTeams wipes out all teams, incluings members and attempts.
diff --git a/libfic/theme_export.go b/libfic/theme_export.go
index 62fea57f..c691e883 100644
--- a/libfic/theme_export.go
+++ b/libfic/theme_export.go
@@ -6,12 +6,13 @@ import (
// exportedExercice is a structure representing a challenge, as exposed to players.
type exportedExercice struct {
- Title string `json:"title"`
- URLId string `json:"urlid"`
- Gain int64 `json:"gain"`
- Coeff float64 `json:"curcoeff"`
- Solved int64 `json:"solved"`
- Tried int64 `json:"tried"`
+ Title string `json:"title"`
+ URLId string `json:"urlid"`
+ Tags []string `json:"tags"`
+ Gain int64 `json:"gain"`
+ Coeff float64 `json:"curcoeff"`
+ Solved int64 `json:"solved"`
+ Tried int64 `json:"tried"`
}
// exportedTheme is a structure representing a Theme, as exposed to players.
@@ -35,9 +36,11 @@ func ExportThemes() (interface{}, error) {
} else {
exos := map[string]exportedExercice{}
for _, exercice := range exercices {
+ tags, _ := exercice.GetTags()
exos[fmt.Sprintf("%d", exercice.Id)] = exportedExercice{
exercice.Title,
exercice.URLId,
+ tags,
exercice.Gain,
exercice.Coefficient,
exercice.SolvedCount(),