feat(caldav): Import Labels from caldav categories
continuous-integration/drone/pr Build is passing
Details
continuous-integration/drone/pr Build is passing
Details
This commit is contained in:
parent
fdb0169f13
commit
ce47ef1270
|
@ -10,9 +10,9 @@ menu:
|
||||||
|
|
||||||
# Caldav
|
# Caldav
|
||||||
|
|
||||||
> **Warning:** The caldav integration is in an early alpha stage and has bugs.
|
> **Warning:** The caldav integration is in an early alpha stage and has bugs.
|
||||||
> It works well with some clients while having issues with others.
|
> It works well with some clients while having issues with others.
|
||||||
> If you encounter issues, please [report them](https://code.vikunja.io/api/issues/new?body=[caldav])
|
> If you encounter issues, please [report them](https://code.vikunja.io/api/issues/new?body=[caldav])
|
||||||
|
|
||||||
Vikunja supports managing tasks via the [caldav VTODO](https://tools.ietf.org/html/rfc5545#section-3.6.2) extension.
|
Vikunja supports managing tasks via the [caldav VTODO](https://tools.ietf.org/html/rfc5545#section-3.6.2) extension.
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@ Vikunja currently supports the following properties:
|
||||||
* `SUMMARY`
|
* `SUMMARY`
|
||||||
* `DESCRIPTION`
|
* `DESCRIPTION`
|
||||||
* `PRIORITY`
|
* `PRIORITY`
|
||||||
|
* `CATEGORIES`
|
||||||
* `COMPLETED`
|
* `COMPLETED`
|
||||||
* `DUE`
|
* `DUE`
|
||||||
* `DTSTART`
|
* `DTSTART`
|
||||||
|
@ -50,7 +51,6 @@ Vikunja currently supports the following properties:
|
||||||
Vikunja **currently does not** support these properties:
|
Vikunja **currently does not** support these properties:
|
||||||
|
|
||||||
* `ATTACH`
|
* `ATTACH`
|
||||||
* `CATEGORIES`
|
|
||||||
* `CLASS`
|
* `CLASS`
|
||||||
* `COMMENT`
|
* `COMMENT`
|
||||||
* `GEO`
|
* `GEO`
|
||||||
|
|
|
@ -96,12 +96,15 @@ func ParseTaskFromVTODO(content string) (vTask *models.Task, err error) {
|
||||||
description := strings.ReplaceAll(task["DESCRIPTION"], "\\,", ",")
|
description := strings.ReplaceAll(task["DESCRIPTION"], "\\,", ",")
|
||||||
description = strings.ReplaceAll(description, "\\n", "\n")
|
description = strings.ReplaceAll(description, "\\n", "\n")
|
||||||
|
|
||||||
categories := strings.Split(task["CATEGORIES"], ",")
|
var labels []*models.Label
|
||||||
labels := make([]*models.Label, 0, len(categories))
|
if val, ok := task["CATEGORIES"]; ok {
|
||||||
for _, category := range categories {
|
categories := strings.Split(val, ",")
|
||||||
labels = append(labels, &models.Label{
|
labels = make([]*models.Label, 0, len(categories))
|
||||||
Title: category,
|
for _, category := range categories {
|
||||||
})
|
labels = append(labels, &models.Label{
|
||||||
|
Title: category,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vTask = &models.Task{
|
vTask = &models.Task{
|
||||||
|
|
|
@ -176,15 +176,15 @@ func (l *Label) ReadAll(s *xorm.Session, a web.Auth, search string, page int, pe
|
||||||
// @Failure 500 {object} models.Message "Internal error"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /labels/{id} [get]
|
// @Router /labels/{id} [get]
|
||||||
func (l *Label) ReadOne(s *xorm.Session, a web.Auth) (err error) {
|
func (l *Label) ReadOne(s *xorm.Session, a web.Auth) (err error) {
|
||||||
label, err := getLabelByIDSimple(s, l.ID)
|
label, err := getLabelByExampleSimple(s, l)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return
|
||||||
}
|
}
|
||||||
*l = *label
|
*l = *label
|
||||||
|
|
||||||
u, err := user.GetUserByID(s, l.CreatedByID)
|
u, err := user.GetUserByID(s, l.CreatedByID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
l.CreatedBy = u
|
l.CreatedBy = u
|
||||||
|
@ -192,14 +192,16 @@ func (l *Label) ReadOne(s *xorm.Session, a web.Auth) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func getLabelByIDSimple(s *xorm.Session, labelID int64) (*Label, error) {
|
func getLabelByIDSimple(s *xorm.Session, labelID int64) (*Label, error) {
|
||||||
label := Label{}
|
return getLabelByExampleSimple(s, &Label{ID: labelID})
|
||||||
exists, err := s.ID(labelID).Get(&label)
|
}
|
||||||
if err != nil {
|
|
||||||
return &label, err
|
func getLabelByExampleSimple(s *xorm.Session, l *Label) (*Label, error) {
|
||||||
}
|
exists, err := s.Get(l)
|
||||||
|
if err != nil {
|
||||||
if !exists {
|
return l, err
|
||||||
return &Label{}, ErrLabelDoesNotExist{labelID}
|
}
|
||||||
}
|
if !exists {
|
||||||
return &label, err
|
return &Label{}, ErrLabelDoesNotExist{l.ID}
|
||||||
|
}
|
||||||
|
return l, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -264,7 +264,7 @@ func getLabelsByTaskIDs(s *xorm.Session, opts *LabelByTaskIDsOptions) (ls []*lab
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create or update a bunch of task labels
|
// Create or update a bunch of task labels
|
||||||
func (t *Task) updateTaskLabels(s *xorm.Session, creator web.Auth, labels []*Label) (err error) {
|
func (t *Task) UpdateTaskLabels(s *xorm.Session, creator web.Auth, labels []*Label) (err error) {
|
||||||
|
|
||||||
// If we don't have any new labels, delete everything right away. Saves us some hassle.
|
// If we don't have any new labels, delete everything right away. Saves us some hassle.
|
||||||
if len(labels) == 0 && len(t.Labels) > 0 {
|
if len(labels) == 0 && len(t.Labels) > 0 {
|
||||||
|
@ -390,5 +390,5 @@ func (ltb *LabelTaskBulk) Create(s *xorm.Session, a web.Auth) (err error) {
|
||||||
for _, l := range labels {
|
for _, l := range labels {
|
||||||
task.Labels = append(task.Labels, &l.Label)
|
task.Labels = append(task.Labels, &l.Label)
|
||||||
}
|
}
|
||||||
return task.updateTaskLabels(s, a, ltb.Labels)
|
return task.UpdateTaskLabels(s, a, ltb.Labels)
|
||||||
}
|
}
|
||||||
|
|
|
@ -198,6 +198,21 @@ func TestLabel_ReadOne(t *testing.T) {
|
||||||
},
|
},
|
||||||
auth: &user.User{ID: 1},
|
auth: &user.User{ID: 1},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Get label by Title",
|
||||||
|
fields: fields{
|
||||||
|
Title: "Label #2",
|
||||||
|
},
|
||||||
|
want: &Label{
|
||||||
|
ID: 2,
|
||||||
|
Title: "Label #2",
|
||||||
|
CreatedByID: 1,
|
||||||
|
CreatedBy: user1,
|
||||||
|
Created: testCreatedTime,
|
||||||
|
Updated: testUpdatedTime,
|
||||||
|
},
|
||||||
|
auth: &user.User{ID: 1},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Get nonexistant label",
|
name: "Get nonexistant label",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
|
@ -259,9 +274,11 @@ func TestLabel_ReadOne(t *testing.T) {
|
||||||
|
|
||||||
s := db.NewSession()
|
s := db.NewSession()
|
||||||
|
|
||||||
allowed, _, _ := l.CanRead(s, tt.auth)
|
if l.ID != 0 {
|
||||||
if !allowed && !tt.wantForbidden {
|
allowed, _, _ := l.CanRead(s, tt.auth)
|
||||||
t.Errorf("Label.CanRead() forbidden, want %v", tt.wantForbidden)
|
if !allowed && !tt.wantForbidden {
|
||||||
|
t.Errorf("Label.CanRead() forbidden, want %v", tt.wantForbidden)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
err := l.ReadOne(s, tt.auth)
|
err := l.ReadOne(s, tt.auth)
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
|
|
|
@ -273,8 +273,28 @@ func (vcls *VikunjaCaldavListStorage) CreateResource(rpath, content string) (*da
|
||||||
return nil, errs.ForbiddenError
|
return nil, errs.ForbiddenError
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find or create Labels by title
|
for _, label := range vTask.Labels {
|
||||||
// Insert label_task
|
// Find or create Labels by title
|
||||||
|
err = label.ReadOne(s, vcls.user)
|
||||||
|
if err != nil {
|
||||||
|
err = label.Create(s, vcls.user)
|
||||||
|
if err != nil {
|
||||||
|
_ = s.Rollback()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = label.ReadOne(s, vcls.user)
|
||||||
|
if err != nil {
|
||||||
|
_ = s.Rollback()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Insert LabelTask relation
|
||||||
|
err = vcls.task.UpdateTaskLabels(s, vcls.user, vTask.Labels)
|
||||||
|
if err != nil {
|
||||||
|
_ = s.Rollback()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Create the task
|
// Create the task
|
||||||
err = vTask.Create(s, vcls.user)
|
err = vTask.Create(s, vcls.user)
|
||||||
|
@ -321,8 +341,28 @@ func (vcls *VikunjaCaldavListStorage) UpdateResource(rpath, content string) (*da
|
||||||
return nil, errs.ForbiddenError
|
return nil, errs.ForbiddenError
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find or create Labels by title
|
for _, label := range vTask.Labels {
|
||||||
// Update label_task
|
// Find or create Labels by title
|
||||||
|
err = label.ReadOne(s, vcls.user)
|
||||||
|
if err != nil {
|
||||||
|
err = label.Create(s, vcls.user)
|
||||||
|
if err != nil {
|
||||||
|
_ = s.Rollback()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = label.ReadOne(s, vcls.user)
|
||||||
|
if err != nil {
|
||||||
|
_ = s.Rollback()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Update LabelTask relation
|
||||||
|
err = vcls.task.UpdateTaskLabels(s, vcls.user, vTask.Labels)
|
||||||
|
if err != nil {
|
||||||
|
_ = s.Rollback()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Update the task
|
// Update the task
|
||||||
err = vTask.Update(s, vcls.user)
|
err = vTask.Update(s, vcls.user)
|
||||||
|
|
Loading…
Reference in New Issue