forked from vikunja/vikunja
Compare commits
24 Commits
renovate/g
...
main
Author | SHA1 | Date | |
---|---|---|---|
feacbbff74 | |||
f065dcf4ad | |||
addcbdd8ca | |||
054f21821c | |||
38a3a5c6e8 | |||
1ee243f2bd | |||
191c154150 | |||
378759e06d | |||
c5c74e9537 | |||
e34f503674 | |||
bfcefa0217 | |||
c6bdb5752a | |||
68d4dcd7e6 | |||
b2f3a23cb3 | |||
93795d2f29 | |||
adf4b95ed3 | |||
ce3a06f03b | |||
2c0c3ea24e | |||
2d9cf672b8 | |||
ae766f52c7 | |||
107b0b791f | |||
985233ac38 | |||
424bf7647b | |||
06bc92556e |
79
.drone.yml
79
.drone.yml
|
@ -121,23 +121,12 @@ steps:
|
|||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
- name: prepare-build
|
||||
image: vikunja/golang-build:latest
|
||||
pull: always
|
||||
environment:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
depends_on: [ mage ]
|
||||
commands:
|
||||
- ./mage-static do-the-swag
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
- name: build
|
||||
image: vikunja/golang-build:latest
|
||||
pull: always
|
||||
environment:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
depends_on: [ prepare-build ]
|
||||
depends_on: [ mage ]
|
||||
commands:
|
||||
- ./mage-static build:build
|
||||
when:
|
||||
|
@ -230,7 +219,7 @@ steps:
|
|||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
commands:
|
||||
- ./mage-static test:unit
|
||||
depends_on: [ fetch-tags, prepare-build ]
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
|
@ -247,7 +236,7 @@ steps:
|
|||
path: /db
|
||||
commands:
|
||||
- ./mage-static test:unit
|
||||
depends_on: [ fetch-tags, prepare-build ]
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
|
@ -264,7 +253,7 @@ steps:
|
|||
VIKUNJA_DATABASE_DATABASE: vikunjatest
|
||||
commands:
|
||||
- ./mage-static test:unit
|
||||
depends_on: [ fetch-tags, prepare-build ]
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
|
@ -282,7 +271,7 @@ steps:
|
|||
VIKUNJA_DATABASE_SSLMODE: disable
|
||||
commands:
|
||||
- ./mage-static test:unit
|
||||
depends_on: [ fetch-tags, prepare-build ]
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
|
@ -293,7 +282,7 @@ steps:
|
|||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
commands:
|
||||
- ./mage-static test:integration
|
||||
depends_on: [ fetch-tags, prepare-build ]
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
|
@ -310,7 +299,7 @@ steps:
|
|||
path: /db
|
||||
commands:
|
||||
- ./mage-static test:integration
|
||||
depends_on: [ fetch-tags, prepare-build ]
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
|
@ -327,7 +316,7 @@ steps:
|
|||
VIKUNJA_DATABASE_DATABASE: vikunjatest
|
||||
commands:
|
||||
- ./mage-static test:integration
|
||||
depends_on: [ fetch-tags, prepare-build ]
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
|
@ -345,10 +334,54 @@ steps:
|
|||
VIKUNJA_DATABASE_SSLMODE: disable
|
||||
commands:
|
||||
- ./mage-static test:integration
|
||||
depends_on: [ fetch-tags, prepare-build ]
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: generate-swagger-docs
|
||||
|
||||
depends_on:
|
||||
- testing
|
||||
|
||||
workspace:
|
||||
base: /go
|
||||
path: src/code.vikunja.io/api
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
include:
|
||||
- main
|
||||
event:
|
||||
include:
|
||||
- push
|
||||
|
||||
steps:
|
||||
- name: generate-swagger-docs
|
||||
image: vikunja/golang-build:latest
|
||||
pull: always
|
||||
environment:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
commands:
|
||||
- mage do-the-swag
|
||||
|
||||
- name: push
|
||||
pull: always
|
||||
image: appleboy/drone-git-push
|
||||
depends_on:
|
||||
- generate-swagger-docs
|
||||
settings:
|
||||
author_email: "frederik@vikunja.io"
|
||||
author_name: Frederick [Bot]
|
||||
branch: main
|
||||
commit: true
|
||||
commit_message: "[skip ci] Updated swagger docs"
|
||||
remote: "ssh://git@kolaente.dev:9022/vikunja/api.git"
|
||||
ssh_key:
|
||||
from_secret: git_push_ssh_key
|
||||
|
||||
---
|
||||
########
|
||||
# Build a release when tagging
|
||||
|
@ -396,7 +429,6 @@ steps:
|
|||
- export PATH=$PATH:$GOPATH/bin
|
||||
- go install github.com/magefile/mage
|
||||
- ./mage-static release:dirs
|
||||
- ./mage-static do-the-swag
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
|
||||
- name: static-build-windows
|
||||
|
@ -406,6 +438,7 @@ steps:
|
|||
# This path does not exist. However, when we set the gopath to /go, the build fails. Not sure why.
|
||||
# Leaving this here until we know how to resolve this properly.
|
||||
GOPATH: /srv/app
|
||||
GOPROXY: https://goproxy.kolaente.de
|
||||
commands:
|
||||
- export PATH=$PATH:$GOPATH/bin
|
||||
- go install github.com/magefile/mage
|
||||
|
@ -419,6 +452,7 @@ steps:
|
|||
# This path does not exist. However, when we set the gopath to /go, the build fails. Not sure why.
|
||||
# Leaving this here until we know how to resolve this properly.
|
||||
GOPATH: /srv/app
|
||||
GOPROXY: https://goproxy.kolaente.de
|
||||
commands:
|
||||
- export PATH=$PATH:$GOPATH/bin
|
||||
- go install github.com/magefile/mage
|
||||
|
@ -432,6 +466,7 @@ steps:
|
|||
# This path does not exist. However, when we set the gopath to /go, the build fails. Not sure why.
|
||||
# Leaving this here until we know how to resolve this properly.
|
||||
GOPATH: /srv/app
|
||||
GOPROXY: https://goproxy.kolaente.de
|
||||
commands:
|
||||
- export PATH=$PATH:$GOPATH/bin
|
||||
- go install github.com/magefile/mage
|
||||
|
@ -743,6 +778,6 @@ steps:
|
|||
- failure
|
||||
---
|
||||
kind: signature
|
||||
hmac: d47bd1cf6f3e9be2ff3eed2039e65c8b6de2b16c1e636699f66382f941277411
|
||||
hmac: 6bc74f5b7e9c51e725100e05f07cdac656d6c3d49d19c2b112aed812c86e7a9a
|
||||
|
||||
...
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -27,4 +27,3 @@ vikunja-dump*
|
|||
vendor/
|
||||
os-packages/
|
||||
mage_output_file.go
|
||||
pkg/swagger/*
|
||||
|
|
|
@ -13,6 +13,7 @@ COPY . ./
|
|||
|
||||
ARG TARGETOS TARGETARCH TARGETVARIANT
|
||||
|
||||
ENV GOPROXY https://goproxy.kolaente.de
|
||||
RUN export PATH=$PATH:$GOPATH/bin && \
|
||||
mage build:clean && \
|
||||
mage release:xgo "${TARGETOS}/${TARGETARCH}/${TARGETVARIANT}"
|
||||
|
|
13
magefile.go
13
magefile.go
|
@ -177,9 +177,6 @@ func init() {
|
|||
// Some variables have external dependencies (like git) which may not always be available.
|
||||
func initVars() {
|
||||
Tags = os.Getenv("TAGS")
|
||||
if !strings.Contains(Tags, "swagger") {
|
||||
Tags += " swagger"
|
||||
}
|
||||
setVersion()
|
||||
setBinLocation()
|
||||
setPkgVersion()
|
||||
|
@ -349,10 +346,6 @@ const swaggerDocsFolderLocation = `./pkg/swagger/`
|
|||
// Generates the swagger docs from the code annotations
|
||||
func DoTheSwag() {
|
||||
mg.Deps(initVars)
|
||||
if _, err := os.Stat(swaggerDocsFolderLocation + "swagger.json"); err == nil {
|
||||
fmt.Println("Swagger docs already generated, not generating. Remove the files in " + swaggerDocsFolderLocation + " and run this command again to regenerate them.")
|
||||
return
|
||||
}
|
||||
|
||||
checkAndInstallGoTool("swag", "github.com/swaggo/swag/cmd/swag")
|
||||
runAndStreamOutput("swag", "init", "-g", "./pkg/routes/routes.go", "--parseDependency", "-d", RootPath, "-o", RootPath+"/pkg/swagger")
|
||||
|
@ -460,16 +453,12 @@ func (Build) Clean() error {
|
|||
if err := os.RemoveAll(BinLocation); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
if err := os.RemoveAll(swaggerDocsFolderLocation); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Builds a vikunja binary, ready to run
|
||||
func (Build) Build() {
|
||||
mg.Deps(initVars)
|
||||
mg.Deps(DoTheSwag)
|
||||
runAndStreamOutput("go", "build", Goflags[0], "-tags", Tags, "-ldflags", "-s -w "+Ldflags, "-o", Executable)
|
||||
}
|
||||
|
||||
|
@ -479,7 +468,6 @@ type Release mg.Namespace
|
|||
func (Release) Release(ctx context.Context) error {
|
||||
mg.Deps(initVars)
|
||||
mg.Deps(Release.Dirs)
|
||||
mg.Deps(DoTheSwag)
|
||||
|
||||
// Run compiling in parallel to speed it up
|
||||
errs, _ := errgroup.WithContext(ctx)
|
||||
|
@ -521,7 +509,6 @@ func (Release) Dirs() error {
|
|||
|
||||
func runXgo(targets string) error {
|
||||
mg.Deps(initVars)
|
||||
mg.Deps(DoTheSwag)
|
||||
checkAndInstallGoTool("xgo", "src.techknowlogick.com/xgo")
|
||||
|
||||
extraLdflags := `-linkmode external -extldflags "-static" `
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
"time"
|
||||
|
||||
"code.vikunja.io/api/pkg/config"
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/utils"
|
||||
|
@ -90,8 +91,12 @@ func ParseTaskFromVTODO(content string) (vTask *models.Task, err error) {
|
|||
}
|
||||
// We put the vTodo details in a map to be able to handle them more easily
|
||||
task := make(map[string]ics.IANAProperty)
|
||||
var relation ics.IANAProperty
|
||||
for _, c := range vTodo.UnknownPropertiesIANAProperties() {
|
||||
task[c.IANAToken] = c
|
||||
if strings.HasPrefix(c.IANAToken, "RELATED-TO") {
|
||||
relation = c
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the priority
|
||||
|
@ -134,6 +139,19 @@ func ParseTaskFromVTODO(content string) (vTask *models.Task, err error) {
|
|||
DoneAt: caldavTimeToTimestamp(task["COMPLETED"]),
|
||||
}
|
||||
|
||||
if relation.Value != "" {
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
subtask, err := models.GetTaskSimpleByUUID(s, relation.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vTask.RelatedTasks = make(map[models.RelationKind][]*models.Task)
|
||||
vTask.RelatedTasks[models.RelationKindSubtask] = []*models.Task{subtask}
|
||||
}
|
||||
|
||||
if task["STATUS"].Value == "COMPLETED" {
|
||||
vTask.Done = true
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ func init() {
|
|||
ID: "20230828125443",
|
||||
Description: "",
|
||||
Migrate: func(tx *xorm.Engine) error {
|
||||
return tx.Sync2(typesenseSync20230828125443{})
|
||||
return tx.CreateTables(typesenseSync20230828125443{})
|
||||
},
|
||||
Rollback: func(tx *xorm.Engine) error {
|
||||
return nil
|
||||
|
|
|
@ -59,6 +59,7 @@ func GetTables() []interface{} {
|
|||
&Subscription{},
|
||||
&Favorite{},
|
||||
&APIToken{},
|
||||
&TypesenseSync{},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -368,18 +368,20 @@ func getUserProjectsStatement(parentProjectIDs []int64, userID int64, search str
|
|||
}
|
||||
|
||||
var parentCondition builder.Cond
|
||||
parentCondition = builder.Or(
|
||||
builder.IsNull{"l.parent_project_id"},
|
||||
builder.Eq{"l.parent_project_id": 0},
|
||||
// else check for shared sub projects with a parent
|
||||
builder.And(
|
||||
builder.Or(
|
||||
builder.NotNull{"tm2.user_id"},
|
||||
builder.NotNull{"ul.user_id"},
|
||||
if search == "" {
|
||||
parentCondition = builder.Or(
|
||||
builder.IsNull{"l.parent_project_id"},
|
||||
builder.Eq{"l.parent_project_id": 0},
|
||||
// else check for shared sub projects with a parent
|
||||
builder.And(
|
||||
builder.Or(
|
||||
builder.NotNull{"tm2.user_id"},
|
||||
builder.NotNull{"ul.user_id"},
|
||||
),
|
||||
builder.NotNull{"l.parent_project_id"},
|
||||
),
|
||||
builder.NotNull{"l.parent_project_id"},
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
projectCol := "id"
|
||||
if len(parentProjectIDs) > 0 {
|
||||
parentCondition = builder.In("l.parent_project_id", parentProjectIDs)
|
||||
|
@ -1028,7 +1030,12 @@ func (p *Project) DeleteBackgroundFileIfExists() (err error) {
|
|||
}
|
||||
|
||||
file := files.File{ID: p.BackgroundFileID}
|
||||
return file.Delete()
|
||||
err = file.Delete()
|
||||
if err != nil && files.IsErrFileDoesNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// SetProjectBackground sets a background file as project background in the db
|
||||
|
|
|
@ -118,43 +118,9 @@ func (pd *ProjectDuplicate) Create(s *xorm.Session, doer web.Auth) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// Background files + unsplash info
|
||||
if pd.Project.BackgroundFileID != 0 {
|
||||
|
||||
log.Debugf("Duplicating background %d from project %d into %d", pd.Project.BackgroundFileID, pd.ProjectID, pd.Project.ID)
|
||||
|
||||
f := &files.File{ID: pd.Project.BackgroundFileID}
|
||||
if err := f.LoadFileMetaByID(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f.LoadFileByID(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.File.Close()
|
||||
|
||||
file, err := files.Create(f.File, f.Name, f.Size, doer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get unsplash info if applicable
|
||||
up, err := GetUnsplashPhotoByFileID(s, pd.Project.BackgroundFileID)
|
||||
if err != nil && files.IsErrFileIsNotUnsplashFile(err) {
|
||||
return err
|
||||
}
|
||||
if up != nil {
|
||||
up.ID = 0
|
||||
up.FileID = file.ID
|
||||
if err := up.Save(s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := SetProjectBackground(s, pd.Project.ID, file, pd.Project.BackgroundBlurHash); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("Duplicated project background from project %d into %d", pd.ProjectID, pd.Project.ID)
|
||||
err = duplicateProjectBackground(s, pd, doer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Rights / Shares
|
||||
|
@ -207,6 +173,54 @@ func (pd *ProjectDuplicate) Create(s *xorm.Session, doer web.Auth) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func duplicateProjectBackground(s *xorm.Session, pd *ProjectDuplicate, doer web.Auth) (err error) {
|
||||
if pd.Project.BackgroundFileID == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
log.Debugf("Duplicating background %d from project %d into %d", pd.Project.BackgroundFileID, pd.ProjectID, pd.Project.ID)
|
||||
|
||||
f := &files.File{ID: pd.Project.BackgroundFileID}
|
||||
err = f.LoadFileMetaByID()
|
||||
if err != nil && files.IsErrFileDoesNotExist(err) {
|
||||
pd.Project.BackgroundFileID = 0
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f.LoadFileByID(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.File.Close()
|
||||
|
||||
file, err := files.Create(f.File, f.Name, f.Size, doer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get unsplash info if applicable
|
||||
up, err := GetUnsplashPhotoByFileID(s, pd.Project.BackgroundFileID)
|
||||
if err != nil && !files.IsErrFileIsNotUnsplashFile(err) {
|
||||
return err
|
||||
}
|
||||
if up != nil {
|
||||
up.ID = 0
|
||||
up.FileID = file.ID
|
||||
if err := up.Save(s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := SetProjectBackground(s, pd.Project.ID, file, pd.Project.BackgroundBlurHash); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("Duplicated project background from project %d into %d", pd.ProjectID, pd.Project.ID)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func duplicateTasks(s *xorm.Session, doer web.Auth, ld *ProjectDuplicate, bucketMap map[int64]int64) (err error) {
|
||||
// Get all tasks + all task details
|
||||
tasks, _, _, err := getTasksForProjects(s, []*Project{{ID: ld.ProjectID}}, doer, &taskSearchOptions{})
|
||||
|
|
|
@ -329,8 +329,7 @@ func TestProject_DeleteBackgroundFileIfExists(t *testing.T) {
|
|||
err := SetProjectBackground(s, project.ID, file, "")
|
||||
assert.NoError(t, err)
|
||||
err = project.DeleteBackgroundFileIfExists()
|
||||
assert.Error(t, err)
|
||||
assert.True(t, files.IsErrFileDoesNotExist(err))
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
t.Run("project without background", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
|
|
|
@ -371,6 +371,11 @@ func (bt *BulkTask) GetTasksByIDs(s *xorm.Session) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func GetTaskSimpleByUUID(s *xorm.Session, uid string) (task *Task, err error) {
|
||||
_, err = s.In("uid", uid).Get(task)
|
||||
return
|
||||
}
|
||||
|
||||
// GetTasksByUIDs gets all tasks from a bunch of uids
|
||||
func GetTasksByUIDs(s *xorm.Session, uids []string, a web.Auth) (tasks []*Task, err error) {
|
||||
tasks = []*Task{}
|
||||
|
@ -1076,8 +1081,13 @@ func recalculateTaskKanbanPositions(s *xorm.Session, bucketID int64) (err error)
|
|||
|
||||
currentPosition := maxPosition / float64(len(allTasks)) * (float64(i + 1))
|
||||
|
||||
// Here we use "NoAutoTime() to prevent the ORM from updating column "updated" automatically.
|
||||
// Otherwise, this signals to CalDAV clients that the task has changed, which is not the case.
|
||||
// Consequence: when synchronizing a list of tasks, the first one immediately changes the date of all the
|
||||
// following ones from the same batch, which are then unable to be updated.
|
||||
_, err = s.Cols("kanban_position").
|
||||
Where("id = ?", task.ID).
|
||||
NoAutoTime().
|
||||
Update(&Task{KanbanPosition: currentPosition})
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -1104,8 +1114,13 @@ func recalculateTaskPositions(s *xorm.Session, projectID int64) (err error) {
|
|||
|
||||
currentPosition := maxPosition / float64(len(allTasks)) * (float64(i + 1))
|
||||
|
||||
// Here we use "NoAutoTime() to prevent the ORM from updating column "updated" automatically.
|
||||
// Otherwise, this signals to CalDAV clients that the task has changed, which is not the case.
|
||||
// Consequence: when synchronizing a list of tasks, the first one immediately changes the date of all the
|
||||
// following ones from the same batch, which are then unable to be updated.
|
||||
_, err = s.Cols("position").
|
||||
Where("id = ?", task.ID).
|
||||
NoAutoTime().
|
||||
Update(&Task{Position: currentPosition})
|
||||
if err != nil {
|
||||
return
|
||||
|
|
|
@ -243,6 +243,12 @@ func ReindexAllTasks() (err error) {
|
|||
}
|
||||
|
||||
func reindexTasks(s *xorm.Session, tasks map[int64]*Task) (err error) {
|
||||
|
||||
if len(tasks) == 0 {
|
||||
log.Infof("No tasks to index")
|
||||
return
|
||||
}
|
||||
|
||||
err = addMoreInfoToTasks(s, tasks, &user.User{ID: 1})
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not fetch more task info: %s", err.Error())
|
||||
|
|
|
@ -291,11 +291,13 @@ func (p *Provider) Set(s *xorm.Session, image *background.Image, project *models
|
|||
// Remove the old background if one exists
|
||||
if project.BackgroundFileID != 0 {
|
||||
file := files.File{ID: project.BackgroundFileID}
|
||||
if err := file.Delete(); err != nil {
|
||||
err = file.Delete()
|
||||
if err != nil && !files.IsErrFileDoesNotExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := models.RemoveUnsplashPhoto(s, project.BackgroundFileID); err != nil {
|
||||
err = models.RemoveUnsplashPhoto(s, project.BackgroundFileID)
|
||||
if err != nil && !files.IsErrFileDoesNotExist(err) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,7 +56,8 @@ func (p *Provider) Set(s *xorm.Session, img *background.Image, project *models.P
|
|||
// Remove the old background if one exists
|
||||
if project.BackgroundFileID != 0 {
|
||||
file := files.File{ID: project.BackgroundFileID}
|
||||
if err := file.Delete(); err != nil {
|
||||
err := file.Delete()
|
||||
if err != nil && !files.IsErrFileDoesNotExist(err) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,13 +52,44 @@ func insertFromStructure(s *xorm.Session, str []*models.ProjectWithTasksAndBucke
|
|||
labels := make(map[string]*models.Label)
|
||||
archivedProjects := []int64{}
|
||||
|
||||
childRelations := make(map[int64][]int64) // old id is the key, slice of old children ids
|
||||
projectsByOldID := make(map[int64]*models.Project) // old id is the key
|
||||
// Create all projects
|
||||
for _, p := range str {
|
||||
oldID := p.ID
|
||||
|
||||
if p.ParentProjectID != 0 {
|
||||
childRelations[p.ParentProjectID] = append(childRelations[p.ParentProjectID], oldID)
|
||||
}
|
||||
|
||||
p.ID = 0
|
||||
err = createProjectWithChildren(s, p, 0, &archivedProjects, labels, user)
|
||||
err = createProject(s, p, &archivedProjects, labels, user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
projectsByOldID[oldID] = &p.Project
|
||||
}
|
||||
|
||||
// parent / child relations
|
||||
for parentID, children := range childRelations {
|
||||
parent, has := projectsByOldID[parentID]
|
||||
if !has {
|
||||
log.Debugf("[creating structure] could not find parentID project with old id %d", parentID)
|
||||
continue
|
||||
}
|
||||
for _, childID := range children {
|
||||
child, has := projectsByOldID[childID]
|
||||
if !has {
|
||||
log.Debugf("[creating structure] could not find child project with old id %d for parent project with old id %d", childID, parentID)
|
||||
continue
|
||||
}
|
||||
|
||||
child.ParentProjectID = parent.ID
|
||||
err = child.Update(s, user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(archivedProjects) > 0 {
|
||||
|
@ -76,30 +107,18 @@ func insertFromStructure(s *xorm.Session, str []*models.ProjectWithTasksAndBucke
|
|||
return nil
|
||||
}
|
||||
|
||||
func createProjectWithChildren(s *xorm.Session, project *models.ProjectWithTasksAndBuckets, parentProjectID int64, archivedProjectIDs *[]int64, labels map[string]*models.Label, user *user.User) (err error) {
|
||||
err = createProjectWithEverything(s, project, parentProjectID, archivedProjectIDs, labels, user)
|
||||
func createProject(s *xorm.Session, project *models.ProjectWithTasksAndBuckets, archivedProjectIDs *[]int64, labels map[string]*models.Label, user *user.User) (err error) {
|
||||
err = createProjectWithEverything(s, project, archivedProjectIDs, labels, user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("[creating structure] Created project %d", project.ID)
|
||||
|
||||
if len(project.ChildProjects) > 0 {
|
||||
log.Debugf("[creating structure] Creating %d projects", len(project.ChildProjects))
|
||||
|
||||
// Create all projects
|
||||
for _, cp := range project.ChildProjects {
|
||||
err = createProjectWithChildren(s, cp, project.ID, archivedProjectIDs, labels, user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func createProjectWithEverything(s *xorm.Session, project *models.ProjectWithTasksAndBuckets, parentProjectID int64, archivedProjects *[]int64, labels map[string]*models.Label, user *user.User) (err error) {
|
||||
func createProjectWithEverything(s *xorm.Session, project *models.ProjectWithTasksAndBuckets, archivedProjects *[]int64, labels map[string]*models.Label, user *user.User) (err error) {
|
||||
// The tasks and bucket slices are going to be reset during the creation of the project, so we rescue it here
|
||||
// to be able to still loop over them aftere the project was created.
|
||||
tasks := project.Tasks
|
||||
|
@ -114,9 +133,12 @@ func createProjectWithEverything(s *xorm.Session, project *models.ProjectWithTas
|
|||
project.IsArchived = false
|
||||
}
|
||||
|
||||
project.ParentProjectID = parentProjectID
|
||||
project.ID = 0
|
||||
err = project.Create(s, user)
|
||||
if err != nil && models.IsErrProjectIdentifierIsNotUnique(err) {
|
||||
project.Identifier = ""
|
||||
err = project.Create(s, user)
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -174,15 +196,18 @@ func createProjectWithEverything(s *xorm.Session, project *models.ProjectWithTas
|
|||
}
|
||||
}
|
||||
|
||||
tasksByOldID := make(map[int64]*models.TaskWithComments, len(tasks))
|
||||
// Create all tasks
|
||||
for _, t := range tasks {
|
||||
setBucketOrDefault(&t.Task)
|
||||
|
||||
oldid := t.ID
|
||||
t.ProjectID = project.ID
|
||||
err = t.Create(s, user)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tasksByOldID[oldid] = t
|
||||
|
||||
log.Debugf("[creating structure] Created task %d", t.ID)
|
||||
if len(t.RelatedTasks) > 0 {
|
||||
|
@ -198,13 +223,15 @@ func createProjectWithEverything(s *xorm.Session, project *models.ProjectWithTas
|
|||
|
||||
for _, rt := range tasks {
|
||||
// First create the related tasks if they do not exist
|
||||
if rt.ID == 0 {
|
||||
if _, exists := tasksByOldID[rt.ID]; !exists || rt.ID == 0 {
|
||||
oldid := rt.ID
|
||||
setBucketOrDefault(rt)
|
||||
rt.ProjectID = t.ProjectID
|
||||
err = rt.Create(s, user)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tasksByOldID[oldid] = &models.TaskWithComments{Task: *rt}
|
||||
log.Debugf("[creating structure] Created related task %d", rt.ID)
|
||||
}
|
||||
|
||||
|
@ -214,8 +241,11 @@ func createProjectWithEverything(s *xorm.Session, project *models.ProjectWithTas
|
|||
OtherTaskID: rt.ID,
|
||||
RelationKind: kind,
|
||||
}
|
||||
if ttt, exists := tasksByOldID[rt.ID]; exists {
|
||||
taskRel.OtherTaskID = ttt.ID
|
||||
}
|
||||
err = taskRel.Create(s, user)
|
||||
if err != nil {
|
||||
if err != nil && !models.IsErrRelationAlreadyExists(err) {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ func TestInsertFromStructure(t *testing.T) {
|
|||
testStructure := []*models.ProjectWithTasksAndBuckets{
|
||||
{
|
||||
Project: models.Project{
|
||||
ID: 1,
|
||||
Title: "Test1",
|
||||
Description: "Lorem Ipsum",
|
||||
},
|
||||
|
@ -45,113 +46,112 @@ func TestInsertFromStructure(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
ChildProjects: []*models.ProjectWithTasksAndBuckets{
|
||||
},
|
||||
{
|
||||
Project: models.Project{
|
||||
Title: "Testproject1",
|
||||
Description: "Something",
|
||||
ParentProjectID: 1,
|
||||
},
|
||||
Buckets: []*models.Bucket{
|
||||
{
|
||||
Project: models.Project{
|
||||
Title: "Testproject1",
|
||||
Description: "Something",
|
||||
ID: 1234,
|
||||
Title: "Test Bucket",
|
||||
},
|
||||
},
|
||||
Tasks: []*models.TaskWithComments{
|
||||
{
|
||||
Task: models.Task{
|
||||
Title: "Task1",
|
||||
Description: "Lorem",
|
||||
},
|
||||
Buckets: []*models.Bucket{
|
||||
{
|
||||
ID: 1234,
|
||||
Title: "Test Bucket",
|
||||
},
|
||||
{
|
||||
Task: models.Task{
|
||||
Title: "Task with related tasks",
|
||||
RelatedTasks: map[models.RelationKind][]*models.Task{
|
||||
models.RelationKindSubtask: {
|
||||
{
|
||||
Title: "Related to task with related task",
|
||||
Description: "As subtask",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Tasks: []*models.TaskWithComments{
|
||||
{
|
||||
Task: models.Task{
|
||||
Title: "Task1",
|
||||
Description: "Lorem",
|
||||
},
|
||||
},
|
||||
{
|
||||
Task: models.Task{
|
||||
Title: "Task with related tasks",
|
||||
RelatedTasks: map[models.RelationKind][]*models.Task{
|
||||
models.RelationKindSubtask: {
|
||||
{
|
||||
Title: "Related to task with related task",
|
||||
Description: "As subtask",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Task: models.Task{
|
||||
Title: "Task with attachments",
|
||||
Attachments: []*models.TaskAttachment{
|
||||
{
|
||||
File: &files.File{
|
||||
Name: "testfile",
|
||||
Size: 4,
|
||||
FileContent: []byte{1, 2, 3, 4},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Task: models.Task{
|
||||
Title: "Task with attachments",
|
||||
Attachments: []*models.TaskAttachment{
|
||||
{
|
||||
File: &files.File{
|
||||
Name: "testfile",
|
||||
Size: 4,
|
||||
FileContent: []byte{1, 2, 3, 4},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Task: models.Task{
|
||||
Title: "Task with labels",
|
||||
Labels: []*models.Label{
|
||||
{
|
||||
Title: "Label1",
|
||||
HexColor: "ff00ff",
|
||||
},
|
||||
{
|
||||
Title: "Label2",
|
||||
HexColor: "ff00ff",
|
||||
},
|
||||
},
|
||||
{
|
||||
Task: models.Task{
|
||||
Title: "Task with labels",
|
||||
Labels: []*models.Label{
|
||||
{
|
||||
Title: "Label1",
|
||||
HexColor: "ff00ff",
|
||||
},
|
||||
{
|
||||
Title: "Label2",
|
||||
HexColor: "ff00ff",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Task: models.Task{
|
||||
Title: "Task with same label",
|
||||
Labels: []*models.Label{
|
||||
{
|
||||
Title: "Label1",
|
||||
HexColor: "ff00ff",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Task: models.Task{
|
||||
Title: "Task in a bucket",
|
||||
BucketID: 1234,
|
||||
},
|
||||
},
|
||||
{
|
||||
Task: models.Task{
|
||||
Title: "Task in a nonexisting bucket",
|
||||
BucketID: 1111,
|
||||
},
|
||||
},
|
||||
{
|
||||
Task: models.Task{
|
||||
Title: "Task with same label",
|
||||
Labels: []*models.Label{
|
||||
{
|
||||
Title: "Label1",
|
||||
HexColor: "ff00ff",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Task: models.Task{
|
||||
Title: "Task in a bucket",
|
||||
BucketID: 1234,
|
||||
},
|
||||
},
|
||||
{
|
||||
Task: models.Task{
|
||||
Title: "Task in a nonexisting bucket",
|
||||
BucketID: 1111,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
err := InsertFromStructure(testStructure, u)
|
||||
assert.NoError(t, err)
|
||||
db.AssertExists(t, "projects", map[string]interface{}{
|
||||
"title": testStructure[0].ChildProjects[0].Title,
|
||||
"description": testStructure[0].ChildProjects[0].Description,
|
||||
"title": testStructure[1].Title,
|
||||
"description": testStructure[1].Description,
|
||||
}, false)
|
||||
db.AssertExists(t, "tasks", map[string]interface{}{
|
||||
"title": testStructure[0].ChildProjects[0].Tasks[5].Title,
|
||||
"bucket_id": testStructure[0].ChildProjects[0].Buckets[0].ID,
|
||||
"title": testStructure[1].Tasks[5].Title,
|
||||
"bucket_id": testStructure[1].Buckets[0].ID,
|
||||
}, false)
|
||||
db.AssertMissing(t, "tasks", map[string]interface{}{
|
||||
"title": testStructure[0].ChildProjects[0].Tasks[6].Title,
|
||||
"title": testStructure[1].Tasks[6].Title,
|
||||
"bucket_id": 1111, // No task with that bucket should exist
|
||||
})
|
||||
db.AssertExists(t, "tasks", map[string]interface{}{
|
||||
"title": testStructure[0].Tasks[0].Title,
|
||||
}, false)
|
||||
assert.NotEqual(t, 0, testStructure[0].ChildProjects[0].Tasks[0].BucketID) // Should get the default bucket
|
||||
assert.NotEqual(t, 0, testStructure[0].ChildProjects[0].Tasks[6].BucketID) // Should get the default bucket
|
||||
assert.NotEqual(t, 0, testStructure[1].Tasks[0].BucketID) // Should get the default bucket
|
||||
assert.NotEqual(t, 0, testStructure[1].Tasks[6].BucketID) // Should get the default bucket
|
||||
})
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import (
|
|||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/modules/migration"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
vversion "code.vikunja.io/api/pkg/version"
|
||||
|
||||
"github.com/hashicorp/go-version"
|
||||
)
|
||||
|
@ -119,17 +120,22 @@ func (v *FileMigrator) Migrate(user *user.User, file io.ReaderAt, size int64) er
|
|||
return fmt.Errorf("could not read version file: %w", err)
|
||||
}
|
||||
|
||||
dumpedVersion, err := version.NewVersion(bufVersion.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
minVersion, err := version.NewVersion("0.20.1+61")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
versionString := bufVersion.String()
|
||||
if versionString == "dev" && vversion.Version == "dev" {
|
||||
log.Debugf(logPrefix + "Importing from dev version")
|
||||
} else {
|
||||
dumpedVersion, err := version.NewVersion(bufVersion.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
minVersion, err := version.NewVersion("0.20.1+61")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if dumpedVersion.LessThan(minVersion) {
|
||||
return fmt.Errorf("export was created with an older version, need at least %s but the export needs at least %s", dumpedVersion, minVersion)
|
||||
if dumpedVersion.LessThan(minVersion) {
|
||||
return fmt.Errorf("export was created with an older version, need at least %s but the export needs at least %s", dumpedVersion, minVersion)
|
||||
}
|
||||
}
|
||||
|
||||
//////
|
||||
|
|
|
@ -133,9 +133,7 @@ func (vcls *VikunjaCaldavProjectStorage) GetResourcesByList(rpaths []string) ([]
|
|||
var uids []string
|
||||
for _, path := range rpaths {
|
||||
parts := strings.Split(path, "/")
|
||||
uid := []rune(parts[4]) // The 4th part is the id with ".ics" suffix
|
||||
endlen := len(uid) - len(".ics") // ".ics" are 4 bytes
|
||||
uids = append(uids, string(uid[:endlen]))
|
||||
uids = append(uids, strings.TrimSuffix(parts[4], ".ics"))
|
||||
}
|
||||
|
||||
s := db.NewSession()
|
||||
|
|
8663
pkg/swagger/docs.go
Normal file
8663
pkg/swagger/docs.go
Normal file
File diff suppressed because it is too large
Load Diff
8638
pkg/swagger/swagger.json
Normal file
8638
pkg/swagger/swagger.json
Normal file
File diff suppressed because it is too large
Load Diff
5911
pkg/swagger/swagger.yaml
Normal file
5911
pkg/swagger/swagger.yaml
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -1,26 +0,0 @@
|
|||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-present2023 Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public Licensee for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public Licensee
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//go:build swagger
|
||||
// +build swagger
|
||||
|
||||
package version
|
||||
|
||||
import "code.vikunja.io/api/pkg/swagger"
|
||||
|
||||
func init() {
|
||||
// Additional swagger information
|
||||
swagger.SwaggerInfo.Version = Version
|
||||
}
|
|
@ -16,8 +16,15 @@
|
|||
|
||||
package version
|
||||
|
||||
import "code.vikunja.io/api/pkg/swagger"
|
||||
|
||||
// This package holds the version info
|
||||
// It is an own package to avoid import cycles
|
||||
|
||||
// Version sets the version to be printed to the user. Gets overwritten by "make release" or "make build" with last git commit or tag.
|
||||
var Version = "dev"
|
||||
|
||||
func init() {
|
||||
// Additional swagger information
|
||||
swagger.SwaggerInfo.Version = Version
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user