Decoupling user credential from participant model

This commit is contained in:
Andrea Fazzi 2019-11-29 12:44:43 +01:00
parent 17f1be188e
commit 43dd305835
10 changed files with 475 additions and 37 deletions

View file

@ -15,11 +15,11 @@ import (
"github.com/gorilla/sessions" "github.com/gorilla/sessions"
) )
type User struct { type UserToken struct {
Name string Username string
Admin bool Admin bool
Role string Role string
UserID string UserID string
} }
var ( var (
@ -88,17 +88,17 @@ func loginHandler() http.Handler {
// FIXME: This is an hack for fast prototyping: users should have // FIXME: This is an hack for fast prototyping: users should have
// their own table on DB. // their own table on DB.
func checkCredential(username string, password string) (*User, error) { func checkCredential(username string, password string) (*UserToken, error) {
var participant orm.Participant var participant orm.Participant
if username == config.Config.Admin.Username && password == config.Config.Admin.Password { if username == config.Config.Admin.Username && password == config.Config.Admin.Password {
return &User{username, true, "administrator", "0"}, nil return &UserToken{username, true, "administrator", "0"}, nil
} }
if err := orm.DB().Where("username = ? AND password = ?", username, password).First(&participant).Error; err != nil { if err := orm.DB().Where("username = ? AND password = ?", username, password).First(&participant).Error; err != nil {
return nil, errors.New("Authentication failed!") return nil, errors.New("Authentication failed!")
} else { } else {
return &User{username, false, "participant", strconv.Itoa(int(participant.ID))}, nil return &UserToken{username, false, "participant", strconv.Itoa(int(participant.ID))}, nil
} }
} }
@ -112,7 +112,7 @@ func getToken(username string, password string) ([]byte, error) {
/* Set token claims */ /* Set token claims */
claims := make(map[string]interface{}) claims := make(map[string]interface{})
claims["admin"] = user.Admin claims["admin"] = user.Admin
claims["name"] = user.Name claims["username"] = user.Username
claims["role"] = user.Role claims["role"] = user.Role
claims["user_id"] = user.UserID claims["user_id"] = user.UserID
claims["exp"] = time.Now().Add(time.Hour * 24).Unix() claims["exp"] = time.Now().Add(time.Hour * 24).Unix()
@ -141,7 +141,7 @@ func tokenHandler() http.Handler {
/* Set token claims */ /* Set token claims */
claims := make(map[string]interface{}) claims := make(map[string]interface{})
claims["admin"] = true claims["admin"] = true
claims["name"] = user.Name claims["name"] = user.Username
claims["exp"] = time.Now().Add(time.Hour * 24).Unix() claims["exp"] = time.Now().Add(time.Hour * 24).Unix()
/* Create the token */ /* Create the token */
@ -154,7 +154,7 @@ func tokenHandler() http.Handler {
} }
w.Header().Set("Content-Type", "application/json; charset=utf-8") w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Write([]byte(fmt.Sprintf("{\"Token\":\"%s\",\"User\":\"%s\"}", tokenString, user.Name))) w.Write([]byte(fmt.Sprintf("{\"Token\":\"%s\",\"User\":\"%s\"}", tokenString, user.Username)))
} }
return http.HandlerFunc(fn) return http.HandlerFunc(fn)
} }

View file

@ -31,6 +31,7 @@ var (
&orm.Contest{}, &orm.Contest{},
&orm.Participant{}, &orm.Participant{},
&orm.Response{}, &orm.Response{},
&orm.User{},
} }
) )

201
oef_dev.sql Normal file
View file

@ -0,0 +1,201 @@
-- MariaDB dump 10.17 Distrib 10.4.8-MariaDB, for debian-linux-gnu (x86_64)
--
-- Host: localhost Database: oef_test
-- ------------------------------------------------------
-- Server version 10.4.8-MariaDB-1:10.4.8+maria~bionic
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Table structure for table `answers`
--
DROP TABLE IF EXISTS `answers`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `answers` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
`text` varchar(255) DEFAULT NULL,
`correct` tinyint(1) DEFAULT NULL,
`question_id` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_answers_deleted_at` (`deleted_at`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `answers`
--
LOCK TABLES `answers` WRITE;
/*!40000 ALTER TABLE `answers` DISABLE KEYS */;
INSERT INTO `answers` VALUES (1,'2019-11-13 15:44:39','2019-11-13 15:44:39',NULL,'la quantità di moneta che viene richiesta dalle imprese sotto forma di prestiti richiesti al sistema bancario',0,1),(2,'2019-11-14 11:48:06','2019-11-14 11:48:06',NULL,'la quantità di moneta richiesta dalla Banca Centrale quando mette in vendita dei titoli per ridurre la moneta in circolazione',0,1),(3,'2019-11-14 11:48:28','2019-11-14 11:48:28',NULL,'la quantità di moneta richiesta dalle famiglie per mantenere in forma liquida i loro risparmi',0,1),(4,'2019-11-14 11:49:05','2019-11-14 12:21:09',NULL,'la quantità di moneta richiesta dai soggetti del sistema economico per transazioni, per ragioni speculative o prudenziali o per altri motivi',1,1),(5,'2019-11-15 10:17:49','2019-11-15 10:17:49',NULL,'elevata differenziazione dei prodotti offerti',0,2),(6,'2019-11-15 10:18:14','2019-11-15 10:18:53',NULL,'trasparenza delle informazioni',1,2),(7,'2019-11-15 10:18:29','2019-11-15 10:18:29',NULL,'presenza di un solo consumatore',0,2),(8,'2019-11-15 10:18:44','2019-11-15 10:18:44',NULL,'presenza di un numero limitato di grandi produttori',0,2),(9,'2019-11-15 10:23:11','2019-11-15 10:23:11',NULL,'un ciclo economico',0,3),(10,'2019-11-15 10:23:24','2019-11-15 10:23:35',NULL,'l\'attività di trasformazione materiale di beni e servizi (input) in altri (output) al fine di accrescerne l\'utilità',1,3),(11,'2019-11-15 10:23:47','2019-11-15 10:23:47',NULL,'l\'insieme dei beni di produzione',0,3),(12,'2019-11-15 10:23:59','2019-11-15 10:23:59',NULL,'il risultato del lavoro dei dipendenti dell\'impresa',0,3);
/*!40000 ALTER TABLE `answers` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `contests`
--
DROP TABLE IF EXISTS `contests`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `contests` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`category` varchar(255) DEFAULT NULL,
`start_date` timestamp NULL DEFAULT NULL,
`end_date` timestamp NULL DEFAULT NULL,
`start_time` timestamp NULL DEFAULT NULL,
`end_time` timestamp NULL DEFAULT NULL,
`date` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_contests_deleted_at` (`deleted_at`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `contests`
--
LOCK TABLES `contests` WRITE;
/*!40000 ALTER TABLE `contests` DISABLE KEYS */;
INSERT INTO `contests` VALUES (1,'2019-11-14 10:02:17','2019-11-18 15:57:14',NULL,'Regionale JUNIOR','','0000-00-00 00:00:00','0000-00-00 00:00:00','2020-04-01 10:00:00','2020-04-01 11:00:00','2020-04-01 10:00:00'),(2,'2019-11-15 10:15:57','2019-11-18 15:58:55',NULL,'Test Diagnostico','',NULL,NULL,'2019-11-15 13:00:00','2019-11-15 14:00:00','2019-11-15 13:00:00');
/*!40000 ALTER TABLE `contests` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `participants`
--
DROP TABLE IF EXISTS `participants`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `participants` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
`firstname` varchar(255) DEFAULT NULL,
`lastname` varchar(255) DEFAULT NULL,
`username` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_participants_deleted_at` (`deleted_at`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `participants`
--
LOCK TABLES `participants` WRITE;
/*!40000 ALTER TABLE `participants` DISABLE KEYS */;
INSERT INTO `participants` VALUES (1,'2019-11-15 10:02:46','2019-11-18 15:58:55',NULL,'Mario','Rossi','mario.rossi','EqAs1z7M'),(2,'2019-11-18 12:00:07','2019-11-18 15:57:14',NULL,'Luigi','BIANCHI','luigi.bianchi','FpWJj89n'),(3,'2019-11-18 12:01:55','2019-11-18 12:12:26',NULL,'Francesco','VERDI','francesco.verdi','MiJ9Ig4L'),(4,'2019-11-18 15:57:36','2019-11-18 15:57:36',NULL,'Franco','neri','franco.neri','YtGGU28p');
/*!40000 ALTER TABLE `participants` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `questions`
--
DROP TABLE IF EXISTS `questions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `questions` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
`text` varchar(255) DEFAULT NULL,
`contest_id` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_questions_deleted_at` (`deleted_at`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `questions`
--
LOCK TABLES `questions` WRITE;
/*!40000 ALTER TABLE `questions` DISABLE KEYS */;
INSERT INTO `questions` VALUES (1,'2019-11-13 14:45:17','2019-11-14 12:21:09',NULL,'Cosa si intende per domanda di moneta?',1),(2,'2019-11-15 10:17:24','2019-11-15 10:18:53',NULL,'È una caratteristica della concorrenza perfetta',2),(3,'2019-11-15 10:21:14','2019-11-15 10:23:35',NULL,'La produzione è',2);
/*!40000 ALTER TABLE `questions` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `responses`
--
DROP TABLE IF EXISTS `responses`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `responses` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_responses_deleted_at` (`deleted_at`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `responses`
--
LOCK TABLES `responses` WRITE;
/*!40000 ALTER TABLE `responses` DISABLE KEYS */;
/*!40000 ALTER TABLE `responses` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `subscriptions`
--
DROP TABLE IF EXISTS `subscriptions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `subscriptions` (
`participant_id` int(10) unsigned NOT NULL,
`contest_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`participant_id`,`contest_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `subscriptions`
--
LOCK TABLES `subscriptions` WRITE;
/*!40000 ALTER TABLE `subscriptions` DISABLE KEYS */;
INSERT INTO `subscriptions` VALUES (1,2),(2,1),(2,2),(3,2);
/*!40000 ALTER TABLE `subscriptions` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2019-11-18 16:05:30

View file

@ -8,17 +8,19 @@ import (
"git.andreafazzi.eu/andrea/oef/renderer" "git.andreafazzi.eu/andrea/oef/renderer"
"github.com/jinzhu/gorm" "github.com/jinzhu/gorm"
"github.com/sethvargo/go-password/password"
) )
type Participant struct { type Participant struct {
gorm.Model gorm.Model
UserID uint
Firstname string Firstname string
Lastname string Lastname string
Username string FiscalCode string
Password string
User *User
Responses []*Response Responses []*Response
@ -35,19 +37,8 @@ func (model *Participant) sanitize(s string) string {
return r.Replace(lower) return r.Replace(lower)
} }
func (model *Participant) genUsername() error { func (model *Participant) username() string {
model.Username = strings.Join([]string{model.sanitize(model.Firstname), model.sanitize(model.Lastname)}, ".") return strings.Join([]string{model.sanitize(model.Firstname), model.sanitize(model.Lastname)}, ".")
return nil
}
func (model *Participant) genPassword() error {
password, err := password.Generate(8, 2, 0, false, true)
if err != nil {
return err
}
model.Password = password
return nil
} }
func (model *Participant) GetID() uint { return model.ID } func (model *Participant) GetID() uint { return model.ID }
@ -57,14 +48,11 @@ func (model *Participant) String() string {
} }
func (model *Participant) BeforeSave(tx *gorm.DB) error { func (model *Participant) BeforeSave(tx *gorm.DB) error {
if err := model.genUsername(); err != nil { var user User
if err := tx.FirstOrCreate(&user, &User{Username: model.username()}).Error; err != nil {
return err return err
} }
if model.Password == "" { model.UserID = user.ID
if err := model.genPassword(); err != nil {
return err
}
}
return nil return nil
} }
@ -84,6 +72,13 @@ func (model *Participant) AfterSave(tx *gorm.DB) error {
return nil return nil
} }
func (model *Participant) AfterDelete(tx *gorm.DB) error {
if err := tx.Unscoped().Delete(model.User).Error; err != nil {
return err
}
return nil
}
func (model *Participant) Create(args map[string]string, r *http.Request) (interface{}, error) { func (model *Participant) Create(args map[string]string, r *http.Request) (interface{}, error) {
if r.Method == "GET" { if r.Method == "GET" {
participant := new(Participant) participant := new(Participant)
@ -110,7 +105,7 @@ func (model *Participant) Read(args map[string]string, r *http.Request) (interfa
id := args["id"] id := args["id"]
if err := DB().Preload("Responses").Preload("Contests").First(&participant, id).Error; err != nil { if err := DB().Preload("User").Preload("Responses").Preload("Contests").First(&participant, id).Error; err != nil {
return nil, err return nil, err
} }
@ -187,6 +182,9 @@ func (model *Participant) Delete(args map[string]string, r *http.Request) (inter
} }
func CreateParticipant(participant *Participant) (*Participant, error) { func CreateParticipant(participant *Participant) (*Participant, error) {
if err := DB().Where(participant.ContestIDs).Find(&participant.Contests).Error; err != nil {
return nil, err
}
if err := DB().Create(participant).Error; err != nil { if err := DB().Create(participant).Error; err != nil {
return nil, err return nil, err
} }

147
orm/user.go Normal file
View file

@ -0,0 +1,147 @@
package orm
import (
"net/http"
"git.andreafazzi.eu/andrea/oef/renderer"
"github.com/jinzhu/gorm"
"github.com/sethvargo/go-password/password"
)
type User struct {
gorm.Model
Username string
Password string
Role string
}
func (model *User) GetID() uint { return model.ID }
func (model *User) String() string {
return "" // Please implement this.
}
func genPassword() (string, error) {
password, err := password.Generate(8, 2, 0, false, true)
if err != nil {
return "", err
}
return password, nil
}
func (model *User) BeforeSave(tx *gorm.DB) error {
if model.Password == "" {
password, err := genPassword()
if err != nil {
return err
}
model.Password = password
}
return nil
}
func (model *User) Create(args map[string]string, r *http.Request) (interface{}, error) {
if r.Method == "GET" {
user := new(User)
// if err := DB().Find(&user.AllContests).Error; err != nil {
// return nil, err
// }
return user, nil
} else {
user := new(User)
err := renderer.Decode(user, r)
if err != nil {
return nil, err
}
user, err = CreateUser(user)
if err != nil {
return nil, err
}
return user, nil
}
}
func (model *User) Read(args map[string]string, r *http.Request) (interface{}, error) {
var user User
id := args["id"]
if err := DB(). /*.Preload("Something")*/ First(&user, id).Error; err != nil {
return nil, err
}
return &user, nil
}
func (model *User) ReadAll(args map[string]string, r *http.Request) (interface{}, error) {
var users []*User
if err := DB(). /*.Preload("Something")*/ Order("created_at").Find(&users).Error; err != nil {
return nil, err
}
return users, nil
}
func (model *User) Update(args map[string]string, r *http.Request) (interface{}, error) {
if r.Method == "GET" {
result, err := model.Read(args, r)
if err != nil {
return nil, err
}
user := result.(*User)
// if err := DB().Find(&user.AllElements).Error; err != nil {
// return nil, err
// }
// user.SelectedElement = make(map[uint]string)
// user.SelectedElement[user.ElementID] = "selected"
return user, nil
} else {
user, err := model.Read(args, nil)
if err != nil {
return nil, err
}
err = renderer.Decode(user, r)
if err != nil {
return nil, err
}
_, err = SaveUser(user)
if err != nil {
return nil, err
}
user, err = model.Read(args, nil)
if err != nil {
return nil, err
}
return user.(*User), nil
}
}
func (model *User) Delete(args map[string]string, r *http.Request) (interface{}, error) {
user, err := model.Read(args, nil)
if err != nil {
return nil, err
}
if err := DB().Unscoped().Delete(user.(*User)).Error; err != nil {
return nil, err
}
return user.(*User), nil
}
func CreateUser(user *User) (*User, error) {
if err := DB().Create(user).Error; err != nil {
return nil, err
}
return user, nil
}
func SaveUser(user interface{}) (interface{}, error) {
if err := DB(). /*.Omit("Something")*/ Save(user).Error; err != nil {
return nil, err
}
return user, nil
}

View file

@ -30,6 +30,9 @@
</div> </div>
</div> </div>
{{$options := ` { name: "FiscalCode",id: "participant_fiscalcode",label: "Codice fiscale del partecipante",placeholder: "Inserire il codice fiscale",type: "text",required: "true"} `}}
{{template "input" dict "options" ($options|yaml) "value" (.Data|field "FiscalCode") "update" $update}}
{{$options := ` { name: "contest_ids", id: "contest_ids", label: "Gare a cui il partecipante è iscritto", title: "Seleziona le gare", multiple: "true"}`}} {{$options := ` { name: "contest_ids", id: "contest_ids", label: "Gare a cui il partecipante è iscritto", title: "Seleziona le gare", multiple: "true"}`}}
{{template "select" dict "options" ($options|yaml) "data" (.Data|field "AllContests") "selected" (.Data|field "SelectedContest") "update" $update "form" $form}} {{template "select" dict "options" ($options|yaml) "data" (.Data|field "AllContests") "selected" (.Data|field "SelectedContest") "update" $update "form" $form}}

View file

@ -7,10 +7,10 @@
<h2 class="karmen-relation-header">Informazioni generali</h2> <h2 class="karmen-relation-header">Informazioni generali</h2>
<p> <p>
Il nome utente del partecipante è <strong>{{.Data.Username}}</strong> Il nome utente del partecipante è {{if .Data.User}}<strong>{{.Data.User.Username}}</strong>{{end}}
</p> </p>
<p> <p>
La sua password è <strong>{{.Data.Password}}</strong> La sua password è {{if .Data.User}}<strong>{{.Data.User.Password}}</strong>{{end}}
</p> </p>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">

33
templates/users.html.tpl Normal file
View file

@ -0,0 +1,33 @@
{{ define "content" }}
<div class="container">
{{$options := `
title: "Users"
buttonTitle: "Crea nuovo User"
`}}
{{template "read_all_header" dict "options" ($options | yaml) "lengthData" (len .Data) "modelPath" (create "User")}}
{{template "search_input"}}
{{if not .}}
{{template "display_no_elements"}}
{{else}}
<div class="list-group" id="myUL">
{{range $element := .Data}}
<a class="list-group-item list-group-item-action" href={{$element.ID|show "User"}}>
<span class="fa fa-user"></span>
{{$element|string}}
<div class="text-right">
{{$options := `noElements: "no subelements"`}}
{{/*template "small" dict "options" ($options | yaml) "data" $element.SubElements*/}}
</div>
</a>
{{end}}
{{end}}
</div>
</div>
{{ end }}

View file

@ -0,0 +1,28 @@
{{ define "content" }}
<div class="container">
{{$update := .Options.Get "update"}}
{{if $update}}
{{template "breadcrumb" toSlice "Users" (all "User") (.Data|string) (.Data.ID|show "User") "Aggiorna" "current"}}
{{else}}
{{template "breadcrumb" toSlice "Users" (all "User") "Aggiungi" "current"}}
{{end}}
{{template "add_update_header" dict "update" $update "addTitle" "Crea nuovo ELEMENTO" "updateTitle" (printf "Aggiorna ELEMENTO %s" (.Data|string))}}
{{$form := "form_add_update"}}
<form
class="needs-validation"
action="{{if $update}}{{.Data.ID|update "User"}}{{else}}{{create "User"}}{{end}}"
method="POST"
role="form"
id={{$form}}>
{{$options := ` { cancelTitle: "Annulla", saveTitle: "Salva", model: "User" } `}}
{{template "submit_cancel_buttons" dict "options" ($options|yaml) "id" (.Data|field "ID") "update" $update}}
</form>
</div>
{{ end }}

View file

@ -0,0 +1,27 @@
{{ define "content" }}
<div class="container">
{{template "breadcrumb" toSlice "ELEMENTS" (all "User") (.Data|string) "current"}}
{{template "show_header" dict "title" (.Data|string) "updatePath" (.Data.ID|update "User") "deletePath" (.Data.ID|delete "User")}}
<h2 class="karmen-relation-header">GENERAL SECTION</h2>
<div class="row">
<div class="col-md-12">
{{$options := `
title: "RELATIONS"
model: "MODEL"
icon: "ICON_CLASS"
`}}
{{$noElements := "NO ELEMENTS"}}
{{template "relation_list" dict "options" ($options|yaml) "data" .Data.RELATIONS "noElements" $noElements}}
</div>
</div>
</div>
{{ end }}