Compare commits

...

9 Commits

Author SHA1 Message Date
renovate[bot]
310685973b chore(deps): update dev-dependencies 2025-12-05 16:55:30 +00:00
renovate[bot]
f865bd8555 fix(deps): update dependency express to v5.2.1 (#1932)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [express](https://expressjs.com/)
([source](https://redirect.github.com/expressjs/express)) | [`5.2.0` ->
`5.2.1`](https://renovatebot.com/diffs/npm/express/5.2.0/5.2.1) |
![age](https://developer.mend.io/api/mc/badges/age/npm/express/5.2.1?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/express/5.2.0/5.2.1?slim=true)
|

---

### Release Notes

<details>
<summary>expressjs/express (express)</summary>

###
[`v5.2.1`](https://redirect.github.com/expressjs/express/blob/HEAD/History.md#521--2025-12-01)

[Compare
Source](https://redirect.github.com/expressjs/express/compare/v5.2.0...v5.2.1)

\=======================

- Revert security fix for
[CVE-2024-51999](https://www.cve.org/CVERecord?id=CVE-2024-51999)
([GHSA-pj86-cfqh-vqx6](https://redirect.github.com/expressjs/express/security/advisories/GHSA-pj86-cfqh-vqx6))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/go-vikunja/vikunja).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi4zMi4yIiwidXBkYXRlZEluVmVyIjoiNDIuMzIuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIl19-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-05 09:04:57 +01:00
renovate[bot]
5a17c56735 chore(deps): update node.js to 682368d (#1931)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [node](https://redirect.github.com/nodejs/node)
([changelog](https://redirect.github.com/nodejs/node/compare/sha256:2867d550cf9d8bb50059a0fff528741f11a84d985c732e60e19e8e75c7239c43..sha256:682368d8253e0c3364b803956085c456a612d738bd635926d73fa24db3ce53d7))
| stage | digest | `2867d55` -> `682368d` |

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/go-vikunja/vikunja).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi4zMi4yIiwidXBkYXRlZEluVmVyIjoiNDIuMzIuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIl19-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-05 09:04:37 +01:00
kolaente
112df4a752 fix(caldav): init logger in tests 2025-12-04 11:10:19 +01:00
kolaente
da0822c3f4 feat(caldav): add more error logging 2025-12-04 10:54:31 +01:00
Copilot
30104fb749 fix: escape backticks and special chars in commit message for GitHub Action (#1928)
The `issue-closed-comment` workflow fails when commit messages contain
backticks because they're interpolated directly into JS template
strings, breaking syntax.

### Changes
- Escape backslashes, backticks, and `${` sequences before setting the
commit message output
- Order matters: backslashes first to avoid interfering with subsequent
escaping

```javascript
// Before: raw message breaks template string if it contains backticks
core.setOutput('commit_message', commit.message);

// After: properly escaped for safe interpolation
const escapedMessage = commit.message.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$\{/g, '\\${');
core.setOutput('commit_message', escapedMessage);
```

<!-- START COPILOT CODING AGENT SUFFIX -->



<details>

<summary>Original prompt</summary>

> the github action which comments on issue closure fails when the
commit message contains ` since these are js strings. Make sure to
escape them.


</details>



<!-- START COPILOT CODING AGENT TIPS -->
---

 Let Copilot coding agent [set things up for
you](https://github.com/go-vikunja/vikunja/issues/new?title=+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot)
— coding agent works faster and does higher quality work when set up for
your repo.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: kolaente <13721712+kolaente@users.noreply.github.com>
2025-12-04 09:38:24 +00:00
Copilot
7cf2a6886e fix: clear error when duplicating project with uploaded background (#1926)
Resolves https://github.com/go-vikunja/vikunja/issues/1745

- [x] Understand the issue from GitHub issue #1745
- [x] Analyze the codebase to locate the bug in
`duplicateProjectBackground` function
- [x] Fix the bug: return nil explicitly at the end of
duplicateProjectBackground
- [x] Add test for duplicating a project with an uploaded background (as
subtest)
- [x] Run tests and verify the fix
- [x] Run code review and address any feedback
- [x] Run CodeQL security scan

## Summary of Changes

### Problem
When duplicating a project with an uploaded (non-Unsplash) background
image, users encounter an internal server error (HTTP 500). The backend
logs show: `file was not downloaded from unsplash [FileID: X]`

### Root Cause
The `duplicateProjectBackground` function in
`pkg/models/project_duplicate.go` uses named returns. When
`GetUnsplashPhotoByFileID` returns `ErrFileIsNotUnsplashFile` for an
uploaded background, the error was intentionally ignored (to proceed
with copying the file) but not cleared from the named return variable.
This caused the error to be returned at the end of the function via the
bare `return` statement, triggering a 500 response.

### Solution
Changed the bare `return` at the end of `duplicateProjectBackground` to
`return nil` explicitly.

### Changes
1. **`pkg/models/project_duplicate.go`**: Changed bare `return` to
`return nil` at the end of `duplicateProjectBackground`
2. **`pkg/models/project_duplicate_test.go`**: Added subtest "duplicate
project with uploaded background" to `TestProjectDuplicate`

### Testing
- All existing tests pass
- Added subtest to `TestProjectDuplicate` for uploaded background
scenario (project 35 with non-Unsplash background)

### Security Summary
- No security vulnerabilities found by CodeQL
- Code review passed

<!-- START COPILOT CODING AGENT SUFFIX -->



<details>

<summary>Original prompt</summary>

> # Duplicate project with uploaded background - Implementation Plan
> 
> ## Overview
> Users encounter an internal server error when duplicating a project
that uses an uploaded background image (non-Unsplash). The b
> ackend attempt to copy the background leaves a non-Unsplash error
(`ErrFileIsNotUnsplashFile`) in a named return value, causing
> the duplication API call to fail even though the error should be
ignored. We need to adjust the duplication flow to allow upload
> ed backgrounds and add regression tests.
> 
> ## Current State Analysis
> - Project duplication calls `duplicateProjectBackground` to copy the
background file. The helper tries to copy a downloaded Unsp
> lash image and returns `ErrFileIsNotUnsplashFile` for uploaded files.
> - In the duplication code, the error variable is not cleared after
intentionally ignoring this specific error, so the function s
> till returns the error and triggers a 500 response.
> - There are no automated regression tests covering project duplication
with uploaded backgrounds.
> 
> ### Key Discoveries
> - The duplication logic treats Unsplash and uploaded backgrounds
differently and only clears the Unsplash download error, leavin
> g the non-Unsplash error set.
> - The API currently works for Unsplash backgrounds but fails for
uploaded backgrounds due to the lingering error value.
> 
> ## Desired End State
> - Duplicating a project succeeds for both Unsplash and uploaded
backgrounds.
> - Uploaded background files (and their metadata) are copied correctly
to the new project when possible, or gracefully skipped wi
> thout failing duplication.
> - Regression tests cover duplication with both background types to
prevent future regressions.
> 
> ## What We're NOT Doing
> - No changes to the background upload endpoints or UI selection
workflow.
> - No changes to Unsplash download behavior or quota handling.
> - No new migration or database schema changes.
> 
> ## Implementation Approach
> 1. Fix backend duplication error handling so uploaded backgrounds do
not cause a fatal error.
> 2. Add backend tests to cover duplication with uploaded backgrounds
and Unsplash backgrounds (success paths) and verify duplicat
> ion works without returning 500 errors.
> 3. Ensure tests document the expected behavior and guard against
regressions.
> 
> ## Phase 1: Fix duplication error handling
> ### Overview
> Make project duplication tolerate uploaded backgrounds by clearing or
not propagating `ErrFileIsNotUnsplashFile` once it has bee
> n intentionally ignored.
> 
> ### Changes Required
> - **File:** `pkg/models/projects.go` (or relevant duplication helper)
> - Adjust `duplicateProjectBackground` (or the calling logic) to reset
the named return error after handling `ErrFileIsNotUnspl
> ashFile`, ensuring the function returns `nil` when no real error
occurs.
> - Keep existing behavior for other errors and for Unsplash downloads.
> 
> ### Success Criteria
> - Uploaded background duplication no longer returns an internal server
error.
> - Unsplash background duplication remains functional and still
surfaces real errors.
> 
> ## Phase 2: Add regression tests
> ### Overview
> Add automated tests verifying project duplication works for both
uploaded and Unsplash backgrounds.
> 
> ### Changes Required
> - **File:** `pkg/models/projects_test.go` (or closest existing test
file for project duplication)
> - Add a test that sets up a project with an uploaded background file,
duplicates the project, and asserts duplication succeeds
>  and the duplicated project has an appropriate background reference.
> - Add/adjust test coverage for Unsplash background duplication to
confirm unchanged behavior.
> - Use existing fixtures or temporary files as needed for uploaded
background setup.
> 
> ### Success Criteria
> - Tests fail on current main branch but pass after the fix.
> - Tests validate that duplication completes without 500 errors for
both background types.
> 
> ## Testing Strategy
> - Automated Go tests via `mage test:filter` targeting the new
duplication tests.
> - Optionally run the broader suite (`mage test:feature`) if time
permits to ensure no regressions.
> 
> ## Manual Verification
> 1. Create a project and upload a background via the UI; duplicate it;
observe duplication succeeds and background is present or
> gracefully handled.
> 2. Create a project with an Unsplash background; duplicate it; verify
duplication succeeds.
> 3. Check API responses for duplication calls to ensure no internal
server errors.


</details>



<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in
our [2 minute survey](https://gh.io/copilot-coding-agent-survey).

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: kolaente <13721712+kolaente@users.noreply.github.com>
2025-12-04 10:16:16 +01:00
renovate[bot]
5bb53eaefa fix(deps): update module github.com/spf13/cobra to v1.10.2 (#1927)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [github.com/spf13/cobra](https://redirect.github.com/spf13/cobra) |
`v1.10.1` -> `v1.10.2` |
![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fspf13%2fcobra/v1.10.2?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fspf13%2fcobra/v1.10.1/v1.10.2?slim=true)
|

---

### Release Notes

<details>
<summary>spf13/cobra (github.com/spf13/cobra)</summary>

###
[`v1.10.2`](https://redirect.github.com/spf13/cobra/releases/tag/v1.10.2)

[Compare
Source](https://redirect.github.com/spf13/cobra/compare/v1.10.1...v1.10.2)

#### 🔧 Dependencies

- chore: Migrate from `gopkg.in/yaml.v3` to `go.yaml.in/yaml/v3` by
[@&#8203;dims](https://redirect.github.com/dims) in
[#&#8203;2336](https://redirect.github.com/spf13/cobra/pull/2336) - the
`gopkg.in/yaml.v3` package has been deprecated for some time: this
should significantly cleanup dependency/supply-chains for consumers of
`spf13/cobra`

#### 📈 CI/CD

- Fix linter and allow CI to pass by
[@&#8203;marckhouzam](https://redirect.github.com/marckhouzam) in
[#&#8203;2327](https://redirect.github.com/spf13/cobra/pull/2327)
- fix: actions/setup-go v6 by
[@&#8203;jpmcb](https://redirect.github.com/jpmcb) in
[#&#8203;2337](https://redirect.github.com/spf13/cobra/pull/2337)

#### 🔥✍🏼 Docs

- Add documentation for repeated flags functionality by
[@&#8203;rvergis](https://redirect.github.com/rvergis) in
[#&#8203;2316](https://redirect.github.com/spf13/cobra/pull/2316)

#### 🍂 Refactors

- refactor: replace several vars with consts by
[@&#8203;htoyoda18](https://redirect.github.com/htoyoda18) in
[#&#8203;2328](https://redirect.github.com/spf13/cobra/pull/2328)
- refactor: change minUsagePadding from var to const by
[@&#8203;ssam18](https://redirect.github.com/ssam18) in
[#&#8203;2325](https://redirect.github.com/spf13/cobra/pull/2325)

#### 🤗 New Contributors

- [@&#8203;rvergis](https://redirect.github.com/rvergis) made their
first contribution in
[#&#8203;2316](https://redirect.github.com/spf13/cobra/pull/2316)
- [@&#8203;htoyoda18](https://redirect.github.com/htoyoda18) made their
first contribution in
[#&#8203;2328](https://redirect.github.com/spf13/cobra/pull/2328)
- [@&#8203;ssam18](https://redirect.github.com/ssam18) made their first
contribution in
[#&#8203;2325](https://redirect.github.com/spf13/cobra/pull/2325)
- [@&#8203;dims](https://redirect.github.com/dims) made their first
contribution in
[#&#8203;2336](https://redirect.github.com/spf13/cobra/pull/2336)

**Full Changelog**:
<https://github.com/spf13/cobra/compare/v1.10.1...v1.10.2>

Thank you to our amazing contributors!!!!! 🐍 🚀

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/go-vikunja/vikunja).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi4zMi4yIiwidXBkYXRlZEluVmVyIjoiNDIuMzIuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIl19-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-04 09:07:54 +01:00
Frederick [Bot]
cec8daba59 chore(i18n): update translations via Crowdin 2025-12-04 00:57:27 +00:00
17 changed files with 344 additions and 372 deletions

View File

@@ -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)`);

View File

@@ -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

View File

@@ -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
View File

@@ -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: {}

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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.",

View File

@@ -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.",

View File

@@ -181,6 +181,9 @@
"dark": "Тёмная"
}
},
"backgroundBrightness": {
"title": "Яркость фона"
},
"apiTokens": {
"title": "Токены API",
"general": "Токены API позволяют использовать Vikunja API без использования данных для входа пользователя.",

2
go.mod
View File

@@ -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
View File

@@ -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=

View File

@@ -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
}

View File

@@ -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

View File

@@ -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) が削除されました",

View File

@@ -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) {

View File

@@ -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)
}
}

View File

@@ -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
}