frontend: team registration
authornemunaire <nemunaire@nemunai.re>
Thu, 21 Dec 2017 21:18:18 +0000 (22:18 +0100)
committernemunaire <nemunaire@nemunai.re>
Thu, 18 Jan 2018 11:08:11 +0000 (12:08 +0100)
backend/main.go
backend/registration.go
frontend/main.go
frontend/register.go
frontend/static/index.html
frontend/static/js/challenge.js
frontend/static/views/register.html [new file with mode: 0644]
libfic/db.go
libfic/member.go
libfic/team.go

index b7b5322122ed8ea6e92e7c3b67f6f4575c54f85e..41a26cb50a8dbb63b03aacf7477dc06be035c66c 100644 (file)
@@ -138,7 +138,7 @@ func treat(raw_path string) {
 
        if len(spath) == 3 {
                if spath[1] == "_registration" {
-                       treatRegistration(raw_path)
+                       treatRegistration(raw_path, spath[2])
                } else if team, err := fic.GetTeamByInitialName(spath[1]); err != nil {
                        log.Println("[ERR]", err)
                } else if spath[2] == "name" {
index 7d318444f90f8c61d62f0369ee679d11dcd7e89b..fad3bf5996b33072881ae1eb56882c1808dc631b 100644 (file)
@@ -2,6 +2,7 @@ package main
 
 import (
        "encoding/json"
+       "fmt"
        "io/ioutil"
        "log"
        "math/rand"
@@ -11,23 +12,48 @@ import (
        "srs.epita.fr/fic-server/libfic"
 )
 
-func treatRegistration(pathname string) {
-       var keys map[string]string
+type uTeamRegistration struct {
+       TeamName string
+       Members  []fic.Member
+}
+
+func treatRegistration(pathname string, initial_name string) {
+       var nTeam uTeamRegistration
 
        if cnt_raw, err := ioutil.ReadFile(pathname); err != nil {
                log.Println("[ERR]", err)
-       } else if err := json.Unmarshal(cnt_raw, &keys); err != nil {
+       } else if err := json.Unmarshal(cnt_raw, &nTeam); err != nil {
                log.Println("[ERR]", err)
-       } else if validTeamName(keys["name"]) {
-               if team, err := fic.CreateTeam(keys["name"], rand.Uint32()); err != nil {
-                       log.Printf("[ERR] Unable to register new team %s: %s\n", keys["name"], err)
+       } else if validTeamName(nTeam.TeamName) {
+               if team, err := fic.RegisterTeam(initial_name, nTeam.TeamName, uint32(rand.Int31n(16581376))); err != nil {
+                       log.Printf("[ERR] Unable to register new team %s: %s\n", nTeam.TeamName, err)
                } else {
+                       for _, m := range nTeam.Members {
+                               // Force Id to 0, as it shouldn't have been defined yet
+                               m.Id = 0
+                               if err := team.GainMember(m); err != nil {
+                                       log.Println("[WRN] Unable to add member (", m, ") to team (", team, "): ", err)
+                               }
+                       }
+
+                       if err := os.Remove(pathname); err != nil {
+                               log.Println("[WRN]", err)
+                       }
+                       if _, err := fic.NewEvent(fmt.Sprintf("Souhaitons bonne chance à l'équipe <strong>%s</strong> qui vient de nous rejoindre !", team.Name), "alert-info"); err != nil {
+                               log.Println("[WRN] Unable to create event:", err)
+                       }
+
                        os.MkdirAll(path.Join(SubmissionDir, team.InitialName), 0777)
                        os.MkdirAll(path.Join(TeamsDir, team.InitialName), 0777)
-               }
 
-               if err := os.Remove(pathname); err != nil {
-                       log.Println("[ERR]", err)
+                       go func() {
+                               if err := genTeamMyFile(team); err != nil {
+                                       log.Println("Team generation error: ", err)
+                               }
+                               if err := genTeamsFile(); err != nil {
+                                       log.Println("teams.json generation error: ", err)
+                               }
+                       }()
                }
        }
 }
index dcc29a6b3df89f5caf5f45fb4ed17668d7630b7f..951fef8ba5e249ab3337756439382ab378a7b552 100644 (file)
@@ -41,7 +41,7 @@ func main() {
        // Register handlers
        http.Handle(fmt.Sprintf("%s/chname/", *prefix), http.StripPrefix(fmt.Sprintf("%s/chname/", *prefix), submissionTeamChecker{"name change", ChNameHandler, *teamsDir}))
        http.Handle(fmt.Sprintf("%s/openhint/", *prefix), http.StripPrefix(fmt.Sprintf("%s/openhint/", *prefix), submissionTeamChecker{"opening hint", HintHandler, *teamsDir}))
-       http.Handle(fmt.Sprintf("%s/registration", *prefix), http.StripPrefix(fmt.Sprintf("%s/registration", *prefix), submissionChecker{"registration", RegistrationHandler}))
+       http.Handle(fmt.Sprintf("%s/registration/", *prefix), http.StripPrefix(fmt.Sprintf("%s/registration/", *prefix), submissionChecker{"registration", RegistrationHandler}))
        http.Handle(fmt.Sprintf("%s/resolution/", *prefix), http.StripPrefix(fmt.Sprintf("%s/resolution/", *prefix), ResolutionHandler{}))
        http.Handle(fmt.Sprintf("%s/submission/", *prefix), http.StripPrefix(fmt.Sprintf("%s/submission/", *prefix), submissionTeamChecker{"submission", SubmissionHandler, *teamsDir}))
        http.Handle(fmt.Sprintf("%s/time.json", *prefix), http.StripPrefix(*prefix, fronttime.TimeHandler{}))
index 13abe08e917e45be89eb496c5e2f39f22371e438..60de99a1c37f28e8767c26845efd05802c169c1a 100644 (file)
@@ -1,7 +1,6 @@
 package main
 
 import (
-       "io/ioutil"
        "log"
        "net/http"
        "path"
@@ -16,28 +15,26 @@ func RegistrationHandler(w http.ResponseWriter, r *http.Request, sURL []string)
                return
        }
 
+       if len(sURL) < 1 || len(sURL[0]) == 0 {
+               http.Error(w, "{\"errmsg\":\"Arguments manquants.\"}", http.StatusBadRequest)
+               return
+       }
+       teamInitialName := sURL[0]
+
        // Check request type and size
        if r.Method != "POST" {
                http.Error(w, "{\"errmsg\":\"Requête invalide.\"}", http.StatusBadRequest)
                return
-       } else if r.ContentLength < 0 || r.ContentLength > 1023 {
+       } else if r.ContentLength < 0 || r.ContentLength > 4095 {
                http.Error(w, "{\"errmsg\":\"Requête trop longue ou de taille inconnue\"}", http.StatusRequestEntityTooLarge)
                return
        }
 
-       if tmpfile, err := ioutil.TempFile(path.Join(SubmissionDir, "_registration"), ""); err != nil {
-               log.Println("Unable to generate registration file:", err)
+       if err := saveFile(path.Join(SubmissionDir, "_registration", teamInitialName), r); err != nil {
+               log.Println("Unable to open registration file:", err)
                http.Error(w, "{\"errmsg\":\"Internal server error. Please retry in few seconds.\"}", http.StatusInternalServerError)
        } else {
-               // The file will be reopened by saveFile
-               tmpfile.Close()
-
-               if err := saveFile(tmpfile.Name(), r); err != nil {
-                       log.Println("Unable to open registration file:", err)
-                       http.Error(w, "{\"errmsg\":\"Internal server error. Please retry in few seconds.\"}", http.StatusInternalServerError)
-               } else {
-                       // File enqueued for backend treatment
-                       http.Error(w, "{\"errmsg\":\"Demande d'enregistrement acceptée\"}", http.StatusAccepted)
-               }
+               // File enqueued for backend treatment
+               http.Error(w, "{\"errmsg\":\"Demande d'enregistrement acceptée\"}", http.StatusAccepted)
        }
 }
index d30543127b283933cbe24d0b03acacf505f0fa98..a5c5413e8e83faa60dfbb9c1426db2c7e7a2ef22 100644 (file)
          <span class="teamname">{{ my.name }}</span>
        </a>
       </span>
+      <span class="navbar-text text-light" ng-show="!my.team_id && time.start" ng-cloak>
+       <a ng-href="/register" class="badge badge-warning" role="button">
+         Inscription
+       </a>
+      </span>
     </nav>
 
     <div class="container">
index 22595e29fba0a3d5cb79b164840ce696dac7e6ec..a9ef2ec75c55985bb6648e92fb74b9953d0c649a 100644 (file)
@@ -13,6 +13,10 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"])
                controller: "RankController",
                templateUrl: "views/rank.html"
            })
+           .when("/register", {
+               controller: "RegisterController",
+               templateUrl: "views/register.html"
+           })
            .when("/videos", {
                controller: "VideosController",
                templateUrl: "views/videos.html"
@@ -74,7 +78,7 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"])
        updTime();
        $interval(updTime, 1000);
     })
-    .controller("DataController", function($sce, $scope, $http, $rootScope, $timeout) {
+    .controller("DataController", function($sce, $scope, $http, $rootScope, $timeout, $location) {
        var actMenu = function() {
            if ($scope.my && $scope.themes) {
                angular.forEach($scope.themes, function(theme, key) {
@@ -146,6 +150,10 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"])
                        });
                    });
                }
+           }, function(response) {
+               if (!$scope.my && response.status == 404) {
+                   $location.url("/register");
+               }
            });
            console.log("refresh!");
        }
@@ -358,6 +366,76 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"])
            });
        };
     })
+    .controller("RegisterController", function($scope, $rootScope, $location, $http) {
+       $rootScope.current_theme = 0;
+       $rootScope.current_exercice = 0;
+       $rootScope.title = "Bienvenue au challenge forensic !";
+       $rootScope.authors = null;
+
+       $scope.members = [{}];
+
+       $scope.AddMember = function() {
+           $scope.members.push({});
+       }
+       $scope.RemoveMember = function(k) {
+           $scope.members.splice(k, 1);
+       }
+       $scope.Validate = function() {
+           if ($scope.teamName.length <= 3) {
+               $('#teamName').addClass("is-invalid")
+               return;
+           } else {
+               $('#vldBtn').removeClass("input-group-btn");
+               $('#vldBtn').css("display", "none");
+               $scope.part2 = true;
+           }
+       }
+
+       $scope.rsubmit = function() {
+           if (!$scope.part2)
+               return $scope.Validate();
+
+           // Remove empty members
+           $scope.members = $scope.members.filter(function(m) {
+               return ((m.lastname != undefined && m.lastname != "") || (m.firstname != undefined && m.firstname != "") || (m.nickname != undefined && m.nickname != ""));
+           });
+
+           if ($scope.members.length == 0) {
+               $scope.messageClass = {"text-danger": true};
+               $scope.message = "Veuillez ajouter au moins un membre dans votre équipe !";
+
+               $scope.members.push({});
+               return;
+           }
+
+           $http({
+               url: "/registration",
+               method: "POST",
+               data: {
+                   teamName: $scope.teamName,
+                   members: $scope.members,
+               }
+           }).then(function(response, status, header, config) {
+               $scope.messageClass = {"text-success": true};
+               $scope.message = response.data.errmsg;
+
+               $rootScope.refresh();
+               if ($scope.my)
+                   $location.url("/");
+           }, function(response) {
+               $scope.messageClass = {"text-danger": true};
+               console.log(response);
+               if (response.data && response.data.errmsg)
+                   $scope.message = response.data.errmsg;
+               else
+                   $scope.message = "Une erreur est survenue lors de l'inscription de l'équipe. Veuillez réessayer dans quelques instants.";
+           });
+       }
+
+       if ($scope.my) {
+           $location.url("/");
+       }
+    })
     .controller("RankController", function($scope, $rootScope) {
        $rootScope.current_theme = 0;
        $rootScope.current_exercice = 0;
diff --git a/frontend/static/views/register.html b/frontend/static/views/register.html
new file mode 100644 (file)
index 0000000..a83c70a
--- /dev/null
@@ -0,0 +1,62 @@
+<div class="jumbotron" style="text-indent: 1em">
+  <p>
+    Félicitations&nbsp;! vous êtes maintenant authentifié auprès de notre
+    serveur&nbsp;!
+  </p>
+  <p>
+    Votre équipe n'est pas encore enregistrée sur notre serveur. Afin de
+    pouvoir participer au challenge, nous vous remercions de bien vouloir
+    remplir le formulaire d'inscription suivant&nbsp;:
+  </p>
+  <form ng-submit="rsubmit()">
+
+    <div class="row">
+      <label for="teamName" class="col col-form-label">Nom d'équipe</label>
+      <div class="col-sm-10">
+       <div class="input-group">
+         <input type="text" class="form-control" id="teamName" ng-model="teamName" placeholder="" autofocus required>
+         <div class="invalid-feedback">
+           Veuillez indiquer un nom d'équipe valide.
+         </div>
+         <span class="input-group-btn" id="vldBtn">
+           <button class="btn btn-info" type="button" ng-click="Validate()">Valider</button>
+         </span>
+       </div>
+      </div>
+    </div>
+
+    <div ng-if="part2">
+      <h4 style="text-indent: 0; margin-top: 20px">
+       Membres d'équipe
+       <button class="btn btn-sm btn-success" type="button" ng-click="AddMember()">
+         <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Ajouter un membre
+       </button>
+      </h4>
+      <p ng-if="message" ng-class="messageClass" ng-bind="message"></p>
+      <div class="row form-group" ng-repeat="(mid, member) in members">
+       <div class="col-sm">
+         <input type="text" class="form-control" ng-model="member.lastname" placeholder="Nom" autofocus>
+       </div>
+       <div class="col-sm">
+         <input type="text" class="form-control" ng-model="member.firstname" placeholder="Prénom">
+       </div>
+       <div class="col-sm">
+         <input type="text" class="form-control" ng-model="member.nickname" placeholder="Pseudo">
+       </div>
+       <div class="col-sm">
+         <input type="text" class="form-control" ng-model="member.company" placeholder="Entreprise">
+       </div>
+       <div class="col-sm-auto">
+         <button class="btn btn-danger" type="button" ng-click="RemoveMember(mid)">
+           <span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
+         </button>
+       </div>
+      </div>
+      <button class="btn btn-info" style="margin-left: 40%;" type="submit">
+       <span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
+       S'inscrire
+      </button>
+    </div>
+
+  </form>
+</div>
index ed3f64cb2d66a1da3f5bce03823d77d3b4b7b2c0..617dfac16edb037feb49660ab2dde083e11b7ecb 100644 (file)
@@ -68,7 +68,7 @@ CREATE TABLE IF NOT EXISTS themes(
        if _, err := db.Exec(`
 CREATE TABLE IF NOT EXISTS teams(
   id_team INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
-  initial_name VARCHAR(255) NOT NULL,
+  initial_name VARCHAR(255) NOT NULL UNIQUE,
   name VARCHAR(255) NOT NULL,
   color INTEGER NOT NULL
 ) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
index 40132e909d73dde0973df48783f96335a10196d6..0286603ac16285bb9c5de6d0de98a63f69131c27 100644 (file)
@@ -52,7 +52,15 @@ func (t Team) AddMember(firstname string, lastname string, nickname string, comp
 }
 
 func (t Team) GainMember(m Member) error {
-       if res, err := DBExec("UPDATE team_members SET id_team = ? WHERE id_member = ?", t.Id, m.Id); err != nil {
+       if m.Id == 0 {
+               if res, err := DBExec("INSERT INTO team_members (id_team, firstname, lastname, nickname, company) VALUES (?, ?, ?, ?, ?)", t.Id, m.Firstname, m.Lastname, m.Nickname, m.Company); err != nil {
+                       return err
+               } else if _, err := res.LastInsertId(); err != nil {
+                       return err
+               } else {
+                       return nil
+               }
+       } else if res, err := DBExec("UPDATE team_members SET id_team = ? WHERE id_member = ?", t.Id, m.Id); err != nil {
                return err
        } else if _, err := res.RowsAffected(); err != nil {
                return err
index 18473525b695a55b683d537c57cfc19396f8a9fb..ecfe2bc1ed2fb57c1e7feea4bd60aafa10eb0e8c 100644 (file)
@@ -62,6 +62,10 @@ func GetTeamByInitialName(initialName string) (Team, error) {
 func CreateTeam(name string, color uint32) (Team, error) {
        re := regexp.MustCompile("[^a-zA-Z0-9]+")
        initialName := re.ReplaceAllLiteralString(name, "_")
+       return RegisterTeam(initialName, name, color)
+}
+
+func RegisterTeam(initialName string, name string, color uint32) (Team, error) {
        if res, err := DBExec("INSERT INTO teams (initial_name, name, color) VALUES (?, ?, ?)", initialName, name, color); err != nil {
                return Team{}, err
        } else if tid, err := res.LastInsertId(); err != nil {