about summary refs log tree commit diff
diff options
context:
space:
mode:
authormaride <maride@darknebu.la>2018-08-22 11:18:37 +0200
committermaride <maride@darknebu.la>2018-08-22 11:18:37 +0200
commit71f083bebcb76d28ced994f24a2babd79dd84bb5 (patch)
tree3c0569092367f08a90ccab63acdda87770542795
parent1e15776d63fc685549e694b5e99529100e96c9ba (diff)
Add filter for challenge categories
-rw-r--r--hosted/challenges.html135
-rw-r--r--src/seed.go7
2 files changed, 95 insertions, 47 deletions
diff --git a/hosted/challenges.html b/hosted/challenges.html
index 34c1ab5..e42d7f0 100644
--- a/hosted/challenges.html
+++ b/hosted/challenges.html
@@ -5,50 +5,68 @@
         <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
         <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.11/handlebars.min.js"></script>
         <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
+        <style type="text/css">
+            body {
+                overflow-y: scroll;
+            }
+        </style>
     </head>
-    <script id="body-template" type="text/x-handlebars-template">
-        <div class="container">
-            <div class="alert alert-secondary" role="alert">
-                <strong>Please note:</strong> All flags are case-sensitive and look like <samp>CIRCUS{IS_REALLY_C00L}</samp>.
-            </div>
-            {{#each challenges}}
-                <div class="card {{#if foundFlag}}border-success{{/if}}" style="margin-bottom: 20px">
-                    <div class="card-header">
+    <script id="nav-template" type="text/x-handlebars-template">
+        <ul class="nav nav-pills" id="navigation-pills">
+            <li class="nav-item">
+                <a class="nav-link active" href="#" onclick="filterChallenges()" circus-category="ALL">
+                    All
+                </a>
+            </li>
+            {{#each categories as |count name|}}
+                <li class="nav-item">
+                    <a class="nav-link" href="#" onclick="filterChallenges('{{ name }}')" circus-category="{{ name }}">
                         {{ name }}
-                        <span class="badge badge-light">
-                            {{ category }}
-                        </span>
-                    </div>
-                    <div class="card-body">
-                        <p>
-                            {{{ description }}}
-                        </p>
-                        {{#if ContainsLaunchable}}
-                            {{#if IPAddress}}
-                                <p class="text-primary">
-                                    Container running IPv4: <kbd>{{IPAddress}}</kbd>
-                                </p>
-                                <button class="btn btn-primary" onclick="stopContainer('{{ name }}');$(this).prop('disabled', true)">Stop Container</button>
-                            {{else}}
-                                <button class="btn btn-primary" onclick="startContainer('{{ name }}');$(this).prop('disabled', true)">Launch Container</button>
-                            {{/if}}
-                        {{/if}}
-                        <hr>
-                        {{#if foundFlag}}
-                            <p class="text-success">Flag found!</p>
+                        <span class="badge badge-light">{{ count }}</span>
+                    </a>
+                </li>
+            {{/each}}
+        </ul>
+    </script>
+    <script id="challenge-template" type="text/x-handlebars-template">
+        <div id="challenge-container">
+        {{#each challenges}}
+            <div class="card {{#if foundFlag}}border-success{{/if}}" style="margin-bottom: 20px" circus-category="{{ category }}">
+                <div class="card-header">
+                    {{ name }}
+                    <span class="badge badge-light">
+                        {{ category }}
+                    </span>
+                </div>
+                <div class="card-body">
+                    <p>
+                        {{{ description }}}
+                    </p>
+                    {{#if ContainsLaunchable}}
+                        {{#if IPAddress}}
+                            <p class="text-primary">
+                                Container running IPv4: <kbd>{{IPAddress}}</kbd>
+                            </p>
+                            <button class="btn btn-primary" onclick="stopContainer('{{ name }}');$(this).prop('disabled', true)">Stop Container</button>
                         {{else}}
-                            <form class="input-group mb-3" onsubmit="event.preventDefault();submitThis(this);">
-                                <input type="hidden" name="challengeName" value="{{ name }}">
-                                <input type="text" class="form-control" aria-label="Flag" aria-describedby="button-submit" name="flag">
-                                <div class="input-group-append">
-                                    <button class="btn btn-outline-primary" type="submit" id="button-submit">Submit flag</button>
-                                </div>
-                            </form>
+                            <button class="btn btn-primary" onclick="startContainer('{{ name }}');$(this).prop('disabled', true)">Launch Container</button>
                         {{/if}}
-                    </div>
+                    {{/if}}
+                    <hr>
+                    {{#if foundFlag}}
+                        <p class="text-success">Flag found!</p>
+                    {{else}}
+                        <form class="input-group mb-3" onsubmit="event.preventDefault();submitThis(this);">
+                            <input type="hidden" name="challengeName" value="{{ name }}">
+                            <input type="text" class="form-control" aria-label="Flag" aria-describedby="button-submit" name="flag">
+                            <div class="input-group-append">
+                                <button class="btn btn-outline-primary" type="submit" id="button-submit">Submit flag</button>
+                            </div>
+                        </form>
+                    {{/if}}
                 </div>
-            {{/each}}
-        </div>
+            </div>
+        {{/each}}
     </script>
     <body>
         <nav class="navbar navbar-expand-md navbar-dark bg-dark mb-4">
@@ -70,22 +88,33 @@
                 </form>
             </div>
         </nav>
-        <main class="container" role="main" id="challenge-list">
-            <div class="alert alert-info" role="alert">
-                Loading challenges...
+        <main class="container" role="main">
+            <div id="nav-list">
+
+            </div>
+            <br>
+            <div id="challenge-list">
+                <div class="alert alert-info" role="alert">
+                    Loading challenges...
+                </div>
             </div>
         </main>
     </body>
     <script type="text/javascript">
-        var template = Handlebars.compile($("#body-template").html());
+        var navTemplate = Handlebars.compile($("#nav-template").html());
+        var challengeTemplate = Handlebars.compile($("#challenge-template").html());
+        var renderedNav = false;
 
         function loadChallengesAndRender() {
             $.ajax({
                 url: "/api/getChallenges",
                 success: function(result) {
-                    $("#challenge-list").html(
-                        template(jQuery.parseJSON(result))
-                    );
+                    if(!renderedNav) {
+                        $("#nav-list").html(navTemplate(jQuery.parseJSON(result)));
+                        renderedNav = true;
+                    }
+                    $("#challenge-list").html(challengeTemplate(jQuery.parseJSON(result)));
+                    filterChallenges($('#navigation-pills > li > a.active').attr("circus-category"));
                 }
             });
         }
@@ -126,6 +155,20 @@
             });
         }
 
+        function filterChallenges(category) {
+            $("#navigation-pills > li > a").removeClass("active");
+
+            if(category && category != "ALL") {
+                $("#challenge-container > div").hide();
+                $("#challenge-container > div[circus-category=" + category + "]").show();
+                $("#navigation-pills > li > a[circus-category=" + category + "]").addClass("active");
+            } else {
+                // Show ALL
+                $("#challenge-container > div").show();
+                $("#navigation-pills > li > a[circus-category=ALL]").addClass("active");
+            }
+        }
+
         $(document).ready(function() {
             loadChallengesAndRender();
         });
diff --git a/src/seed.go b/src/seed.go
index fde4265..5dc849c 100644
--- a/src/seed.go
+++ b/src/seed.go
@@ -78,13 +78,18 @@ func getChallengesFromSeedFile() {
 func generateJSONFromChallenges() (string, error) {
 	// To avoid leakage of container name or the flag towards the user, we need to strip the challenges
 	var strippedChallenges []StrippedChallenge
+	categories := map[string]int{}
 
 	for _, challenge := range challenges {
+		// Append challenge to list
 		strippedChallenges = append(strippedChallenges, stripChallenge(challenge))
+
+		categories[challenge.Category]++
 	}
 
-	marshalled, marshalError := json.Marshal(map[string][]StrippedChallenge{
+	marshalled, marshalError := json.Marshal(map[string]interface{}{
 		"challenges": strippedChallenges,
+		"categories": categories,
 	})
 	return string(marshalled), marshalError
 }