package contacts import ( "errors" "fmt" "math/rand" "net/http" "strings" "time" "github.com/jinzhu/gorm" "github.com/kirsle/golog" ) // DB is a reference to the parent app's gorm DB. var DB *gorm.DB // UseDB registers the DB from the root app. func UseDB(db *gorm.DB) { DB = db DB.AutoMigrate(&Contact{}) } var log *golog.Logger func init() { log = golog.GetLogger("blog") } // Contact is an individual contact in the address book. type Contact struct { ID int `json:"id"` Secret string `json:"secret" gorm:"unique"` // their lazy insecure login token FirstName string `json:"firstName"` LastName string `json:"lastName"` Email string `json:"email"` SMS string `json:"sms"` LastSeen time.Time `json:"lastSeen"` Created time.Time `json:"created"` Updated time.Time `json:"updated"` } // Contacts is the plurality of all contacts. type Contacts []Contact // NewContact initializes a new contact entry. func NewContact() Contact { return Contact{} } // pre-save checks. func (c *Contact) presave() { if c.Created.IsZero() { c.Created = time.Now().UTC() } if c.Updated.IsZero() { c.Updated = time.Now().UTC() } if c.Secret == "" { // Make a random ID. n := 8 var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") secret := make([]rune, n) for i := range secret { secret[i] = letters[rand.Intn(len(letters))] } c.Secret = string(secret) fmt.Printf("contact.presave: secret=%s", string(secret)) } } // Add a new contact. func Add(c *Contact) error { c.presave() log.Error("contacts.Add: %+v", c) return DB.Create(&c).Error } // All contacts from the database alphabetically sorted. func All() (Contacts, error) { result := Contacts{} err := DB.Order("last_name").Find(&result).Error return result, err } // Get a contact by ID. func Get(id int) (Contact, error) { contact := Contact{} err := DB.First(&contact, id).Error return contact, err } // Save the contact. func (c Contact) Save() error { c.presave() return DB.Update(&c).Error } // GetEmail queries a contact by email address. func GetEmail(email string) (Contact, error) { contact := Contact{} err := DB.Where("email = ?", email).First(&contact).Error return contact, err } // GetSMS queries a contact by SMS number. func GetSMS(number string) (Contact, error) { contact := Contact{} err := DB.Where("sms = ?", number).First(&contact).Error return contact, err } // GetBySecret queries a contact by their secret. func GetBySecret(secret string) (Contact, error) { contact := Contact{} err := DB.Where("secret = ?", secret).First(&contact).Error return contact, err } // Name returns a friendly name for the contact. func (c Contact) Name() string { var parts []string if c.FirstName != "" { parts = append(parts, c.FirstName) } if c.LastName != "" { parts = append(parts, c.LastName) } if len(parts) == 0 { if c.Email != "" { parts = append(parts, c.Email) } else if c.SMS != "" { parts = append(parts, c.SMS) } } return strings.Join(parts, " ") } // ParseForm accepts form data for a contact. func (c *Contact) ParseForm(r *http.Request) { c.FirstName = r.FormValue("first_name") c.LastName = r.FormValue("last_name") c.Email = strings.ToLower(r.FormValue("email")) c.SMS = r.FormValue("sms") } // Validate the contact form. func (c Contact) Validate() error { if c.Email == "" && c.SMS == "" { return errors.New("email or sms number required") } if c.FirstName == "" && c.LastName == "" { return errors.New("first or last name required") } // Check for uniqueness of email and SMS. if c.Email != "" { if _, err := GetEmail(c.Email); err == nil { return errors.New("email address already exists") } } if c.SMS != "" { if _, err := GetSMS(c.SMS); err == nil { return errors.New("sms number already exists") } } return nil }