forked from vikunja/vikunja
Compare commits
1 Commits
main
...
renovate/g
Author | SHA1 | Date | |
---|---|---|---|
58a096f281 |
79
.drone.yml
79
.drone.yml
|
@ -121,12 +121,23 @@ steps:
|
|||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
- name: build
|
||||
- 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 ]
|
||||
commands:
|
||||
- ./mage-static build:build
|
||||
when:
|
||||
|
@ -219,7 +230,7 @@ steps:
|
|||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
commands:
|
||||
- ./mage-static test:unit
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
depends_on: [ fetch-tags, prepare-build ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
|
@ -236,7 +247,7 @@ steps:
|
|||
path: /db
|
||||
commands:
|
||||
- ./mage-static test:unit
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
depends_on: [ fetch-tags, prepare-build ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
|
@ -253,7 +264,7 @@ steps:
|
|||
VIKUNJA_DATABASE_DATABASE: vikunjatest
|
||||
commands:
|
||||
- ./mage-static test:unit
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
depends_on: [ fetch-tags, prepare-build ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
|
@ -271,7 +282,7 @@ steps:
|
|||
VIKUNJA_DATABASE_SSLMODE: disable
|
||||
commands:
|
||||
- ./mage-static test:unit
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
depends_on: [ fetch-tags, prepare-build ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
|
@ -282,7 +293,7 @@ steps:
|
|||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
commands:
|
||||
- ./mage-static test:integration
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
depends_on: [ fetch-tags, prepare-build ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
|
@ -299,7 +310,7 @@ steps:
|
|||
path: /db
|
||||
commands:
|
||||
- ./mage-static test:integration
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
depends_on: [ fetch-tags, prepare-build ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
|
@ -316,7 +327,7 @@ steps:
|
|||
VIKUNJA_DATABASE_DATABASE: vikunjatest
|
||||
commands:
|
||||
- ./mage-static test:integration
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
depends_on: [ fetch-tags, prepare-build ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
|
@ -334,54 +345,10 @@ steps:
|
|||
VIKUNJA_DATABASE_SSLMODE: disable
|
||||
commands:
|
||||
- ./mage-static test:integration
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
depends_on: [ fetch-tags, prepare-build ]
|
||||
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
|
||||
|
@ -429,6 +396,7 @@ 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
|
||||
|
@ -438,7 +406,6 @@ 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
|
||||
|
@ -452,7 +419,6 @@ 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
|
||||
|
@ -466,7 +432,6 @@ 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
|
||||
|
@ -778,6 +743,6 @@ steps:
|
|||
- failure
|
||||
---
|
||||
kind: signature
|
||||
hmac: 6bc74f5b7e9c51e725100e05f07cdac656d6c3d49d19c2b112aed812c86e7a9a
|
||||
hmac: d47bd1cf6f3e9be2ff3eed2039e65c8b6de2b16c1e636699f66382f941277411
|
||||
|
||||
...
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -27,3 +27,4 @@ vikunja-dump*
|
|||
vendor/
|
||||
os-packages/
|
||||
mage_output_file.go
|
||||
pkg/swagger/*
|
||||
|
|
|
@ -13,7 +13,6 @@ 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}"
|
||||
|
|
2
go.mod
2
go.mod
|
@ -67,7 +67,7 @@ require (
|
|||
github.com/yuin/goldmark v1.5.4
|
||||
golang.org/x/crypto v0.12.0
|
||||
golang.org/x/image v0.11.0
|
||||
golang.org/x/oauth2 v0.10.0
|
||||
golang.org/x/oauth2 v0.12.0
|
||||
golang.org/x/sync v0.3.0
|
||||
golang.org/x/sys v0.11.0
|
||||
golang.org/x/term v0.11.0
|
||||
|
|
13
magefile.go
13
magefile.go
|
@ -177,6 +177,9 @@ 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()
|
||||
|
@ -346,6 +349,10 @@ 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")
|
||||
|
@ -453,12 +460,16 @@ 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)
|
||||
}
|
||||
|
||||
|
@ -468,6 +479,7 @@ 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)
|
||||
|
@ -509,6 +521,7 @@ 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,7 +23,6 @@ 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"
|
||||
|
@ -91,12 +90,8 @@ 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
|
||||
|
@ -139,19 +134,6 @@ 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.CreateTables(typesenseSync20230828125443{})
|
||||
return tx.Sync2(typesenseSync20230828125443{})
|
||||
},
|
||||
Rollback: func(tx *xorm.Engine) error {
|
||||
return nil
|
||||
|
|
|
@ -59,7 +59,6 @@ func GetTables() []interface{} {
|
|||
&Subscription{},
|
||||
&Favorite{},
|
||||
&APIToken{},
|
||||
&TypesenseSync{},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -368,20 +368,18 @@ func getUserProjectsStatement(parentProjectIDs []int64, userID int64, search str
|
|||
}
|
||||
|
||||
var parentCondition builder.Cond
|
||||
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"},
|
||||
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"},
|
||||
),
|
||||
)
|
||||
projectCol := "id"
|
||||
if len(parentProjectIDs) > 0 {
|
||||
parentCondition = builder.In("l.parent_project_id", parentProjectIDs)
|
||||
|
@ -1030,12 +1028,7 @@ func (p *Project) DeleteBackgroundFileIfExists() (err error) {
|
|||
}
|
||||
|
||||
file := files.File{ID: p.BackgroundFileID}
|
||||
err = file.Delete()
|
||||
if err != nil && files.IsErrFileDoesNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
return file.Delete()
|
||||
}
|
||||
|
||||
// SetProjectBackground sets a background file as project background in the db
|
||||
|
|
|
@ -118,9 +118,43 @@ func (pd *ProjectDuplicate) Create(s *xorm.Session, doer web.Auth) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
err = duplicateProjectBackground(s, pd, doer)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
// Rights / Shares
|
||||
|
@ -173,54 +207,6 @@ 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,7 +329,8 @@ func TestProject_DeleteBackgroundFileIfExists(t *testing.T) {
|
|||
err := SetProjectBackground(s, project.ID, file, "")
|
||||
assert.NoError(t, err)
|
||||
err = project.DeleteBackgroundFileIfExists()
|
||||
assert.NoError(t, err)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, files.IsErrFileDoesNotExist(err))
|
||||
})
|
||||
t.Run("project without background", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
|
|
|
@ -371,11 +371,6 @@ 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{}
|
||||
|
@ -1081,13 +1076,8 @@ 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
|
||||
|
@ -1114,13 +1104,8 @@ 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,12 +243,6 @@ 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,13 +291,11 @@ 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}
|
||||
err = file.Delete()
|
||||
if err != nil && !files.IsErrFileDoesNotExist(err) {
|
||||
if err := file.Delete(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = models.RemoveUnsplashPhoto(s, project.BackgroundFileID)
|
||||
if err != nil && !files.IsErrFileDoesNotExist(err) {
|
||||
if err := models.RemoveUnsplashPhoto(s, project.BackgroundFileID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,8 +56,7 @@ 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}
|
||||
err := file.Delete()
|
||||
if err != nil && !files.IsErrFileDoesNotExist(err) {
|
||||
if err := file.Delete(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,44 +52,13 @@ 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 = createProject(s, p, &archivedProjects, labels, user)
|
||||
err = createProjectWithChildren(s, p, 0, &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 {
|
||||
|
@ -107,18 +76,30 @@ func insertFromStructure(s *xorm.Session, str []*models.ProjectWithTasksAndBucke
|
|||
return nil
|
||||
}
|
||||
|
||||
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)
|
||||
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)
|
||||
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, archivedProjects *[]int64, labels map[string]*models.Label, user *user.User) (err error) {
|
||||
func createProjectWithEverything(s *xorm.Session, project *models.ProjectWithTasksAndBuckets, parentProjectID int64, 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
|
||||
|
@ -133,12 +114,9 @@ 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
|
||||
}
|
||||
|
@ -196,18 +174,15 @@ 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 {
|
||||
|
@ -223,15 +198,13 @@ func createProjectWithEverything(s *xorm.Session, project *models.ProjectWithTas
|
|||
|
||||
for _, rt := range tasks {
|
||||
// First create the related tasks if they do not exist
|
||||
if _, exists := tasksByOldID[rt.ID]; !exists || rt.ID == 0 {
|
||||
oldid := rt.ID
|
||||
if rt.ID == 0 {
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -241,11 +214,8 @@ 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 && !models.IsErrRelationAlreadyExists(err) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,6 @@ func TestInsertFromStructure(t *testing.T) {
|
|||
testStructure := []*models.ProjectWithTasksAndBuckets{
|
||||
{
|
||||
Project: models.Project{
|
||||
ID: 1,
|
||||
Title: "Test1",
|
||||
Description: "Lorem Ipsum",
|
||||
},
|
||||
|
@ -46,89 +45,90 @@ func TestInsertFromStructure(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Project: models.Project{
|
||||
Title: "Testproject1",
|
||||
Description: "Something",
|
||||
ParentProjectID: 1,
|
||||
},
|
||||
Buckets: []*models.Bucket{
|
||||
ChildProjects: []*models.ProjectWithTasksAndBuckets{
|
||||
{
|
||||
ID: 1234,
|
||||
Title: "Test Bucket",
|
||||
},
|
||||
},
|
||||
Tasks: []*models.TaskWithComments{
|
||||
{
|
||||
Task: models.Task{
|
||||
Title: "Task1",
|
||||
Description: "Lorem",
|
||||
Project: models.Project{
|
||||
Title: "Testproject1",
|
||||
Description: "Something",
|
||||
},
|
||||
},
|
||||
{
|
||||
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",
|
||||
Buckets: []*models.Bucket{
|
||||
{
|
||||
ID: 1234,
|
||||
Title: "Test Bucket",
|
||||
},
|
||||
},
|
||||
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 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 in a bucket",
|
||||
BucketID: 1234,
|
||||
},
|
||||
},
|
||||
{
|
||||
Task: models.Task{
|
||||
Title: "Task in a nonexisting bucket",
|
||||
BucketID: 1111,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -137,21 +137,21 @@ func TestInsertFromStructure(t *testing.T) {
|
|||
err := InsertFromStructure(testStructure, u)
|
||||
assert.NoError(t, err)
|
||||
db.AssertExists(t, "projects", map[string]interface{}{
|
||||
"title": testStructure[1].Title,
|
||||
"description": testStructure[1].Description,
|
||||
"title": testStructure[0].ChildProjects[0].Title,
|
||||
"description": testStructure[0].ChildProjects[0].Description,
|
||||
}, false)
|
||||
db.AssertExists(t, "tasks", map[string]interface{}{
|
||||
"title": testStructure[1].Tasks[5].Title,
|
||||
"bucket_id": testStructure[1].Buckets[0].ID,
|
||||
"title": testStructure[0].ChildProjects[0].Tasks[5].Title,
|
||||
"bucket_id": testStructure[0].ChildProjects[0].Buckets[0].ID,
|
||||
}, false)
|
||||
db.AssertMissing(t, "tasks", map[string]interface{}{
|
||||
"title": testStructure[1].Tasks[6].Title,
|
||||
"title": testStructure[0].ChildProjects[0].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[1].Tasks[0].BucketID) // Should get the default bucket
|
||||
assert.NotEqual(t, 0, testStructure[1].Tasks[6].BucketID) // Should get the default bucket
|
||||
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
|
||||
})
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@ 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"
|
||||
)
|
||||
|
@ -120,22 +119,17 @@ func (v *FileMigrator) Migrate(user *user.User, file io.ReaderAt, size int64) er
|
|||
return fmt.Errorf("could not read version file: %w", 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
|
||||
}
|
||||
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,7 +133,9 @@ func (vcls *VikunjaCaldavProjectStorage) GetResourcesByList(rpaths []string) ([]
|
|||
var uids []string
|
||||
for _, path := range rpaths {
|
||||
parts := strings.Split(path, "/")
|
||||
uids = append(uids, strings.TrimSuffix(parts[4], ".ics"))
|
||||
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]))
|
||||
}
|
||||
|
||||
s := db.NewSession()
|
||||
|
|
8663
pkg/swagger/docs.go
8663
pkg/swagger/docs.go
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
26
pkg/version/swagger.go
Normal file
26
pkg/version/swagger.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
// 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,15 +16,8 @@
|
|||
|
||||
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