Compare commits
21 Commits
343a964d90
...
8169cd5217
Author | SHA1 | Date |
---|---|---|
renovate | 8169cd5217 | |
kolaente | 45c97a456d | |
kolaente | 1255bdc4ab | |
kolaente | 99856b2031 | |
kolaente | eec53e8a54 | |
kolaente | d4a389279c | |
renovate | 3a65324a7d | |
renovate | bec11d42fc | |
renovate | d2eeac2dbe | |
renovate | 05e7468135 | |
renovate | 4babe72f24 | |
renovate | e9fe927644 | |
renovate | 7cefb86a78 | |
kolaente | 6c4122264a | |
kolaente | 4162c64ed9 | |
kolaente | 2e57b6e409 | |
kolaente | 8752ae2a0b | |
kolaente | 7edb53ca12 | |
kolaente | 25a03d1789 | |
kolaente | 5ab9fb89bb | |
kolaente | 55e271b329 |
34
.drone.yml
34
.drone.yml
|
@ -681,7 +681,7 @@ steps:
|
|||
path_style: true
|
||||
strip_prefix: dist/zip/
|
||||
source: dist/zip/*
|
||||
target: /api/unstable/
|
||||
target: /vikunja/unstable/
|
||||
when:
|
||||
branch:
|
||||
- main
|
||||
|
@ -703,7 +703,7 @@ steps:
|
|||
path_style: true
|
||||
strip_prefix: dist/zip/
|
||||
source: dist/zip/*
|
||||
target: /api/${DRONE_TAG##v}/
|
||||
target: /vikunja/${DRONE_TAG##v}/
|
||||
when:
|
||||
event:
|
||||
- tag
|
||||
|
@ -755,7 +755,7 @@ steps:
|
|||
path_style: true
|
||||
strip_prefix: dist/os-packages/
|
||||
source: dist/os-packages/*
|
||||
target: /api/unstable/
|
||||
target: /vikunja/unstable/
|
||||
when:
|
||||
branch:
|
||||
- main
|
||||
|
@ -777,7 +777,7 @@ steps:
|
|||
path_style: true
|
||||
strip_prefix: dist/os-packages/
|
||||
source: dist/os-packages/*
|
||||
target: /api/${DRONE_TAG##v}/
|
||||
target: /vikunja/${DRONE_TAG##v}/
|
||||
when:
|
||||
event:
|
||||
- tag
|
||||
|
@ -919,7 +919,7 @@ steps:
|
|||
region: fr-par
|
||||
path_style: true
|
||||
source: frontend/vikunja-frontend-unstable.zip
|
||||
target: /frontend/
|
||||
target: /
|
||||
depends_on: [ static ]
|
||||
|
||||
---
|
||||
|
@ -978,7 +978,7 @@ steps:
|
|||
region: fr-par
|
||||
path_style: true
|
||||
source: frontend/vikunja-frontend-${DRONE_TAG##v}.zip
|
||||
target: /frontend/
|
||||
target: /
|
||||
depends_on: [ static ]
|
||||
|
||||
---
|
||||
|
@ -1199,8 +1199,21 @@ steps:
|
|||
# - '.cache'
|
||||
# depends_on:
|
||||
# - build
|
||||
|
||||
- name: rename-unstable
|
||||
image: apline
|
||||
pull: true
|
||||
commands:
|
||||
- cd desktop/dist
|
||||
- for file in Vikunja*; do suffix=".${file##*.}"; if [[ ! -d $file ]]; then mv "$file" "Vikunja-Desktop-unstable${suffix}"; fi; done
|
||||
depends_on:
|
||||
- build
|
||||
when:
|
||||
event:
|
||||
exclude:
|
||||
- tag
|
||||
|
||||
- name: release-latest
|
||||
- name: release-unstable
|
||||
image: plugins/s3
|
||||
pull: true
|
||||
settings:
|
||||
|
@ -1213,13 +1226,14 @@ steps:
|
|||
region: fr-par
|
||||
path_style: true
|
||||
strip_prefix: desktop/dist/
|
||||
source: desktop/dist/*
|
||||
source: desktop/dist/Vikunja-Desktop*
|
||||
target: /desktop/unstable/
|
||||
when:
|
||||
event:
|
||||
exclude:
|
||||
- tag
|
||||
depends_on: [ build ]
|
||||
depends_on:
|
||||
- rename-unstable
|
||||
|
||||
- name: release-version
|
||||
image: plugins/s3
|
||||
|
@ -1360,6 +1374,6 @@ steps:
|
|||
- failure
|
||||
---
|
||||
kind: signature
|
||||
hmac: 25297bbae6b6b8e388bd78cfeed7be41b7bd9958efb59061c95e7514bdebb085
|
||||
hmac: 90827e8c19d58f370a55c1ba17d188461e3aa1fe1b27cce52fa58925227894de
|
||||
|
||||
...
|
||||
|
|
102
CHANGELOG.md
102
CHANGELOG.md
|
@ -7,6 +7,108 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|||
|
||||
All releases can be found on https://code.vikunja.io/api/releases.
|
||||
|
||||
## [0.23.0] - 2024-02-10
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* *(assignees)* Use correct amount of spacing in assignee selection
|
||||
* *(ci)* cd to frontend in frontend pipelines
|
||||
* *(ci)* Deploy packages into the correct directory
|
||||
* *(ci)* Swagger docs generate should use the correct url
|
||||
* *(ci)* Typo
|
||||
* *(ci)* Update shasum
|
||||
* *(docs)* Old install pages redirect
|
||||
* *(editor)* Don't set editor content intitially
|
||||
* *(export)* Don't crash when an exported file does not exist
|
||||
* *(filters)* Add explicit check for string slice filter
|
||||
* *(gantt)* Correctly import languages from dayjs
|
||||
* *(kanban)* Assignee spacing
|
||||
* *(kanban)* Bottom spacing of labels
|
||||
* *(notifications)* Mark all notifications as read in ui directly when marking as read on the server
|
||||
* *(progress)* Cleanup unused css
|
||||
* *(progress)* Less rounding
|
||||
* *(reminders)* Set reminder date on datepicker when editing a reminder
|
||||
* *(task)* Make sure the drag handle is shown as intended
|
||||
* *(task)* Move cover image setter to store
|
||||
* *(task)* Remove default task color
|
||||
* *(tasks)* Check for cycles during creation of task relations and prevent them
|
||||
* *(tasks)* Show any errors happening during task load
|
||||
* *(tests)* Adjust gantt rows identifier
|
||||
* *(webhook)* Fetch all event details before sending the webhook
|
||||
|
||||
### Features
|
||||
|
||||
* Merge API, Frontend and Desktop repos
|
||||
* *(ci)* Combine api and frontend drone configs
|
||||
* *(ci)* Merge desktop ci config
|
||||
* *(ci)* Save .tags file to generate release tags
|
||||
* *(ci)* Run desktop build without waiting on the frontend when not doing release builds
|
||||
* *(ci)* Run desktop pipeline only on PRs
|
||||
* *(editor)* Use primary color for currently selected node
|
||||
* *(filters)* Log type if unknown filter type
|
||||
* *(progress)* Move customizations into progress bar component
|
||||
|
||||
### Dependencies
|
||||
|
||||
* *(deps)* Update dependency @4tw/cypress-drag-drop to v1.8.1 (#693)
|
||||
* *(deps)* Update dependency @fortawesome/vue-fontawesome to v3.0.6
|
||||
* *(deps)* Update dependency @kyvg/vue3-notification to v3.1.4
|
||||
* *(deps)* Update dependency @types/node to v20.11.10
|
||||
* *(deps)* Update dependency autoprefixer to v10.3.3 (#684)
|
||||
* *(deps)* Update dependency autoprefixer to v10.3.4 (#697)
|
||||
* *(deps)* Update dependency axios to v0.21.2 (#698)
|
||||
* *(deps)* Update dependency axios to v0.21.3 (#700)
|
||||
* *(deps)* Update dependency cypress to v8.3.1 (#689)
|
||||
* *(deps)* Update dependency electron to v28.2.1 (#186)
|
||||
* *(deps)* Update dependency electron to v28.2.2 (#187)
|
||||
* *(deps)* Update dependency esbuild to v0.12.23 (#683)
|
||||
* *(deps)* Update dependency esbuild to v0.12.24 (#688)
|
||||
* *(deps)* Update dependency esbuild to v0.12.25 (#696)
|
||||
* *(deps)* Update dependency esbuild to v0.14.53 (#2217)
|
||||
* *(deps)* Update dependency eslint-plugin-vue to v7.17.0 (#686)
|
||||
* *(deps)* Update dependency floating-vue to v5.2.1
|
||||
* *(deps)* Update dependency floating-vue to v5.2.2
|
||||
* *(deps)* Update dependency jest to v27.1.0 (#687)
|
||||
* *(deps)* Update dependency marked to v3.0.1 (#677)
|
||||
* *(deps)* Update dependency marked to v3.0.2 (#682)
|
||||
* *(deps)* Update dependency postcss to v8.4.19 (#2673)
|
||||
* *(deps)* Update dependency sass to v1.38.1 (#679)
|
||||
* *(deps)* Update dependency sass to v1.38.2 (#690)
|
||||
* *(deps)* Update dependency sass to v1.39.0 (#695)
|
||||
* *(deps)* Update dependency typescript to v4.4.2 (#685)
|
||||
* *(deps)* Update dependency ufo to v1.4.0
|
||||
* *(deps)* Update dependency vite to v2.5.1 (#680)
|
||||
* *(deps)* Update dependency vite to v2.5.2 (#692)
|
||||
* *(deps)* Update dependency vite to v2.5.3 (#694)
|
||||
* *(deps)* Update dependency vite-plugin-pwa to v0.11.2 (#681)
|
||||
* *(deps)* Update dependency vue to v3.2.45
|
||||
* *(deps)* Update dependency vue-i18n to v9.9.1
|
||||
* *(deps)* Update goreleaser/nfpm docker tag to v2.35.3 (#1692)
|
||||
* *(deps)* Update module github.com/arran4/golang-ical to v0.2.4
|
||||
* *(deps)* Update module github.com/mattn/go-sqlite3 to v1.14.21
|
||||
* *(deps)* Update module github.com/mattn/go-sqlite3 to v1.14.22
|
||||
* *(deps)* Update module github.com/swaggo/swag to v1.16.3
|
||||
* *(deps)* Update module github.com/yuin/goldmark to v1.7.0
|
||||
* *(deps)* Update pnpm to v8.15.0
|
||||
* *(deps)* Update pnpm to v8.15.1
|
||||
* *(deps)* Update sentry-javascript monorepo to v7.100.1
|
||||
* *(deps)* Update sentry-javascript monorepo to v7.17.2 (#2587)
|
||||
* *(deps)* Update sentry-javascript monorepo to v7.19.0 (#2670)
|
||||
* *(deps)* Update sentry-javascript monorepo to v7.99.0
|
||||
* *(deps)* Update src.techknowlogick.com/xgo digest to 45b9ea6
|
||||
* *(deps)* Update src.techknowlogick.com/xgo digest to 5aae655
|
||||
* *(deps)* Update tiptap to v2.2.0
|
||||
* *(deps)* Update tiptap to v2.2.1
|
||||
* *(deps)* Update typescript-eslint monorepo to v4.29.3 (#676)
|
||||
* *(deps)* Update typescript-eslint monorepo to v4.30.0 (#691)
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
* *(Expandable)* Spelling ⛈
|
||||
* *(deps)* Move renovate config
|
||||
* *(deps)* Remove redundant renovate config
|
||||
* *(quick actions)* Format
|
||||
|
||||
## [0.22.1] - 2024-01-28
|
||||
|
||||
### Bug Fixes
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
|
||||
[![Build Status](https://drone.kolaente.de/api/badges/vikunja/vikunjaa/status.svg)](https://drone.kolaente.de/vikunja/vikunja)
|
||||
[![License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](LICENSE)
|
||||
[![Download](https://img.shields.io/badge/download-v0.22.1-brightgreen.svg)](https://dl.vikunja.io)
|
||||
[![Download](https://img.shields.io/badge/download-v0.23.0-brightgreen.svg)](https://dl.vikunja.io)
|
||||
[![Docker Pulls](https://img.shields.io/docker/pulls/vikunja/vikunja.svg)](https://hub.docker.com/r/vikunja/vikunja/)
|
||||
[![Swagger Docs](https://img.shields.io/badge/swagger-docs-brightgreen.svg)](https://try.vikunja.io/api/v1/docs)
|
||||
[![Go Report Card](https://goreportcard.com/badge/kolaente.dev/vikunja/vikunja)](https://goreportcard.com/report/kolaente.dev/vikunja/vikunja)
|
||||
|
||||
# Vikunja API
|
||||
# Vikunja
|
||||
|
||||
> The Todo-app to organize your life.
|
||||
|
||||
|
@ -43,12 +43,9 @@ All docs can be found on [the Vikunja home page](https://vikunja.io/docs/).
|
|||
|
||||
See [the roadmap](https://my.vikunja.cloud/share/QFyzYEmEYfSyQfTOmIRSwLUpkFjboaBqQCnaPmWd/auth) (hosted on Vikunja!) for more!
|
||||
|
||||
* [ ] [Mobile apps](https://code.vikunja.io/app) (separate repo) *In Progress*
|
||||
* [ ] [Webapp](https://code.vikunja.io/frontend) (separate repo) *In Progress*
|
||||
|
||||
## Contributing
|
||||
|
||||
Fork -> Push -> Pull-Request. Also see the [dev docs](https://vikunja.io/docs/development/) for more info.
|
||||
Please check out the contribuition guidelines on [the website](https://vikunja.io/docs/development/).
|
||||
|
||||
## License
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
# Changelog
|
||||
|
||||
THIS CHANGELOG ONLY EXISTS FOR HISTORICAL REASONS.
|
||||
Starting with version 0.23.0, all changes are logged in the CHANGELOG.md in the root of this repository since the repos were merged.
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
|
|
|
@ -2,9 +2,8 @@
|
|||
|
||||
set -xe
|
||||
|
||||
frontend_version=$(sed -n 's/.*"VERSION": "\([^"]*\)".*/\1/p' ./frontend/version.json)
|
||||
frontend_version=$(git describe --tags --always --abbrev=10)
|
||||
|
||||
sed -i "s/\${version}/$frontend_version/g" package.json
|
||||
|
||||
sed -i "s/\"version\": \".*\"/\"version\": \"$frontend_version\"/" package.json
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
"electron-builder": "24.9.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"connect-history-api-fallback": "^2.0.0",
|
||||
"express": "^4.17.1"
|
||||
"connect-history-api-fallback": "2.0.0",
|
||||
"express": "4.18.2"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -558,7 +558,7 @@ config-file-ts@^0.2.4:
|
|||
glob "^7.1.6"
|
||||
typescript "^4.0.2"
|
||||
|
||||
connect-history-api-fallback@^2.0.0:
|
||||
connect-history-api-fallback@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8"
|
||||
integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==
|
||||
|
@ -829,7 +829,7 @@ etag@~1.8.1:
|
|||
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
|
||||
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
|
||||
|
||||
express@^4.17.1:
|
||||
express@4.18.2:
|
||||
version "4.18.2"
|
||||
resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59"
|
||||
integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
title: "Desktop Packages"
|
||||
date: 2024-02-11T15:58:18+01:00
|
||||
draft: false
|
||||
type: "doc"
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "setup"
|
||||
---
|
||||
|
||||
# Desktop Packages
|
||||
|
||||
Vikunja is available as an electron-based desktop application for Linux and Windows.
|
||||
|
||||
## Installation
|
||||
|
||||
1. Download the latest release for your platform from [the download page](https://dl.vikunja.io/desktop/).
|
||||
* For Windows, choose the file with the `.exe` or `.msi` file ending
|
||||
* For a Linux-based operating system, choose a file with an ending for your operating system - we have builds for Alpine, AppImage, Arch Linux, Debian-based systems, FreeBSD, Fedora and Snap.
|
||||
2. Run the downloaded package in the same way you would normally install a package for your OS.
|
||||
|
||||
## Flatpack
|
||||
|
||||
Vikunja Desktop can be installed via the [Flathub](https://flathub.org/apps/io.vikunja.Vikunja).
|
||||
|
||||
To install it, run the following command:
|
||||
|
||||
```
|
||||
flatpak install flathub io.vikunja.Vikunja
|
||||
```
|
|
@ -160,6 +160,7 @@ services:
|
|||
restart: unless-stopped
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.docker.network=web"
|
||||
- "traefik.http.routers.vikunja.rule=Host(`vikunja.example.com`)"
|
||||
- "traefik.http.routers.vikunja.entrypoints=https"
|
||||
- "traefik.http.routers.vikunja.tls.certResolver=acme"
|
||||
|
|
|
@ -29,10 +29,11 @@ You can also:
|
|||
Vikunja can be installed in various ways.
|
||||
This document provides an overview and instructions for the different methods:
|
||||
|
||||
* [Installing from binary](#install-from-binary)
|
||||
* [Installing from binary (manual)](#install-from-binary)
|
||||
* [Build from source]({{< ref "build-from-source.md">}})
|
||||
* [Docker](#docker)
|
||||
* [Debian packages](#debian-packages)
|
||||
* [Debian](#debian-packages)
|
||||
* [RPM](#rpm)
|
||||
* [FreeBSD](#freebsd--freenas)
|
||||
* [Kubernetes]({{< ref "k8s.md" >}})
|
||||
|
||||
|
@ -175,7 +176,7 @@ Check out the [docker examples]({{<ref "full-docker-example.md">}}) for more adv
|
|||
|
||||
## Debian packages
|
||||
|
||||
Vikunja is available as debian packages.
|
||||
Vikunja is available as deb package for installation on debian-like systems.
|
||||
|
||||
To install these, grab a `.deb` file from [the download page](https://dl.vikunja.io/vikunja) and run
|
||||
|
||||
|
@ -186,6 +187,18 @@ dpkg -i vikunja.deb
|
|||
This will install Vikunja to `/opt/vikunja`.
|
||||
To configure it, use the config file in `/etc/vikunja/config.yml`.
|
||||
|
||||
## RPM
|
||||
|
||||
Vikunja is available as rpm package for installation on Fedora, CentOS and others.
|
||||
|
||||
To install these, grab a `.rpm` file from [the download page](https://dl.vikunja.io/vikunja) and run
|
||||
|
||||
```
|
||||
rpm -i vikunja.rpm
|
||||
```
|
||||
|
||||
To configure Vikunja, use the config file in `/etc/vikunja/config.yml`.
|
||||
|
||||
## FreeBSD / FreeNAS
|
||||
|
||||
Unfortunately, we currently can't provide pre-built binaries for FreeBSD.
|
||||
|
|
|
@ -70,30 +70,31 @@ This document describes the different errors Vikunja can return.
|
|||
|
||||
## Task
|
||||
|
||||
| ErrorCode | HTTP Status Code | Description |
|
||||
|-----------|------------------|-------------|
|
||||
| 4001 | 400 | The project task text cannot be empty. |
|
||||
| 4002 | 404 | The project task does not exist. |
|
||||
| 4003 | 403 | All bulk editing tasks must belong to the same project. |
|
||||
| 4004 | 403 | Need at least one task when bulk editing tasks. |
|
||||
| 4005 | 403 | The user does not have the right to see the task. |
|
||||
| 4006 | 403 | The user tried to set a parent task as the task itself. |
|
||||
| ErrorCode | HTTP Status Code | Description |
|
||||
|-----------|------------------|----------------------------------------------------------------------------|
|
||||
| 4001 | 400 | The project task text cannot be empty. |
|
||||
| 4002 | 404 | The project task does not exist. |
|
||||
| 4003 | 403 | All bulk editing tasks must belong to the same project. |
|
||||
| 4004 | 403 | Need at least one task when bulk editing tasks. |
|
||||
| 4005 | 403 | The user does not have the right to see the task. |
|
||||
| 4006 | 403 | The user tried to set a parent task as the task itself. |
|
||||
| 4007 | 400 | The user tried to create a task relation with an invalid kind of relation. |
|
||||
| 4008 | 409 | The user tried to create a task relation which already exists. |
|
||||
| 4009 | 404 | The task relation does not exist. |
|
||||
| 4010 | 400 | Cannot relate a task with itself. |
|
||||
| 4011 | 404 | The task attachment does not exist. |
|
||||
| 4012 | 400 | The task attachment is too large. |
|
||||
| 4013 | 400 | The task sort param is invalid. |
|
||||
| 4014 | 400 | The task sort order is invalid. |
|
||||
| 4015 | 404 | The task comment does not exist. |
|
||||
| 4016 | 400 | Invalid task field. |
|
||||
| 4017 | 400 | Invalid task filter comparator. |
|
||||
| 4018 | 400 | Invalid task filter concatinator. |
|
||||
| 4019 | 400 | Invalid task filter value. |
|
||||
| 4020 | 400 | The provided attachment does not belong to that task. |
|
||||
| 4021 | 400 | This user is already assigned to that task. |
|
||||
| 4022 | 400 | The task has a relative reminder which does not specify relative to what. |
|
||||
| 4008 | 409 | The user tried to create a task relation which already exists. |
|
||||
| 4009 | 404 | The task relation does not exist. |
|
||||
| 4010 | 400 | Cannot relate a task with itself. |
|
||||
| 4011 | 404 | The task attachment does not exist. |
|
||||
| 4012 | 400 | The task attachment is too large. |
|
||||
| 4013 | 400 | The task sort param is invalid. |
|
||||
| 4014 | 400 | The task sort order is invalid. |
|
||||
| 4015 | 404 | The task comment does not exist. |
|
||||
| 4016 | 400 | Invalid task field. |
|
||||
| 4017 | 400 | Invalid task filter comparator. |
|
||||
| 4018 | 400 | Invalid task filter concatinator. |
|
||||
| 4019 | 400 | Invalid task filter value. |
|
||||
| 4020 | 400 | The provided attachment does not belong to that task. |
|
||||
| 4021 | 400 | This user is already assigned to that task. |
|
||||
| 4022 | 400 | The task has a relative reminder which does not specify relative to what. |
|
||||
| 4023 | 409 | Tried to create a task relation which would create a cycle. |
|
||||
|
||||
## Team
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
# Changelog
|
||||
|
||||
THIS CHANGELOG ONLY EXISTS FOR HISTORICAL REASONS.
|
||||
Starting with version 0.23.0, all changes are logged in the CHANGELOG.md in the root of this repository since the repos were merged.
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres
|
||||
|
|
|
@ -58,39 +58,39 @@
|
|||
"@kyvg/vue3-notification": "3.1.4",
|
||||
"@sentry/tracing": "7.100.1",
|
||||
"@sentry/vue": "7.100.1",
|
||||
"@tiptap/core": "2.2.1",
|
||||
"@tiptap/extension-blockquote": "2.2.1",
|
||||
"@tiptap/extension-bold": "2.2.1",
|
||||
"@tiptap/extension-bullet-list": "2.2.1",
|
||||
"@tiptap/extension-code": "2.2.1",
|
||||
"@tiptap/extension-code-block-lowlight": "2.2.1",
|
||||
"@tiptap/extension-document": "2.2.1",
|
||||
"@tiptap/extension-dropcursor": "2.2.1",
|
||||
"@tiptap/extension-gapcursor": "2.2.1",
|
||||
"@tiptap/extension-hard-break": "2.2.1",
|
||||
"@tiptap/extension-heading": "2.2.1",
|
||||
"@tiptap/extension-history": "2.2.1",
|
||||
"@tiptap/extension-horizontal-rule": "2.2.1",
|
||||
"@tiptap/extension-image": "2.2.1",
|
||||
"@tiptap/extension-italic": "2.2.1",
|
||||
"@tiptap/extension-link": "2.2.1",
|
||||
"@tiptap/extension-list-item": "2.2.1",
|
||||
"@tiptap/extension-ordered-list": "2.2.1",
|
||||
"@tiptap/extension-paragraph": "2.2.1",
|
||||
"@tiptap/extension-placeholder": "2.2.1",
|
||||
"@tiptap/extension-strike": "2.2.1",
|
||||
"@tiptap/extension-table": "2.2.1",
|
||||
"@tiptap/extension-table-cell": "2.2.1",
|
||||
"@tiptap/extension-table-header": "2.2.1",
|
||||
"@tiptap/extension-table-row": "2.2.1",
|
||||
"@tiptap/extension-task-item": "2.2.1",
|
||||
"@tiptap/extension-task-list": "2.2.1",
|
||||
"@tiptap/extension-text": "2.2.1",
|
||||
"@tiptap/extension-typography": "2.2.1",
|
||||
"@tiptap/extension-underline": "2.2.1",
|
||||
"@tiptap/pm": "2.2.1",
|
||||
"@tiptap/suggestion": "2.2.1",
|
||||
"@tiptap/vue-3": "2.2.1",
|
||||
"@tiptap/core": "2.2.2",
|
||||
"@tiptap/extension-blockquote": "2.2.2",
|
||||
"@tiptap/extension-bold": "2.2.2",
|
||||
"@tiptap/extension-bullet-list": "2.2.2",
|
||||
"@tiptap/extension-code": "2.2.2",
|
||||
"@tiptap/extension-code-block-lowlight": "2.2.2",
|
||||
"@tiptap/extension-document": "2.2.2",
|
||||
"@tiptap/extension-dropcursor": "2.2.2",
|
||||
"@tiptap/extension-gapcursor": "2.2.2",
|
||||
"@tiptap/extension-hard-break": "2.2.2",
|
||||
"@tiptap/extension-heading": "2.2.2",
|
||||
"@tiptap/extension-history": "2.2.2",
|
||||
"@tiptap/extension-horizontal-rule": "2.2.2",
|
||||
"@tiptap/extension-image": "2.2.2",
|
||||
"@tiptap/extension-italic": "2.2.2",
|
||||
"@tiptap/extension-link": "2.2.2",
|
||||
"@tiptap/extension-list-item": "2.2.2",
|
||||
"@tiptap/extension-ordered-list": "2.2.2",
|
||||
"@tiptap/extension-paragraph": "2.2.2",
|
||||
"@tiptap/extension-placeholder": "2.2.2",
|
||||
"@tiptap/extension-strike": "2.2.2",
|
||||
"@tiptap/extension-table": "2.2.2",
|
||||
"@tiptap/extension-table-cell": "2.2.2",
|
||||
"@tiptap/extension-table-header": "2.2.2",
|
||||
"@tiptap/extension-table-row": "2.2.2",
|
||||
"@tiptap/extension-task-item": "2.2.2",
|
||||
"@tiptap/extension-task-list": "2.2.2",
|
||||
"@tiptap/extension-text": "2.2.2",
|
||||
"@tiptap/extension-typography": "2.2.2",
|
||||
"@tiptap/extension-underline": "2.2.2",
|
||||
"@tiptap/pm": "2.2.2",
|
||||
"@tiptap/suggestion": "2.2.2",
|
||||
"@tiptap/vue-3": "2.2.2",
|
||||
"@types/is-touch-device": "1.0.2",
|
||||
"@types/lodash.clonedeep": "4.5.9",
|
||||
"@vueuse/core": "10.7.2",
|
||||
|
@ -116,7 +116,7 @@
|
|||
"sortablejs": "1.15.2",
|
||||
"tippy.js": "6.3.7",
|
||||
"ufo": "1.4.0",
|
||||
"vue": "3.4.15",
|
||||
"vue": "3.4.18",
|
||||
"vue-advanced-cropper": "2.8.8",
|
||||
"vue-flatpickr-component": "11.0.3",
|
||||
"vue-i18n": "9.9.1",
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -13,8 +13,6 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {defineProps} from 'vue'
|
||||
|
||||
defineProps({
|
||||
value: {
|
||||
type: Number,
|
||||
|
|
16
go.mod
16
go.mod
|
@ -31,7 +31,7 @@ require (
|
|||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/dustinkirkland/golang-petname v0.0.0-20231002161417-6a283f1aaaf2
|
||||
github.com/gabriel-vasile/mimetype v1.4.3
|
||||
github.com/getsentry/sentry-go v0.26.0
|
||||
github.com/getsentry/sentry-go v0.27.0
|
||||
github.com/go-sql-driver/mysql v1.7.1
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.9.0
|
||||
github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a
|
||||
|
@ -39,6 +39,7 @@ require (
|
|||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/hashicorp/go-version v1.6.0
|
||||
github.com/hhsnopek/etag v0.0.0-20171206181245-aea95f647346
|
||||
github.com/iancoleman/strcase v0.3.0
|
||||
github.com/jinzhu/copier v0.4.0
|
||||
github.com/jszwedko/go-datemath v0.1.1-0.20230526204004-640a500621d6
|
||||
|
@ -65,12 +66,12 @@ require (
|
|||
github.com/ulule/limiter/v3 v3.11.2
|
||||
github.com/wneessen/go-mail v0.4.0
|
||||
github.com/yuin/goldmark v1.7.0
|
||||
golang.org/x/crypto v0.18.0
|
||||
golang.org/x/crypto v0.19.0
|
||||
golang.org/x/image v0.15.0
|
||||
golang.org/x/oauth2 v0.16.0
|
||||
golang.org/x/oauth2 v0.17.0
|
||||
golang.org/x/sync v0.6.0
|
||||
golang.org/x/sys v0.16.0
|
||||
golang.org/x/term v0.16.0
|
||||
golang.org/x/sys v0.17.0
|
||||
golang.org/x/term v0.17.0
|
||||
golang.org/x/text v0.14.0
|
||||
gopkg.in/d4l3k/messagediff.v1 v1.2.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
|
@ -78,7 +79,7 @@ require (
|
|||
src.techknowlogick.com/xgo v1.7.1-0.20240206231429-45b9ea635e03
|
||||
src.techknowlogick.com/xormigrate v1.7.1
|
||||
xorm.io/builder v0.3.13
|
||||
xorm.io/xorm v1.3.7
|
||||
xorm.io/xorm v1.3.8
|
||||
)
|
||||
|
||||
require (
|
||||
|
@ -120,7 +121,6 @@ require (
|
|||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/hhsnopek/etag v0.0.0-20171206181245-aea95f647346 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
|
@ -174,7 +174,7 @@ require (
|
|||
golang.org/x/arch v0.4.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/net v0.20.0 // indirect
|
||||
golang.org/x/net v0.21.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.13.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
|
|
14
go.sum
14
go.sum
|
@ -113,6 +113,8 @@ github.com/getsentry/sentry-go v0.25.0 h1:q6Eo+hS+yoJlTO3uu/azhQadsD8V+jQn2D8VvX
|
|||
github.com/getsentry/sentry-go v0.25.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
|
||||
github.com/getsentry/sentry-go v0.26.0 h1:IX3++sF6/4B5JcevhdZfdKIHfyvMmAq/UnqcyT2H6mA=
|
||||
github.com/getsentry/sentry-go v0.26.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
|
||||
github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps=
|
||||
github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
||||
|
@ -599,6 +601,8 @@ golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
|||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
|
@ -637,10 +641,14 @@ golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
|||
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ=
|
||||
golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM=
|
||||
golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ=
|
||||
golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
|
||||
golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ=
|
||||
golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -684,6 +692,8 @@ golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
|||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
|
@ -692,6 +702,8 @@ golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
|||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
|
||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
|
@ -835,3 +847,5 @@ xorm.io/xorm v1.3.6 h1:hfpWHkDIWWqUi8FRF2H2M9O8lO3Ov47rwFcS9gPzPkU=
|
|||
xorm.io/xorm v1.3.6/go.mod h1:qFJGFoVYbbIdnz2vaL5OxSQ2raleMpyRRalnq3n9OJo=
|
||||
xorm.io/xorm v1.3.7 h1:mLceAGu0b87r9pD4qXyxGHxifOXIIrAdVcA6k95/osw=
|
||||
xorm.io/xorm v1.3.7/go.mod h1:LsCCffeeYp63ssk0pKumP6l96WZcHix7ChpurcLNuMw=
|
||||
xorm.io/xorm v1.3.8 h1:CJmplmWqfSRpLWSPMmqz+so8toBp3m7ehuRehIWedZo=
|
||||
xorm.io/xorm v1.3.8/go.mod h1:LsCCffeeYp63ssk0pKumP6l96WZcHix7ChpurcLNuMw=
|
||||
|
|
|
@ -1003,6 +1003,35 @@ func (err ErrReminderRelativeToMissing) HTTPError() web.HTTPError {
|
|||
}
|
||||
}
|
||||
|
||||
// ErrTaskRelationCycle represents an error where the user tries to create an already existing relation
|
||||
type ErrTaskRelationCycle struct {
|
||||
Kind RelationKind
|
||||
TaskID int64
|
||||
OtherTaskID int64
|
||||
}
|
||||
|
||||
// IsErrTaskRelationCycle checks if an error is ErrTaskRelationCycle.
|
||||
func IsErrTaskRelationCycle(err error) bool {
|
||||
_, ok := err.(ErrTaskRelationCycle)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrTaskRelationCycle) Error() string {
|
||||
return fmt.Sprintf("Task relation cycle detectetd [TaskID: %v, OtherTaskID: %v, Kind: %v]", err.TaskID, err.OtherTaskID, err.Kind)
|
||||
}
|
||||
|
||||
// ErrCodeTaskRelationCycle holds the unique world-error code of this error
|
||||
const ErrCodeTaskRelationCycle = 4022
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrTaskRelationCycle) HTTPError() web.HTTPError {
|
||||
return web.HTTPError{
|
||||
HTTPCode: http.StatusConflict,
|
||||
Code: ErrCodeTaskRelationCycle,
|
||||
Message: "This task relation would create a cycle.",
|
||||
}
|
||||
}
|
||||
|
||||
// ============
|
||||
// Team errors
|
||||
// ============
|
||||
|
|
|
@ -19,8 +19,10 @@ package models
|
|||
import (
|
||||
"archive/zip"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
@ -219,15 +221,20 @@ func exportTaskAttachments(s *xorm.Session, wr *zip.Writer, taskIDs []int64) (er
|
|||
return err
|
||||
}
|
||||
|
||||
fs := make(map[int64]io.ReadCloser)
|
||||
attachmentFiles := make(map[int64]io.ReadCloser)
|
||||
for _, ta := range tas {
|
||||
if err := ta.File.LoadFileByID(); err != nil {
|
||||
err = ta.File.LoadFileByID()
|
||||
if err != nil {
|
||||
var pathError *fs.PathError
|
||||
if errors.As(err, &pathError) {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
fs[ta.FileID] = ta.File.File
|
||||
attachmentFiles[ta.FileID] = ta.File.File
|
||||
}
|
||||
|
||||
return utils.WriteFilesToZip(fs, wr)
|
||||
return utils.WriteFilesToZip(attachmentFiles, wr)
|
||||
}
|
||||
|
||||
func exportSavedFilters(s *xorm.Session, u *user.User, wr *zip.Writer) (err error) {
|
||||
|
@ -256,7 +263,7 @@ func exportProjectBackgrounds(s *xorm.Session, u *user.User, wr *zip.Writer) (er
|
|||
return err
|
||||
}
|
||||
|
||||
fs := make(map[int64]io.ReadCloser)
|
||||
backgroundFiles := make(map[int64]io.ReadCloser)
|
||||
for _, l := range projects {
|
||||
if l.BackgroundFileID == 0 {
|
||||
continue
|
||||
|
@ -267,13 +274,17 @@ func exportProjectBackgrounds(s *xorm.Session, u *user.User, wr *zip.Writer) (er
|
|||
}
|
||||
err = bgFile.LoadFileByID()
|
||||
if err != nil {
|
||||
return
|
||||
var pathError *fs.PathError
|
||||
if errors.As(err, &pathError) {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
fs[l.BackgroundFileID] = bgFile.File
|
||||
backgroundFiles[l.BackgroundFileID] = bgFile.File
|
||||
}
|
||||
|
||||
return utils.WriteFilesToZip(fs, wr)
|
||||
return utils.WriteFilesToZip(backgroundFiles, wr)
|
||||
}
|
||||
|
||||
func RegisterOldExportCleanupCron() {
|
||||
|
|
|
@ -747,6 +747,48 @@ func (wl *WebhookListener) Handle(msg *message.Message) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Load event data again so that it is always populated in the webhook payload
|
||||
var doerID int64
|
||||
if doer, has := event["doer"]; has {
|
||||
d := doer.(map[string]interface{})
|
||||
if rawDoerID, has := d["id"]; has {
|
||||
doerID = getIDAsInt64(rawDoerID)
|
||||
fullDoer, err := user.GetUserByID(s, doerID)
|
||||
if err != nil && !user.IsErrUserDoesNotExist(err) {
|
||||
return err
|
||||
}
|
||||
if err == nil {
|
||||
event["doer"] = fullDoer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if task, has := event["task"]; has && doerID != 0 {
|
||||
t := task.(map[string]interface{})
|
||||
if taskID, has := t["id"]; has {
|
||||
id := getIDAsInt64(taskID)
|
||||
fullTask := Task{ID: id}
|
||||
err = fullTask.ReadOne(s, &user.User{ID: doerID})
|
||||
if err != nil && !IsErrTaskDoesNotExist(err) {
|
||||
return err
|
||||
}
|
||||
if err == nil {
|
||||
event["task"] = fullTask
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if _, has := event["project"]; has && doerID != 0 {
|
||||
project := &Project{ID: projectID}
|
||||
err = project.ReadOne(s, &user.User{ID: doerID})
|
||||
if err != nil && !IsErrProjectDoesNotExist(err) {
|
||||
return err
|
||||
}
|
||||
if err == nil {
|
||||
event["project"] = project
|
||||
}
|
||||
}
|
||||
|
||||
err = webhook.sendWebhookPayload(&WebhookPayload{
|
||||
EventName: wl.EventName,
|
||||
Time: time.Now(),
|
||||
|
|
|
@ -138,6 +138,54 @@ func getInverseRelation(kind RelationKind) RelationKind {
|
|||
return RelationKindUnknown
|
||||
}
|
||||
|
||||
func checkTaskRelationCycle(s *xorm.Session, relation *TaskRelation, otherTaskIDToCheck int64, visited map[int64]bool, currentPath map[int64]bool) (err error) {
|
||||
if visited == nil {
|
||||
visited = make(map[int64]bool)
|
||||
}
|
||||
|
||||
if currentPath == nil {
|
||||
currentPath = make(map[int64]bool)
|
||||
}
|
||||
|
||||
if visited[relation.TaskID] {
|
||||
return nil // Node already visited, no cycle detected
|
||||
}
|
||||
|
||||
if relation.TaskID == otherTaskIDToCheck || // This checks for cycles between leaf nodes
|
||||
currentPath[relation.TaskID] ||
|
||||
currentPath[otherTaskIDToCheck] {
|
||||
// Cycle detected
|
||||
return ErrTaskRelationCycle{
|
||||
TaskID: relation.TaskID,
|
||||
OtherTaskID: relation.OtherTaskID,
|
||||
Kind: relation.RelationKind,
|
||||
}
|
||||
}
|
||||
|
||||
visited[relation.TaskID] = true
|
||||
currentPath[relation.TaskID] = true
|
||||
|
||||
parenttasks := []*TaskRelation{}
|
||||
// where child = relation.id
|
||||
err = s.Where("other_task_id = ? AND relation_kind = ?", relation.TaskID, relation.RelationKind).
|
||||
Find(&parenttasks)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, parent := range parenttasks {
|
||||
err = checkTaskRelationCycle(s, parent, otherTaskIDToCheck, visited, currentPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the current node from the currentPath to avoid false positives
|
||||
delete(currentPath, relation.TaskID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create creates a new task relation
|
||||
// @Summary Create a new relation between two tasks
|
||||
// @Description Creates a new relation between two tasks. The user needs to have update rights on the base task and at least read rights on the other task. Both tasks do not need to be on the same project. Take a look at the docs for available task relation kinds.
|
||||
|
@ -191,6 +239,14 @@ func (rel *TaskRelation) Create(s *xorm.Session, a web.Auth) error {
|
|||
RelationKind: getInverseRelation(rel.RelationKind),
|
||||
}
|
||||
|
||||
// If we're creating a subtask relation, check if we're about to create a cycle
|
||||
if rel.RelationKind == RelationKindSubtask || rel.RelationKind == RelationKindParenttask {
|
||||
err = checkTaskRelationCycle(s, rel, rel.OtherTaskID, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Finally insert everything
|
||||
_, err = s.Insert(&[]*TaskRelation{
|
||||
rel,
|
||||
|
|
|
@ -96,6 +96,182 @@ func TestTaskRelation_Create(t *testing.T) {
|
|||
require.Error(t, err)
|
||||
assert.True(t, IsErrRelationTasksCannotBeTheSame(err))
|
||||
})
|
||||
t.Run("cycle with one subtask", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
rel := TaskRelation{
|
||||
TaskID: 29,
|
||||
OtherTaskID: 1,
|
||||
RelationKind: RelationKindSubtask,
|
||||
}
|
||||
err := rel.Create(s, &user.User{ID: 1})
|
||||
require.Error(t, err)
|
||||
assert.True(t, IsErrTaskRelationCycle(err))
|
||||
})
|
||||
t.Run("cycle with multiple subtasks", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
rel1 := TaskRelation{
|
||||
TaskID: 1,
|
||||
OtherTaskID: 2,
|
||||
RelationKind: RelationKindSubtask,
|
||||
}
|
||||
err := rel1.Create(s, &user.User{ID: 1})
|
||||
require.NoError(t, err)
|
||||
rel2 := TaskRelation{
|
||||
TaskID: 2,
|
||||
OtherTaskID: 3,
|
||||
RelationKind: RelationKindSubtask,
|
||||
}
|
||||
err = rel2.Create(s, &user.User{ID: 1})
|
||||
require.NoError(t, err)
|
||||
rel3 := TaskRelation{
|
||||
TaskID: 3,
|
||||
OtherTaskID: 4,
|
||||
RelationKind: RelationKindSubtask,
|
||||
}
|
||||
err = rel3.Create(s, &user.User{ID: 1})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Cycle happens here
|
||||
rel4 := TaskRelation{
|
||||
TaskID: 4,
|
||||
OtherTaskID: 2,
|
||||
RelationKind: RelationKindSubtask,
|
||||
}
|
||||
err = rel4.Create(s, &user.User{ID: 1})
|
||||
require.Error(t, err)
|
||||
assert.True(t, IsErrTaskRelationCycle(err))
|
||||
})
|
||||
t.Run("cycle with multiple subtasks tasks and relation back to parent", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
rel1 := TaskRelation{
|
||||
TaskID: 1,
|
||||
OtherTaskID: 2,
|
||||
RelationKind: RelationKindSubtask,
|
||||
}
|
||||
err := rel1.Create(s, &user.User{ID: 1})
|
||||
require.NoError(t, err)
|
||||
rel2 := TaskRelation{
|
||||
TaskID: 2,
|
||||
OtherTaskID: 3,
|
||||
RelationKind: RelationKindSubtask,
|
||||
}
|
||||
err = rel2.Create(s, &user.User{ID: 1})
|
||||
require.NoError(t, err)
|
||||
rel3 := TaskRelation{
|
||||
TaskID: 3,
|
||||
OtherTaskID: 4,
|
||||
RelationKind: RelationKindSubtask,
|
||||
}
|
||||
err = rel3.Create(s, &user.User{ID: 1})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Cycle happens here
|
||||
rel4 := TaskRelation{
|
||||
TaskID: 4,
|
||||
OtherTaskID: 1,
|
||||
RelationKind: RelationKindSubtask,
|
||||
}
|
||||
err = rel4.Create(s, &user.User{ID: 1})
|
||||
require.Error(t, err)
|
||||
assert.True(t, IsErrTaskRelationCycle(err))
|
||||
})
|
||||
t.Run("cycle with one parenttask", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
rel := TaskRelation{
|
||||
TaskID: 1,
|
||||
OtherTaskID: 29,
|
||||
RelationKind: RelationKindParenttask,
|
||||
}
|
||||
err := rel.Create(s, &user.User{ID: 1})
|
||||
require.Error(t, err)
|
||||
assert.True(t, IsErrTaskRelationCycle(err))
|
||||
})
|
||||
t.Run("cycle with multiple parenttasks", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
rel1 := TaskRelation{
|
||||
TaskID: 1,
|
||||
OtherTaskID: 2,
|
||||
RelationKind: RelationKindParenttask,
|
||||
}
|
||||
err := rel1.Create(s, &user.User{ID: 1})
|
||||
require.NoError(t, err)
|
||||
rel2 := TaskRelation{
|
||||
TaskID: 2,
|
||||
OtherTaskID: 3,
|
||||
RelationKind: RelationKindParenttask,
|
||||
}
|
||||
err = rel2.Create(s, &user.User{ID: 1})
|
||||
require.NoError(t, err)
|
||||
rel3 := TaskRelation{
|
||||
TaskID: 3,
|
||||
OtherTaskID: 4,
|
||||
RelationKind: RelationKindParenttask,
|
||||
}
|
||||
err = rel3.Create(s, &user.User{ID: 1})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Cycle happens here
|
||||
rel4 := TaskRelation{
|
||||
TaskID: 4,
|
||||
OtherTaskID: 2,
|
||||
RelationKind: RelationKindParenttask,
|
||||
}
|
||||
err = rel4.Create(s, &user.User{ID: 1})
|
||||
require.Error(t, err)
|
||||
assert.True(t, IsErrTaskRelationCycle(err))
|
||||
})
|
||||
t.Run("cycle with multiple parenttasks and relation back to parent", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
rel1 := TaskRelation{
|
||||
TaskID: 1,
|
||||
OtherTaskID: 2,
|
||||
RelationKind: RelationKindParenttask,
|
||||
}
|
||||
err := rel1.Create(s, &user.User{ID: 1})
|
||||
require.NoError(t, err)
|
||||
rel2 := TaskRelation{
|
||||
TaskID: 2,
|
||||
OtherTaskID: 3,
|
||||
RelationKind: RelationKindParenttask,
|
||||
}
|
||||
err = rel2.Create(s, &user.User{ID: 1})
|
||||
require.NoError(t, err)
|
||||
rel3 := TaskRelation{
|
||||
TaskID: 3,
|
||||
OtherTaskID: 4,
|
||||
RelationKind: RelationKindParenttask,
|
||||
}
|
||||
err = rel3.Create(s, &user.User{ID: 1})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Cycle happens here
|
||||
rel4 := TaskRelation{
|
||||
TaskID: 4,
|
||||
OtherTaskID: 1,
|
||||
RelationKind: RelationKindParenttask,
|
||||
}
|
||||
err = rel4.Create(s, &user.User{ID: 1})
|
||||
require.Error(t, err)
|
||||
assert.True(t, IsErrTaskRelationCycle(err))
|
||||
})
|
||||
}
|
||||
|
||||
func TestTaskRelation_Delete(t *testing.T) {
|
||||
|
|
|
@ -151,7 +151,7 @@ func setupSentry(e *echo.Echo) {
|
|||
e.HTTPErrorHandler = func(err error, c echo.Context) {
|
||||
// Only capture errors not already handled by echo
|
||||
var herr *echo.HTTPError
|
||||
if errors.As(err, &herr) && herr.Code > 403 {
|
||||
if errors.As(err, &herr) && herr.Code > 499 {
|
||||
hub := sentryecho.GetHubFromContext(c)
|
||||
if hub != nil {
|
||||
hub.WithScope(func(scope *sentry.Scope) {
|
||||
|
|
Loading…
Reference in New Issue