A nil payload signals data corruption or a version mismatch on the
event bus, not a safe-to-drop condition. Returning an error lets the
watermill retry middleware retry the message and eventually park it in
the poison queue instead of silently acking it.
Also clear the example.com fixture webhook (id=1) in the existing
TestTaskUpdateWebhookE2E, since it now errors after sendWebhookPayload
returns non-nil for non-2xx responses.
Previously the HTTP response status was only logged, so retries never
triggered for failing webhooks and downstream fan-out bugs (#2569) were
impossible to exercise via tests. Returning an error lets the watermill
retry middleware do its job.
The filter view cron built an unbounded builder.Or(deleteCond...) tree
that exceeded SQLite's 1000-node expression depth limit when many tasks
needed removal. Delete conditions are now processed in chunks of 500.
Ref: #2550
resolvePositionConflictsAfterInsert now falls back to a full position
recalculation when resolveTaskPositionConflicts returns
ErrNeedsFullRecalculation, instead of bubbling the error up as HTTP 500.
This mirrors the existing fallback logic in the CLI repair command.
Ref: #2550
Regression test for #2552. Deletes the background of project 35 (owned by
testuser6) and then fetches the project to confirm the title is still
'Test35 with background'.
Fixes#2552. RemoveProjectBackground was passing a minimal Project struct
(only ID set) through UpdateProject, which always includes 'title' in its
Cols() list. This caused XORM to write the zero-value empty title to the
DB, wiping the real project title. Now uses ClearProjectBackground which
only updates background_file_id and background_blur_hash.
Register CachedAvatar and Photo with encoding/gob so Redis can properly
deserialize them. Migrate both to use RememberValue[T] which calls
GetWithValue() internally, fixing the broken type assertion when Redis
is the keyvalue backend.
Also removes the recursion-depth fallback in upload.go since
RememberValue eliminates the type mismatch failure mode entirely.
Instead of hardcoding the workbox version string in the service worker,
read it from workbox-precaching/package.json via Vite's define option.
This ensures the service worker always references the correct workbox
version that is actually installed.
Resolves#2549
Allow users to skip the first N data rows when importing CSV files.
This is useful when the CSV contains metadata rows before the actual
task data begins. Adds skip_rows to ImportConfig (backend) and a
number input in the parsing options UI (frontend).
Add a new CSV migration module that allows users to import tasks from
any CSV file with custom column mapping and parsing options.
Backend changes:
- New CSV migrator module with detection, preview, and import endpoints
- Auto-detection of delimiter, quote character, and date format
- Suggested column mappings based on column name patterns
- Transactional import using InsertFromStructure
Frontend changes:
- New CSV migration UI with two-step flow (upload -> mapping -> import)
- Column mapping selectors for all task attributes
- Live preview showing first 5 tasks with current mapping
- Parsing option controls for delimiter and date format
The CSV migrator creates a parent "Imported from CSV" project with
child projects based on the project column if provided, or a default
"Tasks" project for tasks without a specified project.
The docker/metadata-action uses github.sha for the SHA tag, which for
pull_request_target events is the base branch commit, not the PR head.
The comment step was independently constructing SHA tags from the PR
head SHA, causing preview URLs that didn't match any actual docker
image tag. Now reads the actual tags from docker meta output instead.
Syncs the sort choice to a ?sort=field:order URL parameter so it
survives page refreshes and can be shared. The default position sort
is omitted from the URL to keep links clean.