Split mail functionality in a separate package, improve multiuser features
This commit is contained in:
parent
b2348b2df9
commit
768c8b84ca
10 changed files with 181 additions and 47 deletions
79
mail/mail.go
Normal file
79
mail/mail.go
Normal file
|
@ -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
|
||||
|
||||
}
|
34
orm/creator.go
Normal file
34
orm/creator.go
Normal file
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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("<a href=\"%s\">%s</a>", 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
|
||||
|
||||
|
|
|
@ -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}}<small class="form-text text-muted">{{.options.help}}</small>{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
|
|
@ -6,12 +6,20 @@
|
|||
{{template "show_header" dict "title" (.Data|string) "updatePath" (.Data.ID|update "Participant") "deletePath" (.Data.ID|delete "Participant")}}
|
||||
|
||||
<h2 class="karmen-relation-header">Informazioni generali</h2>
|
||||
<p>
|
||||
Il nome utente del partecipante è {{if .Data.User}}<strong>{{.Data.User.Username}}</strong>{{end}}
|
||||
</p>
|
||||
<p>
|
||||
La sua password è {{if .Data.User}}<strong>{{.Data.User.Password}}</strong>{{end}}
|
||||
</p>
|
||||
<dl class="row">
|
||||
<dt class="col-sm-3">Username</dt>
|
||||
<dd class="col-sm-9">{{.Data.User.Username}}</dd>
|
||||
<dt class="col-sm-3">Password</dt>
|
||||
<dd class="col-sm-9">{{.Data.User.Password}}</dd>
|
||||
{{/*if $user:=.Data.CreatedBy*/}}
|
||||
<!-- <dt class="col-sm-3">Creato da</dt> -->
|
||||
<!-- <dd class="col-sm-9">{{/*$user.Username*/}}</dd> -->
|
||||
{{/*end*/}}
|
||||
<dt class="col-sm-3">IP del creatore</dt>
|
||||
<dd class="col-sm-9">{{.Data.CreatorIP}}</dd>
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -24,21 +24,22 @@
|
|||
{{template "show_header" dict "title" (.Data|string|trim) "updatePath" (.Data.ID|update "School") "deletePath" (.Data.ID|delete "School")}}
|
||||
|
||||
<h2 class="karmen-relation-header">Informazioni sulla scuola</h2>
|
||||
<p>La denominazione della scuola è <strong>{{.Data.Name}}</strong>.</p>
|
||||
<p>Il codice meccanografico della scuola è <strong>{{.Data.Code}}</strong>.</p>
|
||||
<p>La mail istituzionale della scuola è <strong>{{.Data.Email}}</strong>.
|
||||
<p>
|
||||
<dl class="row">
|
||||
<dt class="col-sm-3">Denominazione</dt>
|
||||
<dd class="col-sm-9">{{.Data.Name}}</dd>
|
||||
<dt class="col-sm-3">Codice meccanografico</dt>
|
||||
<dd class="col-sm-9">{{.Data.Code}}</dd>
|
||||
<dt class="col-sm-3">Email</dt>
|
||||
<dd class="col-sm-9">{{.Data.Email}}</dd>
|
||||
{{if .Data.EmailSentDate}}
|
||||
Una mail contenente le credenziali di accesso per l'iscrizione
|
||||
degli studenti è stata inviata a questo indirizzo in data
|
||||
<strong>{{.Data.EmailSentDate|prettyDate}}</strong> alle
|
||||
ore <strong>{{.Data.EmailSentDate|convertTime}}</strong>.
|
||||
{{else}}
|
||||
A questo indirizzo non è stata inviata ancora nessuna mail.
|
||||
<dt class="col-sm-3">Data invio credenziali</dt>
|
||||
<dd class="col-sm-9">{{.Data.EmailSentDate|prettyDate}} alle ore {{.Data.EmailSentDate|prettyTime}}</dd>
|
||||
{{end}}
|
||||
</p>
|
||||
<p>Il nome utente della scuola è {{if .Data.User}}<strong>{{.Data.User.Username}}</strong>{{end}}</p>
|
||||
<p>La sua password è {{if .Data.User}}<strong>{{.Data.User.Password}}</strong>{{end}}</p>
|
||||
<dt class="col-sm-3">Username</dt>
|
||||
<dd class="col-sm-9">{{.Data.User.Username}}</dd>
|
||||
<dt class="col-sm-3">Password</dt>
|
||||
<dd class="col-sm-9">{{.Data.User.Password}}</dd>
|
||||
</dl>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
|
|
Loading…
Reference in a new issue