mirror of
https://github.com/go-vikunja/vikunja.git
synced 2025-12-05 19:16:51 -06:00
Compare commits
9 Commits
186a7182f6
...
310685973b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
310685973b | ||
|
|
f865bd8555 | ||
|
|
5a17c56735 | ||
|
|
112df4a752 | ||
|
|
da0822c3f4 | ||
|
|
30104fb749 | ||
|
|
7cf2a6886e | ||
|
|
5bb53eaefa | ||
|
|
cec8daba59 |
4
.github/workflows/issue-closed-comment.yml
vendored
4
.github/workflows/issue-closed-comment.yml
vendored
@@ -53,7 +53,9 @@ jobs:
|
||||
|
||||
core.setOutput('closed_by_commit', 'true');
|
||||
core.setOutput('commit_sha', commitId);
|
||||
core.setOutput('commit_message', commit.message);
|
||||
// Escape backslashes, backticks and ${ to prevent breaking JS template strings
|
||||
const escapedMessage = commit.message.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$\{/g, '\\${');
|
||||
core.setOutput('commit_message', escapedMessage);
|
||||
core.setOutput('commit_url', closedEvent.commit_url);
|
||||
} else {
|
||||
console.log(`ℹ️ Issue #${issueNumber} was closed manually (not by commit)`);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# syntax=docker/dockerfile:1@sha256:b6afd42430b15f2d2a4c5a02b919e98a525b785b1aaff16747d2f623364e39b6
|
||||
FROM --platform=$BUILDPLATFORM node:24.11.1-alpine@sha256:2867d550cf9d8bb50059a0fff528741f11a84d985c732e60e19e8e75c7239c43 AS frontendbuilder
|
||||
FROM --platform=$BUILDPLATFORM node:24.11.1-alpine@sha256:682368d8253e0c3364b803956085c456a612d738bd635926d73fa24db3ce53d7 AS frontendbuilder
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
|
||||
@@ -52,12 +52,12 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron": "37.10.3",
|
||||
"electron": "39.2.4",
|
||||
"electron-builder": "26.0.12",
|
||||
"unzipper": "0.12.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"express": "5.2.0"
|
||||
"express": "5.2.1"
|
||||
},
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
|
||||
30
desktop/pnpm-lock.yaml
generated
30
desktop/pnpm-lock.yaml
generated
@@ -9,12 +9,12 @@ importers:
|
||||
.:
|
||||
dependencies:
|
||||
express:
|
||||
specifier: 5.2.0
|
||||
version: 5.2.0
|
||||
specifier: 5.2.1
|
||||
version: 5.2.1
|
||||
devDependencies:
|
||||
electron:
|
||||
specifier: 37.10.3
|
||||
version: 37.10.3
|
||||
specifier: 39.2.4
|
||||
version: 39.2.4
|
||||
electron-builder:
|
||||
specifier: 26.0.12
|
||||
version: 26.0.12(electron-builder-squirrel-windows@24.13.3)
|
||||
@@ -571,8 +571,8 @@ packages:
|
||||
electron-publish@26.0.11:
|
||||
resolution: {integrity: sha512-a8QRH0rAPIWH9WyyS5LbNvW9Ark6qe63/LqDB7vu2JXYpi0Gma5Q60Dh4tmTqhOBQt0xsrzD8qE7C+D7j+B24A==}
|
||||
|
||||
electron@37.10.3:
|
||||
resolution: {integrity: sha512-3IjCGSjQmH50IbW2PFveaTzK+KwcFX9PEhE7KXb9v5IT8cLAiryAN7qezm/XzODhDRlLu0xKG1j8xWBtZ/bx/g==}
|
||||
electron@39.2.4:
|
||||
resolution: {integrity: sha512-KxPtwpFceQKSxRtUY39piHLYhJMMyHfOhc70e6zRnKGrbRdK6hzEqssth8IGjlKOdkeT4KCvIEngnNraYk39+g==}
|
||||
engines: {node: '>= 12.20.55'}
|
||||
hasBin: true
|
||||
|
||||
@@ -636,8 +636,8 @@ packages:
|
||||
exponential-backoff@3.1.3:
|
||||
resolution: {integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==}
|
||||
|
||||
express@5.2.0:
|
||||
resolution: {integrity: sha512-XdpJDLxfztVY59X0zPI6sibRiGcxhTPXRD3IhJmjKf2jwMvkRGV1j7loB8U+heeamoU3XvihAaGRTR4aXXUN3A==}
|
||||
express@5.2.1:
|
||||
resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
extract-zip@2.0.1:
|
||||
@@ -1651,7 +1651,7 @@ snapshots:
|
||||
make-fetch-happen: 10.2.1
|
||||
nopt: 6.0.0
|
||||
proc-log: 2.0.1
|
||||
semver: 7.7.2
|
||||
semver: 7.7.3
|
||||
tar: 6.2.1
|
||||
which: 2.0.2
|
||||
transitivePeerDependencies:
|
||||
@@ -1771,7 +1771,7 @@ snapshots:
|
||||
'@npmcli/fs@2.1.2':
|
||||
dependencies:
|
||||
'@gar/promisify': 1.1.3
|
||||
semver: 7.7.2
|
||||
semver: 7.7.3
|
||||
|
||||
'@npmcli/move-file@2.0.1':
|
||||
dependencies:
|
||||
@@ -2432,7 +2432,7 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
electron@37.10.3:
|
||||
electron@39.2.4:
|
||||
dependencies:
|
||||
'@electron/get': 2.0.3
|
||||
'@types/node': 22.18.0
|
||||
@@ -2488,7 +2488,7 @@ snapshots:
|
||||
|
||||
exponential-backoff@3.1.3: {}
|
||||
|
||||
express@5.2.0:
|
||||
express@5.2.1:
|
||||
dependencies:
|
||||
accepts: 2.0.0
|
||||
body-parser: 2.2.1
|
||||
@@ -2679,7 +2679,7 @@ snapshots:
|
||||
es6-error: 4.1.1
|
||||
matcher: 3.0.0
|
||||
roarr: 2.15.4
|
||||
semver: 7.7.2
|
||||
semver: 7.7.3
|
||||
serialize-error: 7.0.1
|
||||
optional: true
|
||||
|
||||
@@ -3030,14 +3030,14 @@ snapshots:
|
||||
|
||||
node-abi@3.71.0:
|
||||
dependencies:
|
||||
semver: 7.7.2
|
||||
semver: 7.7.3
|
||||
|
||||
node-addon-api@1.7.2:
|
||||
optional: true
|
||||
|
||||
node-api-version@0.2.0:
|
||||
dependencies:
|
||||
semver: 7.7.2
|
||||
semver: 7.7.3
|
||||
|
||||
node-int64@0.4.0: {}
|
||||
|
||||
|
||||
@@ -112,14 +112,14 @@
|
||||
"@4tw/cypress-drag-drop": "2.3.1",
|
||||
"@cypress/vite-dev-server": "7.0.1",
|
||||
"@cypress/vue": "6.0.2",
|
||||
"@faker-js/faker": "9.9.0",
|
||||
"@faker-js/faker": "10.1.0",
|
||||
"@histoire/plugin-screenshot": "1.0.0-alpha.5",
|
||||
"@histoire/plugin-vue": "1.0.0-alpha.5",
|
||||
"@playwright/test": "1.57.0",
|
||||
"@tsconfig/node22": "22.0.5",
|
||||
"@types/codemirror": "5.60.17",
|
||||
"@types/is-touch-device": "1.0.3",
|
||||
"@types/node": "22.19.1",
|
||||
"@types/node": "24.10.1",
|
||||
"@types/sortablejs": "1.15.9",
|
||||
"@typescript-eslint/eslint-plugin": "8.48.0",
|
||||
"@typescript-eslint/parser": "8.48.0",
|
||||
@@ -132,7 +132,7 @@
|
||||
"browserslist": "4.28.0",
|
||||
"caniuse-lite": "1.0.30001757",
|
||||
"csstype": "3.2.3",
|
||||
"cypress": "14.5.4",
|
||||
"cypress": "15.7.0",
|
||||
"esbuild": "0.27.0",
|
||||
"eslint": "9.39.1",
|
||||
"eslint-plugin-vue": "10.6.2",
|
||||
@@ -148,7 +148,7 @@
|
||||
"stylelint": "16.26.1",
|
||||
"stylelint-config-property-sort-order-smacss": "10.0.0",
|
||||
"stylelint-config-recommended-vue": "1.6.1",
|
||||
"stylelint-config-standard-scss": "15.0.1",
|
||||
"stylelint-config-standard-scss": "16.0.0",
|
||||
"stylelint-use-logical": "2.1.2",
|
||||
"typescript": "5.9.3",
|
||||
"unplugin-inject-preload": "3.0.0",
|
||||
@@ -157,9 +157,9 @@
|
||||
"vite-plugin-sentry": "1.4.1",
|
||||
"vite-plugin-vue-devtools": "8.0.5",
|
||||
"vite-svg-loader": "5.1.0",
|
||||
"vitest": "3.2.4",
|
||||
"vitest": "4.0.15",
|
||||
"vue-tsc": "3.1.5",
|
||||
"wait-on": "8.0.5",
|
||||
"wait-on": "9.0.3",
|
||||
"workbox-cli": "7.4.0"
|
||||
},
|
||||
"pnpm": {
|
||||
|
||||
545
frontend/pnpm-lock.yaml
generated
545
frontend/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -181,6 +181,9 @@
|
||||
"dark": "Dunkel"
|
||||
}
|
||||
},
|
||||
"backgroundBrightness": {
|
||||
"title": "Hintergrundhelligkeit"
|
||||
},
|
||||
"apiTokens": {
|
||||
"title": "API-Tokens",
|
||||
"general": "Mit API-Token kannst du die API von Vikunja ohne Login-Daten verwenden.",
|
||||
|
||||
@@ -181,6 +181,9 @@
|
||||
"dark": "Dunkel"
|
||||
}
|
||||
},
|
||||
"backgroundBrightness": {
|
||||
"title": "Hintergrundhelligkeit"
|
||||
},
|
||||
"apiTokens": {
|
||||
"title": "API-Tokens",
|
||||
"general": "Mit API-Token kannst du die API von Vikunja ohne Login-Daten verwenden.",
|
||||
|
||||
@@ -181,6 +181,9 @@
|
||||
"dark": "Тёмная"
|
||||
}
|
||||
},
|
||||
"backgroundBrightness": {
|
||||
"title": "Яркость фона"
|
||||
},
|
||||
"apiTokens": {
|
||||
"title": "Токены API",
|
||||
"general": "Токены API позволяют использовать Vikunja API без использования данных для входа пользователя.",
|
||||
|
||||
2
go.mod
2
go.mod
@@ -62,7 +62,7 @@ require (
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/samedi/caldav-go v3.0.0+incompatible
|
||||
github.com/spf13/afero v1.15.0
|
||||
github.com/spf13/cobra v1.10.1
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/spf13/viper v1.21.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/swaggo/swag v1.16.6
|
||||
|
||||
12
go.sum
12
go.sum
@@ -57,14 +57,10 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
|
||||
github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY=
|
||||
github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic=
|
||||
github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/clipperhouse/displaywidth v0.3.1 h1:k07iN9gD32177o1y4O1jQMzbLdCrsGJh+blirVYybsk=
|
||||
github.com/clipperhouse/displaywidth v0.3.1/go.mod h1:tgLJKKyaDOCadywag3agw4snxS5kYEuYR6Y9+qWDDYM=
|
||||
github.com/clipperhouse/displaywidth v0.6.0 h1:k32vueaksef9WIKCNcoqRNyKbyvkvkysNYnAWz2fN4s=
|
||||
github.com/clipperhouse/displaywidth v0.6.0/go.mod h1:R+kHuzaYWFkTm7xoMmK1lFydbci4X2CicfbGstSGg0o=
|
||||
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
|
||||
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
|
||||
github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY=
|
||||
github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
||||
github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4=
|
||||
github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
@@ -364,12 +360,8 @@ github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj
|
||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0=
|
||||
github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM=
|
||||
github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
|
||||
github.com/olekukonko/ll v0.1.2 h1:lkg/k/9mlsy0SxO5aC+WEpbdT5K83ddnNhAepz7TQc0=
|
||||
github.com/olekukonko/ll v0.1.2/go.mod h1:b52bVQRRPObe+yyBl0TxNfhesL0nedD4Cht0/zx55Ew=
|
||||
github.com/olekukonko/ll v0.1.3 h1:sV2jrhQGq5B3W0nENUISCR6azIPf7UBUpVq0x/y70Fg=
|
||||
github.com/olekukonko/ll v0.1.3/go.mod h1:b52bVQRRPObe+yyBl0TxNfhesL0nedD4Cht0/zx55Ew=
|
||||
github.com/olekukonko/tablewriter v1.1.1 h1:b3reP6GCfrHwmKkYwNRFh2rxidGHcT6cgxj/sHiDDx0=
|
||||
github.com/olekukonko/tablewriter v1.1.1/go.mod h1:De/bIcTF+gpBDB3Alv3fEsZA+9unTsSzAg/ZGADCtn4=
|
||||
github.com/olekukonko/tablewriter v1.1.2 h1:L2kI1Y5tZBct/O/TyZK1zIE9GlBj/TVs+AY5tZDCDSc=
|
||||
github.com/olekukonko/tablewriter v1.1.2/go.mod h1:z7SYPugVqGVavWoA2sGsFIoOVNmEHxUAAMrhXONtfkg=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
@@ -404,8 +396,6 @@ github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9Z
|
||||
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
|
||||
github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
|
||||
github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
|
||||
github.com/redis/go-redis/v9 v9.17.1 h1:7tl732FjYPRT9H9aNfyTwKg9iTETjWjGKEJ2t/5iWTs=
|
||||
github.com/redis/go-redis/v9 v9.17.1/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370=
|
||||
github.com/redis/go-redis/v9 v9.17.2 h1:P2EGsA4qVIM3Pp+aPocCJ7DguDHhqrXNhVcEp4ViluI=
|
||||
github.com/redis/go-redis/v9 v9.17.2/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
@@ -442,6 +432,8 @@ github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
|
||||
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
|
||||
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
|
||||
@@ -265,6 +265,7 @@ func getHexColorFromCaldavColor(caldavColor string) string {
|
||||
return hexColor
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
func ParseTaskFromVTODO(content string) (vTask *models.Task, err error) {
|
||||
parsed, err := ics.ParseCalendar(strings.NewReader(content))
|
||||
if err != nil {
|
||||
@@ -301,11 +302,23 @@ func ParseTaskFromVTODO(content string) (vTask *models.Task, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Get UID and SUMMARY (log warning if missing, but don't fail for backwards compatibility)
|
||||
uid, hasUID := task["UID"]
|
||||
if !hasUID {
|
||||
log.Warningf("[CALDAV] VTODO missing UID field")
|
||||
}
|
||||
|
||||
summary, hasSummary := task["SUMMARY"]
|
||||
if !hasSummary {
|
||||
log.Warningf("[CALDAV] VTODO missing SUMMARY field")
|
||||
}
|
||||
|
||||
// Parse the priority
|
||||
var priority int64
|
||||
if _, ok := task["PRIORITY"]; ok {
|
||||
priorityParsed, err := strconv.ParseInt(task["PRIORITY"].Value, 10, 64)
|
||||
if err != nil {
|
||||
log.Errorf("[CALDAV] Failed to parse PRIORITY: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -313,10 +326,16 @@ func ParseTaskFromVTODO(content string) (vTask *models.Task, err error) {
|
||||
}
|
||||
|
||||
// Parse the enddate
|
||||
duration, _ := time.ParseDuration(task["DURATION"].Value)
|
||||
var duration time.Duration
|
||||
if durationProp, ok := task["DURATION"]; ok {
|
||||
duration, _ = time.ParseDuration(durationProp.Value)
|
||||
}
|
||||
|
||||
description := strings.ReplaceAll(task["DESCRIPTION"].Value, "\\,", ",")
|
||||
description = strings.ReplaceAll(description, "\\n", "\n")
|
||||
description := ""
|
||||
if descProp, ok := task["DESCRIPTION"]; ok {
|
||||
description = strings.ReplaceAll(descProp.Value, "\\,", ",")
|
||||
description = strings.ReplaceAll(description, "\\n", "\n")
|
||||
}
|
||||
|
||||
var labels []*models.Label
|
||||
if val, ok := task["CATEGORIES"]; ok {
|
||||
@@ -329,9 +348,18 @@ func ParseTaskFromVTODO(content string) (vTask *models.Task, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Safely extract values
|
||||
var uidValue, titleValue string
|
||||
if hasUID {
|
||||
uidValue = uid.Value
|
||||
}
|
||||
if hasSummary {
|
||||
titleValue = summary.Value
|
||||
}
|
||||
|
||||
vTask = &models.Task{
|
||||
UID: task["UID"].Value,
|
||||
Title: task["SUMMARY"].Value,
|
||||
UID: uidValue,
|
||||
Title: titleValue,
|
||||
Description: description,
|
||||
Priority: priority,
|
||||
Labels: labels,
|
||||
@@ -371,7 +399,7 @@ func ParseTaskFromVTODO(content string) (vTask *models.Task, err error) {
|
||||
})
|
||||
}
|
||||
|
||||
if task["STATUS"].Value == "COMPLETED" {
|
||||
if status, ok := task["STATUS"]; ok && status.Value == "COMPLETED" {
|
||||
vTask.Done = true
|
||||
}
|
||||
|
||||
|
||||
@@ -17,14 +17,26 @@
|
||||
package caldav
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"code.vikunja.io/api/pkg/config"
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"gopkg.in/d4l3k/messagediff.v1"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
// Initialize logger for tests
|
||||
log.InitLogger()
|
||||
|
||||
// Run tests
|
||||
code := m.Run()
|
||||
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
func TestParseTaskFromVTODO(t *testing.T) {
|
||||
type args struct {
|
||||
content string
|
||||
|
||||
@@ -79,7 +79,9 @@
|
||||
"subject_to_assignee": "「%[1]」(%[2]s) の担当者に割り当てられました",
|
||||
"message_to_assignee": "%[1]s があなたを「%[2]」の担当者に割り当てました。",
|
||||
"subject_to_others": "「%[1]」(%[2]s) の担当者に %[3]s が割り当てられました",
|
||||
"message_to_others": "%[1]s がこのタスクの担当者を %[2]s に割り当てました。"
|
||||
"message_to_others": "%[1]s がこのタスクの担当者を %[2]s に割り当てました。",
|
||||
"subject_to_others_self": "「%[1]」(%[2]s) は %[3]s が自身に割り当てました",
|
||||
"message_to_others_self": "%[1]s がこのタスクを自身に割り当てました。"
|
||||
},
|
||||
"deleted": {
|
||||
"subject": "「%[1]」(%[2]s) が削除されました",
|
||||
|
||||
@@ -325,7 +325,7 @@ func duplicateProjectBackground(s *xorm.Session, pd *ProjectDuplicate, doer web.
|
||||
|
||||
log.Debugf("Duplicated project background from project %d into %d", pd.ProjectID, pd.Project.ID)
|
||||
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
func duplicateTasks(s *xorm.Session, doer web.Auth, ld *ProjectDuplicate) (newTaskIDs map[int64]int64, err error) {
|
||||
|
||||
@@ -28,18 +28,30 @@ import (
|
||||
)
|
||||
|
||||
func TestProjectDuplicate(t *testing.T) {
|
||||
t.Run("duplicate project", func(t *testing.T) {
|
||||
testProjectDuplicate(t, 1, 1)
|
||||
})
|
||||
|
||||
t.Run("duplicate project with uploaded background", func(t *testing.T) {
|
||||
// Project 35 has a background_file_id of 1, which is NOT an Unsplash photo
|
||||
// This tests the fix for issue #1745 where duplicating a project with an uploaded
|
||||
// (non-Unsplash) background would fail with an internal server error
|
||||
testProjectDuplicate(t, 35, 6)
|
||||
})
|
||||
}
|
||||
|
||||
func testProjectDuplicate(t *testing.T, projectID int64, userID int64) {
|
||||
files.InitTestFileFixtures(t)
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
u := &user.User{
|
||||
ID: 1,
|
||||
ID: userID,
|
||||
}
|
||||
|
||||
l := &ProjectDuplicate{
|
||||
ProjectID: 1,
|
||||
ProjectID: projectID,
|
||||
}
|
||||
can, err := l.CanCreate(s, u)
|
||||
require.NoError(t, err)
|
||||
@@ -118,6 +130,7 @@ func TestProjectDuplicate(t *testing.T) {
|
||||
}
|
||||
|
||||
// Check that the kanban view in the duplicated project has a different default bucket than the original
|
||||
// (only if the original kanban view has a default bucket configured)
|
||||
var originalKanbanView *ProjectView
|
||||
var duplicatedKanbanView *ProjectView
|
||||
|
||||
@@ -135,10 +148,7 @@ func TestProjectDuplicate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
require.NotNil(t, originalKanbanView, "Original project does not have a kanban view")
|
||||
require.NotNil(t, duplicatedKanbanView, "Duplicated project does not have a kanban view")
|
||||
|
||||
if originalKanbanView != nil && duplicatedKanbanView != nil {
|
||||
if originalKanbanView != nil && duplicatedKanbanView != nil && originalKanbanView.DefaultBucketID != 0 {
|
||||
assert.NotEqual(t, originalKanbanView.DefaultBucketID, duplicatedKanbanView.DefaultBucketID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,6 +288,8 @@ func (vcls *VikunjaCaldavProjectStorage) CreateResource(rpath, content string) (
|
||||
|
||||
vTask, err := caldav.ParseTaskFromVTODO(content)
|
||||
if err != nil {
|
||||
log.Errorf("[CALDAV] Failed to parse VTODO in CreateResource: %v", err)
|
||||
log.Debugf("[CALDAV] VTODO content that failed to parse: %s", content)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -296,15 +298,18 @@ func (vcls *VikunjaCaldavProjectStorage) CreateResource(rpath, content string) (
|
||||
// Check the permissions
|
||||
canCreate, err := vTask.CanCreate(s, vcls.user)
|
||||
if err != nil {
|
||||
log.Errorf("[CALDAV] Permission check failed in CreateResource for user %s, project %d: %v", vcls.user.Username, vcls.project.ID, err)
|
||||
return nil, err
|
||||
}
|
||||
if !canCreate {
|
||||
log.Warningf("[CALDAV] User %s does not have permission to create task in project %d", vcls.user.Username, vcls.project.ID)
|
||||
return nil, errs.ForbiddenError
|
||||
}
|
||||
|
||||
// Create the task
|
||||
err = vTask.Create(s, vcls.user)
|
||||
if err != nil {
|
||||
log.Errorf("[CALDAV] Failed to create task in CreateResource: %v, task: %+v", err, vTask)
|
||||
_ = s.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
@@ -312,6 +317,7 @@ func (vcls *VikunjaCaldavProjectStorage) CreateResource(rpath, content string) (
|
||||
vcls.task.ID = vTask.ID
|
||||
err = persistLabels(s, vcls.user, vcls.task, vTask.Labels)
|
||||
if err != nil {
|
||||
log.Errorf("[CALDAV] Failed to persist labels in CreateResource: %v, labels: %+v", err, vTask.Labels)
|
||||
_ = s.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
@@ -319,11 +325,13 @@ func (vcls *VikunjaCaldavProjectStorage) CreateResource(rpath, content string) (
|
||||
vcls.task.ProjectID = vcls.project.ID
|
||||
err = persistRelations(s, vcls.user, vcls.task, vTask.RelatedTasks)
|
||||
if err != nil {
|
||||
log.Errorf("[CALDAV] Failed to persist relations in CreateResource: %v, relations: %+v", err, vTask.RelatedTasks)
|
||||
_ = s.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
log.Errorf("[CALDAV] Failed to commit transaction in CreateResource: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -341,6 +349,8 @@ func (vcls *VikunjaCaldavProjectStorage) UpdateResource(rpath, content string) (
|
||||
|
||||
vTask, err := caldav.ParseTaskFromVTODO(content)
|
||||
if err != nil {
|
||||
log.Errorf("[CALDAV] Failed to parse VTODO in UpdateResource: %v", err)
|
||||
log.Debugf("[CALDAV] VTODO content that failed to parse: %s", content)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -357,10 +367,12 @@ func (vcls *VikunjaCaldavProjectStorage) UpdateResource(rpath, content string) (
|
||||
// Check the permissions
|
||||
canUpdate, err := vTask.CanUpdate(s, vcls.user)
|
||||
if err != nil {
|
||||
log.Errorf("[CALDAV] Permission check failed in UpdateResource for user %s, task %d: %v", vcls.user.Username, vcls.task.ID, err)
|
||||
_ = s.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
if !canUpdate {
|
||||
log.Warningf("[CALDAV] User %s does not have permission to update task %d", vcls.user.Username, vcls.task.ID)
|
||||
_ = s.Rollback()
|
||||
return nil, errs.ForbiddenError
|
||||
}
|
||||
@@ -368,23 +380,27 @@ func (vcls *VikunjaCaldavProjectStorage) UpdateResource(rpath, content string) (
|
||||
// Update the task
|
||||
err = vTask.Update(s, vcls.user)
|
||||
if err != nil {
|
||||
log.Errorf("[CALDAV] Failed to update task in UpdateResource: %v, task: %+v", err, vTask)
|
||||
_ = s.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = persistLabels(s, vcls.user, vcls.task, vTask.Labels)
|
||||
if err != nil {
|
||||
log.Errorf("[CALDAV] Failed to persist labels in UpdateResource: %v, labels: %+v", err, vTask.Labels)
|
||||
_ = s.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = persistRelations(s, vcls.user, vcls.task, vTask.RelatedTasks)
|
||||
if err != nil {
|
||||
log.Errorf("[CALDAV] Failed to persist relations in UpdateResource: %v, relations: %+v", err, vTask.RelatedTasks)
|
||||
_ = s.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
log.Errorf("[CALDAV] Failed to commit transaction in UpdateResource: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user