Can lock theme

This commit is contained in:
nemunaire 2023-03-20 15:20:20 +01:00
parent 4f28e8b1bc
commit 8038407b07
9 changed files with 71 additions and 35 deletions

View File

@ -210,6 +210,23 @@ func updateTheme(c *gin.Context) {
return
}
if theme.Locked != ut.Locked {
exercices, err := theme.GetExercices()
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
return
}
for _, exercice := range exercices {
if exercice.Disabled != ut.Locked {
exercice.Disabled = ut.Locked
_, err = exercice.Update()
if err != nil {
log.Println("Unable to enable/disable exercice: ", exercice.Id, err.Error())
}
}
}
}
c.JSON(http.StatusOK, ut)
}

View File

@ -1539,7 +1539,7 @@ angular.module("FICApp")
})
.controller("ThemeController", function($scope, Theme, $routeParams, $location, $rootScope, $http) {
$scope.theme = Theme.get({ themeId: $routeParams.themeId });
$scope.fields = ["name", "urlid", "authors", "headline", "intro", "image", "partner_txt", "partner_href", "partner_img"];
$scope.fields = ["name", "urlid", "locked", "authors", "headline", "intro", "image", "partner_txt", "partner_href", "partner_img"];
$scope.saveTheme = function() {
if (this.theme.id) {

View File

@ -9,10 +9,10 @@
<div class="row">
<form ng-submit="saveTheme()" class="col-4">
<div ng-class="{'form-group': field != 'disabled', 'form-check': field == 'disabled'}" ng-repeat="field in fields">
<input type="checkbox" class="form-check-input" id="{{ field }}" ng-model="theme[field]" ng-if="field == 'disabled'">
<div ng-class="{'form-group': field != 'locked', 'form-check': field == 'locked'}" ng-repeat="field in fields">
<input type="checkbox" class="form-check-input" id="{{ field }}" ng-model="theme[field]" ng-if="field == 'locked'">
<label for="{{ field }}">{{ field | capitalize }}</label>
<input type="text" class="form-control form-control-sm" id="{{ field }}" ng-model="theme[field]" ng-if="field != 'intro' && field != 'disabled'">
<input type="text" class="form-control form-control-sm" id="{{ field }}" ng-model="theme[field]" ng-if="field != 'intro' && field != 'locked'">
<textarea class="form-control form-control-sm" id="{{ field }}" ng-model="theme[field]" ng-if="field == 'intro'"></textarea>
</div>
<div class="text-right" ng-show="theme.id">

View File

@ -63,6 +63,12 @@ func treatSubmission(pathname string, team *fic.Team, exercice_id string) {
return
}
// Theme should not be locked
if theme.Locked {
log.Printf("%s [!!!] Submission received for locked theme %d (eid=%d): %s\n", id, exercice.IdTheme, eid, theme.Name)
return
}
// Read received file
cnt_raw, err := ioutil.ReadFile(pathname)
if err != nil {

View File

@ -27,7 +27,11 @@
on:click
>
{#if theme.image}
<div class="card-img-top" style="background-image: url({ theme.image.substr(0, theme.image.length-3) }thumb.jpg)"></div>
<div
class="card-img-top"
style="background-image: url({ theme.image.substr(0, theme.image.length-3) }thumb.jpg)"
style:filter={theme.locked ? "gray":null}
></div>
{/if}
<CardBody class="text-indent">
<CardTitle class="fw-bolder">

View File

@ -52,7 +52,7 @@
{#each $themes as th, index}
<Col class="mb-3">
<CardTheme
class="{$my && $my.team_id && $myThemes[th.id].exercice_solved > 0?'border-light ':''}{th.exercice_coeff_max > 1?'border-success ':''}"
class="{$my && $my.team_id && $myThemes[th.id].exercice_solved > 0?'border-light ':''}{th.exercice_coeff_max > 1?'border-success ':''}{th.locked?' border-secondary ':''}"
theme={th}
on:click={goto(`${th.urlid}`)}
/>

View File

@ -87,6 +87,7 @@ CREATE TABLE IF NOT EXISTS events(
CREATE TABLE IF NOT EXISTS themes(
id_theme INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
locked BOOLEAN NOT NULL DEFAULT 0,
url_id VARCHAR(191) NOT NULL UNIQUE,
path VARCHAR(191) NOT NULL UNIQUE,
headline TEXT NOT NULL,

View File

@ -7,6 +7,7 @@ type Theme struct {
Id int64 `json:"id"`
Language string `json:"lang,omitempty"`
Name string `json:"name"`
Locked bool `json:"locked"`
URLId string `json:"urlid"`
Path string `json:"path"`
Authors string `json:"authors,omitempty"`
@ -25,7 +26,7 @@ func CmpTheme(t1 *Theme, t2 *Theme) bool {
// GetThemes returns a list of registered Themes from the database.
func GetThemes() ([]*Theme, error) {
if rows, err := DBQuery("SELECT id_theme, name, url_id, path, authors, intro, headline, image, partner_img, partner_href, partner_text FROM themes"); err != nil {
if rows, err := DBQuery("SELECT id_theme, name, locked, url_id, path, authors, intro, headline, image, partner_img, partner_href, partner_text FROM themes"); err != nil {
return nil, err
} else {
defer rows.Close()
@ -33,7 +34,7 @@ func GetThemes() ([]*Theme, error) {
var themes []*Theme
for rows.Next() {
t := &Theme{}
if err := rows.Scan(&t.Id, &t.Name, &t.URLId, &t.Path, &t.Authors, &t.Intro, &t.Headline, &t.Image, &t.PartnerImage, &t.PartnerLink, &t.PartnerText); err != nil {
if err := rows.Scan(&t.Id, &t.Name, &t.Locked, &t.URLId, &t.Path, &t.Authors, &t.Intro, &t.Headline, &t.Image, &t.PartnerImage, &t.PartnerLink, &t.PartnerText); err != nil {
return nil, err
}
themes = append(themes, t)
@ -49,7 +50,7 @@ func GetThemes() ([]*Theme, error) {
// GetTheme retrieves a Theme from its identifier.
func GetTheme(id int64) (*Theme, error) {
t := &Theme{}
if err := DBQueryRow("SELECT id_theme, name, url_id, path, authors, intro, headline, image, partner_img, partner_href, partner_text FROM themes WHERE id_theme=?", id).Scan(&t.Id, &t.Name, &t.URLId, &t.Path, &t.Authors, &t.Intro, &t.Headline, &t.Image, &t.PartnerImage, &t.PartnerLink, &t.PartnerText); err != nil {
if err := DBQueryRow("SELECT id_theme, name, locked, url_id, path, authors, intro, headline, image, partner_img, partner_href, partner_text FROM themes WHERE id_theme=?", id).Scan(&t.Id, &t.Name, &t.Locked, &t.URLId, &t.Path, &t.Authors, &t.Intro, &t.Headline, &t.Image, &t.PartnerImage, &t.PartnerLink, &t.PartnerText); err != nil {
return t, err
}
@ -59,7 +60,7 @@ func GetTheme(id int64) (*Theme, error) {
// GetTheme retrieves a Theme from an given Exercice.
func (e *Exercice) GetTheme() (*Theme, error) {
t := &Theme{}
if err := DBQueryRow("SELECT id_theme, name, url_id, path, authors, intro, headline, image, partner_img, partner_href, partner_text FROM themes WHERE id_theme=?", e.IdTheme).Scan(&t.Id, &t.Name, &t.URLId, &t.Path, &t.Authors, &t.Intro, &t.Headline, &t.Image, &t.PartnerImage, &t.PartnerLink, &t.PartnerText); err != nil {
if err := DBQueryRow("SELECT id_theme, name, locked, url_id, path, authors, intro, headline, image, partner_img, partner_href, partner_text FROM themes WHERE id_theme=?", e.IdTheme).Scan(&t.Id, &t.Name, &t.Locked, &t.URLId, &t.Path, &t.Authors, &t.Intro, &t.Headline, &t.Image, &t.PartnerImage, &t.PartnerLink, &t.PartnerText); err != nil {
return t, err
}
@ -69,7 +70,7 @@ func (e *Exercice) GetTheme() (*Theme, error) {
// GetThemeByName retrieves a Theme from its title
func GetThemeByName(name string) (*Theme, error) {
t := &Theme{}
if err := DBQueryRow("SELECT id_theme, name, url_id, path, authors, intro, headline, image, partner_img, partner_text FROM themes WHERE name=?", name).Scan(&t.Id, &t.Name, &t.URLId, &t.Path, &t.Authors, &t.Intro, &t.Headline, &t.Image, &t.PartnerImage, &t.PartnerText); err != nil {
if err := DBQueryRow("SELECT id_theme, name, locked, url_id, path, authors, intro, headline, image, partner_img, partner_text FROM themes WHERE name=?", name).Scan(&t.Id, &t.Name, &t.Locked, &t.URLId, &t.Path, &t.Authors, &t.Intro, &t.Headline, &t.Image, &t.PartnerImage, &t.PartnerText); err != nil {
return t, err
}
@ -79,14 +80,14 @@ func GetThemeByName(name string) (*Theme, error) {
// GetThemeByPath retrieves a Theme from its dirname
func GetThemeByPath(dirname string) (*Theme, error) {
t := &Theme{}
err := DBQueryRow("SELECT id_theme, name, url_id, path, authors, intro, headline, image, partner_img, partner_href, partner_text FROM themes WHERE path=?", dirname).Scan(&t.Id, &t.Name, &t.URLId, &t.Path, &t.Authors, &t.Intro, &t.Headline, &t.Image, &t.PartnerImage, &t.PartnerLink, &t.PartnerText)
err := DBQueryRow("SELECT id_theme, name, locked, url_id, path, authors, intro, headline, image, partner_img, partner_href, partner_text FROM themes WHERE path=?", dirname).Scan(&t.Id, &t.Name, &t.Locked, &t.URLId, &t.Path, &t.Authors, &t.Intro, &t.Headline, &t.Image, &t.PartnerImage, &t.PartnerLink, &t.PartnerText)
return t, err
}
// CreateTheme creates and fills a new struct Theme and registers it into the database.
func CreateTheme(theme *Theme) (*Theme, error) {
if res, err := DBExec("INSERT INTO themes (name, url_id, authors, path, intro, headline, image, partner_img, partner_href, partner_text) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", theme.Name, theme.URLId, theme.Authors, theme.Path, theme.Intro, theme.Headline, theme.Image, theme.PartnerImage, theme.PartnerLink, theme.PartnerText); err != nil {
if res, err := DBExec("INSERT INTO themes (name, locked, url_id, authors, path, intro, headline, image, partner_img, partner_href, partner_text) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", theme.Name, theme.Locked, theme.URLId, theme.Authors, theme.Path, theme.Intro, theme.Headline, theme.Image, theme.PartnerImage, theme.PartnerLink, theme.PartnerText); err != nil {
return nil, err
} else if theme.Id, err = res.LastInsertId(); err != nil {
return nil, err
@ -107,7 +108,7 @@ func (t *Theme) FixURLId() bool {
// Update applies modifications back to the database.
func (t *Theme) Update() (int64, error) {
if res, err := DBExec("UPDATE themes SET name = ?, url_id = ?, authors = ?, path = ?, intro = ?, headline = ?, image = ?, partner_img = ?, partner_href = ?, partner_text = ? WHERE id_theme = ?", t.Name, t.URLId, t.Authors, t.Path, t.Intro, t.Headline, t.Image, t.PartnerImage, t.PartnerLink, t.PartnerText, t.Id); err != nil {
if res, err := DBExec("UPDATE themes SET name = ?, locked = ?, url_id = ?, authors = ?, path = ?, intro = ?, headline = ?, image = ?, partner_img = ?, partner_href = ?, partner_text = ? WHERE id_theme = ?", t.Name, t.Locked, t.URLId, t.Authors, t.Path, t.Intro, t.Headline, t.Image, t.PartnerImage, t.PartnerLink, t.PartnerText, t.Id); err != nil {
return 0, err
} else if nb, err := res.RowsAffected(); err != nil {
return 0, err

View File

@ -25,6 +25,7 @@ type exportedExercice struct {
type exportedTheme struct {
Name string `json:"name"`
URLId string `json:"urlid"`
Locked bool `json:"locked,omitempty"`
Authors string `json:"authors"`
Headline string `json:"headline,omitempty"`
Intro string `json:"intro"`
@ -42,11 +43,16 @@ func ExportThemes() (interface{}, error) {
} else {
ret := map[string]exportedTheme{}
for _, theme := range themes {
exos := []exportedExercice{}
if exercices, err := theme.GetExercices(); err != nil {
return nil, err
} else {
exos := []exportedExercice{}
for _, exercice := range exercices {
if exercice.Disabled {
continue
}
tags, _ := exercice.GetTags()
exos = append(exos, exportedExercice{
exercice.Id,
@ -60,29 +66,30 @@ func ExportThemes() (interface{}, error) {
exercice.TriedTeamCount(),
})
}
}
imgpath := ""
if len(theme.Image) > 0 {
imgpath = path.Join(FilesDir, theme.Image)
}
imgpath := ""
if len(theme.Image) > 0 {
imgpath = path.Join(FilesDir, theme.Image)
}
partnerImgpath := ""
if len(theme.PartnerImage) > 0 {
partnerImgpath = path.Join(FilesDir, theme.PartnerImage)
}
partnerImgpath := ""
if len(theme.PartnerImage) > 0 {
partnerImgpath = path.Join(FilesDir, theme.PartnerImage)
}
ret[fmt.Sprintf("%d", theme.Id)] = exportedTheme{
theme.Name,
theme.URLId,
theme.Authors,
theme.Headline,
strings.Replace(theme.Intro, "$FILES$", FilesDir, -1),
imgpath,
partnerImgpath,
theme.PartnerLink,
theme.PartnerText,
exos,
}
ret[fmt.Sprintf("%d", theme.Id)] = exportedTheme{
theme.Name,
theme.URLId,
theme.Locked,
theme.Authors,
theme.Headline,
strings.Replace(theme.Intro, "$FILES$", FilesDir, -1),
imgpath,
partnerImgpath,
theme.PartnerLink,
theme.PartnerText,
exos,
}
}