admin: New page to list forge link per theme and exercice

This commit is contained in:
nemunaire 2025-03-31 15:42:07 +02:00
parent b713eba2a5
commit 7d775fe26d
5 changed files with 98 additions and 0 deletions

View File

@ -20,6 +20,7 @@ import (
func declareGlobalExercicesRoutes(router *gin.RouterGroup) { func declareGlobalExercicesRoutes(router *gin.RouterGroup) {
router.GET("/resolutions.json", exportResolutionMovies) router.GET("/resolutions.json", exportResolutionMovies)
router.GET("/exercices_stats.json", getExercicesStats) router.GET("/exercices_stats.json", getExercicesStats)
router.GET("/exercices_forge_bindings.json", getExercicesForgeLinks)
router.GET("/tags", listTags) router.GET("/tags", listTags)
} }
@ -487,6 +488,71 @@ func getExercicesStats(c *gin.Context) {
c.JSON(http.StatusOK, ret) c.JSON(http.StatusOK, ret)
} }
type themeForgeBinding struct {
ThemeName string `json:"name"`
ThemePath string `json:"path"`
ForgeLink string `json:"forge_link"`
Exercices []exerciceForgeBinding `json:"exercices"`
}
type exerciceForgeBinding struct {
ExerciceName string `json:"name"`
ExercicePath string `json:"path"`
ForgeLink string `json:"forge_link"`
}
func getExercicesForgeLinks(c *gin.Context) {
themes, err := fic.GetThemesExtended()
if err != nil {
log.Println("Unable to listThemes:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during themes listing."})
return
}
fli, ok := sync.GlobalImporter.(sync.ForgeLinkedImporter)
if !ok {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Current importer is not compatible with ForgeLinkedImporter"})
return
}
ret := []themeForgeBinding{}
for _, theme := range themes {
exercices, err := theme.GetExercices()
if err != nil {
log.Println("Unable to listExercices:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during exercice listing."})
return
}
var exlinks []exerciceForgeBinding
for _, exercice := range exercices {
var forgelink string
if u, _ := fli.GetExerciceLink(exercice); u != nil {
forgelink = u.String()
}
exlinks = append(exlinks, exerciceForgeBinding{
ExerciceName: exercice.Title,
ExercicePath: exercice.Path,
ForgeLink: forgelink,
})
}
var forgelink string
if u, _ := fli.GetThemeLink(theme); u != nil {
forgelink = u.String()
}
ret = append(ret, themeForgeBinding{
ThemeName: theme.Name,
ThemePath: theme.Path,
ForgeLink: forgelink,
Exercices: exlinks,
})
}
c.JSON(http.StatusOK, ret)
}
func AssigneeCookieHandler(c *gin.Context) { func AssigneeCookieHandler(c *gin.Context) {
myassignee, err := c.Cookie("myassignee") myassignee, err := c.Cookie("myassignee")
if err != nil { if err != nil {

View File

@ -80,6 +80,9 @@ func declareStaticRoutes(router *gin.RouterGroup, cfg *settings.Settings, baseUR
router.GET("/files", func(c *gin.Context) { router.GET("/files", func(c *gin.Context) {
serveIndex(c) serveIndex(c)
}) })
router.GET("/forge-links", func(c *gin.Context) {
serveIndex(c)
})
router.GET("/public/*_", func(c *gin.Context) { router.GET("/public/*_", func(c *gin.Context) {
serveIndex(c) serveIndex(c)
}) })

View File

@ -49,6 +49,10 @@ angular.module("FICApp", ["ngRoute", "ngResource", "ngSanitize"])
controller: "ExerciceController", controller: "ExerciceController",
templateUrl: "views/exercice-resolution.html" templateUrl: "views/exercice-resolution.html"
}) })
.when("/forge-links", {
controller: "ForgeLinksController",
templateUrl: "views/exercices-forgelink.html"
})
.when("/tags", { .when("/tags", {
controller: "TagsListController", controller: "TagsListController",
templateUrl: "views/tags.html" templateUrl: "views/tags.html"
@ -2189,6 +2193,16 @@ angular.module("FICApp")
}; };
}) })
.controller("ForgeLinksController", function ($scope, $http) {
$http({
url: "api/exercices_forge_bindings.json",
}).then(function (response) {
$scope.forge_links = response.data;
}, function (response) {
$scope.addToast('danger', 'An error occurs when generating exercice forge links: ', response.data.errmsg);
});
})
.controller("ExerciceTagsController", function ($scope, ExerciceTags, $routeParams, $rootScope) { .controller("ExerciceTagsController", function ($scope, ExerciceTags, $routeParams, $rootScope) {
$scope.tags = ExerciceTags.query({ exerciceId: $routeParams.exerciceId }); $scope.tags = ExerciceTags.query({ exerciceId: $routeParams.exerciceId });

View File

@ -0,0 +1,14 @@
<h1>Accès rapide aux exercices</h1>
<div ng-repeat="theme in forge_links">
<h2>
<a ng-href="{{ theme.forge_link }}" target="_blank">
{{ theme.name }}
</a>&nbsp;: {{ theme.path }}
</h2>
<ul>
<li ng-repeat="exercice in theme.exercices">
<a ng-href="{{ exercice.forge_link }}" target="_blank">{{ exercice.name }}</a>&nbsp;: <span class="text-monospace">{{ exercice.path }}</span>
</li>
</ul>
</div>

View File

@ -2,6 +2,7 @@
Thèmes Thèmes
<button type="button" ng-click="show('new')" class="float-right btn btn-sm btn-primary ml-2"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Ajouter un thème</button> <button type="button" ng-click="show('new')" class="float-right btn btn-sm btn-primary ml-2"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Ajouter un thème</button>
<button type="button" ng-click="sync()" ng-class="{'disabled': inSync}" class="float-right btn btn-sm btn-secondary ml-2"><span class="glyphicon glyphicon-refresh" aria-hidden="true"></span> Synchroniser</button> <button type="button" ng-click="sync()" ng-class="{'disabled': inSync}" class="float-right btn btn-sm btn-secondary ml-2"><span class="glyphicon glyphicon-refresh" aria-hidden="true"></span> Synchroniser</button>
<a href="forge-links" class="float-right btn btn-sm btn-dark ml-2"><span class="glyphicon glyphicon-folder-open" aria-hidden="true"></span> Liens d'accès à la forge</a>
</h2> </h2>
<p><input type="search" class="form-control" placeholder="Search" ng-model="query" ng-keypress="validateSearch($event)" autofocus></p> <p><input type="search" class="form-control" placeholder="Search" ng-model="query" ng-keypress="validateSearch($event)" autofocus></p>