From 768c8b84cae42ad5844d97876fb21b69b1f84851 Mon Sep 17 00:00:00 2001 From: Andrea Fazzi Date: Fri, 6 Dec 2019 10:59:00 +0100 Subject: [PATCH] Split mail functionality in a separate package, improve multiuser features --- mail/mail.go | 79 +++++++++++++++++++++++++++ orm/creator.go | 34 ++++++++++++ orm/participant.go | 4 ++ orm/school.go | 45 ++++++++------- orm/user.go | 3 - renderer/funcmap.go | 12 +++- templates/layout/input.html.tpl | 2 +- templates/participants_show.html.tpl | 20 +++++-- templates/schools_add_update.html.tpl | 2 +- templates/schools_show.html.tpl | 27 ++++----- 10 files changed, 181 insertions(+), 47 deletions(-) create mode 100644 mail/mail.go create mode 100644 orm/creator.go diff --git a/mail/mail.go b/mail/mail.go new file mode 100644 index 00000000..e26d0341 --- /dev/null +++ b/mail/mail.go @@ -0,0 +1,79 @@ +package mail + +import ( + "bytes" + "crypto/tls" + "text/template" + + "git.andreafazzi.eu/andrea/oef/config" + + "gopkg.in/gomail.v2" +) + +type Subscriber interface { + NameForMail() string + Username() string + Password() string + To() string +} + +var ( + mail = ` +Spettabile {{.NameForMail}}, + +grazie per l'interesse manifestato per le Olimpiadi di Economia e Finanza. + +Di seguito riportiamo le credenziali di accesso tramite le quali potrà gestire le iscrizioni dei suoi studenti alla competizione (Fase Regionale). + +username: {{.Username}} +password: {{.Password}} + +Per accedere alla pagina di login occorrerà seguire questo link + +https://iscrizioni.olimpiadi-economiaefinanza.it/ + +ed inserire le credenziali riportate sopra (si consiglia di effettuare un copia/incolla). + +Cordialmente, +Lo Staff delle OEF 2020. +` + subject = "[OEF2020] - Credenziali di accesso della scuola" + + mailTpl *template.Template +) + +func init() { + mailTpl = template.Must(template.New("subscription_mail").Parse(mail)) +} + +func SendSubscriptionMail(rcv Subscriber) error { + var body bytes.Buffer + + m := gomail.NewMessage() + m.SetHeader("Subject", subject) + m.SetHeader("From", config.Config.Smtp.From) + m.SetHeader("To", rcv.To()) + m.SetHeader("Bcc", config.Config.Smtp.Bcc) + + err := mailTpl.Execute(&body, rcv) + if err != nil { + return err + } + + m.SetBody("text/plain", body.String()) + + dialer := gomail.NewDialer( + config.Config.Smtp.Host, + config.Config.Smtp.Port, + config.Config.Smtp.Username, + config.Config.Smtp.Password, + ) + dialer.TLSConfig = &tls.Config{InsecureSkipVerify: true} + + if err := dialer.DialAndSend(m); err != nil { + return err + } + + return nil + +} diff --git a/orm/creator.go b/orm/creator.go new file mode 100644 index 00000000..537c9152 --- /dev/null +++ b/orm/creator.go @@ -0,0 +1,34 @@ +package orm + +import ( + "net/http" + + "github.com/dgrijalva/jwt-go" +) + +type Creator struct { + CreatorID string + CreatorRole string + CreatorIP string +} + +func NewCreator(r *http.Request) *Creator { + var claims jwt.MapClaims + if r.Context().Value("user") != nil { + claims = r.Context().Value("user").(*jwt.Token).Claims.(jwt.MapClaims) + } + + return &Creator{ + CreatorID: claims["user_id"].(string), + CreatorRole: claims["role"].(string), + CreatorIP: r.RemoteAddr, + } +} + +func (c *Creator) CreatedBy() (*User, error) { + var user User + if err := DB().First(&user, c.CreatorID).Error; err != nil { + return nil, err + } + return &user, nil +} diff --git a/orm/participant.go b/orm/participant.go index 6f762c2e..36e42f7a 100644 --- a/orm/participant.go +++ b/orm/participant.go @@ -15,6 +15,8 @@ import ( type Participant struct { gorm.Model + *Creator + UserID uint Firstname string @@ -143,6 +145,8 @@ func (model *Participant) Create(args map[string]string, w http.ResponseWriter, return nil, err } + participant.Creator = NewCreator(r) + participant, err = CreateParticipant(participant) if err != nil { return nil, err diff --git a/orm/school.go b/orm/school.go index 6321825d..26e9d6e3 100644 --- a/orm/school.go +++ b/orm/school.go @@ -1,21 +1,21 @@ package orm import ( - "crypto/tls" "fmt" "net/http" "strings" "time" - "git.andreafazzi.eu/andrea/oef/config" + "git.andreafazzi.eu/andrea/oef/mail" "git.andreafazzi.eu/andrea/oef/renderer" "github.com/jinzhu/gorm" - "gopkg.in/gomail.v2" ) type School struct { gorm.Model + *Creator + Name string Email string Code string @@ -33,13 +33,25 @@ func (model *School) String() string { return fmt.Sprintf("%s (%s)", model.Code, model.Name) } -func (model *School) username() string { +func (model *School) NameForMail() string { + return fmt.Sprintf("%s (%s)", model.Name, model.Code) +} + +func (model *School) Username() string { return strings.ToUpper(model.Code) } +func (model *School) Password() string { + return model.User.Password +} + +func (model *School) To() string { + return model.Email +} + func (model *School) exists() (*User, error) { var user User - if err := DB().First(&user, &User{Username: model.username()}).Error; err != nil && err != gorm.ErrRecordNotFound { + if err := DB().First(&user, &User{Username: model.Username()}).Error; err != nil && err != gorm.ErrRecordNotFound { return nil, err } else if err == gorm.ErrRecordNotFound { return nil, nil @@ -50,7 +62,7 @@ func (model *School) exists() (*User, error) { func (model *School) BeforeSave(tx *gorm.DB) error { var user User if err := tx.FirstOrCreate(&user, &User{ - Username: model.username(), + Username: model.Username(), Role: "school", }).Error; err != nil { return err @@ -67,21 +79,10 @@ func (model *School) AfterDelete(tx *gorm.DB) error { } func (model *School) AfterCreate(tx *gorm.DB) error { - m := gomail.NewMessage() - m.SetHeader("From", config.Config.Smtp.From) - m.SetHeader("To", model.Email) - m.SetHeader("Bcc", config.Config.Smtp.Bcc) - m.SetBody("text/plain", "SMTP test message.") - - dialer := gomail.NewDialer( - config.Config.Smtp.Host, - config.Config.Smtp.Port, - config.Config.Smtp.Username, - config.Config.Smtp.Password, - ) - dialer.TLSConfig = &tls.Config{InsecureSkipVerify: true} - - if err := dialer.DialAndSend(m); err != nil { + if err := tx.Preload("User").First(model).Error; err != nil { + return err + } + if err := mail.SendSubscriptionMail(model); err != nil { return err } @@ -117,6 +118,8 @@ func (model *School) Create(args map[string]string, w http.ResponseWriter, r *ht return nil, err } + school.Creator = NewCreator(r) + school, err = CreateSchool(school) if err != nil { return nil, err diff --git a/orm/user.go b/orm/user.go index 32eb127f..b99b967b 100644 --- a/orm/user.go +++ b/orm/user.go @@ -45,9 +45,6 @@ func (model *User) BeforeSave(tx *gorm.DB) error { func (model *User) Create(args map[string]string, w http.ResponseWriter, 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) diff --git a/renderer/funcmap.go b/renderer/funcmap.go index 0cf52c0e..51e0288a 100644 --- a/renderer/funcmap.go +++ b/renderer/funcmap.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "html/template" - "log" "net/url" "reflect" "strings" @@ -25,6 +24,7 @@ var ( "convertDate": convertDate, "convertTime": convertTime, "prettyDate": prettyDate, + "prettyTime": prettyTime, "modelPath": modelPath, "dict": dict, "yaml": yaml, @@ -52,6 +52,7 @@ var ( "isAdmin": isAdmin, "isParticipant": isParticipant, "isSubscriber": isSubscriber, + "attr": attr, } ) @@ -118,6 +119,10 @@ func html(content string) template.HTML { return template.HTML(content) } +func attr(attr string) template.HTMLAttr { + return template.HTMLAttr(attr) +} + func anchor(text, url string) template.HTML { return template.HTML(fmt.Sprintf("%s", url, text)) } @@ -155,7 +160,6 @@ func yaml(content string) (interface{}, error) { if err != nil { return nil, err } - log.Println(result) return result, nil } @@ -235,6 +239,10 @@ func convertTime(value interface{}) string { return fmt.Sprintf("%02d:%02d", t.Hour(), t.Minute()) } +func prettyTime(value interface{}) string { + return convertTime(value) +} + func modelPath(model string, action string, id uint) string { var q template.URL diff --git a/templates/layout/input.html.tpl b/templates/layout/input.html.tpl index d1ed457f..58772691 100644 --- a/templates/layout/input.html.tpl +++ b/templates/layout/input.html.tpl @@ -5,7 +5,7 @@ name="{{.options.name}}" class="{{if .options.inputClass}}{{.options.inputClass}}{{else}}form-control{{end}}" id="{{.options.id}}" - placeholder="{{.options.placeholder}}" {{if .update}}value="{{.value}}"{{end}} {{if .options.otherAttrs}}{{.options.otherAttrs|html}}{{end}} {{if .options.required}}required{{end}}> + placeholder="{{.options.placeholder}}" {{if .update}}value="{{.value}}"{{end}} {{if .options.otherAttrs}}{{.options.otherAttrs|attr}}{{end}} {{if .options.required}}required{{end}}> {{if .options.help}}{{.options.help}}{{end}} {{end}} diff --git a/templates/participants_show.html.tpl b/templates/participants_show.html.tpl index 388329ea..b56e50e3 100644 --- a/templates/participants_show.html.tpl +++ b/templates/participants_show.html.tpl @@ -6,12 +6,20 @@ {{template "show_header" dict "title" (.Data|string) "updatePath" (.Data.ID|update "Participant") "deletePath" (.Data.ID|delete "Participant")}}

Informazioni generali

-

- Il nome utente del partecipante è {{if .Data.User}}{{.Data.User.Username}}{{end}} -

-

- La sua password è {{if .Data.User}}{{.Data.User.Password}}{{end}} -

+
+
Username
+
{{.Data.User.Username}}
+
Password
+
{{.Data.User.Password}}
+ {{/*if $user:=.Data.CreatedBy*/}} + + + {{/*end*/}} +
IP del creatore
+
{{.Data.CreatorIP}}
+ + +
diff --git a/templates/schools_add_update.html.tpl b/templates/schools_add_update.html.tpl index abee617a..5a2396c1 100644 --- a/templates/schools_add_update.html.tpl +++ b/templates/schools_add_update.html.tpl @@ -38,7 +38,7 @@ placeholder: "Inserire il codice meccanografico" type: "text" inputClass: "form-control uppercase" - otherAttrs: "maxlength=10" + otherAttrs: "maxlength=10 minlength=10" required: "true" `}} {{template "input" dict "options" ($options|yaml) "value" (.Data|field "Code") "update" $update}} diff --git a/templates/schools_show.html.tpl b/templates/schools_show.html.tpl index cfc92db9..8a2be46e 100644 --- a/templates/schools_show.html.tpl +++ b/templates/schools_show.html.tpl @@ -24,21 +24,22 @@ {{template "show_header" dict "title" (.Data|string|trim) "updatePath" (.Data.ID|update "School") "deletePath" (.Data.ID|delete "School")}}

Informazioni sulla scuola

-

La denominazione della scuola è {{.Data.Name}}.

-

Il codice meccanografico della scuola è {{.Data.Code}}.

-

La mail istituzionale della scuola è {{.Data.Email}}. -

+

+
Denominazione
+
{{.Data.Name}}
+
Codice meccanografico
+
{{.Data.Code}}
+
Email
+
{{.Data.Email}}
{{if .Data.EmailSentDate}} - Una mail contenente le credenziali di accesso per l'iscrizione - degli studenti è stata inviata a questo indirizzo in data - {{.Data.EmailSentDate|prettyDate}} alle - ore {{.Data.EmailSentDate|convertTime}}. - {{else}} - A questo indirizzo non è stata inviata ancora nessuna mail. +
Data invio credenziali
+
{{.Data.EmailSentDate|prettyDate}} alle ore {{.Data.EmailSentDate|prettyTime}}
{{end}} -

-

Il nome utente della scuola è {{if .Data.User}}{{.Data.User.Username}}{{end}}

-

La sua password è {{if .Data.User}}{{.Data.User.Password}}{{end}}

+
Username
+
{{.Data.User.Username}}
+
Password
+
{{.Data.User.Password}}
+