blog/models/events/events.go

196 lines
4.8 KiB
Go

package events
import (
"errors"
"fmt"
"net/http"
"regexp"
"strconv"
"strings"
"time"
"github.com/jinzhu/gorm"
"github.com/kirsle/blog/models/contacts"
"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(&Event{}, &RSVP{})
DB.Model(&Event{}).Related(&RSVP{})
DB.Model(&RSVP{}).Related(&contacts.Contact{})
}
var log *golog.Logger
func init() {
log = golog.GetLogger("blog")
}
// Event holds information about events.
type Event struct {
ID int `json:"id"`
Title string `json:"title"`
Fragment string `json:"fragment"`
Description string `json:"description"`
Location string `json:"location"`
CoverPhoto string `json:"coverPhoto"`
StartTime time.Time `json:"startTime"`
EndTime time.Time `json:"endTime"`
AllDay bool `json:"allDay"`
OpenSignup bool `json:"openSignup"`
RSVP []RSVP `json:"rsvp"`
Created time.Time `json:"created"`
Updated time.Time `json:"updated"`
}
// New creates a blank event with sensible defaults.
func New() *Event {
return &Event{
StartTime: time.Now().UTC(),
EndTime: time.Now().UTC(),
}
}
// All returns all the events.
func All() ([]*Event, error) {
result := []*Event{}
err := DB.Order("start_time desc").Find(&result).Error
return result, err
}
// ParseForm populates the event from form values.
func (ev *Event) ParseForm(r *http.Request) {
id, _ := strconv.Atoi(r.FormValue("id"))
ev.ID = id
ev.Title = r.FormValue("title")
ev.Fragment = r.FormValue("fragment")
ev.Description = r.FormValue("description")
ev.Location = r.FormValue("location")
ev.AllDay = r.FormValue("all_day") == "true"
ev.OpenSignup = r.FormValue("open_signup") == "true"
startTime, err := parseDateTime(r, "start_date", "start_time")
ev.StartTime = startTime
if err != nil {
log.Error("startTime parse error: %s", err)
}
endTime, err := parseDateTime(r, "end_date", "end_time")
ev.EndTime = endTime
if err != nil {
log.Error("endTime parse error: %s", err)
}
}
// parseDateTime parses separate date + time fields into a single time.Time.
func parseDateTime(r *http.Request, dateField, timeField string) (time.Time, error) {
dateValue := r.FormValue(dateField)
timeValue := r.FormValue(timeField)
if dateValue != "" && timeValue != "" {
datetime, err := time.Parse("2006-01-02 15:04", dateValue+" "+timeValue)
return datetime, err
} else if dateValue != "" {
datetime, err := time.Parse("2006-01-02", dateValue)
return datetime, err
} else {
return time.Time{}, errors.New("no date/times given")
}
}
// Validate makes sure the required fields are all present.
func (ev *Event) Validate() error {
if ev.Title == "" {
return errors.New("title is required")
} else if ev.Description == "" {
return errors.New("description is required")
}
return nil
}
// joinedLoad loads the Event with its RSVPs and their Contacts.
func joinedLoad() *gorm.DB {
return DB.Preload("RSVP").Preload("RSVP.Contact")
}
// Load an event by its ID.
func Load(id int) (*Event, error) {
ev := &Event{}
err := joinedLoad().First(ev, id).Error
return ev, err
}
// LoadFragment loads an event by its URL fragment.
func LoadFragment(fragment string) (*Event, error) {
ev := &Event{}
err := joinedLoad().Where("fragment = ?", fragment).First(ev).Error
return ev, err
}
// Save the event.
func (ev *Event) Save() error {
// Generate a URL fragment if needed.
if ev.Fragment == "" {
fragment := strings.ToLower(ev.Title)
fragment = regexp.MustCompile(`[^A-Za-z0-9]+`).ReplaceAllString(fragment, "-")
if strings.Contains(fragment, "--") {
log.Error("Generated event fragment '%s' contains double dashes still!", fragment)
}
ev.Fragment = strings.Trim(fragment, "-")
// If still no fragment, make one based on the post ID.
if ev.Fragment == "" {
ev.Fragment = fmt.Sprintf("event-%d", ev.ID)
}
}
// Make sure the URL fragment is unique!
if len(ev.Fragment) > 0 {
if exist, err := LoadFragment(ev.Fragment); err == nil && exist.ID != ev.ID {
var resolved bool
for i := 1; i <= 100; i++ {
fragment := fmt.Sprintf("%s-%d", ev.Fragment, i)
_, err := LoadFragment(fragment)
if err == nil {
continue
}
ev.Fragment = fragment
resolved = true
break
}
if !resolved {
return fmt.Errorf("failed to generate a unique URL fragment for '%s' after 100 attempts", ev.Fragment)
}
}
}
// Dates & times.
if ev.Created.IsZero() {
ev.Created = time.Now().UTC()
}
if ev.Updated.IsZero() {
ev.Updated = ev.Created
}
// Write the event.
return DB.Save(&ev).Error
}
// Delete an event.
func (ev *Event) Delete() error {
if ev.ID == 0 {
return errors.New("event has no ID")
}
// Delete the DB files.
return DB.Delete(ev).Error
}