[admin] Add new routes to manage hints, files and keys
This commit is contained in:
parent
875cb11747
commit
c64f1969d9
@ -18,12 +18,20 @@ func init() {
|
||||
|
||||
router.GET("/api/exercices/:eid/files", apiHandler(exerciceHandler(listExerciceFiles)))
|
||||
router.POST("/api/exercices/:eid/files", apiHandler(exerciceHandler(createExerciceFile)))
|
||||
router.GET("/api/exercices/:eid/files/:fid", apiHandler(fileHandler(showExerciceFile)))
|
||||
router.DELETE("/api/exercices/:eid/files/:fid", apiHandler(fileHandler(deleteExerciceFile)))
|
||||
|
||||
router.GET("/api/exercices/:eid/hints", apiHandler(exerciceHandler(listExerciceHints)))
|
||||
router.POST("/api/exercices/:eid/hints", apiHandler(exerciceHandler(createExerciceHint)))
|
||||
router.GET("/api/exercices/:eid/hints/:hid", apiHandler(hintHandler(showExerciceHint)))
|
||||
router.PUT("/api/exercices/:eid/hints/:hid", apiHandler(hintHandler(updateExerciceHint)))
|
||||
router.DELETE("/api/exercices/:eid/hints/:hid", apiHandler(hintHandler(deleteExerciceHint)))
|
||||
|
||||
router.GET("/api/exercices/:eid/keys", apiHandler(exerciceHandler(listExerciceKeys)))
|
||||
router.POST("/api/exercices/:eid/keys", apiHandler(exerciceHandler(createExerciceKey)))
|
||||
router.GET("/api/exercices/:eid/keys/:kid", apiHandler(keyHandler(showExerciceKey)))
|
||||
router.PUT("/api/exercices/:eid/keys/:kid", apiHandler(keyHandler(updateExerciceKey)))
|
||||
router.DELETE("/api/exercices/:eid/keys/:kid", apiHandler(keyHandler(deleteExerciceKey)))
|
||||
}
|
||||
|
||||
func listExercices(_ httprouter.Params, body []byte) (interface{}, error) {
|
||||
@ -94,7 +102,7 @@ func createExercice(theme fic.Theme, body []byte) (interface{}, error) {
|
||||
}
|
||||
|
||||
type uploadedKey struct {
|
||||
Name string
|
||||
Type string
|
||||
Key string
|
||||
}
|
||||
|
||||
@ -108,7 +116,7 @@ func createExerciceKey(exercice fic.Exercice, body []byte) (interface{}, error)
|
||||
return nil, errors.New("Key not filled")
|
||||
}
|
||||
|
||||
return exercice.AddRawKey(uk.Name, uk.Key)
|
||||
return exercice.AddRawKey(uk.Type, uk.Key)
|
||||
}
|
||||
|
||||
func createExerciceHint(exercice fic.Exercice, body []byte) (interface{}, error) {
|
||||
@ -123,3 +131,68 @@ func createExerciceHint(exercice fic.Exercice, body []byte) (interface{}, error)
|
||||
|
||||
return exercice.AddHint(uh.Title, uh.Content, uh.Cost)
|
||||
}
|
||||
|
||||
func showExerciceHint(hint fic.EHint, body []byte) (interface{}, error) {
|
||||
return hint, nil
|
||||
}
|
||||
|
||||
func updateExerciceHint(hint fic.EHint, body []byte) (interface{}, error) {
|
||||
var uh fic.EHint
|
||||
if err := json.Unmarshal(body, &uh); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
uh.Id = hint.Id
|
||||
|
||||
if len(uh.Title) == 0 {
|
||||
return nil, errors.New("Hint's title not filled")
|
||||
}
|
||||
|
||||
if _, err := uh.Update(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return uh, nil
|
||||
}
|
||||
|
||||
func deleteExerciceHint(hint fic.EHint, _ []byte) (interface{}, error) {
|
||||
return hint.Delete()
|
||||
}
|
||||
|
||||
|
||||
func showExerciceKey(key fic.Key, _ fic.Exercice, body []byte) (interface{}, error) {
|
||||
return key, nil
|
||||
}
|
||||
|
||||
func updateExerciceKey(key fic.Key, exercice fic.Exercice, body []byte) (interface{}, error) {
|
||||
var uk fic.Key
|
||||
if err := json.Unmarshal(body, &uk); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
uk.Id = key.Id
|
||||
uk.IdExercice = exercice.Id
|
||||
|
||||
if len(uk.Type) == 0 {
|
||||
uk.Type = "Flag"
|
||||
}
|
||||
|
||||
if _, err := uk.Update(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return uk, nil
|
||||
}
|
||||
|
||||
func deleteExerciceKey(key fic.Key, _ fic.Exercice, _ []byte) (interface{}, error) {
|
||||
return key.Delete()
|
||||
}
|
||||
|
||||
|
||||
func showExerciceFile(file fic.EFile, body []byte) (interface{}, error) {
|
||||
return file, nil
|
||||
}
|
||||
|
||||
func deleteExerciceFile(file fic.EFile, _ []byte) (interface{}, error) {
|
||||
return file.Delete()
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
@ -151,6 +152,76 @@ func themedExerciceHandler(f func(fic.Theme,fic.Exercice,[]byte) (interface{}, e
|
||||
}
|
||||
}
|
||||
|
||||
func hintHandler(f func(fic.EHint,[]byte) (interface{}, error)) func (httprouter.Params,[]byte) (interface{}, error) {
|
||||
return func (ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
if hid, err := strconv.Atoi(string(ps.ByName("hid"))); err != nil {
|
||||
return nil, err
|
||||
} else if hint, err := fic.GetHint(int64(hid)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return f(hint, body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func keyHandler(f func(fic.Key,fic.Exercice,[]byte) (interface{}, error)) func (httprouter.Params,[]byte) (interface{}, error) {
|
||||
return func (ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
var exercice fic.Exercice
|
||||
exerciceHandler(func (ex fic.Exercice, _[]byte) (interface{}, error) {
|
||||
exercice = ex
|
||||
return nil,nil
|
||||
})(ps, body)
|
||||
|
||||
if kid, err := strconv.Atoi(string(ps.ByName("kid"))); err != nil {
|
||||
return nil, err
|
||||
} else if keys, err := exercice.GetKeys(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
for _, key := range keys {
|
||||
if (key.Id == int64(kid)) {
|
||||
return f(key, exercice, body)
|
||||
}
|
||||
}
|
||||
return nil, errors.New("Unable to find the requested key")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fileHandler(f func(fic.EFile,[]byte) (interface{}, error)) func (httprouter.Params,[]byte) (interface{}, error) {
|
||||
return func (ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
var exercice fic.Exercice
|
||||
exerciceHandler(func (ex fic.Exercice, _[]byte) (interface{}, error) {
|
||||
exercice = ex
|
||||
return nil,nil
|
||||
})(ps, body)
|
||||
|
||||
if fid, err := strconv.Atoi(string(ps.ByName("fid"))); err != nil {
|
||||
return nil, err
|
||||
} else if files, err := exercice.GetFiles(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
for _, file := range files {
|
||||
if (file.Id == int64(fid)) {
|
||||
return f(file, body)
|
||||
}
|
||||
}
|
||||
return nil, errors.New("Unable to find the requested file")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func eventHandler(f func(fic.Event,[]byte) (interface{}, error)) func (httprouter.Params,[]byte) (interface{}, error) {
|
||||
return func (ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
if evid, err := strconv.Atoi(string(ps.ByName("evid"))); err != nil {
|
||||
return nil, err
|
||||
} else if event, err := fic.GetEvent(evid); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return f(event, body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func notFound(ps httprouter.Params, _ []byte) (interface{}, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
@ -60,10 +60,10 @@ new_hint() {
|
||||
new_key() {
|
||||
THEME="$1"
|
||||
EXERCICE="$2"
|
||||
NAME="$3"
|
||||
TYPE="$3"
|
||||
KEY=`echo $4 | sed 's/"/\\\\"/g' | sed 's#\\\\#\\\\\\\\#g'`
|
||||
|
||||
curl -f -s -d "{\"name\": \"$NAME\", \"key\": \"$KEY\"}" "${BASEURL}/api/themes/$THEME/exercices/$EXERCICE/keys" |
|
||||
curl -f -s -d "{\"type\": \"$TYPE\", \"key\": \"$KEY\"}" "${BASEURL}/api/themes/$THEME/exercices/$EXERCICE/keys" |
|
||||
grep -Eo '"id":[0-9]+,' | grep -Eo "[0-9]+"
|
||||
}
|
||||
|
||||
|
@ -97,18 +97,18 @@ angular.module("FICApp")
|
||||
})
|
||||
})
|
||||
.factory("ExerciceFile", function($resource) {
|
||||
return $resource("/api/exercices/:exerciceId/files", { exerciceId: '@idExercice' }, {
|
||||
update: {method: 'PATCH'}
|
||||
return $resource("/api/exercices/:exerciceId/files/:fileId", { exerciceId: '@idExercice', fileId: '@id' }, {
|
||||
update: {method: 'PUT'}
|
||||
})
|
||||
})
|
||||
.factory("ExerciceHint", function($resource) {
|
||||
return $resource("/api/exercices/:exerciceId/hints", { exerciceId: '@idExercice' }, {
|
||||
update: {method: 'PATCH'}
|
||||
return $resource("/api/exercices/:exerciceId/hints/:hintId", { exerciceId: '@idExercice', hintId: '@id' }, {
|
||||
update: {method: 'PUT'}
|
||||
})
|
||||
})
|
||||
.factory("ExerciceKey", function($resource) {
|
||||
return $resource("/api/exercices/:exerciceId/keys", { exerciceId: '@idExercice' }, {
|
||||
update: {method: 'PATCH'}
|
||||
return $resource("/api/exercices/:exerciceId/keys/:keyId", { exerciceId: '@idExercice', keyId: '@id' }, {
|
||||
update: {method: 'PUT'}
|
||||
})
|
||||
});
|
||||
|
||||
@ -175,6 +175,17 @@ angular.module("FICApp")
|
||||
}
|
||||
})
|
||||
|
||||
.directive('integer', function() {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
link: function(scope, ele, attr, ctrl){
|
||||
ctrl.$parsers.unshift(function(viewValue){
|
||||
return parseInt(viewValue, 10);
|
||||
});
|
||||
}
|
||||
};
|
||||
})
|
||||
|
||||
.controller("VersionController", function($scope, Version) {
|
||||
$scope.v = Version.get();
|
||||
})
|
||||
@ -228,7 +239,13 @@ angular.module("FICApp")
|
||||
$scope.fields = ["name", "authors"];
|
||||
|
||||
$scope.saveTheme = function() {
|
||||
this.theme.$update();
|
||||
if (this.theme.id) {
|
||||
this.theme.$update();
|
||||
} else {
|
||||
this.theme.$save(function() {
|
||||
$location.url("/themes/" + $scope.theme.id);
|
||||
});
|
||||
}
|
||||
}
|
||||
$scope.deleteTheme = function() {
|
||||
this.theme.$remove(function() { $location.url("/themes/");});
|
||||
@ -237,7 +254,7 @@ angular.module("FICApp")
|
||||
|
||||
.controller("AllExercicesListController", function($scope, Exercice, $routeParams, $location) {
|
||||
$scope.exercices = Exercice.query();
|
||||
$scope.fields = ["id", "title", "statement", "videoURI"];
|
||||
$scope.fields = ["title", "statement", "videoURI"];
|
||||
|
||||
$scope.show = function(id) {
|
||||
$location.url("/exercices/" + id);
|
||||
@ -245,27 +262,40 @@ angular.module("FICApp")
|
||||
})
|
||||
.controller("ExercicesListController", function($scope, ThemedExercice, $routeParams, $location) {
|
||||
$scope.exercices = ThemedExercice.query({ themeId: $routeParams.themeId });
|
||||
$scope.fields = ["id", "title", "statement", "videoURI"];
|
||||
$scope.fields = ["title", "statement", "videoURI"];
|
||||
|
||||
$scope.show = function(id) {
|
||||
$location.url("/themes/" + $routeParams.themeId + "/exercices/" + id);
|
||||
};
|
||||
})
|
||||
.controller("ExerciceController", function($scope, Exercice, $routeParams) {
|
||||
$scope.exercice = Exercice.get({ exerciceId: $routeParams.exerciceId });
|
||||
.controller("ExerciceController", function($scope, Exercice, ThemedExercice, $routeParams, $location) {
|
||||
if ($routeParams.themeId && $routeParams.exerciceId == "new") {
|
||||
$scope.exercice = new ThemedExercice();
|
||||
} else {
|
||||
$scope.exercice = Exercice.get({ exerciceId: $routeParams.exerciceId });
|
||||
}
|
||||
$scope.exercices = Exercice.query();
|
||||
$scope.fields = ["title", "statement", "depend", "gain", "videoURI"];
|
||||
|
||||
$scope.saveExercice = function() {
|
||||
this.exercice.$update();
|
||||
if (this.exercice.id) {
|
||||
this.exercice.$update();
|
||||
} else if ($routeParams.themeId) {
|
||||
this.exercice.$save({ themeId: $routeParams.themeId }, function() {
|
||||
$location.url("/themes/" + $scope.exercice.idTheme + "/exercices/" + $scope.exercice.id);
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
.controller("ExerciceFilesController", function($scope, ExerciceFile, $routeParams) {
|
||||
.controller("ExerciceFilesController", function($scope, ExerciceFile, $routeParams, $location) {
|
||||
$scope.files = ExerciceFile.query({ exerciceId: $routeParams.exerciceId });
|
||||
|
||||
$scope.deleteFile = function() {
|
||||
this.file.$delete();
|
||||
this.file.$delete(function() {
|
||||
$scope.files.splice($scope.files.indexOf(this.file), 1);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
$scope.saveFile = function() {
|
||||
this.file.$update();
|
||||
@ -275,22 +305,40 @@ angular.module("FICApp")
|
||||
.controller("ExerciceHintsController", function($scope, ExerciceHint, $routeParams) {
|
||||
$scope.hints = ExerciceHint.query({ exerciceId: $routeParams.exerciceId });
|
||||
|
||||
$scope.addHint = function() {
|
||||
$scope.hints.push(new ExerciceHint());
|
||||
}
|
||||
$scope.deleteHint = function() {
|
||||
this.hint.$delete();
|
||||
this.hint.$delete(function() {
|
||||
$scope.hints.splice($scope.hints.indexOf(this.hint), 1);
|
||||
});
|
||||
}
|
||||
$scope.saveHint = function() {
|
||||
this.hint.$update();
|
||||
if (this.hint.id) {
|
||||
this.hint.$update();
|
||||
} else {
|
||||
this.hint.$save({ exerciceId: $routeParams.exerciceId });
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
.controller("ExerciceKeysController", function($scope, ExerciceKey, $routeParams) {
|
||||
$scope.keys = ExerciceKey.query({ exerciceId: $routeParams.exerciceId });
|
||||
|
||||
$scope.addKey = function() {
|
||||
$scope.keys.push(new ExerciceKey());
|
||||
}
|
||||
$scope.deleteKey = function() {
|
||||
this.key.$delete();
|
||||
this.key.$delete(function() {
|
||||
$scope.keys.splice($scope.keys.indexOf(this.key), 1);
|
||||
});
|
||||
}
|
||||
$scope.saveKey = function() {
|
||||
this.key.$update();
|
||||
if (this.key.id) {
|
||||
this.key.$update();
|
||||
} else {
|
||||
this.key.$save({ exerciceId: $routeParams.exerciceId });
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -1,25 +1,29 @@
|
||||
<h2>{{exercice.title}}</h2>
|
||||
|
||||
<form class="form form-horizontal" ng-submit="saveExercice()">
|
||||
<form class="form-horizontal" ng-submit="saveExercice()">
|
||||
<div class="form-group" ng-repeat="field in fields">
|
||||
<label for="{{ field }}" class="col-xs-1 control-label">{{ field | capitalize }}</label>
|
||||
<div class="col-xs-11">
|
||||
<input type="text" class="form-control" id="{{ field }}" ng-model="exercice[field]" ng-show="field != 'statement' && field != 'depend'">
|
||||
<textarea class="form-control" id="hcnt{{hint.id}}" ng-bind-html="exercice[field]" ng-show="field == 'statement'"></textarea>
|
||||
<select class="form-control" id="hcnt{{hint.id}}" ng-model="exercice[field]" ng-options="ex.id as ex.title for ex in exercices" ng-show="field == 'depend'">
|
||||
<input type="text" class="form-control" id="{{ field }}" ng-model="exercice[field]" ng-show="field != 'statement' && field != 'depend' && field != 'gain'">
|
||||
<input type="text" class="form-control" id="{{ field }}" ng-model="exercice[field]" ng-show="field == 'gain'" integer>
|
||||
<textarea class="form-control" id="{{field}}" ng-model="exercice[field]" ng-show="field == 'statement'"></textarea>
|
||||
<select class="form-control" id="{{field}}" ng-model="exercice[field]" ng-options="ex.id as ex.title for ex in exercices" ng-show="field == 'depend'">
|
||||
<option value="">Aucune</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<div class="text-right" ng-show="exercice.id">
|
||||
<button type="submit" class="btn btn-primary"><span class="glyphicon glyphicon-save" aria-hidden="true"></span> Save</button>
|
||||
<button class="btn btn-danger" ng-click="deleteExercice()"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span> Delete</button>
|
||||
<a class="btn btn-danger" ng-click="deleteExercice()"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span> Delete</a>
|
||||
</div>
|
||||
<div class="text-right" ng-show="!exercice.id">
|
||||
<button type="submit" class="btn btn-primary"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Create exercice</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="row">
|
||||
<div class="row" ng-show="exercice.id">
|
||||
|
||||
<div class="col-md-4" ng-controller="ExerciceHintsController">
|
||||
<div class="panel panel-default">
|
||||
@ -37,17 +41,17 @@
|
||||
<div class="form-group">
|
||||
<label for="hcnt{{hint.id}}" class="col-xs-2 control-label">Contenu</label>
|
||||
<div class="col-xs-10">
|
||||
<textarea class="form-control" id="hcnt{{hint.id}}" ng-bind-html="hint.content"></textarea>
|
||||
<textarea class="form-control" id="hcnt{{hint.id}}" ng-model="hint.content"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="hcost{{hint.id}}" class="col-xs-2 control-label">Coût</label>
|
||||
<div class="col-xs-10">
|
||||
<input type="text" id="hcost{{hint.id}}" ng-model="hint.cost" class="form-control">
|
||||
<input type="text" id="hcost{{hint.id}}" ng-model="hint.cost" class="form-control" integer>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-success"><span class="glyphicon glyphicon-ok" aria-hidden="true"></span></button>
|
||||
<button class="btn btn-danger"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span></button>
|
||||
<a ng-click="deleteHint()" class="btn btn-danger" ng-show="hint.id"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span></a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@ -80,15 +84,30 @@
|
||||
<form ng-submit="saveKey()" class="list-group-item form-horizontal" ng-repeat="key in keys">
|
||||
<div class="form-group">
|
||||
<label for="ktype{{key.id}}" class="col-xs-2 control-label">Intitulé</label>
|
||||
<div class="col-xs-10">
|
||||
<div class="col-xs-8">
|
||||
<input type="text" id="ktype{{key.id}}" ng-model="key.type" class="form-control">
|
||||
</div>
|
||||
<div class="col-xs-1" ng-show="key.id">
|
||||
<a ng-click="deleteKey()" class="btn btn-danger"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-group" ng-show="key.id">
|
||||
<label for="kvalue{{key.id}}" class="col-xs-2 control-label">Hash</label>
|
||||
<div class="col-xs-10">
|
||||
<div class="col-xs-8">
|
||||
<input type="text" id="kvalue{{key.id}}" ng-model="key.value" class="form-control">
|
||||
</div>
|
||||
<div class="col-xs-1">
|
||||
<button type="submit" class="btn btn-success"><span class="glyphicon glyphicon-ok" aria-hidden="true"></span></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-show="!key.id">
|
||||
<label for="kvalue{{key.id}}" class="col-xs-2 control-label">Clef brute</label>
|
||||
<div class="col-xs-8">
|
||||
<input type="text" id="kvalue{{key.id}}" ng-model="key.key" class="form-control">
|
||||
</div>
|
||||
<div class="col-xs-1">
|
||||
<button type="submit" class="btn btn-success"><span class="glyphicon glyphicon-ok" aria-hidden="true"></span></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<h2>Thèmes</h2>
|
||||
<h2>Thèmes<a ng-click="show('new')" class="pull-right btn btn-primary"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Ajouter un thème</a></h2>
|
||||
|
||||
<p><input type="search" class="form-control" placeholder="Search" ng-model="query"></p>
|
||||
<table class="table table-hover table-bordered">
|
||||
|
@ -1,19 +1,24 @@
|
||||
<h2>{{theme.name}} <small>{{theme.authors}}</small></h2>
|
||||
|
||||
<form class="form" ng-submit="saveTheme()" class="form-horizontal">
|
||||
<form ng-submit="saveTheme()" class="form-horizontal">
|
||||
<div class="form-group" ng-repeat="field in fields">
|
||||
<label for="{{ field }}">{{ field | capitalize }}</label>
|
||||
<input type="text" class="form-control" id="{{ field }}" ng-model="theme[field]">
|
||||
<label for="{{ field }}" class="col-sm-1 control-label">{{ field | capitalize }}</label>
|
||||
<div class="col-sm-11">
|
||||
<input type="text" class="form-control" id="{{ field }}" ng-model="theme[field]">
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<div class="text-right" ng-show="theme.id">
|
||||
<button type="submit" class="btn btn-primary"><span class="glyphicon glyphicon-save" aria-hidden="true"></span> Save</button>
|
||||
<button class="btn btn-danger" ng-click="deleteTheme()"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span> Delete</button>
|
||||
<a class="btn btn-danger" ng-click="deleteTheme()"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span> Delete</a>
|
||||
</div>
|
||||
<div class="text-right" ng-show="!theme.id">
|
||||
<button type="submit" class="btn btn-primary"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Create theme</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<h3>Exercices</h3>
|
||||
<div ng-show="theme.id" ng-controller="ExercicesListController">
|
||||
<h3>Exercices<a ng-click="show('new')" class="pull-right btn btn-sm btn-primary"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Ajouter un exercice</a></h3>
|
||||
|
||||
<div ng-controller="ExercicesListController">
|
||||
<p><input type="search" class="form-control" placeholder="Search" ng-model="query"></p>
|
||||
<table class="table table-hover table-bordered">
|
||||
<thead>
|
||||
|
Loading…
Reference in New Issue
Block a user