feat: add calendar
							parent
							
								
									82c82b0b42
								
							
						
					
					
						commit
						8f207bf356
					
				| 
						 | 
				
			
			@ -0,0 +1,117 @@
 | 
			
		|||
package calendar
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
	"kumoly.io/kumoly/app/auth"
 | 
			
		||||
	"kumoly.io/kumoly/app/errors"
 | 
			
		||||
	"kumoly.io/kumoly/app/history"
 | 
			
		||||
	"kumoly.io/kumoly/app/server"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func ApiCalQuery(c *gin.Context) {
 | 
			
		||||
	id := c.Query("id")
 | 
			
		||||
	if id != "" {
 | 
			
		||||
		cal := &Calendar{}
 | 
			
		||||
		err := HasCalAccess(c, cal, id)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			panic(err)
 | 
			
		||||
		}
 | 
			
		||||
		server.OK(c, cal)
 | 
			
		||||
	} else {
 | 
			
		||||
		grp := c.Query("grp")
 | 
			
		||||
		cals := []Calendar{}
 | 
			
		||||
		cl, err := auth.GetContextClaims(c)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			panic(err)
 | 
			
		||||
		}
 | 
			
		||||
		var result *gorm.DB
 | 
			
		||||
 | 
			
		||||
		if grp != "" && auth.ACHas(c, auth.ADMIN, auth.SYSTEM, grp) {
 | 
			
		||||
			var grp_id uint
 | 
			
		||||
			db.Raw("select id from groups where name = ?", grp).Scan(&grp_id)
 | 
			
		||||
			if grp_id == 0 {
 | 
			
		||||
				panic(errors.ErrorNotFound)
 | 
			
		||||
			}
 | 
			
		||||
			result = db.Find(&cals, "`group_id` = ? ", grp_id)
 | 
			
		||||
		} else if !auth.ACHas(c, auth.ADMIN, auth.SYSTEM) {
 | 
			
		||||
			result = db.
 | 
			
		||||
				Find(&cals, "`group_id` in (?) or group_id = 0",
 | 
			
		||||
					db.Table("groups").Select("id").Where("name in ?", cl.Groups))
 | 
			
		||||
		} else {
 | 
			
		||||
			result = db.Find(&cals)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if result.Error != nil {
 | 
			
		||||
			panic(result.Error)
 | 
			
		||||
		}
 | 
			
		||||
		server.OK(c, cals)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ApiCalNew(c *gin.Context) {
 | 
			
		||||
	cal := &Calendar{}
 | 
			
		||||
	if err := c.ShouldBindJSON(cal); err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	if cal.ID != "" {
 | 
			
		||||
		panic(errors.ErrorBadRequest)
 | 
			
		||||
	}
 | 
			
		||||
	if !auth.ACHas(c, auth.ADMIN, auth.SYSTEM, cal.GroupName) {
 | 
			
		||||
		panic(errors.ErrorForbidden)
 | 
			
		||||
	}
 | 
			
		||||
	if err := db.Create(cal).Error; err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	history.Send(history.Info().
 | 
			
		||||
		Nm("Create").
 | 
			
		||||
		Grp(cal.GroupName).Bd(cal).
 | 
			
		||||
		Iss(c.GetString(auth.GinUserKey)).
 | 
			
		||||
		Msg("Calendar created"))
 | 
			
		||||
	server.OK(c, cal)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ApiCalUpdate(c *gin.Context) {
 | 
			
		||||
	cal := &Calendar{}
 | 
			
		||||
	if err := c.ShouldBindJSON(cal); err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	if cal.ID == "" {
 | 
			
		||||
		panic(errors.ErrorBadRequest)
 | 
			
		||||
	}
 | 
			
		||||
	if err := HasCalAccess(c, &Calendar{}, cal.ID); err != nil {
 | 
			
		||||
		panic(errors.ErrorForbidden)
 | 
			
		||||
	}
 | 
			
		||||
	if err := db.Save(cal).Error; err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	history.Send(history.Info().
 | 
			
		||||
		Nm("Update").
 | 
			
		||||
		Grp(cal.GroupName).Bd(cal).
 | 
			
		||||
		Iss(c.GetString(auth.GinUserKey)).
 | 
			
		||||
		Msg("Calendar Updated"))
 | 
			
		||||
	server.OK(c, cal)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ApiCalDelete(c *gin.Context) {
 | 
			
		||||
	id := c.Query("id")
 | 
			
		||||
	if id == "" {
 | 
			
		||||
		panic(errors.ErrorBadRequest)
 | 
			
		||||
	}
 | 
			
		||||
	cal := &Calendar{}
 | 
			
		||||
	err := HasCalAccess(c, cal, id)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = db.Delete(&Calendar{}, "id = ?", id).Error
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(errors.NewError(404, err))
 | 
			
		||||
	}
 | 
			
		||||
	history.Send(history.Info().
 | 
			
		||||
		Nm("Delete").
 | 
			
		||||
		Grp(cal.GroupName).Bd(cal).
 | 
			
		||||
		Iss(c.GetString(auth.GinUserKey)).
 | 
			
		||||
		Msg("Calendar Deleted"))
 | 
			
		||||
	server.OK(c, "ok")
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,120 @@
 | 
			
		|||
package calendar
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
	"kumoly.io/kumoly/app/auth"
 | 
			
		||||
	"kumoly.io/kumoly/app/errors"
 | 
			
		||||
	"kumoly.io/kumoly/app/history"
 | 
			
		||||
	"kumoly.io/kumoly/app/server"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func ApiEventQuery(c *gin.Context) {
 | 
			
		||||
	id := c.Query("id")
 | 
			
		||||
	if id != "" {
 | 
			
		||||
		e := &Event{}
 | 
			
		||||
		err := HasEventAccess(c, e, id)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			panic(err)
 | 
			
		||||
		}
 | 
			
		||||
		server.OK(c, e)
 | 
			
		||||
	} else {
 | 
			
		||||
		grp := c.Query("grp")
 | 
			
		||||
		events := []Event{}
 | 
			
		||||
		cl, err := auth.GetContextClaims(c)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			panic(err)
 | 
			
		||||
		}
 | 
			
		||||
		var result *gorm.DB
 | 
			
		||||
 | 
			
		||||
		if grp != "" && auth.ACHas(c, auth.ADMIN, auth.SYSTEM, grp) {
 | 
			
		||||
			var grp_id uint
 | 
			
		||||
			db.Raw("select id from groups where name = ?", grp).Scan(&grp_id)
 | 
			
		||||
			if grp_id == 0 {
 | 
			
		||||
				panic(errors.ErrorNotFound)
 | 
			
		||||
			}
 | 
			
		||||
			result = db.Find(&events, "`group_id` = ? ", grp_id)
 | 
			
		||||
		} else if !auth.ACHas(c, auth.ADMIN, auth.SYSTEM) {
 | 
			
		||||
			result = db.
 | 
			
		||||
				Find(&events, "`group_id` in (?) or group_id = 0",
 | 
			
		||||
					db.Table("groups").Select("id").Where("name in ?", cl.Groups))
 | 
			
		||||
		} else {
 | 
			
		||||
			result = db.Find(&events)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if result.Error != nil {
 | 
			
		||||
			panic(result.Error)
 | 
			
		||||
		}
 | 
			
		||||
		server.OK(c, events)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ApiEventNew(c *gin.Context) {
 | 
			
		||||
	e := &Event{}
 | 
			
		||||
	if err := c.ShouldBindJSON(e); err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	if e.ID != "" {
 | 
			
		||||
		panic(errors.ErrorBadRequest)
 | 
			
		||||
	}
 | 
			
		||||
	if e.Start.IsZero() || e.End.IsZero() || e.Start.Before(e.End) {
 | 
			
		||||
		panic(ErrorInvalidTime)
 | 
			
		||||
	}
 | 
			
		||||
	if !auth.ACHas(c, auth.ADMIN, auth.SYSTEM, e.GroupName) {
 | 
			
		||||
		panic(errors.ErrorForbidden)
 | 
			
		||||
	}
 | 
			
		||||
	if err := db.Create(e).Error; err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	history.Send(history.Info().
 | 
			
		||||
		Nm("Create").
 | 
			
		||||
		Grp(e.GroupName).Bd(e).
 | 
			
		||||
		Iss(c.GetString(auth.GinUserKey)).
 | 
			
		||||
		Msg("Event created"))
 | 
			
		||||
	server.OK(c, e)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ApiEventUpdate(c *gin.Context) {
 | 
			
		||||
	e := &Event{}
 | 
			
		||||
	if err := c.ShouldBindJSON(e); err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	if e.ID == "" {
 | 
			
		||||
		panic(errors.ErrorBadRequest)
 | 
			
		||||
	}
 | 
			
		||||
	if err := HasEventAccess(c, &Event{}, e.ID); err != nil {
 | 
			
		||||
		panic(errors.ErrorForbidden)
 | 
			
		||||
	}
 | 
			
		||||
	if err := db.Save(e).Error; err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	history.Send(history.Info().
 | 
			
		||||
		Nm("Update").
 | 
			
		||||
		Grp(e.GroupName).Bd(e).
 | 
			
		||||
		Iss(c.GetString(auth.GinUserKey)).
 | 
			
		||||
		Msg("Event Updated"))
 | 
			
		||||
	server.OK(c, e)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ApiEventDelete(c *gin.Context) {
 | 
			
		||||
	id := c.Query("id")
 | 
			
		||||
	if id == "" {
 | 
			
		||||
		panic(errors.ErrorBadRequest)
 | 
			
		||||
	}
 | 
			
		||||
	e := &Event{}
 | 
			
		||||
	err := HasEventAccess(c, e, id)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = db.Delete(&Calendar{}, "id = ?", id).Error
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(errors.NewError(404, err))
 | 
			
		||||
	}
 | 
			
		||||
	history.Send(history.Info().
 | 
			
		||||
		Nm("Delete").
 | 
			
		||||
		Grp(e.GroupName).Bd(e).
 | 
			
		||||
		Iss(c.GetString(auth.GinUserKey)).
 | 
			
		||||
		Msg("Event Deleted"))
 | 
			
		||||
	server.OK(c, "ok")
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -6,22 +6,62 @@ import (
 | 
			
		|||
	"github.com/rs/xid"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
	"kumoly.io/kumoly/app/auth"
 | 
			
		||||
	"kumoly.io/kumoly/app/errors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var l zerolog.Logger
 | 
			
		||||
 | 
			
		||||
var db *gorm.DB
 | 
			
		||||
 | 
			
		||||
type Calendar struct {
 | 
			
		||||
	ID string `gorm:"primaryKey"`
 | 
			
		||||
 | 
			
		||||
	Name        string `gorm:"index:idx_cal,unique"`
 | 
			
		||||
	Description string
 | 
			
		||||
	ExtLink     string
 | 
			
		||||
 | 
			
		||||
	Timezone string
 | 
			
		||||
 | 
			
		||||
	Events []Event
 | 
			
		||||
 | 
			
		||||
	Group     auth.Group `json:"-"`
 | 
			
		||||
	GroupName string     `gorm:"-" json:"Group"`
 | 
			
		||||
	GroupID   uint       `gorm:"index:idx_cal,unique" json:"-"`
 | 
			
		||||
 | 
			
		||||
	CreatedAt time.Time
 | 
			
		||||
	UpdatedAt time.Time
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Calendar) BeforeSave(tx *gorm.DB) (err error) {
 | 
			
		||||
	if c.GroupName != "" {
 | 
			
		||||
		var grp_id uint
 | 
			
		||||
		db.Raw("select id from groups where name = ?", c.GroupName).Scan(&grp_id)
 | 
			
		||||
		if grp_id == 0 {
 | 
			
		||||
			return errors.ErrorNotFound
 | 
			
		||||
		}
 | 
			
		||||
		c.GroupID = grp_id
 | 
			
		||||
	} else {
 | 
			
		||||
		c.GroupID = 0
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Calendar) BeforeCreate(tx *gorm.DB) (err error) {
 | 
			
		||||
	if c.ID == "" {
 | 
			
		||||
		c.ID = xid.New().String()
 | 
			
		||||
	}
 | 
			
		||||
	if c.Timezone == "" {
 | 
			
		||||
		c.Timezone = "Asia/Taipei"
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Calendar) AfterFind(tx *gorm.DB) (err error) {
 | 
			
		||||
	if c.GroupID != 0 {
 | 
			
		||||
		var name string
 | 
			
		||||
		db.Raw("select name from groups where id = ?", c.GroupID).Scan(&name)
 | 
			
		||||
		c.GroupName = name
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,16 @@
 | 
			
		|||
package calendar
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestOffsetStr(t *testing.T) {
 | 
			
		||||
	fmt.Println(offsetStr(-28800))
 | 
			
		||||
	fmt.Println(offsetStr(28800))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestT(t *testing.T) {
 | 
			
		||||
	fmt.Println(icstime(time.Now()))
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,13 @@
 | 
			
		|||
package calendar
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"kumoly.io/kumoly/app/errors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var ErrorInvalidTime = errors.Error{
 | 
			
		||||
	Code:    http.StatusBadRequest,
 | 
			
		||||
	ID:      "ErrorInvalidTime",
 | 
			
		||||
	Message: "time is not valid",
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -5,24 +5,62 @@ import (
 | 
			
		|||
 | 
			
		||||
	"github.com/rs/xid"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
	"kumoly.io/kumoly/app/auth"
 | 
			
		||||
	"kumoly.io/kumoly/app/errors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Event struct {
 | 
			
		||||
	ID string `gorm:"primaryKey"`
 | 
			
		||||
 | 
			
		||||
	Start time.Time
 | 
			
		||||
	End   time.Time
 | 
			
		||||
	Start       time.Time
 | 
			
		||||
	End         time.Time
 | 
			
		||||
	Name        string `gorm:"not null"`
 | 
			
		||||
	Hosts       string
 | 
			
		||||
	Description string
 | 
			
		||||
	Location    string
 | 
			
		||||
 | 
			
		||||
	// ntu
 | 
			
		||||
	Class    string
 | 
			
		||||
	CourseID string
 | 
			
		||||
	Semester string
 | 
			
		||||
 | 
			
		||||
	CalendarID   string
 | 
			
		||||
	EventGroupID string
 | 
			
		||||
 | 
			
		||||
	Group     auth.Group `json:"-"`
 | 
			
		||||
	GroupName string     `gorm:"-" json:"Group"`
 | 
			
		||||
	GroupID   uint       `json:"-"`
 | 
			
		||||
 | 
			
		||||
	CreatedAt time.Time
 | 
			
		||||
	UpdatedAt time.Time
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e *Event) BeforeSave(tx *gorm.DB) (err error) {
 | 
			
		||||
	if e.GroupName != "" {
 | 
			
		||||
		var grp_id uint
 | 
			
		||||
		db.Raw("select id from groups where name = ?", e.GroupName).Scan(&grp_id)
 | 
			
		||||
		if grp_id == 0 {
 | 
			
		||||
			return errors.ErrorNotFound
 | 
			
		||||
		}
 | 
			
		||||
		e.GroupID = grp_id
 | 
			
		||||
	} else {
 | 
			
		||||
		e.GroupID = 0
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e *Event) BeforeCreate(tx *gorm.DB) (err error) {
 | 
			
		||||
	if e.ID == "" {
 | 
			
		||||
		e.ID = xid.New().String()
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e *Event) AfterFind(tx *gorm.DB) (err error) {
 | 
			
		||||
	if e.GroupID != 0 {
 | 
			
		||||
		var name string
 | 
			
		||||
		db.Raw("select name from groups where id = ?", e.GroupID).Scan(&name)
 | 
			
		||||
		e.GroupName = name
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,20 +5,51 @@ import (
 | 
			
		|||
 | 
			
		||||
	"github.com/rs/xid"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
	"kumoly.io/kumoly/app/auth"
 | 
			
		||||
	"kumoly.io/kumoly/app/errors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type EventGroup struct {
 | 
			
		||||
	ID string `gorm:"primaryKey"`
 | 
			
		||||
 | 
			
		||||
	Name  string
 | 
			
		||||
	Rrule string
 | 
			
		||||
 | 
			
		||||
	Events []Event
 | 
			
		||||
 | 
			
		||||
	Group     auth.Group `json:"-"`
 | 
			
		||||
	GroupName string     `gorm:"-" json:"Group"`
 | 
			
		||||
	GroupID   uint       `json:"-"`
 | 
			
		||||
 | 
			
		||||
	CreatedAt time.Time
 | 
			
		||||
	UpdatedAt time.Time
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (eg *EventGroup) BeforeSave(tx *gorm.DB) (err error) {
 | 
			
		||||
	if eg.GroupName != "" {
 | 
			
		||||
		var grp_id uint
 | 
			
		||||
		db.Raw("select id from groups where name = ?", eg.GroupName).Scan(&grp_id)
 | 
			
		||||
		if grp_id == 0 {
 | 
			
		||||
			return errors.ErrorNotFound
 | 
			
		||||
		}
 | 
			
		||||
		eg.GroupID = grp_id
 | 
			
		||||
	} else {
 | 
			
		||||
		eg.GroupID = 0
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
func (eg *EventGroup) BeforeCreate(tx *gorm.DB) (err error) {
 | 
			
		||||
	if eg.ID == "" {
 | 
			
		||||
		eg.ID = xid.New().String()
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (eg *EventGroup) AfterFind(tx *gorm.DB) (err error) {
 | 
			
		||||
	if eg.GroupID != 0 {
 | 
			
		||||
		var name string
 | 
			
		||||
		db.Raw("select name from groups where id = ?", eg.GroupID).Scan(&name)
 | 
			
		||||
		eg.GroupName = name
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,74 @@
 | 
			
		|||
package calendar
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"text/template"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"kumoly.io/kumoly/app/store"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const ics_base = `BEGIN:VCALENDAR
 | 
			
		||||
PRODID:-//Kumoly//Kumoly App v0.0.1//EN
 | 
			
		||||
VERSION:2.0
 | 
			
		||||
CALSCALE:GREGORIAN
 | 
			
		||||
METHOD:PUBLISH
 | 
			
		||||
X-WR-CALNAME:{{.Name}}
 | 
			
		||||
X-WR-TIMEZONE:Asia/Taipei
 | 
			
		||||
X-WR-CALDESC:{{.Name}}
 | 
			
		||||
BEGIN:VTIMEZONE
 | 
			
		||||
TZID:{{.Timezone}}
 | 
			
		||||
X-LIC-LOCATION:{{.Timezone}}
 | 
			
		||||
BEGIN:STANDARD
 | 
			
		||||
TZOFFSETFROM:+0800
 | 
			
		||||
TZOFFSETTO:+0800
 | 
			
		||||
TZNAME:CST
 | 
			
		||||
DTSTART:19700101T000000
 | 
			
		||||
END:STANDARD
 | 
			
		||||
END:VTIMEZONE
 | 
			
		||||
{{with .Events}}{{ if gt (len .) 0 }}{{ range $event := . }}
 | 
			
		||||
BEGIN:VEVENT
 | 
			
		||||
DTSTART:{{$event.Start|icstime}}
 | 
			
		||||
DTEND:{{$event.End|icstime}}
 | 
			
		||||
DTSTAMP:{{now|icstime}}
 | 
			
		||||
UID:{{$event.ID}}
 | 
			
		||||
CREATED:{{$event.CreatedAt|icstime}}
 | 
			
		||||
DESCRIPTION:{{$event.ID}}
 | 
			
		||||
LAST-MODIFIED:{{$event.UpdatedAt|icstime}}
 | 
			
		||||
LOCATION:{{$event.Location}}
 | 
			
		||||
SEQUENCE:0
 | 
			
		||||
STATUS:CONFIRMED
 | 
			
		||||
SUMMARY:{{$event.Name}}
 | 
			
		||||
TRANSP:TRANSPARENT
 | 
			
		||||
END:VEVENT
 | 
			
		||||
{{end}}{{end}}{{end}}
 | 
			
		||||
END:VCALENDAR`
 | 
			
		||||
 | 
			
		||||
var tmpl *template.Template
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	tmpl = template.Must(template.New("").Funcs(template.FuncMap{
 | 
			
		||||
		"icstime": icstime,
 | 
			
		||||
		"now":     func() time.Time { return time.Now() },
 | 
			
		||||
	}).Parse(ics_base))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ICS(cal_id string, from time.Time, to time.Time) (string, error) {
 | 
			
		||||
	cal := &Calendar{}
 | 
			
		||||
	store.DB.Preload("Events", "start > ? and end < ?", from, to).
 | 
			
		||||
		First(cal, "id = ?", cal_id)
 | 
			
		||||
	buf := &bytes.Buffer{}
 | 
			
		||||
	if err := tmpl.Execute(buf, cal); err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	return buf.String(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func icstime(t time.Time) string {
 | 
			
		||||
	return t.UTC().Format("20060102T150405Z")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func offsetStr(offset int) string {
 | 
			
		||||
	return fmt.Sprintf("%+05d", offset/36)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
package calendar
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"kumoly.io/kumoly/app/server"
 | 
			
		||||
	"kumoly.io/kumoly/app/store"
 | 
			
		||||
	"kumoly.io/kumoly/app/util"
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -15,6 +16,7 @@ func (srv Service) Del()                      {}
 | 
			
		|||
func (srv Service) Health() error             { return nil }
 | 
			
		||||
 | 
			
		||||
func (srv Service) Init() error {
 | 
			
		||||
	db = store.DB
 | 
			
		||||
 | 
			
		||||
	l = util.Klog.With().Str("mod", "auth").Logger()
 | 
			
		||||
	l.Debug().Msg("Migrating database for calendar.Service ...")
 | 
			
		||||
| 
						 | 
				
			
			@ -25,4 +27,18 @@ func (srv Service) Init() error {
 | 
			
		|||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (srv Service) Load() error { return nil }
 | 
			
		||||
func (srv Service) Load() error {
 | 
			
		||||
	calApi := server.API.Group("cal")
 | 
			
		||||
	calApi.GET("", ApiCalQuery)
 | 
			
		||||
	calApi.POST("", ApiCalNew)
 | 
			
		||||
	calApi.PUT("", ApiCalUpdate)
 | 
			
		||||
	calApi.DELETE("", ApiCalDelete)
 | 
			
		||||
 | 
			
		||||
	eventApi := server.API.Group("event")
 | 
			
		||||
	eventApi.GET("", ApiEventQuery)
 | 
			
		||||
	eventApi.POST("", ApiEventNew)
 | 
			
		||||
	eventApi.PUT("", ApiEventUpdate)
 | 
			
		||||
	eventApi.DELETE("", ApiEventDelete)
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,35 @@
 | 
			
		|||
package calendar
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
	"kumoly.io/kumoly/app/auth"
 | 
			
		||||
	"kumoly.io/kumoly/app/errors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func HasCalAccess(c *gin.Context, cal *Calendar, cid string) error {
 | 
			
		||||
	err := db.First(cal, "id = ?", cid).Error
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.NewError(404, err)
 | 
			
		||||
	}
 | 
			
		||||
	if cal.GroupName == "" {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if !auth.ACHas(c, auth.ADMIN, auth.SYSTEM, cal.GroupName) {
 | 
			
		||||
		return errors.ErrorForbidden
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func HasEventAccess(c *gin.Context, e *Event, cid string) error {
 | 
			
		||||
	err := db.First(e, "id = ?", cid).Error
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.NewError(404, err)
 | 
			
		||||
	}
 | 
			
		||||
	if e.GroupName == "" {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if !auth.ACHas(c, auth.ADMIN, auth.SYSTEM, e.GroupName) {
 | 
			
		||||
		return errors.ErrorForbidden
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -4,6 +4,7 @@ import (
 | 
			
		|||
	"github.com/spf13/viper"
 | 
			
		||||
	"kumoly.io/kumoly/app/attribute"
 | 
			
		||||
	"kumoly.io/kumoly/app/auth"
 | 
			
		||||
	"kumoly.io/kumoly/app/calendar"
 | 
			
		||||
	"kumoly.io/kumoly/app/control"
 | 
			
		||||
	"kumoly.io/kumoly/app/email"
 | 
			
		||||
	"kumoly.io/kumoly/app/history"
 | 
			
		||||
| 
						 | 
				
			
			@ -30,6 +31,7 @@ func Default() *system.System {
 | 
			
		|||
		&history.Service{},
 | 
			
		||||
		&email.Service{},
 | 
			
		||||
		&control.Service{},
 | 
			
		||||
		&calendar.Service{},
 | 
			
		||||
	)
 | 
			
		||||
	return sys
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue