From 21697f01ca696c3f21cf1e8ab0b3ddcb6b84e2f9 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sun, 2 Dec 2018 17:53:26 +0100 Subject: [PATCH] New field for exercice to display a text after exercice validation --- admin/api/exercice.go | 6 +++++- admin/static/js/app.js | 2 +- admin/static/views/exercice.html | 4 ++-- admin/sync/README.md | 1 + admin/sync/exercices.go | 15 +++++++++++++-- frontend/static/views/defi.html | 3 +++ libfic/db.go | 1 + libfic/exercice.go | 26 ++++++++++++++------------ libfic/team_my.go | 3 +++ 9 files changed, 43 insertions(+), 18 deletions(-) diff --git a/admin/api/exercice.go b/admin/api/exercice.go index 039cc1df..580693ec 100644 --- a/admin/api/exercice.go +++ b/admin/api/exercice.go @@ -148,6 +148,10 @@ func partUpdateExercice(exercice fic.Exercice, body []byte) (interface{}, error) exercice.Headline = ue.Headline } + if len(ue.Finished) > 0 { + exercice.Finished = ue.Finished + } + if len(ue.Overview) > 0 { exercice.Overview = ue.Overview } @@ -203,7 +207,7 @@ func createExercice(theme fic.Theme, body []byte) (interface{}, error) { } } - return theme.AddExercice(ue.Title, ue.URLId, ue.Path, ue.Statement, ue.Overview, ue.Headline, depend, ue.Gain, ue.VideoURI) + return theme.AddExercice(ue.Title, ue.URLId, ue.Path, ue.Statement, ue.Overview, ue.Headline, depend, ue.Gain, ue.VideoURI, ue.Finished) } type uploadedHint struct { diff --git a/admin/static/js/app.js b/admin/static/js/app.js index 0b2c4be0..5656ec6d 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -1044,7 +1044,7 @@ angular.module("FICApp") $scope.exercice = Exercice.get({ exerciceId: $routeParams.exerciceId }); } $scope.exercices = Exercice.query(); - $scope.fields = ["title", "urlid", "statement", "headline", "overview", "depend", "gain", "coefficient", "videoURI", "issue", "issuekind"]; + $scope.fields = ["title", "urlid", "statement", "headline", "overview", "finished", "depend", "gain", "coefficient", "videoURI", "issue", "issuekind"]; $scope.showTags = false; $scope.toggleTags = function(val) { diff --git a/admin/static/views/exercice.html b/admin/static/views/exercice.html index 563fc564..67221231 100644 --- a/admin/static/views/exercice.html +++ b/admin/static/views/exercice.html @@ -6,10 +6,10 @@
- + - + diff --git a/admin/sync/README.md b/admin/sync/README.md index d56c3647..9d34debf 100644 --- a/admin/sync/README.md +++ b/admin/sync/README.md @@ -10,6 +10,7 @@ Tous les textes doivent utiliser l'encodage UTF8. * Un dossier par challenge : `CHID-Titre du challenge` (avec `CHID` l'identifiant numérique du challenge, typiquement son numéro d'ordre), contenant : + `overview.txt` une présentation rapide du challenge (~1-2 phrases d'accroche), compréhensible par un décideur, petit schéma à l'appui si besoin + `statement.txt` contenant le scénario du challenge, tel qu'il sera affiché sur le site, à destination des participants + + `finished.txt` (facultatif) contenant un texte affiché au participant ayant validé l'exercice : par exemple pour donner plus d'informations sur les vulnérabilités rencontrées + `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 ; diff --git a/admin/sync/exercices.go b/admin/sync/exercices.go index d840336a..f0ff4ea1 100644 --- a/admin/sync/exercices.go +++ b/admin/sync/exercices.go @@ -71,6 +71,15 @@ func SyncExercices(i Importer, theme fic.Theme) []string { continue } + var finished string + if i.exists(path.Join(theme.Path, edir, "finished.txt")) { + finished, err = getFileContent(i, path.Join(theme.Path, edir, "finished.txt")) + if err != nil { + errs = append(errs, fmt.Sprintf("%q: statement.txt: %s", edir, err)) + continue + } + } + // Handle score gain var gain int64 var depend *fic.Exercice @@ -117,19 +126,21 @@ func SyncExercices(i Importer, theme fic.Theme) []string { statement = ProcessMarkdown(i, statement, edir) overview = ProcessMarkdown(i, overview, edir) headline = string(blackfriday.Run([]byte(headline))) + finished = ProcessMarkdown(i, finished, edir) e, err := theme.GetExerciceByTitle(ename) if err != nil { - if e, err = theme.AddExercice(ename, fic.ToURLid(ename), path.Join(theme.Path, edir), statement, overview, headline, depend, gain, videoURI); err != nil { + if e, err = theme.AddExercice(ename, fic.ToURLid(ename), path.Join(theme.Path, edir), statement, overview, headline, depend, gain, videoURI, finished); err != nil { errs = append(errs, fmt.Sprintf("%q: error on exercice add: %s", edir, err)) continue } - } else if e.Title != ename || e.URLId == "" || e.Statement != statement || e.Overview != overview || e.Headline != headline || e.Gain != gain || e.VideoURI != videoURI { + } else if e.Title != ename || e.URLId == "" || e.Statement != statement || e.Overview != overview || e.Headline != headline || e.Gain != gain || e.VideoURI != videoURI || e.Finished != finished { e.Title = ename e.URLId = fic.ToURLid(ename) e.Statement = statement e.Overview = overview e.Headline = headline + e.Finished = finished e.Gain = gain e.VideoURI = videoURI if _, err := e.Update(); err != nil { diff --git a/frontend/static/views/defi.html b/frontend/static/views/defi.html index 7ac608c7..9f1499d0 100644 --- a/frontend/static/views/defi.html +++ b/frontend/static/views/defi.html @@ -121,6 +121,9 @@

Vous êtes la {{ my.exercices[current_exercice].solved_rank }} équipe à avoir résolu ce challenge à {{ my.exercices[current_exercice].solved_time | date:"mediumTime" }}. Vous avez marqué !

+
+

+
Passer au challenge suivant
diff --git a/libfic/db.go b/libfic/db.go index 9692a829..67ae5d28 100644 --- a/libfic/db.go +++ b/libfic/db.go @@ -128,6 +128,7 @@ CREATE TABLE IF NOT EXISTS exercices( gain INTEGER NOT NULL, coefficient_cur FLOAT NOT NULL DEFAULT 1.0, video_uri VARCHAR(255) NOT NULL, + finished TEXT NOT NULL, FOREIGN KEY(id_theme) REFERENCES themes(id_theme), FOREIGN KEY(depend) REFERENCES exercices(id_exercice) ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; diff --git a/libfic/exercice.go b/libfic/exercice.go index 20bae160..fcafada7 100644 --- a/libfic/exercice.go +++ b/libfic/exercice.go @@ -28,6 +28,8 @@ type Exercice struct { Overview string `json:"overview"` // Headline is the challenge headline to fill in small part Headline string `json:"headline"` + // Finished is the text shown when the exercice is solved + Finished string `json:"finished"` // Issue is an optional text describing an issue with the exercice Issue string `json:"issue"` // IssueKind is the criticity level of the previous issue @@ -47,7 +49,7 @@ type Exercice struct { // GetExercice retrieves the challenge with the given id. func GetExercice(id int64) (Exercice, error) { var e Exercice - if err := DBQueryRow("SELECT id_exercice, title, url_id, path, statement, overview, headline, issue, issue_kind, depend, gain, coefficient_cur, video_uri FROM exercices WHERE id_exercice = ?", id).Scan(&e.Id, &e.Title, &e.URLId, &e.Path, &e.Statement, &e.Overview, &e.Headline, &e.Issue, &e.IssueKind, &e.Depend, &e.Gain, &e.Coefficient, &e.VideoURI); err != nil { + if err := DBQueryRow("SELECT id_exercice, title, url_id, path, statement, overview, headline, issue, issue_kind, depend, gain, coefficient_cur, video_uri, finished FROM exercices WHERE id_exercice = ?", id).Scan(&e.Id, &e.Title, &e.URLId, &e.Path, &e.Statement, &e.Overview, &e.Headline, &e.Issue, &e.IssueKind, &e.Depend, &e.Gain, &e.Coefficient, &e.VideoURI, &e.Finished); err != nil { return Exercice{}, err } @@ -57,7 +59,7 @@ func GetExercice(id int64) (Exercice, error) { // GetExercice retrieves the challenge with the given id. func (t Theme) GetExercice(id int) (Exercice, error) { var e Exercice - if err := DBQueryRow("SELECT id_exercice, title, url_id, path, statement, overview, headline, issue, issue_kind, depend, gain, coefficient_cur, video_uri FROM exercices WHERE id_theme = ? AND id_exercice = ?", t.Id, id).Scan(&e.Id, &e.Title, &e.URLId, &e.Path, &e.Statement, &e.Overview, &e.Headline, &e.Issue, &e.IssueKind, &e.Depend, &e.Gain, &e.Coefficient, &e.VideoURI); err != nil { + if err := DBQueryRow("SELECT id_exercice, title, url_id, path, statement, overview, headline, issue, issue_kind, depend, gain, coefficient_cur, video_uri, finished FROM exercices WHERE id_theme = ? AND id_exercice = ?", t.Id, id).Scan(&e.Id, &e.Title, &e.URLId, &e.Path, &e.Statement, &e.Overview, &e.Headline, &e.Issue, &e.IssueKind, &e.Depend, &e.Gain, &e.Coefficient, &e.VideoURI, &e.Finished); err != nil { return Exercice{}, err } @@ -67,7 +69,7 @@ func (t Theme) GetExercice(id int) (Exercice, error) { // GetExerciceByTitle retrieves the challenge with the given title. func (t Theme) GetExerciceByTitle(title string) (Exercice, error) { var e Exercice - if err := DBQueryRow("SELECT id_exercice, title, url_id, path, statement, overview, headline, issue, issue_kind, depend, gain, coefficient_cur, video_uri FROM exercices WHERE id_theme = ? AND title = ?", t.Id, title).Scan(&e.Id, &e.Title, &t.URLId, &e.Path, &e.Statement, &e.Overview, &e.Headline, &e.Issue, &e.IssueKind, &e.Depend, &e.Gain, &e.Coefficient, &e.VideoURI); err != nil { + if err := DBQueryRow("SELECT id_exercice, title, url_id, path, statement, overview, headline, issue, issue_kind, depend, gain, coefficient_cur, video_uri, finished FROM exercices WHERE id_theme = ? AND title = ?", t.Id, title).Scan(&e.Id, &e.Title, &t.URLId, &e.Path, &e.Statement, &e.Overview, &e.Headline, &e.Issue, &e.IssueKind, &e.Depend, &e.Gain, &e.Coefficient, &e.VideoURI, &e.Finished); err != nil { return Exercice{}, err } @@ -76,7 +78,7 @@ func (t Theme) GetExerciceByTitle(title string) (Exercice, error) { // GetExercices returns the list of all challenges present in the database. func GetExercices() ([]Exercice, error) { - if rows, err := DBQuery("SELECT id_exercice, title, url_id, path, statement, overview, headline, issue, issue_kind, depend, gain, coefficient_cur, video_uri FROM exercices"); err != nil { + if rows, err := DBQuery("SELECT id_exercice, title, url_id, path, statement, overview, headline, issue, issue_kind, depend, gain, coefficient_cur, video_uri, finished FROM exercices"); err != nil { return nil, err } else { defer rows.Close() @@ -84,7 +86,7 @@ func GetExercices() ([]Exercice, error) { var exos = make([]Exercice, 0) for rows.Next() { var e Exercice - if err := rows.Scan(&e.Id, &e.Title, &e.URLId, &e.Path, &e.Statement, &e.Overview, &e.Headline, &e.Issue, &e.IssueKind, &e.Depend, &e.Gain, &e.Coefficient, &e.VideoURI); err != nil { + if err := rows.Scan(&e.Id, &e.Title, &e.URLId, &e.Path, &e.Statement, &e.Overview, &e.Headline, &e.Issue, &e.IssueKind, &e.Depend, &e.Gain, &e.Coefficient, &e.VideoURI, &e.Finished); err != nil { return nil, err } exos = append(exos, e) @@ -99,7 +101,7 @@ func GetExercices() ([]Exercice, error) { // GetExercices returns the list of all challenges in the Theme. func (t Theme) GetExercices() ([]Exercice, error) { - if rows, err := DBQuery("SELECT id_exercice, title, url_id, path, statement, overview, headline, issue, issue_kind, depend, gain, coefficient_cur, video_uri FROM exercices WHERE id_theme = ?", t.Id); err != nil { + if rows, err := DBQuery("SELECT id_exercice, title, url_id, path, statement, overview, headline, issue, issue_kind, depend, gain, coefficient_cur, video_uri, finished FROM exercices WHERE id_theme = ?", t.Id); err != nil { return nil, err } else { defer rows.Close() @@ -107,7 +109,7 @@ func (t Theme) GetExercices() ([]Exercice, error) { var exos = make([]Exercice, 0) for rows.Next() { var e Exercice - if err := rows.Scan(&e.Id, &e.Title, &e.URLId, &e.Path, &e.Statement, &e.Overview, &e.Headline, &e.Issue, &e.IssueKind, &e.Depend, &e.Gain, &e.Coefficient, &e.VideoURI); err != nil { + if err := rows.Scan(&e.Id, &e.Title, &e.URLId, &e.Path, &e.Statement, &e.Overview, &e.Headline, &e.Issue, &e.IssueKind, &e.Depend, &e.Gain, &e.Coefficient, &e.VideoURI, &e.Finished); err != nil { return nil, err } exos = append(exos, e) @@ -121,29 +123,29 @@ func (t Theme) GetExercices() ([]Exercice, error) { } // AddExercice creates and fills a new struct Exercice and registers it into the database. -func (t Theme) AddExercice(title string, urlId string, path string, statement string, overview string, headline string, depend *Exercice, gain int64, videoURI string) (Exercice, error) { +func (t Theme) AddExercice(title string, urlId string, path string, statement string, overview string, headline string, depend *Exercice, gain int64, videoURI string, finished string) (Exercice, error) { var dpd interface{} if depend == nil { dpd = nil } else { dpd = depend.Id } - if res, err := DBExec("INSERT INTO exercices (id_theme, title, url_id, path, statement, overview, headline, issue, depend, gain, video_uri) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", t.Id, title, urlId, path, statement, overview, headline, "", dpd, gain, videoURI); err != nil { + if res, err := DBExec("INSERT INTO exercices (id_theme, title, url_id, path, statement, overview, headline, issue, depend, gain, video_uri, finished) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", t.Id, title, urlId, path, statement, overview, headline, "", dpd, gain, videoURI, finished); err != nil { return Exercice{}, err } else if eid, err := res.LastInsertId(); err != nil { return Exercice{}, err } else { if depend == nil { - return Exercice{eid, title, urlId, path, statement, overview, headline, "", "info", nil, gain, 1.0, videoURI}, nil + return Exercice{eid, title, urlId, path, statement, overview, headline, finished, "", "info", nil, gain, 1.0, videoURI}, nil } else { - return Exercice{eid, title, urlId, path, statement, overview, headline, "", "info", &depend.Id, gain, 1.0, videoURI}, nil + return Exercice{eid, title, urlId, path, statement, overview, headline, finished, "", "info", &depend.Id, gain, 1.0, videoURI}, nil } } } // Update applies modifications back to the database. func (e Exercice) Update() (int64, error) { - if res, err := DBExec("UPDATE exercices SET title = ?, url_id = ?, path = ?, statement = ?, overview = ?, headline = ?, issue = ?, issue_kind = ?, depend = ?, gain = ?, coefficient_cur = ?, video_uri = ? WHERE id_exercice = ?", e.Title, e.URLId, e.Path, e.Statement, e.Overview, e.Headline, e.Issue, e.IssueKind, e.Depend, e.Gain, e.Coefficient, e.VideoURI, e.Id); err != nil { + if res, err := DBExec("UPDATE exercices SET title = ?, url_id = ?, path = ?, statement = ?, overview = ?, headline = ?, issue = ?, issue_kind = ?, depend = ?, gain = ?, coefficient_cur = ?, video_uri = ?, finished = ? WHERE id_exercice = ?", e.Title, e.URLId, e.Path, e.Statement, e.Overview, e.Headline, e.Issue, e.IssueKind, e.Depend, e.Gain, e.Coefficient, e.VideoURI, e.Finished, e.Id); err != nil { return 0, err } else if nb, err := res.RowsAffected(); err != nil { return 0, err diff --git a/libfic/team_my.go b/libfic/team_my.go index 300d9bdf..d77bf167 100644 --- a/libfic/team_my.go +++ b/libfic/team_my.go @@ -47,6 +47,7 @@ type myTeamExercice struct { ThemeId int64 `json:"theme_id"` Statement string `json:"statement"` Overview string `json:"overview,omitempty"` + Finished string `json:"finished,omitempty"` Hints []myTeamHint `json:"hints,omitempty"` Gain int `json:"gain"` Files []myTeamFile `json:"files,omitempty"` @@ -106,6 +107,7 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) { if t == nil { exercice.Overview = strings.Replace(e.Overview, "$FILES$", FilesDir, -1) + exercice.Finished = strings.Replace(e.Finished, "$FILES$", FilesDir, -1) exercice.VideoURI = e.VideoURI exercice.Tries = e.TriedCount() exercice.Gain = int(float64(e.Gain) * e.Coefficient) @@ -115,6 +117,7 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) { exercice.SolvedRank, _ = t.GetSolvedRank(e) if solved { + exercice.Finished = e.Finished exercice.Tries, _ = t.CountTries(e) } else { exercice.Tries, stime = t.CountTries(e)