mirror of
https://github.com/wekan/wekan.git
synced 2026-11-05 21:44:59 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7f9018a60 | ||
|
|
b6fe0a7256 | ||
|
|
02e3c35d69 | ||
|
|
4766b83da7 | ||
|
|
011c172c8c | ||
|
|
cb18f9b735 | ||
|
|
abe8af0788 | ||
|
|
997f1fb9df | ||
|
|
9828081c76 | ||
|
|
d2ec3ee400 | ||
|
|
43006f9025 | ||
|
|
fcfd5bce9b | ||
|
|
4cbefed339 | ||
|
|
7d594c95ae | ||
|
|
7d5f29e114 | ||
|
|
29b617769a | ||
|
|
f0d25a2431 | ||
|
|
09805a8a91 | ||
|
|
6f7ad270d1 | ||
|
|
bd1c611688 | ||
|
|
ae9be62ef3 | ||
|
|
41cd166358 | ||
|
|
e39dcd4ebf | ||
|
|
4f1a60aa7b | ||
|
|
5757cfc8ca | ||
|
|
c7c8a200ff | ||
|
|
71fcf2b454 | ||
|
|
1dbc26d200 | ||
|
|
84fee7b660 | ||
|
|
3b7a276347 | ||
|
|
02cf16d8c6 | ||
|
|
54944717ee | ||
|
|
b99d21d9f1 | ||
|
|
3dfd84973c | ||
|
|
dd4611e43f | ||
|
|
69e6b8bcd1 | ||
|
|
6ab29dc81e | ||
|
|
9ae6a91daf | ||
|
|
c3674330ac | ||
|
|
be5ddd77ef | ||
|
|
9fc88a783c | ||
|
|
748fd2d391 | ||
|
|
4ac24dc546 | ||
|
|
4c630d4ba8 | ||
|
|
a563d3c4b2 | ||
|
|
95b186e50a | ||
|
|
bbc525ef27 | ||
|
|
bfb3fd246b | ||
|
|
3a968052b9 | ||
|
|
d424bd6386 | ||
|
|
3824066f7f | ||
|
|
774d752620 | ||
|
|
dbca7cd72d | ||
|
|
8497e7c537 | ||
|
|
7f44459c9f | ||
|
|
d5c69e0259 | ||
|
|
598aa8dfa1 | ||
|
|
0360ca1583 | ||
|
|
9c51391a75 | ||
|
|
2ed13a1593 | ||
|
|
ca50c9d1aa | ||
|
|
548d7b483e | ||
|
|
71bbc9f67e | ||
|
|
05fd6fd3a5 | ||
|
|
e8939bc1e6 | ||
|
|
bc8d58011c | ||
|
|
0bf8cae81c | ||
|
|
984ec160da | ||
|
|
a2df80e64d | ||
|
|
3aaf8f9b61 | ||
|
|
1dfc223ca0 | ||
|
|
604cc455e0 | ||
|
|
e85072a14a | ||
|
|
1621f3784a | ||
|
|
ba6ad20a30 | ||
|
|
3847157753 | ||
|
|
4e77133aca | ||
|
|
77c696e37b | ||
|
|
e3f17be14d | ||
|
|
5ca4ebe09a | ||
|
|
3117e419bb | ||
|
|
3eba4aa782 | ||
|
|
127f1fb04e | ||
|
|
14b4f5b5ed | ||
|
|
e6a4abd394 | ||
|
|
25cdc4db0d | ||
|
|
c062d93d57 | ||
|
|
e014d5c816 | ||
|
|
84ab805ec1 | ||
|
|
a9a5ac7a07 | ||
|
|
0119e62da5 | ||
|
|
284d4b25ea | ||
|
|
75df523ef0 | ||
|
|
e5c844fca7 | ||
|
|
8a84707726 | ||
|
|
9f7eac158e | ||
|
|
9b729b502e | ||
|
|
cef965f73f | ||
|
|
43dd98f8a1 | ||
|
|
2cd772fcf5 | ||
|
|
eaa1d823de | ||
|
|
1a41c72c21 | ||
|
|
7943a17432 | ||
|
|
d41e17d6a0 | ||
|
|
4ec2cbcd5a | ||
|
|
1ce2d8cc88 | ||
|
|
8f76c7b2b0 | ||
|
|
b07c4d0a7b | ||
|
|
5b43b7159e | ||
|
|
7ce71dbb2e | ||
|
|
d615e599f4 | ||
|
|
aad00cdba2 | ||
|
|
07800da9af | ||
|
|
da0d338e33 | ||
|
|
c3c091635d | ||
|
|
8c499823cb | ||
|
|
1844425e8f | ||
|
|
68dd14140f | ||
|
|
1ba32f5269 | ||
|
|
30f36a7f31 | ||
|
|
20b9c9ddd6 | ||
|
|
0e9aed5a4b | ||
|
|
27d8468be0 | ||
|
|
d01526bbff | ||
|
|
ab3b48b334 | ||
|
|
b0da9097c1 | ||
|
|
158130cbfc | ||
|
|
53328f40c6 | ||
|
|
e7eeaf0b10 | ||
|
|
609b07a8db | ||
|
|
7545bb5506 | ||
|
|
457713968c | ||
|
|
372a58c112 | ||
|
|
527962966b | ||
|
|
a37e7d1cc7 | ||
|
|
33ad40d012 | ||
|
|
44b5e12de9 | ||
|
|
1207b573b5 | ||
|
|
a26295a2fb | ||
|
|
b2da9ac8d4 | ||
|
|
53dcf7379d | ||
|
|
dfa6c78d4f | ||
|
|
795850ed05 | ||
|
|
9ef7f4a07a | ||
|
|
cb2394a95d | ||
|
|
ac368de06a | ||
|
|
b26494766f | ||
|
|
68ab63c7fe | ||
|
|
e3e71b254a | ||
|
|
fc4eadedc9 | ||
|
|
9dca403782 | ||
|
|
dd90995db7 | ||
|
|
f8e31745d8 | ||
|
|
736d308860 | ||
|
|
9eca7a3941 | ||
|
|
af436e16c4 | ||
|
|
b1b414e850 | ||
|
|
57da3885a2 | ||
|
|
cb9c897309 | ||
|
|
81f07baeb8 | ||
|
|
001c258f96 | ||
|
|
9d77b9d7b7 | ||
|
|
2242da4176 | ||
|
|
0a3a28ef29 | ||
|
|
dcf5aa65b0 | ||
|
|
dd3be179ca | ||
|
|
bc1f149ea3 | ||
|
|
0dd11ea573 | ||
|
|
5feb83d919 | ||
|
|
8980668686 | ||
|
|
0d4e48ddd6 | ||
|
|
a01b175895 | ||
|
|
83f63c9efa | ||
|
|
3b031bd901 | ||
|
|
e2a7e300c9 | ||
|
|
844f59e12e | ||
|
|
1a3d7b5da4 | ||
|
|
3d2bdf4f22 | ||
|
|
b9fe1f1639 | ||
|
|
a39a7b90b1 | ||
|
|
5d1c979e3b | ||
|
|
d00dc6056b | ||
|
|
878a24f586 | ||
|
|
6d08181edd | ||
|
|
d5596f05b7 | ||
|
|
22b280a18e | ||
|
|
2e7c4ede2a | ||
|
|
ed77abd90d | ||
|
|
45fb547947 | ||
|
|
477ad737d3 | ||
|
|
a2802a850b | ||
|
|
5cbe27be7e | ||
|
|
8b21158736 | ||
|
|
651c6f8fb6 | ||
|
|
5b67312f0f | ||
|
|
1a222c4477 |
No files matched your search
@@ -7,13 +7,18 @@ name: Release All Platforms
|
||||
# Or use the convenience script (bumps versions, commits, pushes, then triggers):
|
||||
# ./releases/release-all.sh 9.35 9.36
|
||||
#
|
||||
# The Docker image and snap cover the platforms MongoDB ships server binaries for
|
||||
# (amd64, arm64; win64 + mac bundles too); MongoDB 7.x is the default database.
|
||||
# Additionally, WeKan server bundles are built for every other architecture
|
||||
# Node.js 24.x publishes binaries for — s390x and ppc64le — and attached to the
|
||||
# GitHub Release as extra downloads (run a separate MongoDB for those). These
|
||||
# extra-arch bundles are NOT part of the Docker image or snap. (armv7l/32-bit ARM
|
||||
# is not built: Node.js 24 ships no armv7l binaries.)
|
||||
# WeKan's default database is MongoDB, which ships server binaries only for amd64
|
||||
# and arm64 (+ the win64/mac bundles). On every OTHER architecture that has a
|
||||
# Node.js 24 build — ppc64le, s390x, riscv64 — there is no MongoDB, so those
|
||||
# bundles ship FerretDB v1 instead: the wekan/FerretDB fork with its embedded
|
||||
# pure-Go SQLite backend, which speaks the MongoDB wire protocol. The FerretDB
|
||||
# binary is cross-compiled once (build-ferretdb) and baked into EVERY bundle, so
|
||||
# amd64/arm64 users can opt into it too (WEKAN_DB=ferretdb, `snap set wekan
|
||||
# database=ferretdb`, or Docker env). Whichever backend is chosen binds the same
|
||||
# MongoDB port, so MONGO_URL is unchanged. ppc64le/s390x/riscv64 are now covered
|
||||
# by the Docker image as well (self-contained, FerretDB auto-started). riscv64
|
||||
# uses unofficial-builds.nodejs.org for Node 24. (armv7l/32-bit ARM is still not
|
||||
# built: there is NO Node.js 24 build for it anywhere, so nothing could run it.)
|
||||
#
|
||||
# This workflow does EVERYTHING for a release remotely (nothing is built or
|
||||
# pushed on the maintainer's machine), running jobs in parallel where possible:
|
||||
@@ -22,19 +27,27 @@ name: Release All Platforms
|
||||
# commits and pushes the bump to wekan/wekan main
|
||||
# - prepare: tags the bumped main as v<new_version> and extracts release notes
|
||||
# - website: updates the wekan/wekan.fi repo (install page + API docs) and pushes
|
||||
# - charts: updates and publishes the wekan/charts Helm chart and pushes
|
||||
# - charts: updates and publishes the wekan/charts Helm chart and pushes,
|
||||
# only AFTER the `docker` job has published the image (skipped if
|
||||
# docker fails)
|
||||
# - build-ferretdb: downloads the newest prebuilt ferretdb.zip from the
|
||||
# wekan/FerretDB releases (FerretDB is built + released separately by
|
||||
# its own build.sh — not compiled here); every bundle embeds its own
|
||||
# - build-*: builds the amd64 bundle (full Meteor 3 build), then rebuilds native
|
||||
# modules for arm64 from that amd64 base
|
||||
# modules for arm64 from that amd64 base (each embeds its FerretDB)
|
||||
# - release: creates the GitHub Release with the newest CHANGELOG.md notes and
|
||||
# uploads the core wekan-<version>-{amd64,arm64}.zip bundles
|
||||
# - build-win64 / build-mac-arm64 / build-extra-arches: built AFTER release, each
|
||||
# rebuilds the amd64 bundle's native modules for its platform (Windows,
|
||||
# macOS arm64, and QEMU-emulated s390x/ppc64le) and attaches its
|
||||
# .zip to the Release as an extra download. Decoupled from `release` so
|
||||
# a slow/flaky build never blocks it; not in the Docker image or snap
|
||||
# - docker: builds and pushes the multi-arch image to Docker Hub, Quay.io, GHCR
|
||||
# macOS arm64, and QEMU-emulated ppc64le/s390x/riscv64) and attaches
|
||||
# its .zip to the Release. Windows/macOS strip the Linux FerretDB back
|
||||
# out (MongoDB-only). Decoupled from `release` so a slow/flaky build
|
||||
# never blocks it
|
||||
# - docker: builds and pushes the multi-arch image (amd64, arm64, ppc64le, s390x,
|
||||
# riscv64) to Docker Hub, Quay.io, GHCR
|
||||
# - snap: builds the snap on Launchpad (amd64 + arm64), pushes it to the Snap
|
||||
# Store and attaches the .snap files to the GitHub Release
|
||||
# Store and attaches the .snap files to the GitHub Release. `snap set
|
||||
# wekan database=ferretdb` switches the running backend to FerretDB v1
|
||||
#
|
||||
# Required GitHub Secrets:
|
||||
# GHCR_AUTH - base64("username:token") for ghcr.io
|
||||
@@ -210,12 +223,14 @@ jobs:
|
||||
cd ..
|
||||
./wekan/releases/release-website.sh "${{ inputs.old_version }}" "${{ inputs.new_version }}"
|
||||
|
||||
# ─── 1c. Update and publish the Helm chart repo (parallel) ──────────────────
|
||||
# ─── 1c. Update and publish the Helm chart repo (after docker) ──────────────
|
||||
# Bumps the WeKan version in wekan/charts, packages the chart and publishes it
|
||||
# to the gh-pages Helm index, then pushes both branches. Runs in parallel with
|
||||
# the platform builds; packaging the chart does not need the built artifacts.
|
||||
# to the gh-pages Helm index, then pushes both branches. Runs only AFTER the
|
||||
# `docker` job succeeds: the chart points users at the freshly published
|
||||
# multi-arch image, so we don't advertise a new chart version until that image
|
||||
# is actually on the registries. If `docker` fails, this job is skipped.
|
||||
charts:
|
||||
needs: bump
|
||||
needs: docker
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout wekan repo (for releases/release-charts.sh + version refs)
|
||||
@@ -254,7 +269,7 @@ jobs:
|
||||
# The full Meteor build always runs here on x86_64; every other platform then
|
||||
# only rebuilds the native npm modules from this bundle.
|
||||
build-amd64:
|
||||
needs: prepare
|
||||
needs: [prepare, build-ferretdb]
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 180
|
||||
env:
|
||||
@@ -304,6 +319,31 @@ jobs:
|
||||
cd .build/bundle/programs/server
|
||||
npm install
|
||||
|
||||
- name: Download ferretdb.zip
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: ferretdb-zip
|
||||
|
||||
- name: Embed Node.js, FerretDB (amd64) and launcher into the bundle
|
||||
# Make the release .zip self-contained (offline): bundle Node.js + FerretDB
|
||||
# + a start-wekan.sh that runs WeKan on the bundled Node against the bundled
|
||||
# FerretDB (SQLite) by default, storing files on the filesystem under
|
||||
# WRITABLE_PATH. This amd64 bundle is the base every other Linux arch is
|
||||
# derived from; those jobs overwrite bundle/node and bundle/ferretdb with
|
||||
# their own arch's binaries and inherit this launcher.
|
||||
run: |
|
||||
# Bundled Node.js: the runner's node (setup-node) is linux-x64 Node 24,
|
||||
# matching this base bundle's arch.
|
||||
cp -L "$(command -v node)" .build/bundle/node
|
||||
chmod +x .build/bundle/node
|
||||
# Bundled FerretDB (amd64) from the single-source ferretdb.zip.
|
||||
unzip -o -j ferretdb.zip "ferretdb/amd64/ferretdb-amd64" -d .build/bundle
|
||||
mv .build/bundle/ferretdb-amd64 .build/bundle/ferretdb
|
||||
chmod +x .build/bundle/ferretdb
|
||||
# Self-contained launcher (defaults to FerretDB SQLite + WRITABLE_PATH).
|
||||
cp releases/ferretdb/start-wekan.sh .build/bundle/
|
||||
chmod +x .build/bundle/start-wekan.sh
|
||||
|
||||
- name: Create amd64 zip
|
||||
run: |
|
||||
cd .build
|
||||
@@ -317,7 +357,7 @@ jobs:
|
||||
|
||||
# ─── 3. Build arm64 bundle (rebuild native modules natively) ─────────────────
|
||||
build-arm64:
|
||||
needs: [prepare, build-amd64]
|
||||
needs: [prepare, build-amd64, build-ferretdb]
|
||||
runs-on: ubuntu-24.04-arm
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
@@ -356,6 +396,21 @@ jobs:
|
||||
node:${NODE_VERSION}-slim \
|
||||
sh -c "apt-get update -qq && apt-get install -y -q build-essential python3 && npm install"
|
||||
|
||||
- name: Download ferretdb.zip
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: ferretdb-zip
|
||||
|
||||
- name: Embed Node.js + FerretDB (arm64) into the bundle
|
||||
# The launcher (start-wekan.sh) is inherited from the amd64 base bundle;
|
||||
# swap bundle/node (runner is arm64 -> linux-arm64 Node 24) and bundle/ferretdb.
|
||||
run: |
|
||||
cp -L "$(command -v node)" bundle/node
|
||||
chmod +x bundle/node
|
||||
unzip -o -j ferretdb.zip "ferretdb/arm64/ferretdb-arm64" -d bundle
|
||||
mv bundle/ferretdb-arm64 bundle/ferretdb
|
||||
chmod +x bundle/ferretdb
|
||||
|
||||
- name: Create arm64 zip
|
||||
run: |
|
||||
zip -r wekan-${VERSION}-arm64.zip bundle
|
||||
@@ -372,6 +427,7 @@ jobs:
|
||||
- prepare
|
||||
- build-amd64
|
||||
- build-arm64
|
||||
- build-ferretdb
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
@@ -388,6 +444,10 @@ jobs:
|
||||
tag_name: v${{ needs.prepare.outputs.version }}
|
||||
name: v${{ needs.prepare.outputs.version }}
|
||||
body: ${{ needs.prepare.outputs.changelog }}
|
||||
# ferretdb.zip is NOT attached here: it already lives on the
|
||||
# wekan/FerretDB releases (https://github.com/wekan/FerretDB/releases),
|
||||
# which is where the WeKan builds fetch it from. The per-arch FerretDB
|
||||
# binaries are embedded inside each wekan-<version>-<arch>.zip bundle.
|
||||
files: |
|
||||
wekan-${{ needs.prepare.outputs.version }}-amd64.zip
|
||||
wekan-${{ needs.prepare.outputs.version }}-arm64.zip
|
||||
@@ -402,7 +462,7 @@ jobs:
|
||||
|
||||
# Build win64 bundle (rebuild native modules on Windows).
|
||||
build-win64:
|
||||
needs: [prepare, build-amd64, release]
|
||||
needs: [prepare, build-amd64, release, build-ferretdb]
|
||||
runs-on: windows-latest
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
@@ -432,6 +492,10 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
7z x wekan-${VERSION}-amd64.zip
|
||||
# Drop the Linux node + FerretDB binaries + POSIX launcher inherited from
|
||||
# the amd64 base bundle; the Windows node.exe, ferretdb.exe and
|
||||
# start-wekan.bat are added in a later step.
|
||||
rm -f bundle/node bundle/ferretdb bundle/.ferretdb-default bundle/start-wekan.sh
|
||||
# Fix: bcrypt 5.0.1 (Meteor accounts-password) ships node-addon-api@3
|
||||
# which fails to compile on Node.js 24. Replace with bcrypt 6.0.0.
|
||||
BCRYPT_DIR="$(pwd)/bundle/programs/server/npm/node_modules/meteor/accounts-password/node_modules/bcrypt"
|
||||
@@ -488,6 +552,34 @@ jobs:
|
||||
echo "=== bundle contents ==="
|
||||
ls -1 bundle/
|
||||
|
||||
- name: Download ferretdb.zip
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: ferretdb-zip
|
||||
|
||||
- name: Embed Node.js, FerretDB (win64) and launcher into the bundle
|
||||
shell: bash
|
||||
# Make the Windows .zip self-contained: bundle node.exe (from the runner's
|
||||
# win-x64 Node 24), ferretdb.exe (from the single-source ferretdb.zip) and
|
||||
# start-wekan.bat. FerretDB embedding is tolerant: if it did not compile for
|
||||
# windows/amd64 the bundle stays MongoDB-only (start-wekan.bat then won't
|
||||
# find ferretdb.exe).
|
||||
run: |
|
||||
# Bundled Node.js (win-x64 node.exe from setup-node).
|
||||
NODE_SRC="$(command -v node)"
|
||||
[ -f "$NODE_SRC" ] || NODE_SRC="${NODE_SRC}.exe"
|
||||
cp "$NODE_SRC" bundle/node.exe
|
||||
# Bundled FerretDB (win64).
|
||||
if 7z e -y ferretdb.zip "ferretdb/win64/ferretdb-win64.exe" -obundle > /dev/null 2>&1 \
|
||||
&& [ -f bundle/ferretdb-win64.exe ]; then
|
||||
mv bundle/ferretdb-win64.exe bundle/ferretdb.exe
|
||||
echo "Embedded FerretDB win64 -> bundle/ferretdb.exe"
|
||||
else
|
||||
echo "No win64 FerretDB in ferretdb.zip; Windows bundle stays MongoDB-only."
|
||||
fi
|
||||
# Self-contained Windows launcher (defaults to FerretDB SQLite).
|
||||
cp src/releases/ferretdb/start-wekan.bat bundle/
|
||||
|
||||
- name: Create win64 zip
|
||||
shell: cmd
|
||||
run: 7z a wekan-%VERSION%-win64.zip bundle
|
||||
@@ -510,7 +602,7 @@ jobs:
|
||||
|
||||
# Build mac-arm64 bundle (rebuild native modules on Apple Silicon).
|
||||
build-mac-arm64:
|
||||
needs: [prepare, build-amd64, release]
|
||||
needs: [prepare, build-amd64, release, build-ferretdb]
|
||||
runs-on: macos-14 # macos-14 runners are Apple Silicon (arm64)
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
@@ -528,6 +620,10 @@ jobs:
|
||||
- name: Rebuild native modules for macOS arm64
|
||||
run: |
|
||||
unzip -q wekan-${VERSION}-amd64.zip
|
||||
# Replace the Linux node + FerretDB binaries inherited from the amd64 base
|
||||
# bundle with the macOS builds (added in a later step). The POSIX launcher
|
||||
# start-wekan.sh works on macOS, so keep it.
|
||||
rm -f bundle/node bundle/ferretdb bundle/.ferretdb-default
|
||||
# Fix: bcrypt 5.0.1 (Meteor accounts-password) ships node-addon-api@3
|
||||
# which fails to compile on Node.js 24. Replace with bcrypt 6.0.0.
|
||||
BCRYPT_DIR="$(pwd)/bundle/programs/server/npm/node_modules/meteor/accounts-password/node_modules/bcrypt"
|
||||
@@ -547,6 +643,29 @@ jobs:
|
||||
npm install
|
||||
cd ../../..
|
||||
|
||||
- name: Download ferretdb.zip
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: ferretdb-zip
|
||||
|
||||
- name: Embed Node.js + FerretDB (mac-arm64) into the bundle
|
||||
# Bundle the macOS arm64 Node.js (runner's darwin-arm64 Node 24) and the
|
||||
# macOS FerretDB binary from the single-source ferretdb.zip. FerretDB is
|
||||
# tolerant: if it did not compile for darwin/arm64 the bundle stays
|
||||
# MongoDB-only (start-wekan.sh then won't find ferretdb).
|
||||
run: |
|
||||
cp -L "$(command -v node)" bundle/node
|
||||
chmod +x bundle/node
|
||||
if unzip -o -j ferretdb.zip "ferretdb/mac-arm64/ferretdb-mac-arm64" -d bundle >/dev/null 2>&1 \
|
||||
&& [ -f bundle/ferretdb-mac-arm64 ]; then
|
||||
mv bundle/ferretdb-mac-arm64 bundle/ferretdb
|
||||
chmod +x bundle/ferretdb
|
||||
echo "Embedded FerretDB mac-arm64 -> bundle/ferretdb"
|
||||
else
|
||||
echo "No mac-arm64 FerretDB in ferretdb.zip; macOS bundle stays MongoDB-only."
|
||||
rm -f bundle/start-wekan.sh
|
||||
fi
|
||||
|
||||
- name: Create mac-arm64 zip
|
||||
run: zip -r wekan-${VERSION}-mac-arm64.zip bundle
|
||||
|
||||
@@ -564,13 +683,49 @@ jobs:
|
||||
GH_REPO: ${{ github.repository }}
|
||||
run: gh release upload "v${VERSION}" "wekan-${VERSION}-mac-arm64.zip" --clobber
|
||||
|
||||
# Extra-architecture Linux bundles (s390x, ppc64le).
|
||||
# armv7l (32-bit ARM) is intentionally excluded: Node.js 24 ships no armv7l
|
||||
# binaries (official or unofficial), so there is no node:24 linux/arm/v7 image
|
||||
# to rebuild native modules against and no Node 24 runtime to run a bundle on
|
||||
# an armv7 device. See https://nodejs.org/dist/latest-v24.x/ (no -linux-armv7l).
|
||||
# ─── Fetch the prebuilt standalone FerretDB binaries: ferretdb.zip ───────────
|
||||
# WeKan normally uses MongoDB, but MongoDB Community only ships amd64/arm64
|
||||
# server binaries. On every other platform WeKan can instead use FerretDB v1
|
||||
# (the wekan/FerretDB fork with its embedded pure-Go SQLite backend, which
|
||||
# speaks the MongoDB wire protocol). FerretDB is built + released SEPARATELY in
|
||||
# the wekan/FerretDB repo (its build.sh cross-compiles every platform into
|
||||
# ferretdb.zip and it is attached to a GitHub Release there); this workflow does
|
||||
# NOT build FerretDB with Go. Here we just download the newest released
|
||||
# ferretdb.zip and republish it, so every WeKan build (bundle .zip, snap, docker,
|
||||
# win64, mac) takes its FerretDB binary from that same single source. The zip
|
||||
# layout is ferretdb/<arch>/ferretdb-<arch>[.exe] + ferretdb/README.md.
|
||||
build-ferretdb:
|
||||
needs: prepare
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: Download the latest ferretdb.zip from wekan/FerretDB releases
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# /releases/latest/download/<asset> always resolves to the asset on the
|
||||
# newest (non-prerelease) release of wekan/FerretDB.
|
||||
url="https://github.com/wekan/FerretDB/releases/latest/download/ferretdb.zip"
|
||||
echo "Downloading ${url} ..."
|
||||
curl -fSL --retry 5 --retry-delay 10 -o ferretdb.zip "$url"
|
||||
echo "=== ferretdb.zip contents ==="
|
||||
unzip -l ferretdb.zip
|
||||
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: ferretdb-zip
|
||||
path: ferretdb.zip
|
||||
retention-days: 1
|
||||
|
||||
# Extra-architecture Linux bundles (ppc64le, s390x, riscv64) — the arches that
|
||||
# have a Node.js 24 build but NO MongoDB server, so they ship FerretDB instead.
|
||||
# ppc64le and s390x use official nodejs.org binaries; riscv64 uses the
|
||||
# unofficial-builds.nodejs.org binary (nodejs.org ships no riscv64). armv7l
|
||||
# (32-bit ARM) is intentionally excluded: there is NO Node.js 24 build for it
|
||||
# anywhere (official or unofficial), so there is no runtime to run the bundle on
|
||||
# an armv7 device, regardless of RAM. See https://nodejs.org/dist/latest-v24.x/
|
||||
# (no -linux-armv7l) and https://unofficial-builds.nodejs.org/ (riscv64 only).
|
||||
build-extra-arches:
|
||||
needs: [prepare, build-amd64, release]
|
||||
needs: [prepare, build-amd64, release, build-ferretdb]
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 300
|
||||
strategy:
|
||||
@@ -579,8 +734,13 @@ jobs:
|
||||
include:
|
||||
- arch: s390x
|
||||
platform: linux/s390x
|
||||
node_base: official
|
||||
- arch: ppc64le
|
||||
platform: linux/ppc64le
|
||||
node_base: official
|
||||
- arch: riscv64
|
||||
platform: linux/riscv64
|
||||
node_base: unofficial
|
||||
env:
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
steps:
|
||||
@@ -616,11 +776,60 @@ jobs:
|
||||
popd > /dev/null
|
||||
rm -rf "$TMP"
|
||||
fi
|
||||
# The official node:${NODE_VERSION} Docker image only publishes amd64,
|
||||
# arm64 and ppc64le manifests — no s390x, no riscv64 — so
|
||||
# `docker run --platform linux/s390x node:24-slim` fails with "no matching
|
||||
# manifest". Instead build on an ubuntu:26.04 base (which publishes every
|
||||
# arch) and install the Node.js binary tarball inside the emulated
|
||||
# container: ppc64le/s390x from nodejs.org (official), riscv64 from
|
||||
# unofficial-builds.nodejs.org (nodejs.org ships no riscv64). Resolve the
|
||||
# exact latest ${NODE_VERSION}.x version on the host (fast, no emulation)
|
||||
# and pass it in along with which mirror to use.
|
||||
NODE_FULL=$(curl -fsSL https://nodejs.org/dist/index.json \
|
||||
| python3 -c "import json,sys; print(next(r['version'] for r in json.load(sys.stdin) if r['version'].startswith('v${NODE_VERSION}.')))")
|
||||
echo "Using Node ${NODE_FULL} (${{ matrix.node_base }}) for ${{ matrix.arch }}"
|
||||
docker run --rm --platform ${{ matrix.platform }} \
|
||||
-e NODE_FULL="$NODE_FULL" -e ARCH="${{ matrix.arch }}" \
|
||||
-e NODE_BASE="${{ matrix.node_base }}" \
|
||||
-v "$(pwd)/bundle:/bundle" \
|
||||
-w /bundle/programs/server \
|
||||
node:${NODE_VERSION}-slim \
|
||||
sh -c "apt-get update -qq && apt-get install -y -q build-essential python3 && npm install"
|
||||
ubuntu:26.04 \
|
||||
bash -c '
|
||||
set -eux
|
||||
apt-get update -qq
|
||||
apt-get install -y -q build-essential python3 curl xz-utils ca-certificates
|
||||
if [ "$NODE_BASE" = unofficial ]; then
|
||||
NODE_URL="https://unofficial-builds.nodejs.org/download/release/${NODE_FULL}/node-${NODE_FULL}-linux-${ARCH}.tar.xz"
|
||||
else
|
||||
NODE_URL="https://nodejs.org/dist/${NODE_FULL}/node-${NODE_FULL}-linux-${ARCH}.tar.xz"
|
||||
fi
|
||||
curl -fsSL -o /tmp/node.tar.xz "$NODE_URL"
|
||||
mkdir -p /opt/node
|
||||
tar -xJf /tmp/node.tar.xz -C /opt/node --strip-components=1
|
||||
export PATH=/opt/node/bin:$PATH
|
||||
node --version
|
||||
npm install
|
||||
# Bundle the target-arch Node.js binary for the self-contained
|
||||
# launcher. /bundle is the host bundle/ dir (see -v mount above).
|
||||
cp /opt/node/bin/node /bundle/node
|
||||
chmod +x /bundle/node
|
||||
'
|
||||
|
||||
- name: Download ferretdb.zip
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: ferretdb-zip
|
||||
|
||||
- name: Embed FerretDB (${{ matrix.arch }}) into the bundle
|
||||
# These arches have no MongoDB server, so FerretDB is their default DB.
|
||||
# The launcher (start-wekan.sh) is inherited from the amd64 base
|
||||
# bundle; swap in this arch's binary from ferretdb.zip. A marker file makes
|
||||
# FerretDB the default backend at runtime (see the launcher + Docker entrypoint).
|
||||
run: |
|
||||
unzip -o -j ferretdb.zip "ferretdb/${{ matrix.arch }}/ferretdb-${{ matrix.arch }}" -d bundle
|
||||
mv bundle/ferretdb-${{ matrix.arch }} bundle/ferretdb
|
||||
chmod +x bundle/ferretdb
|
||||
touch bundle/.ferretdb-default
|
||||
|
||||
- name: Create ${{ matrix.arch }} zip
|
||||
run: zip -r wekan-${VERSION}-${{ matrix.arch }}.zip bundle
|
||||
@@ -643,9 +852,11 @@ jobs:
|
||||
# Mirrors releases/docker-build.sh step 3 (multi-arch buildx --push).
|
||||
# Needs `release` because the Dockerfile downloads wekan-<version>-<arch>.zip
|
||||
# from the GitHub Release; building before those assets exist 404s and (without
|
||||
# set -e in the Dockerfile) shipped a broken image missing /build/main.js.
|
||||
# set -e in the Dockerfile) shipped a broken image missing /build/main.js. Also
|
||||
# needs `build-extra-arches` because the image now covers ppc64le/s390x/riscv64,
|
||||
# whose zips (with FerretDB baked in) are attached by that job after `release`.
|
||||
docker:
|
||||
needs: [prepare, release]
|
||||
needs: [prepare, release, build-extra-arches]
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
@@ -683,14 +894,14 @@ jobs:
|
||||
# Release for each built platform. Even though this job needs `release`
|
||||
# (so the assets are uploaded), GitHub's releases/download/<tag>/<asset>
|
||||
# URL can briefly 404 right after upload (CDN/propagation lag), which used
|
||||
# to fail the multi-arch build mid-way. Poll here until both the amd64 and
|
||||
# arm64 bundles return 200, so the (expensive, emulated) buildx run only
|
||||
# starts once the assets are actually fetchable. The Dockerfile also
|
||||
# to fail the multi-arch build mid-way. Poll here until every arch bundle
|
||||
# baked into the image returns 200, so the (expensive, emulated) buildx run
|
||||
# only starts once the assets are actually fetchable. The Dockerfile also
|
||||
# retries per-arch as a second layer of safety.
|
||||
run: |
|
||||
set -euo pipefail
|
||||
base="https://github.com/wekan/wekan/releases/download/v${VERSION}"
|
||||
for arch in amd64 arm64; do
|
||||
for arch in amd64 arm64 ppc64le s390x riscv64; do
|
||||
url="${base}/wekan-${VERSION}-${arch}.zip"
|
||||
echo "Waiting for ${url} ..."
|
||||
ok=0
|
||||
@@ -767,7 +978,7 @@ jobs:
|
||||
# report in the Admin Panel — a stale, different version.
|
||||
run: |
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--platform linux/amd64,linux/arm64,linux/ppc64le,linux/s390x,linux/riscv64 \
|
||||
--build-arg VERSION=${VERSION} \
|
||||
-t wekanteam/wekan:v${VERSION} \
|
||||
-t wekanteam/wekan:latest \
|
||||
@@ -796,7 +1007,20 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install snapcraft
|
||||
run: sudo snap install snapcraft --classic
|
||||
# The Snap Store intermittently rejects the install with
|
||||
# 'error: cannot install "snapcraft": too many requests' (a 429-style
|
||||
# rate limit) that instantly fails the whole snap job before any build.
|
||||
# Retry a few times so one transient store throttle does not fail the
|
||||
# release (same spirit as the remote-build retry below).
|
||||
run: |
|
||||
set -uo pipefail
|
||||
for i in 1 2 3 4 5; do
|
||||
sudo snap install snapcraft --classic && exit 0
|
||||
echo "snapcraft install attempt $i/5 failed (Snap Store throttle?); retrying in 30s..."
|
||||
sleep 30
|
||||
done
|
||||
echo "::error::snapcraft install failed after 5 attempts (Snap Store rate limit)."
|
||||
exit 1
|
||||
|
||||
- name: Provide Launchpad credentials for remote-build
|
||||
env:
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
name: Build Sandstorm .spk
|
||||
|
||||
# Manually triggered. Builds ONLY the Sandstorm .spk package (nothing else) and
|
||||
# uploads it as wekan-sandstorm-YYYY_MM_DD-HH_MM_SS.spk to the NEWEST WeKan GitHub
|
||||
# Release as an attachment, so you can download it, test whether the Sandstorm
|
||||
# build works, and see any errors — without running a full release.
|
||||
#
|
||||
# gh workflow run sandstorm.yml
|
||||
#
|
||||
# EXPERIMENTAL in CI (same caveats as the build-sandstorm job in release-all.yml):
|
||||
# * Sandstorm needs unprivileged user namespaces; GitHub's Ubuntu runners are VMs
|
||||
# that allow them, but this is the fragile part.
|
||||
# * meteor-spk SIGNS the .spk with the app private key from the Sandstorm keyring;
|
||||
# provide it as the SANDSTORM_KEYRING secret (base64 of ~/.sandstorm keyring).
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
NODE_VERSION: '24'
|
||||
|
||||
jobs:
|
||||
sandstorm:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 120
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
ref: main
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Install system dependencies and Meteor
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y p7zip-full wget curl build-essential g++ make python3 unzip
|
||||
if [ ! -f "$HOME/.meteor/meteor" ]; then curl https://install.meteor.com/ | sh; fi
|
||||
echo "$HOME/.meteor" >> "$GITHUB_PATH"
|
||||
|
||||
- name: Install meteor-spk
|
||||
run: |
|
||||
cd "$HOME"
|
||||
wget -q https://releases.wekan.team/dev/meteor-spk/projects.7z
|
||||
7z x projects.7z >/dev/null
|
||||
rm -f projects.7z
|
||||
echo "$HOME/projects/meteor-spk/meteor-spk-0.6.0" >> "$GITHUB_PATH"
|
||||
|
||||
- name: Install Sandstorm (dev, unattended)
|
||||
run: curl https://install.sandstorm.io | sudo bash -s -- -d
|
||||
|
||||
- name: Restore the Sandstorm signing keyring
|
||||
env:
|
||||
SANDSTORM_KEYRING: ${{ secrets.SANDSTORM_KEYRING }}
|
||||
run: |
|
||||
if [ -n "${SANDSTORM_KEYRING:-}" ]; then
|
||||
mkdir -p "$HOME/.sandstorm"
|
||||
printf '%s' "$SANDSTORM_KEYRING" | base64 -d > "$HOME/.sandstorm/sandstorm-keyring"
|
||||
chmod 600 "$HOME/.sandstorm/sandstorm-keyring"
|
||||
else
|
||||
echo "::warning::SANDSTORM_KEYRING secret not set; meteor-spk pack may fail to sign the app update."
|
||||
fi
|
||||
|
||||
- name: Install Meteor npm dependencies
|
||||
run: |
|
||||
(meteor update --npm 2>/dev/null || true) && meteor npm install
|
||||
|
||||
- name: Build the Sandstorm .spk
|
||||
id: build
|
||||
run: |
|
||||
set -euo pipefail
|
||||
STAMP="$(date +'%Y_%m_%d-%H_%M_%S')"
|
||||
SPK="wekan-sandstorm-${STAMP}.spk"
|
||||
meteor-spk pack "$SPK"
|
||||
ls -la "$SPK"
|
||||
echo "spk=$SPK" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: ${{ steps.build.outputs.spk }}
|
||||
path: ${{ steps.build.outputs.spk }}
|
||||
retention-days: 7
|
||||
|
||||
- name: Upload the .spk to the newest WeKan release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GH_REPO: ${{ github.repository }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
TAG="$(gh release list --limit 1 --json tagName --jq '.[0].tagName' 2>/dev/null || true)"
|
||||
if [ -z "${TAG:-}" ] || [ "$TAG" = "null" ]; then
|
||||
echo "::error::No existing GitHub Release found to attach the .spk to."
|
||||
exit 1
|
||||
fi
|
||||
echo "Uploading ${{ steps.build.outputs.spk }} to the newest release: $TAG"
|
||||
gh release upload "$TAG" "${{ steps.build.outputs.spk }}" --clobber
|
||||
+1212
-144
File diff suppressed because it is too large.
Load diff
+1
-1
@@ -2,10 +2,10 @@
|
||||
|
||||
For all code at WeKan GitHub Organization https://github.com/wekan
|
||||
|
||||
- For pull requests, mention only those participants that are **human**.
|
||||
- All code in pull requests need to have permission already to add it to WeKan with MIT license, and will become MIT license.
|
||||
- For any dependencies, permissive licenses like https://copyfree.org are preferred
|
||||
- For anything currently that is non-permissive (like GPL, AGPL, SSPL), those will be replaced with permissive-licensed alternatives
|
||||
- Any intelligence allowed: Human, AI or any other. No discrimination, please have world peace. Mentioning participants optional.
|
||||
- All code is checked and validated by xet7 and many different security AI. If code is real working fix or feature, it usually will be merged.
|
||||
- All known vulnerabilities have been fixed at newest WeKan, please upgrade https://wekan.fi/upgrade/
|
||||
- There is no known backdoors. If WeKan is installed by someone else, first registered user is Admin and has Admin Panel access.
|
||||
|
||||
+34
-10
@@ -7,7 +7,7 @@ LABEL org.opencontainers.image.source="https://github.com/wekan/wekan"
|
||||
# TARGETARCH and TARGETVARIANT are automatically provided by Docker Buildx
|
||||
ARG TARGETARCH
|
||||
ARG TARGETVARIANT
|
||||
ARG VERSION=9.74
|
||||
ARG VERSION=9.84
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
ENV BUILD_DEPS="apt-utils gnupg wget bzip2 g++ curl libarchive-tools build-essential git ca-certificates python3 unzip"
|
||||
@@ -185,24 +185,35 @@ apt-get update --assume-yes
|
||||
apt-get upgrade --assume-yes
|
||||
apt-get install --assume-yes --no-install-recommends ${BUILD_DEPS}
|
||||
|
||||
# Multi-arch mapping: Docker TARGETARCH -> Node.js arch name + WeKan bundle name
|
||||
# arm/v7 uses TARGETARCH=arm; armhf has no MongoDB Community -> uses FerretDB
|
||||
# Multi-arch mapping: Docker TARGETARCH -> Node.js arch name + WeKan bundle name.
|
||||
# amd64/arm64 have MongoDB Community; ppc64le/s390x/riscv64 have no MongoDB server
|
||||
# and ship FerretDB v1 instead (the ferretdb binary is baked into their .zip and
|
||||
# started by wekan-entrypoint.sh). riscv64's Node.js 24 comes from
|
||||
# unofficial-builds.nodejs.org (nodejs.org ships no riscv64). armv7l is excluded:
|
||||
# there is no Node.js 24 build for it anywhere.
|
||||
NODE_BASE="official"
|
||||
case "${TARGETARCH}" in
|
||||
"amd64") NODE_ARCH="x64" WEKAN_ARCH="amd64" ;;
|
||||
"arm64") NODE_ARCH="arm64" WEKAN_ARCH="arm64" ;;
|
||||
"arm") NODE_ARCH="armv7l" WEKAN_ARCH="armhf" ;;
|
||||
"ppc64le") NODE_ARCH="ppc64le" WEKAN_ARCH="ppc64le" ;;
|
||||
"s390x") NODE_ARCH="s390x" WEKAN_ARCH="s390x" ;;
|
||||
"riscv64") NODE_ARCH="riscv64" WEKAN_ARCH="riscv64" NODE_BASE="unofficial" ;;
|
||||
*) echo "Unsupported architecture: ${TARGETARCH}"; exit 1 ;;
|
||||
esac
|
||||
|
||||
# Node.js Installation (official nodejs.org builds for Node 22)
|
||||
# Node.js installation. Official nodejs.org builds for amd64/arm64/ppc64le/s390x;
|
||||
# unofficial-builds.nodejs.org for riscv64 (nodejs.org ships no riscv64 binary).
|
||||
cd /tmp
|
||||
wget "https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-linux-${NODE_ARCH}.tar.gz"
|
||||
wget "https://nodejs.org/dist/${NODE_VERSION}/SHASUMS256.txt.asc"
|
||||
grep "node-${NODE_VERSION}-linux-${NODE_ARCH}.tar.gz" SHASUMS256.txt.asc | shasum -a 256 -c -
|
||||
if [ "${NODE_BASE}" = "unofficial" ]; then
|
||||
NODE_DIST="https://unofficial-builds.nodejs.org/download/release/${NODE_VERSION}"
|
||||
else
|
||||
NODE_DIST="https://nodejs.org/dist/${NODE_VERSION}"
|
||||
fi
|
||||
wget "${NODE_DIST}/node-${NODE_VERSION}-linux-${NODE_ARCH}.tar.gz"
|
||||
wget "${NODE_DIST}/SHASUMS256.txt"
|
||||
grep " node-${NODE_VERSION}-linux-${NODE_ARCH}.tar.gz\$" SHASUMS256.txt | shasum -a 256 -c -
|
||||
tar xzf "node-${NODE_VERSION}-linux-${NODE_ARCH}.tar.gz" -C /usr/local --strip-components=1 --no-same-owner
|
||||
rm -f "node-${NODE_VERSION}-linux-${NODE_ARCH}.tar.gz" SHASUMS256.txt.asc
|
||||
rm -f "node-${NODE_VERSION}-linux-${NODE_ARCH}.tar.gz" SHASUMS256.txt
|
||||
ln -s "/usr/local/bin/node" "/usr/local/bin/nodejs"
|
||||
|
||||
# NPM configuration
|
||||
@@ -229,6 +240,12 @@ rm "wekan-${VERSION}-${WEKAN_ARCH}.zip"
|
||||
npm install --prefix ./bundle/programs/server
|
||||
mv /home/wekan/app/bundle /build
|
||||
|
||||
# The .zip bundle now ships a self-contained launcher + its own Node.js for the
|
||||
# offline downloads; the Docker image installs its own Node and uses
|
||||
# wekan-entrypoint.sh, so drop the redundant bundled node + launchers (keeps
|
||||
# /build/ferretdb, which the entrypoint uses). Saves ~80 MB per arch.
|
||||
rm -f /build/node /build/start-wekan.sh /build/start-wekan.bat
|
||||
|
||||
# Restore original tar
|
||||
mv $(which tar)~ $(which tar)
|
||||
|
||||
@@ -247,10 +264,17 @@ mkdir -p /data
|
||||
chown wekan:wekan --recursive /data
|
||||
EOR
|
||||
|
||||
# Database-backend selector entrypoint. Every arch's bundle now ships a FerretDB
|
||||
# binary (baked into the .zip and moved to /build/ferretdb above); this script
|
||||
# starts FerretDB v1 (SQLite) or leaves the DB external based on WEKAN_DB and the
|
||||
# /build/.ferretdb-default marker (present only on MongoDB-less arches). See
|
||||
# releases/ferretdb/wekan-entrypoint.sh.
|
||||
COPY --chmod=755 releases/ferretdb/wekan-entrypoint.sh /build/wekan-entrypoint.sh
|
||||
|
||||
USER wekan
|
||||
ENV PORT=8080
|
||||
EXPOSE $PORT
|
||||
STOPSIGNAL SIGKILL
|
||||
WORKDIR /build
|
||||
|
||||
CMD ["bash", "-c", "ulimit -s 65500; exec node main.js"]
|
||||
CMD ["bash", "/build/wekan-entrypoint.sh"]
|
||||
@@ -103,7 +103,7 @@ By working directly with WeKan ® you get the benefit of active maintenance and
|
||||
|
||||
## Getting Started with Development
|
||||
|
||||
The default branch uses Meteor 3.5-rc.2 with Node.js 24.x.
|
||||
The main branch uses Meteor 3.5 with Node.js 24.x.
|
||||
See [CHANGELOG.md](https://github.com/wekan/wekan/blob/main/CHANGELOG.md) for the latest runtime updates.
|
||||
|
||||
To contribute, [create a fork](https://github.com/wekan/wekan/blob/main/docs/DeveloperDocs/Build-and-Create-Pull-Request.md#2-create-fork-of-httpsgithubcomwekanwekan-at-github-web-page) and run `./rebuild-wekan.sh` (or `./rebuild-wekan.bat` on Windows) as detailed [here](https://github.com/wekan/wekan/blob/main/docs/DeveloperDocs/Build-and-Create-Pull-Request.md#3-select-option-1-to-install-dependencies-and-then-enter). Once you're ready, please test your code and [submit a pull request (PR)](https://github.com/wekan/wekan/blob/main/docs/DeveloperDocs/Build-and-Create-Pull-Request.md#7-test).
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
appId: wekan-public/apps/77b94f60-dec9-0136-304e-16ff53095928
|
||||
appVersion: "v9.74.0"
|
||||
appVersion: "v9.84.0"
|
||||
files:
|
||||
userUploads:
|
||||
- README.md
|
||||
|
||||
@@ -37,6 +37,8 @@ If *nix: chmod +x api.py => ./api.py users
|
||||
python3 api.py lists BOARDID # Lists of BOARDID
|
||||
python3 api.py list BOARDID LISTID # Info of LISTID
|
||||
python3 api.py createlist BOARDID LISTTITLE # Create list
|
||||
python3 api.py editlist BOARDID LISTID NEWLISTTITLE # Edit list title
|
||||
python3 api.py editlistcolor BOARDID LISTID COLOR (Color available: `white`, `green`, `yellow`, `orange`, `red`, `purple`, `blue`, `sky`, `lime`, `pink`, `black`, `silver`, `peachpuff`, `crimson`, `plum`, `darkgreen`, `slateblue`, `magenta`, `gold`, `navy`, `gray`, `saddlebrown`, `paleturquoise`, `mistyrose`, `indigo`) # Edit list color
|
||||
python3 api.py addcard AUTHORID BOARDID SWIMLANEID LISTID CARDTITLE CARDDESCRIPTION
|
||||
python3 api.py editcard BOARDID LISTID CARDID NEWCARDTITLE NEWCARDDESCRIPTION
|
||||
python3 api.py getcard BOARDID LISTID CARDID # Get card info
|
||||
@@ -432,6 +434,49 @@ if arguments == 5:
|
||||
|
||||
if arguments >= 4:
|
||||
|
||||
if sys.argv[1] == 'editlist':
|
||||
|
||||
# ------- EDIT LIST START -----------
|
||||
boardid = sys.argv[2]
|
||||
listid = sys.argv[3]
|
||||
newlisttitle = sys.argv[4]
|
||||
edlist = wekanurl + apiboards + boardid + s + l + s + listid
|
||||
print(edlist)
|
||||
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
|
||||
put_data = {'title': '{}'.format(newlisttitle)}
|
||||
body = requests.put(edlist, data=put_data, headers=headers)
|
||||
print("=== EDIT LIST ===\n")
|
||||
body = requests.get(edlist, headers=headers)
|
||||
data2 = body.text.replace('}', "}\n")
|
||||
print(data2)
|
||||
# ------- EDIT LIST END -----------
|
||||
|
||||
if sys.argv[1] == 'editlistcolor':
|
||||
|
||||
# ------- EDIT LIST COLOR START -----------
|
||||
boardid = sys.argv[2]
|
||||
listid = sys.argv[3]
|
||||
newcolor = sys.argv[4]
|
||||
|
||||
valid_colors = ['white', 'green', 'yellow', 'orange', 'red', 'purple', 'blue', 'sky', 'lime', 'pink', 'black',
|
||||
'silver', 'peachpuff', 'crimson', 'plum', 'darkgreen', 'slateblue', 'magenta', 'gold', 'navy',
|
||||
'gray', 'saddlebrown', 'paleturquoise', 'mistyrose', 'indigo']
|
||||
|
||||
if newcolor not in valid_colors:
|
||||
print("Invalid color. Choose a color from the list.")
|
||||
sys.exit(1)
|
||||
|
||||
edlist = wekanurl + apiboards + boardid + s + l + s + listid
|
||||
print(edlist)
|
||||
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
|
||||
put_data = {'color': '{}'.format(newcolor)}
|
||||
body = requests.put(edlist, data=put_data, headers=headers)
|
||||
print("=== EDIT LIST COLOR ===\n")
|
||||
body = requests.get(edlist, headers=headers)
|
||||
data2 = body.text.replace('}', "}\n")
|
||||
print(data2)
|
||||
# ------- EDIT LIST COLOR END -----------
|
||||
|
||||
if sys.argv[1] == 'newuser':
|
||||
|
||||
# ------- CREATE NEW USER START -----------
|
||||
|
||||
+10
-4
@@ -1,7 +1,13 @@
|
||||
// Fix dynamic-import chunk loading when Wekan runs under a sub-path (ROOT_URL includes a pathname,
|
||||
// e.g. https://example.com/wekan). Rspack uses __webpack_public_path__ to prefix chunk URLs; by
|
||||
// default it is set to /build-chunks/ (no context path). We update it here, at module evaluation
|
||||
// time, before any import() expression runs (those are deferred to Meteor.startup callbacks).
|
||||
// e.g. https://example.com/wekan). Rspack's runtime builds every chunk URL as
|
||||
// __webpack_public_path__ + chunkName, and the chunk name ALREADY includes the "build-chunks/"
|
||||
// prefix (its runtime does `u = e => "build-chunks/" + e + "." + hash + ".js"`). The default
|
||||
// public path is "/", so at the site root chunks correctly resolve to "/build-chunks/<chunk>".
|
||||
// Under a sub-path we must prefix that sub-path — but must NOT re-add "build-chunks/" ourselves,
|
||||
// or the URL becomes "/wekan/build-chunks/build-chunks/<chunk>" and 404s (issue #6445, which broke
|
||||
// the lazy-loaded language chunks). So set the public path to "<sub-path>/" and let rspack append
|
||||
// "build-chunks/" itself. Done here at module evaluation time, before any import() expression runs
|
||||
// (those are deferred to Meteor.startup callbacks).
|
||||
/* global __webpack_public_path__:writable */
|
||||
try {
|
||||
const _cfg = typeof window !== 'undefined' && window.__meteor_runtime_config__;
|
||||
@@ -9,7 +15,7 @@ try {
|
||||
if (_rootUrl) {
|
||||
const _rootPath = new URL(_rootUrl).pathname.replace(/\/+$/, '');
|
||||
if (_rootPath && _rootPath !== '/') {
|
||||
__webpack_public_path__ = _rootPath + '/build-chunks/';
|
||||
__webpack_public_path__ = _rootPath + '/';
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
@@ -15,6 +15,20 @@
|
||||
.reactions-popup .add-comment-reaction:hover {
|
||||
background-color: #b0c4de;
|
||||
}
|
||||
/* Eye / eye-slash icon toggle for showing/hiding activities. Open eye
|
||||
(.eye-icon) = activities shown; crossed eye (.eye-slash-icon) = hidden.
|
||||
Matches the password-visibility toggle used on the login/register pages. */
|
||||
.activities-eye-toggle {
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.4em;
|
||||
text-decoration: none;
|
||||
}
|
||||
.activities-eye-toggle .eye-icon,
|
||||
.activities-eye-toggle .eye-slash-icon {
|
||||
line-height: 1;
|
||||
}
|
||||
.activities {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
@@ -106,10 +106,10 @@ template(name="activity")
|
||||
|
||||
//- comment activity ----------------------------------------------------
|
||||
if($eq activity.activityType 'deleteComment')
|
||||
| {{{_ 'activity-deleteComment' activity.commentId}}}.
|
||||
| {{{_ 'activity-deleteComment' (sanitize activity.commentDisplayText)}}}.
|
||||
|
||||
if($eq activity.activityType 'editComment')
|
||||
| {{{_ 'activity-editComment' activity.commentId}}}.
|
||||
| {{{_ 'activity-editComment' (sanitize activity.commentDisplayText)}}}.
|
||||
|
||||
if($eq activity.activityType 'addComment')
|
||||
| {{{_ 'activity-on' cardLink}}}
|
||||
@@ -133,6 +133,9 @@ template(name="activity")
|
||||
if($eq activity.activityType 'a-changedTitle')
|
||||
| {{{_ 'activity-changedTitle' (sanitize activity.value) cardLink}}}.
|
||||
|
||||
if($eq activity.activityType 'a-changedDescription')
|
||||
| {{{_ 'activity-changedDescription' cardLink}}}.
|
||||
|
||||
//- customField activity ------------------------------------------------
|
||||
if($eq activity.activityType 'createCustomField')
|
||||
| {{_ 'activity-customfield-created' customField}}.
|
||||
|
||||
@@ -6,11 +6,13 @@ import '/client/lib/dragscrollTouch';
|
||||
import { boardConverter } from '/client/lib/boardConverter';
|
||||
import { formatDateByUserPreference } from '/imports/lib/dateUtils';
|
||||
import { toFullCalendarFirstDay } from '/client/lib/calendarFirstDay';
|
||||
import { weekNumberByFirstDay } from '/models/lib/weekStart';
|
||||
import Swimlanes from '/models/swimlanes';
|
||||
import Lists from '/models/lists';
|
||||
import TableVisibilityModeSettings from '/models/tableVisibilityModeSettings';
|
||||
import { EscapeActions } from '/client/lib/escapeActions';
|
||||
import { Utils } from '/client/lib/utils';
|
||||
import { Filter } from '/client/lib/filter';
|
||||
|
||||
// SubsManager removed for Meteor 3 migration
|
||||
const { calculateIndex } = Utils;
|
||||
@@ -578,7 +580,15 @@ Template.boardBody.onRendered(function () {
|
||||
});
|
||||
|
||||
dragscroll.reset();
|
||||
Utils.setBackgroundImage();
|
||||
// #4978: apply the board background inside a reactive autorun instead of a
|
||||
// one-shot call. Switching directly between boards (e.g. the favorites bar)
|
||||
// reuses this template instance and never re-fires onRendered, so the
|
||||
// background used to stay on the previously shown board. Reading the current
|
||||
// board inside setBackgroundImage() tracks both board switches and background
|
||||
// edits; it performs no DB writes, so there is no autorun write loop.
|
||||
this.autorun(() => {
|
||||
Utils.setBackgroundImage();
|
||||
});
|
||||
});
|
||||
|
||||
Template.boardBody.onDestroyed(function () {
|
||||
@@ -929,6 +939,11 @@ Template.calendarView.onRendered(function () {
|
||||
document.documentElement.dir = TAPi18n.getLanguageDirection();
|
||||
|
||||
this.autorun(function () {
|
||||
// #5656: refetch calendar events whenever the active board Filter changes
|
||||
// so the Calendar view stays in sync with the other views. Reading the
|
||||
// Filter's reactive state here establishes the dependency; the events()
|
||||
// callback below applies the actual selector.
|
||||
Filter.isActive();
|
||||
const calendarEl = document.getElementById('calendar-view');
|
||||
if (calendarEl && calendarEl._wekanCalendar) {
|
||||
calendarEl._wekanCalendar.refetchEvents();
|
||||
@@ -959,6 +974,12 @@ Template.calendarView.helpers({
|
||||
editable: true,
|
||||
selectable: true,
|
||||
weekNumbers: true,
|
||||
// #4946: FullCalendar's default weekNumberCalculation is locale-based
|
||||
// (Sunday-anchored for many locales), so the week-number column ignored
|
||||
// the user's start-day-of-week setting even though `firstDay` moved the
|
||||
// grid. Compute the number from the SAME firstDay so the label matches the
|
||||
// way the week is laid out.
|
||||
weekNumberCalculation: date => weekNumberByFirstDay(date, firstDay),
|
||||
// Use non-localized AM/PM time format to avoid confusing notations like 上/下/中
|
||||
// Use full 'am'/'pm' instead of single-letter 'a'/'p' for clarity
|
||||
eventTimeFormat: {
|
||||
@@ -1002,6 +1023,12 @@ Template.calendarView.helpers({
|
||||
isRTL: TAPi18n.isRTL(),
|
||||
events(fetchInfo, callback) {
|
||||
const currentBoard = Utils.getCurrentBoard();
|
||||
// #5656: apply the active board Filter (member / assignee / due-date /
|
||||
// label / custom-field) so the Calendar view shows the same scoped set
|
||||
// of cards as the Board / Lists / Swimlanes views.
|
||||
const filterSelector = Filter.isActive()
|
||||
? Filter._getMongoSelector()
|
||||
: undefined;
|
||||
const events = [];
|
||||
const pushEvent = function (card, title, start, end, extraCls) {
|
||||
start = start || card.startAt;
|
||||
@@ -1026,12 +1053,12 @@ Template.calendarView.helpers({
|
||||
});
|
||||
};
|
||||
currentBoard
|
||||
.cardsInInterval(fetchInfo.start, fetchInfo.end)
|
||||
.cardsInInterval(fetchInfo.start, fetchInfo.end, filterSelector)
|
||||
.forEach(function (card) {
|
||||
pushEvent(card);
|
||||
});
|
||||
currentBoard
|
||||
.cardsDueInBetween(fetchInfo.start, fetchInfo.end)
|
||||
.cardsDueInBetween(fetchInfo.start, fetchInfo.end, filterSelector)
|
||||
.forEach(function (card) {
|
||||
pushEvent(
|
||||
card,
|
||||
|
||||
@@ -1078,6 +1078,13 @@ body.grey-icons-enabled .checkmark-no-grey {
|
||||
margin: 8px; /* Space between board items */
|
||||
}
|
||||
|
||||
/* #6439: highlight the board a dragged board will be dropped before, when
|
||||
reordering in the Custom (drag order) sort mode. */
|
||||
.js-board.board-reorder-over {
|
||||
outline: 2px dashed #3498db;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* Reset background for add-board button */
|
||||
.js-add-board {
|
||||
background-color: transparent !important;
|
||||
|
||||
@@ -6,6 +6,10 @@ import TableVisibilityModeSettings from '/models/tableVisibilityModeSettings';
|
||||
import { BoardMultiSelection } from '/client/lib/boardMultiSelection';
|
||||
import { EscapeActions } from '/client/lib/escapeActions';
|
||||
import { Utils } from '/client/lib/utils';
|
||||
import {
|
||||
isDragReorderEnabled,
|
||||
computeReorderedSortIndex,
|
||||
} from '/models/lib/boardSortReorder';
|
||||
|
||||
// SubsManager removed for Meteor 3 migration
|
||||
|
||||
@@ -59,6 +63,15 @@ const DEFAULT_WORKSPACE_ICON = '📁';
|
||||
// Matches the Admin Panel > People page size.
|
||||
const BOARDS_PER_PAGE = 25;
|
||||
|
||||
// #6439: the effective All Boards sort mode for the current user, defaulting to
|
||||
// 'custom' (manual drag order) when unknown. Used to gate board drag-reordering.
|
||||
function currentAllBoardsSortBy() {
|
||||
const cu = ReactiveCache.getCurrentUser();
|
||||
return cu && typeof cu.getAllBoardsSortBy === 'function'
|
||||
? cu.getAllBoardsSortBy()
|
||||
: 'custom';
|
||||
}
|
||||
|
||||
function getCurrentWorkspacesTree() {
|
||||
const currentUser = ReactiveCache.getCurrentUser();
|
||||
const tree =
|
||||
@@ -973,6 +986,55 @@ Template.boardList.events({
|
||||
document.querySelectorAll('.workspace-node.board-drag-hint, .js-select-menu.board-drag-hint').forEach((el) => {
|
||||
el.classList.remove('board-drag-hint');
|
||||
});
|
||||
document.querySelectorAll('.js-board.board-reorder-over').forEach((el) => {
|
||||
el.classList.remove('board-reorder-over');
|
||||
});
|
||||
},
|
||||
// #6439: reorder boards on the All Boards page by dropping one board onto
|
||||
// another. Only active in the "custom" (manual drag order) sort mode — in the
|
||||
// sorted modes the order comes from the title, so a board is not a valid drop
|
||||
// target (which is why, before this, the browser showed the not-allowed cursor
|
||||
// and nothing persisted). The reorder decision/index math lives in the pure,
|
||||
// unit-tested models/lib/boardSortReorder.js.
|
||||
'dragover .js-board'(evt, tpl) {
|
||||
if (!isDragReorderEnabled(currentAllBoardsSortBy())) return;
|
||||
// A multi-board drag is a share/assign gesture, not a reorder.
|
||||
const dt = evt.originalEvent.dataTransfer;
|
||||
if (dt && dt.getData('application/x-board-multi') === 'true') return;
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
if (dt) dt.dropEffect = 'move';
|
||||
evt.currentTarget.classList.add('board-reorder-over');
|
||||
},
|
||||
'dragleave .js-board'(evt) {
|
||||
evt.currentTarget.classList.remove('board-reorder-over');
|
||||
},
|
||||
'drop .js-board'(evt, tpl) {
|
||||
if (!isDragReorderEnabled(currentAllBoardsSortBy())) return;
|
||||
const dt = evt.originalEvent.dataTransfer;
|
||||
if (dt && dt.getData('application/x-board-multi') === 'true') return;
|
||||
const draggedId = dt ? dt.getData('text/plain') : '';
|
||||
if (!draggedId) return;
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
evt.currentTarget.classList.remove('board-reorder-over');
|
||||
const targetId = this._id;
|
||||
// #6442: current display order of the board grid. Read each board's _id from
|
||||
// its Blaze data context (the same source dragstart uses via `this._id`) —
|
||||
// NOT el.classList[0]. On `li.js-board(class="{{_id}} …")` Jade emits the
|
||||
// literal `js-board` class FIRST, so classList[0] is the string "js-board"
|
||||
// for every board; the old code therefore produced ['js-board','js-board',…],
|
||||
// computeReorderedSortIndex could not find the dragged/target ids among them,
|
||||
// returned null, and the drop silently did nothing (board snapped back).
|
||||
const orderedIds = Array.from(tpl.findAll('.js-board'))
|
||||
.map((el) => Blaze.getData(el)?._id)
|
||||
.filter(Boolean);
|
||||
const mapping = computeReorderedSortIndex(orderedIds, draggedId, targetId);
|
||||
if (!mapping) return;
|
||||
const currentUser = ReactiveCache.getCurrentUser();
|
||||
if (currentUser && typeof currentUser.setBoardSortIndexes === 'function') {
|
||||
currentUser.setBoardSortIndexes(mapping);
|
||||
}
|
||||
},
|
||||
'click .js-clone-board'(evt) {
|
||||
if (confirm(TAPi18n.__('duplicate-board-confirm'))) {
|
||||
|
||||
@@ -66,7 +66,7 @@ template(name="tableView")
|
||||
td.table-view-cell-labels
|
||||
.div
|
||||
each label in row.labels
|
||||
span.card-label(class="card-label-{{label.color}}" title="{{label.name}}")
|
||||
span.card-label(class="{{labelColorClass label.color}}" style="{{labelColorStyle label.color}}" title="{{label.name}}")
|
||||
+viewer
|
||||
= label.name
|
||||
if showReceivedColumn
|
||||
|
||||
@@ -6,7 +6,7 @@ template(name="cardDetails")
|
||||
+attachmentViewer
|
||||
|
||||
section.card-details.js-card-details.nodragscroll(class='{{#if cardMaximized}}card-details-maximized{{/if}}' class='{{#if isPopup}}card-details-popup{{/if}}' class='{{#unless isVerticalScrollbars}}no-scrollbars{{/unless}}' class='{{#if cardCollapsed}}card-details-collapsed{{/if}}'): .card-details-canvas
|
||||
.card-details-header(class='{{#if colorClass}}card-details-{{colorClass}}{{/if}}')
|
||||
.card-details-header(class='{{#if colorClass}}card-details-{{colorClass}}{{/if}}' style='{{colorStyle}}')
|
||||
+inlinedForm(classNames="js-card-details-title")
|
||||
+editCardTitleForm
|
||||
else
|
||||
@@ -127,7 +127,7 @@ template(name="cardDetails")
|
||||
| {{_ 'labels'}}
|
||||
a(class="{{#if canModifyCard}}js-add-labels{{else}}is-disabled{{/if}}" title="{{_ 'card-labels-title'}}")
|
||||
each labels
|
||||
span.card-label(class="card-label-{{color}}" title=name)
|
||||
span.card-label(class="{{labelColorClass color}}" style="{{labelColorStyle color}}" title=name)
|
||||
+viewer
|
||||
= name
|
||||
if canModifyCard
|
||||
@@ -741,12 +741,12 @@ template(name="cardDetails")
|
||||
i.fa.fa-history
|
||||
| {{ _ 'activities'}}
|
||||
if currentUser.isBoardMember
|
||||
.material-toggle-switch(title="{{_ 'show-activities'}}")
|
||||
if showActivities
|
||||
input.toggle-switch(type="checkbox" id="toggleShowActivitiesCard" checked="checked")
|
||||
else
|
||||
input.toggle-switch(type="checkbox" id="toggleShowActivitiesCard")
|
||||
label.toggle-label(for="toggleShowActivitiesCard")
|
||||
if showActivities
|
||||
a.activities-eye-toggle.js-toggle-show-activities-card(title="{{_ 'hide-activities'}}" aria-label="{{_ 'hide-activities'}}")
|
||||
i.fa.fa-eye.eye-icon
|
||||
else
|
||||
a.activities-eye-toggle.js-toggle-show-activities-card(title="{{_ 'show-activities'}}" aria-label="{{_ 'show-activities'}}")
|
||||
i.fa.fa-eye-slash.eye-slash-icon
|
||||
|
||||
if currentUser.isBoardAdmin
|
||||
if isLoaded.get
|
||||
@@ -1199,6 +1199,10 @@ template(name="setCardColorPopup")
|
||||
span.card-label.palette-color.js-palette-color(class="card-details-{{color}}")
|
||||
if(isSelected color)
|
||||
i.fa.fa-check
|
||||
// #5514: native color wheel for picking a custom RGB / hex color
|
||||
.color-wheel-wrap
|
||||
label(for="cardColorWheel") {{_ 'custom-color'}}
|
||||
input.js-card-color-wheel#cardColorWheel(type="color" value=currentColorHex title="{{_ 'custom-color'}}")
|
||||
button.primary.confirm.js-submit {{_ 'save'}}
|
||||
button.js-remove-color.negate.wide.right {{_ 'unset-color'}}
|
||||
|
||||
|
||||
@@ -50,7 +50,10 @@ import Users from '/models/users';
|
||||
import Lists from '/models/lists';
|
||||
import CardComments from '/models/cardComments';
|
||||
import { ALLOWED_COLORS } from '/config/const';
|
||||
import { isHexColor, toHex } from '/models/lib/contrastColor';
|
||||
import { uniqBy } from '/imports/lib/collectionHelpers';
|
||||
import { memberTargetBoardId } from '/models/lib/linkedCardMembers';
|
||||
import { isSubmitKey } from '/models/lib/editorSubmitKey';
|
||||
import { UserAvatar } from '../users/userAvatar';
|
||||
import { Filter } from '/client/lib/filter';
|
||||
import { BoardSwimlaneListCardDialog } from '/client/lib/dialogWithBoardSwimlaneListCard';
|
||||
@@ -1045,7 +1048,7 @@ Template.cardDetails.events({
|
||||
const card = Template.currentData();
|
||||
await card.toggleHideCheckedChecklistItems();
|
||||
},
|
||||
async 'change #toggleShowActivitiesCard'() {
|
||||
async 'click .js-toggle-show-activities-card'() {
|
||||
const card = Template.currentData();
|
||||
await card.toggleShowActivities();
|
||||
},
|
||||
@@ -1497,9 +1500,11 @@ Template.editCardTitleForm.events({
|
||||
Utils.showCopied(promise, $tooltip);
|
||||
},
|
||||
'keydown .js-edit-card-title'(event) {
|
||||
// If enter key was pressed, submit the data
|
||||
// Unless the shift key is also being pressed
|
||||
if (event.keyCode === 13 && !event.shiftKey) {
|
||||
// #4236: match the card description field — Ctrl/Cmd+Enter saves; plain
|
||||
// Enter (and Shift+Enter) insert a new line instead of submitting. The old
|
||||
// handler submitted on any Enter without Shift, so Enter could not add a
|
||||
// newline in the title.
|
||||
if (isSubmitKey(event)) {
|
||||
$('.js-submit-edit-card-title-form').click();
|
||||
}
|
||||
},
|
||||
@@ -1657,7 +1662,25 @@ Template.cardLocationsPopup.events({
|
||||
});
|
||||
|
||||
const filterMembers = (filterTerm) => {
|
||||
let currBoard = Utils.getCurrentBoard();
|
||||
// #5676: for a linked card the members live on the REAL card it points at,
|
||||
// which may be on another board (that is what getMembers()/toggleMember() act
|
||||
// on). Offer that board's members instead of the viewing board's, so the
|
||||
// picker is consistent with where membership is actually stored. Falls back to
|
||||
// the current board for normal cards or when the real card isn't loaded yet.
|
||||
const card = getCurrentCardFromContext();
|
||||
let currBoard = null;
|
||||
if (card) {
|
||||
const boardId = memberTargetBoardId(card, id => ReactiveCache.getCard(id));
|
||||
if (boardId) {
|
||||
currBoard = ReactiveCache.getBoard(boardId);
|
||||
}
|
||||
}
|
||||
if (!currBoard) {
|
||||
currBoard = Utils.getCurrentBoard();
|
||||
}
|
||||
if (!currBoard) {
|
||||
return [];
|
||||
}
|
||||
let members = currBoard.activeMembers();
|
||||
|
||||
if (filterTerm) {
|
||||
@@ -2013,6 +2036,10 @@ Template.setCardColorPopup.helpers({
|
||||
}
|
||||
return tpl.currentColor.get() === color;
|
||||
},
|
||||
// #5514: current color as a '#rrggbb' hex for the native color wheel <input>.
|
||||
currentColorHex() {
|
||||
return toHex(Template.instance().currentColor.get()) || '#0079bf';
|
||||
},
|
||||
});
|
||||
|
||||
Template.setCardColorPopup.events({
|
||||
@@ -2020,6 +2047,13 @@ Template.setCardColorPopup.events({
|
||||
const paletteData = Blaze.getData(event.currentTarget);
|
||||
tpl.currentColor.set(paletteData?.color);
|
||||
},
|
||||
// #5514: picking from the native color wheel stores a custom hex.
|
||||
'input .js-card-color-wheel'(event, tpl) {
|
||||
const value = event.currentTarget.value;
|
||||
if (isHexColor(value)) {
|
||||
tpl.currentColor.set(value);
|
||||
}
|
||||
},
|
||||
async 'click .js-submit'(event, tpl) {
|
||||
event.preventDefault();
|
||||
const card = Cards.findOne(getCardId());
|
||||
|
||||
@@ -1,3 +1,24 @@
|
||||
/* #5514: native color-wheel (custom RGB / hex) picker wrapper, shared by the
|
||||
label / list / swimlane / card color popups. */
|
||||
.color-wheel-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin: 6px 0 10px 0;
|
||||
}
|
||||
.color-wheel-wrap label {
|
||||
margin: 0;
|
||||
}
|
||||
.color-wheel-wrap input[type="color"] {
|
||||
width: 44px;
|
||||
height: 28px;
|
||||
padding: 0;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.card-label {
|
||||
border: 1px solid #000;
|
||||
border-radius: 4px;
|
||||
|
||||
@@ -4,9 +4,12 @@ template(name="formLabel")
|
||||
|
||||
label {{_ "select-color"}}
|
||||
.palette-colors: each labels
|
||||
span.card-label.palette-color.js-palette-color(class="card-label-{{color}}")
|
||||
span.card-label.palette-color.js-palette-color(class="{{labelColorClass color}}" style="{{labelColorStyle color}}")
|
||||
if(isSelected color)
|
||||
i.fa.fa-check
|
||||
.color-wheel-wrap
|
||||
label(for="labelColorWheel") {{_ 'custom-color'}}
|
||||
input.js-label-color-wheel#labelColorWheel(type="color" value=currentColorHex title="{{_ 'custom-color'}}")
|
||||
|
||||
template(name="createLabelPopup")
|
||||
form.create-label
|
||||
@@ -31,8 +34,8 @@ template(name="cardLabelsPopup")
|
||||
a.card-label-edit-button.fa.fa-pencil.js-edit-label
|
||||
if isTouchScreenOrShowDesktopDragHandles
|
||||
span.fa.label-handle(class="fa-arrows" title="{{_ 'dragLabel'}}")
|
||||
span.card-label.card-label-selectable.js-select-label.card-label-wrapper(class="card-label-{{color}}"
|
||||
class="{{# if isLabelSelected ../_id }}active{{/if}}")
|
||||
span.card-label.card-label-selectable.js-select-label.card-label-wrapper(class="{{labelColorClass color}}"
|
||||
class="{{# if isLabelSelected ../_id }}active{{/if}}" style="{{labelColorStyle color}}")
|
||||
+viewer
|
||||
= name
|
||||
if(isLabelSelected ../_id)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { ReactiveCache } from '/imports/reactiveCache';
|
||||
import { LABEL_COLORS } from '/models/metadata/colors';
|
||||
import { isHexColor, toHex } from '/models/lib/contrastColor';
|
||||
import { EscapeActions } from '/client/lib/escapeActions';
|
||||
import { Utils } from '/client/lib/utils';
|
||||
|
||||
@@ -28,6 +29,11 @@ Template.formLabel.helpers({
|
||||
isSelected(color) {
|
||||
return Template.instance().currentColor.get() === color;
|
||||
},
|
||||
// #5514: current color as a '#rrggbb' hex for the color-wheel <input>, which
|
||||
// only accepts hex. Named colors map to their palette hex; fall back to green.
|
||||
currentColorHex() {
|
||||
return toHex(Template.instance().currentColor.get()) || '#3cb500';
|
||||
},
|
||||
});
|
||||
|
||||
Template.formLabel.events({
|
||||
@@ -36,6 +42,13 @@ Template.formLabel.events({
|
||||
const selectedColor = paletteData?.color || Template.currentData()?.color || getFallbackLabelColor();
|
||||
tpl.currentColor.set(selectedColor);
|
||||
},
|
||||
// #5514: picking a color from the native color wheel stores a custom hex.
|
||||
'input .js-label-color-wheel'(event, tpl) {
|
||||
const value = event.currentTarget.value;
|
||||
if (isHexColor(value)) {
|
||||
tpl.currentColor.set(value);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Template.createLabelPopup.helpers({
|
||||
@@ -131,7 +144,16 @@ Template.createLabelPopup.events({
|
||||
? selectedColorIcon.closest('.js-palette-color')
|
||||
: null;
|
||||
const selectedColorData = selectedPaletteNode && Blaze.getData(selectedPaletteNode);
|
||||
const color = selectedColorData?.color || getFallbackLabelColor();
|
||||
// #5514: a named swatch (has the check icon) wins; otherwise fall back to
|
||||
// the custom color-wheel hex, then to the default palette color.
|
||||
let color = selectedColorData?.color;
|
||||
if (!color) {
|
||||
const wheel = templateInstance.find('.js-label-color-wheel');
|
||||
if (wheel && isHexColor(wheel.value)) {
|
||||
color = wheel.value;
|
||||
}
|
||||
}
|
||||
color = color || getFallbackLabelColor();
|
||||
board.addLabel(name, color);
|
||||
Popup.back();
|
||||
},
|
||||
@@ -158,7 +180,16 @@ Template.editLabelPopup.events({
|
||||
? selectedColorIcon.closest('.js-palette-color')
|
||||
: null;
|
||||
const selectedColorData = selectedPaletteNode && Blaze.getData(selectedPaletteNode);
|
||||
const color = selectedColorData?.color || getFallbackLabelColor();
|
||||
// #5514: a named swatch (has the check icon) wins; otherwise fall back to
|
||||
// the custom color-wheel hex, then to the default palette color.
|
||||
let color = selectedColorData?.color;
|
||||
if (!color) {
|
||||
const wheel = templateInstance.find('.js-label-color-wheel');
|
||||
if (wheel && isHexColor(wheel.value)) {
|
||||
color = wheel.value;
|
||||
}
|
||||
}
|
||||
color = color || getFallbackLabelColor();
|
||||
board.editLabel(this._id, name, color);
|
||||
Popup.back();
|
||||
},
|
||||
|
||||
@@ -183,6 +183,13 @@
|
||||
}
|
||||
.minicard .minicard-title {
|
||||
margin-inline-end: 1.5vw;
|
||||
/* #6444: isolate each title's bidirectional context. The title is a
|
||||
dir="auto" .viewer; under a global dir="rtl" document, non-isolated
|
||||
dir="auto" runs share bidi state, so typing a strong RTL (e.g. Arabic)
|
||||
character into another list's dir="auto" composer re-resolved the shared
|
||||
context and visibly reflowed OTHER lists' minicard titles (no data change,
|
||||
reverts on refresh). Isolation makes each title a self-contained run. */
|
||||
unicode-bidi: isolate;
|
||||
}
|
||||
.minicard .minicard-title .card-number {
|
||||
color: #b3b3b3;
|
||||
@@ -448,6 +455,10 @@
|
||||
min-height: 50px;
|
||||
margin-bottom: 20px;
|
||||
overflow-y: auto;
|
||||
/* #6444: isolate the composer's dir="auto" bidi run so typing a strong RTL
|
||||
character here cannot perturb the rendering of the surrounding minicard
|
||||
titles under a dir="rtl" document. */
|
||||
unicode-bidi: isolate;
|
||||
}
|
||||
.parent-prefix {
|
||||
color: #b3b3b3;
|
||||
|
||||
@@ -3,7 +3,8 @@ template(name="minicard")
|
||||
class="{{#if isLinkedCard}}linked-card{{/if}}"
|
||||
class="{{#if isLinkedBoard}}linked-board{{/if}}"
|
||||
class="{{agingClass}}"
|
||||
class="{{#if colorClass}}minicard-{{colorClass}}{{/if}}")
|
||||
class="{{#if colorClass}}minicard-{{colorClass}}{{/if}}"
|
||||
style="{{colorStyle}}")
|
||||
if canMoveCard
|
||||
if isTouchScreenOrShowDesktopDragHandles
|
||||
.handle
|
||||
@@ -93,11 +94,11 @@ template(name="minicard")
|
||||
.minicard-labels(class="{{#if hiddenMinicardLabelText}}minicard-labels-no-text{{/if}}")
|
||||
each labels
|
||||
unless hiddenMinicardLabelText
|
||||
span.js-card-label.card-label(class="card-label-{{color}}" title=name)
|
||||
span.js-card-label.card-label(class="{{labelColorClass color}}" style="{{labelColorStyle color}}" title=name)
|
||||
+viewer
|
||||
= name
|
||||
if hiddenMinicardLabelText
|
||||
.minicard-label(class="card-label-{{color}}" title="{{name}}")
|
||||
.minicard-label(class="{{labelColorClass color}}" style="{{labelColorStyle color}}" title="{{name}}")
|
||||
|
||||
.minicard-custom-fields
|
||||
each customFieldsWD
|
||||
|
||||
@@ -6,6 +6,11 @@ import uploadProgressManager from '../../lib/uploadProgressManager';
|
||||
import { Utils } from '/client/lib/utils';
|
||||
import ChecklistItems from '/models/checklistItems';
|
||||
import Cards from '/models/cards';
|
||||
import { resolveCoverId } from '/models/lib/linkedCardCover';
|
||||
import {
|
||||
parseChecklistItemTitles,
|
||||
buildChecklistItemPayload,
|
||||
} from '/models/lib/checklistItemTitles';
|
||||
import { normalizeDependencies } from '/models/metadata/dependencies';
|
||||
|
||||
function getMinicardFlag(board, onMinicardField, legacyField, defaultValue) {
|
||||
@@ -164,8 +169,12 @@ Template.minicard.helpers({
|
||||
}
|
||||
},
|
||||
cover() {
|
||||
if (!this.coverId) return null;
|
||||
const attachment = ReactiveCache.getAttachment(this.coverId);
|
||||
// #5666: for a linked card the cover lives on the real card it points at, so
|
||||
// resolve the cover id through it (mirroring getTitle/getDue/...); a plain
|
||||
// card resolves to its own coverId.
|
||||
const coverId = resolveCoverId(this, id => ReactiveCache.getCard(id));
|
||||
if (!coverId) return null;
|
||||
const attachment = ReactiveCache.getAttachment(coverId);
|
||||
if (!attachment) return null;
|
||||
const coverLink = typeof attachment.link === 'function' ? attachment.link() : '';
|
||||
if (!coverLink) return null;
|
||||
@@ -376,6 +385,28 @@ Template.minicardChecklist.helpers({
|
||||
},
|
||||
});
|
||||
|
||||
// #6440: create the checklist item(s) for the minicard add-item form. Shared by
|
||||
// the Save-button click handler and the (rarely reached) form-submit handler.
|
||||
// The Save button needs an explicit click handler because the minicard — and so
|
||||
// the add-item <form> — is rendered inside the `a.minicard-wrapper` anchor, where
|
||||
// the native form `submit` event never reaches the Blaze event map; without this
|
||||
// the "+" add-item on the minicard did nothing. The blank-input guard and the
|
||||
// title parsing live in the pure, unit-tested models/lib/checklistItemTitles.js.
|
||||
function addMinicardChecklistItems(textarea, checklist) {
|
||||
if (!textarea || !checklist) return;
|
||||
const titles = parseChecklistItemTitles(textarea.value);
|
||||
if (titles.length === 0) return;
|
||||
const items = checklist.items();
|
||||
const lastItem = items[items.length - 1] || null;
|
||||
let sortIndex = Utils.calculateIndexData(lastItem, null).base;
|
||||
titles.forEach(title => {
|
||||
ChecklistItems.insert(buildChecklistItemPayload(title, checklist, sortIndex));
|
||||
sortIndex += 1;
|
||||
});
|
||||
textarea.value = '';
|
||||
textarea.focus();
|
||||
}
|
||||
|
||||
Template.minicardChecklist.events({
|
||||
'click .js-convert-checklist-item-to-card'(event) {
|
||||
event.stopPropagation();
|
||||
@@ -417,25 +448,26 @@ Template.minicardChecklist.events({
|
||||
}
|
||||
$form.find('.js-close-inlined-form').trigger('click');
|
||||
},
|
||||
// #6440: explicit click handler for the minicard "+" add-item Save button.
|
||||
// The native form `submit` event does not fire on the minicard (the form is
|
||||
// inside the `a.minicard-wrapper` anchor), so — unlike card-detail — we cannot
|
||||
// rely on `submit .js-add-checklist-item`. This mirrors the already-working
|
||||
// `click .js-submit-edit-checklist-item-form` above.
|
||||
'click .js-submit-add-checklist-item-form'(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const $form = $(event.currentTarget).closest('form');
|
||||
const textarea = $form.find('textarea.js-add-checklist-item')[0];
|
||||
const formData = Blaze.getData($form[0]) || Template.currentData() || {};
|
||||
addMinicardChecklistItems(textarea, formData.checklist);
|
||||
},
|
||||
// Kept for completeness (e.g. card-detail-style contexts); on the minicard this
|
||||
// rarely fires, so the click handler above is the real path. See #6440.
|
||||
'submit .js-add-checklist-item'(event, tpl) {
|
||||
event.preventDefault();
|
||||
const textarea = tpl.find('textarea.js-add-checklist-item');
|
||||
if (!textarea) return;
|
||||
const title = textarea.value.trim();
|
||||
const data = Template.currentData() || {};
|
||||
const checklist = data.checklist;
|
||||
if (!checklist || !title) return;
|
||||
const items = checklist.items();
|
||||
const lastItem = items[items.length - 1] || null;
|
||||
const sortIndex = Utils.calculateIndexData(lastItem, null).base;
|
||||
ChecklistItems.insert({
|
||||
title,
|
||||
checklistId: checklist._id,
|
||||
cardId: checklist.cardId,
|
||||
sort: sortIndex,
|
||||
});
|
||||
textarea.value = '';
|
||||
textarea.focus();
|
||||
addMinicardChecklistItems(textarea, data.checklist);
|
||||
},
|
||||
'click .js-delete-checklist-item': Popup.afterConfirm('checklistItemDelete', function () {
|
||||
Popup.back();
|
||||
|
||||
@@ -736,9 +736,28 @@ form.inlined-form .list-composer {
|
||||
flex: 1 1 auto;
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
overflow-y: auto;
|
||||
/* #5439: always show a scrollbar on the list body across desktop and mobile
|
||||
browsers (overlay scrollbars on macOS/iOS/Android/Firefox otherwise auto-
|
||||
hide it). Keep these declarations in sync with models/lib/scrollbarCss.js
|
||||
(alwaysVisibleScrollbarCss), which is unit tested. */
|
||||
overflow-y: scroll;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(0, 0, 0, 0.35) rgba(0, 0, 0, 0.08);
|
||||
scrollbar-gutter: stable;
|
||||
padding: 5px 11px;
|
||||
}
|
||||
.list-body::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
.list-body::-webkit-scrollbar-track {
|
||||
background: rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
.list-body::-webkit-scrollbar-thumb {
|
||||
background: rgba(0, 0, 0, 0.35);
|
||||
border-radius: 5px;
|
||||
}
|
||||
.list-body .minicards {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 0;
|
||||
|
||||
@@ -46,7 +46,7 @@ template(name="addCardForm")
|
||||
if getLabels
|
||||
.minicard-labels
|
||||
each getLabels
|
||||
.minicard-label(class="card-label-{{color}}" title="{{name}}")
|
||||
.minicard-label(class="{{labelColorClass color}}" style="{{labelColorStyle color}}" title="{{name}}")
|
||||
textarea.minicard-composer-textarea.js-card-title(autofocus dir="auto")
|
||||
if members.get
|
||||
.minicard-members.js-minicard-composer-members
|
||||
|
||||
@@ -9,6 +9,10 @@ import Swimlanes from '/models/swimlanes';
|
||||
import { Filter } from '/client/lib/filter';
|
||||
import { MultiSelection } from '/client/lib/multiSelection';
|
||||
import { Utils } from '/client/lib/utils';
|
||||
import { isLinkableCardTarget } from '/models/lib/linkedCardTarget';
|
||||
import { listCardsSelector } from '/models/lib/swimlaneFilter';
|
||||
import { sortCardsByTitle } from '/models/lib/sortCardsByTitle';
|
||||
import { isLazyCards, BoardListCardCounts, windowCountId } from '/client/lib/lazyCards';
|
||||
import autosize from 'autosize';
|
||||
|
||||
// SubsManager removed for Meteor 3 migration
|
||||
@@ -338,20 +342,36 @@ Template.listBody.helpers({
|
||||
if (!sortBy) {
|
||||
sortBy = defaultSort;
|
||||
}
|
||||
const selector = {
|
||||
listId: Template.currentData()._id,
|
||||
archived: false,
|
||||
};
|
||||
if (swimlaneId) {
|
||||
// Fallback: also show cards with no swimlaneId (null/empty) so that
|
||||
// pre-migration / orphaned cards are always visible without a DB migration.
|
||||
selector.$or = [
|
||||
{ swimlaneId },
|
||||
{ swimlaneId: null }, // null covers null AND missing field
|
||||
{ swimlaneId: '' }, // empty string from shared-lists era
|
||||
];
|
||||
// #6441: build the swimlane-membership fallback as a single `swimlaneId:
|
||||
// { $in: [...] }` clause (via the shared, unit-tested helper) instead of a
|
||||
// bare top-level `$or`, so it never competes with the board Filter's own
|
||||
// top-level `$or` (labels/members/exceptions) and the label filter applies
|
||||
// board-wide across every swimlane.
|
||||
// #6443: in the first swimlane also surface orphaned cards (a swimlaneId
|
||||
// pointing at a deleted swimlane) so they are not invisible in swimlane view.
|
||||
const list = Template.currentData();
|
||||
const selector = listCardsSelector(
|
||||
list._id,
|
||||
swimlaneId,
|
||||
list.orphanedCardsSwimlaneIds
|
||||
? list.orphanedCardsSwimlaneIds(swimlaneId)
|
||||
: undefined,
|
||||
);
|
||||
const mongoSelector = Filter.mongoSelector(selector);
|
||||
// Lazy card loading: the board publication ships no cards, so subscribe to
|
||||
// just this list/swimlane window (grows with `limit` as the user scrolls)
|
||||
// plus its reactive total count. Default mode keeps everything in minimongo
|
||||
// already, so these subscriptions are skipped.
|
||||
if (isLazyCards() && list.boardId) {
|
||||
tpl.subscribe('boardCardsWindow', list.boardId, mongoSelector, sortBy, limit);
|
||||
tpl.subscribe(
|
||||
'boardListCardCount',
|
||||
windowCountId(list._id, swimlaneId),
|
||||
list.boardId,
|
||||
mongoSelector,
|
||||
);
|
||||
}
|
||||
const ret = ReactiveCache.getCards(Filter.mongoSelector(selector), {
|
||||
const ret = ReactiveCache.getCards(mongoSelector, {
|
||||
// sort: ['sort'],
|
||||
sort: sortBy,
|
||||
limit,
|
||||
@@ -362,6 +382,13 @@ Template.listBody.helpers({
|
||||
showSpinner(swimlaneId) {
|
||||
const tpl = Template.instance();
|
||||
const list = Template.currentData();
|
||||
if (isLazyCards()) {
|
||||
// In lazy mode minimongo only holds the loaded window, so the total comes
|
||||
// from the server count published into BoardListCardCounts.
|
||||
const countDoc = BoardListCardCounts.findOne(windowCountId(list._id, swimlaneId));
|
||||
const total = countDoc ? countDoc.count : 0;
|
||||
return total > tpl.cardlimit.get();
|
||||
}
|
||||
return list.cards(swimlaneId).length > tpl.cardlimit.get();
|
||||
},
|
||||
|
||||
@@ -706,14 +733,19 @@ Template.linkCardPopup.helpers({
|
||||
archived: false,
|
||||
linkedId: { $nin: ownCardsIds },
|
||||
_id: { $nin: ownCardsIds },
|
||||
type: { $nin: ['template-card'] },
|
||||
// #5808: never offer an existing linked card/board as a link target —
|
||||
// linking to one builds a chain of linkedId pointers that renders the
|
||||
// card inaccessible. Only real cards may be linked.
|
||||
type: { $nin: ['template-card', 'cardType-linkedCard', 'cardType-linkedBoard'] },
|
||||
};
|
||||
if (tpl.selectedBoardId.get()) selector.boardId = tpl.selectedBoardId.get();
|
||||
if (tpl.selectedSwimlaneId.get()) selector.swimlaneId = tpl.selectedSwimlaneId.get();
|
||||
if (tpl.selectedListId.get()) selector.listId = tpl.selectedListId.get();
|
||||
|
||||
const ret = ReactiveCache.getCards(selector, { sort: { sort: 1 } });
|
||||
return ret;
|
||||
// #5394: sort the Cards dropdown alphabetically by title
|
||||
// (case-insensitive) so a card can be found on boards with many cards.
|
||||
return sortCardsByTitle(ret);
|
||||
},
|
||||
|
||||
isTitleDefault(title) {
|
||||
@@ -790,6 +822,17 @@ Template.linkCardPopup.events({
|
||||
Popup.back();
|
||||
return;
|
||||
}
|
||||
// #5808: refuse to link to a linked card/board or to a card that links back
|
||||
// to this board — those build a chain/cycle of linkedId pointers that renders
|
||||
// the cards inaccessible. The <select> already filters these out, but its
|
||||
// options can be stale, so re-check the resolved target here.
|
||||
const targetCard = ReactiveCache.getCard(linkedId);
|
||||
const ownCardsIds = tpl.board.cards().map(card => card.getRealId());
|
||||
if (!isLinkableCardTarget(targetCard, ownCardsIds)) {
|
||||
alert(TAPi18n.__('error-linked-card-not-allowed'));
|
||||
Popup.back();
|
||||
return;
|
||||
}
|
||||
const nextCardNumber = await tpl.board.getNextCardNumber();
|
||||
const sortIndex = tpl.getSortIndex();
|
||||
const _id = Cards.insert({
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
template(name="listHeader")
|
||||
.list-header.js-list-header.nodragscroll(
|
||||
class="{{#if limitToShowCardsCount}}list-header-card-count{{/if}}"
|
||||
class=colorClass)
|
||||
class=colorClass
|
||||
style=colorStyle)
|
||||
//- #459: keyboard/screen-reader list reordering (visually hidden, focusable).
|
||||
.list-move-buttons.sr-only
|
||||
a.js-list-move-left(href="#" role="button" aria-label="{{_ 'move-list-left'}}") {{_ 'move-list-left'}}
|
||||
@@ -15,7 +16,7 @@ template(name="listHeader")
|
||||
i.fa.fa-caret-left
|
||||
else
|
||||
if collapsed
|
||||
if showCardsCountForList cards.length
|
||||
if showCardsCountForList cardsCountWhole
|
||||
br
|
||||
span.cardCount {{cardsCount}}
|
||||
if isMiniScreen
|
||||
@@ -26,10 +27,10 @@ template(name="listHeader")
|
||||
= title
|
||||
if wipLimit.enabled
|
||||
| (
|
||||
span(class="{{#if exceededWipLimit}}highlight{{/if}}") {{cards.length}}
|
||||
span(class="{{#if exceededWipLimit}}highlight{{/if}}") {{cardsCountWhole}}
|
||||
|/#{wipLimit.value})
|
||||
if showCardsCountForList cards.length
|
||||
span.cardCount {{cardsCount}} {{cardsCountForListIsOne cards.length}}
|
||||
if showCardsCountForList cardsCountWhole
|
||||
span.cardCount {{cardsCount}} {{cardsCountForListIsOne cardsCountWhole}}
|
||||
if hasNumberFieldsSum
|
||||
|
|
||||
span.list-sum-badge(title="{{_ 'sum-of-number-fields'}}") ∑ {{numberFieldsSum}}
|
||||
@@ -47,11 +48,11 @@ template(name="listHeader")
|
||||
= title
|
||||
if wipLimit.enabled
|
||||
| (
|
||||
span(class="{{#if exceededWipLimit}}highlight{{/if}}") {{cards.length}}
|
||||
span(class="{{#if exceededWipLimit}}highlight{{/if}}") {{cardsCountWhole}}
|
||||
|/#{wipLimit.value})
|
||||
unless collapsed
|
||||
if showCardsCountForList cards.length
|
||||
span.cardCount {{cardsCount}} {{cardsCountForListIsOne cards.length}}
|
||||
if showCardsCountForList cardsCountWhole
|
||||
span.cardCount {{cardsCount}} {{cardsCountForListIsOne cardsCountWhole}}
|
||||
if hasNumberFieldsSum
|
||||
|
|
||||
span.list-sum-badge(title="{{_ 'sum-of-number-fields'}}") ∑ {{numberFieldsSum}}
|
||||
@@ -279,6 +280,10 @@ template(name="setListColorPopup")
|
||||
span.card-label.palette-color.js-palette-color(class="card-details-{{color}}")
|
||||
if(isSelected color)
|
||||
i.fa.fa-check
|
||||
// #5514: native color wheel for picking a custom RGB / hex color
|
||||
.color-wheel-wrap
|
||||
label(for="listColorWheel") {{_ 'custom-color'}}
|
||||
input.js-list-color-wheel#listColorWheel(type="color" value=currentColorHex title="{{_ 'custom-color'}}")
|
||||
button.primary.confirm.js-submit {{_ 'save'}}
|
||||
button.js-remove-color.negate.wide.right {{_ 'unset-color'}}
|
||||
|
||||
|
||||
@@ -5,8 +5,10 @@ import dragscroll from '@wekanteam/dragscroll';
|
||||
import { BoardSwimlaneListDialog } from '/client/lib/dialogWithBoardSwimlaneList';
|
||||
import Cards from '/models/cards';
|
||||
import { LIST_COLORS } from '/models/metadata/colors';
|
||||
import { isHexColor, toHex } from '/models/lib/contrastColor';
|
||||
import { MultiSelection } from '/client/lib/multiSelection';
|
||||
import { Utils } from '/client/lib/utils';
|
||||
import { lazyListCardCount } from '/client/lib/lazyCards';
|
||||
|
||||
let listsColors;
|
||||
Meteor.startup(() => {
|
||||
@@ -59,24 +61,33 @@ Template.listHeader.helpers({
|
||||
swimlaneId = list.swimlaneId || '';
|
||||
}
|
||||
|
||||
const ret = list.cards(swimlaneId).length;
|
||||
return ret;
|
||||
// In lazy mode minimongo holds only the loaded window, so use the accurate
|
||||
// server count for the same selector; fall back to the loaded length.
|
||||
const lazyCount = lazyListCardCount(list, swimlaneId);
|
||||
return lazyCount !== null ? lazyCount : list.cards(swimlaneId).length;
|
||||
},
|
||||
|
||||
reachedWipLimit() {
|
||||
const list = Template.currentData();
|
||||
return (
|
||||
list.getWipLimit('enabled') &&
|
||||
list.getWipLimit('value') <= list.cards().length
|
||||
);
|
||||
const lazyCount = lazyListCardCount(list, undefined);
|
||||
const count = lazyCount !== null ? lazyCount : list.cards().length;
|
||||
return list.getWipLimit('enabled') && list.getWipLimit('value') <= count;
|
||||
},
|
||||
|
||||
exceededWipLimit() {
|
||||
const list = Template.currentData();
|
||||
return (
|
||||
list.getWipLimit('enabled') &&
|
||||
list.getWipLimit('value') < list.cards().length
|
||||
);
|
||||
const lazyCount = lazyListCardCount(list, undefined);
|
||||
const count = lazyCount !== null ? lazyCount : list.cards().length;
|
||||
return list.getWipLimit('enabled') && list.getWipLimit('value') < count;
|
||||
},
|
||||
|
||||
// Accurate whole-list card count (used by the badge visibility, WIP-limit
|
||||
// number and pluralization in the template). In lazy mode `cards.length`
|
||||
// (= list.cards().length) is only the loaded window, so prefer the server count.
|
||||
cardsCountWhole() {
|
||||
const list = Template.currentData();
|
||||
const lazyCount = lazyListCardCount(list, undefined);
|
||||
return lazyCount !== null ? lazyCount : list.cards().length;
|
||||
},
|
||||
|
||||
showCardsCountForList(count) {
|
||||
@@ -141,10 +152,9 @@ Template.listHeader.helpers({
|
||||
Template.listHeader.onCreated(function () {
|
||||
this.reachedWipLimit = function () {
|
||||
const list = Template.currentData();
|
||||
return (
|
||||
list.getWipLimit('enabled') &&
|
||||
list.getWipLimit('value') <= list.cards().length
|
||||
);
|
||||
const lazyCount = lazyListCardCount(list, undefined);
|
||||
const count = lazyCount !== null ? lazyCount : list.cards().length;
|
||||
return list.getWipLimit('enabled') && list.getWipLimit('value') <= count;
|
||||
};
|
||||
});
|
||||
|
||||
@@ -305,11 +315,10 @@ Template.setWipLimitPopup.events({
|
||||
async 'click .js-enable-wip-limit'() {
|
||||
const list = Template.currentData();
|
||||
// Prevent user from using previously stored wipLimit.value if it is less than the current number of cards in the list
|
||||
if (
|
||||
!list.getWipLimit('enabled') &&
|
||||
list.getWipLimit('value') < list.cards().length
|
||||
) {
|
||||
await list.setWipLimit(list.cards().length);
|
||||
const lazyCount = lazyListCardCount(list, undefined);
|
||||
const count = lazyCount !== null ? lazyCount : list.cards().length;
|
||||
if (!list.getWipLimit('enabled') && list.getWipLimit('value') < count) {
|
||||
await list.setWipLimit(count);
|
||||
}
|
||||
Meteor.call('enableWipLimit', list._id);
|
||||
},
|
||||
@@ -476,6 +485,10 @@ Template.setListColorPopup.helpers({
|
||||
return tpl.currentColor.get() === color;
|
||||
}
|
||||
},
|
||||
// #5514: current color as a '#rrggbb' hex for the native color wheel <input>.
|
||||
currentColorHex() {
|
||||
return toHex(Template.instance().currentColor.get()) || '#0079bf';
|
||||
},
|
||||
});
|
||||
|
||||
Template.setListColorPopup.events({
|
||||
@@ -483,6 +496,13 @@ Template.setListColorPopup.events({
|
||||
const paletteData = Blaze.getData(event.currentTarget);
|
||||
tpl.currentColor.set(paletteData?.color);
|
||||
},
|
||||
// #5514: picking from the native color wheel stores a custom hex.
|
||||
'input .js-list-color-wheel'(event, tpl) {
|
||||
const value = event.currentTarget.value;
|
||||
if (isHexColor(value)) {
|
||||
tpl.currentColor.set(value);
|
||||
}
|
||||
},
|
||||
async 'submit form'(event, tpl) {
|
||||
event.preventDefault();
|
||||
try {
|
||||
|
||||
@@ -83,14 +83,23 @@ Template.userFormsLayout.onRendered(() => {
|
||||
})();
|
||||
});
|
||||
|
||||
// #4394 (security): a one-shot $('.at-signup-link').hide() does not survive
|
||||
// the useraccounts form re-rendering that happens after a failed login (e.g.
|
||||
// an LDAP "User not Found") — the links were recreated without the inline
|
||||
// display:none and reappeared even though registration / forgot-password
|
||||
// were disabled in the Admin Panel. Toggle a class on the stable <body>
|
||||
// ancestor and hide the links via CSS (userForm.css) so the rule keeps
|
||||
// applying to every freshly re-rendered copy of the links.
|
||||
Meteor.call('isDisableRegistration', (_, result) => {
|
||||
if (result) {
|
||||
document.body.classList.add('registration-disabled');
|
||||
$('.at-signup-link').hide();
|
||||
}
|
||||
});
|
||||
|
||||
Meteor.call('isDisableForgotPassword', (_, result) => {
|
||||
if (result) {
|
||||
document.body.classList.add('forgotpwd-disabled');
|
||||
$('.at-pwd-link').hide();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -94,7 +94,7 @@ template(name="myCards")
|
||||
td
|
||||
.div
|
||||
each label in card.labelIds
|
||||
span.card-label(class="card-label-{{labelColor board label}}" title=name)
|
||||
span.card-label(class="{{labelColorClass (labelColor board label)}}" style="{{labelColorStyle (labelColor board label)}}" title=name)
|
||||
| {{labelName board label}}
|
||||
td
|
||||
if card.dueAt
|
||||
|
||||
@@ -4,6 +4,7 @@ import Actions from '/models/actions';
|
||||
import Rules from '/models/rules';
|
||||
import Triggers from '/models/triggers';
|
||||
import { Utils } from '/client/lib/utils';
|
||||
import { canSelectBoardInRules } from '/models/lib/rulesBoardSelector';
|
||||
|
||||
Template.boardActions.onCreated(function () {
|
||||
this.subscribe('boards');
|
||||
@@ -11,19 +12,30 @@ Template.boardActions.onCreated(function () {
|
||||
|
||||
Template.boardActions.helpers({
|
||||
boards() {
|
||||
const ret = ReactiveCache.getBoards(
|
||||
{
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: {
|
||||
$ne: ReactiveCache.getCurrentUser().getTemplatesBoardId(),
|
||||
},
|
||||
},
|
||||
// #5698: don't restrict to boards where the user is a DIRECT member — that
|
||||
// hid every board reached through an Organization / Team / email-domain
|
||||
// share (the reporter's whole board dropdown was empty, current board
|
||||
// included). Filter the already access-scoped client cache with the same
|
||||
// visibility rule as Boards.userBoards() so org/team/domain boards show too.
|
||||
const user = ReactiveCache.getCurrentUser();
|
||||
if (!user) return [];
|
||||
const ctx = {
|
||||
userId: user._id,
|
||||
orgIds: typeof user.orgIds === 'function' ? user.orgIds() : [],
|
||||
teamIds: typeof user.teamIds === 'function' ? user.teamIds() : [],
|
||||
emailDomains:
|
||||
typeof user.emailDomains === 'function' ? user.emailDomains() : [],
|
||||
};
|
||||
const templatesBoardId = user.getTemplatesBoardId();
|
||||
const boards = ReactiveCache.getBoards(
|
||||
{ archived: false },
|
||||
{
|
||||
sort: { sort: 1 /* boards default sorting */ },
|
||||
},
|
||||
);
|
||||
return ret;
|
||||
return boards.filter(board =>
|
||||
canSelectBoardInRules(board, ctx, templatesBoardId),
|
||||
);
|
||||
},
|
||||
|
||||
loadingBoardsLabel() {
|
||||
@@ -94,38 +106,19 @@ Template.boardActions.events({
|
||||
const boardId = Session.get('currentBoard');
|
||||
const destBoardId = tpl.find('#board-id').value;
|
||||
const desc = Utils.getTriggerActionDesc(event, tpl);
|
||||
if (actionSelected === 'top') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'moveCardToTop',
|
||||
listName,
|
||||
swimlaneName,
|
||||
boardId: destBoardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'bottom') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'moveCardToBottom',
|
||||
listName,
|
||||
swimlaneName,
|
||||
boardId: destBoardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
// #5536: create the rule on the server (see server/rulesButton.js →
|
||||
// rules.createRule) instead of three optimistic client Collection.insert()
|
||||
// calls. The optimistic writes landed in minimongo limbo once the wizard
|
||||
// subscription stopped, so the cross-board move action rendered BLANK; and
|
||||
// the client allow/deny rules rejected them outright for non-owner members.
|
||||
// The action keeps its own destination `boardId` (the board to move to).
|
||||
Meteor.call('rules.createRule', boardId, ruleName, trigger, {
|
||||
actionType: actionSelected === 'bottom' ? 'moveCardToBottom' : 'moveCardToTop',
|
||||
listName,
|
||||
swimlaneName,
|
||||
boardId: destBoardId,
|
||||
desc,
|
||||
});
|
||||
},
|
||||
'click .js-add-gen-move-action'(event, tpl) {
|
||||
const desc = Utils.getTriggerActionDesc(event, tpl);
|
||||
@@ -191,20 +184,16 @@ Template.boardActions.events({
|
||||
const boardId = Session.get('currentBoard');
|
||||
const destBoardId = tpl.find('#board-id-link').value;
|
||||
const desc = Utils.getTriggerActionDesc(event, tpl);
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
// #5536: create on the server so the cross-board link action does not land
|
||||
// blank in minimongo limbo / get rejected by client allow/deny. The action
|
||||
// keeps its own destination `boardId` (the board to link the card into).
|
||||
Meteor.call('rules.createRule', boardId, ruleName, trigger, {
|
||||
actionType: 'linkCard',
|
||||
listName,
|
||||
swimlaneName,
|
||||
boardId: destBoardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
},
|
||||
'click .js-sort-list-action'(event, tpl) {
|
||||
const data = Template.currentData();
|
||||
|
||||
@@ -100,8 +100,15 @@ template(name="cardActions")
|
||||
option(value="receivedAt") {{_'r-df-received-at'}}
|
||||
div.trigger-dropdown
|
||||
input(id="reldate-days",type="number",value="0")
|
||||
div.trigger-dropdown
|
||||
select(id="reldate-unit")
|
||||
option(value="minutes") {{_'r-unit-minutes'}}
|
||||
option(value="hours") {{_'r-unit-hours'}}
|
||||
option(value="days",selected="selected") {{_'r-unit-days'}}
|
||||
option(value="weeks") {{_'r-unit-weeks'}}
|
||||
option(value="months") {{_'r-unit-months'}}
|
||||
div.trigger-text
|
||||
| {{_'r-days-after'}}
|
||||
| {{_'r-later'}}
|
||||
div.trigger-button.js-set-reldate-action.js-goto-rules
|
||||
i.fa.fa-plus
|
||||
|
||||
|
||||
@@ -238,12 +238,16 @@ Template.cardActions.events({
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, tpl);
|
||||
const dateField = tpl.find('#reldate-datefield').value;
|
||||
// `days` keeps its name for backward compatibility: it is the numeric
|
||||
// amount, interpreted together with `unit` (minutes/hours/days/weeks/months).
|
||||
const days = parseInt(tpl.find('#reldate-days').value, 10) || 0;
|
||||
const unit = tpl.find('#reldate-unit').value;
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'setDateRelative',
|
||||
dateField,
|
||||
days,
|
||||
unit,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { ReactiveCache } from '/imports/reactiveCache';
|
||||
import Actions from '/models/actions';
|
||||
import Rules from '/models/rules';
|
||||
import Triggers from '/models/triggers';
|
||||
|
||||
function boardRuleIds() {
|
||||
const boardId = Session.get('currentBoard');
|
||||
@@ -62,9 +60,10 @@ Template.rulesList.events({
|
||||
getSelected().forEach(ruleId => {
|
||||
const rule = ReactiveCache.getRule(ruleId);
|
||||
if (rule) {
|
||||
Rules.remove(rule._id);
|
||||
Actions.remove(rule.actionId);
|
||||
Triggers.remove(rule.triggerId);
|
||||
// Delete the rule + its trigger + action server-side (see
|
||||
// server/rulesButton.js `rules.deleteRule`): three client-side
|
||||
// Collection.remove() calls were rejected with 403 "Access denied".
|
||||
Meteor.call('rules.deleteRule', rule._id);
|
||||
}
|
||||
});
|
||||
setSelected([]);
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { ReactiveCache } from '/imports/reactiveCache';
|
||||
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
|
||||
import { Utils } from '/client/lib/utils';
|
||||
import Actions from '/models/actions';
|
||||
import Rules from '/models/rules';
|
||||
import Triggers from '/models/triggers';
|
||||
|
||||
Template.rulesMain.onCreated(function () {
|
||||
this.rulesCurrentTab = new ReactiveVar('rulesList');
|
||||
@@ -83,9 +80,12 @@ function sanitizeObject(obj) {
|
||||
Template.rulesMain.events({
|
||||
'click .js-delete-rule'() {
|
||||
const rule = Template.currentData();
|
||||
Rules.remove(rule._id);
|
||||
Actions.remove(rule.actionId);
|
||||
Triggers.remove(rule.triggerId);
|
||||
// Delete the rule + its trigger + action in one server call. Doing this
|
||||
// client-side as three separate Collection.remove() calls failed with 403
|
||||
// "Access denied" whenever a trigger/action document's boardId did not
|
||||
// resolve to a board in the allow() rule; the method authorizes once and
|
||||
// removes all three server-side.
|
||||
Meteor.call('rules.deleteRule', rule._id);
|
||||
},
|
||||
'click .js-goto-trigger'(event, tpl) {
|
||||
event.preventDefault();
|
||||
|
||||
@@ -12,7 +12,7 @@ template(name="rulesWorkflow")
|
||||
.workflow-node.workflow-node-when
|
||||
span.workflow-node-label {{_ 'r-when'}}
|
||||
span.workflow-node-text= builderTriggerLabel
|
||||
a.workflow-slot-clear.js-clear-when(title="{{_ 'remove'}}") ✕
|
||||
a.workflow-slot-clear.js-clear-when(title="{{_ 'r-remove'}}") ✕
|
||||
else
|
||||
span.workflow-slot-placeholder {{_ 'r-when'}}: {{_ 'r-drop-trigger'}}
|
||||
.workflow-arrow
|
||||
@@ -22,7 +22,7 @@ template(name="rulesWorkflow")
|
||||
.workflow-node.workflow-node-then
|
||||
span.workflow-node-label {{_ 'r-action'}}
|
||||
span.workflow-node-text= builderActionLabel
|
||||
a.workflow-slot-clear.js-clear-then(title="{{_ 'remove'}}") ✕
|
||||
a.workflow-slot-clear.js-clear-then(title="{{_ 'r-remove'}}") ✕
|
||||
else
|
||||
span.workflow-slot-placeholder {{_ 'r-action'}}: {{_ 'r-drop-action'}}
|
||||
.workflow-builder-controls
|
||||
|
||||
@@ -12,31 +12,44 @@ import Triggers from '/models/triggers';
|
||||
// can be built entirely by dragging. Parameterized rules (specific list, label,
|
||||
// member, schedule time, …) are still created with the form builder on the
|
||||
// List view.
|
||||
// Each palette entry carries an i18n `labelKey` (translated at render time via
|
||||
// TAPi18n.__) instead of a hardcoded English string, so the whole workflow view
|
||||
// follows the UI language. `labelParams` is passed to TAPi18n.__ for labels that
|
||||
// need a placeholder (e.g. the scheduled time). Existing rule keys are reused
|
||||
// where they fit; workflow-only labels use new `r-w-*` keys (see en.i18n.json).
|
||||
const TRIGGER_PALETTE = [
|
||||
{ label: 'Card is created', doc: { activityType: 'createCard', listName: '*', swimlaneName: '*', cardTitle: '*', userId: '*' } },
|
||||
{ label: 'Card is moved', doc: { activityType: 'moveCard', listName: '*', oldListName: '*', swimlaneName: '*', cardTitle: '*', userId: '*' } },
|
||||
{ label: 'Card is archived', doc: { activityType: 'archivedCard', userId: '*' } },
|
||||
{ label: 'Card is unarchived', doc: { activityType: 'restoredCard', userId: '*' } },
|
||||
{ label: 'Any label is added', doc: { activityType: 'addedLabel', labelId: '*', userId: '*' } },
|
||||
{ label: 'Any label is removed', doc: { activityType: 'removedLabel', labelId: '*', userId: '*' } },
|
||||
{ label: 'A member is added', doc: { activityType: 'joinMember', username: '*', userId: '*' } },
|
||||
{ label: 'A member is removed', doc: { activityType: 'unjoinMember', username: '*', userId: '*' } },
|
||||
{ label: 'A checklist is added', doc: { activityType: 'addChecklist', checklistName: '*', userId: '*' } },
|
||||
{ label: 'An attachment is added', doc: { activityType: 'addAttachment', userId: '*' } },
|
||||
{ label: 'Every day at 09:00', doc: { activityType: 'scheduledTrigger', scheduleKind: 'calendar', scheduleType: 'daily', atTime: '09:00', listName: '*', swimlaneName: '*' } },
|
||||
{ labelKey: 'r-w-card-created', doc: { activityType: 'createCard', listName: '*', swimlaneName: '*', cardTitle: '*', userId: '*' } },
|
||||
{ labelKey: 'r-when-a-card-is-moved', doc: { activityType: 'moveCard', listName: '*', oldListName: '*', swimlaneName: '*', cardTitle: '*', userId: '*' } },
|
||||
{ labelKey: 'r-w-card-archived', doc: { activityType: 'archivedCard', userId: '*' } },
|
||||
{ labelKey: 'r-w-card-unarchived', doc: { activityType: 'restoredCard', userId: '*' } },
|
||||
{ labelKey: 'r-w-label-added', doc: { activityType: 'addedLabel', labelId: '*', userId: '*' } },
|
||||
{ labelKey: 'r-w-label-removed', doc: { activityType: 'removedLabel', labelId: '*', userId: '*' } },
|
||||
{ labelKey: 'r-w-member-added', doc: { activityType: 'joinMember', username: '*', userId: '*' } },
|
||||
{ labelKey: 'r-w-member-removed', doc: { activityType: 'unjoinMember', username: '*', userId: '*' } },
|
||||
{ labelKey: 'r-w-checklist-added', doc: { activityType: 'addChecklist', checklistName: '*', userId: '*' } },
|
||||
{ labelKey: 'r-w-attachment-added', doc: { activityType: 'addAttachment', userId: '*' } },
|
||||
{ labelKey: 'r-w-every-day-at', labelParams: { time: '09:00' }, doc: { activityType: 'scheduledTrigger', scheduleKind: 'calendar', scheduleType: 'daily', atTime: '09:00', listName: '*', swimlaneName: '*' } },
|
||||
];
|
||||
|
||||
const ACTION_PALETTE = [
|
||||
{ label: 'Move card to top', doc: { actionType: 'moveCardToTop', listName: '*', swimlaneName: '*' } },
|
||||
{ label: 'Move card to bottom', doc: { actionType: 'moveCardToBottom', listName: '*', swimlaneName: '*' } },
|
||||
{ label: 'Archive card', doc: { actionType: 'archive' } },
|
||||
{ label: 'Unarchive card', doc: { actionType: 'unarchive' } },
|
||||
{ label: 'Mark card complete', doc: { actionType: 'markCardComplete' } },
|
||||
{ label: 'Mark card incomplete', doc: { actionType: 'markCardIncomplete' } },
|
||||
{ label: 'Remove all members', doc: { actionType: 'removeMember', username: '*' } },
|
||||
{ label: 'Set received date to now', doc: { actionType: 'setDate', dateField: 'receivedAt' } },
|
||||
{ labelKey: 'r-d-move-to-top-gen', doc: { actionType: 'moveCardToTop', listName: '*', swimlaneName: '*' } },
|
||||
{ labelKey: 'r-d-move-to-bottom-gen', doc: { actionType: 'moveCardToBottom', listName: '*', swimlaneName: '*' } },
|
||||
{ labelKey: 'r-d-archive', doc: { actionType: 'archive' } },
|
||||
{ labelKey: 'r-d-unarchive', doc: { actionType: 'unarchive' } },
|
||||
{ labelKey: 'r-mark-complete', doc: { actionType: 'markCardComplete' } },
|
||||
{ labelKey: 'r-mark-incomplete', doc: { actionType: 'markCardIncomplete' } },
|
||||
{ labelKey: 'r-remove-all', doc: { actionType: 'removeMember', username: '*' } },
|
||||
{ labelKey: 'r-w-set-received-now', doc: { actionType: 'setDate', dateField: 'receivedAt' } },
|
||||
];
|
||||
|
||||
// Translate a palette entry's label for display / storage in the current UI
|
||||
// language (matches the classic Rules view, which also stores already-translated
|
||||
// description text at creation time).
|
||||
function paletteLabel(entry) {
|
||||
if (!entry) return '';
|
||||
return TAPi18n.__(entry.labelKey, entry.labelParams);
|
||||
}
|
||||
|
||||
Template.rulesWorkflow.onCreated(function () {
|
||||
this.builderTrigger = new ReactiveVar(null);
|
||||
this.builderAction = new ReactiveVar(null);
|
||||
@@ -52,24 +65,22 @@ Template.rulesWorkflow.helpers({
|
||||
return Utils.getCurrentBoard();
|
||||
},
|
||||
triggerPalette() {
|
||||
return TRIGGER_PALETTE.map((t, idx) => ({ idx, label: t.label }));
|
||||
return TRIGGER_PALETTE.map((t, idx) => ({ idx, label: paletteLabel(t) }));
|
||||
},
|
||||
actionPalette() {
|
||||
return ACTION_PALETTE.map((a, idx) => ({ idx, label: a.label }));
|
||||
return ACTION_PALETTE.map((a, idx) => ({ idx, label: paletteLabel(a) }));
|
||||
},
|
||||
builderTrigger() {
|
||||
return Template.instance().builderTrigger.get();
|
||||
},
|
||||
builderTriggerLabel() {
|
||||
const t = Template.instance().builderTrigger.get();
|
||||
return t ? t.label : '';
|
||||
return paletteLabel(Template.instance().builderTrigger.get());
|
||||
},
|
||||
builderAction() {
|
||||
return Template.instance().builderAction.get();
|
||||
},
|
||||
builderActionLabel() {
|
||||
const a = Template.instance().builderAction.get();
|
||||
return a ? a.label : '';
|
||||
return paletteLabel(Template.instance().builderAction.get());
|
||||
},
|
||||
createDisabled() {
|
||||
const tpl = Template.instance();
|
||||
@@ -96,9 +107,11 @@ function persistRule(tpl) {
|
||||
if (!t || !a) return;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const titleField = tpl.find('.js-workflow-rule-title');
|
||||
const title = (titleField.value || '').trim() || `${t.label} → ${a.label}`;
|
||||
const triggerId = Triggers.insert({ ...t.doc, boardId, desc: t.label });
|
||||
const actionId = Actions.insert({ ...a.doc, boardId, desc: a.label });
|
||||
const tLabel = paletteLabel(t);
|
||||
const aLabel = paletteLabel(a);
|
||||
const title = (titleField.value || '').trim() || `${tLabel} → ${aLabel}`;
|
||||
const triggerId = Triggers.insert({ ...t.doc, boardId, desc: tLabel });
|
||||
const actionId = Actions.insert({ ...a.doc, boardId, desc: aLabel });
|
||||
Rules.insert({ title, triggerId, actionId, boardId });
|
||||
tpl.builderTrigger.set(null);
|
||||
tpl.builderAction.set(null);
|
||||
@@ -140,7 +153,7 @@ Template.rulesWorkflow.events({
|
||||
const a = ACTION_PALETTE[tpl.dragItem.idx];
|
||||
const boardId = Session.get('currentBoard');
|
||||
const oldActionId = rule.actionId;
|
||||
const newActionId = Actions.insert({ ...a.doc, boardId, desc: a.label });
|
||||
const newActionId = Actions.insert({ ...a.doc, boardId, desc: paletteLabel(a) });
|
||||
Rules.update(ruleId, { $set: { actionId: newActionId } });
|
||||
if (oldActionId) Actions.remove(oldActionId);
|
||||
tpl.dragItem = null;
|
||||
@@ -163,8 +176,9 @@ Template.rulesWorkflow.events({
|
||||
const ruleId = node && node.dataset.ruleId;
|
||||
const rule = ReactiveCache.getRule(ruleId);
|
||||
if (!rule) return;
|
||||
Rules.remove(rule._id);
|
||||
Actions.remove(rule.actionId);
|
||||
Triggers.remove(rule.triggerId);
|
||||
// Delete the rule + its trigger + action server-side (see
|
||||
// server/rulesButton.js `rules.deleteRule`): three client-side
|
||||
// Collection.remove() calls were rejected with 403 "Access denied".
|
||||
Meteor.call('rules.deleteRule', rule._id);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
template(name="adminFeatures")
|
||||
.setting-content
|
||||
unless currentUser.isAdmin
|
||||
| {{_ 'error-notAuthorized'}}
|
||||
else
|
||||
.content-title.ext-box
|
||||
span
|
||||
span.emoji-icon
|
||||
i.fa.fa-flask
|
||||
| {{_ 'features'}}
|
||||
.content-body
|
||||
.side-menu
|
||||
ul
|
||||
li(class="{{#if isPerformanceActive}}active{{/if}}")
|
||||
a.js-features-menu(data-id="performance")
|
||||
span.emoji-icon
|
||||
i.fa.fa-tachometer
|
||||
| {{_ 'features-performance'}}
|
||||
.main-body
|
||||
if isPerformanceActive
|
||||
ul.setting-detail
|
||||
li.layout-form
|
||||
.title {{_ 'cards-loading'}}
|
||||
.form-group
|
||||
select#cards-loading.js-cards-loading
|
||||
each cardsLoadingOptions
|
||||
if this.selected
|
||||
option(value="{{value}}" selected) {{_ label}}
|
||||
else
|
||||
option(value="{{value}}") {{_ label}}
|
||||
p.description {{_ 'cards-loading-description'}}
|
||||
p.description {{_ 'cards-loading-lazy-note'}}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { ReactiveCache } from '/imports/reactiveCache';
|
||||
import { ReactiveVar } from 'meteor/reactive-var';
|
||||
import Settings from '/models/settings';
|
||||
|
||||
// Admin Panel / Features — a home for optional / performance / tier-gated
|
||||
// capabilities. First entry: "Card loading" (all | lazy), which also mirrors the
|
||||
// CARDS_LOADING env var. Grouped by category in the side menu so more feature
|
||||
// toggles (and, later, pricing-tier gating) can be added here.
|
||||
Template.adminFeatures.onCreated(function () {
|
||||
this.activePane = new ReactiveVar('performance');
|
||||
Meteor.subscribe('setting');
|
||||
});
|
||||
|
||||
Template.adminFeatures.helpers({
|
||||
isPerformanceActive() {
|
||||
return Template.instance().activePane.get() === 'performance';
|
||||
},
|
||||
cardsLoadingOptions() {
|
||||
const current = (ReactiveCache.getCurrentSetting() || {}).cardsLoading || 'all';
|
||||
return [
|
||||
{ value: 'all', label: 'cards-loading-all', selected: current === 'all' },
|
||||
{ value: 'lazy', label: 'cards-loading-lazy', selected: current === 'lazy' },
|
||||
];
|
||||
},
|
||||
});
|
||||
|
||||
Template.adminFeatures.events({
|
||||
'click .js-features-menu'(event, tpl) {
|
||||
tpl.activePane.set(event.currentTarget.dataset.id);
|
||||
},
|
||||
'change .js-cards-loading'(event) {
|
||||
const value = event.currentTarget.value === 'lazy' ? 'lazy' : 'all';
|
||||
const setting = ReactiveCache.getCurrentSetting();
|
||||
if (setting) {
|
||||
Settings.update(setting._id, { $set: { cardsLoading: value } });
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -51,6 +51,16 @@ template(name="attachments")
|
||||
span.emoji-icon
|
||||
i.fa.fa-cloud
|
||||
| {{_ 'gcs-storage'}}
|
||||
li(class="{{#if isDatabaseMigrationActive}}active{{/if}}")
|
||||
a.js-attachments-menu(data-id="database-migration")
|
||||
span.emoji-icon
|
||||
i.fa.fa-exchange
|
||||
| {{_ 'database-migration'}}
|
||||
li(class="{{#if isBackupActive}}active{{/if}}")
|
||||
a.js-attachments-menu(data-id="backup")
|
||||
span.emoji-icon
|
||||
i.fa.fa-archive
|
||||
| {{_ 'backup'}}
|
||||
|
||||
.main-body
|
||||
if loading.get
|
||||
@@ -475,6 +485,101 @@ template(name="attachments")
|
||||
if cloudTestError.gcs
|
||||
p.form-text.text-danger {{_ 'cloud-connection-failed'}}: {{cloudTestError.gcs}}
|
||||
|
||||
else if isDatabaseMigrationActive
|
||||
ul#attachment-database-migration-setting.setting-detail
|
||||
li
|
||||
h3 {{_ 'database-migration'}}
|
||||
.description {{_ 'database-migration-description'}}
|
||||
.form-group
|
||||
button.js-migrate-to-ferretdb.btn.btn-primary(type="button" disabled=migrateRunning) {{_ 'database-migrate-to-ferretdb'}}
|
||||
button.js-migrate-to-mongodb.btn.btn-primary(type="button" disabled=migrateRunning) {{_ 'database-migrate-to-mongodb'}}
|
||||
if migrateStatus
|
||||
.move-progress-section
|
||||
.move-progress-counter
|
||||
| {{_ 'database-migration-phase'}}: {{migrateStatus.phase}}
|
||||
if migrateStatus.collection
|
||||
| — {{migrateStatus.collection}} ({{migrateStatus.collDone}}/{{migrateStatus.collTotal}}), {{migrateStatus.collectionsDone}}/{{migrateStatus.collectionsTotal}}
|
||||
if migrateStatus.error
|
||||
p.form-text.text-danger {{migrateStatus.error}}
|
||||
if migrateSuccess
|
||||
p.form-text.text-success {{_ 'database-migration-done'}}
|
||||
|
||||
else if isBackupActive
|
||||
ul#attachment-backup-setting.setting-detail
|
||||
li
|
||||
h3 {{_ 'backup'}}
|
||||
.description {{_ 'backup-description'}}
|
||||
.form-group
|
||||
label
|
||||
input.js-backup-attachments(type="checkbox" checked="checked")
|
||||
| {{_ 'attachments'}}
|
||||
label
|
||||
input.js-backup-avatars(type="checkbox" checked="checked")
|
||||
| {{_ 'avatars'}}
|
||||
label
|
||||
input.js-backup-data(type="checkbox" checked="checked")
|
||||
| {{_ 'backup-data'}}
|
||||
.form-group
|
||||
label {{_ 'backup-storage'}}
|
||||
select.wekan-form-control.js-backup-storage
|
||||
option(value="filesystem") {{_ 'filesystem-storage'}}
|
||||
option(value="s3") {{_ 's3-minio-storage'}}
|
||||
option(value="azure") {{_ 'azure-blob-storage'}}
|
||||
option(value="gcs") {{_ 'gcs-storage'}}
|
||||
.form-group
|
||||
button.js-run-backup.btn.btn-primary(type="button" disabled=backupRunning) {{_ 'backup-now'}}
|
||||
if backupStatus
|
||||
p.form-text {{_ 'database-migration-phase'}}: {{backupStatus.phase}} {{backupStatus.detail}}
|
||||
if backupStatus.error
|
||||
p.form-text.text-danger {{backupStatus.error}}
|
||||
if backupSuccess
|
||||
p.form-text.text-success {{_ 'backup-done'}}: {{backupStatus.file}}
|
||||
|
||||
h4 {{_ 'backup-schedule'}}
|
||||
.form-group
|
||||
label {{_ 'backup-frequency'}}
|
||||
select.wekan-form-control.js-backup-frequency
|
||||
option(value="off" selected=scheduleOff) {{_ 'backup-frequency-off'}}
|
||||
option(value="daily" selected=scheduleDaily) {{_ 'backup-frequency-daily'}}
|
||||
option(value="weekly" selected=scheduleWeekly) {{_ 'backup-frequency-weekly'}}
|
||||
option(value="monthly" selected=scheduleMonthly) {{_ 'backup-frequency-monthly'}}
|
||||
.form-group
|
||||
label {{_ 'backup-time'}}
|
||||
input.wekan-form-control.js-backup-time(type="time" value=scheduleTime)
|
||||
.form-group
|
||||
label {{_ 'backup-day-of-week'}}
|
||||
input.wekan-form-control.js-backup-dow(type="text" value=scheduleDow placeholder="Sunday")
|
||||
.form-group
|
||||
label {{_ 'backup-day-of-month'}}
|
||||
input.wekan-form-control.js-backup-dom(type="number" min="1" max="28" value=scheduleDom)
|
||||
.form-group
|
||||
button.js-save-backup-schedule.btn.btn-primary(type="button") {{_ 'save'}}
|
||||
|
||||
h4 {{_ 'backup-restore'}}
|
||||
.form-group
|
||||
button.js-list-backups.btn.btn-secondary(type="button") {{_ 'backup-list'}}
|
||||
if backupList
|
||||
table.gridfs-stats-table
|
||||
tr
|
||||
th
|
||||
th {{_ 'backup-storage'}}
|
||||
th {{_ 'backup-datetime'}}
|
||||
th {{_ 'backup-path'}}
|
||||
each backupList
|
||||
tr
|
||||
td
|
||||
input.js-backup-select(type="radio" name="backup-select" value=path)
|
||||
td {{storage}}
|
||||
td {{datetime}}
|
||||
td {{path}}
|
||||
.form-group
|
||||
label {{_ 'backup-restore-mode'}}
|
||||
select.wekan-form-control.js-restore-mode
|
||||
option(value="add-missing") {{_ 'backup-restore-add-missing'}}
|
||||
option(value="replace-all") {{_ 'backup-restore-replace-all'}}
|
||||
.form-group
|
||||
button.js-restore-backup.btn.btn-warning(type="button" disabled=backupRunning) {{_ 'backup-restore'}}
|
||||
|
||||
template(name="moveAttachments")
|
||||
.move-storage-form
|
||||
.move-storage-selects
|
||||
|
||||
@@ -267,6 +267,14 @@ Template.attachments.onCreated(function () {
|
||||
this.cloudTestResults = new ReactiveVar({});
|
||||
this.cloudTestErrors = new ReactiveVar({});
|
||||
this.loading = new ReactiveVar(false);
|
||||
this.migrateStatus = new ReactiveVar(null);
|
||||
this.migratePoll = null;
|
||||
this.backupStatus = new ReactiveVar(null);
|
||||
this.backupList = new ReactiveVar(null);
|
||||
this.backupSchedule = new ReactiveVar(null);
|
||||
this.selectedBackup = new ReactiveVar('');
|
||||
this.backupPoll = null;
|
||||
Meteor.call('getBackupSchedule', (err, s) => { if (!err && s) this.backupSchedule.set(s); });
|
||||
|
||||
this.autorun(() => {
|
||||
const ready = this.storageSettingsSubscription.ready();
|
||||
@@ -279,6 +287,54 @@ Template.attachments.onCreated(function () {
|
||||
});
|
||||
});
|
||||
|
||||
Template.attachments.onDestroyed(function () {
|
||||
if (this.migratePoll) {
|
||||
Meteor.clearTimeout(this.migratePoll);
|
||||
this.migratePoll = null;
|
||||
}
|
||||
if (this.backupPoll) {
|
||||
Meteor.clearTimeout(this.backupPoll);
|
||||
this.backupPoll = null;
|
||||
}
|
||||
});
|
||||
|
||||
// Text-data database migration (MongoDB <-> FerretDB v1 SQLite) helpers.
|
||||
function pollMigrateStatus(tpl) {
|
||||
Meteor.call('migrateTextDatabaseStatus', (err, status) => {
|
||||
if (err || !status) return;
|
||||
tpl.migrateStatus.set(status);
|
||||
if (status.running) {
|
||||
tpl.migratePoll = Meteor.setTimeout(() => pollMigrateStatus(tpl), 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function startDbMigration(tpl, direction) {
|
||||
const which = direction === 'toFerretDB' ? 'FerretDB v1 (SQLite)' : 'MongoDB';
|
||||
// eslint-disable-next-line no-alert
|
||||
if (!window.confirm(TAPi18n.__('database-migration-confirm', { db: which }))) {
|
||||
return;
|
||||
}
|
||||
tpl.migrateStatus.set({ running: true, phase: 'starting', direction });
|
||||
Meteor.call('migrateTextDatabase', direction, error => {
|
||||
if (error) {
|
||||
tpl.migrateStatus.set({ phase: 'error', error: error.reason || error.message, success: false });
|
||||
} else {
|
||||
pollMigrateStatus(tpl);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function pollBackupStatus(tpl) {
|
||||
Meteor.call('backupStatus', (err, status) => {
|
||||
if (err || !status) return;
|
||||
tpl.backupStatus.set(status);
|
||||
if (status.running) {
|
||||
tpl.backupPoll = Meteor.setTimeout(() => pollBackupStatus(tpl), 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Template.attachments.helpers({
|
||||
loading() {
|
||||
return Template.instance().loading;
|
||||
@@ -298,6 +354,44 @@ Template.attachments.helpers({
|
||||
isGcsActive() {
|
||||
return Template.instance().activeSection.get() === 'gcs';
|
||||
},
|
||||
isDatabaseMigrationActive() {
|
||||
return Template.instance().activeSection.get() === 'database-migration';
|
||||
},
|
||||
migrateStatus() {
|
||||
return Template.instance().migrateStatus.get();
|
||||
},
|
||||
migrateRunning() {
|
||||
const s = Template.instance().migrateStatus.get();
|
||||
return !!(s && s.running);
|
||||
},
|
||||
migrateSuccess() {
|
||||
const s = Template.instance().migrateStatus.get();
|
||||
return !!(s && s.success === true);
|
||||
},
|
||||
isBackupActive() {
|
||||
return Template.instance().activeSection.get() === 'backup';
|
||||
},
|
||||
backupStatus() {
|
||||
return Template.instance().backupStatus.get();
|
||||
},
|
||||
backupRunning() {
|
||||
const s = Template.instance().backupStatus.get();
|
||||
return !!(s && s.running);
|
||||
},
|
||||
backupSuccess() {
|
||||
const s = Template.instance().backupStatus.get();
|
||||
return !!(s && s.success === true && s.phase === 'completed');
|
||||
},
|
||||
backupList() {
|
||||
return Template.instance().backupList.get();
|
||||
},
|
||||
scheduleOff() { const s = Template.instance().backupSchedule.get(); return !s || !s.frequency || s.frequency === 'off'; },
|
||||
scheduleDaily() { const s = Template.instance().backupSchedule.get(); return !!(s && s.frequency === 'daily'); },
|
||||
scheduleWeekly() { const s = Template.instance().backupSchedule.get(); return !!(s && s.frequency === 'weekly'); },
|
||||
scheduleMonthly() { const s = Template.instance().backupSchedule.get(); return !!(s && s.frequency === 'monthly'); },
|
||||
scheduleTime() { const s = Template.instance().backupSchedule.get(); return (s && s.time) || '04:00'; },
|
||||
scheduleDow() { const s = Template.instance().backupSchedule.get(); return (s && s.dayOfWeek) || 'Sunday'; },
|
||||
scheduleDom() { const s = Template.instance().backupSchedule.get(); return (s && s.dayOfMonth) || 1; },
|
||||
avatarsUploadBlocked() {
|
||||
const settings = Template.instance().attachmentStorageSettings.get();
|
||||
return settings?.limitSettings?.avatarsUploadBlocked === true;
|
||||
@@ -561,6 +655,66 @@ Template.attachments.events({
|
||||
|
||||
tpl.activeSection.set(targetID);
|
||||
},
|
||||
'click .js-migrate-to-ferretdb'(event, tpl) {
|
||||
event.preventDefault();
|
||||
startDbMigration(tpl, 'toFerretDB');
|
||||
},
|
||||
'click .js-migrate-to-mongodb'(event, tpl) {
|
||||
event.preventDefault();
|
||||
startDbMigration(tpl, 'toMongoDB');
|
||||
},
|
||||
'click .js-run-backup'(event, tpl) {
|
||||
event.preventDefault();
|
||||
const opts = {
|
||||
attachments: tpl.$('.js-backup-attachments').is(':checked'),
|
||||
avatars: tpl.$('.js-backup-avatars').is(':checked'),
|
||||
data: tpl.$('.js-backup-data').is(':checked'),
|
||||
};
|
||||
const storage = tpl.$('.js-backup-storage').val() || 'filesystem';
|
||||
tpl.backupStatus.set({ running: true, phase: 'starting' });
|
||||
Meteor.call('runBackup', opts, storage, error => {
|
||||
if (error) tpl.backupStatus.set({ phase: 'error', error: error.reason || error.message, success: false });
|
||||
else pollBackupStatus(tpl);
|
||||
});
|
||||
},
|
||||
'click .js-save-backup-schedule'(event, tpl) {
|
||||
event.preventDefault();
|
||||
const schedule = {
|
||||
enabled: (tpl.$('.js-backup-frequency').val() || 'off') !== 'off',
|
||||
frequency: tpl.$('.js-backup-frequency').val() || 'off',
|
||||
time: tpl.$('.js-backup-time').val() || '04:00',
|
||||
dayOfWeek: tpl.$('.js-backup-dow').val() || 'Sunday',
|
||||
dayOfMonth: parseInt(tpl.$('.js-backup-dom').val(), 10) || 1,
|
||||
attachments: tpl.$('.js-backup-attachments').is(':checked'),
|
||||
avatars: tpl.$('.js-backup-avatars').is(':checked'),
|
||||
data: tpl.$('.js-backup-data').is(':checked'),
|
||||
storage: tpl.$('.js-backup-storage').val() || 'filesystem',
|
||||
};
|
||||
Meteor.call('saveBackupSchedule', schedule, (error, saved) => {
|
||||
if (!error && saved) tpl.backupSchedule.set(saved);
|
||||
});
|
||||
},
|
||||
'click .js-list-backups'(event, tpl) {
|
||||
event.preventDefault();
|
||||
Meteor.call('listBackups', (error, list) => {
|
||||
if (!error) tpl.backupList.set(list || []);
|
||||
});
|
||||
},
|
||||
'change .js-backup-select'(event, tpl) {
|
||||
tpl.selectedBackup.set($(event.currentTarget).val());
|
||||
},
|
||||
'click .js-restore-backup'(event, tpl) {
|
||||
event.preventDefault();
|
||||
const zipPath = tpl.selectedBackup.get();
|
||||
if (!zipPath) { window.alert(TAPi18n.__('backup-restore-select-first')); return; }
|
||||
const mode = tpl.$('.js-restore-mode').val() || 'add-missing';
|
||||
if (!window.confirm(TAPi18n.__('backup-restore-confirm'))) return;
|
||||
tpl.backupStatus.set({ running: true, phase: 'restore' });
|
||||
Meteor.call('restoreBackup', zipPath, mode, error => {
|
||||
if (error) tpl.backupStatus.set({ phase: 'error', error: error.reason || error.message, success: false });
|
||||
else pollBackupStatus(tpl);
|
||||
});
|
||||
},
|
||||
'change select.js-attachment-limit-unit'(event, tpl) {
|
||||
const fieldName = event.currentTarget.dataset.field;
|
||||
const selectedUnit = event.currentTarget.value;
|
||||
|
||||
@@ -29,15 +29,31 @@ template(name='statistics')
|
||||
tr
|
||||
th {{_ 'Node_version'}}
|
||||
td {{statistics.process.nodeVersion}}
|
||||
tr
|
||||
th {{_ 'Database_type'}}
|
||||
td {{statistics.mongo.databaseType}}
|
||||
tr
|
||||
th {{_ 'MongoDB_version'}}
|
||||
td {{statistics.mongo.mongoVersion}}
|
||||
tr
|
||||
th {{_ 'Database_commit'}}
|
||||
td {{statistics.mongo.databaseCommit}}
|
||||
if statistics.mongo.ferretdbVersion
|
||||
tr
|
||||
th {{_ 'FerretDB_version'}}
|
||||
td {{statistics.mongo.ferretdbVersion}}
|
||||
tr
|
||||
th {{_ 'FerretDB_commit'}}
|
||||
td {{statistics.mongo.ferretdbCommit}}
|
||||
tr
|
||||
th {{_ 'MongoDB_storage_engine'}}
|
||||
td {{statistics.mongo.mongoStorageEngine}}
|
||||
tr
|
||||
th {{_ 'MongoDB_Oplog_enabled'}}
|
||||
td {{formatBoolean statistics.mongo.mongoOplogEnabled}}
|
||||
tr
|
||||
th {{_ 'Reactivity_mode'}}
|
||||
td {{statistics.mongo.reactivity}}
|
||||
tr
|
||||
th {{_ 'OS_Type'}}
|
||||
td {{statistics.os.type}}
|
||||
|
||||
@@ -378,3 +378,62 @@ table td:first-child {
|
||||
.roles-settings .roles-description {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
/* Admin Panel > People > Domains table: search + column sort + pagination,
|
||||
matching the Board Table view / other admin tables. */
|
||||
.domain-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.domain-controls input.js-domain-search {
|
||||
flex: 1 1 auto;
|
||||
height: 30px;
|
||||
padding: 0 8px;
|
||||
border: 1px solid #bbb;
|
||||
border-radius: 0.4vw;
|
||||
}
|
||||
|
||||
.domain-controls button.js-domain-search-button {
|
||||
height: 30px;
|
||||
padding: 0 12px;
|
||||
border: 1px solid #bbb;
|
||||
background: #fff;
|
||||
border-radius: 0.4vw;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.domain-pagination {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.domain-pagination button {
|
||||
min-width: 32px;
|
||||
height: 30px;
|
||||
padding: 0 10px;
|
||||
border: 1px solid #bbb;
|
||||
background: #fff;
|
||||
border-radius: 0.4vw;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.domain-pagination button.disabled {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.domain-page-info {
|
||||
min-width: 72px;
|
||||
text-align: center;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
th.js-domain-sort {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
@@ -122,7 +122,7 @@ template(name="people")
|
||||
else if templatesSetting.get
|
||||
+templatesGeneral
|
||||
else if domainSetting.get
|
||||
+domainGeneral(domainList=domainList)
|
||||
+domainGeneral
|
||||
|
||||
|
||||
template(name="orgGeneral")
|
||||
@@ -239,11 +239,18 @@ template(name="selectAllUser")
|
||||
input.allUserChkBox(type="checkbox", id="chkSelectAll")
|
||||
|
||||
template(name="domainGeneral")
|
||||
.domain-controls
|
||||
input.js-domain-search(type="text" placeholder="{{_ 'search'}}" aria-label="{{_ 'search'}}")
|
||||
button.js-domain-search-button(type="button") {{_ 'search'}}
|
||||
.domain-pagination
|
||||
button.js-domain-prev-page(type="button" class="{{#unless hasPrevPage}}disabled{{/unless}}" aria-label="{{_ 'previous'}}") {{_ 'previous'}}
|
||||
span.domain-page-info {{currentPage}} / {{totalPages}}
|
||||
button.js-domain-next-page(type="button" class="{{#unless hasNextPage}}disabled{{/unless}}" aria-label="{{_ 'next'}}") {{_ 'next'}}
|
||||
table
|
||||
thead
|
||||
tr
|
||||
th {{_ 'domain'}}
|
||||
th {{_ 'domain-user-count'}}
|
||||
th.js-domain-sort(scope="col" data-sort="domain") {{_ 'domain'}} {{domainSortIndicator 'domain'}}
|
||||
th.js-domain-sort(scope="col" data-sort="count") {{_ 'domain-user-count'}} {{domainSortIndicator 'count'}}
|
||||
tbody
|
||||
if domainList.length
|
||||
each domainList
|
||||
|
||||
@@ -14,6 +14,7 @@ import InviteToBoardRolesSettings, {
|
||||
const orgsPerPage = 25;
|
||||
const teamsPerPage = 25;
|
||||
const usersPerPage = 25;
|
||||
const domainsPerPage = 25;
|
||||
let userOrgsTeamsAction = ""; //poosible actions 'addOrg', 'addTeam', 'removeOrg' or 'removeTeam' when adding or modifying a user
|
||||
let selectedUserChkBoxUserIds = [];
|
||||
|
||||
@@ -28,9 +29,9 @@ Template.people.onCreated(function () {
|
||||
this.lockedUsersSetting = new ReactiveVar(false);
|
||||
this.rolesSetting = new ReactiveVar(false);
|
||||
this.templatesSetting = new ReactiveVar(false);
|
||||
// #5850: Admin Panel > People > Domains tab.
|
||||
// #5850: Admin Panel > People > Domains tab. The domains table itself
|
||||
// (domainGeneral) now owns its data via getDomainsWithUserCountsPage.
|
||||
this.domainSetting = new ReactiveVar(false);
|
||||
this.domains = new ReactiveVar([]);
|
||||
this.subscribe('inviteToBoardRolesSettings');
|
||||
this.findOrgsOptions = new ReactiveVar({});
|
||||
this.findTeamsOptions = new ReactiveVar({});
|
||||
@@ -205,12 +206,9 @@ Template.people.onCreated(function () {
|
||||
this.templatesSetting.set('templates-setting' === targetID);
|
||||
this.domainSetting.set('domains-setting' === targetID);
|
||||
|
||||
// #5850: load the domains + per-domain user counts when the tab opens.
|
||||
if ('domains-setting' === targetID) {
|
||||
Meteor.call('getDomainsWithUserCounts', (err, res) => {
|
||||
if (!err) this.domains.set(res || []);
|
||||
});
|
||||
}
|
||||
// The Domains table (domainGeneral) now fetches its own single page from
|
||||
// getDomainsWithUserCountsPage (server-side search / sort / pagination), so
|
||||
// the parent no longer eagerly loads every domain into the browser.
|
||||
|
||||
// When switching to locked users tab, refresh the locked users list
|
||||
if ('locked-users-setting' === targetID) {
|
||||
@@ -292,9 +290,6 @@ Template.people.helpers({
|
||||
domainSetting() {
|
||||
return Template.instance().domainSetting;
|
||||
},
|
||||
domainList() {
|
||||
return Template.instance().domains.get();
|
||||
},
|
||||
orgList() {
|
||||
const tpl = Template.instance();
|
||||
// The 'org' publication already returns only the current page (server-side
|
||||
@@ -1897,3 +1892,96 @@ Template.settingsUserPopup.helpers({
|
||||
return Template.instance().errorMessage.get();
|
||||
},
|
||||
});
|
||||
|
||||
// Admin Panel > People > Domains table. Self-contained (like the Board Table
|
||||
// view): it keeps its own search / sort / page state and fetches only ONE page
|
||||
// from the server method getDomainsWithUserCountsPage, so the whole domain list
|
||||
// is never loaded into the browser.
|
||||
Template.domainGeneral.onCreated(function () {
|
||||
this.searchQuery = new ReactiveVar('');
|
||||
this.sortField = new ReactiveVar('domain'); // domain | count
|
||||
this.sortDirection = new ReactiveVar(1); // 1 ascending, -1 descending
|
||||
this.page = new ReactiveVar(1);
|
||||
this.pageData = new ReactiveVar({ rows: [], total: 0, totalPages: 1 });
|
||||
|
||||
this.autorun(() => {
|
||||
const params = {
|
||||
search: this.searchQuery.get(),
|
||||
sortField: this.sortField.get(),
|
||||
sortDirection: this.sortDirection.get(),
|
||||
page: this.page.get(),
|
||||
perPage: domainsPerPage,
|
||||
};
|
||||
Meteor.call('getDomainsWithUserCountsPage', params, (err, res) => {
|
||||
if (!err && res) {
|
||||
this.pageData.set(res);
|
||||
// Server clamps the page into range; mirror that so the controls agree.
|
||||
if (typeof res.page === 'number' && res.page !== this.page.get()) {
|
||||
this.page.set(res.page);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Template.domainGeneral.helpers({
|
||||
domainList() {
|
||||
return Template.instance().pageData.get().rows;
|
||||
},
|
||||
currentPage() {
|
||||
return Template.instance().page.get();
|
||||
},
|
||||
totalPages() {
|
||||
return Template.instance().pageData.get().totalPages || 1;
|
||||
},
|
||||
hasPrevPage() {
|
||||
return Template.instance().page.get() > 1;
|
||||
},
|
||||
hasNextPage() {
|
||||
const tpl = Template.instance();
|
||||
return tpl.page.get() < (tpl.pageData.get().totalPages || 1);
|
||||
},
|
||||
// Excel-like sort arrow on the active column, matching the Board Table view.
|
||||
domainSortIndicator(field) {
|
||||
const tpl = Template.instance();
|
||||
if (tpl.sortField.get() !== field) return '';
|
||||
return tpl.sortDirection.get() === 1 ? '▲' : '▼';
|
||||
},
|
||||
});
|
||||
|
||||
Template.domainGeneral.events({
|
||||
'click .js-domain-search-button'(event, tpl) {
|
||||
event.preventDefault();
|
||||
tpl.searchQuery.set(tpl.$('.js-domain-search').val() || '');
|
||||
tpl.page.set(1);
|
||||
},
|
||||
'keydown .js-domain-search'(event, tpl) {
|
||||
if (event.keyCode === 13) {
|
||||
event.preventDefault();
|
||||
tpl.searchQuery.set(tpl.$('.js-domain-search').val() || '');
|
||||
tpl.page.set(1);
|
||||
}
|
||||
},
|
||||
'click .js-domain-prev-page'(event, tpl) {
|
||||
event.preventDefault();
|
||||
const current = tpl.page.get();
|
||||
if (current > 1) tpl.page.set(current - 1);
|
||||
},
|
||||
'click .js-domain-next-page'(event, tpl) {
|
||||
event.preventDefault();
|
||||
const current = tpl.page.get();
|
||||
if (current < (tpl.pageData.get().totalPages || 1)) tpl.page.set(current + 1);
|
||||
},
|
||||
'click .js-domain-sort'(event, tpl) {
|
||||
event.preventDefault();
|
||||
const field = event.currentTarget.dataset.sort;
|
||||
if (!field) return;
|
||||
if (tpl.sortField.get() === field) {
|
||||
tpl.sortDirection.set(tpl.sortDirection.get() * -1);
|
||||
} else {
|
||||
tpl.sortField.set(field);
|
||||
tpl.sortDirection.set(1);
|
||||
}
|
||||
tpl.page.set(1);
|
||||
},
|
||||
});
|
||||
@@ -6,6 +6,7 @@ import AccessibilitySettings from '/models/accessibilitySettings';
|
||||
import AccountSettings from '/models/accountSettings';
|
||||
import Announcements from '/models/announcements';
|
||||
import Settings from '/models/settings';
|
||||
import { resolveDefaultAuthenticationMethod } from '/models/lib/authenticationMethod';
|
||||
import TableVisibilityModeSettings from '/models/tableVisibilityModeSettings';
|
||||
// import {
|
||||
// cronMigrationProgress,
|
||||
@@ -803,7 +804,17 @@ Template.setting.events({
|
||||
$('input[name=hideBoardMemberList]:checked').val() === 'true';
|
||||
const displayAuthenticationMethod =
|
||||
$('input[name=displayAuthenticationMethod]:checked').val() === 'true';
|
||||
const defaultAuthenticationMethod = $('#defaultAuthenticationMethod').val();
|
||||
// #5879: the <select> options are populated by an async Meteor.call, so its
|
||||
// value can still be '' / null when Save is clicked. Saving that empty value
|
||||
// over the required `defaultAuthenticationMethod` string silently failed
|
||||
// validation, so the Layout save looked like it hung / did nothing. Fall back
|
||||
// to the currently stored method so a real value is never overwritten by ''.
|
||||
const currentDefaultAuthenticationMethod =
|
||||
ReactiveCache.getCurrentSetting()?.defaultAuthenticationMethod;
|
||||
const defaultAuthenticationMethod = resolveDefaultAuthenticationMethod(
|
||||
$('#defaultAuthenticationMethod').val(),
|
||||
currentDefaultAuthenticationMethod,
|
||||
);
|
||||
const spinnerName = ($('#spinnerName').val() || '').trim();
|
||||
|
||||
try {
|
||||
|
||||
@@ -14,6 +14,11 @@ template(name="settingHeaderBar")
|
||||
i.fa.fa-users
|
||||
span {{_ 'people'}}
|
||||
|
||||
a.setting-header-btn.informations(class=isFeaturesActive href="{{pathFor 'admin-features'}}")
|
||||
span.emoji-icon
|
||||
i.fa.fa-flask
|
||||
span {{_ 'features'}}
|
||||
|
||||
a.setting-header-btn.informations(class=isAdminReportsActive href="{{pathFor 'admin-reports'}}")
|
||||
span.emoji-icon
|
||||
i.fa.fa-file-text-o
|
||||
|
||||
@@ -7,6 +7,9 @@ Template.settingHeaderBar.helpers({
|
||||
isPeopleActive() {
|
||||
return FlowRouter.getRouteName() === 'people' ? 'active' : '';
|
||||
},
|
||||
isFeaturesActive() {
|
||||
return FlowRouter.getRouteName() === 'admin-features' ? 'active' : '';
|
||||
},
|
||||
isAdminReportsActive() {
|
||||
return FlowRouter.getRouteName() === 'admin-reports' ? 'active' : '';
|
||||
},
|
||||
|
||||
@@ -55,9 +55,14 @@ template(name='homeSidebar')
|
||||
i.fa.fa-comment-o
|
||||
| {{_ 'activities'}}
|
||||
|
||||
a.flex.js-toggle-show-activities(title="{{_ 'show-activities'}}")
|
||||
i.fa(class="{{#if showActivities}}fa-check{{else}}fa-square-o{{/if}}")
|
||||
span {{_ 'show-activities'}}
|
||||
if showActivities
|
||||
a.flex.activities-eye-toggle.js-toggle-show-activities(title="{{_ 'hide-activities'}}" aria-label="{{_ 'hide-activities'}}")
|
||||
i.fa.fa-eye.eye-icon
|
||||
span {{_ 'hide-activities'}}
|
||||
else
|
||||
a.flex.activities-eye-toggle.js-toggle-show-activities(title="{{_ 'show-activities'}}" aria-label="{{_ 'show-activities'}}")
|
||||
i.fa.fa-eye-slash.eye-slash-icon
|
||||
span {{_ 'show-activities'}}
|
||||
+activities(mode="board")
|
||||
|
||||
template(name="membersWidget")
|
||||
@@ -823,7 +828,7 @@ template(name="labelsWidget")
|
||||
| {{_ 'labels'}}
|
||||
.board-widget-content
|
||||
each currentBoard.labels
|
||||
a.card-label(class="card-label-{{color}}"
|
||||
a.card-label(class="{{labelColorClass color}}" style="{{labelColorStyle color}}"
|
||||
class="{{#if currentUser.isNotCommentOnly}}{{#if currentUser.isNotWorker}}js-label{{/if}}{{/if}}")
|
||||
span.card-label-name
|
||||
+viewer
|
||||
|
||||
@@ -29,7 +29,7 @@ template(name="filterSidebar")
|
||||
each currentBoard.labels
|
||||
li
|
||||
a.name.js-toggle-label-filter(data-filter-id="{{_id}}")
|
||||
span.card-label.square(class="card-label-{{color}}")
|
||||
span.card-label.square(class="{{labelColorClass color}}" style="{{labelColorStyle color}}")
|
||||
span.sidebar-list-item-description
|
||||
if name
|
||||
= name
|
||||
@@ -188,7 +188,7 @@ template(name="multiselectionSidebar")
|
||||
each currentBoard.labels
|
||||
li
|
||||
a.name.js-toggle-label-multiselection
|
||||
span.card-label.square(class="card-label-{{color}}")
|
||||
span.card-label.square(class="{{labelColorClass color}}" style="{{labelColorStyle color}}")
|
||||
span.sidebar-list-item-description
|
||||
if name
|
||||
= name
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
template(name="swimlaneHeader")
|
||||
.swimlane-header-wrap.js-swimlane-header(class=colorClass title="{{_ 'rename'}}")
|
||||
.swimlane-header-wrap.js-swimlane-header(class=colorClass style=colorStyle title="{{_ 'rename'}}")
|
||||
if this.isTemplateContainer
|
||||
+swimlaneFixedHeader(this)
|
||||
else
|
||||
@@ -120,6 +120,10 @@ template(name="setSwimlaneColorPopup")
|
||||
span.card-label.palette-color.js-palette-color(class="card-details-{{color}}")
|
||||
if(isSelected color)
|
||||
i.fa.fa-check
|
||||
// #5514: native color wheel for picking a custom RGB / hex color
|
||||
.flush-left.color-wheel-wrap
|
||||
label(for="swimlaneColorWheel") {{_ 'custom-color'}}
|
||||
input.js-swimlane-color-wheel#swimlaneColorWheel(type="color" value=currentColorHex title="{{_ 'custom-color'}}")
|
||||
// Buttons aligned left too
|
||||
.flush-left
|
||||
button.primary.confirm.js-submit(style="margin-inline-start:0") {{_ 'save'}}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { TAPi18n } from '/imports/i18n';
|
||||
import { ReactiveCache } from '/imports/reactiveCache';
|
||||
import { SWIMLANE_COLORS } from '/models/metadata/colors';
|
||||
import { isHexColor, toHex } from '/models/lib/contrastColor';
|
||||
import Swimlanes from '/models/swimlanes';
|
||||
import { Utils } from '/client/lib/utils';
|
||||
const { calculateIndexData } = Utils;
|
||||
@@ -166,6 +167,10 @@ Template.setSwimlaneColorPopup.helpers({
|
||||
isSelected(color) {
|
||||
return Template.instance().currentColor.get() === color;
|
||||
},
|
||||
// #5514: current color as a '#rrggbb' hex for the native color wheel <input>.
|
||||
currentColorHex() {
|
||||
return toHex(Template.instance().currentColor.get()) || '#0079bf';
|
||||
},
|
||||
});
|
||||
|
||||
Template.setSwimlaneColorPopup.events({
|
||||
@@ -178,6 +183,13 @@ Template.setSwimlaneColorPopup.events({
|
||||
const paletteData = Blaze.getData(event.currentTarget);
|
||||
tpl.currentColor.set(paletteData?.color);
|
||||
},
|
||||
// #5514: picking from the native color wheel stores a custom hex.
|
||||
'input .js-swimlane-color-wheel'(event, tpl) {
|
||||
const value = event.currentTarget.value;
|
||||
if (isHexColor(value)) {
|
||||
tpl.currentColor.set(value);
|
||||
}
|
||||
},
|
||||
async 'click .js-submit'(event, tpl) {
|
||||
event.preventDefault();
|
||||
await tpl.currentSwimlane.setColor(tpl.currentColor.get());
|
||||
|
||||
@@ -98,6 +98,19 @@ body.mobile-mode .auth-layout .auth-dialog .at-form .password-input-container .p
|
||||
margin-top: 2vh;
|
||||
color: #4d4d4d;
|
||||
}
|
||||
/* #4394 (security): when registration / forgot-password are disabled in the
|
||||
Admin Panel, keep the corresponding links hidden even after useraccounts
|
||||
re-renders the form on a failed login. The class lives on the stable <body>
|
||||
ancestor (set in layouts.js), so this rule re-hides every recreated copy of
|
||||
the link — unlike a one-shot inline display:none that the re-render dropped. */
|
||||
body.registration-disabled .at-signup-link,
|
||||
body.registration-disabled .at-signUp {
|
||||
display: none !important;
|
||||
}
|
||||
body.forgotpwd-disabled .at-pwd-link,
|
||||
body.forgotpwd-disabled .at-forgotPwd {
|
||||
display: none !important;
|
||||
}
|
||||
.auth-layout .auth-dialog .at-form .at-signup-link .at-signUp,
|
||||
.auth-layout .auth-dialog .at-form .at-signin-link .at-signUp,
|
||||
.auth-layout .auth-dialog .at-form .at-forgotPwd .at-signUp,
|
||||
|
||||
@@ -29,6 +29,27 @@ import {
|
||||
calendar,
|
||||
} from '/imports/lib/dateUtils';
|
||||
import { Utils } from '/client/lib/utils';
|
||||
import { isHexColor, contrastText } from '/models/lib/contrastColor';
|
||||
|
||||
// #5514: a color (label / list / swimlane / card) may be either a named palette
|
||||
// color rendered via a CSS class (e.g. `card-label-green`) or a custom
|
||||
// '#rrggbb' hex chosen from the color wheel. For a hex value there is no CSS
|
||||
// class, so we emit an inline `background-color` plus an automatically readable
|
||||
// text `color` (contrastText). These helpers let templates keep the class path
|
||||
// for named colors and switch to inline styles for hex.
|
||||
|
||||
// Class for a label chip: `card-label-<name>` for named colors, empty for hex.
|
||||
Blaze.registerHelper('labelColorClass', color =>
|
||||
isHexColor(color) ? '' : `card-label-${color}`,
|
||||
);
|
||||
|
||||
// Inline style for a label chip: background + readable text for a hex color,
|
||||
// empty for a named color (its CSS class already sets both).
|
||||
Blaze.registerHelper('labelColorStyle', color =>
|
||||
isHexColor(color)
|
||||
? `background-color:${color};color:${contrastText(color)};`
|
||||
: '',
|
||||
);
|
||||
|
||||
Blaze.registerHelper('currentBoard', () => {
|
||||
const ret = Utils.getCurrentBoard();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import '/client/components/settings/adminFeatures.jade';
|
||||
import '/client/components/settings/adminReports.jade';
|
||||
import '/client/components/settings/attachments.jade';
|
||||
import '/client/components/settings/attachmentSettings.jade';
|
||||
@@ -11,6 +12,7 @@ import '/client/components/settings/settingBody.jade';
|
||||
import '/client/components/settings/settingHeader.jade';
|
||||
import '/client/components/settings/translationBody.jade';
|
||||
|
||||
import '/client/components/settings/adminFeatures.js';
|
||||
import '/client/components/settings/adminReports.js';
|
||||
import '/client/components/settings/attachments.js';
|
||||
import '/client/components/settings/connectionMethod.js';
|
||||
|
||||
@@ -18,6 +18,84 @@ window.ExportHtml = Popup => {
|
||||
}
|
||||
};
|
||||
|
||||
// The board is rendered with infinite scroll — each list only keeps ~10 cards
|
||||
// in the DOM (client/components/lists/listBody.js). A naive DOM-clone export
|
||||
// would therefore drop every card past the fold. Before cloning we lift every
|
||||
// list's `cardlimit` so the whole board renders, then wait for Blaze to flush.
|
||||
const expandAllCardsForExport = async () => {
|
||||
let changed = false;
|
||||
Array.from(document.querySelectorAll('.list-body')).forEach(el => {
|
||||
try {
|
||||
let view = Blaze.getView(el);
|
||||
while (view) {
|
||||
if (view.template && view.template.viewName === 'Template.listBody' && view.templateInstance) {
|
||||
const inst = view.templateInstance();
|
||||
if (inst && inst.cardlimit && inst.cardlimit.get() < 1e6) {
|
||||
inst.cardlimit.set(1e6); // render all cards in this list
|
||||
changed = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
view = view.parentView;
|
||||
}
|
||||
} catch (e) { /* best effort per list */ }
|
||||
});
|
||||
if (changed) {
|
||||
// Let the reactive re-render settle before we snapshot the DOM.
|
||||
await new Promise(resolve => Tracker.afterFlush(() => setTimeout(resolve, 800)));
|
||||
}
|
||||
};
|
||||
|
||||
// Write the zip to disk WITHOUT holding the whole archive in memory. When the
|
||||
// browser supports the File System Access API we pipe JSZip's internal stream
|
||||
// straight to the chosen file, chunk by chunk, applying backpressure so a
|
||||
// board with many attachments/covers never buffers the entire .zip in RAM.
|
||||
// Older browsers fall back to the previous in-memory blob download.
|
||||
const saveZipStreaming = async (zip, filename) => {
|
||||
const genOpts = {
|
||||
type: 'uint8array',
|
||||
streamFiles: true,
|
||||
compression: 'DEFLATE',
|
||||
compressionOptions: { level: 6 },
|
||||
};
|
||||
if (typeof window.showSaveFilePicker === 'function') {
|
||||
let handle = null;
|
||||
try {
|
||||
handle = await window.showSaveFilePicker({
|
||||
suggestedName: filename,
|
||||
types: [{ description: 'Zip archive', accept: { 'application/zip': ['.zip'] } }],
|
||||
});
|
||||
} catch (e) {
|
||||
// User dismissed the save dialog — nothing to do.
|
||||
if (e && e.name === 'AbortError') return;
|
||||
handle = null; // any other failure → fall back to blob download
|
||||
}
|
||||
if (handle) {
|
||||
const writable = await handle.createWritable();
|
||||
await new Promise((resolve, reject) => {
|
||||
const helper = zip.generateInternalStream(genOpts);
|
||||
helper
|
||||
.on('data', data => {
|
||||
// Pause the zip stream until this chunk is flushed to disk.
|
||||
helper.pause();
|
||||
writable.write(data).then(() => helper.resume(), reject);
|
||||
})
|
||||
.on('error', reject)
|
||||
.on('end', () => { writable.close().then(resolve, reject); })
|
||||
.resume();
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Fallback: build the blob in memory (older browsers only).
|
||||
const content = await zip.generateAsync({
|
||||
type: 'blob',
|
||||
compression: 'DEFLATE',
|
||||
compressionOptions: { level: 6 },
|
||||
});
|
||||
saveAs(content, filename);
|
||||
};
|
||||
|
||||
const getPageHtmlString = (clonedElement = null) => {
|
||||
const element = clonedElement || window.document.querySelector('html');
|
||||
return `<!doctype html>${element.outerHTML}`;
|
||||
@@ -526,6 +604,10 @@ body { font-family: sans-serif !important; }
|
||||
const zipFilename = boardTitle || boardSlug;
|
||||
const zipDirName = zipFilename; // Directory name inside the ZIP
|
||||
|
||||
// Render EVERY card first (infinite scroll otherwise leaves most cards out
|
||||
// of the DOM), then snapshot.
|
||||
await expandAllCardsForExport();
|
||||
|
||||
// Clone the HTML element to process for export without modifying live DOM
|
||||
const htmlElement = window.document.querySelector('html');
|
||||
const clonedHtmlElement = htmlElement.cloneNode(true);
|
||||
@@ -549,8 +631,8 @@ body { font-family: sans-serif !important; }
|
||||
|
||||
addBoardHTMLToZip(boardSlug, zip, clonedHtmlElement, zipDirName);
|
||||
|
||||
const content = await zip.generateAsync({ type: 'blob' });
|
||||
saveAs(content, `${zipFilename}.zip`);
|
||||
// Stream the .zip straight to disk (low memory) with a blob fallback.
|
||||
await saveZipStreaming(zip, `${zipFilename}.zip`);
|
||||
// No page reload - impersonation session is preserved!
|
||||
};
|
||||
};
|
||||
+19
-29
@@ -19,8 +19,9 @@ import {
|
||||
now,
|
||||
createDate,
|
||||
fromNow,
|
||||
calendar
|
||||
calendar
|
||||
} from '/imports/lib/dateUtils';
|
||||
import { weekRange } from '/models/lib/weekStart';
|
||||
// Sidebar is imported late to avoid circular dependency (sidebar.js needs its
|
||||
// jade template loaded first, but router.js → filter.js would load it too early)
|
||||
let _Sidebar;
|
||||
@@ -119,44 +120,33 @@ class DateFilter {
|
||||
// weeks up to today +/- offset. This considers the user's preferred
|
||||
// start of week day (as defined by Meteor).
|
||||
relativeWeek(offset, week) {
|
||||
if (this._filterState == 'thisweek') {
|
||||
// #4881: toggle per-week so clicking the same button again clears it, but
|
||||
// clicking the OTHER week switches to it instead of just resetting (the old
|
||||
// code reset on either state, so you could not go straight from "this week"
|
||||
// to "next week").
|
||||
const targetState = week === 'next' ? 'nextweek' : 'thisweek';
|
||||
if (this._filterState === targetState) {
|
||||
this.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._filterState == 'nextweek') {
|
||||
this.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
// getStartDayOfWeek returns the offset from Sunday of the user's
|
||||
// preferred starting day of the week. This date should be added
|
||||
// to the moment start of week to get the real start of week date.
|
||||
// The default is 1, meaning Monday.
|
||||
// #4881: the old window came from startOf(now(), 'week'), but native
|
||||
// dateUtils.startOf() has no 'week' case, so it returned "now" unchanged and
|
||||
// the window started at today + startDayOfWeek — i.e. it selected NEXT
|
||||
// week's cards for "this week" and ignored the configured start weekday.
|
||||
// weekRange() computes the correct window that CONTAINS today for the user's
|
||||
// configured start day of week (0=Sunday..6=Saturday, default Monday).
|
||||
const currentUser = ReactiveCache.getCurrentUser();
|
||||
const weekStartDay = currentUser ? currentUser.getStartDayOfWeek() : 1;
|
||||
const weekOffset = week === 'next' ? 1 : 0;
|
||||
const { start: WeekStart, end: WeekEnd } = weekRange(now(), weekStartDay, weekOffset);
|
||||
|
||||
if (week === 'this') {
|
||||
// Create week start and end dates
|
||||
var WeekStart = startOf(add(startOf(now(), 'week'), weekStartDay, 'days'), 'day');
|
||||
var WeekEnd = endOf(add(WeekStart, 6, 'days'), 'day');
|
||||
|
||||
this._updateState('thisweek');
|
||||
} else if (week === 'next') {
|
||||
// Create next week start and end dates
|
||||
var WeekStart = startOf(add(startOf(now(), 'week'), weekStartDay + 7, 'days'), 'day');
|
||||
var WeekEnd = endOf(add(WeekStart, 6, 'days'), 'day');
|
||||
|
||||
this._updateState('nextweek');
|
||||
}
|
||||
|
||||
var startDate = WeekStart;
|
||||
var endDate = WeekEnd;
|
||||
this._updateState(targetState);
|
||||
|
||||
if (offset >= 0) {
|
||||
this._filter = { $gte: startDate, $lte: endDate };
|
||||
this._filter = { $gte: WeekStart, $lte: WeekEnd };
|
||||
} else {
|
||||
this._filter = { $lte: startDate, $gte: endDate };
|
||||
this._filter = { $lte: WeekStart, $gte: WeekEnd };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import { Mongo } from 'meteor/mongo';
|
||||
import { ReactiveCache } from '/imports/reactiveCache';
|
||||
import { Filter } from '/client/lib/filter';
|
||||
import { listCardsSelector } from '/models/lib/swimlaneFilter';
|
||||
const { windowCountId: windowCountIdImpl, resolveCardsLoadingMode } = require('/models/lib/cardsLoading');
|
||||
|
||||
// Client-only mirror of the server `boardListCardCounts` docs published by the
|
||||
// 'boardListCardCount' publication (one doc per list/swimlane window, holding
|
||||
// the total card count so the board knows whether more cards remain to scroll
|
||||
// in). Only used in lazy card-loading mode.
|
||||
export const BoardListCardCounts = new Mongo.Collection('boardListCardCounts');
|
||||
|
||||
// Effective card-loading mode. Prefers the admin Setting doc (reactive, so an
|
||||
// Admin Panel change is picked up), falls back to the public env value, then to
|
||||
// 'all'. See server/cards-loading.js and Admin Panel / Features.
|
||||
export function cardsLoadingMode() {
|
||||
const setting = ReactiveCache.getCurrentSetting();
|
||||
const fromDoc = setting && setting.cardsLoading;
|
||||
const fromEnv =
|
||||
Meteor.settings && Meteor.settings.public && Meteor.settings.public.cardsLoading;
|
||||
return resolveCardsLoadingMode(fromDoc || fromEnv);
|
||||
}
|
||||
|
||||
export function isLazyCards() {
|
||||
return cardsLoadingMode() === 'lazy';
|
||||
}
|
||||
|
||||
// Stable id for one (list, swimlane) window's count doc (shared, unit-tested).
|
||||
export function windowCountId(listId, swimlaneId) {
|
||||
return windowCountIdImpl(listId, swimlaneId);
|
||||
}
|
||||
|
||||
// Accurate total card count for `list.cards(swimlaneId)` in lazy mode, where
|
||||
// minimongo holds only the loaded window. Builds the SAME selector
|
||||
// list.cards(swimlaneId) uses (listCardsSelector + the board Filter), subscribes
|
||||
// (deduped) to the server-side count for it, and returns that count. Returns null
|
||||
// when not in lazy mode or the count has not arrived yet, so callers can fall
|
||||
// back to the loaded-window length. Because the selector is identical to the one
|
||||
// list.cards() runs against minimongo, the server count is exactly the accurate
|
||||
// version of whatever list.cards(swimlaneId).length would report.
|
||||
export function lazyListCardCount(list, swimlaneId) {
|
||||
if (!isLazyCards() || !list || !list.boardId) return null;
|
||||
const selector = listCardsSelector(
|
||||
list._id,
|
||||
swimlaneId,
|
||||
list.orphanedCardsSwimlaneIds ? list.orphanedCardsSwimlaneIds(swimlaneId) : undefined,
|
||||
);
|
||||
const mongoSelector = Filter.mongoSelector(selector);
|
||||
const id = windowCountId(list._id, swimlaneId);
|
||||
const inst = typeof Template !== 'undefined' && Template.instance ? Template.instance() : null;
|
||||
if (inst && inst.subscribe) {
|
||||
inst.subscribe('boardListCardCount', id, list.boardId, mongoSelector);
|
||||
}
|
||||
const doc = BoardListCardCounts.findOne(id);
|
||||
return doc ? doc.count : null;
|
||||
}
|
||||
+27
-101
@@ -4,6 +4,7 @@ import { Tracker } from 'meteor/tracker';
|
||||
import { TAPi18n } from '/imports/i18n';
|
||||
import { EscapeActions } from '/client/lib/escapeActions';
|
||||
import { Utils } from '/client/lib/utils';
|
||||
import { computePopupOffset } from '/client/lib/popupOffset';
|
||||
|
||||
window.Popup = new (class {
|
||||
constructor() {
|
||||
@@ -222,108 +223,33 @@ window.Popup = new (class {
|
||||
return () => {
|
||||
Utils.windowResizeDep.depend();
|
||||
|
||||
if (Utils.isMiniScreen()) return { left: 0, top: 0 };
|
||||
// #5667: read the current page scroll and the opener's document offset,
|
||||
// then delegate the geometry to the pure, unit-tested computePopupOffset.
|
||||
// It lays the popup out in VIEWPORT coordinates — so a date-picker opened
|
||||
// low on a scrolled page stays fully visible instead of extending past the
|
||||
// visible area — and returns document coordinates for the absolute style.
|
||||
const hasOpener = !!($element && $element.length);
|
||||
const offset = hasOpener ? $element.offset() : null;
|
||||
const opener = hasOpener
|
||||
? { top: offset.top, left: offset.left, height: $element.outerHeight() }
|
||||
: null;
|
||||
|
||||
const viewportWidth = $(window).width();
|
||||
const viewportHeight = $(window).height();
|
||||
const viewportPadding = 10;
|
||||
// Calculate actual popup width based on CSS: min(380px, 55vw)
|
||||
const popupWidth = Math.min(380, viewportWidth * 0.55);
|
||||
|
||||
// Card details popup: dock it to the top of the viewport so it overlays
|
||||
// the global and board header bars (its z-index already sits above them)
|
||||
// instead of opening downward from the clicked minicard.
|
||||
if (popupName === 'cardDetailsPopup') {
|
||||
const cardWidth = Math.min(600, viewportWidth * 0.8);
|
||||
const centeredLeft = (viewportWidth - cardWidth) / 2;
|
||||
// top is forced to 0 in CSS (.pop-over[data-popup="cardDetailsPopup"])
|
||||
// so the card overlays the header bars; use the full viewport height.
|
||||
return {
|
||||
left: Math.max(viewportPadding, centeredLeft),
|
||||
top: 0,
|
||||
maxHeight: viewportHeight,
|
||||
};
|
||||
}
|
||||
|
||||
// If the opener element is missing (e.g., programmatic open), fallback to viewport origin
|
||||
if (!$element || $element.length === 0) {
|
||||
return {
|
||||
left: viewportPadding,
|
||||
top: viewportPadding,
|
||||
maxHeight: viewportHeight - viewportPadding * 2,
|
||||
};
|
||||
}
|
||||
|
||||
const offset = $element.offset();
|
||||
const openerTop = offset.top;
|
||||
const openerBottom = offset.top + $element.outerHeight();
|
||||
const clampedLeft = Math.max(
|
||||
viewportPadding,
|
||||
Math.min(offset.left, viewportWidth - popupWidth - viewportPadding),
|
||||
);
|
||||
|
||||
// Check if this is an admin panel edit popup
|
||||
const isAdminEditPopup = $element.hasClass('edit-user') ||
|
||||
$element.hasClass('edit-org') ||
|
||||
$element.hasClass('edit-team');
|
||||
|
||||
if (isAdminEditPopup) {
|
||||
// Center the popup horizontally and use full height
|
||||
const centeredLeft = (viewportWidth - popupWidth) / 2;
|
||||
|
||||
return {
|
||||
left: Math.max(viewportPadding, centeredLeft), // Ensure popup doesn't go off screen
|
||||
top: viewportPadding, // Start from top with small margin
|
||||
maxHeight: viewportHeight - viewportPadding * 2, // Use full height minus small margins
|
||||
};
|
||||
}
|
||||
|
||||
// Calculate available heights around the opener
|
||||
const spaceBelow = viewportHeight - openerBottom - viewportPadding;
|
||||
const spaceAbove = openerTop - viewportPadding;
|
||||
const preferBelow = spaceBelow >= spaceAbove;
|
||||
|
||||
// For language popup, don't use dynamic height to avoid overlapping board
|
||||
const isLanguagePopup = $element.hasClass('js-change-language');
|
||||
let availableHeight;
|
||||
|
||||
if (isLanguagePopup) {
|
||||
// For language popup, position content area below right vertical scrollbar
|
||||
const languageTop = openerBottom;
|
||||
const languageAvailable = Math.max(
|
||||
0,
|
||||
viewportHeight - languageTop - viewportPadding,
|
||||
);
|
||||
const calculatedHeight = Math.min(
|
||||
languageAvailable,
|
||||
viewportHeight * 0.5,
|
||||
); // Max 50% of viewport
|
||||
|
||||
return {
|
||||
left: clampedLeft,
|
||||
top: languageTop,
|
||||
maxHeight: Math.max(calculatedHeight, 0),
|
||||
};
|
||||
} else {
|
||||
// For other popups, choose the side with more space and keep popup fully in viewport.
|
||||
if (preferBelow) {
|
||||
availableHeight = Math.max(0, Math.min(spaceBelow, viewportHeight * 0.8));
|
||||
return {
|
||||
left: clampedLeft,
|
||||
top: openerBottom,
|
||||
maxHeight: availableHeight,
|
||||
};
|
||||
}
|
||||
|
||||
availableHeight = Math.max(0, Math.min(spaceAbove, viewportHeight * 0.8));
|
||||
const top = Math.max(viewportPadding, openerTop - availableHeight);
|
||||
|
||||
return {
|
||||
left: clampedLeft,
|
||||
top,
|
||||
maxHeight: availableHeight,
|
||||
};
|
||||
}
|
||||
return computePopupOffset({
|
||||
viewportWidth: $(window).width(),
|
||||
viewportHeight: $(window).height(),
|
||||
scrollTop: $(window).scrollTop() || 0,
|
||||
scrollLeft: $(window).scrollLeft() || 0,
|
||||
opener,
|
||||
popupName,
|
||||
isMiniScreen: Utils.isMiniScreen(),
|
||||
isAdminEditPopup:
|
||||
hasOpener &&
|
||||
($element.hasClass('edit-user') ||
|
||||
$element.hasClass('edit-org') ||
|
||||
$element.hasClass('edit-team')),
|
||||
isLanguagePopup: hasOpener && $element.hasClass('js-change-language'),
|
||||
viewportPadding: 10,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
'use strict';
|
||||
|
||||
// Pure geometry for Popup._getOffset — extracted so the positioning math is
|
||||
// unit-testable without a DOM / Meteor (see tests/popupOffset.test.cjs).
|
||||
//
|
||||
// #5667 ("Problem when scrolling in a map with date fields"): a date-picker
|
||||
// calendar (due/start/end date, or a date custom field) opened low on the screen
|
||||
// appeared partly outside the visible area, and — because the pop-over is
|
||||
// `position: absolute` (document coordinates) — trying to scroll it into view
|
||||
// moved it along with the page, so the calendar could never be seen fully.
|
||||
//
|
||||
// Root cause: _getOffset computed the space above / below the opener (and the
|
||||
// clamped `top`) from the opener's DOCUMENT offset (`$el.offset()`) mixed with
|
||||
// the VIEWPORT height, ignoring the page scroll. On a scrolled page that placed
|
||||
// the anchored popup outside the visible viewport.
|
||||
//
|
||||
// Fix: do the space / clamp math in VIEWPORT coordinates (subtract
|
||||
// scrollTop/scrollLeft), then convert the chosen top/left back to DOCUMENT
|
||||
// coordinates (add the scroll back) for the absolute style. The popup box is then
|
||||
// always laid out fully within the visible viewport. When the page is not
|
||||
// scrolled (scrollTop === scrollLeft === 0) the output is identical to the
|
||||
// previous behaviour, so nothing regresses in the common case.
|
||||
|
||||
function computePopupOffset(params) {
|
||||
const {
|
||||
viewportWidth,
|
||||
viewportHeight,
|
||||
scrollTop = 0,
|
||||
scrollLeft = 0,
|
||||
// Opener document offset { top, left, height }, or null for a programmatic
|
||||
// open with no reference element.
|
||||
opener = null,
|
||||
popupName,
|
||||
isMiniScreen = false,
|
||||
isAdminEditPopup = false,
|
||||
isLanguagePopup = false,
|
||||
viewportPadding = 10,
|
||||
} = params;
|
||||
|
||||
if (isMiniScreen) return { left: 0, top: 0 };
|
||||
|
||||
// Actual popup width from CSS: min(380px, 55vw).
|
||||
const popupWidth = Math.min(380, viewportWidth * 0.55);
|
||||
|
||||
// Card details popup: docked to the top of the viewport (CSS also forces
|
||||
// top:0) so it overlays the header bars instead of opening from the minicard.
|
||||
if (popupName === 'cardDetailsPopup') {
|
||||
const cardWidth = Math.min(600, viewportWidth * 0.8);
|
||||
const centeredLeft = (viewportWidth - cardWidth) / 2;
|
||||
return {
|
||||
left: Math.max(viewportPadding, centeredLeft),
|
||||
top: scrollTop, // viewport top in document coords
|
||||
maxHeight: viewportHeight,
|
||||
};
|
||||
}
|
||||
|
||||
// No opener element: fall back to the top-left of the VISIBLE viewport.
|
||||
if (!opener) {
|
||||
return {
|
||||
left: viewportPadding + scrollLeft,
|
||||
top: viewportPadding + scrollTop,
|
||||
maxHeight: viewportHeight - viewportPadding * 2,
|
||||
};
|
||||
}
|
||||
|
||||
// Opener position in viewport coordinates (this is the #5667 fix: the previous
|
||||
// code used the raw document offset here).
|
||||
const openerTopVp = opener.top - scrollTop;
|
||||
const openerBottomVp = openerTopVp + opener.height;
|
||||
const openerLeftVp = opener.left - scrollLeft;
|
||||
|
||||
const clampedLeftVp = Math.max(
|
||||
viewportPadding,
|
||||
Math.min(openerLeftVp, viewportWidth - popupWidth - viewportPadding),
|
||||
);
|
||||
const clampedLeft = clampedLeftVp + scrollLeft;
|
||||
|
||||
// Admin panel edit popups: horizontally centered, anchored to the viewport top.
|
||||
if (isAdminEditPopup) {
|
||||
const centeredLeft = (viewportWidth - popupWidth) / 2;
|
||||
return {
|
||||
left: Math.max(viewportPadding, centeredLeft),
|
||||
top: viewportPadding + scrollTop,
|
||||
maxHeight: viewportHeight - viewportPadding * 2,
|
||||
};
|
||||
}
|
||||
|
||||
const spaceBelow = viewportHeight - openerBottomVp - viewportPadding;
|
||||
const spaceAbove = openerTopVp - viewportPadding;
|
||||
const preferBelow = spaceBelow >= spaceAbove;
|
||||
|
||||
// Language popup: fixed-ish height below the opener, capped at 50% viewport.
|
||||
if (isLanguagePopup) {
|
||||
const languageTopVp = openerBottomVp;
|
||||
const languageAvailable = Math.max(
|
||||
0,
|
||||
viewportHeight - languageTopVp - viewportPadding,
|
||||
);
|
||||
const calculatedHeight = Math.min(languageAvailable, viewportHeight * 0.5);
|
||||
return {
|
||||
left: clampedLeft,
|
||||
top: languageTopVp + scrollTop,
|
||||
maxHeight: Math.max(calculatedHeight, 0),
|
||||
};
|
||||
}
|
||||
|
||||
// Other popups: open on the side with more room and stay fully in the viewport.
|
||||
if (preferBelow) {
|
||||
const maxHeight = Math.max(0, Math.min(spaceBelow, viewportHeight * 0.8));
|
||||
return { left: clampedLeft, top: openerBottomVp + scrollTop, maxHeight };
|
||||
}
|
||||
|
||||
const maxHeight = Math.max(0, Math.min(spaceAbove, viewportHeight * 0.8));
|
||||
const topVp = Math.max(viewportPadding, openerTopVp - maxHeight);
|
||||
return { left: clampedLeft, top: topVp + scrollTop, maxHeight };
|
||||
}
|
||||
|
||||
module.exports = { computePopupOffset };
|
||||
+16
-6
@@ -4,16 +4,26 @@ import { Tracker } from 'meteor/tracker';
|
||||
import { findWhere, where, uniqBy, groupBy, indexBy, debounce, once } from '/imports/lib/collectionHelpers';
|
||||
import Settings from '/models/settings';
|
||||
import Users from '/models/users';
|
||||
import { computeBoardBackground } from '/models/lib/boardBackground';
|
||||
|
||||
export const Utils = {
|
||||
async setBackgroundImage(url) {
|
||||
const currentBoard = Utils.getCurrentBoard();
|
||||
if (currentBoard.backgroundImageURL !== undefined) {
|
||||
$(".board-wrapper").css({"background":"url(" + currentBoard.backgroundImageURL + ")","background-size":"cover"});
|
||||
$(".swimlane,.swimlane .list,.swimlane .list .list-body,.swimlane .list:first-child .list-body").css({"background-color":"transparent"});
|
||||
$(".minicard").css({"opacity": "0.9"});
|
||||
} else if (currentBoard["background-color"]) {
|
||||
await currentBoard.setColor(currentBoard["background-color"]);
|
||||
const bg = computeBoardBackground(currentBoard);
|
||||
const swimlaneSelector =
|
||||
".swimlane,.swimlane .list,.swimlane .list .list-body,.swimlane .list:first-child .list-body";
|
||||
if (bg.type === "image") {
|
||||
$(".board-wrapper").css({ "background": "url(" + bg.url + ")", "background-size": "cover" });
|
||||
$(swimlaneSelector).css({ "background-color": "transparent" });
|
||||
$(".minicard").css({ "opacity": "0.9" });
|
||||
} else {
|
||||
// #4978: clear any inline background left over from a previously shown
|
||||
// board, so switching image -> color or image -> plain (e.g. via the
|
||||
// favorites bar) actually removes the old image. The board's color, when
|
||||
// any, is applied reactively via the `.board-wrapper` colorClass.
|
||||
$(".board-wrapper").css({ "background": "", "background-size": "" });
|
||||
$(swimlaneSelector).css({ "background-color": "" });
|
||||
$(".minicard").css({ "opacity": "" });
|
||||
}
|
||||
},
|
||||
/** returns the current board id
|
||||
|
||||
@@ -300,4 +300,19 @@ if (Meteor.isServer) {
|
||||
Accounts.sendResetPasswordEmail,
|
||||
(code, message) => new Meteor.Error(code, message),
|
||||
);
|
||||
|
||||
// #5672: "Internal Server Error When Signing Up Despite Successful Account
|
||||
// Creation". useraccounts' ATCreateUserServer creates the account and then
|
||||
// calls Accounts.sendVerificationEmail(); when SMTP is not configured that
|
||||
// send throws AFTER the user is inserted, so the createUser method returns a
|
||||
// raw HTTP 500 even though the account exists and can sign in. The
|
||||
// verification email is best-effort at sign-up, so swallow + log a transport
|
||||
// failure (registration then completes and redirects to sign-in). An
|
||||
// "already verified" error is re-thrown so ATResendVerificationEmail still
|
||||
// reports it. Unit-tested in tests/verificationEmail.test.cjs.
|
||||
const { wrapSendVerificationEmail } = require('/server/lib/verificationEmail');
|
||||
Accounts.sendVerificationEmail = wrapSendVerificationEmail(
|
||||
Accounts.sendVerificationEmail,
|
||||
message => console.warn(message),
|
||||
);
|
||||
}
|
||||
@@ -576,6 +576,30 @@ FlowRouter.route('/translation', {
|
||||
},
|
||||
});
|
||||
|
||||
FlowRouter.route('/admin-features', {
|
||||
name: 'admin-features',
|
||||
triggersEnter: [
|
||||
AccountsTemplates.ensureSignedIn,
|
||||
() => {
|
||||
Session.set('currentBoard', null);
|
||||
Session.set('currentList', null);
|
||||
Session.set('currentCard', null);
|
||||
Session.set('popupCardId', null);
|
||||
Session.set('popupCardBoardId', null);
|
||||
|
||||
Filter.reset();
|
||||
Session.set('sortBy', '');
|
||||
EscapeActions.executeAll();
|
||||
},
|
||||
],
|
||||
action() {
|
||||
this.render('defaultLayout', {
|
||||
headerBar: 'settingHeaderBar',
|
||||
content: 'adminFeatures',
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
FlowRouter.route('*', {
|
||||
action() {
|
||||
this.render('defaultLayout', { content: 'notFound' });
|
||||
|
||||
@@ -0,0 +1,803 @@
|
||||
# WeKan with FerretDB v1 + embedded SQLite (no PostgreSQL, no MongoDB)
|
||||
#
|
||||
# Use this compose file instead of docker-compose.yml for a light, self-contained
|
||||
# database: FerretDB v1 (the wekan/FerretDB fork, https://github.com/wekan/FerretDB)
|
||||
# with its embedded pure-Go SQLite backend, which speaks the MongoDB wire protocol.
|
||||
# There is no separate PostgreSQL or MongoDB server — FerretDB stores everything in
|
||||
# SQLite files on a volume, and WeKan stores attachments/avatars on the filesystem.
|
||||
# For the PostgreSQL-backed FerretDB 2 option instead, see
|
||||
# docker-compose-ferretdb-v2-postgresql.yml.
|
||||
#
|
||||
# The FerretDB binary is downloaded (once, then cached on the volume) from the
|
||||
# newest wekan/FerretDB release: https://github.com/wekan/FerretDB/releases
|
||||
# The ferretdb.zip there contains ferretdb/<arch>/ferretdb-<arch> for every
|
||||
# platform; the container picks the binary matching its architecture (amd64,
|
||||
# arm64, ppc64le, s390x, riscv64, ...).
|
||||
#
|
||||
# Setup:
|
||||
# docker compose -f docker-compose-ferretdb-v1-sqlite.yml up -d
|
||||
#
|
||||
# To stop:
|
||||
# docker compose -f docker-compose-ferretdb-v1-sqlite.yml stop
|
||||
|
||||
version: '3.8'
|
||||
services:
|
||||
|
||||
# FerretDB v1 with the embedded SQLite backend (MongoDB-compatible on :27017).
|
||||
# A tiny Debian container fetches the wekan/FerretDB v1 release binary for its
|
||||
# architecture (cached on the volume so restarts do not re-download) and runs it.
|
||||
# SQLite data lives in /data/sqlite on the ferretdb-data volume.
|
||||
ferretdb:
|
||||
image: debian:bookworm-slim
|
||||
container_name: wekan-ferretdb
|
||||
restart: always
|
||||
environment:
|
||||
DO_NOT_TRACK: "1"
|
||||
FERRETDB_TELEMETRY: disable
|
||||
# Pin a specific release instead of the newest by setting e.g.
|
||||
# FERRETDB_RELEASE=download/v1.24.2
|
||||
FERRETDB_RELEASE: ${FERRETDB_RELEASE:-latest/download}
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
set -eu
|
||||
BIN=/data/bin/ferretdb
|
||||
if [ ! -x "$$BIN" ]; then
|
||||
echo "Fetching FerretDB v1 from the wekan/FerretDB release ..."
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq --no-install-recommends curl unzip ca-certificates >/dev/null
|
||||
arch="$$(dpkg --print-architecture)"
|
||||
case "$$arch" in ppc64el) arch=ppc64le ;; esac
|
||||
mkdir -p /data/bin
|
||||
curl -fSL --retry 5 -o /tmp/ferretdb.zip \
|
||||
"https://github.com/wekan/FerretDB/releases/$${FERRETDB_RELEASE}/ferretdb.zip"
|
||||
unzip -o -j /tmp/ferretdb.zip "ferretdb/$${arch}/ferretdb-$${arch}" -d /data/bin
|
||||
mv "/data/bin/ferretdb-$${arch}" "$$BIN"
|
||||
chmod +x "$$BIN"
|
||||
rm -f /tmp/ferretdb.zip
|
||||
fi
|
||||
mkdir -p /data/files/db
|
||||
exec "$$BIN" \
|
||||
--handler=sqlite \
|
||||
--sqlite-url=file:/data/files/db/ \
|
||||
--listen-addr=0.0.0.0:27017 \
|
||||
--telemetry=disable
|
||||
volumes:
|
||||
- ferretdb-data:/data
|
||||
networks:
|
||||
- wekan-tier
|
||||
# Expose only on localhost by default; remove to keep it internal to the network.
|
||||
ports:
|
||||
- 127.0.0.1:27017:27017
|
||||
|
||||
wekan:
|
||||
#-------------------------------------------------------------------------------------
|
||||
# ==== WEKAN FROM GITHUB/QUAY/DOCKER HUB ====
|
||||
# All of GitHub, Quay and Docker Hub have latest, but because
|
||||
# latest tag changes when is newest release,
|
||||
# when upgrading would be better to use version tag.
|
||||
# a) Using specific version tag is better:
|
||||
# image: ghcr.io/wekan/wekan:v6.89
|
||||
# image: quay.io/wekan/wekan:v6.89
|
||||
# image: wekanteam/wekan:v6.89
|
||||
# b) GitHub Container registry.
|
||||
# b1) Latest release tag:
|
||||
image: ghcr.io/wekan/wekan:latest
|
||||
# b2) Newest git commit automatic build:
|
||||
#image: ghcr.io/wekan/wekan:main
|
||||
# c) Quay:
|
||||
#image: quay.io/wekan/wekan:latest
|
||||
# d) Docker Hub:
|
||||
#image: wekanteam/wekan:latest
|
||||
#-------------------------------------------------------------------------------------
|
||||
container_name: wekan-app
|
||||
# On CentOS 7 there is seccomp issue with glibc 6,
|
||||
# so CentOS 7 users shoud use these security_opt seccomp:unconfined
|
||||
# settings to get WeKan working. See:
|
||||
# - https://github.com/wekan/wekan/issues/4585
|
||||
# - https://github.com/wekan/wekan/issues/4587
|
||||
#security_opt:
|
||||
# - seccomp:unconfined
|
||||
restart: always
|
||||
networks:
|
||||
- wekan-tier
|
||||
#-------------------------------------------------------------------------------------
|
||||
# ==== BUILD wekan-app DOCKER CONTAINER FROM SOURCE, if you uncomment these ====
|
||||
# ==== and use commands: docker compose up -d --build
|
||||
#build:
|
||||
# context: .
|
||||
# dockerfile: Dockerfile
|
||||
#-------------------------------------------------------------------------------------
|
||||
ports:
|
||||
# Docker outsideport:insideport. Do not add anything extra here.
|
||||
# For example, if you want to have wekan on port 3001,
|
||||
# use 3001:8080 . Do not add any extra address etc here, that way it does not work.
|
||||
# remove port mapping if you use nginx reverse proxy, port 8080 is already exposed to wekan-tier network
|
||||
- 80:8080
|
||||
environment:
|
||||
#-------------------- USING MONGODB CHANGE STREAMS WITH REPLICA SETS AT CURRENT DATABASE --------------------
|
||||
# If you would not like to use Change Streams and replica set for improving speed, change to use polling:
|
||||
#- METEOR_REACTIVITY_ORDER=polling
|
||||
# https://forums.meteor.com/t/meteor-3-5-beta-change-streams-performance-improvements/64461#change-streams-setup-3
|
||||
# https://github.com/meteor/meteor/blob/release-3.5/v3-docs/docs/performance/change-streams-observer-driver.md#choosing-the-reactivity-driver-order
|
||||
# Keep oplog/polling fallback available for deployments that cannot use Change Streams:
|
||||
# https://github.com/wekan/wekan/issues/6307#issuecomment-4299349231
|
||||
# FerretDB has no MongoDB change streams / replica-set oplog, so use polling:
|
||||
- METEOR_REACTIVITY_ORDER=polling
|
||||
- DDP_TRANSPORT=uws
|
||||
#- DDP_TRANSPORT=sockjs
|
||||
#-----------------------------------------------------------------
|
||||
# ==== WRITEABLE PATH FOR FILE UPLOADS ====
|
||||
- WRITABLE_PATH=/data
|
||||
#-----------------------------------------------------------------
|
||||
# ==== AWS S3 FOR FILES ====
|
||||
# Any region. For example:
|
||||
# us-standard,us-west-1,us-west-2,
|
||||
# eu-west-1,eu-central-1,
|
||||
# ap-southeast-1,ap-northeast-1,sa-east-1
|
||||
#
|
||||
#- S3='{"s3":{"key": "xxx", "secret": "xxx", "bucket": "xxx", "region": "xxx"}}'
|
||||
#- S3_SECRET_FILE=/run/secrets/s3_secret
|
||||
#-----------------------------------------------------------------
|
||||
# ==== MONGO_URL — points to FerretDB (MongoDB-compatible) ====
|
||||
#- MONGO_URL=mongodb://wekan-ferretdb:27017/wekan
|
||||
- MONGO_URL=mongodb://ferretdb:27017/wekan
|
||||
#- MONGO_PASSWORD_FILE=/run/secrets/mongo_password
|
||||
#---------------------------------------------------------------
|
||||
# ==== ROOT_URL SETTING ====
|
||||
# Change ROOT_URL to your real Wekan URL, for example:
|
||||
# If you have Caddy/Nginx/Apache providing SSL
|
||||
# - https://example.com
|
||||
# - https://boards.example.com
|
||||
# This can be problematic with avatars https://github.com/wekan/wekan/issues/1776
|
||||
# - https://example.com/wekan
|
||||
# If without https, can be only wekan node, no need for Caddy/Nginx/Apache if you don't need them
|
||||
# - http://example.com
|
||||
# - http://boards.example.com
|
||||
# - http://192.168.1.100 <=== using at local LAN
|
||||
- ROOT_URL=http://localhost # <=== using only at same laptop/desktop where Wekan is installed
|
||||
#---------------------------------------------------------------
|
||||
# ==== EMAIL SETTINGS ====
|
||||
# Email settings are only at MAIL_URL and MAIL_FROM.
|
||||
# Admin Panel has test button, but it's not used for settings.
|
||||
# see https://github.com/wekan/wekan/blob/main/docs/Email/Troubleshooting-Mail.md
|
||||
# For SSL in email, change smtp:// to smtps://
|
||||
# NOTE: Special characters need to be url-encoded in MAIL_URL.
|
||||
# You can encode those characters for example at: https://www.urlencoder.org
|
||||
#- MAIL_URL=smtp://user:pass@mailserver.example.com:25/
|
||||
- MAIL_URL=smtp://<mail_url>:25/?ignoreTLS=true&tls={rejectUnauthorized:false}
|
||||
- MAIL_FROM=Wekan Notifications <noreply.wekan@mydomain.com>
|
||||
# Currently MAIL_SERVICE is not in use.
|
||||
#- MAIL_SERVICE=Outlook365
|
||||
#- MAIL_SERVICE_USER=firstname.lastname@hotmail.com
|
||||
#- MAIL_SERVICE_PASSWORD=SecretPassword
|
||||
#- MAIL_SERVICE_PASSWORD_FILE=/run/secrets/mail_service_password
|
||||
#---------------------------------------------------------------
|
||||
# https://github.com/wekan/wekan/issues/3585#issuecomment-1021522132
|
||||
# Add more Node heap, this is done by default at Dockerfile:
|
||||
# - NODE_OPTIONS="--max_old_space_size=4096"
|
||||
# Add more stack, this is done at Dockerfile:
|
||||
# bash -c "ulimit -s 65500; exec node --stack-size=65500 main.js"
|
||||
#---------------------------------------------------------------
|
||||
# ==== OPTIONAL: MONGO OPLOG SETTINGS =====
|
||||
# https://github.com/wekan/wekan-mongodb/issues/2#issuecomment-378343587
|
||||
# HIGHLY RECOMMENDED for pub/sub performance!
|
||||
# MongoDB oplog is used by Meteor for real-time data synchronization.
|
||||
# Without oplog, Meteor falls back to polling which increases:
|
||||
# - CPU usage by 3-5x
|
||||
# - Network traffic significantly
|
||||
# - Latency from 50ms to 2000ms
|
||||
# Must configure MongoDB replica set first
|
||||
# See: https://blog.meteor.com/tuning-meteor-mongo-livedata-for-scalability-13fe9deb8908
|
||||
# FerretDB emulates OpLog via --replication-set-name=rs0.
|
||||
# HIGHLY RECOMMENDED — enables real-time DDP updates (sub-100ms latency).
|
||||
#- MONGO_OPLOG_URL=mongodb://wekan-ferretdb:27017/local?replicaSet=rs0
|
||||
# MONGO_OPLOG_URL intentionally unset: FerretDB v1 has no replica-set oplog (WeKan uses polling).
|
||||
#---------------------------------------------------------------
|
||||
# ==== OPTIONAL: KADIRA PERFORMANCE MONITORING FOR METEOR ====
|
||||
# https://github.com/edemaine/kadira-compose
|
||||
# https://github.com/meteor/meteor-apm-agent
|
||||
# https://blog.meteor.com/kadira-apm-is-now-open-source-490469ffc85f
|
||||
#- APM_OPTIONS_ENDPOINT=http://<kadira-ip>:11011
|
||||
#- APM_APP_ID=
|
||||
#- APM_APP_SECRET=
|
||||
#---------------------------------------------------------------
|
||||
# ==== OPTIONAL: LOGS AND STATS ====
|
||||
# https://github.com/wekan/wekan/blob/main/docs/Features/Logs.md
|
||||
#
|
||||
# Daily export of Wekan changes as JSON to Logstash and ElasticSearch / Kibana (ELK)
|
||||
# https://github.com/wekan/wekan-logstash
|
||||
#
|
||||
# Statistics Python script for Wekan Dashboard
|
||||
# https://github.com/wekan/wekan-stats
|
||||
#
|
||||
# Console, file, and zulip logger on database changes https://github.com/wekan/wekan/pull/1010
|
||||
# with fix to replace console.log by winston logger https://github.com/wekan/wekan/pull/1033
|
||||
# but there could be bug https://github.com/wekan/wekan/issues/1094
|
||||
#
|
||||
# There is Feature Request: Logging date and time of all activity with summary reports,
|
||||
# and requesting reason for changing card to other column https://github.com/wekan/wekan/issues/1598
|
||||
#---------------------------------------------------------------
|
||||
# ==== NUMBER OF SEARCH RESULTS PER PAGE BY DEFAULT ====
|
||||
#- RESULTS_PER_PAGE=20
|
||||
#---------------------------------------------------------------
|
||||
# ==== AFTER OIDC LOGIN, ADD USERS AUTOMATICALLY TO THIS BOARD ID ====
|
||||
# https://github.com/wekan/wekan/pull/5098
|
||||
#- DEFAULT_BOARD_ID=abcd1234
|
||||
#---------------------------------------------------------------
|
||||
# ==== WEKAN API AND EXPORT BOARD ====
|
||||
# Wekan Export Board works when WITH_API=true.
|
||||
# https://github.com/wekan/wekan/blob/main/docs/API/REST-API.md
|
||||
# https://github.com/wekan/wekan-gogs
|
||||
# If you disable Wekan API with false, Export Board does not work.
|
||||
- WITH_API=true
|
||||
#---------------------------------------------------------------
|
||||
# ==== PASSWORD BRUTE FORCE PROTECTION ====
|
||||
#https://atmospherejs.com/lucasantoniassi/accounts-lockout
|
||||
#Defaults below. Uncomment to change. wekan/server/accounts-lockout.js
|
||||
#- ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURES_BEFORE=3
|
||||
#- ACCOUNTS_LOCKOUT_KNOWN_USERS_PERIOD=60
|
||||
#- ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURE_WINDOW=15
|
||||
#- ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURES_BERORE=3
|
||||
#- ACCOUNTS_LOCKOUT_UNKNOWN_USERS_LOCKOUT_PERIOD=60
|
||||
#- ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURE_WINDOW=15
|
||||
#---------------------------------------------------------------
|
||||
# ==== ACCOUNT OPTIONS ====
|
||||
# https://docs.meteor.com/api/accounts-multi.html#AccountsCommon-config
|
||||
# Defaults below. Uncomment to change. wekan/server/accounts-common.js
|
||||
# - ACCOUNTS_COMMON_LOGIN_EXPIRATION_IN_DAYS=90
|
||||
#---------------------------------------------------------------
|
||||
# ==== RICH TEXT EDITOR IN CARD COMMENTS ====
|
||||
# https://github.com/wekan/wekan/pull/2560
|
||||
- RICHER_CARD_COMMENT_EDITOR=false
|
||||
#---------------------------------------------------------------
|
||||
# ==== CARD LOADING: all (default) or lazy ====
|
||||
# 'all' = load every card of a board into the browser (default).
|
||||
# 'lazy' = each list loads only the cards currently visible
|
||||
# (infinite scroll) plus a live count, so boards with
|
||||
# thousands of cards use less memory/bandwidth.
|
||||
# Also changeable at runtime in Admin Panel / Features.
|
||||
- CARDS_LOADING=all
|
||||
#---------------------------------------------------------------
|
||||
# ==== CARD OPENED, SEND WEBHOOK MESSAGE ====
|
||||
# https://github.com/wekan/wekan/issues/2518
|
||||
- CARD_OPENED_WEBHOOK_ENABLED=false
|
||||
#---------------------------------------------------------------
|
||||
# ==== Allow configuration to validate uploaded attachments ====
|
||||
#-ATTACHMENTS_UPLOAD_EXTERNAL_PROGRAM=/usr/local/bin/avscan {file}
|
||||
#-ATTACHMENTS_UPLOAD_MIME_TYPES=image/*,text/*
|
||||
#-ATTACHMENTS_UPLOAD_MAX_SIZE=5000000
|
||||
#---------------------------------------------------------------
|
||||
# ==== Allow configuration to validate uploaded avatars ====
|
||||
#-AVATARS_UPLOAD_EXTERNAL_PROGRAM=/usr/local/bin/avscan {file}
|
||||
#-AVATARS_UPLOAD_MIME_TYPES=image/*
|
||||
#-AVATARS_UPLOAD_MAX_SIZE=500000
|
||||
#---------------------------------------------------------------
|
||||
# ==== Allow to shrink attached/pasted image ====
|
||||
# https://github.com/wekan/wekan/pull/2544
|
||||
#- MAX_IMAGE_PIXEL=1024
|
||||
#- IMAGE_COMPRESS_RATIO=80
|
||||
#---------------------------------------------------------------
|
||||
# ==== NOTIFICATION TRAY AFTER READ DAYS BEFORE REMOVE =====
|
||||
# Number of days after a notification is read before we remove it.
|
||||
# Default: 2
|
||||
#- NOTIFICATION_TRAY_AFTER_READ_DAYS_BEFORE_REMOVE=2
|
||||
#---------------------------------------------------------------
|
||||
# ==== BIGEVENTS DUE ETC NOTIFICATIONS =====
|
||||
# https://github.com/wekan/wekan/pull/2541
|
||||
# Introduced a system env var BIGEVENTS_PATTERN default as "NONE",
|
||||
# so any activityType matches the pattern, system will send out
|
||||
# notifications to all board members no matter they are watching
|
||||
# or tracking the board or not. Owner of the wekan server can
|
||||
# disable the feature by setting this variable to "NONE" or
|
||||
# change the pattern to any valid regex. i.e. '|' delimited
|
||||
# activityType names.
|
||||
# a) Example
|
||||
#- BIGEVENTS_PATTERN=due
|
||||
# b) All
|
||||
#- BIGEVENTS_PATTERN=received|start|due|end
|
||||
# c) Disabled
|
||||
- BIGEVENTS_PATTERN=NONE
|
||||
#---------------------------------------------------------------
|
||||
# ==== NOTIFY ON ASSIGN =====
|
||||
# Notify the user directly when they are added as a card member or
|
||||
# assignee. Set false to disable.
|
||||
#- NOTIFY_ON_ASSIGN=true
|
||||
#---------------------------------------------------------------
|
||||
# ==== EMAIL DUE DATE NOTIFICATION =====
|
||||
# https://github.com/wekan/wekan/pull/2536
|
||||
# System timelines will be showing any user modification for
|
||||
# dueat startat endat receivedat, also notification to
|
||||
# the watchers and if any card is due, about due or past due.
|
||||
#
|
||||
# Notify due days, default is None, 2 days before and on the event day
|
||||
#- NOTIFY_DUE_DAYS_BEFORE_AND_AFTER=2,0
|
||||
#
|
||||
# Notify due at hour of day. Default every morning at 8am. Can be 0-23.
|
||||
# If env variable has parsing error, use default. Notification sent to watchers.
|
||||
#- NOTIFY_DUE_AT_HOUR_OF_DAY=8
|
||||
#-----------------------------------------------------------------
|
||||
# ==== EMAIL NOTIFICATION TIMEOUT, ms =====
|
||||
# Default: 30000 ms = 30s
|
||||
#- EMAIL_NOTIFICATION_TIMEOUT=30000
|
||||
#-----------------------------------------------------------------
|
||||
# ==== CORS =====
|
||||
# CORS: Set Access-Control-Allow-Origin header.
|
||||
#- CORS=*
|
||||
# CORS_ALLOW_HEADERS: Set Access-Control-Allow-Headers header. "Authorization,Content-Type" is required for cross-origin use of the API.
|
||||
#- CORS_ALLOW_HEADERS=Authorization,Content-Type
|
||||
# CORS_EXPOSE_HEADERS: Set Access-Control-Expose-Headers header. This is not needed for typical CORS situations
|
||||
#- CORS_EXPOSE_HEADERS=*
|
||||
#-----------------------------------------------------------------
|
||||
# ==== MATOMO INTEGRATION ====
|
||||
# Optional: Integration with Matomo https://matomo.org that is installed to your server
|
||||
# The address of the server where Matomo is hosted.
|
||||
#- MATOMO_ADDRESS=https://example.com/matomo
|
||||
# The value of the site ID given in Matomo server for Wekan
|
||||
#- MATOMO_SITE_ID=1
|
||||
# The option do not track which enables users to not be tracked by matomo
|
||||
#- MATOMO_DO_NOT_TRACK=true
|
||||
# The option that allows matomo to retrieve the username:
|
||||
#- MATOMO_WITH_USERNAME=true
|
||||
#-----------------------------------------------------------------
|
||||
# ==== BROWSER POLICY AND TRUSTED IFRAME URL ====
|
||||
# Enable browser policy and allow one trusted URL that can have iframe that has Wekan embedded inside.
|
||||
# Setting this to false is not recommended, it also disables all other browser policy protections
|
||||
# and allows all iframing etc. See wekan/server/policy.js
|
||||
- BROWSER_POLICY_ENABLED=true
|
||||
# When browser policy is enabled, HTML code at this Trusted URL can have iframe that embeds Wekan inside.
|
||||
#- TRUSTED_URL=https://intra.example.com
|
||||
#-----------------------------------------------------------------
|
||||
# ==== METRICS ALLOWED IP ADDRESSES ====
|
||||
# https://github.com/wekan/wekan/blob/main/docs/Features/Metrics.md
|
||||
#- METRICS_ALLOWED_IP_ADDRESSES=192.168.0.100,192.168.0.200
|
||||
#-----------------------------------------------------------------
|
||||
# ==== OUTGOING WEBHOOKS ====
|
||||
# What to send to Outgoing Webhook, or leave out. If commented out the default values will be: cardId,listId,oldListId,boardId,comment,user,card,commentId,swimlaneId,customerField,customFieldValue
|
||||
#- WEBHOOKS_ATTRIBUTES=cardId,listId,oldListId,boardId,comment,user,card,board,list,swimlane,commentId
|
||||
#-----------------------------------------------------------------
|
||||
# ==== Debug OIDC OAuth2 etc ====
|
||||
#- DEBUG=true
|
||||
#---------------------------------------------
|
||||
# ==== AUTOLOGIN WITH OIDC/OAUTH2 ====
|
||||
# https://github.com/wekan/wekan/blob/main/docs/Login/autologin.md
|
||||
#- OIDC_REDIRECTION_ENABLED=true
|
||||
# OIDC RP-initiated logout endpoint (end_session_endpoint). When set, the
|
||||
# "Log Out" menu redirects to this endpoint so the identity provider session
|
||||
# is also ended and the user is returned to Wekan (ROOT_URL) via
|
||||
# post_logout_redirect_uri, instead of landing on the provider home page
|
||||
# (which errors for non-admin users). See https://github.com/wekan/wekan/issues/6158
|
||||
# Keycloak example:
|
||||
#- OAUTH2_LOGOUT_ENDPOINT=/realms/<keycloak realm>/protocol/openid-connect/logout
|
||||
#-----------------------------------------------------------------
|
||||
# ==== OAUTH2 ORACLE on premise identity manager OIM ====
|
||||
#- ORACLE_OIM_ENABLED=true
|
||||
#-----------------------------------------------------------------
|
||||
# ==== OAUTH2 AZURE ====
|
||||
# https://github.com/wekan/wekan/blob/main/docs/Login/Azure/Azure.md
|
||||
# 1) Register the application with Azure. Make sure you capture
|
||||
# the application ID as well as generate a secret key.
|
||||
# 2) Configure the environment variables. This differs slightly
|
||||
# by installation type, but make sure you have the following:
|
||||
#- OAUTH2_ENABLED=true
|
||||
# Optional OAuth2 CA Cert, see https://github.com/wekan/wekan/issues/3299
|
||||
#- OAUTH2_CA_CERT=ABCD1234
|
||||
# Use OAuth2 ADFS additional changes. Also needs OAUTH2_ENABLED=true setting.
|
||||
#- OAUTH2_ADFS_ENABLED=false
|
||||
# Azure AD B2C. https://github.com/wekan/wekan/issues/5242
|
||||
#- OAUTH2_B2C_ENABLED=false
|
||||
# SECURITY (GHSA-mp7g-hj5q-gxhq): Link an OIDC login to a pre-existing
|
||||
# Wekan account (password/LDAP) with the same email. OFF by default; when
|
||||
# false an OIDC login whose email already exists is rejected instead of
|
||||
# merged, preventing account takeover via spoofed email claims. Only
|
||||
# enable if you fully trust your OIDC provider's email claims, and even
|
||||
# then the provider must send email_verified=true for the merge to happen.
|
||||
#- OAUTH2_MERGE_EXISTING_USERS=false
|
||||
# When false, OAuth2/OIDC login is refused for users that do not already
|
||||
# have a Wekan account (matched by verified email).
|
||||
#- OAUTH2_AUTO_REGISTRATION=true
|
||||
# Comma/space separated OAuth2/OIDC group names whose members become Wekan
|
||||
# admins. Empty = off.
|
||||
#- OAUTH2_ADMIN_GROUPS=
|
||||
# OAuth2 login style: popup or redirect.
|
||||
#- OAUTH2_LOGIN_STYLE=redirect
|
||||
# Application GUID captured during app registration:
|
||||
#- OAUTH2_CLIENT_ID=xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx
|
||||
# Secret key generated during app registration:
|
||||
#- OAUTH2_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
#- OAUTH2_SECRET_FILE=/run/secrets/oauth2_secret
|
||||
#- OAUTH2_SERVER_URL=https://login.microsoftonline.com/
|
||||
#- OAUTH2_AUTH_ENDPOINT=/oauth2/v2.0/authorize
|
||||
#- OAUTH2_USERINFO_ENDPOINT=https://graph.microsoft.com/oidc/userinfo
|
||||
#- OAUTH2_TOKEN_ENDPOINT=/oauth2/v2.0/token
|
||||
# The claim name you want to map to the unique ID field:
|
||||
#- OAUTH2_ID_MAP=email
|
||||
# The claim name you want to map to the username field:
|
||||
#- OAUTH2_USERNAME_MAP=email
|
||||
# The claim name you want to map to the full name field:
|
||||
#- OAUTH2_FULLNAME_MAP=name
|
||||
# The claim name you want to map to the email field:
|
||||
#- OAUTH2_EMAIL_MAP=email
|
||||
#-----------------------------------------------------------------
|
||||
# ==== OAUTH2 Nextcloud ====
|
||||
# 1) Register the application with Nextcloud: https://your.nextcloud/index.php/settings/admin/security
|
||||
# Make sure you capture the application ID as well as generate a secret key.
|
||||
# Use https://your.wekan/_oauth/oidc for the redirect URI.
|
||||
# 2) Configure the environment variables. This differs slightly
|
||||
# by installation type, but make sure you have the following:
|
||||
#- OAUTH2_ENABLED=true
|
||||
# OAuth2 login style: popup or redirect.
|
||||
#- OAUTH2_LOGIN_STYLE=redirect
|
||||
# Application GUID captured during app registration:
|
||||
#- OAUTH2_CLIENT_ID=xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx
|
||||
# Secret key generated during app registration:
|
||||
#- OAUTH2_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
#- OAUTH2_SECRET_FILE=/run/secrets/oauth2_secret
|
||||
#- OAUTH2_SERVER_URL=https://your-nextcloud.tld
|
||||
#- OAUTH2_AUTH_ENDPOINT=/index.php/apps/oauth2/authorize
|
||||
#- OAUTH2_USERINFO_ENDPOINT=/ocs/v2.php/cloud/user?format=json
|
||||
#- OAUTH2_TOKEN_ENDPOINT=/index.php/apps/oauth2/api/v1/token
|
||||
# The claim name you want to map to the unique ID field:
|
||||
#- OAUTH2_ID_MAP=id
|
||||
# The claim name you want to map to the username field:
|
||||
#- OAUTH2_USERNAME_MAP=id
|
||||
# The claim name you want to map to the full name field:
|
||||
#- OAUTH2_FULLNAME_MAP=display-name
|
||||
# The claim name you want to map to the email field:
|
||||
#- OAUTH2_EMAIL_MAP=email
|
||||
#-----------------------------------------------------------------
|
||||
# ==== OAUTH2 KEYCLOAK ====
|
||||
# https://github.com/wekan/wekan/blob/main/docs/Login/Keycloak/Keycloak.md <== MAPPING INFO, REQUIRED
|
||||
#- OAUTH2_ENABLED=true
|
||||
# OAuth2 login style: popup or redirect.
|
||||
#- OAUTH2_LOGIN_STYLE=redirect
|
||||
#- OAUTH2_CLIENT_ID=<Keycloak create Client ID>
|
||||
#- OAUTH2_SERVER_URL=<Keycloak server url - https://keycloak.example.com>
|
||||
#- OAUTH2_AUTH_ENDPOINT=/realms/<keycloak realm>/protocol/openid-connect/auth
|
||||
#- OAUTH2_USERINFO_ENDPOINT=/realms/<keycloak realm>/protocol/openid-connect/userinfo
|
||||
#- OAUTH2_TOKEN_ENDPOINT=/realms/<keycloak realm>/protocol/openid-connect/token
|
||||
#- OAUTH2_SECRET=<keycloak client secret>
|
||||
#- OAUTH2_SECRET_FILE=/run/secrets/oauth2_secret
|
||||
#- OAUTH2_ID_MAP=sub
|
||||
#- OAUTH2_USERNAME_MAP=preferred_username
|
||||
#- OAUTH2_EMAIL_MAP=email
|
||||
#- OAUTH2_FULLNAME_MAP=name
|
||||
#-----------------------------------------------------------------
|
||||
# ==== OAUTH2 DOORKEEPER ====
|
||||
# https://github.com/wekan/wekan/issues/1874
|
||||
# https://github.com/wekan/wekan/blob/main/docs/Login/OAuth2.md2
|
||||
# Enable the OAuth2 connection
|
||||
#- OAUTH2_ENABLED=true
|
||||
# OAuth2 docs: https://github.com/wekan/wekan/blob/main/docs/Login/OAuth2.md
|
||||
# OAuth2 login style: popup or redirect.
|
||||
#- OAUTH2_LOGIN_STYLE=redirect
|
||||
# OAuth2 Client ID.
|
||||
#- OAUTH2_CLIENT_ID=abcde12345
|
||||
# OAuth2 Secret.
|
||||
#- OAUTH2_SECRET=54321abcde
|
||||
#- OAUTH2_SECRET_FILE=/run/secrets/oauth2_secret
|
||||
# OAuth2 Server URL.
|
||||
#- OAUTH2_SERVER_URL=https://chat.example.com
|
||||
# OAuth2 Authorization Endpoint.
|
||||
#- OAUTH2_AUTH_ENDPOINT=/oauth/authorize
|
||||
# OAuth2 Userinfo Endpoint.
|
||||
#- OAUTH2_USERINFO_ENDPOINT=/oauth/userinfo
|
||||
# OAuth2 Token Endpoint.
|
||||
#- OAUTH2_TOKEN_ENDPOINT=/oauth/token
|
||||
# OAUTH2 ID Token Whitelist Fields.
|
||||
#- OAUTH2_ID_TOKEN_WHITELIST_FIELDS=""
|
||||
# OAUTH2 Request Permissions.
|
||||
#- OAUTH2_REQUEST_PERMISSIONS=openid profile email
|
||||
# OAuth2 ID Mapping
|
||||
#- OAUTH2_ID_MAP=
|
||||
# OAuth2 Username Mapping
|
||||
#- OAUTH2_USERNAME_MAP=
|
||||
# OAuth2 Fullname Mapping
|
||||
#- OAUTH2_FULLNAME_MAP=
|
||||
# OAuth2 Email Mapping
|
||||
#- OAUTH2_EMAIL_MAP=
|
||||
#-----------------------------------------------------------------
|
||||
# ==== LDAP: UNCOMMENT ALL TO ENABLE LDAP ====
|
||||
# https://github.com/wekan/wekan/blob/main/docs/Login/LDAP.md
|
||||
# For Snap settings see https://github.com/wekan/wekan/blob/main/docs/Platforms/FOSS/Snap/Supported-settings-keys.md
|
||||
# Most settings work both on Snap and Docker below.
|
||||
# Note: Do not add single quotes '' to variables. Having spaces still works without quotes where required.
|
||||
#
|
||||
# The default authentication method used if a user does not exist to create and authenticate. Can be set as ldap.
|
||||
# (this is set properly in the Admin Panel, changing this item does not remove Password login option)
|
||||
#- DEFAULT_AUTHENTICATION_METHOD=ldap
|
||||
#
|
||||
# Enable or not the connection by the LDAP
|
||||
#- LDAP_ENABLE=true
|
||||
#
|
||||
# The port of the LDAP server
|
||||
#- LDAP_PORT=389
|
||||
#
|
||||
# The host server for the LDAP server
|
||||
#- LDAP_HOST=localhost
|
||||
#
|
||||
#-----------------------------------------------------------------
|
||||
# ==== LDAP AD Simple Auth ====
|
||||
#
|
||||
# Set to true, if you want to connect with Active Directory by Simple Authentication.
|
||||
# When using AD Simple Auth, LDAP_BASEDN is not needed.
|
||||
#
|
||||
# Example:
|
||||
#- LDAP_AD_SIMPLE_AUTH=true
|
||||
#
|
||||
# === LDAP User Authentication ===
|
||||
#
|
||||
# a) Option to login to the LDAP server with the user's own username and password, instead of
|
||||
# an administrator key. Default: false (use administrator key).
|
||||
#
|
||||
# b) When using AD Simple Auth, set to true, when login user is used for binding,
|
||||
# and LDAP_BASEDN is not needed.
|
||||
#
|
||||
# Example:
|
||||
#- LDAP_USER_AUTHENTICATION=true
|
||||
#
|
||||
# Which field is used to find the user for the user authentication. Default: uid.
|
||||
#- LDAP_USER_AUTHENTICATION_FIELD=uid
|
||||
#
|
||||
# === LDAP Default Domain ===
|
||||
#
|
||||
# a) In case AD SimpleAuth is configured, the default domain is appended to the given
|
||||
# loginname for creating the correct username for the bind request to AD.
|
||||
#
|
||||
# b) The default domain of the ldap it is used to create email if the field is not map
|
||||
# correctly with the LDAP_SYNC_USER_DATA_FIELDMAP
|
||||
#
|
||||
# Example :
|
||||
#- LDAP_DEFAULT_DOMAIN=mydomain.com
|
||||
#
|
||||
#-----------------------------------------------------------------
|
||||
# ==== LDAP BASEDN Auth ====
|
||||
#
|
||||
# The base DN for the LDAP Tree
|
||||
#- LDAP_BASEDN=ou=user,dc=example,dc=org
|
||||
#
|
||||
#-----------------------------------------------------------------
|
||||
# Fallback on the default authentication method
|
||||
#- LDAP_LOGIN_FALLBACK=false
|
||||
#
|
||||
# Reconnect to the server if the connection is lost
|
||||
#- LDAP_RECONNECT=true
|
||||
#
|
||||
# Overall timeout, in milliseconds
|
||||
#- LDAP_TIMEOUT=10000
|
||||
#
|
||||
# Specifies the timeout for idle LDAP connections in milliseconds
|
||||
#- LDAP_IDLE_TIMEOUT=10000
|
||||
#
|
||||
# Connection timeout, in milliseconds
|
||||
#- LDAP_CONNECT_TIMEOUT=10000
|
||||
#
|
||||
# If the LDAP needs a user account to search
|
||||
#- LDAP_AUTHENTIFICATION=true
|
||||
#
|
||||
# The search user DN - You need quotes when you have spaces in parameters
|
||||
# 2 examples:
|
||||
#- LDAP_AUTHENTIFICATION_USERDN=CN=ldap admin,CN=users,DC=domainmatter,DC=lan
|
||||
#- LDAP_AUTHENTIFICATION_USERDN=CN=wekan_adm,OU=serviceaccounts,OU=admin,OU=prod,DC=mydomain,DC=com
|
||||
#
|
||||
# The password for the search user
|
||||
#- LDAP_AUTHENTIFICATION_PASSWORD=pwd
|
||||
#- LDAP_AUTHENTIFICATION_PASSWORD_FILE=/run/secrets/ldap_auth_password
|
||||
#
|
||||
# Enable logs for the module
|
||||
#- LDAP_LOG_ENABLED=true
|
||||
#
|
||||
# If the sync of the users should be done in the background
|
||||
#- LDAP_BACKGROUND_SYNC=false
|
||||
#
|
||||
# LDAP_BACKGROUND_SYNC_INTERVAL : At which interval does the background task sync in milliseconds
|
||||
# The format must be as specified in:
|
||||
# https://bunkat.github.io/later/parsers.html#text
|
||||
#- LDAP_BACKGROUND_SYNC_INTERVAL=every 1 hours
|
||||
# At which interval does the background task sync in milliseconds.
|
||||
# Leave this unset, so it uses default, and does not crash.
|
||||
# https://github.com/wekan/wekan/issues/2354#issuecomment-515305722
|
||||
- LDAP_BACKGROUND_SYNC_INTERVAL=''
|
||||
#
|
||||
#- LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED=false
|
||||
#
|
||||
#- LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS=false
|
||||
#
|
||||
# LDAP_BACKGROUND_SYNC_DISABLE_NONEXISTANT_USERS : When true, the LDAP
|
||||
# background sync disables users no longer present in LDAP (and re-enables
|
||||
# them when they reappear).
|
||||
#- LDAP_BACKGROUND_SYNC_DISABLE_NONEXISTANT_USERS=false
|
||||
#
|
||||
# If using LDAPS: LDAP_ENCRYPTION=ssl
|
||||
#- LDAP_ENCRYPTION=false
|
||||
#
|
||||
# The certification for the LDAPS server. Certificate needs to be included in this docker compose.yml file.
|
||||
#- LDAP_CA_CERT=-----BEGIN CERTIFICATE-----MIIE+G2FIdAgIC...-----END CERTIFICATE-----
|
||||
#
|
||||
# Reject Unauthorized Certificate
|
||||
#- LDAP_REJECT_UNAUTHORIZED=false
|
||||
#
|
||||
# Optional extra LDAP filters. Don't forget the outmost enclosing parentheses if needed
|
||||
#- LDAP_USER_SEARCH_FILTER=
|
||||
#
|
||||
# base (search only in the provided DN), one (search only in the provided DN and one level deep), or sub (search the whole subtree)
|
||||
#- LDAP_USER_SEARCH_SCOPE=one
|
||||
#
|
||||
# Which field is used to find the user, like uid / sAMAccountName
|
||||
#- LDAP_USER_SEARCH_FIELD=sAMAccountName
|
||||
#
|
||||
# Used for pagination (0=unlimited)
|
||||
#- LDAP_SEARCH_PAGE_SIZE=0
|
||||
#
|
||||
# The limit number of entries (0=unlimited)
|
||||
#- LDAP_SEARCH_SIZE_LIMIT=0
|
||||
#
|
||||
# Enable the login restriction group filter. Note the authenticated ldap user must be able to query all relevant group data with own login data from ldap.
|
||||
# When true, only members of LDAP_GROUP_FILTER_GROUP_NAME are allowed to log in.
|
||||
# NOTE: This flag ONLY controls the login restriction. Admin status sync
|
||||
# (LDAP_SYNC_ADMIN_STATUS / LDAP_SYNC_ADMIN_GROUPS) and group->role sync
|
||||
# (LDAP_SYNC_GROUP_ROLES) query LDAP groups independently and do NOT require
|
||||
# this flag to be true. The group filter metadata below
|
||||
# (LDAP_GROUP_FILTER_OBJECTCLASS, LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE,
|
||||
# LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT, LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE)
|
||||
# must still be configured for any group search to work.
|
||||
#- LDAP_GROUP_FILTER_ENABLE=false
|
||||
#
|
||||
# The object class for filtering. Example: group
|
||||
#- LDAP_GROUP_FILTER_OBJECTCLASS=
|
||||
#
|
||||
# The attribute of a group identifying it. Example: cn
|
||||
#- LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE=
|
||||
#
|
||||
# The attribute inside a group object listing its members. Example: member
|
||||
#- LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE=
|
||||
#
|
||||
# The format of the value of LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE. Example: 'dn' if the users dn is saved as value into the attribute.
|
||||
#- LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT=
|
||||
#
|
||||
# The group name (id) that matches all users.
|
||||
#- LDAP_GROUP_FILTER_GROUP_NAME=
|
||||
#
|
||||
# LDAP_UNIQUE_IDENTIFIER_FIELD : This field is sometimes class GUID (Globally Unique Identifier). Example: guid
|
||||
#- LDAP_UNIQUE_IDENTIFIER_FIELD=
|
||||
#
|
||||
# LDAP_UTF8_NAMES_SLUGIFY : Convert the username to utf8
|
||||
#- LDAP_UTF8_NAMES_SLUGIFY=true
|
||||
#
|
||||
# LDAP_USERNAME_FIELD : Which field contains the ldap username. username / sAMAccountName
|
||||
#- LDAP_USERNAME_FIELD=sAMAccountName
|
||||
#
|
||||
# LDAP_FULLNAME_FIELD : Which field contains the ldap fullname. fullname / sAMAccountName
|
||||
#- LDAP_FULLNAME_FIELD=fullname
|
||||
#
|
||||
#- LDAP_MERGE_EXISTING_USERS=false
|
||||
#
|
||||
# Allow existing account matching by e-mail address when username does not match
|
||||
#- LDAP_EMAIL_MATCH_ENABLE=true
|
||||
#
|
||||
# LDAP_EMAIL_MATCH_REQUIRE : require existing account matching by e-mail address when username does match
|
||||
#- LDAP_EMAIL_MATCH_REQUIRE=true
|
||||
#
|
||||
# LDAP_EMAIL_MATCH_VERIFIED : require existing account email address to be verified for matching
|
||||
#- LDAP_EMAIL_MATCH_VERIFIED=true
|
||||
#
|
||||
# LDAP_EMAIL_FIELD : which field contains the LDAP e-mail address
|
||||
#- LDAP_EMAIL_FIELD=mail
|
||||
#-----------------------------------------------------------------
|
||||
#- LDAP_SYNC_USER_DATA=false
|
||||
#
|
||||
#- LDAP_SYNC_USER_DATA_FIELDMAP={"cn":"name", "mail":"email"}
|
||||
#
|
||||
#- LDAP_SYNC_GROUP_ROLES=
|
||||
#
|
||||
# The default domain of the ldap it is used to create email if the field is not map correctly
|
||||
# with the LDAP_SYNC_USER_DATA_FIELDMAP is defined in setting LDAP_DEFAULT_DOMAIN above.
|
||||
#
|
||||
# Enable/Disable syncing of admin status based on ldap groups:
|
||||
# NOTE: Admin status sync and group->role sync (LDAP_SYNC_GROUP_ROLES) query
|
||||
# LDAP groups on their own. They no longer require LDAP_GROUP_FILTER_ENABLE=true,
|
||||
# which only controls the login restriction filter. The group filter metadata
|
||||
# (LDAP_GROUP_FILTER_OBJECTCLASS, LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE,
|
||||
# LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT, LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE)
|
||||
# must still be configured for the group search to work.
|
||||
#- LDAP_SYNC_ADMIN_STATUS=true
|
||||
#
|
||||
# Comma separated list of admin group names to sync.
|
||||
#- LDAP_SYNC_ADMIN_GROUPS=group1,group2
|
||||
#
|
||||
# When true, sync a user's LDAP groups as Wekan Organizations.
|
||||
#- LDAP_SYNC_ORGANIZATIONS=false
|
||||
#
|
||||
# Comma separated allowlist of LDAP group names to sync as Organizations. Empty = all of the user's groups.
|
||||
#- LDAP_SYNC_ORGANIZATIONS_GROUPS=
|
||||
#
|
||||
# When true, sync a user's LDAP groups as Wekan Teams.
|
||||
#- LDAP_SYNC_TEAMS=false
|
||||
#
|
||||
# Comma separated allowlist of LDAP group names to sync as Teams. Empty = all of the user's groups.
|
||||
#- LDAP_SYNC_TEAMS_GROUPS=
|
||||
#---------------------------------------------------------------------
|
||||
# Login to LDAP automatically with HTTP header.
|
||||
# In below example for siteminder, at right side of = is header name.
|
||||
#- HEADER_LOGIN_ID=HEADERUID
|
||||
#- HEADER_LOGIN_FIRSTNAME=HEADERFIRSTNAME
|
||||
#- HEADER_LOGIN_LASTNAME=HEADERLASTNAME
|
||||
#- HEADER_LOGIN_EMAIL=HEADEREMAILADDRESS
|
||||
# SECURITY (GHSA-jggc-qvfc-jr6x): comma-separated allowlist of source IPs
|
||||
# permitted to use header login. The source IP is the real TCP peer of the
|
||||
# connection (your reverse proxy), NOT the spoofable X-Forwarded-For header.
|
||||
# REQUIRED when header login is enabled: if empty/unset, header login fails
|
||||
# CLOSED and authenticates no one.
|
||||
#- HEADER_LOGIN_TRUSTED_IPS=127.0.0.1,10.0.0.2
|
||||
# Optional: if WeKan sits behind MULTIPLE proxy hops, list the intermediate
|
||||
# proxy IPs here. X-Forwarded-For is then honored ONLY when the immediate
|
||||
# TCP peer is one of these trusted proxies, and the right-most hop that is
|
||||
# not itself a trusted proxy (the real client) is matched against
|
||||
# HEADER_LOGIN_TRUSTED_IPS above.
|
||||
#- HEADER_LOGIN_TRUSTED_PROXIES=10.0.0.1,10.0.0.2
|
||||
#---------------------------------------------------------------------
|
||||
# ==== LOGOUT TIMER, probably does not work yet ====
|
||||
# LOGOUT_WITH_TIMER : Enables or not the option logout with timer
|
||||
# example : LOGOUT_WITH_TIMER=true
|
||||
#- LOGOUT_WITH_TIMER=
|
||||
#
|
||||
# LOGOUT_IN : The number of days
|
||||
# example : LOGOUT_IN=1
|
||||
#- LOGOUT_IN=
|
||||
#
|
||||
# LOGOUT_ON_HOURS : The number of hours
|
||||
# example : LOGOUT_ON_HOURS=9
|
||||
#- LOGOUT_ON_HOURS=
|
||||
#
|
||||
# LOGOUT_ON_MINUTES : The number of minutes
|
||||
# example : LOGOUT_ON_MINUTES=55
|
||||
#- LOGOUT_ON_MINUTES=
|
||||
#-------------------------------------------------------------------
|
||||
# Hide password login form
|
||||
# - PASSWORD_LOGIN_ENABLED=true
|
||||
#-------------------------------------------------------------------
|
||||
#- CAS_ENABLED=true
|
||||
#- CAS_BASE_URL=https://cas.example.com/cas
|
||||
#- CAS_LOGIN_URL=https://cas.example.com/login
|
||||
#- CAS_VALIDATE_URL=https://cas.example.com/cas/p3/serviceValidate
|
||||
#---------------------------------------------------------------------
|
||||
#- SAML_ENABLED=true
|
||||
#- SAML_PROVIDER=
|
||||
#- SAML_ENTRYPOINT=
|
||||
#- SAML_ISSUER=
|
||||
#- SAML_CERT=
|
||||
#- SAML_IDPSLO_REDIRECTURL=
|
||||
#- SAML_PRIVATE_KEYFILE=
|
||||
#- SAML_PUBLIC_CERTFILE=
|
||||
#- SAML_IDENTIFIER_FORMAT=
|
||||
#- SAML_LOCAL_PROFILE_MATCH_ATTRIBUTE=
|
||||
#- SAML_ATTRIBUTES=
|
||||
#---------------------------------------------------------------------
|
||||
# Wait spinner to use
|
||||
# - WAIT_SPINNER=Bounce
|
||||
#---------------------------------------------------------------------
|
||||
# Wait for MongoDB to be healthy (replica-set primary elected) before
|
||||
# starting WeKan, so index creation does not crash with "Topology is closed".
|
||||
# If you use FerretDB instead, comment out the wekandb block below and
|
||||
# uncomment the wekan-ferretdb block.
|
||||
depends_on:
|
||||
ferretdb:
|
||||
condition: service_started
|
||||
# wekan-migrate:
|
||||
# condition: service_completed_successfully
|
||||
# wekan-ferretdb:
|
||||
# condition: service_healthy
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- wekan-files:/data:rw
|
||||
|
||||
volumes:
|
||||
wekan-files:
|
||||
driver: local
|
||||
ferretdb-data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
wekan-tier:
|
||||
driver: bridge
|
||||
@@ -0,0 +1,781 @@
|
||||
# WeKan with FerretDB 2 + PostgreSQL (DocumentDB extension)
|
||||
#
|
||||
# Use this compose file instead of docker-compose.yml on platforms where
|
||||
# MongoDB Community Edition is not available, or when you prefer PostgreSQL
|
||||
# as the backing store. For a lighter, single-container, no-PostgreSQL option,
|
||||
# see docker-compose-ferretdb-v1-sqlite.yml (FerretDB v1 with the embedded
|
||||
# SQLite backend, using the wekan/FerretDB release binaries).
|
||||
#
|
||||
# Supported on all platforms where Node.js 22 runs:
|
||||
# linux/amd64, linux/arm64, linux/arm/v7, linux/ppc64le, linux/s390x
|
||||
#
|
||||
# Based on: docs/Databases/FerretDB2-PostgreSQL.md
|
||||
#
|
||||
# Setup:
|
||||
# docker compose -f docker-compose-ferretdb-v2-postgresql.yml up -d
|
||||
#
|
||||
# To stop:
|
||||
# docker compose -f docker-compose-ferretdb-v2-postgresql.yml stop
|
||||
|
||||
version: '3.8'
|
||||
services:
|
||||
|
||||
# PostgreSQL 17 with the DocumentDB extension (FerretDB 2 backing store).
|
||||
postgres-documentdb:
|
||||
image: ghcr.io/ferretdb/postgres-documentdb:17
|
||||
container_name: wekan-postgres
|
||||
restart: always
|
||||
environment:
|
||||
POSTGRES_USER: ferretdb
|
||||
POSTGRES_PASSWORD: ${FERRETDB_POSTGRES_PASSWORD:-ferretdb_secret}
|
||||
POSTGRES_DB: ferretdb
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
networks:
|
||||
- wekan-tier
|
||||
|
||||
# FerretDB 2 - MongoDB-compatible protocol layer over PostgreSQL (on :27017).
|
||||
ferretdb:
|
||||
image: ghcr.io/ferretdb/ferretdb:2
|
||||
container_name: wekan-ferretdb
|
||||
restart: always
|
||||
environment:
|
||||
FERRETDB_POSTGRESQL_URL: postgres://ferretdb:${FERRETDB_POSTGRES_PASSWORD:-ferretdb_secret}@postgres-documentdb/ferretdb
|
||||
depends_on:
|
||||
- postgres-documentdb
|
||||
networks:
|
||||
- wekan-tier
|
||||
# Expose only on localhost by default; remove to keep it internal to the network.
|
||||
ports:
|
||||
- 127.0.0.1:27017:27017
|
||||
|
||||
wekan:
|
||||
#-------------------------------------------------------------------------------------
|
||||
# ==== WEKAN FROM GITHUB/QUAY/DOCKER HUB ====
|
||||
# All of GitHub, Quay and Docker Hub have latest, but because
|
||||
# latest tag changes when is newest release,
|
||||
# when upgrading would be better to use version tag.
|
||||
# a) Using specific version tag is better:
|
||||
# image: ghcr.io/wekan/wekan:v6.89
|
||||
# image: quay.io/wekan/wekan:v6.89
|
||||
# image: wekanteam/wekan:v6.89
|
||||
# b) GitHub Container registry.
|
||||
# b1) Latest release tag:
|
||||
image: ghcr.io/wekan/wekan:latest
|
||||
# b2) Newest git commit automatic build:
|
||||
#image: ghcr.io/wekan/wekan:main
|
||||
# c) Quay:
|
||||
#image: quay.io/wekan/wekan:latest
|
||||
# d) Docker Hub:
|
||||
#image: wekanteam/wekan:latest
|
||||
#-------------------------------------------------------------------------------------
|
||||
container_name: wekan-app
|
||||
# On CentOS 7 there is seccomp issue with glibc 6,
|
||||
# so CentOS 7 users shoud use these security_opt seccomp:unconfined
|
||||
# settings to get WeKan working. See:
|
||||
# - https://github.com/wekan/wekan/issues/4585
|
||||
# - https://github.com/wekan/wekan/issues/4587
|
||||
#security_opt:
|
||||
# - seccomp:unconfined
|
||||
restart: always
|
||||
networks:
|
||||
- wekan-tier
|
||||
#-------------------------------------------------------------------------------------
|
||||
# ==== BUILD wekan-app DOCKER CONTAINER FROM SOURCE, if you uncomment these ====
|
||||
# ==== and use commands: docker compose up -d --build
|
||||
#build:
|
||||
# context: .
|
||||
# dockerfile: Dockerfile
|
||||
#-------------------------------------------------------------------------------------
|
||||
ports:
|
||||
# Docker outsideport:insideport. Do not add anything extra here.
|
||||
# For example, if you want to have wekan on port 3001,
|
||||
# use 3001:8080 . Do not add any extra address etc here, that way it does not work.
|
||||
# remove port mapping if you use nginx reverse proxy, port 8080 is already exposed to wekan-tier network
|
||||
- 80:8080
|
||||
environment:
|
||||
#-------------------- USING MONGODB CHANGE STREAMS WITH REPLICA SETS AT CURRENT DATABASE --------------------
|
||||
# If you would not like to use Change Streams and replica set for improving speed, change to use polling:
|
||||
#- METEOR_REACTIVITY_ORDER=polling
|
||||
# https://forums.meteor.com/t/meteor-3-5-beta-change-streams-performance-improvements/64461#change-streams-setup-3
|
||||
# https://github.com/meteor/meteor/blob/release-3.5/v3-docs/docs/performance/change-streams-observer-driver.md#choosing-the-reactivity-driver-order
|
||||
# Keep oplog/polling fallback available for deployments that cannot use Change Streams:
|
||||
# https://github.com/wekan/wekan/issues/6307#issuecomment-4299349231
|
||||
# FerretDB has no MongoDB change streams / replica-set oplog, so use polling:
|
||||
- METEOR_REACTIVITY_ORDER=polling
|
||||
- DDP_TRANSPORT=uws
|
||||
#- DDP_TRANSPORT=sockjs
|
||||
#-----------------------------------------------------------------
|
||||
# ==== WRITEABLE PATH FOR FILE UPLOADS ====
|
||||
- WRITABLE_PATH=/data
|
||||
#-----------------------------------------------------------------
|
||||
# ==== AWS S3 FOR FILES ====
|
||||
# Any region. For example:
|
||||
# us-standard,us-west-1,us-west-2,
|
||||
# eu-west-1,eu-central-1,
|
||||
# ap-southeast-1,ap-northeast-1,sa-east-1
|
||||
#
|
||||
#- S3='{"s3":{"key": "xxx", "secret": "xxx", "bucket": "xxx", "region": "xxx"}}'
|
||||
#- S3_SECRET_FILE=/run/secrets/s3_secret
|
||||
#-----------------------------------------------------------------
|
||||
# ==== MONGO_URL — points to FerretDB (MongoDB-compatible) ====
|
||||
#- MONGO_URL=mongodb://wekan-ferretdb:27017/wekan
|
||||
- MONGO_URL=mongodb://ferretdb:${FERRETDB_POSTGRES_PASSWORD:-ferretdb_secret}@ferretdb:27017/wekan?authMechanism=PLAIN
|
||||
#- MONGO_PASSWORD_FILE=/run/secrets/mongo_password
|
||||
#---------------------------------------------------------------
|
||||
# ==== ROOT_URL SETTING ====
|
||||
# Change ROOT_URL to your real Wekan URL, for example:
|
||||
# If you have Caddy/Nginx/Apache providing SSL
|
||||
# - https://example.com
|
||||
# - https://boards.example.com
|
||||
# This can be problematic with avatars https://github.com/wekan/wekan/issues/1776
|
||||
# - https://example.com/wekan
|
||||
# If without https, can be only wekan node, no need for Caddy/Nginx/Apache if you don't need them
|
||||
# - http://example.com
|
||||
# - http://boards.example.com
|
||||
# - http://192.168.1.100 <=== using at local LAN
|
||||
- ROOT_URL=http://localhost # <=== using only at same laptop/desktop where Wekan is installed
|
||||
#---------------------------------------------------------------
|
||||
# ==== EMAIL SETTINGS ====
|
||||
# Email settings are only at MAIL_URL and MAIL_FROM.
|
||||
# Admin Panel has test button, but it's not used for settings.
|
||||
# see https://github.com/wekan/wekan/blob/main/docs/Email/Troubleshooting-Mail.md
|
||||
# For SSL in email, change smtp:// to smtps://
|
||||
# NOTE: Special characters need to be url-encoded in MAIL_URL.
|
||||
# You can encode those characters for example at: https://www.urlencoder.org
|
||||
#- MAIL_URL=smtp://user:pass@mailserver.example.com:25/
|
||||
- MAIL_URL=smtp://<mail_url>:25/?ignoreTLS=true&tls={rejectUnauthorized:false}
|
||||
- MAIL_FROM=Wekan Notifications <noreply.wekan@mydomain.com>
|
||||
# Currently MAIL_SERVICE is not in use.
|
||||
#- MAIL_SERVICE=Outlook365
|
||||
#- MAIL_SERVICE_USER=firstname.lastname@hotmail.com
|
||||
#- MAIL_SERVICE_PASSWORD=SecretPassword
|
||||
#- MAIL_SERVICE_PASSWORD_FILE=/run/secrets/mail_service_password
|
||||
#---------------------------------------------------------------
|
||||
# https://github.com/wekan/wekan/issues/3585#issuecomment-1021522132
|
||||
# Add more Node heap, this is done by default at Dockerfile:
|
||||
# - NODE_OPTIONS="--max_old_space_size=4096"
|
||||
# Add more stack, this is done at Dockerfile:
|
||||
# bash -c "ulimit -s 65500; exec node --stack-size=65500 main.js"
|
||||
#---------------------------------------------------------------
|
||||
# ==== OPTIONAL: MONGO OPLOG SETTINGS =====
|
||||
# https://github.com/wekan/wekan-mongodb/issues/2#issuecomment-378343587
|
||||
# HIGHLY RECOMMENDED for pub/sub performance!
|
||||
# MongoDB oplog is used by Meteor for real-time data synchronization.
|
||||
# Without oplog, Meteor falls back to polling which increases:
|
||||
# - CPU usage by 3-5x
|
||||
# - Network traffic significantly
|
||||
# - Latency from 50ms to 2000ms
|
||||
# Must configure MongoDB replica set first
|
||||
# See: https://blog.meteor.com/tuning-meteor-mongo-livedata-for-scalability-13fe9deb8908
|
||||
# FerretDB emulates OpLog via --replication-set-name=rs0.
|
||||
# HIGHLY RECOMMENDED — enables real-time DDP updates (sub-100ms latency).
|
||||
#- MONGO_OPLOG_URL=mongodb://wekan-ferretdb:27017/local?replicaSet=rs0
|
||||
# MONGO_OPLOG_URL intentionally unset: FerretDB v1 has no replica-set oplog (WeKan uses polling).
|
||||
#---------------------------------------------------------------
|
||||
# ==== OPTIONAL: KADIRA PERFORMANCE MONITORING FOR METEOR ====
|
||||
# https://github.com/edemaine/kadira-compose
|
||||
# https://github.com/meteor/meteor-apm-agent
|
||||
# https://blog.meteor.com/kadira-apm-is-now-open-source-490469ffc85f
|
||||
#- APM_OPTIONS_ENDPOINT=http://<kadira-ip>:11011
|
||||
#- APM_APP_ID=
|
||||
#- APM_APP_SECRET=
|
||||
#---------------------------------------------------------------
|
||||
# ==== OPTIONAL: LOGS AND STATS ====
|
||||
# https://github.com/wekan/wekan/blob/main/docs/Features/Logs.md
|
||||
#
|
||||
# Daily export of Wekan changes as JSON to Logstash and ElasticSearch / Kibana (ELK)
|
||||
# https://github.com/wekan/wekan-logstash
|
||||
#
|
||||
# Statistics Python script for Wekan Dashboard
|
||||
# https://github.com/wekan/wekan-stats
|
||||
#
|
||||
# Console, file, and zulip logger on database changes https://github.com/wekan/wekan/pull/1010
|
||||
# with fix to replace console.log by winston logger https://github.com/wekan/wekan/pull/1033
|
||||
# but there could be bug https://github.com/wekan/wekan/issues/1094
|
||||
#
|
||||
# There is Feature Request: Logging date and time of all activity with summary reports,
|
||||
# and requesting reason for changing card to other column https://github.com/wekan/wekan/issues/1598
|
||||
#---------------------------------------------------------------
|
||||
# ==== NUMBER OF SEARCH RESULTS PER PAGE BY DEFAULT ====
|
||||
#- RESULTS_PER_PAGE=20
|
||||
#---------------------------------------------------------------
|
||||
# ==== AFTER OIDC LOGIN, ADD USERS AUTOMATICALLY TO THIS BOARD ID ====
|
||||
# https://github.com/wekan/wekan/pull/5098
|
||||
#- DEFAULT_BOARD_ID=abcd1234
|
||||
#---------------------------------------------------------------
|
||||
# ==== WEKAN API AND EXPORT BOARD ====
|
||||
# Wekan Export Board works when WITH_API=true.
|
||||
# https://github.com/wekan/wekan/blob/main/docs/API/REST-API.md
|
||||
# https://github.com/wekan/wekan-gogs
|
||||
# If you disable Wekan API with false, Export Board does not work.
|
||||
- WITH_API=true
|
||||
#---------------------------------------------------------------
|
||||
# ==== PASSWORD BRUTE FORCE PROTECTION ====
|
||||
#https://atmospherejs.com/lucasantoniassi/accounts-lockout
|
||||
#Defaults below. Uncomment to change. wekan/server/accounts-lockout.js
|
||||
#- ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURES_BEFORE=3
|
||||
#- ACCOUNTS_LOCKOUT_KNOWN_USERS_PERIOD=60
|
||||
#- ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURE_WINDOW=15
|
||||
#- ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURES_BERORE=3
|
||||
#- ACCOUNTS_LOCKOUT_UNKNOWN_USERS_LOCKOUT_PERIOD=60
|
||||
#- ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURE_WINDOW=15
|
||||
#---------------------------------------------------------------
|
||||
# ==== ACCOUNT OPTIONS ====
|
||||
# https://docs.meteor.com/api/accounts-multi.html#AccountsCommon-config
|
||||
# Defaults below. Uncomment to change. wekan/server/accounts-common.js
|
||||
# - ACCOUNTS_COMMON_LOGIN_EXPIRATION_IN_DAYS=90
|
||||
#---------------------------------------------------------------
|
||||
# ==== RICH TEXT EDITOR IN CARD COMMENTS ====
|
||||
# https://github.com/wekan/wekan/pull/2560
|
||||
- RICHER_CARD_COMMENT_EDITOR=false
|
||||
#---------------------------------------------------------------
|
||||
# ==== CARD LOADING: all (default) or lazy ====
|
||||
# 'all' = load every card of a board into the browser (default).
|
||||
# 'lazy' = each list loads only the cards currently visible
|
||||
# (infinite scroll) plus a live count, so boards with
|
||||
# thousands of cards use less memory/bandwidth.
|
||||
# Also changeable at runtime in Admin Panel / Features.
|
||||
- CARDS_LOADING=all
|
||||
#---------------------------------------------------------------
|
||||
# ==== CARD OPENED, SEND WEBHOOK MESSAGE ====
|
||||
# https://github.com/wekan/wekan/issues/2518
|
||||
- CARD_OPENED_WEBHOOK_ENABLED=false
|
||||
#---------------------------------------------------------------
|
||||
# ==== Allow configuration to validate uploaded attachments ====
|
||||
#-ATTACHMENTS_UPLOAD_EXTERNAL_PROGRAM=/usr/local/bin/avscan {file}
|
||||
#-ATTACHMENTS_UPLOAD_MIME_TYPES=image/*,text/*
|
||||
#-ATTACHMENTS_UPLOAD_MAX_SIZE=5000000
|
||||
#---------------------------------------------------------------
|
||||
# ==== Allow configuration to validate uploaded avatars ====
|
||||
#-AVATARS_UPLOAD_EXTERNAL_PROGRAM=/usr/local/bin/avscan {file}
|
||||
#-AVATARS_UPLOAD_MIME_TYPES=image/*
|
||||
#-AVATARS_UPLOAD_MAX_SIZE=500000
|
||||
#---------------------------------------------------------------
|
||||
# ==== Allow to shrink attached/pasted image ====
|
||||
# https://github.com/wekan/wekan/pull/2544
|
||||
#- MAX_IMAGE_PIXEL=1024
|
||||
#- IMAGE_COMPRESS_RATIO=80
|
||||
#---------------------------------------------------------------
|
||||
# ==== NOTIFICATION TRAY AFTER READ DAYS BEFORE REMOVE =====
|
||||
# Number of days after a notification is read before we remove it.
|
||||
# Default: 2
|
||||
#- NOTIFICATION_TRAY_AFTER_READ_DAYS_BEFORE_REMOVE=2
|
||||
#---------------------------------------------------------------
|
||||
# ==== BIGEVENTS DUE ETC NOTIFICATIONS =====
|
||||
# https://github.com/wekan/wekan/pull/2541
|
||||
# Introduced a system env var BIGEVENTS_PATTERN default as "NONE",
|
||||
# so any activityType matches the pattern, system will send out
|
||||
# notifications to all board members no matter they are watching
|
||||
# or tracking the board or not. Owner of the wekan server can
|
||||
# disable the feature by setting this variable to "NONE" or
|
||||
# change the pattern to any valid regex. i.e. '|' delimited
|
||||
# activityType names.
|
||||
# a) Example
|
||||
#- BIGEVENTS_PATTERN=due
|
||||
# b) All
|
||||
#- BIGEVENTS_PATTERN=received|start|due|end
|
||||
# c) Disabled
|
||||
- BIGEVENTS_PATTERN=NONE
|
||||
#---------------------------------------------------------------
|
||||
# ==== NOTIFY ON ASSIGN =====
|
||||
# Notify the user directly when they are added as a card member or
|
||||
# assignee. Set false to disable.
|
||||
#- NOTIFY_ON_ASSIGN=true
|
||||
#---------------------------------------------------------------
|
||||
# ==== EMAIL DUE DATE NOTIFICATION =====
|
||||
# https://github.com/wekan/wekan/pull/2536
|
||||
# System timelines will be showing any user modification for
|
||||
# dueat startat endat receivedat, also notification to
|
||||
# the watchers and if any card is due, about due or past due.
|
||||
#
|
||||
# Notify due days, default is None, 2 days before and on the event day
|
||||
#- NOTIFY_DUE_DAYS_BEFORE_AND_AFTER=2,0
|
||||
#
|
||||
# Notify due at hour of day. Default every morning at 8am. Can be 0-23.
|
||||
# If env variable has parsing error, use default. Notification sent to watchers.
|
||||
#- NOTIFY_DUE_AT_HOUR_OF_DAY=8
|
||||
#-----------------------------------------------------------------
|
||||
# ==== EMAIL NOTIFICATION TIMEOUT, ms =====
|
||||
# Default: 30000 ms = 30s
|
||||
#- EMAIL_NOTIFICATION_TIMEOUT=30000
|
||||
#-----------------------------------------------------------------
|
||||
# ==== CORS =====
|
||||
# CORS: Set Access-Control-Allow-Origin header.
|
||||
#- CORS=*
|
||||
# CORS_ALLOW_HEADERS: Set Access-Control-Allow-Headers header. "Authorization,Content-Type" is required for cross-origin use of the API.
|
||||
#- CORS_ALLOW_HEADERS=Authorization,Content-Type
|
||||
# CORS_EXPOSE_HEADERS: Set Access-Control-Expose-Headers header. This is not needed for typical CORS situations
|
||||
#- CORS_EXPOSE_HEADERS=*
|
||||
#-----------------------------------------------------------------
|
||||
# ==== MATOMO INTEGRATION ====
|
||||
# Optional: Integration with Matomo https://matomo.org that is installed to your server
|
||||
# The address of the server where Matomo is hosted.
|
||||
#- MATOMO_ADDRESS=https://example.com/matomo
|
||||
# The value of the site ID given in Matomo server for Wekan
|
||||
#- MATOMO_SITE_ID=1
|
||||
# The option do not track which enables users to not be tracked by matomo
|
||||
#- MATOMO_DO_NOT_TRACK=true
|
||||
# The option that allows matomo to retrieve the username:
|
||||
#- MATOMO_WITH_USERNAME=true
|
||||
#-----------------------------------------------------------------
|
||||
# ==== BROWSER POLICY AND TRUSTED IFRAME URL ====
|
||||
# Enable browser policy and allow one trusted URL that can have iframe that has Wekan embedded inside.
|
||||
# Setting this to false is not recommended, it also disables all other browser policy protections
|
||||
# and allows all iframing etc. See wekan/server/policy.js
|
||||
- BROWSER_POLICY_ENABLED=true
|
||||
# When browser policy is enabled, HTML code at this Trusted URL can have iframe that embeds Wekan inside.
|
||||
#- TRUSTED_URL=https://intra.example.com
|
||||
#-----------------------------------------------------------------
|
||||
# ==== METRICS ALLOWED IP ADDRESSES ====
|
||||
# https://github.com/wekan/wekan/blob/main/docs/Features/Metrics.md
|
||||
#- METRICS_ALLOWED_IP_ADDRESSES=192.168.0.100,192.168.0.200
|
||||
#-----------------------------------------------------------------
|
||||
# ==== OUTGOING WEBHOOKS ====
|
||||
# What to send to Outgoing Webhook, or leave out. If commented out the default values will be: cardId,listId,oldListId,boardId,comment,user,card,commentId,swimlaneId,customerField,customFieldValue
|
||||
#- WEBHOOKS_ATTRIBUTES=cardId,listId,oldListId,boardId,comment,user,card,board,list,swimlane,commentId
|
||||
#-----------------------------------------------------------------
|
||||
# ==== Debug OIDC OAuth2 etc ====
|
||||
#- DEBUG=true
|
||||
#---------------------------------------------
|
||||
# ==== AUTOLOGIN WITH OIDC/OAUTH2 ====
|
||||
# https://github.com/wekan/wekan/blob/main/docs/Login/autologin.md
|
||||
#- OIDC_REDIRECTION_ENABLED=true
|
||||
# OIDC RP-initiated logout endpoint (end_session_endpoint). When set, the
|
||||
# "Log Out" menu redirects to this endpoint so the identity provider session
|
||||
# is also ended and the user is returned to Wekan (ROOT_URL) via
|
||||
# post_logout_redirect_uri, instead of landing on the provider home page
|
||||
# (which errors for non-admin users). See https://github.com/wekan/wekan/issues/6158
|
||||
# Keycloak example:
|
||||
#- OAUTH2_LOGOUT_ENDPOINT=/realms/<keycloak realm>/protocol/openid-connect/logout
|
||||
#-----------------------------------------------------------------
|
||||
# ==== OAUTH2 ORACLE on premise identity manager OIM ====
|
||||
#- ORACLE_OIM_ENABLED=true
|
||||
#-----------------------------------------------------------------
|
||||
# ==== OAUTH2 AZURE ====
|
||||
# https://github.com/wekan/wekan/blob/main/docs/Login/Azure/Azure.md
|
||||
# 1) Register the application with Azure. Make sure you capture
|
||||
# the application ID as well as generate a secret key.
|
||||
# 2) Configure the environment variables. This differs slightly
|
||||
# by installation type, but make sure you have the following:
|
||||
#- OAUTH2_ENABLED=true
|
||||
# Optional OAuth2 CA Cert, see https://github.com/wekan/wekan/issues/3299
|
||||
#- OAUTH2_CA_CERT=ABCD1234
|
||||
# Use OAuth2 ADFS additional changes. Also needs OAUTH2_ENABLED=true setting.
|
||||
#- OAUTH2_ADFS_ENABLED=false
|
||||
# Azure AD B2C. https://github.com/wekan/wekan/issues/5242
|
||||
#- OAUTH2_B2C_ENABLED=false
|
||||
# SECURITY (GHSA-mp7g-hj5q-gxhq): Link an OIDC login to a pre-existing
|
||||
# Wekan account (password/LDAP) with the same email. OFF by default; when
|
||||
# false an OIDC login whose email already exists is rejected instead of
|
||||
# merged, preventing account takeover via spoofed email claims. Only
|
||||
# enable if you fully trust your OIDC provider's email claims, and even
|
||||
# then the provider must send email_verified=true for the merge to happen.
|
||||
#- OAUTH2_MERGE_EXISTING_USERS=false
|
||||
# When false, OAuth2/OIDC login is refused for users that do not already
|
||||
# have a Wekan account (matched by verified email).
|
||||
#- OAUTH2_AUTO_REGISTRATION=true
|
||||
# Comma/space separated OAuth2/OIDC group names whose members become Wekan
|
||||
# admins. Empty = off.
|
||||
#- OAUTH2_ADMIN_GROUPS=
|
||||
# OAuth2 login style: popup or redirect.
|
||||
#- OAUTH2_LOGIN_STYLE=redirect
|
||||
# Application GUID captured during app registration:
|
||||
#- OAUTH2_CLIENT_ID=xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx
|
||||
# Secret key generated during app registration:
|
||||
#- OAUTH2_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
#- OAUTH2_SECRET_FILE=/run/secrets/oauth2_secret
|
||||
#- OAUTH2_SERVER_URL=https://login.microsoftonline.com/
|
||||
#- OAUTH2_AUTH_ENDPOINT=/oauth2/v2.0/authorize
|
||||
#- OAUTH2_USERINFO_ENDPOINT=https://graph.microsoft.com/oidc/userinfo
|
||||
#- OAUTH2_TOKEN_ENDPOINT=/oauth2/v2.0/token
|
||||
# The claim name you want to map to the unique ID field:
|
||||
#- OAUTH2_ID_MAP=email
|
||||
# The claim name you want to map to the username field:
|
||||
#- OAUTH2_USERNAME_MAP=email
|
||||
# The claim name you want to map to the full name field:
|
||||
#- OAUTH2_FULLNAME_MAP=name
|
||||
# The claim name you want to map to the email field:
|
||||
#- OAUTH2_EMAIL_MAP=email
|
||||
#-----------------------------------------------------------------
|
||||
# ==== OAUTH2 Nextcloud ====
|
||||
# 1) Register the application with Nextcloud: https://your.nextcloud/index.php/settings/admin/security
|
||||
# Make sure you capture the application ID as well as generate a secret key.
|
||||
# Use https://your.wekan/_oauth/oidc for the redirect URI.
|
||||
# 2) Configure the environment variables. This differs slightly
|
||||
# by installation type, but make sure you have the following:
|
||||
#- OAUTH2_ENABLED=true
|
||||
# OAuth2 login style: popup or redirect.
|
||||
#- OAUTH2_LOGIN_STYLE=redirect
|
||||
# Application GUID captured during app registration:
|
||||
#- OAUTH2_CLIENT_ID=xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx
|
||||
# Secret key generated during app registration:
|
||||
#- OAUTH2_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
#- OAUTH2_SECRET_FILE=/run/secrets/oauth2_secret
|
||||
#- OAUTH2_SERVER_URL=https://your-nextcloud.tld
|
||||
#- OAUTH2_AUTH_ENDPOINT=/index.php/apps/oauth2/authorize
|
||||
#- OAUTH2_USERINFO_ENDPOINT=/ocs/v2.php/cloud/user?format=json
|
||||
#- OAUTH2_TOKEN_ENDPOINT=/index.php/apps/oauth2/api/v1/token
|
||||
# The claim name you want to map to the unique ID field:
|
||||
#- OAUTH2_ID_MAP=id
|
||||
# The claim name you want to map to the username field:
|
||||
#- OAUTH2_USERNAME_MAP=id
|
||||
# The claim name you want to map to the full name field:
|
||||
#- OAUTH2_FULLNAME_MAP=display-name
|
||||
# The claim name you want to map to the email field:
|
||||
#- OAUTH2_EMAIL_MAP=email
|
||||
#-----------------------------------------------------------------
|
||||
# ==== OAUTH2 KEYCLOAK ====
|
||||
# https://github.com/wekan/wekan/blob/main/docs/Login/Keycloak/Keycloak.md <== MAPPING INFO, REQUIRED
|
||||
#- OAUTH2_ENABLED=true
|
||||
# OAuth2 login style: popup or redirect.
|
||||
#- OAUTH2_LOGIN_STYLE=redirect
|
||||
#- OAUTH2_CLIENT_ID=<Keycloak create Client ID>
|
||||
#- OAUTH2_SERVER_URL=<Keycloak server url - https://keycloak.example.com>
|
||||
#- OAUTH2_AUTH_ENDPOINT=/realms/<keycloak realm>/protocol/openid-connect/auth
|
||||
#- OAUTH2_USERINFO_ENDPOINT=/realms/<keycloak realm>/protocol/openid-connect/userinfo
|
||||
#- OAUTH2_TOKEN_ENDPOINT=/realms/<keycloak realm>/protocol/openid-connect/token
|
||||
#- OAUTH2_SECRET=<keycloak client secret>
|
||||
#- OAUTH2_SECRET_FILE=/run/secrets/oauth2_secret
|
||||
#- OAUTH2_ID_MAP=sub
|
||||
#- OAUTH2_USERNAME_MAP=preferred_username
|
||||
#- OAUTH2_EMAIL_MAP=email
|
||||
#- OAUTH2_FULLNAME_MAP=name
|
||||
#-----------------------------------------------------------------
|
||||
# ==== OAUTH2 DOORKEEPER ====
|
||||
# https://github.com/wekan/wekan/issues/1874
|
||||
# https://github.com/wekan/wekan/blob/main/docs/Login/OAuth2.md2
|
||||
# Enable the OAuth2 connection
|
||||
#- OAUTH2_ENABLED=true
|
||||
# OAuth2 docs: https://github.com/wekan/wekan/blob/main/docs/Login/OAuth2.md
|
||||
# OAuth2 login style: popup or redirect.
|
||||
#- OAUTH2_LOGIN_STYLE=redirect
|
||||
# OAuth2 Client ID.
|
||||
#- OAUTH2_CLIENT_ID=abcde12345
|
||||
# OAuth2 Secret.
|
||||
#- OAUTH2_SECRET=54321abcde
|
||||
#- OAUTH2_SECRET_FILE=/run/secrets/oauth2_secret
|
||||
# OAuth2 Server URL.
|
||||
#- OAUTH2_SERVER_URL=https://chat.example.com
|
||||
# OAuth2 Authorization Endpoint.
|
||||
#- OAUTH2_AUTH_ENDPOINT=/oauth/authorize
|
||||
# OAuth2 Userinfo Endpoint.
|
||||
#- OAUTH2_USERINFO_ENDPOINT=/oauth/userinfo
|
||||
# OAuth2 Token Endpoint.
|
||||
#- OAUTH2_TOKEN_ENDPOINT=/oauth/token
|
||||
# OAUTH2 ID Token Whitelist Fields.
|
||||
#- OAUTH2_ID_TOKEN_WHITELIST_FIELDS=""
|
||||
# OAUTH2 Request Permissions.
|
||||
#- OAUTH2_REQUEST_PERMISSIONS=openid profile email
|
||||
# OAuth2 ID Mapping
|
||||
#- OAUTH2_ID_MAP=
|
||||
# OAuth2 Username Mapping
|
||||
#- OAUTH2_USERNAME_MAP=
|
||||
# OAuth2 Fullname Mapping
|
||||
#- OAUTH2_FULLNAME_MAP=
|
||||
# OAuth2 Email Mapping
|
||||
#- OAUTH2_EMAIL_MAP=
|
||||
#-----------------------------------------------------------------
|
||||
# ==== LDAP: UNCOMMENT ALL TO ENABLE LDAP ====
|
||||
# https://github.com/wekan/wekan/blob/main/docs/Login/LDAP.md
|
||||
# For Snap settings see https://github.com/wekan/wekan/blob/main/docs/Platforms/FOSS/Snap/Supported-settings-keys.md
|
||||
# Most settings work both on Snap and Docker below.
|
||||
# Note: Do not add single quotes '' to variables. Having spaces still works without quotes where required.
|
||||
#
|
||||
# The default authentication method used if a user does not exist to create and authenticate. Can be set as ldap.
|
||||
# (this is set properly in the Admin Panel, changing this item does not remove Password login option)
|
||||
#- DEFAULT_AUTHENTICATION_METHOD=ldap
|
||||
#
|
||||
# Enable or not the connection by the LDAP
|
||||
#- LDAP_ENABLE=true
|
||||
#
|
||||
# The port of the LDAP server
|
||||
#- LDAP_PORT=389
|
||||
#
|
||||
# The host server for the LDAP server
|
||||
#- LDAP_HOST=localhost
|
||||
#
|
||||
#-----------------------------------------------------------------
|
||||
# ==== LDAP AD Simple Auth ====
|
||||
#
|
||||
# Set to true, if you want to connect with Active Directory by Simple Authentication.
|
||||
# When using AD Simple Auth, LDAP_BASEDN is not needed.
|
||||
#
|
||||
# Example:
|
||||
#- LDAP_AD_SIMPLE_AUTH=true
|
||||
#
|
||||
# === LDAP User Authentication ===
|
||||
#
|
||||
# a) Option to login to the LDAP server with the user's own username and password, instead of
|
||||
# an administrator key. Default: false (use administrator key).
|
||||
#
|
||||
# b) When using AD Simple Auth, set to true, when login user is used for binding,
|
||||
# and LDAP_BASEDN is not needed.
|
||||
#
|
||||
# Example:
|
||||
#- LDAP_USER_AUTHENTICATION=true
|
||||
#
|
||||
# Which field is used to find the user for the user authentication. Default: uid.
|
||||
#- LDAP_USER_AUTHENTICATION_FIELD=uid
|
||||
#
|
||||
# === LDAP Default Domain ===
|
||||
#
|
||||
# a) In case AD SimpleAuth is configured, the default domain is appended to the given
|
||||
# loginname for creating the correct username for the bind request to AD.
|
||||
#
|
||||
# b) The default domain of the ldap it is used to create email if the field is not map
|
||||
# correctly with the LDAP_SYNC_USER_DATA_FIELDMAP
|
||||
#
|
||||
# Example :
|
||||
#- LDAP_DEFAULT_DOMAIN=mydomain.com
|
||||
#
|
||||
#-----------------------------------------------------------------
|
||||
# ==== LDAP BASEDN Auth ====
|
||||
#
|
||||
# The base DN for the LDAP Tree
|
||||
#- LDAP_BASEDN=ou=user,dc=example,dc=org
|
||||
#
|
||||
#-----------------------------------------------------------------
|
||||
# Fallback on the default authentication method
|
||||
#- LDAP_LOGIN_FALLBACK=false
|
||||
#
|
||||
# Reconnect to the server if the connection is lost
|
||||
#- LDAP_RECONNECT=true
|
||||
#
|
||||
# Overall timeout, in milliseconds
|
||||
#- LDAP_TIMEOUT=10000
|
||||
#
|
||||
# Specifies the timeout for idle LDAP connections in milliseconds
|
||||
#- LDAP_IDLE_TIMEOUT=10000
|
||||
#
|
||||
# Connection timeout, in milliseconds
|
||||
#- LDAP_CONNECT_TIMEOUT=10000
|
||||
#
|
||||
# If the LDAP needs a user account to search
|
||||
#- LDAP_AUTHENTIFICATION=true
|
||||
#
|
||||
# The search user DN - You need quotes when you have spaces in parameters
|
||||
# 2 examples:
|
||||
#- LDAP_AUTHENTIFICATION_USERDN=CN=ldap admin,CN=users,DC=domainmatter,DC=lan
|
||||
#- LDAP_AUTHENTIFICATION_USERDN=CN=wekan_adm,OU=serviceaccounts,OU=admin,OU=prod,DC=mydomain,DC=com
|
||||
#
|
||||
# The password for the search user
|
||||
#- LDAP_AUTHENTIFICATION_PASSWORD=pwd
|
||||
#- LDAP_AUTHENTIFICATION_PASSWORD_FILE=/run/secrets/ldap_auth_password
|
||||
#
|
||||
# Enable logs for the module
|
||||
#- LDAP_LOG_ENABLED=true
|
||||
#
|
||||
# If the sync of the users should be done in the background
|
||||
#- LDAP_BACKGROUND_SYNC=false
|
||||
#
|
||||
# LDAP_BACKGROUND_SYNC_INTERVAL : At which interval does the background task sync in milliseconds
|
||||
# The format must be as specified in:
|
||||
# https://bunkat.github.io/later/parsers.html#text
|
||||
#- LDAP_BACKGROUND_SYNC_INTERVAL=every 1 hours
|
||||
# At which interval does the background task sync in milliseconds.
|
||||
# Leave this unset, so it uses default, and does not crash.
|
||||
# https://github.com/wekan/wekan/issues/2354#issuecomment-515305722
|
||||
- LDAP_BACKGROUND_SYNC_INTERVAL=''
|
||||
#
|
||||
#- LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED=false
|
||||
#
|
||||
#- LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS=false
|
||||
#
|
||||
# LDAP_BACKGROUND_SYNC_DISABLE_NONEXISTANT_USERS : When true, the LDAP
|
||||
# background sync disables users no longer present in LDAP (and re-enables
|
||||
# them when they reappear).
|
||||
#- LDAP_BACKGROUND_SYNC_DISABLE_NONEXISTANT_USERS=false
|
||||
#
|
||||
# If using LDAPS: LDAP_ENCRYPTION=ssl
|
||||
#- LDAP_ENCRYPTION=false
|
||||
#
|
||||
# The certification for the LDAPS server. Certificate needs to be included in this docker compose.yml file.
|
||||
#- LDAP_CA_CERT=-----BEGIN CERTIFICATE-----MIIE+G2FIdAgIC...-----END CERTIFICATE-----
|
||||
#
|
||||
# Reject Unauthorized Certificate
|
||||
#- LDAP_REJECT_UNAUTHORIZED=false
|
||||
#
|
||||
# Optional extra LDAP filters. Don't forget the outmost enclosing parentheses if needed
|
||||
#- LDAP_USER_SEARCH_FILTER=
|
||||
#
|
||||
# base (search only in the provided DN), one (search only in the provided DN and one level deep), or sub (search the whole subtree)
|
||||
#- LDAP_USER_SEARCH_SCOPE=one
|
||||
#
|
||||
# Which field is used to find the user, like uid / sAMAccountName
|
||||
#- LDAP_USER_SEARCH_FIELD=sAMAccountName
|
||||
#
|
||||
# Used for pagination (0=unlimited)
|
||||
#- LDAP_SEARCH_PAGE_SIZE=0
|
||||
#
|
||||
# The limit number of entries (0=unlimited)
|
||||
#- LDAP_SEARCH_SIZE_LIMIT=0
|
||||
#
|
||||
# Enable the login restriction group filter. Note the authenticated ldap user must be able to query all relevant group data with own login data from ldap.
|
||||
# When true, only members of LDAP_GROUP_FILTER_GROUP_NAME are allowed to log in.
|
||||
# NOTE: This flag ONLY controls the login restriction. Admin status sync
|
||||
# (LDAP_SYNC_ADMIN_STATUS / LDAP_SYNC_ADMIN_GROUPS) and group->role sync
|
||||
# (LDAP_SYNC_GROUP_ROLES) query LDAP groups independently and do NOT require
|
||||
# this flag to be true. The group filter metadata below
|
||||
# (LDAP_GROUP_FILTER_OBJECTCLASS, LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE,
|
||||
# LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT, LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE)
|
||||
# must still be configured for any group search to work.
|
||||
#- LDAP_GROUP_FILTER_ENABLE=false
|
||||
#
|
||||
# The object class for filtering. Example: group
|
||||
#- LDAP_GROUP_FILTER_OBJECTCLASS=
|
||||
#
|
||||
# The attribute of a group identifying it. Example: cn
|
||||
#- LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE=
|
||||
#
|
||||
# The attribute inside a group object listing its members. Example: member
|
||||
#- LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE=
|
||||
#
|
||||
# The format of the value of LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE. Example: 'dn' if the users dn is saved as value into the attribute.
|
||||
#- LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT=
|
||||
#
|
||||
# The group name (id) that matches all users.
|
||||
#- LDAP_GROUP_FILTER_GROUP_NAME=
|
||||
#
|
||||
# LDAP_UNIQUE_IDENTIFIER_FIELD : This field is sometimes class GUID (Globally Unique Identifier). Example: guid
|
||||
#- LDAP_UNIQUE_IDENTIFIER_FIELD=
|
||||
#
|
||||
# LDAP_UTF8_NAMES_SLUGIFY : Convert the username to utf8
|
||||
#- LDAP_UTF8_NAMES_SLUGIFY=true
|
||||
#
|
||||
# LDAP_USERNAME_FIELD : Which field contains the ldap username. username / sAMAccountName
|
||||
#- LDAP_USERNAME_FIELD=sAMAccountName
|
||||
#
|
||||
# LDAP_FULLNAME_FIELD : Which field contains the ldap fullname. fullname / sAMAccountName
|
||||
#- LDAP_FULLNAME_FIELD=fullname
|
||||
#
|
||||
#- LDAP_MERGE_EXISTING_USERS=false
|
||||
#
|
||||
# Allow existing account matching by e-mail address when username does not match
|
||||
#- LDAP_EMAIL_MATCH_ENABLE=true
|
||||
#
|
||||
# LDAP_EMAIL_MATCH_REQUIRE : require existing account matching by e-mail address when username does match
|
||||
#- LDAP_EMAIL_MATCH_REQUIRE=true
|
||||
#
|
||||
# LDAP_EMAIL_MATCH_VERIFIED : require existing account email address to be verified for matching
|
||||
#- LDAP_EMAIL_MATCH_VERIFIED=true
|
||||
#
|
||||
# LDAP_EMAIL_FIELD : which field contains the LDAP e-mail address
|
||||
#- LDAP_EMAIL_FIELD=mail
|
||||
#-----------------------------------------------------------------
|
||||
#- LDAP_SYNC_USER_DATA=false
|
||||
#
|
||||
#- LDAP_SYNC_USER_DATA_FIELDMAP={"cn":"name", "mail":"email"}
|
||||
#
|
||||
#- LDAP_SYNC_GROUP_ROLES=
|
||||
#
|
||||
# The default domain of the ldap it is used to create email if the field is not map correctly
|
||||
# with the LDAP_SYNC_USER_DATA_FIELDMAP is defined in setting LDAP_DEFAULT_DOMAIN above.
|
||||
#
|
||||
# Enable/Disable syncing of admin status based on ldap groups:
|
||||
# NOTE: Admin status sync and group->role sync (LDAP_SYNC_GROUP_ROLES) query
|
||||
# LDAP groups on their own. They no longer require LDAP_GROUP_FILTER_ENABLE=true,
|
||||
# which only controls the login restriction filter. The group filter metadata
|
||||
# (LDAP_GROUP_FILTER_OBJECTCLASS, LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE,
|
||||
# LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT, LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE)
|
||||
# must still be configured for the group search to work.
|
||||
#- LDAP_SYNC_ADMIN_STATUS=true
|
||||
#
|
||||
# Comma separated list of admin group names to sync.
|
||||
#- LDAP_SYNC_ADMIN_GROUPS=group1,group2
|
||||
#
|
||||
# When true, sync a user's LDAP groups as Wekan Organizations.
|
||||
#- LDAP_SYNC_ORGANIZATIONS=false
|
||||
#
|
||||
# Comma separated allowlist of LDAP group names to sync as Organizations. Empty = all of the user's groups.
|
||||
#- LDAP_SYNC_ORGANIZATIONS_GROUPS=
|
||||
#
|
||||
# When true, sync a user's LDAP groups as Wekan Teams.
|
||||
#- LDAP_SYNC_TEAMS=false
|
||||
#
|
||||
# Comma separated allowlist of LDAP group names to sync as Teams. Empty = all of the user's groups.
|
||||
#- LDAP_SYNC_TEAMS_GROUPS=
|
||||
#---------------------------------------------------------------------
|
||||
# Login to LDAP automatically with HTTP header.
|
||||
# In below example for siteminder, at right side of = is header name.
|
||||
#- HEADER_LOGIN_ID=HEADERUID
|
||||
#- HEADER_LOGIN_FIRSTNAME=HEADERFIRSTNAME
|
||||
#- HEADER_LOGIN_LASTNAME=HEADERLASTNAME
|
||||
#- HEADER_LOGIN_EMAIL=HEADEREMAILADDRESS
|
||||
# SECURITY (GHSA-jggc-qvfc-jr6x): comma-separated allowlist of source IPs
|
||||
# permitted to use header login. The source IP is the real TCP peer of the
|
||||
# connection (your reverse proxy), NOT the spoofable X-Forwarded-For header.
|
||||
# REQUIRED when header login is enabled: if empty/unset, header login fails
|
||||
# CLOSED and authenticates no one.
|
||||
#- HEADER_LOGIN_TRUSTED_IPS=127.0.0.1,10.0.0.2
|
||||
# Optional: if WeKan sits behind MULTIPLE proxy hops, list the intermediate
|
||||
# proxy IPs here. X-Forwarded-For is then honored ONLY when the immediate
|
||||
# TCP peer is one of these trusted proxies, and the right-most hop that is
|
||||
# not itself a trusted proxy (the real client) is matched against
|
||||
# HEADER_LOGIN_TRUSTED_IPS above.
|
||||
#- HEADER_LOGIN_TRUSTED_PROXIES=10.0.0.1,10.0.0.2
|
||||
#---------------------------------------------------------------------
|
||||
# ==== LOGOUT TIMER, probably does not work yet ====
|
||||
# LOGOUT_WITH_TIMER : Enables or not the option logout with timer
|
||||
# example : LOGOUT_WITH_TIMER=true
|
||||
#- LOGOUT_WITH_TIMER=
|
||||
#
|
||||
# LOGOUT_IN : The number of days
|
||||
# example : LOGOUT_IN=1
|
||||
#- LOGOUT_IN=
|
||||
#
|
||||
# LOGOUT_ON_HOURS : The number of hours
|
||||
# example : LOGOUT_ON_HOURS=9
|
||||
#- LOGOUT_ON_HOURS=
|
||||
#
|
||||
# LOGOUT_ON_MINUTES : The number of minutes
|
||||
# example : LOGOUT_ON_MINUTES=55
|
||||
#- LOGOUT_ON_MINUTES=
|
||||
#-------------------------------------------------------------------
|
||||
# Hide password login form
|
||||
# - PASSWORD_LOGIN_ENABLED=true
|
||||
#-------------------------------------------------------------------
|
||||
#- CAS_ENABLED=true
|
||||
#- CAS_BASE_URL=https://cas.example.com/cas
|
||||
#- CAS_LOGIN_URL=https://cas.example.com/login
|
||||
#- CAS_VALIDATE_URL=https://cas.example.com/cas/p3/serviceValidate
|
||||
#---------------------------------------------------------------------
|
||||
#- SAML_ENABLED=true
|
||||
#- SAML_PROVIDER=
|
||||
#- SAML_ENTRYPOINT=
|
||||
#- SAML_ISSUER=
|
||||
#- SAML_CERT=
|
||||
#- SAML_IDPSLO_REDIRECTURL=
|
||||
#- SAML_PRIVATE_KEYFILE=
|
||||
#- SAML_PUBLIC_CERTFILE=
|
||||
#- SAML_IDENTIFIER_FORMAT=
|
||||
#- SAML_LOCAL_PROFILE_MATCH_ATTRIBUTE=
|
||||
#- SAML_ATTRIBUTES=
|
||||
#---------------------------------------------------------------------
|
||||
# Wait spinner to use
|
||||
# - WAIT_SPINNER=Bounce
|
||||
#---------------------------------------------------------------------
|
||||
# Wait for MongoDB to be healthy (replica-set primary elected) before
|
||||
# starting WeKan, so index creation does not crash with "Topology is closed".
|
||||
# If you use FerretDB instead, comment out the wekandb block below and
|
||||
# uncomment the wekan-ferretdb block.
|
||||
depends_on:
|
||||
ferretdb:
|
||||
condition: service_started
|
||||
# wekan-migrate:
|
||||
# condition: service_completed_successfully
|
||||
# wekan-ferretdb:
|
||||
# condition: service_healthy
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- wekan-files:/data:rw
|
||||
|
||||
volumes:
|
||||
wekan-files:
|
||||
driver: local
|
||||
postgres-data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
wekan-tier:
|
||||
driver: bridge
|
||||
@@ -1,82 +0,0 @@
|
||||
# WeKan with FerretDB 2 + PostgreSQL (DocumentDB extension)
|
||||
#
|
||||
# Use this compose file instead of docker-compose.yml on platforms where
|
||||
# MongoDB Community Edition is not available, or when you prefer PostgreSQL
|
||||
# as the backing store.
|
||||
#
|
||||
# Supported on all platforms where Node.js 22 runs:
|
||||
# linux/amd64, linux/arm64, linux/arm/v7, linux/ppc64le, linux/s390x
|
||||
#
|
||||
# Based on: docs/Databases/FerretDB2-PostgreSQL.md
|
||||
#
|
||||
# Setup:
|
||||
# docker compose -f docker-compose-ferretdb.yml up -d
|
||||
#
|
||||
# To stop:
|
||||
# docker compose -f docker-compose-ferretdb.yml stop
|
||||
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
|
||||
# PostgreSQL 17 with DocumentDB extension (FerretDB 2 backing store)
|
||||
# ghcr.io/ferretdb/postgres-documentdb is the official PostgreSQL image
|
||||
# pre-installed with the pg_documentdb extension required by FerretDB 2.
|
||||
postgres-documentdb:
|
||||
image: ghcr.io/ferretdb/postgres-documentdb:17
|
||||
restart: always
|
||||
environment:
|
||||
POSTGRES_USER: ferretdb
|
||||
POSTGRES_PASSWORD: ${FERRETDB_POSTGRES_PASSWORD:-ferretdb_secret}
|
||||
POSTGRES_DB: ferretdb
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
networks:
|
||||
- wekan-ferretdb
|
||||
|
||||
# FerretDB 2 — MongoDB-compatible protocol layer over PostgreSQL
|
||||
ferretdb:
|
||||
image: ghcr.io/ferretdb/ferretdb:2
|
||||
restart: always
|
||||
environment:
|
||||
FERRETDB_POSTGRESQL_URL: postgres://ferretdb:${FERRETDB_POSTGRES_PASSWORD:-ferretdb_secret}@postgres-documentdb/ferretdb
|
||||
ports:
|
||||
# Expose the MongoDB-compatible port only on localhost for security.
|
||||
# Remove "127.0.0.1:" prefix if WeKan runs on a different host.
|
||||
- 127.0.0.1:27017:27017
|
||||
depends_on:
|
||||
- postgres-documentdb
|
||||
networks:
|
||||
- wekan-ferretdb
|
||||
|
||||
# WeKan application
|
||||
wekan-app:
|
||||
image: ghcr.io/wekan/wekan:latest
|
||||
restart: always
|
||||
networks:
|
||||
- wekan-ferretdb
|
||||
ports:
|
||||
- 8080:8080
|
||||
environment:
|
||||
# FerretDB speaks the MongoDB wire protocol on port 27017.
|
||||
# The database name "wekan" is created automatically on first run.
|
||||
MONGO_URL: mongodb://ferretdb:${FERRETDB_POSTGRES_PASSWORD:-ferretdb_secret}@ferretdb:27017/wekan?authMechanism=PLAIN
|
||||
ROOT_URL: ${ROOT_URL:-http://localhost:8080}
|
||||
PORT: 8080
|
||||
WITH_API: true
|
||||
WRITABLE_PATH: /data
|
||||
# Mail settings (optional)
|
||||
MAIL_URL: ${MAIL_URL:-}
|
||||
MAIL_FROM: ${MAIL_FROM:-}
|
||||
volumes:
|
||||
- wekan-data:/data
|
||||
depends_on:
|
||||
- ferretdb
|
||||
|
||||
volumes:
|
||||
postgres-data:
|
||||
wekan-data:
|
||||
|
||||
networks:
|
||||
wekan-ferretdb:
|
||||
driver: bridge
|
||||
@@ -454,6 +454,14 @@ services:
|
||||
# https://github.com/wekan/wekan/pull/2560
|
||||
- RICHER_CARD_COMMENT_EDITOR=false
|
||||
#---------------------------------------------------------------
|
||||
# ==== CARD LOADING: all (default) or lazy ====
|
||||
# 'all' = load every card of a board into the browser (default).
|
||||
# 'lazy' = each list loads only the cards currently visible
|
||||
# (infinite scroll) plus a live count, so boards with
|
||||
# thousands of cards use less memory/bandwidth.
|
||||
# Also changeable at runtime in Admin Panel / Features.
|
||||
- CARDS_LOADING=all
|
||||
#---------------------------------------------------------------
|
||||
# ==== CARD OPENED, SEND WEBHOOK MESSAGE ====
|
||||
# https://github.com/wekan/wekan/issues/2518
|
||||
- CARD_OPENED_WEBHOOK_ENABLED=false
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
# Alternative Architectures, JavaScript Engines and Rewrite Options
|
||||
|
||||
This page documents which CPU architectures WeKan can run on, **why** some
|
||||
architectures are not supported, and what the realistic options are for
|
||||
supporting more of them — alternative JavaScript engines and full server
|
||||
rewrites (Go, Tcl/Tk, QuickJS, …).
|
||||
|
||||
It exists because the question keeps coming up: *"WeKan won't run on my 32-bit
|
||||
ARM board / old x86 / ReactOS — can we fix that?"* The short answer is that the
|
||||
limit is **Node.js**, not the database, and getting past it means either
|
||||
compiling Node from source (where still possible) or rewriting the server in a
|
||||
portable language.
|
||||
|
||||
## TL;DR
|
||||
|
||||
- WeKan's **server** is Meteor 3, which runs **only on Node.js 24**. The browser
|
||||
client is architecture-independent. So the supported-architecture list is
|
||||
exactly *"where is there a Node.js 24 build?"*.
|
||||
- The **database is not the bottleneck**: [FerretDB v1](https://github.com/wekan/FerretDB)
|
||||
(pure Go, SQLite backend, MongoDB wire protocol) cross-compiles to far more
|
||||
architectures than Node does.
|
||||
- **Node.js 24 dropped 32-bit**: no `armv7l`/`armhf` binaries (downgraded to
|
||||
*Experimental*, source-build only), no 32-bit x86 Linux (`i386`) at all.
|
||||
- **Other JS runtimes do not help**: Deno and Bun cover *fewer* architectures
|
||||
than Node (x64/arm64 only). Portable engines (QuickJS, JavaScriptCore,
|
||||
SpiderMonkey, JVM-based Rhino/GraalJS) *do* run on 32-bit, but **none of them
|
||||
run Meteor/WeKan** — Meteor is locked to Node.
|
||||
- If universal architecture coverage is the goal, a **Go server** (matching
|
||||
FerretDB) is the best target; **Tcl/Tk and QuickJS are worse fits** for a web
|
||||
app. All of these are large rewrites, not drop-in swaps.
|
||||
|
||||
## The two layers
|
||||
|
||||
Keep these separate — they have very different portability:
|
||||
|
||||
| Layer | Technology | Portability |
|
||||
| --- | --- | --- |
|
||||
| Client | HTML/CSS/JS in the browser | Architecture-independent (runs wherever a browser runs) |
|
||||
| **Server** | **Meteor 3 → Node.js 24** | **Only where a Node.js 24 build exists** |
|
||||
| Database | MongoDB *or* FerretDB v1 (Go) | MongoDB: amd64/arm64 only. FerretDB: almost everything |
|
||||
|
||||
Only the **server binary** (the Node.js runtime) constrains the architecture.
|
||||
|
||||
## Architecture support matrix
|
||||
|
||||
"Node 24" is what limits WeKan; "FerretDB v1" shows the database is available
|
||||
well beyond that. Sources:
|
||||
[nodejs.org/dist/latest-v24.x](https://nodejs.org/dist/latest-v24.x/) (official),
|
||||
[unofficial-builds.nodejs.org](https://unofficial-builds.nodejs.org/download/release/)
|
||||
(extra arches), and [wekan/FerretDB releases](https://github.com/wekan/FerretDB/releases).
|
||||
|
||||
| Arch | Node.js 24 | MongoDB | FerretDB v1 | WeKan runs? |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| amd64 (x64) | ✅ official | ✅ | ✅ | ✅ |
|
||||
| arm64 (aarch64) | ✅ official | ✅ | ✅ | ✅ |
|
||||
| ppc64le | ✅ official | ❌ | ✅ | ✅ (FerretDB) |
|
||||
| s390x | ✅ official | ❌ | ✅ | ✅ (FerretDB) |
|
||||
| riscv64 | ✅ unofficial (glibc) | ❌ | ✅ | ✅ (FerretDB) |
|
||||
| **loong64** | ✅ unofficial (glibc) | ❌ | ✅ | ⚠️ binaries exist, but **not buildable in CI** — see below |
|
||||
| **armhf / armv7l** | ❌ (source-build only, Experimental) | ❌ | ✅ | ❌ (no Node binary) |
|
||||
| **armel (armv5)** | ❌ | ❌ | ✅ | ❌ (no Node binary) |
|
||||
| **i386 (32-bit x86 Linux)** | ❌ (port removed) | ❌ | ✅ | ❌ (no Node binary) |
|
||||
|
||||
### Why `armhf` / `armv7l` is not supported
|
||||
|
||||
`armhf` = `armv7l` = 32-bit ARMv7 hard-float (the same thing; the `l` is
|
||||
little-endian; Debian calls it `armhf`, Docker calls it `linux/arm/v7`).
|
||||
|
||||
Node.js **downgraded ARMv7 to "Experimental" in v24** ("Downgraded as of Node.js
|
||||
24" in Node's `BUILDING.md`). That means the source still compiles for ARMv7, but
|
||||
**no binaries are published or tested**. Neither nodejs.org nor
|
||||
unofficial-builds ship an `armv7l` tarball for v24, and NodeSource does not ship
|
||||
`armhf` for the 24.x line. Distributions (Debian, Alpine) build Node from source
|
||||
for `armhf`, but their versions lag behind 24.x and are their own (often musl)
|
||||
builds, not the portable glibc tarball WeKan's self-contained bundle embeds.
|
||||
|
||||
→ The only way to get an `armhf`/`armv7l` WeKan bundle is to **compile Node 24
|
||||
from source** in CI (slow under emulation, Experimental tier, unsupported).
|
||||
|
||||
### Why `i386` (32-bit x86 Linux) is not supported
|
||||
|
||||
Node.js **removed the 32-bit x86 Linux port** years ago. There is no binary and
|
||||
no practical source build. So 32-bit x86 Linux cannot run current WeKan at all,
|
||||
regardless of effort. (FerretDB has an `i386` binary, but there is nothing to run
|
||||
the WeKan server with.)
|
||||
|
||||
### Why `loong64` is not built in CI (even though binaries exist)
|
||||
|
||||
LoongArch64 has both a Node 24 unofficial-builds binary **and** a FerretDB v1
|
||||
binary, so in principle WeKan could run on it. But the release workflow cannot
|
||||
**build** the bundle/Docker image for it:
|
||||
|
||||
1. **QEMU cannot emulate LoongArch.** `docker/setup-qemu-action`
|
||||
([tonistiigi/binfmt](https://github.com/tonistiigi/binfmt)) supports
|
||||
`amd64, arm64, riscv64, ppc64le, s390x, 386, arm/v7, arm/v6` — **no
|
||||
loong64**. The extra-arch bundles rebuild native modules (e.g. bcrypt) by
|
||||
running an emulated container of the target arch; with no emulator, that step
|
||||
is impossible.
|
||||
2. **No official base image.** Ubuntu/Debian do not publish an official
|
||||
`linux/loong64` container image (LoongArch is a debian-ports / community
|
||||
architecture), so there is nothing to build inside.
|
||||
|
||||
FerretDB's own image builds for `linux/loong64` only because Go **cross-compiles**
|
||||
from the amd64 builder (it never runs loong64 code at build time). WeKan's native
|
||||
`.node` addons would instead need a real LoongArch build environment — a
|
||||
`loongarch64-linux-gnu` cross-toolchain plus a (non-mainstream) loong64 runtime
|
||||
image. That is significantly more work than the riscv64 path and is why loong64
|
||||
is currently left out.
|
||||
|
||||
## Alternative JavaScript engines
|
||||
|
||||
The engine itself (V8) *can* target 32-bit — the problem is that the **Node.js
|
||||
project stopped shipping those binaries** and **Meteor is bound to Node**.
|
||||
Swapping the engine does not run Meteor.
|
||||
|
||||
| Engine / runtime | armhf / armv7 | i386 | Runs Meteor/WeKan? |
|
||||
| --- | --- | --- | --- |
|
||||
| **Node.js 24** (V8) | source-build only (exp.) | ❌ | — (current) |
|
||||
| **Deno** (V8, Rust) | ❌ x64/arm64 only | ❌ | ❌ |
|
||||
| **Bun** (JavaScriptCore, Zig) | ❌ x64/arm64 only | ❌ | ❌ |
|
||||
| **QuickJS** (Bellard, pure C) | ✅ any arch | ✅ | ❌ (no Node API / npm) |
|
||||
| **Duktape / MuJS / JerryScript** | ✅ | ✅ | ❌ (ES5, embeddable) |
|
||||
| **JavaScriptCore / SpiderMonkey** | ✅ (WebKit/Firefox build there) | ✅ | ❌ |
|
||||
| **Rhino / GraalJS** (on a JVM) | ✅ (OpenJDK armhf/i386) | ✅ | ❌ |
|
||||
|
||||
Two takeaways:
|
||||
|
||||
- The modern Node alternatives, **Deno and Bun, are *worse*** for architecture
|
||||
coverage (x64/arm64 only) — they do not help with 32-bit.
|
||||
- Several engines (**QuickJS, JavaScriptCore, SpiderMonkey, JVM-based**) *do* run
|
||||
on `armhf`/`i386`, but **none run Meteor/WeKan**. Meteor requires Node.js
|
||||
specifically (V8 + libuv + Node's native APIs + Meteor's Node-based build), so
|
||||
using another engine means rewriting the server for that runtime — as large a
|
||||
job as a language rewrite.
|
||||
|
||||
## Full server rewrite options
|
||||
|
||||
If the goal is to run on architectures Node cannot reach, the server has to be
|
||||
rewritten in a portable language. The browser client stays as-is; the new server
|
||||
just serves the SPA, the API, reactive sync, accounts and methods, talking to
|
||||
FerretDB/MongoDB.
|
||||
|
||||
### Go (recommended for portability)
|
||||
|
||||
- **Cross-compiles everywhere** — `i386`, `armv7`, `armhf`, `armel`, `loong64`,
|
||||
`mips`, … exactly as [FerretDB](https://github.com/wekan/FerretDB) already does,
|
||||
as a single static binary with no Node-ABI or native-module problems.
|
||||
- Speaks the **MongoDB wire protocol natively** to FerretDB/MongoDB.
|
||||
- Mature HTTP/WebSocket ecosystem for the API and reactive sync.
|
||||
- **Cost:** this is effectively a **different product** — Meteor's DDP, reactive
|
||||
pub/sub, accounts, methods and client sync all have to be rebuilt. Very large
|
||||
effort.
|
||||
|
||||
### QuickJS (the "keep JavaScript" option)
|
||||
|
||||
- Pure C, compiles on virtually **any architecture**, tiny footprint.
|
||||
- **Cost:** no npm ecosystem and no Node API, so the server is written against a
|
||||
minimal runtime — about as much work as the Go rewrite, with a smaller
|
||||
ecosystem and weaker performance. Niche.
|
||||
|
||||
### Tcl/Tk (not recommended for this)
|
||||
|
||||
- **Tcl** is extremely portable (pure C, builds on any arch: `i386`, `armv7`,
|
||||
`armhf`, `armv5`, `mips`, …).
|
||||
- **But Tk is a desktop GUI toolkit**, not a web-server stack. WeKan is a
|
||||
**web application** (browser SPA + server + database); Tk would produce a
|
||||
native desktop window — a completely different product and UX.
|
||||
- Tcl *can* run a web server (NaviServer/AOLserver, tclhttpd, Wapp), but it is a
|
||||
niche stack with no reactive-SPA story. For a web app this is a **step
|
||||
backwards** compared to Go.
|
||||
|
||||
### Comparison
|
||||
|
||||
| Option | Arch coverage | Effort | Fit for WeKan (web app) |
|
||||
| --- | --- | --- | --- |
|
||||
| Compile Node 24 from source (armv7 only) | + armv7 (not i386) | Medium, fragile, Experimental | Keeps Meteor; no rewrite |
|
||||
| **Go server** | Universal (like FerretDB) | Very large (full rewrite) | Best; static binary, native FerretDB |
|
||||
| QuickJS server | Universal | Very large | OK; keeps JS, small ecosystem |
|
||||
| Tcl/Tk | Universal (Tcl) | Very large | Poor; GUI/scripting, not a web stack |
|
||||
|
||||
## Hardware / OS notes
|
||||
|
||||
These are about the **FerretDB (Go) binary** on specific targets; remember the
|
||||
WeKan *server* still needs a Node.js 24 build, which is the actual blocker above.
|
||||
|
||||
- **ODroid-U3 (ARMv7-A, Linux)** — FerretDB `armhf` (GOARM=7) runs fine. The
|
||||
board has enough RAM for FerretDB v1; only the missing Node 24 `armhf` binary
|
||||
stops WeKan itself.
|
||||
- **Raspberry Pi OS 32-bit** — FerretDB `armhf` (v7) runs on Pi 2/3/4 (ARMv7);
|
||||
Pi 1/Zero (ARMv6) need the `armel` (GOARM=5) binary. Again, the WeKan server is
|
||||
blocked only by Node.
|
||||
- **ReactOS 32-bit** — the matching binary would be FerretDB `win32`
|
||||
(windows/386), but it is **doubtful**: Go 1.21+ targets Windows 10 / Server
|
||||
2016-level APIs, while ReactOS is around XP/2003, so Go's runtime may use APIs
|
||||
ReactOS does not implement. (On 32-bit *Linux* the `linux/386` FerretDB binary
|
||||
works.)
|
||||
|
||||
## Conclusion
|
||||
|
||||
- The architecture limit is **Node.js**, not FerretDB. FerretDB (Go) already runs
|
||||
on 32-bit ARM, 32-bit x86 and more.
|
||||
- With current tooling, the practical WeKan set is **amd64, arm64, ppc64le,
|
||||
s390x, riscv64**. `loong64` is blocked by tooling (no QEMU emulation, no base
|
||||
image), not by Node/FerretDB. `armhf`/`armel`/`i386` are blocked by Node.
|
||||
- Alternative JS engines do **not** unblock this — Deno/Bun cover fewer arches,
|
||||
and portable engines cannot run Meteor.
|
||||
- The only way to universal architecture coverage is a **portable-language server
|
||||
rewrite**, for which **Go is the best fit** (matching FerretDB), QuickJS the
|
||||
"keep JS" niche option, and Tcl/Tk a poor fit for a web application.
|
||||
@@ -10,7 +10,7 @@ This is without container (without Docker or Snap).
|
||||
|
||||
Right click and download files 1-4:
|
||||
|
||||
1. [wekan-9.74-win64.zip](https://github.com/wekan/wekan/releases/download/v9.74/wekan-9.74-win64.zip) or newest available at https://github.com/wekan/wekan/releases/
|
||||
1. [wekan-9.84-win64.zip](https://github.com/wekan/wekan/releases/download/v9.84/wekan-9.84-win64.zip) or newest available at https://github.com/wekan/wekan/releases/
|
||||
|
||||
2. [node.exe](https://nodejs.org/dist/latest-v24.x/win-x64/node.exe)
|
||||
|
||||
@@ -22,7 +22,7 @@ Right click and download files 1-4:
|
||||
|
||||
6. Double click `mongodb-windows-x86_64-7.0.34-signed.msi` . In installer, uncheck downloading MongoDB compass.
|
||||
|
||||
7. Unzip `wekan-9.74-win64.zip` , inside it is directory `bundle`, to it copy other files:
|
||||
7. Unzip `wekan-9.84-win64.zip` , inside it is directory `bundle`, to it copy other files:
|
||||
|
||||
```
|
||||
bundle (directory)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accept": "Menerima",
|
||||
"activity-changedTitle": "changed title to %s of %s",
|
||||
"activity-changedDescription": "changed description of %s",
|
||||
"act-deleteCard": "deleted card __card__ from list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-removeBoard": "deleted board __board__",
|
||||
"act-removeList": "deleted list __list__ at board __board__",
|
||||
@@ -441,6 +442,7 @@
|
||||
"createCustomField": "Cipta ruangan",
|
||||
"createCustomFieldPopup-title": "Cipta ruangan",
|
||||
"current": "sekarang",
|
||||
"custom-color": "Custom color",
|
||||
"custom-field-delete-pop": "Tidak boleh undur semula. Ianya akan menghapuskan ruangan khas daripada semua kad dan padamkan sejarahnya.",
|
||||
"custom-field-checkbox": "kotak semak",
|
||||
"custom-field-currency": "Matawang",
|
||||
@@ -508,6 +510,7 @@
|
||||
"error-csv-schema": "CSV anda(Nilai yang dipisahkan koma)/TSV (Nilai yang dipisahkan tab) tidak termasuk informasi yang tepat dalam format yang benar",
|
||||
"error-import-empty-board": "This export contains no board content (no swimlanes, lists or cards). It may have been created by an older WeKan version with a known export bug. Please re-export the board with the current version and import the new file.",
|
||||
"error-list-doesNotExist": "Daftar ini tidak ada",
|
||||
"error-linked-card-not-allowed": "You can only link to a normal card. Linking to another linked card, or to a card that links back to this board, is not allowed because it would make the cards inaccessible.",
|
||||
"error-user-disabled": "This user account is disabled",
|
||||
"error-user-doesNotExist": "Nama pengguna ini tidak ada",
|
||||
"error-user-notAllowSelf": "Anda tidak bisa mengundang diri sendiri",
|
||||
@@ -581,6 +584,7 @@
|
||||
"fullname": "Nama Lengkap",
|
||||
"header-logo-title": "Kembali ke laman panel anda",
|
||||
"show-activities": "Tunjukkan Aktiviti",
|
||||
"hide-activities": "Hide Activities",
|
||||
"headerBarCreateBoardPopup-title": "Buat Panel",
|
||||
"home": "Beranda",
|
||||
"import": "Impor",
|
||||
@@ -894,7 +898,12 @@
|
||||
"no-name": "(Tidak Diketahui)",
|
||||
"Node_version": "Versi Node",
|
||||
"Meteor_version": "Versi Meteor",
|
||||
"MongoDB_version": "Versi MongoDB",
|
||||
"Database_type": "Database type",
|
||||
"Database_commit": "Database commit",
|
||||
"FerretDB_version": "FerretDB version",
|
||||
"FerretDB_commit": "FerretDB commit",
|
||||
"Reactivity_mode": "Reactivity mode (changeStreams / oplog / polling)",
|
||||
"MongoDB_version": "MongoDB compatible version",
|
||||
"MongoDB_storage_engine": "Mesin penyimpanan MongoDb",
|
||||
"MongoDB_Oplog_enabled": "Oplog MongoDB dihidupkan",
|
||||
"OS_Arch": "Arch OS",
|
||||
@@ -1015,6 +1024,17 @@
|
||||
"r-workflow-help": "Drag a trigger and an action into the builder, then add the rule. Drag an action onto an existing rule to change what it does.",
|
||||
"r-drop-trigger": "drop a trigger here",
|
||||
"r-drop-action": "drop an action here",
|
||||
"r-w-card-created": "Card is created",
|
||||
"r-w-card-archived": "Card is archived",
|
||||
"r-w-card-unarchived": "Card is unarchived",
|
||||
"r-w-label-added": "Any label is added",
|
||||
"r-w-label-removed": "Any label is removed",
|
||||
"r-w-member-added": "A member is added",
|
||||
"r-w-member-removed": "A member is removed",
|
||||
"r-w-checklist-added": "A checklist is added",
|
||||
"r-w-attachment-added": "An attachment is added",
|
||||
"r-w-every-day-at": "Every day at __time__",
|
||||
"r-w-set-received-now": "Set received date to now",
|
||||
"r-export-json": "Export rules to JSON",
|
||||
"r-export-csv": "Export rules to CSV",
|
||||
"r-import-json": "Import rules from JSON",
|
||||
@@ -1068,6 +1088,12 @@
|
||||
"r-mark-incomplete": "Mark card incomplete",
|
||||
"r-move-all-cards": "Move all cards in list",
|
||||
"r-set-date-relative": "Set date relative to now",
|
||||
"r-later": "later",
|
||||
"r-unit-minutes": "minute(s)",
|
||||
"r-unit-hours": "hour(s)",
|
||||
"r-unit-days": "day(s)",
|
||||
"r-unit-weeks": "week(s)",
|
||||
"r-unit-months": "month(s)",
|
||||
"r-trigger": "Picu",
|
||||
"r-action": "Tindakan",
|
||||
"r-when-a-card": "Ketika kartu",
|
||||
@@ -1809,6 +1835,45 @@
|
||||
"azure-container": "Container",
|
||||
"gcs-storage": "Google Cloud Storage",
|
||||
"gcs-storage-description": "Google Cloud Storage.",
|
||||
"database-migration": "Database migration",
|
||||
"database-migration-description": "Migrate text-based data (everything except attachments and avatars, which stay on the filesystem) between MongoDB and FerretDB v1 (SQLite). The other database must be running and reachable (default ports: FerretDB mongodb://127.0.0.1:27018, MongoDB mongodb://127.0.0.1:27019; override with WEKAN_FERRETDB_URL / WEKAN_MONGODB_URL). After it finishes, point MONGO_URL at the other database and restart WeKan (on Snap: snap set wekan database=ferretdb or =mongodb).",
|
||||
"database-migrate-to-ferretdb": "Migrate text data to FerretDB v1 (SQLite)",
|
||||
"database-migrate-to-mongodb": "Migrate text data to MongoDB",
|
||||
"database-migration-confirm": "Copy all text-based collections to __db__? Attachments and avatars on the filesystem are not affected. The target database must be running.",
|
||||
"database-migration-phase": "Phase",
|
||||
"database-migration-done": "Text-data migration completed.",
|
||||
"collections": "collections",
|
||||
"features": "Features",
|
||||
"features-performance": "Performance",
|
||||
"cards-loading": "Card loading",
|
||||
"cards-loading-all": "All cards (default)",
|
||||
"cards-loading-lazy": "Lazy (only visible cards)",
|
||||
"cards-loading-description": "How each board loads its cards. All cards: every card is loaded into the browser (simplest, all features work, but a board with thousands of cards sends a lot of data to each viewer). Lazy: each list loads only the cards currently visible (infinite scroll) plus a live count, so very large boards use less memory and bandwidth. Also set by the CARDS_LOADING environment variable.",
|
||||
"cards-loading-lazy-note": "Lazy card loading is experimental: card counters and WIP limits are accurate, but the Calendar/Table/Gantt views and multi-select reflect only the cards loaded so far. Reload open boards after changing this.",
|
||||
"backup": "Backup",
|
||||
"backup-description": "Back up Attachments, Avatars and/or Data (all text-based collections that are not attachments or avatars). The .zip is streamed directly to the selected storage — no temporary file — as backup/YYYY/MM/DD/HH_MM_SS/backup.zip, containing YYYY_MM_DD-HH_MM_SS/attachments, /avatars and /data. Cloud storages (S3/MinIO, Azure, GCS) use the credentials configured in their storage tabs.",
|
||||
"backup-data": "Data (text)",
|
||||
"backup-storage": "Save to storage",
|
||||
"backup-now": "Backup now",
|
||||
"backup-done": "Backup completed",
|
||||
"backup-schedule": "Scheduled backups",
|
||||
"backup-frequency": "Frequency",
|
||||
"backup-frequency-off": "Off",
|
||||
"backup-frequency-daily": "Daily",
|
||||
"backup-frequency-weekly": "Weekly",
|
||||
"backup-frequency-monthly": "Monthly",
|
||||
"backup-time": "Time (HH:MM)",
|
||||
"backup-day-of-week": "Day of week (weekly)",
|
||||
"backup-day-of-month": "Day of month (monthly, 1-28)",
|
||||
"backup-restore": "Pulihkan",
|
||||
"backup-list": "List backups",
|
||||
"backup-datetime": "Date and time",
|
||||
"backup-path": "File path",
|
||||
"backup-restore-mode": "Restore mode",
|
||||
"backup-restore-add-missing": "Add missing data only",
|
||||
"backup-restore-replace-all": "Replace all data",
|
||||
"backup-restore-select-first": "Select a backup from the list first.",
|
||||
"backup-restore-confirm": "Restore the selected backup? Attachments/avatars are written to the filesystem and data to the database according to the chosen mode.",
|
||||
"gcs-project-id": "Project ID",
|
||||
"gcs-bucket": "Bucket",
|
||||
"gcs-key-filename": "Service account key file path",
|
||||
@@ -1986,8 +2051,6 @@
|
||||
"cpu-cores": "CPU Cores",
|
||||
"cpu-usage": "CPU Usage",
|
||||
"current-action": "Current Action",
|
||||
"database-migration": "Database Migration",
|
||||
"database-migration-description": "Updating database structure for improved functionality and performance. This process may take several minutes.",
|
||||
"database-migrations": "Database Migrations",
|
||||
"days-old": "Days Old",
|
||||
"duration": "Duration",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accept": "Aanvaar",
|
||||
"activity-changedTitle": "changed title to %s of %s",
|
||||
"activity-changedDescription": "changed description of %s",
|
||||
"act-deleteCard": "deleted card __card__ from list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-removeBoard": "deleted board __board__",
|
||||
"act-removeList": "deleted list __list__ at board __board__",
|
||||
@@ -441,6 +442,7 @@
|
||||
"createCustomField": "Skep veld",
|
||||
"createCustomFieldPopup-title": "Skep veld",
|
||||
"current": "huidige",
|
||||
"custom-color": "Custom color",
|
||||
"custom-field-delete-pop": "Daar is geen ongedaan maak nie. Dit sal hierdie pasgemaakte veld van alle kaarte verwyder en sy geskiedenis vernietig.",
|
||||
"custom-field-checkbox": "Merkblokkie",
|
||||
"custom-field-currency": "Geldeenheid",
|
||||
@@ -508,6 +510,7 @@
|
||||
"error-csv-schema": "Jou CSV (Comma Separated Values)/TSV (Tab Separated Values) sluit nie die korrekte inligting in die korrekte formaat in nie",
|
||||
"error-import-empty-board": "This export contains no board content (no swimlanes, lists or cards). It may have been created by an older WeKan version with a known export bug. Please re-export the board with the current version and import the new file.",
|
||||
"error-list-doesNotExist": "Hierdie lys bestaan nie",
|
||||
"error-linked-card-not-allowed": "You can only link to a normal card. Linking to another linked card, or to a card that links back to this board, is not allowed because it would make the cards inaccessible.",
|
||||
"error-user-disabled": "This user account is disabled",
|
||||
"error-user-doesNotExist": "Hierdie gebruiker bestaan nie",
|
||||
"error-user-notAllowSelf": "Jy kan nie jouself nooi nie",
|
||||
@@ -581,6 +584,7 @@
|
||||
"fullname": "Volle Naam",
|
||||
"header-logo-title": "Gaan terug na jou bordbladsy.",
|
||||
"show-activities": "Wys aktiwiteite",
|
||||
"hide-activities": "Hide Activities",
|
||||
"headerBarCreateBoardPopup-title": "Skep bord",
|
||||
"home": "Tuis",
|
||||
"import": "Invoer",
|
||||
@@ -894,7 +898,12 @@
|
||||
"no-name": "(Onbekend)",
|
||||
"Node_version": "Node weergawe",
|
||||
"Meteor_version": "Meteoor weergawe",
|
||||
"MongoDB_version": "MongoDB weergawe",
|
||||
"Database_type": "Database type",
|
||||
"Database_commit": "Database commit",
|
||||
"FerretDB_version": "FerretDB version",
|
||||
"FerretDB_commit": "FerretDB commit",
|
||||
"Reactivity_mode": "Reactivity mode (changeStreams / oplog / polling)",
|
||||
"MongoDB_version": "MongoDB compatible version",
|
||||
"MongoDB_storage_engine": "MongoDB stoor enjin",
|
||||
"MongoDB_Oplog_enabled": "MongoDB Oplog geaktiveer",
|
||||
"OS_Arch": "OS Arch",
|
||||
@@ -1015,6 +1024,17 @@
|
||||
"r-workflow-help": "Drag a trigger and an action into the builder, then add the rule. Drag an action onto an existing rule to change what it does.",
|
||||
"r-drop-trigger": "drop a trigger here",
|
||||
"r-drop-action": "drop an action here",
|
||||
"r-w-card-created": "Card is created",
|
||||
"r-w-card-archived": "Card is archived",
|
||||
"r-w-card-unarchived": "Card is unarchived",
|
||||
"r-w-label-added": "Any label is added",
|
||||
"r-w-label-removed": "Any label is removed",
|
||||
"r-w-member-added": "A member is added",
|
||||
"r-w-member-removed": "A member is removed",
|
||||
"r-w-checklist-added": "A checklist is added",
|
||||
"r-w-attachment-added": "An attachment is added",
|
||||
"r-w-every-day-at": "Every day at __time__",
|
||||
"r-w-set-received-now": "Set received date to now",
|
||||
"r-export-json": "Export rules to JSON",
|
||||
"r-export-csv": "Export rules to CSV",
|
||||
"r-import-json": "Import rules from JSON",
|
||||
@@ -1068,6 +1088,12 @@
|
||||
"r-mark-incomplete": "Mark card incomplete",
|
||||
"r-move-all-cards": "Move all cards in list",
|
||||
"r-set-date-relative": "Set date relative to now",
|
||||
"r-later": "later",
|
||||
"r-unit-minutes": "minute(s)",
|
||||
"r-unit-hours": "hour(s)",
|
||||
"r-unit-days": "day(s)",
|
||||
"r-unit-weeks": "week(s)",
|
||||
"r-unit-months": "month(s)",
|
||||
"r-trigger": "Sneller",
|
||||
"r-action": "Aksie",
|
||||
"r-when-a-card": "Wanneer 'n kaart",
|
||||
@@ -1809,6 +1835,45 @@
|
||||
"azure-container": "Container",
|
||||
"gcs-storage": "Google Cloud Storage",
|
||||
"gcs-storage-description": "Google Cloud Storage.",
|
||||
"database-migration": "Database migration",
|
||||
"database-migration-description": "Migrate text-based data (everything except attachments and avatars, which stay on the filesystem) between MongoDB and FerretDB v1 (SQLite). The other database must be running and reachable (default ports: FerretDB mongodb://127.0.0.1:27018, MongoDB mongodb://127.0.0.1:27019; override with WEKAN_FERRETDB_URL / WEKAN_MONGODB_URL). After it finishes, point MONGO_URL at the other database and restart WeKan (on Snap: snap set wekan database=ferretdb or =mongodb).",
|
||||
"database-migrate-to-ferretdb": "Migrate text data to FerretDB v1 (SQLite)",
|
||||
"database-migrate-to-mongodb": "Migrate text data to MongoDB",
|
||||
"database-migration-confirm": "Copy all text-based collections to __db__? Attachments and avatars on the filesystem are not affected. The target database must be running.",
|
||||
"database-migration-phase": "Phase",
|
||||
"database-migration-done": "Text-data migration completed.",
|
||||
"collections": "collections",
|
||||
"features": "Features",
|
||||
"features-performance": "Performance",
|
||||
"cards-loading": "Card loading",
|
||||
"cards-loading-all": "All cards (default)",
|
||||
"cards-loading-lazy": "Lazy (only visible cards)",
|
||||
"cards-loading-description": "How each board loads its cards. All cards: every card is loaded into the browser (simplest, all features work, but a board with thousands of cards sends a lot of data to each viewer). Lazy: each list loads only the cards currently visible (infinite scroll) plus a live count, so very large boards use less memory and bandwidth. Also set by the CARDS_LOADING environment variable.",
|
||||
"cards-loading-lazy-note": "Lazy card loading is experimental: card counters and WIP limits are accurate, but the Calendar/Table/Gantt views and multi-select reflect only the cards loaded so far. Reload open boards after changing this.",
|
||||
"backup": "Backup",
|
||||
"backup-description": "Back up Attachments, Avatars and/or Data (all text-based collections that are not attachments or avatars). The .zip is streamed directly to the selected storage — no temporary file — as backup/YYYY/MM/DD/HH_MM_SS/backup.zip, containing YYYY_MM_DD-HH_MM_SS/attachments, /avatars and /data. Cloud storages (S3/MinIO, Azure, GCS) use the credentials configured in their storage tabs.",
|
||||
"backup-data": "Data (text)",
|
||||
"backup-storage": "Save to storage",
|
||||
"backup-now": "Backup now",
|
||||
"backup-done": "Backup completed",
|
||||
"backup-schedule": "Scheduled backups",
|
||||
"backup-frequency": "Frequency",
|
||||
"backup-frequency-off": "Off",
|
||||
"backup-frequency-daily": "Daily",
|
||||
"backup-frequency-weekly": "Weekly",
|
||||
"backup-frequency-monthly": "Monthly",
|
||||
"backup-time": "Time (HH:MM)",
|
||||
"backup-day-of-week": "Day of week (weekly)",
|
||||
"backup-day-of-month": "Day of month (monthly, 1-28)",
|
||||
"backup-restore": "Herstel",
|
||||
"backup-list": "List backups",
|
||||
"backup-datetime": "Date and time",
|
||||
"backup-path": "File path",
|
||||
"backup-restore-mode": "Restore mode",
|
||||
"backup-restore-add-missing": "Add missing data only",
|
||||
"backup-restore-replace-all": "Replace all data",
|
||||
"backup-restore-select-first": "Select a backup from the list first.",
|
||||
"backup-restore-confirm": "Restore the selected backup? Attachments/avatars are written to the filesystem and data to the database according to the chosen mode.",
|
||||
"gcs-project-id": "Project ID",
|
||||
"gcs-bucket": "Bucket",
|
||||
"gcs-key-filename": "Service account key file path",
|
||||
@@ -1986,8 +2051,6 @@
|
||||
"cpu-cores": "SVE-kerns",
|
||||
"cpu-usage": "SVE Gebruik",
|
||||
"current-action": "Huidige Aksie",
|
||||
"database-migration": "Databasismigrasie",
|
||||
"database-migration-description": "Opdatering van databasisstruktuur vir verbeterde funksionaliteit en werkverrigting. Hierdie proses kan 'n paar minute neem.",
|
||||
"database-migrations": "Databasismigrasies",
|
||||
"days-old": "Dae oud",
|
||||
"duration": "Duur",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accept": "Aanvaar",
|
||||
"activity-changedTitle": "changed title to %s of %s",
|
||||
"activity-changedDescription": "changed description of %s",
|
||||
"act-deleteCard": "deleted card __card__ from list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-removeBoard": "deleted board __board__",
|
||||
"act-removeList": "deleted list __list__ at board __board__",
|
||||
@@ -441,6 +442,7 @@
|
||||
"createCustomField": "Skep veld",
|
||||
"createCustomFieldPopup-title": "Skep veld",
|
||||
"current": "huidige",
|
||||
"custom-color": "Custom color",
|
||||
"custom-field-delete-pop": "Daar is geen ongedaan maak nie. Dit sal hierdie pasgemaakte veld van alle kaarte verwyder en sy geskiedenis vernietig.",
|
||||
"custom-field-checkbox": "Merkblokkie",
|
||||
"custom-field-currency": "Geldeenheid",
|
||||
@@ -508,6 +510,7 @@
|
||||
"error-csv-schema": "Jou CSV (Comma Separated Values)/TSV (Tab Separated Values) sluit nie die korrekte inligting in die korrekte formaat in nie",
|
||||
"error-import-empty-board": "This export contains no board content (no swimlanes, lists or cards). It may have been created by an older WeKan version with a known export bug. Please re-export the board with the current version and import the new file.",
|
||||
"error-list-doesNotExist": "Hierdie lys bestaan nie",
|
||||
"error-linked-card-not-allowed": "You can only link to a normal card. Linking to another linked card, or to a card that links back to this board, is not allowed because it would make the cards inaccessible.",
|
||||
"error-user-disabled": "This user account is disabled",
|
||||
"error-user-doesNotExist": "Hierdie gebruiker bestaan nie",
|
||||
"error-user-notAllowSelf": "Jy kan nie jouself nooi nie",
|
||||
@@ -581,6 +584,7 @@
|
||||
"fullname": "Volle Naam",
|
||||
"header-logo-title": "Gaan terug na jou bordbladsy.",
|
||||
"show-activities": "Wys aktiwiteite",
|
||||
"hide-activities": "Hide Activities",
|
||||
"headerBarCreateBoardPopup-title": "Skep bord",
|
||||
"home": "Tuis",
|
||||
"import": "Invoer",
|
||||
@@ -894,7 +898,12 @@
|
||||
"no-name": "(Onbekend)",
|
||||
"Node_version": "Node weergawe",
|
||||
"Meteor_version": "Meteoor weergawe",
|
||||
"MongoDB_version": "MongoDB weergawe",
|
||||
"Database_type": "Database type",
|
||||
"Database_commit": "Database commit",
|
||||
"FerretDB_version": "FerretDB version",
|
||||
"FerretDB_commit": "FerretDB commit",
|
||||
"Reactivity_mode": "Reactivity mode (changeStreams / oplog / polling)",
|
||||
"MongoDB_version": "MongoDB compatible version",
|
||||
"MongoDB_storage_engine": "MongoDB stoor enjin",
|
||||
"MongoDB_Oplog_enabled": "MongoDB Oplog geaktiveer",
|
||||
"OS_Arch": "OS Arch",
|
||||
@@ -1015,6 +1024,17 @@
|
||||
"r-workflow-help": "Drag a trigger and an action into the builder, then add the rule. Drag an action onto an existing rule to change what it does.",
|
||||
"r-drop-trigger": "drop a trigger here",
|
||||
"r-drop-action": "drop an action here",
|
||||
"r-w-card-created": "Card is created",
|
||||
"r-w-card-archived": "Card is archived",
|
||||
"r-w-card-unarchived": "Card is unarchived",
|
||||
"r-w-label-added": "Any label is added",
|
||||
"r-w-label-removed": "Any label is removed",
|
||||
"r-w-member-added": "A member is added",
|
||||
"r-w-member-removed": "A member is removed",
|
||||
"r-w-checklist-added": "A checklist is added",
|
||||
"r-w-attachment-added": "An attachment is added",
|
||||
"r-w-every-day-at": "Every day at __time__",
|
||||
"r-w-set-received-now": "Set received date to now",
|
||||
"r-export-json": "Export rules to JSON",
|
||||
"r-export-csv": "Export rules to CSV",
|
||||
"r-import-json": "Import rules from JSON",
|
||||
@@ -1068,6 +1088,12 @@
|
||||
"r-mark-incomplete": "Mark card incomplete",
|
||||
"r-move-all-cards": "Move all cards in list",
|
||||
"r-set-date-relative": "Set date relative to now",
|
||||
"r-later": "later",
|
||||
"r-unit-minutes": "minute(s)",
|
||||
"r-unit-hours": "hour(s)",
|
||||
"r-unit-days": "day(s)",
|
||||
"r-unit-weeks": "week(s)",
|
||||
"r-unit-months": "month(s)",
|
||||
"r-trigger": "Sneller",
|
||||
"r-action": "Aksie",
|
||||
"r-when-a-card": "Wanneer 'n kaart",
|
||||
@@ -1809,6 +1835,45 @@
|
||||
"azure-container": "Container",
|
||||
"gcs-storage": "Google Cloud Storage",
|
||||
"gcs-storage-description": "Google Cloud Storage.",
|
||||
"database-migration": "Database migration",
|
||||
"database-migration-description": "Migrate text-based data (everything except attachments and avatars, which stay on the filesystem) between MongoDB and FerretDB v1 (SQLite). The other database must be running and reachable (default ports: FerretDB mongodb://127.0.0.1:27018, MongoDB mongodb://127.0.0.1:27019; override with WEKAN_FERRETDB_URL / WEKAN_MONGODB_URL). After it finishes, point MONGO_URL at the other database and restart WeKan (on Snap: snap set wekan database=ferretdb or =mongodb).",
|
||||
"database-migrate-to-ferretdb": "Migrate text data to FerretDB v1 (SQLite)",
|
||||
"database-migrate-to-mongodb": "Migrate text data to MongoDB",
|
||||
"database-migration-confirm": "Copy all text-based collections to __db__? Attachments and avatars on the filesystem are not affected. The target database must be running.",
|
||||
"database-migration-phase": "Phase",
|
||||
"database-migration-done": "Text-data migration completed.",
|
||||
"collections": "collections",
|
||||
"features": "Features",
|
||||
"features-performance": "Performance",
|
||||
"cards-loading": "Card loading",
|
||||
"cards-loading-all": "All cards (default)",
|
||||
"cards-loading-lazy": "Lazy (only visible cards)",
|
||||
"cards-loading-description": "How each board loads its cards. All cards: every card is loaded into the browser (simplest, all features work, but a board with thousands of cards sends a lot of data to each viewer). Lazy: each list loads only the cards currently visible (infinite scroll) plus a live count, so very large boards use less memory and bandwidth. Also set by the CARDS_LOADING environment variable.",
|
||||
"cards-loading-lazy-note": "Lazy card loading is experimental: card counters and WIP limits are accurate, but the Calendar/Table/Gantt views and multi-select reflect only the cards loaded so far. Reload open boards after changing this.",
|
||||
"backup": "Backup",
|
||||
"backup-description": "Back up Attachments, Avatars and/or Data (all text-based collections that are not attachments or avatars). The .zip is streamed directly to the selected storage — no temporary file — as backup/YYYY/MM/DD/HH_MM_SS/backup.zip, containing YYYY_MM_DD-HH_MM_SS/attachments, /avatars and /data. Cloud storages (S3/MinIO, Azure, GCS) use the credentials configured in their storage tabs.",
|
||||
"backup-data": "Data (text)",
|
||||
"backup-storage": "Save to storage",
|
||||
"backup-now": "Backup now",
|
||||
"backup-done": "Backup completed",
|
||||
"backup-schedule": "Scheduled backups",
|
||||
"backup-frequency": "Frequency",
|
||||
"backup-frequency-off": "Off",
|
||||
"backup-frequency-daily": "Daily",
|
||||
"backup-frequency-weekly": "Weekly",
|
||||
"backup-frequency-monthly": "Monthly",
|
||||
"backup-time": "Time (HH:MM)",
|
||||
"backup-day-of-week": "Day of week (weekly)",
|
||||
"backup-day-of-month": "Day of month (monthly, 1-28)",
|
||||
"backup-restore": "Herstel",
|
||||
"backup-list": "List backups",
|
||||
"backup-datetime": "Date and time",
|
||||
"backup-path": "File path",
|
||||
"backup-restore-mode": "Restore mode",
|
||||
"backup-restore-add-missing": "Add missing data only",
|
||||
"backup-restore-replace-all": "Replace all data",
|
||||
"backup-restore-select-first": "Select a backup from the list first.",
|
||||
"backup-restore-confirm": "Restore the selected backup? Attachments/avatars are written to the filesystem and data to the database according to the chosen mode.",
|
||||
"gcs-project-id": "Project ID",
|
||||
"gcs-bucket": "Bucket",
|
||||
"gcs-key-filename": "Service account key file path",
|
||||
@@ -1986,8 +2051,6 @@
|
||||
"cpu-cores": "SVE-kerns",
|
||||
"cpu-usage": "SVE Gebruik",
|
||||
"current-action": "Huidige Aksie",
|
||||
"database-migration": "Databasismigrasie",
|
||||
"database-migration-description": "Opdatering van databasisstruktuur vir verbeterde funksionaliteit en werkverrigting. Hierdie proses kan 'n paar minute neem.",
|
||||
"database-migrations": "Databasismigrasies",
|
||||
"days-old": "Dae oud",
|
||||
"duration": "Duur",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accept": "قبول",
|
||||
"activity-changedTitle": "changed title to %s of %s",
|
||||
"activity-changedDescription": "changed description of %s",
|
||||
"act-deleteCard": "deleted card __card__ from list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-removeBoard": "deleted board __board__",
|
||||
"act-removeList": "deleted list __list__ at board __board__",
|
||||
@@ -441,6 +442,7 @@
|
||||
"createCustomField": "انشاء حقل",
|
||||
"createCustomFieldPopup-title": "انشاء حقل",
|
||||
"current": "الحالي",
|
||||
"custom-color": "Custom color",
|
||||
"custom-field-delete-pop": "این اقدام فیلد سفارشی را بهمراه تمامی تاریخچه آن از کارت ها پاک می کند و برگشت پذیر نمی باشد",
|
||||
"custom-field-checkbox": "جعبه انتخابی",
|
||||
"custom-field-currency": "عملة",
|
||||
@@ -508,6 +510,7 @@
|
||||
"error-csv-schema": "لا يتضمن ملف CSV (القيم المفصولة بفواصل)/TSV (القيم المفصولة بعلامات التبويب) الخاص بك المعلومات الصحيحة بالتنسيق الصحيح",
|
||||
"error-import-empty-board": "This export contains no board content (no swimlanes, lists or cards). It may have been created by an older WeKan version with a known export bug. Please re-export the board with the current version and import the new file.",
|
||||
"error-list-doesNotExist": "این لیست موجود نیست",
|
||||
"error-linked-card-not-allowed": "You can only link to a normal card. Linking to another linked card, or to a card that links back to this board, is not allowed because it would make the cards inaccessible.",
|
||||
"error-user-disabled": "This user account is disabled",
|
||||
"error-user-doesNotExist": "این کاربر وجود ندارد",
|
||||
"error-user-notAllowSelf": "لا يمكنك دعوة نفسك",
|
||||
@@ -581,6 +584,7 @@
|
||||
"fullname": "الإسم الكامل",
|
||||
"header-logo-title": "الرجوع إلى صفحة اللوحات",
|
||||
"show-activities": "عرض الأنشطة",
|
||||
"hide-activities": "Hide Activities",
|
||||
"headerBarCreateBoardPopup-title": "إنشاء لوحة",
|
||||
"home": "الرئيسية",
|
||||
"import": "وارد کردن",
|
||||
@@ -894,7 +898,12 @@
|
||||
"no-name": "(غير معروف)",
|
||||
"Node_version": "إصدار النود",
|
||||
"Meteor_version": "نسخه متئور",
|
||||
"MongoDB_version": "ورژن MongoDB",
|
||||
"Database_type": "Database type",
|
||||
"Database_commit": "Database commit",
|
||||
"FerretDB_version": "FerretDB version",
|
||||
"FerretDB_commit": "FerretDB commit",
|
||||
"Reactivity_mode": "Reactivity mode (changeStreams / oplog / polling)",
|
||||
"MongoDB_version": "MongoDB compatible version",
|
||||
"MongoDB_storage_engine": "موتور ذخیره سازی MongoDB",
|
||||
"MongoDB_Oplog_enabled": "MongoDB Oplog فعال است",
|
||||
"OS_Arch": "معمارية نظام التشغيل",
|
||||
@@ -1015,6 +1024,17 @@
|
||||
"r-workflow-help": "Drag a trigger and an action into the builder, then add the rule. Drag an action onto an existing rule to change what it does.",
|
||||
"r-drop-trigger": "drop a trigger here",
|
||||
"r-drop-action": "drop an action here",
|
||||
"r-w-card-created": "Card is created",
|
||||
"r-w-card-archived": "Card is archived",
|
||||
"r-w-card-unarchived": "Card is unarchived",
|
||||
"r-w-label-added": "Any label is added",
|
||||
"r-w-label-removed": "Any label is removed",
|
||||
"r-w-member-added": "A member is added",
|
||||
"r-w-member-removed": "A member is removed",
|
||||
"r-w-checklist-added": "A checklist is added",
|
||||
"r-w-attachment-added": "An attachment is added",
|
||||
"r-w-every-day-at": "Every day at __time__",
|
||||
"r-w-set-received-now": "Set received date to now",
|
||||
"r-export-json": "Export rules to JSON",
|
||||
"r-export-csv": "Export rules to CSV",
|
||||
"r-import-json": "Import rules from JSON",
|
||||
@@ -1068,6 +1088,12 @@
|
||||
"r-mark-incomplete": "Mark card incomplete",
|
||||
"r-move-all-cards": "Move all cards in list",
|
||||
"r-set-date-relative": "Set date relative to now",
|
||||
"r-later": "later",
|
||||
"r-unit-minutes": "minute(s)",
|
||||
"r-unit-hours": "hour(s)",
|
||||
"r-unit-days": "day(s)",
|
||||
"r-unit-weeks": "week(s)",
|
||||
"r-unit-months": "month(s)",
|
||||
"r-trigger": "تریگر",
|
||||
"r-action": "عملیات",
|
||||
"r-when-a-card": "زمانی که کارت",
|
||||
@@ -1809,6 +1835,45 @@
|
||||
"azure-container": "Container",
|
||||
"gcs-storage": "Google Cloud Storage",
|
||||
"gcs-storage-description": "Google Cloud Storage.",
|
||||
"database-migration": "Database migration",
|
||||
"database-migration-description": "Migrate text-based data (everything except attachments and avatars, which stay on the filesystem) between MongoDB and FerretDB v1 (SQLite). The other database must be running and reachable (default ports: FerretDB mongodb://127.0.0.1:27018, MongoDB mongodb://127.0.0.1:27019; override with WEKAN_FERRETDB_URL / WEKAN_MONGODB_URL). After it finishes, point MONGO_URL at the other database and restart WeKan (on Snap: snap set wekan database=ferretdb or =mongodb).",
|
||||
"database-migrate-to-ferretdb": "Migrate text data to FerretDB v1 (SQLite)",
|
||||
"database-migrate-to-mongodb": "Migrate text data to MongoDB",
|
||||
"database-migration-confirm": "Copy all text-based collections to __db__? Attachments and avatars on the filesystem are not affected. The target database must be running.",
|
||||
"database-migration-phase": "Phase",
|
||||
"database-migration-done": "Text-data migration completed.",
|
||||
"collections": "collections",
|
||||
"features": "Features",
|
||||
"features-performance": "Performance",
|
||||
"cards-loading": "Card loading",
|
||||
"cards-loading-all": "All cards (default)",
|
||||
"cards-loading-lazy": "Lazy (only visible cards)",
|
||||
"cards-loading-description": "How each board loads its cards. All cards: every card is loaded into the browser (simplest, all features work, but a board with thousands of cards sends a lot of data to each viewer). Lazy: each list loads only the cards currently visible (infinite scroll) plus a live count, so very large boards use less memory and bandwidth. Also set by the CARDS_LOADING environment variable.",
|
||||
"cards-loading-lazy-note": "Lazy card loading is experimental: card counters and WIP limits are accurate, but the Calendar/Table/Gantt views and multi-select reflect only the cards loaded so far. Reload open boards after changing this.",
|
||||
"backup": "Backup",
|
||||
"backup-description": "Back up Attachments, Avatars and/or Data (all text-based collections that are not attachments or avatars). The .zip is streamed directly to the selected storage — no temporary file — as backup/YYYY/MM/DD/HH_MM_SS/backup.zip, containing YYYY_MM_DD-HH_MM_SS/attachments, /avatars and /data. Cloud storages (S3/MinIO, Azure, GCS) use the credentials configured in their storage tabs.",
|
||||
"backup-data": "Data (text)",
|
||||
"backup-storage": "Save to storage",
|
||||
"backup-now": "Backup now",
|
||||
"backup-done": "Backup completed",
|
||||
"backup-schedule": "Scheduled backups",
|
||||
"backup-frequency": "Frequency",
|
||||
"backup-frequency-off": "Off",
|
||||
"backup-frequency-daily": "Daily",
|
||||
"backup-frequency-weekly": "Weekly",
|
||||
"backup-frequency-monthly": "Monthly",
|
||||
"backup-time": "Time (HH:MM)",
|
||||
"backup-day-of-week": "Day of week (weekly)",
|
||||
"backup-day-of-month": "Day of month (monthly, 1-28)",
|
||||
"backup-restore": "استعادة",
|
||||
"backup-list": "List backups",
|
||||
"backup-datetime": "Date and time",
|
||||
"backup-path": "File path",
|
||||
"backup-restore-mode": "Restore mode",
|
||||
"backup-restore-add-missing": "Add missing data only",
|
||||
"backup-restore-replace-all": "Replace all data",
|
||||
"backup-restore-select-first": "Select a backup from the list first.",
|
||||
"backup-restore-confirm": "Restore the selected backup? Attachments/avatars are written to the filesystem and data to the database according to the chosen mode.",
|
||||
"gcs-project-id": "Project ID",
|
||||
"gcs-bucket": "Bucket",
|
||||
"gcs-key-filename": "Service account key file path",
|
||||
@@ -1986,8 +2051,6 @@
|
||||
"cpu-cores": "Cœurs du CPU ",
|
||||
"cpu-usage": "Utilisation du CPU",
|
||||
"current-action": "Current Action",
|
||||
"database-migration": "Migration base de données",
|
||||
"database-migration-description": "Updating database structure for improved functionality and performance. This process may take several minutes.",
|
||||
"database-migrations": "Migrations base de données",
|
||||
"days-old": "Jours d'ancienneté",
|
||||
"duration": "Durée",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accept": "قبول",
|
||||
"activity-changedTitle": "changed title to %s of %s",
|
||||
"activity-changedDescription": "changed description of %s",
|
||||
"act-deleteCard": "deleted card __card__ from list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-removeBoard": "deleted board __board__",
|
||||
"act-removeList": "deleted list __list__ at board __board__",
|
||||
@@ -441,6 +442,7 @@
|
||||
"createCustomField": "انشاء حقل",
|
||||
"createCustomFieldPopup-title": "انشاء حقل",
|
||||
"current": "الحالي",
|
||||
"custom-color": "Custom color",
|
||||
"custom-field-delete-pop": "این اقدام فیلد سفارشی را بهمراه تمامی تاریخچه آن از کارت ها پاک می کند و برگشت پذیر نمی باشد",
|
||||
"custom-field-checkbox": "جعبه انتخابی",
|
||||
"custom-field-currency": "عملة",
|
||||
@@ -508,6 +510,7 @@
|
||||
"error-csv-schema": "لا يتضمن ملف CSV (القيم المفصولة بفواصل)/TSV (القيم المفصولة بعلامات التبويب) الخاص بك المعلومات الصحيحة بالتنسيق الصحيح",
|
||||
"error-import-empty-board": "This export contains no board content (no swimlanes, lists or cards). It may have been created by an older WeKan version with a known export bug. Please re-export the board with the current version and import the new file.",
|
||||
"error-list-doesNotExist": "این لیست موجود نیست",
|
||||
"error-linked-card-not-allowed": "You can only link to a normal card. Linking to another linked card, or to a card that links back to this board, is not allowed because it would make the cards inaccessible.",
|
||||
"error-user-disabled": "This user account is disabled",
|
||||
"error-user-doesNotExist": "این کاربر وجود ندارد",
|
||||
"error-user-notAllowSelf": "لا يمكنك دعوة نفسك",
|
||||
@@ -581,6 +584,7 @@
|
||||
"fullname": "الإسم الكامل",
|
||||
"header-logo-title": "الرجوع إلى صفحة اللوحات",
|
||||
"show-activities": "عرض الأنشطة",
|
||||
"hide-activities": "Hide Activities",
|
||||
"headerBarCreateBoardPopup-title": "إنشاء لوحة",
|
||||
"home": "الرئيسية",
|
||||
"import": "وارد کردن",
|
||||
@@ -894,7 +898,12 @@
|
||||
"no-name": "(غير معروف)",
|
||||
"Node_version": "إصدار النود",
|
||||
"Meteor_version": "نسخه متئور",
|
||||
"MongoDB_version": "ورژن MongoDB",
|
||||
"Database_type": "Database type",
|
||||
"Database_commit": "Database commit",
|
||||
"FerretDB_version": "FerretDB version",
|
||||
"FerretDB_commit": "FerretDB commit",
|
||||
"Reactivity_mode": "Reactivity mode (changeStreams / oplog / polling)",
|
||||
"MongoDB_version": "MongoDB compatible version",
|
||||
"MongoDB_storage_engine": "موتور ذخیره سازی MongoDB",
|
||||
"MongoDB_Oplog_enabled": "MongoDB Oplog فعال است",
|
||||
"OS_Arch": "معمارية نظام التشغيل",
|
||||
@@ -1015,6 +1024,17 @@
|
||||
"r-workflow-help": "Drag a trigger and an action into the builder, then add the rule. Drag an action onto an existing rule to change what it does.",
|
||||
"r-drop-trigger": "drop a trigger here",
|
||||
"r-drop-action": "drop an action here",
|
||||
"r-w-card-created": "Card is created",
|
||||
"r-w-card-archived": "Card is archived",
|
||||
"r-w-card-unarchived": "Card is unarchived",
|
||||
"r-w-label-added": "Any label is added",
|
||||
"r-w-label-removed": "Any label is removed",
|
||||
"r-w-member-added": "A member is added",
|
||||
"r-w-member-removed": "A member is removed",
|
||||
"r-w-checklist-added": "A checklist is added",
|
||||
"r-w-attachment-added": "An attachment is added",
|
||||
"r-w-every-day-at": "Every day at __time__",
|
||||
"r-w-set-received-now": "Set received date to now",
|
||||
"r-export-json": "Export rules to JSON",
|
||||
"r-export-csv": "Export rules to CSV",
|
||||
"r-import-json": "Import rules from JSON",
|
||||
@@ -1068,6 +1088,12 @@
|
||||
"r-mark-incomplete": "Mark card incomplete",
|
||||
"r-move-all-cards": "Move all cards in list",
|
||||
"r-set-date-relative": "Set date relative to now",
|
||||
"r-later": "later",
|
||||
"r-unit-minutes": "minute(s)",
|
||||
"r-unit-hours": "hour(s)",
|
||||
"r-unit-days": "day(s)",
|
||||
"r-unit-weeks": "week(s)",
|
||||
"r-unit-months": "month(s)",
|
||||
"r-trigger": "تریگر",
|
||||
"r-action": "عملیات",
|
||||
"r-when-a-card": "زمانی که کارت",
|
||||
@@ -1809,6 +1835,45 @@
|
||||
"azure-container": "Container",
|
||||
"gcs-storage": "Google Cloud Storage",
|
||||
"gcs-storage-description": "Google Cloud Storage.",
|
||||
"database-migration": "Database migration",
|
||||
"database-migration-description": "Migrate text-based data (everything except attachments and avatars, which stay on the filesystem) between MongoDB and FerretDB v1 (SQLite). The other database must be running and reachable (default ports: FerretDB mongodb://127.0.0.1:27018, MongoDB mongodb://127.0.0.1:27019; override with WEKAN_FERRETDB_URL / WEKAN_MONGODB_URL). After it finishes, point MONGO_URL at the other database and restart WeKan (on Snap: snap set wekan database=ferretdb or =mongodb).",
|
||||
"database-migrate-to-ferretdb": "Migrate text data to FerretDB v1 (SQLite)",
|
||||
"database-migrate-to-mongodb": "Migrate text data to MongoDB",
|
||||
"database-migration-confirm": "Copy all text-based collections to __db__? Attachments and avatars on the filesystem are not affected. The target database must be running.",
|
||||
"database-migration-phase": "Phase",
|
||||
"database-migration-done": "Text-data migration completed.",
|
||||
"collections": "collections",
|
||||
"features": "Features",
|
||||
"features-performance": "Performance",
|
||||
"cards-loading": "Card loading",
|
||||
"cards-loading-all": "All cards (default)",
|
||||
"cards-loading-lazy": "Lazy (only visible cards)",
|
||||
"cards-loading-description": "How each board loads its cards. All cards: every card is loaded into the browser (simplest, all features work, but a board with thousands of cards sends a lot of data to each viewer). Lazy: each list loads only the cards currently visible (infinite scroll) plus a live count, so very large boards use less memory and bandwidth. Also set by the CARDS_LOADING environment variable.",
|
||||
"cards-loading-lazy-note": "Lazy card loading is experimental: card counters and WIP limits are accurate, but the Calendar/Table/Gantt views and multi-select reflect only the cards loaded so far. Reload open boards after changing this.",
|
||||
"backup": "Backup",
|
||||
"backup-description": "Back up Attachments, Avatars and/or Data (all text-based collections that are not attachments or avatars). The .zip is streamed directly to the selected storage — no temporary file — as backup/YYYY/MM/DD/HH_MM_SS/backup.zip, containing YYYY_MM_DD-HH_MM_SS/attachments, /avatars and /data. Cloud storages (S3/MinIO, Azure, GCS) use the credentials configured in their storage tabs.",
|
||||
"backup-data": "Data (text)",
|
||||
"backup-storage": "Save to storage",
|
||||
"backup-now": "Backup now",
|
||||
"backup-done": "Backup completed",
|
||||
"backup-schedule": "Scheduled backups",
|
||||
"backup-frequency": "Frequency",
|
||||
"backup-frequency-off": "Off",
|
||||
"backup-frequency-daily": "Daily",
|
||||
"backup-frequency-weekly": "Weekly",
|
||||
"backup-frequency-monthly": "Monthly",
|
||||
"backup-time": "Time (HH:MM)",
|
||||
"backup-day-of-week": "Day of week (weekly)",
|
||||
"backup-day-of-month": "Day of month (monthly, 1-28)",
|
||||
"backup-restore": "استعادة",
|
||||
"backup-list": "List backups",
|
||||
"backup-datetime": "Date and time",
|
||||
"backup-path": "File path",
|
||||
"backup-restore-mode": "Restore mode",
|
||||
"backup-restore-add-missing": "Add missing data only",
|
||||
"backup-restore-replace-all": "Replace all data",
|
||||
"backup-restore-select-first": "Select a backup from the list first.",
|
||||
"backup-restore-confirm": "Restore the selected backup? Attachments/avatars are written to the filesystem and data to the database according to the chosen mode.",
|
||||
"gcs-project-id": "Project ID",
|
||||
"gcs-bucket": "Bucket",
|
||||
"gcs-key-filename": "Service account key file path",
|
||||
@@ -1986,8 +2051,6 @@
|
||||
"cpu-cores": "CPU Cores",
|
||||
"cpu-usage": "CPU Usage",
|
||||
"current-action": "Current Action",
|
||||
"database-migration": "Database Migration",
|
||||
"database-migration-description": "Updating database structure for improved functionality and performance. This process may take several minutes.",
|
||||
"database-migrations": "Database Migrations",
|
||||
"days-old": "Days Old",
|
||||
"duration": "Duration",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accept": "قبول",
|
||||
"activity-changedTitle": "changed title to %s of %s",
|
||||
"activity-changedDescription": "changed description of %s",
|
||||
"act-deleteCard": "deleted card __card__ from list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-removeBoard": "deleted board __board__",
|
||||
"act-removeList": "deleted list __list__ at board __board__",
|
||||
@@ -441,6 +442,7 @@
|
||||
"createCustomField": "انشاء حقل",
|
||||
"createCustomFieldPopup-title": "انشاء حقل",
|
||||
"current": "الحالي",
|
||||
"custom-color": "Custom color",
|
||||
"custom-field-delete-pop": "این اقدام فیلد سفارشی را بهمراه تمامی تاریخچه آن از کارت ها پاک می کند و برگشت پذیر نمی باشد",
|
||||
"custom-field-checkbox": "جعبه انتخابی",
|
||||
"custom-field-currency": "عملة",
|
||||
@@ -508,6 +510,7 @@
|
||||
"error-csv-schema": "لا يتضمن ملف CSV (القيم المفصولة بفواصل)/TSV (القيم المفصولة بعلامات التبويب) الخاص بك المعلومات الصحيحة بالتنسيق الصحيح",
|
||||
"error-import-empty-board": "This export contains no board content (no swimlanes, lists or cards). It may have been created by an older WeKan version with a known export bug. Please re-export the board with the current version and import the new file.",
|
||||
"error-list-doesNotExist": "این لیست موجود نیست",
|
||||
"error-linked-card-not-allowed": "You can only link to a normal card. Linking to another linked card, or to a card that links back to this board, is not allowed because it would make the cards inaccessible.",
|
||||
"error-user-disabled": "This user account is disabled",
|
||||
"error-user-doesNotExist": "این کاربر وجود ندارد",
|
||||
"error-user-notAllowSelf": "لا يمكنك دعوة نفسك",
|
||||
@@ -581,6 +584,7 @@
|
||||
"fullname": "الإسم الكامل",
|
||||
"header-logo-title": "الرجوع إلى صفحة اللوحات",
|
||||
"show-activities": "عرض الأنشطة",
|
||||
"hide-activities": "Hide Activities",
|
||||
"headerBarCreateBoardPopup-title": "إنشاء لوحة",
|
||||
"home": "الرئيسية",
|
||||
"import": "وارد کردن",
|
||||
@@ -894,7 +898,12 @@
|
||||
"no-name": "(غير معروف)",
|
||||
"Node_version": "إصدار النود",
|
||||
"Meteor_version": "نسخه متئور",
|
||||
"MongoDB_version": "ورژن MongoDB",
|
||||
"Database_type": "Database type",
|
||||
"Database_commit": "Database commit",
|
||||
"FerretDB_version": "FerretDB version",
|
||||
"FerretDB_commit": "FerretDB commit",
|
||||
"Reactivity_mode": "Reactivity mode (changeStreams / oplog / polling)",
|
||||
"MongoDB_version": "MongoDB compatible version",
|
||||
"MongoDB_storage_engine": "موتور ذخیره سازی MongoDB",
|
||||
"MongoDB_Oplog_enabled": "MongoDB Oplog فعال است",
|
||||
"OS_Arch": "معمارية نظام التشغيل",
|
||||
@@ -1015,6 +1024,17 @@
|
||||
"r-workflow-help": "Drag a trigger and an action into the builder, then add the rule. Drag an action onto an existing rule to change what it does.",
|
||||
"r-drop-trigger": "drop a trigger here",
|
||||
"r-drop-action": "drop an action here",
|
||||
"r-w-card-created": "Card is created",
|
||||
"r-w-card-archived": "Card is archived",
|
||||
"r-w-card-unarchived": "Card is unarchived",
|
||||
"r-w-label-added": "Any label is added",
|
||||
"r-w-label-removed": "Any label is removed",
|
||||
"r-w-member-added": "A member is added",
|
||||
"r-w-member-removed": "A member is removed",
|
||||
"r-w-checklist-added": "A checklist is added",
|
||||
"r-w-attachment-added": "An attachment is added",
|
||||
"r-w-every-day-at": "Every day at __time__",
|
||||
"r-w-set-received-now": "Set received date to now",
|
||||
"r-export-json": "Export rules to JSON",
|
||||
"r-export-csv": "Export rules to CSV",
|
||||
"r-import-json": "Import rules from JSON",
|
||||
@@ -1068,6 +1088,12 @@
|
||||
"r-mark-incomplete": "Mark card incomplete",
|
||||
"r-move-all-cards": "Move all cards in list",
|
||||
"r-set-date-relative": "Set date relative to now",
|
||||
"r-later": "later",
|
||||
"r-unit-minutes": "minute(s)",
|
||||
"r-unit-hours": "hour(s)",
|
||||
"r-unit-days": "day(s)",
|
||||
"r-unit-weeks": "week(s)",
|
||||
"r-unit-months": "month(s)",
|
||||
"r-trigger": "تریگر",
|
||||
"r-action": "عملیات",
|
||||
"r-when-a-card": "زمانی که کارت",
|
||||
@@ -1809,6 +1835,45 @@
|
||||
"azure-container": "Container",
|
||||
"gcs-storage": "Google Cloud Storage",
|
||||
"gcs-storage-description": "Google Cloud Storage.",
|
||||
"database-migration": "Database migration",
|
||||
"database-migration-description": "Migrate text-based data (everything except attachments and avatars, which stay on the filesystem) between MongoDB and FerretDB v1 (SQLite). The other database must be running and reachable (default ports: FerretDB mongodb://127.0.0.1:27018, MongoDB mongodb://127.0.0.1:27019; override with WEKAN_FERRETDB_URL / WEKAN_MONGODB_URL). After it finishes, point MONGO_URL at the other database and restart WeKan (on Snap: snap set wekan database=ferretdb or =mongodb).",
|
||||
"database-migrate-to-ferretdb": "Migrate text data to FerretDB v1 (SQLite)",
|
||||
"database-migrate-to-mongodb": "Migrate text data to MongoDB",
|
||||
"database-migration-confirm": "Copy all text-based collections to __db__? Attachments and avatars on the filesystem are not affected. The target database must be running.",
|
||||
"database-migration-phase": "Phase",
|
||||
"database-migration-done": "Text-data migration completed.",
|
||||
"collections": "collections",
|
||||
"features": "Features",
|
||||
"features-performance": "Performance",
|
||||
"cards-loading": "Card loading",
|
||||
"cards-loading-all": "All cards (default)",
|
||||
"cards-loading-lazy": "Lazy (only visible cards)",
|
||||
"cards-loading-description": "How each board loads its cards. All cards: every card is loaded into the browser (simplest, all features work, but a board with thousands of cards sends a lot of data to each viewer). Lazy: each list loads only the cards currently visible (infinite scroll) plus a live count, so very large boards use less memory and bandwidth. Also set by the CARDS_LOADING environment variable.",
|
||||
"cards-loading-lazy-note": "Lazy card loading is experimental: card counters and WIP limits are accurate, but the Calendar/Table/Gantt views and multi-select reflect only the cards loaded so far. Reload open boards after changing this.",
|
||||
"backup": "Backup",
|
||||
"backup-description": "Back up Attachments, Avatars and/or Data (all text-based collections that are not attachments or avatars). The .zip is streamed directly to the selected storage — no temporary file — as backup/YYYY/MM/DD/HH_MM_SS/backup.zip, containing YYYY_MM_DD-HH_MM_SS/attachments, /avatars and /data. Cloud storages (S3/MinIO, Azure, GCS) use the credentials configured in their storage tabs.",
|
||||
"backup-data": "Data (text)",
|
||||
"backup-storage": "Save to storage",
|
||||
"backup-now": "Backup now",
|
||||
"backup-done": "Backup completed",
|
||||
"backup-schedule": "Scheduled backups",
|
||||
"backup-frequency": "Frequency",
|
||||
"backup-frequency-off": "Off",
|
||||
"backup-frequency-daily": "Daily",
|
||||
"backup-frequency-weekly": "Weekly",
|
||||
"backup-frequency-monthly": "Monthly",
|
||||
"backup-time": "Time (HH:MM)",
|
||||
"backup-day-of-week": "Day of week (weekly)",
|
||||
"backup-day-of-month": "Day of month (monthly, 1-28)",
|
||||
"backup-restore": "استعادة",
|
||||
"backup-list": "List backups",
|
||||
"backup-datetime": "Date and time",
|
||||
"backup-path": "File path",
|
||||
"backup-restore-mode": "Restore mode",
|
||||
"backup-restore-add-missing": "Add missing data only",
|
||||
"backup-restore-replace-all": "Replace all data",
|
||||
"backup-restore-select-first": "Select a backup from the list first.",
|
||||
"backup-restore-confirm": "Restore the selected backup? Attachments/avatars are written to the filesystem and data to the database according to the chosen mode.",
|
||||
"gcs-project-id": "Project ID",
|
||||
"gcs-bucket": "Bucket",
|
||||
"gcs-key-filename": "Service account key file path",
|
||||
@@ -1986,8 +2051,6 @@
|
||||
"cpu-cores": "CPU Cores",
|
||||
"cpu-usage": "CPU Usage",
|
||||
"current-action": "Current Action",
|
||||
"database-migration": "Database Migration",
|
||||
"database-migration-description": "Updating database structure for improved functionality and performance. This process may take several minutes.",
|
||||
"database-migrations": "Database Migrations",
|
||||
"days-old": "Days Old",
|
||||
"duration": "Duration",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accept": "قبول",
|
||||
"activity-changedTitle": "changed title to %s of %s",
|
||||
"activity-changedDescription": "changed description of %s",
|
||||
"act-deleteCard": "deleted card __card__ from list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-removeBoard": "deleted board __board__",
|
||||
"act-removeList": "deleted list __list__ at board __board__",
|
||||
@@ -441,6 +442,7 @@
|
||||
"createCustomField": "انشاء حقل",
|
||||
"createCustomFieldPopup-title": "انشاء حقل",
|
||||
"current": "الحالي",
|
||||
"custom-color": "Custom color",
|
||||
"custom-field-delete-pop": "این اقدام فیلد سفارشی را بهمراه تمامی تاریخچه آن از کارت ها پاک می کند و برگشت پذیر نمی باشد",
|
||||
"custom-field-checkbox": "جعبه انتخابی",
|
||||
"custom-field-currency": "عملة",
|
||||
@@ -508,6 +510,7 @@
|
||||
"error-csv-schema": "لا يتضمن ملف CSV (القيم المفصولة بفواصل)/TSV (القيم المفصولة بعلامات التبويب) الخاص بك المعلومات الصحيحة بالتنسيق الصحيح",
|
||||
"error-import-empty-board": "This export contains no board content (no swimlanes, lists or cards). It may have been created by an older WeKan version with a known export bug. Please re-export the board with the current version and import the new file.",
|
||||
"error-list-doesNotExist": "این لیست موجود نیست",
|
||||
"error-linked-card-not-allowed": "You can only link to a normal card. Linking to another linked card, or to a card that links back to this board, is not allowed because it would make the cards inaccessible.",
|
||||
"error-user-disabled": "This user account is disabled",
|
||||
"error-user-doesNotExist": "این کاربر وجود ندارد",
|
||||
"error-user-notAllowSelf": "لا يمكنك دعوة نفسك",
|
||||
@@ -581,6 +584,7 @@
|
||||
"fullname": "الإسم الكامل",
|
||||
"header-logo-title": "الرجوع إلى صفحة اللوحات",
|
||||
"show-activities": "عرض الأنشطة",
|
||||
"hide-activities": "Hide Activities",
|
||||
"headerBarCreateBoardPopup-title": "إنشاء لوحة",
|
||||
"home": "الرئيسية",
|
||||
"import": "وارد کردن",
|
||||
@@ -894,7 +898,12 @@
|
||||
"no-name": "(غير معروف)",
|
||||
"Node_version": "إصدار النود",
|
||||
"Meteor_version": "نسخه متئور",
|
||||
"MongoDB_version": "ورژن MongoDB",
|
||||
"Database_type": "Database type",
|
||||
"Database_commit": "Database commit",
|
||||
"FerretDB_version": "FerretDB version",
|
||||
"FerretDB_commit": "FerretDB commit",
|
||||
"Reactivity_mode": "Reactivity mode (changeStreams / oplog / polling)",
|
||||
"MongoDB_version": "MongoDB compatible version",
|
||||
"MongoDB_storage_engine": "موتور ذخیره سازی MongoDB",
|
||||
"MongoDB_Oplog_enabled": "MongoDB Oplog فعال است",
|
||||
"OS_Arch": "معمارية نظام التشغيل",
|
||||
@@ -1015,6 +1024,17 @@
|
||||
"r-workflow-help": "Drag a trigger and an action into the builder, then add the rule. Drag an action onto an existing rule to change what it does.",
|
||||
"r-drop-trigger": "drop a trigger here",
|
||||
"r-drop-action": "drop an action here",
|
||||
"r-w-card-created": "Card is created",
|
||||
"r-w-card-archived": "Card is archived",
|
||||
"r-w-card-unarchived": "Card is unarchived",
|
||||
"r-w-label-added": "Any label is added",
|
||||
"r-w-label-removed": "Any label is removed",
|
||||
"r-w-member-added": "A member is added",
|
||||
"r-w-member-removed": "A member is removed",
|
||||
"r-w-checklist-added": "A checklist is added",
|
||||
"r-w-attachment-added": "An attachment is added",
|
||||
"r-w-every-day-at": "Every day at __time__",
|
||||
"r-w-set-received-now": "Set received date to now",
|
||||
"r-export-json": "Export rules to JSON",
|
||||
"r-export-csv": "Export rules to CSV",
|
||||
"r-import-json": "Import rules from JSON",
|
||||
@@ -1068,6 +1088,12 @@
|
||||
"r-mark-incomplete": "Mark card incomplete",
|
||||
"r-move-all-cards": "Move all cards in list",
|
||||
"r-set-date-relative": "Set date relative to now",
|
||||
"r-later": "later",
|
||||
"r-unit-minutes": "minute(s)",
|
||||
"r-unit-hours": "hour(s)",
|
||||
"r-unit-days": "day(s)",
|
||||
"r-unit-weeks": "week(s)",
|
||||
"r-unit-months": "month(s)",
|
||||
"r-trigger": "تریگر",
|
||||
"r-action": "عملیات",
|
||||
"r-when-a-card": "زمانی که کارت",
|
||||
@@ -1809,6 +1835,45 @@
|
||||
"azure-container": "Container",
|
||||
"gcs-storage": "Google Cloud Storage",
|
||||
"gcs-storage-description": "Google Cloud Storage.",
|
||||
"database-migration": "Database migration",
|
||||
"database-migration-description": "Migrate text-based data (everything except attachments and avatars, which stay on the filesystem) between MongoDB and FerretDB v1 (SQLite). The other database must be running and reachable (default ports: FerretDB mongodb://127.0.0.1:27018, MongoDB mongodb://127.0.0.1:27019; override with WEKAN_FERRETDB_URL / WEKAN_MONGODB_URL). After it finishes, point MONGO_URL at the other database and restart WeKan (on Snap: snap set wekan database=ferretdb or =mongodb).",
|
||||
"database-migrate-to-ferretdb": "Migrate text data to FerretDB v1 (SQLite)",
|
||||
"database-migrate-to-mongodb": "Migrate text data to MongoDB",
|
||||
"database-migration-confirm": "Copy all text-based collections to __db__? Attachments and avatars on the filesystem are not affected. The target database must be running.",
|
||||
"database-migration-phase": "Phase",
|
||||
"database-migration-done": "Text-data migration completed.",
|
||||
"collections": "collections",
|
||||
"features": "Features",
|
||||
"features-performance": "Performance",
|
||||
"cards-loading": "Card loading",
|
||||
"cards-loading-all": "All cards (default)",
|
||||
"cards-loading-lazy": "Lazy (only visible cards)",
|
||||
"cards-loading-description": "How each board loads its cards. All cards: every card is loaded into the browser (simplest, all features work, but a board with thousands of cards sends a lot of data to each viewer). Lazy: each list loads only the cards currently visible (infinite scroll) plus a live count, so very large boards use less memory and bandwidth. Also set by the CARDS_LOADING environment variable.",
|
||||
"cards-loading-lazy-note": "Lazy card loading is experimental: card counters and WIP limits are accurate, but the Calendar/Table/Gantt views and multi-select reflect only the cards loaded so far. Reload open boards after changing this.",
|
||||
"backup": "Backup",
|
||||
"backup-description": "Back up Attachments, Avatars and/or Data (all text-based collections that are not attachments or avatars). The .zip is streamed directly to the selected storage — no temporary file — as backup/YYYY/MM/DD/HH_MM_SS/backup.zip, containing YYYY_MM_DD-HH_MM_SS/attachments, /avatars and /data. Cloud storages (S3/MinIO, Azure, GCS) use the credentials configured in their storage tabs.",
|
||||
"backup-data": "Data (text)",
|
||||
"backup-storage": "Save to storage",
|
||||
"backup-now": "Backup now",
|
||||
"backup-done": "Backup completed",
|
||||
"backup-schedule": "Scheduled backups",
|
||||
"backup-frequency": "Frequency",
|
||||
"backup-frequency-off": "Off",
|
||||
"backup-frequency-daily": "Daily",
|
||||
"backup-frequency-weekly": "Weekly",
|
||||
"backup-frequency-monthly": "Monthly",
|
||||
"backup-time": "Time (HH:MM)",
|
||||
"backup-day-of-week": "Day of week (weekly)",
|
||||
"backup-day-of-month": "Day of month (monthly, 1-28)",
|
||||
"backup-restore": "استعادة",
|
||||
"backup-list": "List backups",
|
||||
"backup-datetime": "Date and time",
|
||||
"backup-path": "File path",
|
||||
"backup-restore-mode": "Restore mode",
|
||||
"backup-restore-add-missing": "Add missing data only",
|
||||
"backup-restore-replace-all": "Replace all data",
|
||||
"backup-restore-select-first": "Select a backup from the list first.",
|
||||
"backup-restore-confirm": "Restore the selected backup? Attachments/avatars are written to the filesystem and data to the database according to the chosen mode.",
|
||||
"gcs-project-id": "Project ID",
|
||||
"gcs-bucket": "Bucket",
|
||||
"gcs-key-filename": "Service account key file path",
|
||||
@@ -1986,8 +2051,6 @@
|
||||
"cpu-cores": "CPU Cores",
|
||||
"cpu-usage": "CPU Usage",
|
||||
"current-action": "Current Action",
|
||||
"database-migration": "Database Migration",
|
||||
"database-migration-description": "Updating database structure for improved functionality and performance. This process may take several minutes.",
|
||||
"database-migrations": "Database Migrations",
|
||||
"days-old": "Days Old",
|
||||
"duration": "Duration",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accept": "Aceptar",
|
||||
"activity-changedTitle": "changed title to %s of %s",
|
||||
"activity-changedDescription": "changed description of %s",
|
||||
"act-deleteCard": "deleted card __card__ from list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-removeBoard": "deleted board __board__",
|
||||
"act-removeList": "deleted list __list__ at board __board__",
|
||||
@@ -441,6 +442,7 @@
|
||||
"createCustomField": "Crear un campo",
|
||||
"createCustomFieldPopup-title": "Crear un campo",
|
||||
"current": "actual",
|
||||
"custom-color": "Custom color",
|
||||
"custom-field-delete-pop": "Se eliminará este campo personalizado de todas las tarjetas y se destruirá su historial. Esta acción no puede deshacerse.",
|
||||
"custom-field-checkbox": "Casilla de verificación",
|
||||
"custom-field-currency": "Moneda",
|
||||
@@ -508,6 +510,7 @@
|
||||
"error-csv-schema": "Su CSV(Valores separados por coma)/TSV(Valores separados por tab) no incluyen la información apropiada en el formato correcto",
|
||||
"error-import-empty-board": "This export contains no board content (no swimlanes, lists or cards). It may have been created by an older WeKan version with a known export bug. Please re-export the board with the current version and import the new file.",
|
||||
"error-list-doesNotExist": "La lista no existe",
|
||||
"error-linked-card-not-allowed": "You can only link to a normal card. Linking to another linked card, or to a card that links back to this board, is not allowed because it would make the cards inaccessible.",
|
||||
"error-user-disabled": "This user account is disabled",
|
||||
"error-user-doesNotExist": "El usuario no existe",
|
||||
"error-user-notAllowSelf": "No puedes invitarte a ti mismo",
|
||||
@@ -581,6 +584,7 @@
|
||||
"fullname": "Nombre completo",
|
||||
"header-logo-title": "Volver a tu página de tableros",
|
||||
"show-activities": "Mostrar actividades",
|
||||
"hide-activities": "Hide Activities",
|
||||
"headerBarCreateBoardPopup-title": "Crear tablero",
|
||||
"home": "Inicio",
|
||||
"import": "Importar",
|
||||
@@ -894,7 +898,12 @@
|
||||
"no-name": "(Desconocido)",
|
||||
"Node_version": "Versión de Node",
|
||||
"Meteor_version": "Versión de Meteor",
|
||||
"MongoDB_version": "Versión de MongoDB",
|
||||
"Database_type": "Database type",
|
||||
"Database_commit": "Database commit",
|
||||
"FerretDB_version": "FerretDB version",
|
||||
"FerretDB_commit": "FerretDB commit",
|
||||
"Reactivity_mode": "Reactivity mode (changeStreams / oplog / polling)",
|
||||
"MongoDB_version": "MongoDB compatible version",
|
||||
"MongoDB_storage_engine": "Motor de almacenamiento de MongoDB",
|
||||
"MongoDB_Oplog_enabled": "Oplog de MongoDB habilitado",
|
||||
"OS_Arch": "Arquitectura del sistema",
|
||||
@@ -1015,6 +1024,17 @@
|
||||
"r-workflow-help": "Drag a trigger and an action into the builder, then add the rule. Drag an action onto an existing rule to change what it does.",
|
||||
"r-drop-trigger": "drop a trigger here",
|
||||
"r-drop-action": "drop an action here",
|
||||
"r-w-card-created": "Card is created",
|
||||
"r-w-card-archived": "Card is archived",
|
||||
"r-w-card-unarchived": "Card is unarchived",
|
||||
"r-w-label-added": "Any label is added",
|
||||
"r-w-label-removed": "Any label is removed",
|
||||
"r-w-member-added": "A member is added",
|
||||
"r-w-member-removed": "A member is removed",
|
||||
"r-w-checklist-added": "A checklist is added",
|
||||
"r-w-attachment-added": "An attachment is added",
|
||||
"r-w-every-day-at": "Every day at __time__",
|
||||
"r-w-set-received-now": "Set received date to now",
|
||||
"r-export-json": "Export rules to JSON",
|
||||
"r-export-csv": "Export rules to CSV",
|
||||
"r-import-json": "Import rules from JSON",
|
||||
@@ -1068,6 +1088,12 @@
|
||||
"r-mark-incomplete": "Mark card incomplete",
|
||||
"r-move-all-cards": "Move all cards in list",
|
||||
"r-set-date-relative": "Set date relative to now",
|
||||
"r-later": "later",
|
||||
"r-unit-minutes": "minute(s)",
|
||||
"r-unit-hours": "hour(s)",
|
||||
"r-unit-days": "day(s)",
|
||||
"r-unit-weeks": "week(s)",
|
||||
"r-unit-months": "month(s)",
|
||||
"r-trigger": "disparador",
|
||||
"r-action": "acción",
|
||||
"r-when-a-card": "Cuando una tarjeta",
|
||||
@@ -1809,6 +1835,45 @@
|
||||
"azure-container": "Container",
|
||||
"gcs-storage": "Google Cloud Storage",
|
||||
"gcs-storage-description": "Google Cloud Storage.",
|
||||
"database-migration": "Database migration",
|
||||
"database-migration-description": "Migrate text-based data (everything except attachments and avatars, which stay on the filesystem) between MongoDB and FerretDB v1 (SQLite). The other database must be running and reachable (default ports: FerretDB mongodb://127.0.0.1:27018, MongoDB mongodb://127.0.0.1:27019; override with WEKAN_FERRETDB_URL / WEKAN_MONGODB_URL). After it finishes, point MONGO_URL at the other database and restart WeKan (on Snap: snap set wekan database=ferretdb or =mongodb).",
|
||||
"database-migrate-to-ferretdb": "Migrate text data to FerretDB v1 (SQLite)",
|
||||
"database-migrate-to-mongodb": "Migrate text data to MongoDB",
|
||||
"database-migration-confirm": "Copy all text-based collections to __db__? Attachments and avatars on the filesystem are not affected. The target database must be running.",
|
||||
"database-migration-phase": "Phase",
|
||||
"database-migration-done": "Text-data migration completed.",
|
||||
"collections": "collections",
|
||||
"features": "Features",
|
||||
"features-performance": "Performance",
|
||||
"cards-loading": "Card loading",
|
||||
"cards-loading-all": "All cards (default)",
|
||||
"cards-loading-lazy": "Lazy (only visible cards)",
|
||||
"cards-loading-description": "How each board loads its cards. All cards: every card is loaded into the browser (simplest, all features work, but a board with thousands of cards sends a lot of data to each viewer). Lazy: each list loads only the cards currently visible (infinite scroll) plus a live count, so very large boards use less memory and bandwidth. Also set by the CARDS_LOADING environment variable.",
|
||||
"cards-loading-lazy-note": "Lazy card loading is experimental: card counters and WIP limits are accurate, but the Calendar/Table/Gantt views and multi-select reflect only the cards loaded so far. Reload open boards after changing this.",
|
||||
"backup": "Backup",
|
||||
"backup-description": "Back up Attachments, Avatars and/or Data (all text-based collections that are not attachments or avatars). The .zip is streamed directly to the selected storage — no temporary file — as backup/YYYY/MM/DD/HH_MM_SS/backup.zip, containing YYYY_MM_DD-HH_MM_SS/attachments, /avatars and /data. Cloud storages (S3/MinIO, Azure, GCS) use the credentials configured in their storage tabs.",
|
||||
"backup-data": "Data (text)",
|
||||
"backup-storage": "Save to storage",
|
||||
"backup-now": "Backup now",
|
||||
"backup-done": "Backup completed",
|
||||
"backup-schedule": "Scheduled backups",
|
||||
"backup-frequency": "Frequency",
|
||||
"backup-frequency-off": "Off",
|
||||
"backup-frequency-daily": "Daily",
|
||||
"backup-frequency-weekly": "Weekly",
|
||||
"backup-frequency-monthly": "Monthly",
|
||||
"backup-time": "Time (HH:MM)",
|
||||
"backup-day-of-week": "Day of week (weekly)",
|
||||
"backup-day-of-month": "Day of month (monthly, 1-28)",
|
||||
"backup-restore": "Restaurar",
|
||||
"backup-list": "List backups",
|
||||
"backup-datetime": "Date and time",
|
||||
"backup-path": "File path",
|
||||
"backup-restore-mode": "Restore mode",
|
||||
"backup-restore-add-missing": "Add missing data only",
|
||||
"backup-restore-replace-all": "Replace all data",
|
||||
"backup-restore-select-first": "Select a backup from the list first.",
|
||||
"backup-restore-confirm": "Restore the selected backup? Attachments/avatars are written to the filesystem and data to the database according to the chosen mode.",
|
||||
"gcs-project-id": "Project ID",
|
||||
"gcs-bucket": "Bucket",
|
||||
"gcs-key-filename": "Service account key file path",
|
||||
@@ -1986,8 +2051,6 @@
|
||||
"cpu-cores": "CPU Cores",
|
||||
"cpu-usage": "CPU Usage",
|
||||
"current-action": "Current Action",
|
||||
"database-migration": "Database Migration",
|
||||
"database-migration-description": "Updating database structure for improved functionality and performance. This process may take several minutes.",
|
||||
"database-migrations": "Database Migrations",
|
||||
"days-old": "Days Old",
|
||||
"duration": "Duración",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accept": "Qəbul edin",
|
||||
"activity-changedTitle": "changed title to %s of %s",
|
||||
"activity-changedDescription": "changed description of %s",
|
||||
"act-deleteCard": "deleted card __card__ from list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-removeBoard": "deleted board __board__",
|
||||
"act-removeList": "deleted list __list__ at board __board__",
|
||||
@@ -441,6 +442,7 @@
|
||||
"createCustomField": "Sahə yaradın",
|
||||
"createCustomFieldPopup-title": "Sahə yaradın",
|
||||
"current": "cari",
|
||||
"custom-color": "Custom color",
|
||||
"custom-field-delete-pop": "Geri qaytarma yoxdur. Bu, bu fərdi sahəni bütün kartlardan siləcək və onun tarixçəsini məhv edəcək.",
|
||||
"custom-field-checkbox": "Yoxlama qutusu",
|
||||
"custom-field-currency": "Valyuta",
|
||||
@@ -508,6 +510,7 @@
|
||||
"error-csv-schema": "Sizin CSV(Vergüllə Ayrılmış Dəyərlər)/TSV (Vergüllə Ayrılmış Dəyərlər) düzgün formatda düzgün məlumatları ehtiva etmir",
|
||||
"error-import-empty-board": "This export contains no board content (no swimlanes, lists or cards). It may have been created by an older WeKan version with a known export bug. Please re-export the board with the current version and import the new file.",
|
||||
"error-list-doesNotExist": "Bu siyahı mövcud deyil",
|
||||
"error-linked-card-not-allowed": "You can only link to a normal card. Linking to another linked card, or to a card that links back to this board, is not allowed because it would make the cards inaccessible.",
|
||||
"error-user-disabled": "This user account is disabled",
|
||||
"error-user-doesNotExist": "Bu istifadəçi mövcud deyil",
|
||||
"error-user-notAllowSelf": "Özünüzü dəvət edə bilməzsiniz",
|
||||
@@ -581,6 +584,7 @@
|
||||
"fullname": "Tam adı",
|
||||
"header-logo-title": "Lövhələr səhifəsinə qayıdın.",
|
||||
"show-activities": "Fəaliyyətləri göstərin",
|
||||
"hide-activities": "Hide Activities",
|
||||
"headerBarCreateBoardPopup-title": "Şura yaradın",
|
||||
"home": "Ev",
|
||||
"import": "İdxal",
|
||||
@@ -894,7 +898,12 @@
|
||||
"no-name": "(Naməlum)",
|
||||
"Node_version": "Node versiyası",
|
||||
"Meteor_version": "Meteor versiyası",
|
||||
"MongoDB_version": "MongoDB versiyası",
|
||||
"Database_type": "Database type",
|
||||
"Database_commit": "Database commit",
|
||||
"FerretDB_version": "FerretDB version",
|
||||
"FerretDB_commit": "FerretDB commit",
|
||||
"Reactivity_mode": "Reactivity mode (changeStreams / oplog / polling)",
|
||||
"MongoDB_version": "MongoDB compatible version",
|
||||
"MongoDB_storage_engine": "MongoDB saxlama mühərriki",
|
||||
"MongoDB_Oplog_enabled": "MongoDB Oploq aktivləşdirildi",
|
||||
"OS_Arch": "OS Arch",
|
||||
@@ -1015,6 +1024,17 @@
|
||||
"r-workflow-help": "Drag a trigger and an action into the builder, then add the rule. Drag an action onto an existing rule to change what it does.",
|
||||
"r-drop-trigger": "drop a trigger here",
|
||||
"r-drop-action": "drop an action here",
|
||||
"r-w-card-created": "Card is created",
|
||||
"r-w-card-archived": "Card is archived",
|
||||
"r-w-card-unarchived": "Card is unarchived",
|
||||
"r-w-label-added": "Any label is added",
|
||||
"r-w-label-removed": "Any label is removed",
|
||||
"r-w-member-added": "A member is added",
|
||||
"r-w-member-removed": "A member is removed",
|
||||
"r-w-checklist-added": "A checklist is added",
|
||||
"r-w-attachment-added": "An attachment is added",
|
||||
"r-w-every-day-at": "Every day at __time__",
|
||||
"r-w-set-received-now": "Set received date to now",
|
||||
"r-export-json": "Export rules to JSON",
|
||||
"r-export-csv": "Export rules to CSV",
|
||||
"r-import-json": "Import rules from JSON",
|
||||
@@ -1068,6 +1088,12 @@
|
||||
"r-mark-incomplete": "Mark card incomplete",
|
||||
"r-move-all-cards": "Move all cards in list",
|
||||
"r-set-date-relative": "Set date relative to now",
|
||||
"r-later": "later",
|
||||
"r-unit-minutes": "minute(s)",
|
||||
"r-unit-hours": "hour(s)",
|
||||
"r-unit-days": "day(s)",
|
||||
"r-unit-weeks": "week(s)",
|
||||
"r-unit-months": "month(s)",
|
||||
"r-trigger": "Tətik",
|
||||
"r-action": "Fəaliyyət",
|
||||
"r-when-a-card": "Bir kart olanda",
|
||||
@@ -1809,6 +1835,45 @@
|
||||
"azure-container": "Container",
|
||||
"gcs-storage": "Google Cloud Storage",
|
||||
"gcs-storage-description": "Google Cloud Storage.",
|
||||
"database-migration": "Database migration",
|
||||
"database-migration-description": "Migrate text-based data (everything except attachments and avatars, which stay on the filesystem) between MongoDB and FerretDB v1 (SQLite). The other database must be running and reachable (default ports: FerretDB mongodb://127.0.0.1:27018, MongoDB mongodb://127.0.0.1:27019; override with WEKAN_FERRETDB_URL / WEKAN_MONGODB_URL). After it finishes, point MONGO_URL at the other database and restart WeKan (on Snap: snap set wekan database=ferretdb or =mongodb).",
|
||||
"database-migrate-to-ferretdb": "Migrate text data to FerretDB v1 (SQLite)",
|
||||
"database-migrate-to-mongodb": "Migrate text data to MongoDB",
|
||||
"database-migration-confirm": "Copy all text-based collections to __db__? Attachments and avatars on the filesystem are not affected. The target database must be running.",
|
||||
"database-migration-phase": "Phase",
|
||||
"database-migration-done": "Text-data migration completed.",
|
||||
"collections": "collections",
|
||||
"features": "Features",
|
||||
"features-performance": "Performance",
|
||||
"cards-loading": "Card loading",
|
||||
"cards-loading-all": "All cards (default)",
|
||||
"cards-loading-lazy": "Lazy (only visible cards)",
|
||||
"cards-loading-description": "How each board loads its cards. All cards: every card is loaded into the browser (simplest, all features work, but a board with thousands of cards sends a lot of data to each viewer). Lazy: each list loads only the cards currently visible (infinite scroll) plus a live count, so very large boards use less memory and bandwidth. Also set by the CARDS_LOADING environment variable.",
|
||||
"cards-loading-lazy-note": "Lazy card loading is experimental: card counters and WIP limits are accurate, but the Calendar/Table/Gantt views and multi-select reflect only the cards loaded so far. Reload open boards after changing this.",
|
||||
"backup": "Backup",
|
||||
"backup-description": "Back up Attachments, Avatars and/or Data (all text-based collections that are not attachments or avatars). The .zip is streamed directly to the selected storage — no temporary file — as backup/YYYY/MM/DD/HH_MM_SS/backup.zip, containing YYYY_MM_DD-HH_MM_SS/attachments, /avatars and /data. Cloud storages (S3/MinIO, Azure, GCS) use the credentials configured in their storage tabs.",
|
||||
"backup-data": "Data (text)",
|
||||
"backup-storage": "Save to storage",
|
||||
"backup-now": "Backup now",
|
||||
"backup-done": "Backup completed",
|
||||
"backup-schedule": "Scheduled backups",
|
||||
"backup-frequency": "Frequency",
|
||||
"backup-frequency-off": "Off",
|
||||
"backup-frequency-daily": "Daily",
|
||||
"backup-frequency-weekly": "Weekly",
|
||||
"backup-frequency-monthly": "Monthly",
|
||||
"backup-time": "Time (HH:MM)",
|
||||
"backup-day-of-week": "Day of week (weekly)",
|
||||
"backup-day-of-month": "Day of month (monthly, 1-28)",
|
||||
"backup-restore": "Bərpa et",
|
||||
"backup-list": "List backups",
|
||||
"backup-datetime": "Date and time",
|
||||
"backup-path": "File path",
|
||||
"backup-restore-mode": "Restore mode",
|
||||
"backup-restore-add-missing": "Add missing data only",
|
||||
"backup-restore-replace-all": "Replace all data",
|
||||
"backup-restore-select-first": "Select a backup from the list first.",
|
||||
"backup-restore-confirm": "Restore the selected backup? Attachments/avatars are written to the filesystem and data to the database according to the chosen mode.",
|
||||
"gcs-project-id": "Project ID",
|
||||
"gcs-bucket": "Bucket",
|
||||
"gcs-key-filename": "Service account key file path",
|
||||
@@ -1986,8 +2051,6 @@
|
||||
"cpu-cores": "CPU nüvələri",
|
||||
"cpu-usage": "CPU İstifadəsi",
|
||||
"current-action": "Cari Fəaliyyət",
|
||||
"database-migration": "Verilənlər bazasının miqrasiyası",
|
||||
"database-migration-description": "Təkmil funksionallıq və performans üçün verilənlər bazası strukturunun yenilənməsi. Bu proses bir neçə dəqiqə çəkə bilər.",
|
||||
"database-migrations": "Verilənlər Bazasının Miqrasiyaları",
|
||||
"days-old": "Günlər köhnə",
|
||||
"duration": "Müddət",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accept": "Qəbul edin",
|
||||
"activity-changedTitle": "changed title to %s of %s",
|
||||
"activity-changedDescription": "changed description of %s",
|
||||
"act-deleteCard": "deleted card __card__ from list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-removeBoard": "deleted board __board__",
|
||||
"act-removeList": "deleted list __list__ at board __board__",
|
||||
@@ -441,6 +442,7 @@
|
||||
"createCustomField": "Sahə yaradın",
|
||||
"createCustomFieldPopup-title": "Sahə yaradın",
|
||||
"current": "cari",
|
||||
"custom-color": "Custom color",
|
||||
"custom-field-delete-pop": "Geri qaytarma yoxdur. Bu, bu fərdi sahəni bütün kartlardan siləcək və onun tarixçəsini məhv edəcək.",
|
||||
"custom-field-checkbox": "Yoxlama qutusu",
|
||||
"custom-field-currency": "Valyuta",
|
||||
@@ -508,6 +510,7 @@
|
||||
"error-csv-schema": "Sizin CSV(Vergüllə Ayrılmış Dəyərlər)/TSV (Vergüllə Ayrılmış Dəyərlər) düzgün formatda düzgün məlumatları ehtiva etmir",
|
||||
"error-import-empty-board": "This export contains no board content (no swimlanes, lists or cards). It may have been created by an older WeKan version with a known export bug. Please re-export the board with the current version and import the new file.",
|
||||
"error-list-doesNotExist": "Bu siyahı mövcud deyil",
|
||||
"error-linked-card-not-allowed": "You can only link to a normal card. Linking to another linked card, or to a card that links back to this board, is not allowed because it would make the cards inaccessible.",
|
||||
"error-user-disabled": "This user account is disabled",
|
||||
"error-user-doesNotExist": "Bu istifadəçi mövcud deyil",
|
||||
"error-user-notAllowSelf": "Özünüzü dəvət edə bilməzsiniz",
|
||||
@@ -581,6 +584,7 @@
|
||||
"fullname": "Tam adı",
|
||||
"header-logo-title": "Lövhələr səhifəsinə qayıdın.",
|
||||
"show-activities": "Fəaliyyətləri göstərin",
|
||||
"hide-activities": "Hide Activities",
|
||||
"headerBarCreateBoardPopup-title": "Şura yaradın",
|
||||
"home": "Ev",
|
||||
"import": "İdxal",
|
||||
@@ -894,7 +898,12 @@
|
||||
"no-name": "(Naməlum)",
|
||||
"Node_version": "Node versiyası",
|
||||
"Meteor_version": "Meteor versiyası",
|
||||
"MongoDB_version": "MongoDB versiyası",
|
||||
"Database_type": "Database type",
|
||||
"Database_commit": "Database commit",
|
||||
"FerretDB_version": "FerretDB version",
|
||||
"FerretDB_commit": "FerretDB commit",
|
||||
"Reactivity_mode": "Reactivity mode (changeStreams / oplog / polling)",
|
||||
"MongoDB_version": "MongoDB compatible version",
|
||||
"MongoDB_storage_engine": "MongoDB saxlama mühərriki",
|
||||
"MongoDB_Oplog_enabled": "MongoDB Oploq aktivləşdirildi",
|
||||
"OS_Arch": "OS Arch",
|
||||
@@ -1015,6 +1024,17 @@
|
||||
"r-workflow-help": "Drag a trigger and an action into the builder, then add the rule. Drag an action onto an existing rule to change what it does.",
|
||||
"r-drop-trigger": "drop a trigger here",
|
||||
"r-drop-action": "drop an action here",
|
||||
"r-w-card-created": "Card is created",
|
||||
"r-w-card-archived": "Card is archived",
|
||||
"r-w-card-unarchived": "Card is unarchived",
|
||||
"r-w-label-added": "Any label is added",
|
||||
"r-w-label-removed": "Any label is removed",
|
||||
"r-w-member-added": "A member is added",
|
||||
"r-w-member-removed": "A member is removed",
|
||||
"r-w-checklist-added": "A checklist is added",
|
||||
"r-w-attachment-added": "An attachment is added",
|
||||
"r-w-every-day-at": "Every day at __time__",
|
||||
"r-w-set-received-now": "Set received date to now",
|
||||
"r-export-json": "Export rules to JSON",
|
||||
"r-export-csv": "Export rules to CSV",
|
||||
"r-import-json": "Import rules from JSON",
|
||||
@@ -1068,6 +1088,12 @@
|
||||
"r-mark-incomplete": "Mark card incomplete",
|
||||
"r-move-all-cards": "Move all cards in list",
|
||||
"r-set-date-relative": "Set date relative to now",
|
||||
"r-later": "later",
|
||||
"r-unit-minutes": "minute(s)",
|
||||
"r-unit-hours": "hour(s)",
|
||||
"r-unit-days": "day(s)",
|
||||
"r-unit-weeks": "week(s)",
|
||||
"r-unit-months": "month(s)",
|
||||
"r-trigger": "Tətik",
|
||||
"r-action": "Fəaliyyət",
|
||||
"r-when-a-card": "Bir kart olanda",
|
||||
@@ -1809,6 +1835,45 @@
|
||||
"azure-container": "Container",
|
||||
"gcs-storage": "Google Cloud Storage",
|
||||
"gcs-storage-description": "Google Cloud Storage.",
|
||||
"database-migration": "Database migration",
|
||||
"database-migration-description": "Migrate text-based data (everything except attachments and avatars, which stay on the filesystem) between MongoDB and FerretDB v1 (SQLite). The other database must be running and reachable (default ports: FerretDB mongodb://127.0.0.1:27018, MongoDB mongodb://127.0.0.1:27019; override with WEKAN_FERRETDB_URL / WEKAN_MONGODB_URL). After it finishes, point MONGO_URL at the other database and restart WeKan (on Snap: snap set wekan database=ferretdb or =mongodb).",
|
||||
"database-migrate-to-ferretdb": "Migrate text data to FerretDB v1 (SQLite)",
|
||||
"database-migrate-to-mongodb": "Migrate text data to MongoDB",
|
||||
"database-migration-confirm": "Copy all text-based collections to __db__? Attachments and avatars on the filesystem are not affected. The target database must be running.",
|
||||
"database-migration-phase": "Phase",
|
||||
"database-migration-done": "Text-data migration completed.",
|
||||
"collections": "collections",
|
||||
"features": "Features",
|
||||
"features-performance": "Performance",
|
||||
"cards-loading": "Card loading",
|
||||
"cards-loading-all": "All cards (default)",
|
||||
"cards-loading-lazy": "Lazy (only visible cards)",
|
||||
"cards-loading-description": "How each board loads its cards. All cards: every card is loaded into the browser (simplest, all features work, but a board with thousands of cards sends a lot of data to each viewer). Lazy: each list loads only the cards currently visible (infinite scroll) plus a live count, so very large boards use less memory and bandwidth. Also set by the CARDS_LOADING environment variable.",
|
||||
"cards-loading-lazy-note": "Lazy card loading is experimental: card counters and WIP limits are accurate, but the Calendar/Table/Gantt views and multi-select reflect only the cards loaded so far. Reload open boards after changing this.",
|
||||
"backup": "Backup",
|
||||
"backup-description": "Back up Attachments, Avatars and/or Data (all text-based collections that are not attachments or avatars). The .zip is streamed directly to the selected storage — no temporary file — as backup/YYYY/MM/DD/HH_MM_SS/backup.zip, containing YYYY_MM_DD-HH_MM_SS/attachments, /avatars and /data. Cloud storages (S3/MinIO, Azure, GCS) use the credentials configured in their storage tabs.",
|
||||
"backup-data": "Data (text)",
|
||||
"backup-storage": "Save to storage",
|
||||
"backup-now": "Backup now",
|
||||
"backup-done": "Backup completed",
|
||||
"backup-schedule": "Scheduled backups",
|
||||
"backup-frequency": "Frequency",
|
||||
"backup-frequency-off": "Off",
|
||||
"backup-frequency-daily": "Daily",
|
||||
"backup-frequency-weekly": "Weekly",
|
||||
"backup-frequency-monthly": "Monthly",
|
||||
"backup-time": "Time (HH:MM)",
|
||||
"backup-day-of-week": "Day of week (weekly)",
|
||||
"backup-day-of-month": "Day of month (monthly, 1-28)",
|
||||
"backup-restore": "Bərpa et",
|
||||
"backup-list": "List backups",
|
||||
"backup-datetime": "Date and time",
|
||||
"backup-path": "File path",
|
||||
"backup-restore-mode": "Restore mode",
|
||||
"backup-restore-add-missing": "Add missing data only",
|
||||
"backup-restore-replace-all": "Replace all data",
|
||||
"backup-restore-select-first": "Select a backup from the list first.",
|
||||
"backup-restore-confirm": "Restore the selected backup? Attachments/avatars are written to the filesystem and data to the database according to the chosen mode.",
|
||||
"gcs-project-id": "Project ID",
|
||||
"gcs-bucket": "Bucket",
|
||||
"gcs-key-filename": "Service account key file path",
|
||||
@@ -1986,8 +2051,6 @@
|
||||
"cpu-cores": "CPU nüvələri",
|
||||
"cpu-usage": "CPU İstifadəsi",
|
||||
"current-action": "Cari Fəaliyyət",
|
||||
"database-migration": "Verilənlər bazasının miqrasiyası",
|
||||
"database-migration-description": "Təkmil funksionallıq və performans üçün verilənlər bazası strukturunun yenilənməsi. Bu proses bir neçə dəqiqə çəkə bilər.",
|
||||
"database-migrations": "Verilənlər Bazasının Miqrasiyaları",
|
||||
"days-old": "Günlər köhnə",
|
||||
"duration": "Müddət",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accept": "Qəbul edin",
|
||||
"activity-changedTitle": "changed title to %s of %s",
|
||||
"activity-changedDescription": "changed description of %s",
|
||||
"act-deleteCard": "deleted card __card__ from list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-removeBoard": "deleted board __board__",
|
||||
"act-removeList": "deleted list __list__ at board __board__",
|
||||
@@ -441,6 +442,7 @@
|
||||
"createCustomField": "Sahə yaradın",
|
||||
"createCustomFieldPopup-title": "Sahə yaradın",
|
||||
"current": "cari",
|
||||
"custom-color": "Custom color",
|
||||
"custom-field-delete-pop": "Geri qaytarma yoxdur. Bu, bu fərdi sahəni bütün kartlardan siləcək və onun tarixçəsini məhv edəcək.",
|
||||
"custom-field-checkbox": "Yoxlama qutusu",
|
||||
"custom-field-currency": "Valyuta",
|
||||
@@ -508,6 +510,7 @@
|
||||
"error-csv-schema": "Sizin CSV(Vergüllə Ayrılmış Dəyərlər)/TSV (Vergüllə Ayrılmış Dəyərlər) düzgün formatda düzgün məlumatları ehtiva etmir",
|
||||
"error-import-empty-board": "This export contains no board content (no swimlanes, lists or cards). It may have been created by an older WeKan version with a known export bug. Please re-export the board with the current version and import the new file.",
|
||||
"error-list-doesNotExist": "Bu siyahı mövcud deyil",
|
||||
"error-linked-card-not-allowed": "You can only link to a normal card. Linking to another linked card, or to a card that links back to this board, is not allowed because it would make the cards inaccessible.",
|
||||
"error-user-disabled": "This user account is disabled",
|
||||
"error-user-doesNotExist": "Bu istifadəçi mövcud deyil",
|
||||
"error-user-notAllowSelf": "Özünüzü dəvət edə bilməzsiniz",
|
||||
@@ -581,6 +584,7 @@
|
||||
"fullname": "Tam adı",
|
||||
"header-logo-title": "Lövhələr səhifəsinə qayıdın.",
|
||||
"show-activities": "Fəaliyyətləri göstərin",
|
||||
"hide-activities": "Hide Activities",
|
||||
"headerBarCreateBoardPopup-title": "Şura yaradın",
|
||||
"home": "Ev",
|
||||
"import": "İdxal",
|
||||
@@ -894,7 +898,12 @@
|
||||
"no-name": "(Naməlum)",
|
||||
"Node_version": "Node versiyası",
|
||||
"Meteor_version": "Meteor versiyası",
|
||||
"MongoDB_version": "MongoDB versiyası",
|
||||
"Database_type": "Database type",
|
||||
"Database_commit": "Database commit",
|
||||
"FerretDB_version": "FerretDB version",
|
||||
"FerretDB_commit": "FerretDB commit",
|
||||
"Reactivity_mode": "Reactivity mode (changeStreams / oplog / polling)",
|
||||
"MongoDB_version": "MongoDB compatible version",
|
||||
"MongoDB_storage_engine": "MongoDB saxlama mühərriki",
|
||||
"MongoDB_Oplog_enabled": "MongoDB Oploq aktivləşdirildi",
|
||||
"OS_Arch": "OS Arch",
|
||||
@@ -1015,6 +1024,17 @@
|
||||
"r-workflow-help": "Drag a trigger and an action into the builder, then add the rule. Drag an action onto an existing rule to change what it does.",
|
||||
"r-drop-trigger": "drop a trigger here",
|
||||
"r-drop-action": "drop an action here",
|
||||
"r-w-card-created": "Card is created",
|
||||
"r-w-card-archived": "Card is archived",
|
||||
"r-w-card-unarchived": "Card is unarchived",
|
||||
"r-w-label-added": "Any label is added",
|
||||
"r-w-label-removed": "Any label is removed",
|
||||
"r-w-member-added": "A member is added",
|
||||
"r-w-member-removed": "A member is removed",
|
||||
"r-w-checklist-added": "A checklist is added",
|
||||
"r-w-attachment-added": "An attachment is added",
|
||||
"r-w-every-day-at": "Every day at __time__",
|
||||
"r-w-set-received-now": "Set received date to now",
|
||||
"r-export-json": "Export rules to JSON",
|
||||
"r-export-csv": "Export rules to CSV",
|
||||
"r-import-json": "Import rules from JSON",
|
||||
@@ -1068,6 +1088,12 @@
|
||||
"r-mark-incomplete": "Mark card incomplete",
|
||||
"r-move-all-cards": "Move all cards in list",
|
||||
"r-set-date-relative": "Set date relative to now",
|
||||
"r-later": "later",
|
||||
"r-unit-minutes": "minute(s)",
|
||||
"r-unit-hours": "hour(s)",
|
||||
"r-unit-days": "day(s)",
|
||||
"r-unit-weeks": "week(s)",
|
||||
"r-unit-months": "month(s)",
|
||||
"r-trigger": "Tətik",
|
||||
"r-action": "Fəaliyyət",
|
||||
"r-when-a-card": "Bir kart olanda",
|
||||
@@ -1809,6 +1835,45 @@
|
||||
"azure-container": "Container",
|
||||
"gcs-storage": "Google Cloud Storage",
|
||||
"gcs-storage-description": "Google Cloud Storage.",
|
||||
"database-migration": "Database migration",
|
||||
"database-migration-description": "Migrate text-based data (everything except attachments and avatars, which stay on the filesystem) between MongoDB and FerretDB v1 (SQLite). The other database must be running and reachable (default ports: FerretDB mongodb://127.0.0.1:27018, MongoDB mongodb://127.0.0.1:27019; override with WEKAN_FERRETDB_URL / WEKAN_MONGODB_URL). After it finishes, point MONGO_URL at the other database and restart WeKan (on Snap: snap set wekan database=ferretdb or =mongodb).",
|
||||
"database-migrate-to-ferretdb": "Migrate text data to FerretDB v1 (SQLite)",
|
||||
"database-migrate-to-mongodb": "Migrate text data to MongoDB",
|
||||
"database-migration-confirm": "Copy all text-based collections to __db__? Attachments and avatars on the filesystem are not affected. The target database must be running.",
|
||||
"database-migration-phase": "Phase",
|
||||
"database-migration-done": "Text-data migration completed.",
|
||||
"collections": "collections",
|
||||
"features": "Features",
|
||||
"features-performance": "Performance",
|
||||
"cards-loading": "Card loading",
|
||||
"cards-loading-all": "All cards (default)",
|
||||
"cards-loading-lazy": "Lazy (only visible cards)",
|
||||
"cards-loading-description": "How each board loads its cards. All cards: every card is loaded into the browser (simplest, all features work, but a board with thousands of cards sends a lot of data to each viewer). Lazy: each list loads only the cards currently visible (infinite scroll) plus a live count, so very large boards use less memory and bandwidth. Also set by the CARDS_LOADING environment variable.",
|
||||
"cards-loading-lazy-note": "Lazy card loading is experimental: card counters and WIP limits are accurate, but the Calendar/Table/Gantt views and multi-select reflect only the cards loaded so far. Reload open boards after changing this.",
|
||||
"backup": "Backup",
|
||||
"backup-description": "Back up Attachments, Avatars and/or Data (all text-based collections that are not attachments or avatars). The .zip is streamed directly to the selected storage — no temporary file — as backup/YYYY/MM/DD/HH_MM_SS/backup.zip, containing YYYY_MM_DD-HH_MM_SS/attachments, /avatars and /data. Cloud storages (S3/MinIO, Azure, GCS) use the credentials configured in their storage tabs.",
|
||||
"backup-data": "Data (text)",
|
||||
"backup-storage": "Save to storage",
|
||||
"backup-now": "Backup now",
|
||||
"backup-done": "Backup completed",
|
||||
"backup-schedule": "Scheduled backups",
|
||||
"backup-frequency": "Frequency",
|
||||
"backup-frequency-off": "Off",
|
||||
"backup-frequency-daily": "Daily",
|
||||
"backup-frequency-weekly": "Weekly",
|
||||
"backup-frequency-monthly": "Monthly",
|
||||
"backup-time": "Time (HH:MM)",
|
||||
"backup-day-of-week": "Day of week (weekly)",
|
||||
"backup-day-of-month": "Day of month (monthly, 1-28)",
|
||||
"backup-restore": "Bərpa et",
|
||||
"backup-list": "List backups",
|
||||
"backup-datetime": "Date and time",
|
||||
"backup-path": "File path",
|
||||
"backup-restore-mode": "Restore mode",
|
||||
"backup-restore-add-missing": "Add missing data only",
|
||||
"backup-restore-replace-all": "Replace all data",
|
||||
"backup-restore-select-first": "Select a backup from the list first.",
|
||||
"backup-restore-confirm": "Restore the selected backup? Attachments/avatars are written to the filesystem and data to the database according to the chosen mode.",
|
||||
"gcs-project-id": "Project ID",
|
||||
"gcs-bucket": "Bucket",
|
||||
"gcs-key-filename": "Service account key file path",
|
||||
@@ -1986,8 +2051,6 @@
|
||||
"cpu-cores": "CPU nüvələri",
|
||||
"cpu-usage": "CPU İstifadəsi",
|
||||
"current-action": "Cari Fəaliyyət",
|
||||
"database-migration": "Verilənlər bazasının miqrasiyası",
|
||||
"database-migration-description": "Təkmil funksionallıq və performans üçün verilənlər bazası strukturunun yenilənməsi. Bu proses bir neçə dəqiqə çəkə bilər.",
|
||||
"database-migrations": "Verilənlər Bazasının Miqrasiyaları",
|
||||
"days-old": "Günlər köhnə",
|
||||
"duration": "Müddət",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accept": "Accept",
|
||||
"activity-changedTitle": "changed title to %s of %s",
|
||||
"activity-changedDescription": "changed description of %s",
|
||||
"act-deleteCard": "deleted card __card__ from list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-removeBoard": "deleted board __board__",
|
||||
"act-removeList": "deleted list __list__ at board __board__",
|
||||
@@ -441,6 +442,7 @@
|
||||
"createCustomField": "Create Field",
|
||||
"createCustomFieldPopup-title": "Create Field",
|
||||
"current": "current",
|
||||
"custom-color": "Custom color",
|
||||
"custom-field-delete-pop": "There is no undo. This will remove this custom field from all cards and destroy its history.",
|
||||
"custom-field-checkbox": "Checkbox",
|
||||
"custom-field-currency": "Currency",
|
||||
@@ -508,6 +510,7 @@
|
||||
"error-csv-schema": "Your CSV(Comma Separated Values)/TSV (Tab Separated Values) does not include the proper information in the correct format ",
|
||||
"error-import-empty-board": "This export contains no board content (no swimlanes, lists or cards). It may have been created by an older WeKan version with a known export bug. Please re-export the board with the current version and import the new file.",
|
||||
"error-list-doesNotExist": "This list does not exist",
|
||||
"error-linked-card-not-allowed": "You can only link to a normal card. Linking to another linked card, or to a card that links back to this board, is not allowed because it would make the cards inaccessible.",
|
||||
"error-user-disabled": "This user account is disabled",
|
||||
"error-user-doesNotExist": "This user does not exist",
|
||||
"error-user-notAllowSelf": "You can not invite yourself",
|
||||
@@ -581,6 +584,7 @@
|
||||
"fullname": "Full Name",
|
||||
"header-logo-title": "Go back to your boards page.",
|
||||
"show-activities": "Show Activities",
|
||||
"hide-activities": "Hide Activities",
|
||||
"headerBarCreateBoardPopup-title": "Create Board",
|
||||
"home": "Home",
|
||||
"import": "Import",
|
||||
@@ -894,7 +898,12 @@
|
||||
"no-name": "(Unknown)",
|
||||
"Node_version": "Node version",
|
||||
"Meteor_version": "Meteor version",
|
||||
"MongoDB_version": "MongoDB version",
|
||||
"Database_type": "Database type",
|
||||
"Database_commit": "Database commit",
|
||||
"FerretDB_version": "FerretDB version",
|
||||
"FerretDB_commit": "FerretDB commit",
|
||||
"Reactivity_mode": "Reactivity mode (changeStreams / oplog / polling)",
|
||||
"MongoDB_version": "MongoDB compatible version",
|
||||
"MongoDB_storage_engine": "MongoDB storage engine",
|
||||
"MongoDB_Oplog_enabled": "MongoDB Oplog enabled",
|
||||
"OS_Arch": "OS Arch",
|
||||
@@ -1015,6 +1024,17 @@
|
||||
"r-workflow-help": "Drag a trigger and an action into the builder, then add the rule. Drag an action onto an existing rule to change what it does.",
|
||||
"r-drop-trigger": "drop a trigger here",
|
||||
"r-drop-action": "drop an action here",
|
||||
"r-w-card-created": "Card is created",
|
||||
"r-w-card-archived": "Card is archived",
|
||||
"r-w-card-unarchived": "Card is unarchived",
|
||||
"r-w-label-added": "Any label is added",
|
||||
"r-w-label-removed": "Any label is removed",
|
||||
"r-w-member-added": "A member is added",
|
||||
"r-w-member-removed": "A member is removed",
|
||||
"r-w-checklist-added": "A checklist is added",
|
||||
"r-w-attachment-added": "An attachment is added",
|
||||
"r-w-every-day-at": "Every day at __time__",
|
||||
"r-w-set-received-now": "Set received date to now",
|
||||
"r-export-json": "Export rules to JSON",
|
||||
"r-export-csv": "Export rules to CSV",
|
||||
"r-import-json": "Import rules from JSON",
|
||||
@@ -1068,6 +1088,12 @@
|
||||
"r-mark-incomplete": "Mark card incomplete",
|
||||
"r-move-all-cards": "Move all cards in list",
|
||||
"r-set-date-relative": "Set date relative to now",
|
||||
"r-later": "later",
|
||||
"r-unit-minutes": "minute(s)",
|
||||
"r-unit-hours": "hour(s)",
|
||||
"r-unit-days": "day(s)",
|
||||
"r-unit-weeks": "week(s)",
|
||||
"r-unit-months": "month(s)",
|
||||
"r-trigger": "Trigger",
|
||||
"r-action": "Action",
|
||||
"r-when-a-card": "When a card",
|
||||
@@ -1809,6 +1835,45 @@
|
||||
"azure-container": "Container",
|
||||
"gcs-storage": "Google Cloud Storage",
|
||||
"gcs-storage-description": "Google Cloud Storage.",
|
||||
"database-migration": "Database migration",
|
||||
"database-migration-description": "Migrate text-based data (everything except attachments and avatars, which stay on the filesystem) between MongoDB and FerretDB v1 (SQLite). The other database must be running and reachable (default ports: FerretDB mongodb://127.0.0.1:27018, MongoDB mongodb://127.0.0.1:27019; override with WEKAN_FERRETDB_URL / WEKAN_MONGODB_URL). After it finishes, point MONGO_URL at the other database and restart WeKan (on Snap: snap set wekan database=ferretdb or =mongodb).",
|
||||
"database-migrate-to-ferretdb": "Migrate text data to FerretDB v1 (SQLite)",
|
||||
"database-migrate-to-mongodb": "Migrate text data to MongoDB",
|
||||
"database-migration-confirm": "Copy all text-based collections to __db__? Attachments and avatars on the filesystem are not affected. The target database must be running.",
|
||||
"database-migration-phase": "Phase",
|
||||
"database-migration-done": "Text-data migration completed.",
|
||||
"collections": "collections",
|
||||
"features": "Features",
|
||||
"features-performance": "Performance",
|
||||
"cards-loading": "Card loading",
|
||||
"cards-loading-all": "All cards (default)",
|
||||
"cards-loading-lazy": "Lazy (only visible cards)",
|
||||
"cards-loading-description": "How each board loads its cards. All cards: every card is loaded into the browser (simplest, all features work, but a board with thousands of cards sends a lot of data to each viewer). Lazy: each list loads only the cards currently visible (infinite scroll) plus a live count, so very large boards use less memory and bandwidth. Also set by the CARDS_LOADING environment variable.",
|
||||
"cards-loading-lazy-note": "Lazy card loading is experimental: card counters and WIP limits are accurate, but the Calendar/Table/Gantt views and multi-select reflect only the cards loaded so far. Reload open boards after changing this.",
|
||||
"backup": "Backup",
|
||||
"backup-description": "Back up Attachments, Avatars and/or Data (all text-based collections that are not attachments or avatars). The .zip is streamed directly to the selected storage — no temporary file — as backup/YYYY/MM/DD/HH_MM_SS/backup.zip, containing YYYY_MM_DD-HH_MM_SS/attachments, /avatars and /data. Cloud storages (S3/MinIO, Azure, GCS) use the credentials configured in their storage tabs.",
|
||||
"backup-data": "Data (text)",
|
||||
"backup-storage": "Save to storage",
|
||||
"backup-now": "Backup now",
|
||||
"backup-done": "Backup completed",
|
||||
"backup-schedule": "Scheduled backups",
|
||||
"backup-frequency": "Frequency",
|
||||
"backup-frequency-off": "Off",
|
||||
"backup-frequency-daily": "Daily",
|
||||
"backup-frequency-weekly": "Weekly",
|
||||
"backup-frequency-monthly": "Monthly",
|
||||
"backup-time": "Time (HH:MM)",
|
||||
"backup-day-of-week": "Day of week (weekly)",
|
||||
"backup-day-of-month": "Day of month (monthly, 1-28)",
|
||||
"backup-restore": "Restore",
|
||||
"backup-list": "List backups",
|
||||
"backup-datetime": "Date and time",
|
||||
"backup-path": "File path",
|
||||
"backup-restore-mode": "Restore mode",
|
||||
"backup-restore-add-missing": "Add missing data only",
|
||||
"backup-restore-replace-all": "Replace all data",
|
||||
"backup-restore-select-first": "Select a backup from the list first.",
|
||||
"backup-restore-confirm": "Restore the selected backup? Attachments/avatars are written to the filesystem and data to the database according to the chosen mode.",
|
||||
"gcs-project-id": "Project ID",
|
||||
"gcs-bucket": "Bucket",
|
||||
"gcs-key-filename": "Service account key file path",
|
||||
@@ -1986,8 +2051,6 @@
|
||||
"cpu-cores": "CPU Cores",
|
||||
"cpu-usage": "CPU Usage",
|
||||
"current-action": "Current Action",
|
||||
"database-migration": "Database Migration",
|
||||
"database-migration-description": "Updating database structure for improved functionality and performance. This process may take several minutes.",
|
||||
"database-migrations": "Database Migrations",
|
||||
"days-old": "Days Old",
|
||||
"duration": "Duration",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accept": "Приемам",
|
||||
"activity-changedTitle": "changed title to %s of %s",
|
||||
"activity-changedDescription": "changed description of %s",
|
||||
"act-deleteCard": "deleted card __card__ from list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-removeBoard": "deleted board __board__",
|
||||
"act-removeList": "deleted list __list__ at board __board__",
|
||||
@@ -441,6 +442,7 @@
|
||||
"createCustomField": "Създай Поле",
|
||||
"createCustomFieldPopup-title": "Създай Поле",
|
||||
"current": "сегашен",
|
||||
"custom-color": "Custom color",
|
||||
"custom-field-delete-pop": "Опозив ове радње неће бити могућ. Радњом се уклања ова придружена рубрика из свих предмета и уништива њена претходна историја употребе.",
|
||||
"custom-field-checkbox": "Чекбокс",
|
||||
"custom-field-currency": "Валута",
|
||||
@@ -508,6 +510,7 @@
|
||||
"error-csv-schema": "Ваш CSV(Вредности раздвојене зарезом)/TSV (Вредности раздвојене табулатором) не укључује исправну информацију у предвиђеном облику",
|
||||
"error-import-empty-board": "This export contains no board content (no swimlanes, lists or cards). It may have been created by an older WeKan version with a known export bug. Please re-export the board with the current version and import the new file.",
|
||||
"error-list-doesNotExist": "Този списък не съществува",
|
||||
"error-linked-card-not-allowed": "You can only link to a normal card. Linking to another linked card, or to a card that links back to this board, is not allowed because it would make the cards inaccessible.",
|
||||
"error-user-disabled": "This user account is disabled",
|
||||
"error-user-doesNotExist": "Този потребител не съществува",
|
||||
"error-user-notAllowSelf": "Не можете да поканите себе си",
|
||||
@@ -581,6 +584,7 @@
|
||||
"fullname": "Име",
|
||||
"header-logo-title": "Назад към страницата с Вашите табла.",
|
||||
"show-activities": "Писани записник",
|
||||
"hide-activities": "Hide Activities",
|
||||
"headerBarCreateBoardPopup-title": "Създай Табло",
|
||||
"home": "Начало",
|
||||
"import": "Импорт",
|
||||
@@ -894,7 +898,12 @@
|
||||
"no-name": "(Непознат)",
|
||||
"Node_version": "Версия на Node",
|
||||
"Meteor_version": "Версия на Meteor",
|
||||
"MongoDB_version": "Версия на MongoDB",
|
||||
"Database_type": "Database type",
|
||||
"Database_commit": "Database commit",
|
||||
"FerretDB_version": "FerretDB version",
|
||||
"FerretDB_commit": "FerretDB commit",
|
||||
"Reactivity_mode": "Reactivity mode (changeStreams / oplog / polling)",
|
||||
"MongoDB_version": "MongoDB compatible version",
|
||||
"MongoDB_storage_engine": "MongoDB енджин за данни",
|
||||
"MongoDB_Oplog_enabled": "MongoDB Oplog включен",
|
||||
"OS_Arch": "Архитектура на ОС",
|
||||
@@ -1015,6 +1024,17 @@
|
||||
"r-workflow-help": "Drag a trigger and an action into the builder, then add the rule. Drag an action onto an existing rule to change what it does.",
|
||||
"r-drop-trigger": "drop a trigger here",
|
||||
"r-drop-action": "drop an action here",
|
||||
"r-w-card-created": "Card is created",
|
||||
"r-w-card-archived": "Card is archived",
|
||||
"r-w-card-unarchived": "Card is unarchived",
|
||||
"r-w-label-added": "Any label is added",
|
||||
"r-w-label-removed": "Any label is removed",
|
||||
"r-w-member-added": "A member is added",
|
||||
"r-w-member-removed": "A member is removed",
|
||||
"r-w-checklist-added": "A checklist is added",
|
||||
"r-w-attachment-added": "An attachment is added",
|
||||
"r-w-every-day-at": "Every day at __time__",
|
||||
"r-w-set-received-now": "Set received date to now",
|
||||
"r-export-json": "Export rules to JSON",
|
||||
"r-export-csv": "Export rules to CSV",
|
||||
"r-import-json": "Import rules from JSON",
|
||||
@@ -1068,6 +1088,12 @@
|
||||
"r-mark-incomplete": "Mark card incomplete",
|
||||
"r-move-all-cards": "Move all cards in list",
|
||||
"r-set-date-relative": "Set date relative to now",
|
||||
"r-later": "later",
|
||||
"r-unit-minutes": "minute(s)",
|
||||
"r-unit-hours": "hour(s)",
|
||||
"r-unit-days": "day(s)",
|
||||
"r-unit-weeks": "week(s)",
|
||||
"r-unit-months": "month(s)",
|
||||
"r-trigger": "Добави спусък",
|
||||
"r-action": "Действие",
|
||||
"r-when-a-card": "Когато карта",
|
||||
@@ -1809,6 +1835,45 @@
|
||||
"azure-container": "Container",
|
||||
"gcs-storage": "Google Cloud Storage",
|
||||
"gcs-storage-description": "Google Cloud Storage.",
|
||||
"database-migration": "Database migration",
|
||||
"database-migration-description": "Migrate text-based data (everything except attachments and avatars, which stay on the filesystem) between MongoDB and FerretDB v1 (SQLite). The other database must be running and reachable (default ports: FerretDB mongodb://127.0.0.1:27018, MongoDB mongodb://127.0.0.1:27019; override with WEKAN_FERRETDB_URL / WEKAN_MONGODB_URL). After it finishes, point MONGO_URL at the other database and restart WeKan (on Snap: snap set wekan database=ferretdb or =mongodb).",
|
||||
"database-migrate-to-ferretdb": "Migrate text data to FerretDB v1 (SQLite)",
|
||||
"database-migrate-to-mongodb": "Migrate text data to MongoDB",
|
||||
"database-migration-confirm": "Copy all text-based collections to __db__? Attachments and avatars on the filesystem are not affected. The target database must be running.",
|
||||
"database-migration-phase": "Phase",
|
||||
"database-migration-done": "Text-data migration completed.",
|
||||
"collections": "collections",
|
||||
"features": "Features",
|
||||
"features-performance": "Performance",
|
||||
"cards-loading": "Card loading",
|
||||
"cards-loading-all": "All cards (default)",
|
||||
"cards-loading-lazy": "Lazy (only visible cards)",
|
||||
"cards-loading-description": "How each board loads its cards. All cards: every card is loaded into the browser (simplest, all features work, but a board with thousands of cards sends a lot of data to each viewer). Lazy: each list loads only the cards currently visible (infinite scroll) plus a live count, so very large boards use less memory and bandwidth. Also set by the CARDS_LOADING environment variable.",
|
||||
"cards-loading-lazy-note": "Lazy card loading is experimental: card counters and WIP limits are accurate, but the Calendar/Table/Gantt views and multi-select reflect only the cards loaded so far. Reload open boards after changing this.",
|
||||
"backup": "Backup",
|
||||
"backup-description": "Back up Attachments, Avatars and/or Data (all text-based collections that are not attachments or avatars). The .zip is streamed directly to the selected storage — no temporary file — as backup/YYYY/MM/DD/HH_MM_SS/backup.zip, containing YYYY_MM_DD-HH_MM_SS/attachments, /avatars and /data. Cloud storages (S3/MinIO, Azure, GCS) use the credentials configured in their storage tabs.",
|
||||
"backup-data": "Data (text)",
|
||||
"backup-storage": "Save to storage",
|
||||
"backup-now": "Backup now",
|
||||
"backup-done": "Backup completed",
|
||||
"backup-schedule": "Scheduled backups",
|
||||
"backup-frequency": "Frequency",
|
||||
"backup-frequency-off": "Off",
|
||||
"backup-frequency-daily": "Daily",
|
||||
"backup-frequency-weekly": "Weekly",
|
||||
"backup-frequency-monthly": "Monthly",
|
||||
"backup-time": "Time (HH:MM)",
|
||||
"backup-day-of-week": "Day of week (weekly)",
|
||||
"backup-day-of-month": "Day of month (monthly, 1-28)",
|
||||
"backup-restore": "Възстанови",
|
||||
"backup-list": "List backups",
|
||||
"backup-datetime": "Date and time",
|
||||
"backup-path": "File path",
|
||||
"backup-restore-mode": "Restore mode",
|
||||
"backup-restore-add-missing": "Add missing data only",
|
||||
"backup-restore-replace-all": "Replace all data",
|
||||
"backup-restore-select-first": "Select a backup from the list first.",
|
||||
"backup-restore-confirm": "Restore the selected backup? Attachments/avatars are written to the filesystem and data to the database according to the chosen mode.",
|
||||
"gcs-project-id": "Project ID",
|
||||
"gcs-bucket": "Bucket",
|
||||
"gcs-key-filename": "Service account key file path",
|
||||
@@ -1986,8 +2051,6 @@
|
||||
"cpu-cores": "Процесорска језгра",
|
||||
"cpu-usage": "Заузеће процесора",
|
||||
"current-action": "Текућа радња",
|
||||
"database-migration": "Обнова базе података",
|
||||
"database-migration-description": "Преслажем структуру базе података да би била боља и бржа. Процес може потрајати неколико минута.",
|
||||
"database-migrations": "Преслагање базе података",
|
||||
"days-old": "дана стар",
|
||||
"duration": "Трајање",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accept": "Asantiñ",
|
||||
"activity-changedTitle": "changed title to %s of %s",
|
||||
"activity-changedDescription": "changed description of %s",
|
||||
"act-deleteCard": "deleted card __card__ from list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-removeBoard": "deleted board __board__",
|
||||
"act-removeList": "deleted list __list__ at board __board__",
|
||||
@@ -441,6 +442,7 @@
|
||||
"createCustomField": "Créer un champ personnalisé",
|
||||
"createCustomFieldPopup-title": "Créer un champ personnalisé",
|
||||
"current": "actuel",
|
||||
"custom-color": "Custom color",
|
||||
"custom-field-delete-pop": "Cette action est irréversible. Elle supprimera ce champ personnalisé de toutes les cartes et détruira son historique.",
|
||||
"custom-field-checkbox": "Case à cocher",
|
||||
"custom-field-currency": "Devise",
|
||||
@@ -508,6 +510,7 @@
|
||||
"error-csv-schema": "Votre fichier CSV (valeurs séparées par des virgules) / TSV (valeurs séparées par des tabulations) ne contient pas d'informations au bon format",
|
||||
"error-import-empty-board": "This export contains no board content (no swimlanes, lists or cards). It may have been created by an older WeKan version with a known export bug. Please re-export the board with the current version and import the new file.",
|
||||
"error-list-doesNotExist": "Cette liste n'existe pas",
|
||||
"error-linked-card-not-allowed": "You can only link to a normal card. Linking to another linked card, or to a card that links back to this board, is not allowed because it would make the cards inaccessible.",
|
||||
"error-user-disabled": "This user account is disabled",
|
||||
"error-user-doesNotExist": "Cet utilisateur n'existe pas",
|
||||
"error-user-notAllowSelf": "Vous ne pouvez pas vous inviter vous-même",
|
||||
@@ -581,6 +584,7 @@
|
||||
"fullname": "Nom complet",
|
||||
"header-logo-title": "Retourner à la page des tableaux",
|
||||
"show-activities": "Afficher les activités",
|
||||
"hide-activities": "Hide Activities",
|
||||
"headerBarCreateBoardPopup-title": "Créer un tableau",
|
||||
"home": "Accueil",
|
||||
"import": "Importer",
|
||||
@@ -894,7 +898,12 @@
|
||||
"no-name": "(Inconnu)",
|
||||
"Node_version": "Version de Node",
|
||||
"Meteor_version": "Version de Meteor",
|
||||
"MongoDB_version": "Version de MongoDB",
|
||||
"Database_type": "Database type",
|
||||
"Database_commit": "Database commit",
|
||||
"FerretDB_version": "FerretDB version",
|
||||
"FerretDB_commit": "FerretDB commit",
|
||||
"Reactivity_mode": "Reactivity mode (changeStreams / oplog / polling)",
|
||||
"MongoDB_version": "MongoDB compatible version",
|
||||
"MongoDB_storage_engine": "Moteur de stockage MongoDB",
|
||||
"MongoDB_Oplog_enabled": "MongoDB Oplog activé",
|
||||
"OS_Arch": "OS Architecture",
|
||||
@@ -1015,6 +1024,17 @@
|
||||
"r-workflow-help": "Drag a trigger and an action into the builder, then add the rule. Drag an action onto an existing rule to change what it does.",
|
||||
"r-drop-trigger": "drop a trigger here",
|
||||
"r-drop-action": "drop an action here",
|
||||
"r-w-card-created": "Card is created",
|
||||
"r-w-card-archived": "Card is archived",
|
||||
"r-w-card-unarchived": "Card is unarchived",
|
||||
"r-w-label-added": "Any label is added",
|
||||
"r-w-label-removed": "Any label is removed",
|
||||
"r-w-member-added": "A member is added",
|
||||
"r-w-member-removed": "A member is removed",
|
||||
"r-w-checklist-added": "A checklist is added",
|
||||
"r-w-attachment-added": "An attachment is added",
|
||||
"r-w-every-day-at": "Every day at __time__",
|
||||
"r-w-set-received-now": "Set received date to now",
|
||||
"r-export-json": "Export rules to JSON",
|
||||
"r-export-csv": "Export rules to CSV",
|
||||
"r-import-json": "Import rules from JSON",
|
||||
@@ -1068,6 +1088,12 @@
|
||||
"r-mark-incomplete": "Mark card incomplete",
|
||||
"r-move-all-cards": "Move all cards in list",
|
||||
"r-set-date-relative": "Set date relative to now",
|
||||
"r-later": "later",
|
||||
"r-unit-minutes": "minute(s)",
|
||||
"r-unit-hours": "hour(s)",
|
||||
"r-unit-days": "day(s)",
|
||||
"r-unit-weeks": "week(s)",
|
||||
"r-unit-months": "month(s)",
|
||||
"r-trigger": "Déclencheur",
|
||||
"r-action": "Action",
|
||||
"r-when-a-card": "Quand une carte",
|
||||
@@ -1809,6 +1835,45 @@
|
||||
"azure-container": "Container",
|
||||
"gcs-storage": "Google Cloud Storage",
|
||||
"gcs-storage-description": "Google Cloud Storage.",
|
||||
"database-migration": "Database migration",
|
||||
"database-migration-description": "Migrate text-based data (everything except attachments and avatars, which stay on the filesystem) between MongoDB and FerretDB v1 (SQLite). The other database must be running and reachable (default ports: FerretDB mongodb://127.0.0.1:27018, MongoDB mongodb://127.0.0.1:27019; override with WEKAN_FERRETDB_URL / WEKAN_MONGODB_URL). After it finishes, point MONGO_URL at the other database and restart WeKan (on Snap: snap set wekan database=ferretdb or =mongodb).",
|
||||
"database-migrate-to-ferretdb": "Migrate text data to FerretDB v1 (SQLite)",
|
||||
"database-migrate-to-mongodb": "Migrate text data to MongoDB",
|
||||
"database-migration-confirm": "Copy all text-based collections to __db__? Attachments and avatars on the filesystem are not affected. The target database must be running.",
|
||||
"database-migration-phase": "Phase",
|
||||
"database-migration-done": "Text-data migration completed.",
|
||||
"collections": "collections",
|
||||
"features": "Features",
|
||||
"features-performance": "Performance",
|
||||
"cards-loading": "Card loading",
|
||||
"cards-loading-all": "All cards (default)",
|
||||
"cards-loading-lazy": "Lazy (only visible cards)",
|
||||
"cards-loading-description": "How each board loads its cards. All cards: every card is loaded into the browser (simplest, all features work, but a board with thousands of cards sends a lot of data to each viewer). Lazy: each list loads only the cards currently visible (infinite scroll) plus a live count, so very large boards use less memory and bandwidth. Also set by the CARDS_LOADING environment variable.",
|
||||
"cards-loading-lazy-note": "Lazy card loading is experimental: card counters and WIP limits are accurate, but the Calendar/Table/Gantt views and multi-select reflect only the cards loaded so far. Reload open boards after changing this.",
|
||||
"backup": "Backup",
|
||||
"backup-description": "Back up Attachments, Avatars and/or Data (all text-based collections that are not attachments or avatars). The .zip is streamed directly to the selected storage — no temporary file — as backup/YYYY/MM/DD/HH_MM_SS/backup.zip, containing YYYY_MM_DD-HH_MM_SS/attachments, /avatars and /data. Cloud storages (S3/MinIO, Azure, GCS) use the credentials configured in their storage tabs.",
|
||||
"backup-data": "Data (text)",
|
||||
"backup-storage": "Save to storage",
|
||||
"backup-now": "Backup now",
|
||||
"backup-done": "Backup completed",
|
||||
"backup-schedule": "Scheduled backups",
|
||||
"backup-frequency": "Frequency",
|
||||
"backup-frequency-off": "Off",
|
||||
"backup-frequency-daily": "Daily",
|
||||
"backup-frequency-weekly": "Weekly",
|
||||
"backup-frequency-monthly": "Monthly",
|
||||
"backup-time": "Time (HH:MM)",
|
||||
"backup-day-of-week": "Day of week (weekly)",
|
||||
"backup-day-of-month": "Day of month (monthly, 1-28)",
|
||||
"backup-restore": "Restaurer",
|
||||
"backup-list": "List backups",
|
||||
"backup-datetime": "Date and time",
|
||||
"backup-path": "File path",
|
||||
"backup-restore-mode": "Restore mode",
|
||||
"backup-restore-add-missing": "Add missing data only",
|
||||
"backup-restore-replace-all": "Replace all data",
|
||||
"backup-restore-select-first": "Select a backup from the list first.",
|
||||
"backup-restore-confirm": "Restore the selected backup? Attachments/avatars are written to the filesystem and data to the database according to the chosen mode.",
|
||||
"gcs-project-id": "Project ID",
|
||||
"gcs-bucket": "Bucket",
|
||||
"gcs-key-filename": "Service account key file path",
|
||||
@@ -1986,8 +2051,6 @@
|
||||
"cpu-cores": "Cœurs du CPU ",
|
||||
"cpu-usage": "Utilisation du CPU",
|
||||
"current-action": "Current Action",
|
||||
"database-migration": "Migration base de données",
|
||||
"database-migration-description": "Updating database structure for improved functionality and performance. This process may take several minutes.",
|
||||
"database-migrations": "Migrations base de données",
|
||||
"days-old": "Jours d'ancienneté",
|
||||
"duration": "Durée",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accept": "Accepta",
|
||||
"activity-changedTitle": "changed title to %s of %s",
|
||||
"activity-changedDescription": "changed description of %s",
|
||||
"act-deleteCard": "deleted card __card__ from list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-removeBoard": "deleted board __board__",
|
||||
"act-removeList": "deleted list __list__ at board __board__",
|
||||
@@ -441,6 +442,7 @@
|
||||
"createCustomField": "Crear campament",
|
||||
"createCustomFieldPopup-title": "Crear campament",
|
||||
"current": "Real",
|
||||
"custom-color": "Custom color",
|
||||
"custom-field-delete-pop": "No hi ha cap desfer. Això eliminarà aquest camp personalitzat de totes les cartes i destruirà el seu historial.",
|
||||
"custom-field-checkbox": "casella de selecció",
|
||||
"custom-field-currency": "Moneda",
|
||||
@@ -508,6 +510,7 @@
|
||||
"error-csv-schema": "El teu CSV no inclou la informació en el format correcte.",
|
||||
"error-import-empty-board": "This export contains no board content (no swimlanes, lists or cards). It may have been created by an older WeKan version with a known export bug. Please re-export the board with the current version and import the new file.",
|
||||
"error-list-doesNotExist": "La llista no existeix",
|
||||
"error-linked-card-not-allowed": "You can only link to a normal card. Linking to another linked card, or to a card that links back to this board, is not allowed because it would make the cards inaccessible.",
|
||||
"error-user-disabled": "This user account is disabled",
|
||||
"error-user-doesNotExist": "L'usuari no existeix",
|
||||
"error-user-notAllowSelf": "No et pots convidar a la teva matriu",
|
||||
@@ -581,6 +584,7 @@
|
||||
"fullname": "Nom complet",
|
||||
"header-logo-title": "Torna a la teva pàgina de taulers",
|
||||
"show-activities": "Show Activities",
|
||||
"hide-activities": "Hide Activities",
|
||||
"headerBarCreateBoardPopup-title": "Crea tauler",
|
||||
"home": "Inici",
|
||||
"import": "importa",
|
||||
@@ -894,7 +898,12 @@
|
||||
"no-name": "Importa tauler des de Wekan",
|
||||
"Node_version": "Versió Node",
|
||||
"Meteor_version": "Versió Meteor",
|
||||
"MongoDB_version": "Versió MongoDB",
|
||||
"Database_type": "Database type",
|
||||
"Database_commit": "Database commit",
|
||||
"FerretDB_version": "FerretDB version",
|
||||
"FerretDB_commit": "FerretDB commit",
|
||||
"Reactivity_mode": "Reactivity mode (changeStreams / oplog / polling)",
|
||||
"MongoDB_version": "MongoDB compatible version",
|
||||
"MongoDB_storage_engine": "Motor d'emmagatzematge MongoDB",
|
||||
"MongoDB_Oplog_enabled": "MongoDB Oplog habilitat",
|
||||
"OS_Arch": "Arquitectura SO",
|
||||
@@ -1015,6 +1024,17 @@
|
||||
"r-workflow-help": "Drag a trigger and an action into the builder, then add the rule. Drag an action onto an existing rule to change what it does.",
|
||||
"r-drop-trigger": "drop a trigger here",
|
||||
"r-drop-action": "drop an action here",
|
||||
"r-w-card-created": "Card is created",
|
||||
"r-w-card-archived": "Card is archived",
|
||||
"r-w-card-unarchived": "Card is unarchived",
|
||||
"r-w-label-added": "Any label is added",
|
||||
"r-w-label-removed": "Any label is removed",
|
||||
"r-w-member-added": "A member is added",
|
||||
"r-w-member-removed": "A member is removed",
|
||||
"r-w-checklist-added": "A checklist is added",
|
||||
"r-w-attachment-added": "An attachment is added",
|
||||
"r-w-every-day-at": "Every day at __time__",
|
||||
"r-w-set-received-now": "Set received date to now",
|
||||
"r-export-json": "Export rules to JSON",
|
||||
"r-export-csv": "Export rules to CSV",
|
||||
"r-import-json": "Import rules from JSON",
|
||||
@@ -1068,6 +1088,12 @@
|
||||
"r-mark-incomplete": "Mark card incomplete",
|
||||
"r-move-all-cards": "Move all cards in list",
|
||||
"r-set-date-relative": "Set date relative to now",
|
||||
"r-later": "later",
|
||||
"r-unit-minutes": "minute(s)",
|
||||
"r-unit-hours": "hour(s)",
|
||||
"r-unit-days": "day(s)",
|
||||
"r-unit-weeks": "week(s)",
|
||||
"r-unit-months": "month(s)",
|
||||
"r-trigger": "Disparador",
|
||||
"r-action": "Acció",
|
||||
"r-when-a-card": "Quan una fitxa",
|
||||
@@ -1809,6 +1835,45 @@
|
||||
"azure-container": "Container",
|
||||
"gcs-storage": "Google Cloud Storage",
|
||||
"gcs-storage-description": "Google Cloud Storage.",
|
||||
"database-migration": "Database migration",
|
||||
"database-migration-description": "Migrate text-based data (everything except attachments and avatars, which stay on the filesystem) between MongoDB and FerretDB v1 (SQLite). The other database must be running and reachable (default ports: FerretDB mongodb://127.0.0.1:27018, MongoDB mongodb://127.0.0.1:27019; override with WEKAN_FERRETDB_URL / WEKAN_MONGODB_URL). After it finishes, point MONGO_URL at the other database and restart WeKan (on Snap: snap set wekan database=ferretdb or =mongodb).",
|
||||
"database-migrate-to-ferretdb": "Migrate text data to FerretDB v1 (SQLite)",
|
||||
"database-migrate-to-mongodb": "Migrate text data to MongoDB",
|
||||
"database-migration-confirm": "Copy all text-based collections to __db__? Attachments and avatars on the filesystem are not affected. The target database must be running.",
|
||||
"database-migration-phase": "Phase",
|
||||
"database-migration-done": "Text-data migration completed.",
|
||||
"collections": "collections",
|
||||
"features": "Features",
|
||||
"features-performance": "Performance",
|
||||
"cards-loading": "Card loading",
|
||||
"cards-loading-all": "All cards (default)",
|
||||
"cards-loading-lazy": "Lazy (only visible cards)",
|
||||
"cards-loading-description": "How each board loads its cards. All cards: every card is loaded into the browser (simplest, all features work, but a board with thousands of cards sends a lot of data to each viewer). Lazy: each list loads only the cards currently visible (infinite scroll) plus a live count, so very large boards use less memory and bandwidth. Also set by the CARDS_LOADING environment variable.",
|
||||
"cards-loading-lazy-note": "Lazy card loading is experimental: card counters and WIP limits are accurate, but the Calendar/Table/Gantt views and multi-select reflect only the cards loaded so far. Reload open boards after changing this.",
|
||||
"backup": "Backup",
|
||||
"backup-description": "Back up Attachments, Avatars and/or Data (all text-based collections that are not attachments or avatars). The .zip is streamed directly to the selected storage — no temporary file — as backup/YYYY/MM/DD/HH_MM_SS/backup.zip, containing YYYY_MM_DD-HH_MM_SS/attachments, /avatars and /data. Cloud storages (S3/MinIO, Azure, GCS) use the credentials configured in their storage tabs.",
|
||||
"backup-data": "Data (text)",
|
||||
"backup-storage": "Save to storage",
|
||||
"backup-now": "Backup now",
|
||||
"backup-done": "Backup completed",
|
||||
"backup-schedule": "Scheduled backups",
|
||||
"backup-frequency": "Frequency",
|
||||
"backup-frequency-off": "Off",
|
||||
"backup-frequency-daily": "Daily",
|
||||
"backup-frequency-weekly": "Weekly",
|
||||
"backup-frequency-monthly": "Monthly",
|
||||
"backup-time": "Time (HH:MM)",
|
||||
"backup-day-of-week": "Day of week (weekly)",
|
||||
"backup-day-of-month": "Day of month (monthly, 1-28)",
|
||||
"backup-restore": "Restaura",
|
||||
"backup-list": "List backups",
|
||||
"backup-datetime": "Date and time",
|
||||
"backup-path": "File path",
|
||||
"backup-restore-mode": "Restore mode",
|
||||
"backup-restore-add-missing": "Add missing data only",
|
||||
"backup-restore-replace-all": "Replace all data",
|
||||
"backup-restore-select-first": "Select a backup from the list first.",
|
||||
"backup-restore-confirm": "Restore the selected backup? Attachments/avatars are written to the filesystem and data to the database according to the chosen mode.",
|
||||
"gcs-project-id": "Project ID",
|
||||
"gcs-bucket": "Bucket",
|
||||
"gcs-key-filename": "Service account key file path",
|
||||
@@ -1986,8 +2051,6 @@
|
||||
"cpu-cores": "CPU Cores",
|
||||
"cpu-usage": "CPU Usage",
|
||||
"current-action": "Current Action",
|
||||
"database-migration": "Database Migration",
|
||||
"database-migration-description": "Updating database structure for improved functionality and performance. This process may take several minutes.",
|
||||
"database-migrations": "Database Migrations",
|
||||
"days-old": "Days Old",
|
||||
"duration": "Duration",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accept": "Acceptar",
|
||||
"activity-changedTitle": "changed title to %s of %s",
|
||||
"activity-changedDescription": "changed description of %s",
|
||||
"act-deleteCard": "deleted card __card__ from list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-removeBoard": "deleted board __board__",
|
||||
"act-removeList": "deleted list __list__ at board __board__",
|
||||
@@ -441,6 +442,7 @@
|
||||
"createCustomField": "Crear campament",
|
||||
"createCustomFieldPopup-title": "Crear campament",
|
||||
"current": "Real",
|
||||
"custom-color": "Custom color",
|
||||
"custom-field-delete-pop": "No hi ha cap desfer. Això eliminarà aquest camp personalitzat de totes les cartes i destruirà el seu historial.",
|
||||
"custom-field-checkbox": "casella de selecció",
|
||||
"custom-field-currency": "Moneda",
|
||||
@@ -508,6 +510,7 @@
|
||||
"error-csv-schema": "El teu CSV no inclou la informació en el format correcte.",
|
||||
"error-import-empty-board": "This export contains no board content (no swimlanes, lists or cards). It may have been created by an older WeKan version with a known export bug. Please re-export the board with the current version and import the new file.",
|
||||
"error-list-doesNotExist": "La llista no existeix",
|
||||
"error-linked-card-not-allowed": "You can only link to a normal card. Linking to another linked card, or to a card that links back to this board, is not allowed because it would make the cards inaccessible.",
|
||||
"error-user-disabled": "This user account is disabled",
|
||||
"error-user-doesNotExist": "L'usuari no existeix",
|
||||
"error-user-notAllowSelf": "No et pots convidar a la teva matriu",
|
||||
@@ -581,6 +584,7 @@
|
||||
"fullname": "Nom complet",
|
||||
"header-logo-title": "Torna a la teva pàgina de taulers",
|
||||
"show-activities": "Mostrar actividades",
|
||||
"hide-activities": "Hide Activities",
|
||||
"headerBarCreateBoardPopup-title": "Crea tauler",
|
||||
"home": "Inici",
|
||||
"import": "importa",
|
||||
@@ -894,7 +898,12 @@
|
||||
"no-name": "Importa tauler des de Wekan",
|
||||
"Node_version": "Versió Node",
|
||||
"Meteor_version": "Versió Meteor",
|
||||
"MongoDB_version": "Versió MongoDB",
|
||||
"Database_type": "Database type",
|
||||
"Database_commit": "Database commit",
|
||||
"FerretDB_version": "FerretDB version",
|
||||
"FerretDB_commit": "FerretDB commit",
|
||||
"Reactivity_mode": "Reactivity mode (changeStreams / oplog / polling)",
|
||||
"MongoDB_version": "MongoDB compatible version",
|
||||
"MongoDB_storage_engine": "Motor d'emmagatzematge MongoDB",
|
||||
"MongoDB_Oplog_enabled": "MongoDB Oplog habilitat",
|
||||
"OS_Arch": "Arquitectura SO",
|
||||
@@ -1015,6 +1024,17 @@
|
||||
"r-workflow-help": "Drag a trigger and an action into the builder, then add the rule. Drag an action onto an existing rule to change what it does.",
|
||||
"r-drop-trigger": "drop a trigger here",
|
||||
"r-drop-action": "drop an action here",
|
||||
"r-w-card-created": "Card is created",
|
||||
"r-w-card-archived": "Card is archived",
|
||||
"r-w-card-unarchived": "Card is unarchived",
|
||||
"r-w-label-added": "Any label is added",
|
||||
"r-w-label-removed": "Any label is removed",
|
||||
"r-w-member-added": "A member is added",
|
||||
"r-w-member-removed": "A member is removed",
|
||||
"r-w-checklist-added": "A checklist is added",
|
||||
"r-w-attachment-added": "An attachment is added",
|
||||
"r-w-every-day-at": "Every day at __time__",
|
||||
"r-w-set-received-now": "Set received date to now",
|
||||
"r-export-json": "Export rules to JSON",
|
||||
"r-export-csv": "Export rules to CSV",
|
||||
"r-import-json": "Import rules from JSON",
|
||||
@@ -1068,6 +1088,12 @@
|
||||
"r-mark-incomplete": "Mark card incomplete",
|
||||
"r-move-all-cards": "Move all cards in list",
|
||||
"r-set-date-relative": "Set date relative to now",
|
||||
"r-later": "later",
|
||||
"r-unit-minutes": "minute(s)",
|
||||
"r-unit-hours": "hour(s)",
|
||||
"r-unit-days": "day(s)",
|
||||
"r-unit-weeks": "week(s)",
|
||||
"r-unit-months": "month(s)",
|
||||
"r-trigger": "Disparador",
|
||||
"r-action": "Acció",
|
||||
"r-when-a-card": "Quan una fitxa",
|
||||
@@ -1809,6 +1835,45 @@
|
||||
"azure-container": "Container",
|
||||
"gcs-storage": "Google Cloud Storage",
|
||||
"gcs-storage-description": "Google Cloud Storage.",
|
||||
"database-migration": "Database migration",
|
||||
"database-migration-description": "Migrate text-based data (everything except attachments and avatars, which stay on the filesystem) between MongoDB and FerretDB v1 (SQLite). The other database must be running and reachable (default ports: FerretDB mongodb://127.0.0.1:27018, MongoDB mongodb://127.0.0.1:27019; override with WEKAN_FERRETDB_URL / WEKAN_MONGODB_URL). After it finishes, point MONGO_URL at the other database and restart WeKan (on Snap: snap set wekan database=ferretdb or =mongodb).",
|
||||
"database-migrate-to-ferretdb": "Migrate text data to FerretDB v1 (SQLite)",
|
||||
"database-migrate-to-mongodb": "Migrate text data to MongoDB",
|
||||
"database-migration-confirm": "Copy all text-based collections to __db__? Attachments and avatars on the filesystem are not affected. The target database must be running.",
|
||||
"database-migration-phase": "Phase",
|
||||
"database-migration-done": "Text-data migration completed.",
|
||||
"collections": "collections",
|
||||
"features": "Features",
|
||||
"features-performance": "Performance",
|
||||
"cards-loading": "Card loading",
|
||||
"cards-loading-all": "All cards (default)",
|
||||
"cards-loading-lazy": "Lazy (only visible cards)",
|
||||
"cards-loading-description": "How each board loads its cards. All cards: every card is loaded into the browser (simplest, all features work, but a board with thousands of cards sends a lot of data to each viewer). Lazy: each list loads only the cards currently visible (infinite scroll) plus a live count, so very large boards use less memory and bandwidth. Also set by the CARDS_LOADING environment variable.",
|
||||
"cards-loading-lazy-note": "Lazy card loading is experimental: card counters and WIP limits are accurate, but the Calendar/Table/Gantt views and multi-select reflect only the cards loaded so far. Reload open boards after changing this.",
|
||||
"backup": "Backup",
|
||||
"backup-description": "Back up Attachments, Avatars and/or Data (all text-based collections that are not attachments or avatars). The .zip is streamed directly to the selected storage — no temporary file — as backup/YYYY/MM/DD/HH_MM_SS/backup.zip, containing YYYY_MM_DD-HH_MM_SS/attachments, /avatars and /data. Cloud storages (S3/MinIO, Azure, GCS) use the credentials configured in their storage tabs.",
|
||||
"backup-data": "Data (text)",
|
||||
"backup-storage": "Save to storage",
|
||||
"backup-now": "Backup now",
|
||||
"backup-done": "Backup completed",
|
||||
"backup-schedule": "Scheduled backups",
|
||||
"backup-frequency": "Frequency",
|
||||
"backup-frequency-off": "Off",
|
||||
"backup-frequency-daily": "Daily",
|
||||
"backup-frequency-weekly": "Weekly",
|
||||
"backup-frequency-monthly": "Monthly",
|
||||
"backup-time": "Time (HH:MM)",
|
||||
"backup-day-of-week": "Day of week (weekly)",
|
||||
"backup-day-of-month": "Day of month (monthly, 1-28)",
|
||||
"backup-restore": "Restaura",
|
||||
"backup-list": "List backups",
|
||||
"backup-datetime": "Date and time",
|
||||
"backup-path": "File path",
|
||||
"backup-restore-mode": "Restore mode",
|
||||
"backup-restore-add-missing": "Add missing data only",
|
||||
"backup-restore-replace-all": "Replace all data",
|
||||
"backup-restore-select-first": "Select a backup from the list first.",
|
||||
"backup-restore-confirm": "Restore the selected backup? Attachments/avatars are written to the filesystem and data to the database according to the chosen mode.",
|
||||
"gcs-project-id": "Project ID",
|
||||
"gcs-bucket": "Bucket",
|
||||
"gcs-key-filename": "Service account key file path",
|
||||
@@ -1986,8 +2051,6 @@
|
||||
"cpu-cores": "CPU Cores",
|
||||
"cpu-usage": "CPU Usage",
|
||||
"current-action": "Current Action",
|
||||
"database-migration": "Database Migration",
|
||||
"database-migration-description": "Updating database structure for improved functionality and performance. This process may take several minutes.",
|
||||
"database-migrations": "Database Migrations",
|
||||
"days-old": "Days Old",
|
||||
"duration": "Duración",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accept": "Accepta",
|
||||
"activity-changedTitle": "changed title to %s of %s",
|
||||
"activity-changedDescription": "changed description of %s",
|
||||
"act-deleteCard": "deleted card __card__ from list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-removeBoard": "deleted board __board__",
|
||||
"act-removeList": "deleted list __list__ at board __board__",
|
||||
@@ -441,6 +442,7 @@
|
||||
"createCustomField": "Crear campament",
|
||||
"createCustomFieldPopup-title": "Crear campament",
|
||||
"current": "Real",
|
||||
"custom-color": "Custom color",
|
||||
"custom-field-delete-pop": "No hi ha cap desfer. Això eliminarà aquest camp personalitzat de totes les cartes i destruirà el seu historial.",
|
||||
"custom-field-checkbox": "casella de selecció",
|
||||
"custom-field-currency": "Moneda",
|
||||
@@ -508,6 +510,7 @@
|
||||
"error-csv-schema": "El teu CSV no inclou la informació en el format correcte.",
|
||||
"error-import-empty-board": "This export contains no board content (no swimlanes, lists or cards). It may have been created by an older WeKan version with a known export bug. Please re-export the board with the current version and import the new file.",
|
||||
"error-list-doesNotExist": "La llista no existeix",
|
||||
"error-linked-card-not-allowed": "You can only link to a normal card. Linking to another linked card, or to a card that links back to this board, is not allowed because it would make the cards inaccessible.",
|
||||
"error-user-disabled": "This user account is disabled",
|
||||
"error-user-doesNotExist": "L'usuari no existeix",
|
||||
"error-user-notAllowSelf": "No et pots convidar a la teva matriu",
|
||||
@@ -581,6 +584,7 @@
|
||||
"fullname": "Nom complet",
|
||||
"header-logo-title": "Torna a la teva pàgina de taulers",
|
||||
"show-activities": "Show Activities",
|
||||
"hide-activities": "Hide Activities",
|
||||
"headerBarCreateBoardPopup-title": "Crea tauler",
|
||||
"home": "Inici",
|
||||
"import": "importa",
|
||||
@@ -894,7 +898,12 @@
|
||||
"no-name": "Importa tauler des de Wekan",
|
||||
"Node_version": "Versió Node",
|
||||
"Meteor_version": "Versió Meteor",
|
||||
"MongoDB_version": "Versió MongoDB",
|
||||
"Database_type": "Database type",
|
||||
"Database_commit": "Database commit",
|
||||
"FerretDB_version": "FerretDB version",
|
||||
"FerretDB_commit": "FerretDB commit",
|
||||
"Reactivity_mode": "Reactivity mode (changeStreams / oplog / polling)",
|
||||
"MongoDB_version": "MongoDB compatible version",
|
||||
"MongoDB_storage_engine": "Motor d'emmagatzematge MongoDB",
|
||||
"MongoDB_Oplog_enabled": "MongoDB Oplog habilitat",
|
||||
"OS_Arch": "Arquitectura SO",
|
||||
@@ -1015,6 +1024,17 @@
|
||||
"r-workflow-help": "Drag a trigger and an action into the builder, then add the rule. Drag an action onto an existing rule to change what it does.",
|
||||
"r-drop-trigger": "drop a trigger here",
|
||||
"r-drop-action": "drop an action here",
|
||||
"r-w-card-created": "Card is created",
|
||||
"r-w-card-archived": "Card is archived",
|
||||
"r-w-card-unarchived": "Card is unarchived",
|
||||
"r-w-label-added": "Any label is added",
|
||||
"r-w-label-removed": "Any label is removed",
|
||||
"r-w-member-added": "A member is added",
|
||||
"r-w-member-removed": "A member is removed",
|
||||
"r-w-checklist-added": "A checklist is added",
|
||||
"r-w-attachment-added": "An attachment is added",
|
||||
"r-w-every-day-at": "Every day at __time__",
|
||||
"r-w-set-received-now": "Set received date to now",
|
||||
"r-export-json": "Export rules to JSON",
|
||||
"r-export-csv": "Export rules to CSV",
|
||||
"r-import-json": "Import rules from JSON",
|
||||
@@ -1068,6 +1088,12 @@
|
||||
"r-mark-incomplete": "Mark card incomplete",
|
||||
"r-move-all-cards": "Move all cards in list",
|
||||
"r-set-date-relative": "Set date relative to now",
|
||||
"r-later": "later",
|
||||
"r-unit-minutes": "minute(s)",
|
||||
"r-unit-hours": "hour(s)",
|
||||
"r-unit-days": "day(s)",
|
||||
"r-unit-weeks": "week(s)",
|
||||
"r-unit-months": "month(s)",
|
||||
"r-trigger": "Disparador",
|
||||
"r-action": "Acció",
|
||||
"r-when-a-card": "Quan una fitxa",
|
||||
@@ -1809,6 +1835,45 @@
|
||||
"azure-container": "Container",
|
||||
"gcs-storage": "Google Cloud Storage",
|
||||
"gcs-storage-description": "Google Cloud Storage.",
|
||||
"database-migration": "Database migration",
|
||||
"database-migration-description": "Migrate text-based data (everything except attachments and avatars, which stay on the filesystem) between MongoDB and FerretDB v1 (SQLite). The other database must be running and reachable (default ports: FerretDB mongodb://127.0.0.1:27018, MongoDB mongodb://127.0.0.1:27019; override with WEKAN_FERRETDB_URL / WEKAN_MONGODB_URL). After it finishes, point MONGO_URL at the other database and restart WeKan (on Snap: snap set wekan database=ferretdb or =mongodb).",
|
||||
"database-migrate-to-ferretdb": "Migrate text data to FerretDB v1 (SQLite)",
|
||||
"database-migrate-to-mongodb": "Migrate text data to MongoDB",
|
||||
"database-migration-confirm": "Copy all text-based collections to __db__? Attachments and avatars on the filesystem are not affected. The target database must be running.",
|
||||
"database-migration-phase": "Phase",
|
||||
"database-migration-done": "Text-data migration completed.",
|
||||
"collections": "collections",
|
||||
"features": "Features",
|
||||
"features-performance": "Performance",
|
||||
"cards-loading": "Card loading",
|
||||
"cards-loading-all": "All cards (default)",
|
||||
"cards-loading-lazy": "Lazy (only visible cards)",
|
||||
"cards-loading-description": "How each board loads its cards. All cards: every card is loaded into the browser (simplest, all features work, but a board with thousands of cards sends a lot of data to each viewer). Lazy: each list loads only the cards currently visible (infinite scroll) plus a live count, so very large boards use less memory and bandwidth. Also set by the CARDS_LOADING environment variable.",
|
||||
"cards-loading-lazy-note": "Lazy card loading is experimental: card counters and WIP limits are accurate, but the Calendar/Table/Gantt views and multi-select reflect only the cards loaded so far. Reload open boards after changing this.",
|
||||
"backup": "Backup",
|
||||
"backup-description": "Back up Attachments, Avatars and/or Data (all text-based collections that are not attachments or avatars). The .zip is streamed directly to the selected storage — no temporary file — as backup/YYYY/MM/DD/HH_MM_SS/backup.zip, containing YYYY_MM_DD-HH_MM_SS/attachments, /avatars and /data. Cloud storages (S3/MinIO, Azure, GCS) use the credentials configured in their storage tabs.",
|
||||
"backup-data": "Data (text)",
|
||||
"backup-storage": "Save to storage",
|
||||
"backup-now": "Backup now",
|
||||
"backup-done": "Backup completed",
|
||||
"backup-schedule": "Scheduled backups",
|
||||
"backup-frequency": "Frequency",
|
||||
"backup-frequency-off": "Off",
|
||||
"backup-frequency-daily": "Daily",
|
||||
"backup-frequency-weekly": "Weekly",
|
||||
"backup-frequency-monthly": "Monthly",
|
||||
"backup-time": "Time (HH:MM)",
|
||||
"backup-day-of-week": "Day of week (weekly)",
|
||||
"backup-day-of-month": "Day of month (monthly, 1-28)",
|
||||
"backup-restore": "Restaura",
|
||||
"backup-list": "List backups",
|
||||
"backup-datetime": "Date and time",
|
||||
"backup-path": "File path",
|
||||
"backup-restore-mode": "Restore mode",
|
||||
"backup-restore-add-missing": "Add missing data only",
|
||||
"backup-restore-replace-all": "Replace all data",
|
||||
"backup-restore-select-first": "Select a backup from the list first.",
|
||||
"backup-restore-confirm": "Restore the selected backup? Attachments/avatars are written to the filesystem and data to the database according to the chosen mode.",
|
||||
"gcs-project-id": "Project ID",
|
||||
"gcs-bucket": "Bucket",
|
||||
"gcs-key-filename": "Service account key file path",
|
||||
@@ -1986,8 +2051,6 @@
|
||||
"cpu-cores": "CPU Cores",
|
||||
"cpu-usage": "CPU Usage",
|
||||
"current-action": "Current Action",
|
||||
"database-migration": "Database Migration",
|
||||
"database-migration-description": "Updating database structure for improved functionality and performance. This process may take several minutes.",
|
||||
"database-migrations": "Database Migrations",
|
||||
"days-old": "Days Old",
|
||||
"duration": "Duration",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accept": "接受",
|
||||
"activity-changedTitle": "changed title to %s of %s",
|
||||
"activity-changedDescription": "changed description of %s",
|
||||
"act-deleteCard": "deleted card __card__ from list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-removeBoard": "deleted board __board__",
|
||||
"act-removeList": "deleted list __list__ at board __board__",
|
||||
@@ -441,6 +442,7 @@
|
||||
"createCustomField": "创建字段",
|
||||
"createCustomFieldPopup-title": "创建字段",
|
||||
"current": "当前的",
|
||||
"custom-color": "Custom color",
|
||||
"custom-field-delete-pop": "无法撤消。这将从所有卡中删除此自定义字段并销毁其历史记录。",
|
||||
"custom-field-checkbox": "复选框",
|
||||
"custom-field-currency": "货币",
|
||||
@@ -508,6 +510,7 @@
|
||||
"error-csv-schema": "您的 CSV(逗号分隔值)/TSV(制表符分隔值)未包含格式正确的正确信息",
|
||||
"error-import-empty-board": "This export contains no board content (no swimlanes, lists or cards). It may have been created by an older WeKan version with a known export bug. Please re-export the board with the current version and import the new file.",
|
||||
"error-list-doesNotExist": "该列表不存在",
|
||||
"error-linked-card-not-allowed": "You can only link to a normal card. Linking to another linked card, or to a card that links back to this board, is not allowed because it would make the cards inaccessible.",
|
||||
"error-user-disabled": "This user account is disabled",
|
||||
"error-user-doesNotExist": "该用户不存在",
|
||||
"error-user-notAllowSelf": "您不能邀请自己",
|
||||
@@ -581,6 +584,7 @@
|
||||
"fullname": "姓名",
|
||||
"header-logo-title": "返回您的图板页面。",
|
||||
"show-activities": "展会活动",
|
||||
"hide-activities": "Hide Activities",
|
||||
"headerBarCreateBoardPopup-title": "创建板",
|
||||
"home": "家",
|
||||
"import": "进口",
|
||||
@@ -894,7 +898,12 @@
|
||||
"no-name": "(未知)",
|
||||
"Node_version": "节点版本",
|
||||
"Meteor_version": "流星版",
|
||||
"MongoDB_version": "MongoDB版本",
|
||||
"Database_type": "Database type",
|
||||
"Database_commit": "Database commit",
|
||||
"FerretDB_version": "FerretDB version",
|
||||
"FerretDB_commit": "FerretDB commit",
|
||||
"Reactivity_mode": "Reactivity mode (changeStreams / oplog / polling)",
|
||||
"MongoDB_version": "MongoDB compatible version",
|
||||
"MongoDB_storage_engine": "MongoDB 存储引擎",
|
||||
"MongoDB_Oplog_enabled": "MongoDB Oplog 已启用",
|
||||
"OS_Arch": "操作系统架构",
|
||||
@@ -1015,6 +1024,17 @@
|
||||
"r-workflow-help": "Drag a trigger and an action into the builder, then add the rule. Drag an action onto an existing rule to change what it does.",
|
||||
"r-drop-trigger": "drop a trigger here",
|
||||
"r-drop-action": "drop an action here",
|
||||
"r-w-card-created": "Card is created",
|
||||
"r-w-card-archived": "Card is archived",
|
||||
"r-w-card-unarchived": "Card is unarchived",
|
||||
"r-w-label-added": "Any label is added",
|
||||
"r-w-label-removed": "Any label is removed",
|
||||
"r-w-member-added": "A member is added",
|
||||
"r-w-member-removed": "A member is removed",
|
||||
"r-w-checklist-added": "A checklist is added",
|
||||
"r-w-attachment-added": "An attachment is added",
|
||||
"r-w-every-day-at": "Every day at __time__",
|
||||
"r-w-set-received-now": "Set received date to now",
|
||||
"r-export-json": "Export rules to JSON",
|
||||
"r-export-csv": "Export rules to CSV",
|
||||
"r-import-json": "Import rules from JSON",
|
||||
@@ -1068,6 +1088,12 @@
|
||||
"r-mark-incomplete": "Mark card incomplete",
|
||||
"r-move-all-cards": "Move all cards in list",
|
||||
"r-set-date-relative": "Set date relative to now",
|
||||
"r-later": "later",
|
||||
"r-unit-minutes": "minute(s)",
|
||||
"r-unit-hours": "hour(s)",
|
||||
"r-unit-days": "day(s)",
|
||||
"r-unit-weeks": "week(s)",
|
||||
"r-unit-months": "month(s)",
|
||||
"r-trigger": "扳机",
|
||||
"r-action": "行动",
|
||||
"r-when-a-card": "当一张卡",
|
||||
@@ -1809,6 +1835,45 @@
|
||||
"azure-container": "Container",
|
||||
"gcs-storage": "Google Cloud Storage",
|
||||
"gcs-storage-description": "Google Cloud Storage.",
|
||||
"database-migration": "Database migration",
|
||||
"database-migration-description": "Migrate text-based data (everything except attachments and avatars, which stay on the filesystem) between MongoDB and FerretDB v1 (SQLite). The other database must be running and reachable (default ports: FerretDB mongodb://127.0.0.1:27018, MongoDB mongodb://127.0.0.1:27019; override with WEKAN_FERRETDB_URL / WEKAN_MONGODB_URL). After it finishes, point MONGO_URL at the other database and restart WeKan (on Snap: snap set wekan database=ferretdb or =mongodb).",
|
||||
"database-migrate-to-ferretdb": "Migrate text data to FerretDB v1 (SQLite)",
|
||||
"database-migrate-to-mongodb": "Migrate text data to MongoDB",
|
||||
"database-migration-confirm": "Copy all text-based collections to __db__? Attachments and avatars on the filesystem are not affected. The target database must be running.",
|
||||
"database-migration-phase": "Phase",
|
||||
"database-migration-done": "Text-data migration completed.",
|
||||
"collections": "collections",
|
||||
"features": "Features",
|
||||
"features-performance": "Performance",
|
||||
"cards-loading": "Card loading",
|
||||
"cards-loading-all": "All cards (default)",
|
||||
"cards-loading-lazy": "Lazy (only visible cards)",
|
||||
"cards-loading-description": "How each board loads its cards. All cards: every card is loaded into the browser (simplest, all features work, but a board with thousands of cards sends a lot of data to each viewer). Lazy: each list loads only the cards currently visible (infinite scroll) plus a live count, so very large boards use less memory and bandwidth. Also set by the CARDS_LOADING environment variable.",
|
||||
"cards-loading-lazy-note": "Lazy card loading is experimental: card counters and WIP limits are accurate, but the Calendar/Table/Gantt views and multi-select reflect only the cards loaded so far. Reload open boards after changing this.",
|
||||
"backup": "Backup",
|
||||
"backup-description": "Back up Attachments, Avatars and/or Data (all text-based collections that are not attachments or avatars). The .zip is streamed directly to the selected storage — no temporary file — as backup/YYYY/MM/DD/HH_MM_SS/backup.zip, containing YYYY_MM_DD-HH_MM_SS/attachments, /avatars and /data. Cloud storages (S3/MinIO, Azure, GCS) use the credentials configured in their storage tabs.",
|
||||
"backup-data": "Data (text)",
|
||||
"backup-storage": "Save to storage",
|
||||
"backup-now": "Backup now",
|
||||
"backup-done": "Backup completed",
|
||||
"backup-schedule": "Scheduled backups",
|
||||
"backup-frequency": "Frequency",
|
||||
"backup-frequency-off": "Off",
|
||||
"backup-frequency-daily": "Daily",
|
||||
"backup-frequency-weekly": "Weekly",
|
||||
"backup-frequency-monthly": "Monthly",
|
||||
"backup-time": "Time (HH:MM)",
|
||||
"backup-day-of-week": "Day of week (weekly)",
|
||||
"backup-day-of-month": "Day of month (monthly, 1-28)",
|
||||
"backup-restore": "恢复",
|
||||
"backup-list": "List backups",
|
||||
"backup-datetime": "Date and time",
|
||||
"backup-path": "File path",
|
||||
"backup-restore-mode": "Restore mode",
|
||||
"backup-restore-add-missing": "Add missing data only",
|
||||
"backup-restore-replace-all": "Replace all data",
|
||||
"backup-restore-select-first": "Select a backup from the list first.",
|
||||
"backup-restore-confirm": "Restore the selected backup? Attachments/avatars are written to the filesystem and data to the database according to the chosen mode.",
|
||||
"gcs-project-id": "Project ID",
|
||||
"gcs-bucket": "Bucket",
|
||||
"gcs-key-filename": "Service account key file path",
|
||||
@@ -1986,8 +2051,6 @@
|
||||
"cpu-cores": "CPU核心",
|
||||
"cpu-usage": "中央处理器使用率",
|
||||
"current-action": "当前行动",
|
||||
"database-migration": "数据库迁移",
|
||||
"database-migration-description": "更新数据库结构以改进功能和性能。此过程可能需要几分钟。",
|
||||
"database-migrations": "数据库迁移",
|
||||
"days-old": "岁月如梭",
|
||||
"duration": "期间",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accept": "Přijmout",
|
||||
"activity-changedTitle": "changed title to %s of %s",
|
||||
"activity-changedDescription": "changed description of %s",
|
||||
"act-deleteCard": "deleted card __card__ from list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-removeBoard": "deleted board __board__",
|
||||
"act-removeList": "deleted list __list__ at board __board__",
|
||||
@@ -441,6 +442,7 @@
|
||||
"createCustomField": "Vytvořit pole",
|
||||
"createCustomFieldPopup-title": "Vytvořit pole",
|
||||
"current": "Aktuální",
|
||||
"custom-color": "Custom color",
|
||||
"custom-field-delete-pop": "Nelze vrátit zpět. Toto odebere toto vlastní pole ze všech karet a zničí jeho historii.",
|
||||
"custom-field-checkbox": "Checkbox",
|
||||
"custom-field-currency": "Měna",
|
||||
@@ -508,6 +510,7 @@
|
||||
"error-csv-schema": "Váš CSV (Comma Separated Values)/TSV (Tab Separated Values) neobsahuje správné informace ve správném formátu.",
|
||||
"error-import-empty-board": "This export contains no board content (no swimlanes, lists or cards). It may have been created by an older WeKan version with a known export bug. Please re-export the board with the current version and import the new file.",
|
||||
"error-list-doesNotExist": "Tento sloupec ;neexistuje",
|
||||
"error-linked-card-not-allowed": "You can only link to a normal card. Linking to another linked card, or to a card that links back to this board, is not allowed because it would make the cards inaccessible.",
|
||||
"error-user-disabled": "This user account is disabled",
|
||||
"error-user-doesNotExist": "Tento uživatel neexistuje",
|
||||
"error-user-notAllowSelf": "Nemůžeš pozvat sám sebe",
|
||||
@@ -581,6 +584,7 @@
|
||||
"fullname": "Celé jméno",
|
||||
"header-logo-title": "Jit zpět na stránku s tably.",
|
||||
"show-activities": "Show Activities",
|
||||
"hide-activities": "Hide Activities",
|
||||
"headerBarCreateBoardPopup-title": "Vytvořit tablo",
|
||||
"home": "Domů",
|
||||
"import": "Import",
|
||||
@@ -894,7 +898,12 @@
|
||||
"no-name": "(Neznámé)",
|
||||
"Node_version": "Node verze",
|
||||
"Meteor_version": "Verze Meteor",
|
||||
"MongoDB_version": "MongoDB verze",
|
||||
"Database_type": "Database type",
|
||||
"Database_commit": "Database commit",
|
||||
"FerretDB_version": "FerretDB version",
|
||||
"FerretDB_commit": "FerretDB commit",
|
||||
"Reactivity_mode": "Reactivity mode (changeStreams / oplog / polling)",
|
||||
"MongoDB_version": "MongoDB compatible version",
|
||||
"MongoDB_storage_engine": "MongoDB storage engine",
|
||||
"MongoDB_Oplog_enabled": "MongoDB Oplog povolen",
|
||||
"OS_Arch": "OS Architektura",
|
||||
@@ -1015,6 +1024,17 @@
|
||||
"r-workflow-help": "Drag a trigger and an action into the builder, then add the rule. Drag an action onto an existing rule to change what it does.",
|
||||
"r-drop-trigger": "drop a trigger here",
|
||||
"r-drop-action": "drop an action here",
|
||||
"r-w-card-created": "Card is created",
|
||||
"r-w-card-archived": "Card is archived",
|
||||
"r-w-card-unarchived": "Card is unarchived",
|
||||
"r-w-label-added": "Any label is added",
|
||||
"r-w-label-removed": "Any label is removed",
|
||||
"r-w-member-added": "A member is added",
|
||||
"r-w-member-removed": "A member is removed",
|
||||
"r-w-checklist-added": "A checklist is added",
|
||||
"r-w-attachment-added": "An attachment is added",
|
||||
"r-w-every-day-at": "Every day at __time__",
|
||||
"r-w-set-received-now": "Set received date to now",
|
||||
"r-export-json": "Export rules to JSON",
|
||||
"r-export-csv": "Export rules to CSV",
|
||||
"r-import-json": "Import rules from JSON",
|
||||
@@ -1068,6 +1088,12 @@
|
||||
"r-mark-incomplete": "Mark card incomplete",
|
||||
"r-move-all-cards": "Move all cards in list",
|
||||
"r-set-date-relative": "Set date relative to now",
|
||||
"r-later": "later",
|
||||
"r-unit-minutes": "minute(s)",
|
||||
"r-unit-hours": "hour(s)",
|
||||
"r-unit-days": "day(s)",
|
||||
"r-unit-weeks": "week(s)",
|
||||
"r-unit-months": "month(s)",
|
||||
"r-trigger": "Spouštěč",
|
||||
"r-action": "Akce",
|
||||
"r-when-a-card": "Pokud karta",
|
||||
@@ -1809,6 +1835,45 @@
|
||||
"azure-container": "Container",
|
||||
"gcs-storage": "Google Cloud Storage",
|
||||
"gcs-storage-description": "Google Cloud Storage.",
|
||||
"database-migration": "Database migration",
|
||||
"database-migration-description": "Migrate text-based data (everything except attachments and avatars, which stay on the filesystem) between MongoDB and FerretDB v1 (SQLite). The other database must be running and reachable (default ports: FerretDB mongodb://127.0.0.1:27018, MongoDB mongodb://127.0.0.1:27019; override with WEKAN_FERRETDB_URL / WEKAN_MONGODB_URL). After it finishes, point MONGO_URL at the other database and restart WeKan (on Snap: snap set wekan database=ferretdb or =mongodb).",
|
||||
"database-migrate-to-ferretdb": "Migrate text data to FerretDB v1 (SQLite)",
|
||||
"database-migrate-to-mongodb": "Migrate text data to MongoDB",
|
||||
"database-migration-confirm": "Copy all text-based collections to __db__? Attachments and avatars on the filesystem are not affected. The target database must be running.",
|
||||
"database-migration-phase": "Phase",
|
||||
"database-migration-done": "Text-data migration completed.",
|
||||
"collections": "collections",
|
||||
"features": "Features",
|
||||
"features-performance": "Performance",
|
||||
"cards-loading": "Card loading",
|
||||
"cards-loading-all": "All cards (default)",
|
||||
"cards-loading-lazy": "Lazy (only visible cards)",
|
||||
"cards-loading-description": "How each board loads its cards. All cards: every card is loaded into the browser (simplest, all features work, but a board with thousands of cards sends a lot of data to each viewer). Lazy: each list loads only the cards currently visible (infinite scroll) plus a live count, so very large boards use less memory and bandwidth. Also set by the CARDS_LOADING environment variable.",
|
||||
"cards-loading-lazy-note": "Lazy card loading is experimental: card counters and WIP limits are accurate, but the Calendar/Table/Gantt views and multi-select reflect only the cards loaded so far. Reload open boards after changing this.",
|
||||
"backup": "Backup",
|
||||
"backup-description": "Back up Attachments, Avatars and/or Data (all text-based collections that are not attachments or avatars). The .zip is streamed directly to the selected storage — no temporary file — as backup/YYYY/MM/DD/HH_MM_SS/backup.zip, containing YYYY_MM_DD-HH_MM_SS/attachments, /avatars and /data. Cloud storages (S3/MinIO, Azure, GCS) use the credentials configured in their storage tabs.",
|
||||
"backup-data": "Data (text)",
|
||||
"backup-storage": "Save to storage",
|
||||
"backup-now": "Backup now",
|
||||
"backup-done": "Backup completed",
|
||||
"backup-schedule": "Scheduled backups",
|
||||
"backup-frequency": "Frequency",
|
||||
"backup-frequency-off": "Off",
|
||||
"backup-frequency-daily": "Daily",
|
||||
"backup-frequency-weekly": "Weekly",
|
||||
"backup-frequency-monthly": "Monthly",
|
||||
"backup-time": "Time (HH:MM)",
|
||||
"backup-day-of-week": "Day of week (weekly)",
|
||||
"backup-day-of-month": "Day of month (monthly, 1-28)",
|
||||
"backup-restore": "Obnovit",
|
||||
"backup-list": "List backups",
|
||||
"backup-datetime": "Date and time",
|
||||
"backup-path": "File path",
|
||||
"backup-restore-mode": "Restore mode",
|
||||
"backup-restore-add-missing": "Add missing data only",
|
||||
"backup-restore-replace-all": "Replace all data",
|
||||
"backup-restore-select-first": "Select a backup from the list first.",
|
||||
"backup-restore-confirm": "Restore the selected backup? Attachments/avatars are written to the filesystem and data to the database according to the chosen mode.",
|
||||
"gcs-project-id": "Project ID",
|
||||
"gcs-bucket": "Bucket",
|
||||
"gcs-key-filename": "Service account key file path",
|
||||
@@ -1986,8 +2051,6 @@
|
||||
"cpu-cores": "CPU Cores",
|
||||
"cpu-usage": "CPU Usage",
|
||||
"current-action": "Current Action",
|
||||
"database-migration": "Database Migration",
|
||||
"database-migration-description": "Updating database structure for improved functionality and performance. This process may take several minutes.",
|
||||
"database-migrations": "Database Migrations",
|
||||
"days-old": "Days Old",
|
||||
"duration": "Duration",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accept": "Přijmout",
|
||||
"activity-changedTitle": "changed title to %s of %s",
|
||||
"activity-changedDescription": "changed description of %s",
|
||||
"act-deleteCard": "deleted card __card__ from list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-removeBoard": "deleted board __board__",
|
||||
"act-removeList": "deleted list __list__ at board __board__",
|
||||
@@ -441,6 +442,7 @@
|
||||
"createCustomField": "Vytvořit pole",
|
||||
"createCustomFieldPopup-title": "Vytvořit pole",
|
||||
"current": "Aktuální",
|
||||
"custom-color": "Custom color",
|
||||
"custom-field-delete-pop": "Nelze vrátit zpět. Toto odebere toto vlastní pole ze všech karet a zničí jeho historii.",
|
||||
"custom-field-checkbox": "Checkbox",
|
||||
"custom-field-currency": "Měna",
|
||||
@@ -508,6 +510,7 @@
|
||||
"error-csv-schema": "Váš CSV (Comma Separated Values)/TSV (Tab Separated Values) neobsahuje správné informace ve správném formátu.",
|
||||
"error-import-empty-board": "This export contains no board content (no swimlanes, lists or cards). It may have been created by an older WeKan version with a known export bug. Please re-export the board with the current version and import the new file.",
|
||||
"error-list-doesNotExist": "Tento sloupec ;neexistuje",
|
||||
"error-linked-card-not-allowed": "You can only link to a normal card. Linking to another linked card, or to a card that links back to this board, is not allowed because it would make the cards inaccessible.",
|
||||
"error-user-disabled": "This user account is disabled",
|
||||
"error-user-doesNotExist": "Tento uživatel neexistuje",
|
||||
"error-user-notAllowSelf": "Nemůžeš pozvat sám sebe",
|
||||
@@ -581,6 +584,7 @@
|
||||
"fullname": "Celé jméno",
|
||||
"header-logo-title": "Jit zpět na stránku s tably.",
|
||||
"show-activities": "Show Activities",
|
||||
"hide-activities": "Hide Activities",
|
||||
"headerBarCreateBoardPopup-title": "Vytvořit tablo",
|
||||
"home": "Domů",
|
||||
"import": "Import",
|
||||
@@ -894,7 +898,12 @@
|
||||
"no-name": "(Neznámé)",
|
||||
"Node_version": "Node verze",
|
||||
"Meteor_version": "Verze Meteor",
|
||||
"MongoDB_version": "MongoDB verze",
|
||||
"Database_type": "Database type",
|
||||
"Database_commit": "Database commit",
|
||||
"FerretDB_version": "FerretDB version",
|
||||
"FerretDB_commit": "FerretDB commit",
|
||||
"Reactivity_mode": "Reactivity mode (changeStreams / oplog / polling)",
|
||||
"MongoDB_version": "MongoDB compatible version",
|
||||
"MongoDB_storage_engine": "MongoDB storage engine",
|
||||
"MongoDB_Oplog_enabled": "MongoDB Oplog povolen",
|
||||
"OS_Arch": "OS Architektura",
|
||||
@@ -1015,6 +1024,17 @@
|
||||
"r-workflow-help": "Drag a trigger and an action into the builder, then add the rule. Drag an action onto an existing rule to change what it does.",
|
||||
"r-drop-trigger": "drop a trigger here",
|
||||
"r-drop-action": "drop an action here",
|
||||
"r-w-card-created": "Card is created",
|
||||
"r-w-card-archived": "Card is archived",
|
||||
"r-w-card-unarchived": "Card is unarchived",
|
||||
"r-w-label-added": "Any label is added",
|
||||
"r-w-label-removed": "Any label is removed",
|
||||
"r-w-member-added": "A member is added",
|
||||
"r-w-member-removed": "A member is removed",
|
||||
"r-w-checklist-added": "A checklist is added",
|
||||
"r-w-attachment-added": "An attachment is added",
|
||||
"r-w-every-day-at": "Every day at __time__",
|
||||
"r-w-set-received-now": "Set received date to now",
|
||||
"r-export-json": "Export rules to JSON",
|
||||
"r-export-csv": "Export rules to CSV",
|
||||
"r-import-json": "Import rules from JSON",
|
||||
@@ -1068,6 +1088,12 @@
|
||||
"r-mark-incomplete": "Mark card incomplete",
|
||||
"r-move-all-cards": "Move all cards in list",
|
||||
"r-set-date-relative": "Set date relative to now",
|
||||
"r-later": "later",
|
||||
"r-unit-minutes": "minute(s)",
|
||||
"r-unit-hours": "hour(s)",
|
||||
"r-unit-days": "day(s)",
|
||||
"r-unit-weeks": "week(s)",
|
||||
"r-unit-months": "month(s)",
|
||||
"r-trigger": "Spouštěč",
|
||||
"r-action": "Akce",
|
||||
"r-when-a-card": "Pokud karta",
|
||||
@@ -1809,6 +1835,45 @@
|
||||
"azure-container": "Container",
|
||||
"gcs-storage": "Google Cloud Storage",
|
||||
"gcs-storage-description": "Google Cloud Storage.",
|
||||
"database-migration": "Database migration",
|
||||
"database-migration-description": "Migrate text-based data (everything except attachments and avatars, which stay on the filesystem) between MongoDB and FerretDB v1 (SQLite). The other database must be running and reachable (default ports: FerretDB mongodb://127.0.0.1:27018, MongoDB mongodb://127.0.0.1:27019; override with WEKAN_FERRETDB_URL / WEKAN_MONGODB_URL). After it finishes, point MONGO_URL at the other database and restart WeKan (on Snap: snap set wekan database=ferretdb or =mongodb).",
|
||||
"database-migrate-to-ferretdb": "Migrate text data to FerretDB v1 (SQLite)",
|
||||
"database-migrate-to-mongodb": "Migrate text data to MongoDB",
|
||||
"database-migration-confirm": "Copy all text-based collections to __db__? Attachments and avatars on the filesystem are not affected. The target database must be running.",
|
||||
"database-migration-phase": "Phase",
|
||||
"database-migration-done": "Text-data migration completed.",
|
||||
"collections": "collections",
|
||||
"features": "Features",
|
||||
"features-performance": "Performance",
|
||||
"cards-loading": "Card loading",
|
||||
"cards-loading-all": "All cards (default)",
|
||||
"cards-loading-lazy": "Lazy (only visible cards)",
|
||||
"cards-loading-description": "How each board loads its cards. All cards: every card is loaded into the browser (simplest, all features work, but a board with thousands of cards sends a lot of data to each viewer). Lazy: each list loads only the cards currently visible (infinite scroll) plus a live count, so very large boards use less memory and bandwidth. Also set by the CARDS_LOADING environment variable.",
|
||||
"cards-loading-lazy-note": "Lazy card loading is experimental: card counters and WIP limits are accurate, but the Calendar/Table/Gantt views and multi-select reflect only the cards loaded so far. Reload open boards after changing this.",
|
||||
"backup": "Backup",
|
||||
"backup-description": "Back up Attachments, Avatars and/or Data (all text-based collections that are not attachments or avatars). The .zip is streamed directly to the selected storage — no temporary file — as backup/YYYY/MM/DD/HH_MM_SS/backup.zip, containing YYYY_MM_DD-HH_MM_SS/attachments, /avatars and /data. Cloud storages (S3/MinIO, Azure, GCS) use the credentials configured in their storage tabs.",
|
||||
"backup-data": "Data (text)",
|
||||
"backup-storage": "Save to storage",
|
||||
"backup-now": "Backup now",
|
||||
"backup-done": "Backup completed",
|
||||
"backup-schedule": "Scheduled backups",
|
||||
"backup-frequency": "Frequency",
|
||||
"backup-frequency-off": "Off",
|
||||
"backup-frequency-daily": "Daily",
|
||||
"backup-frequency-weekly": "Weekly",
|
||||
"backup-frequency-monthly": "Monthly",
|
||||
"backup-time": "Time (HH:MM)",
|
||||
"backup-day-of-week": "Day of week (weekly)",
|
||||
"backup-day-of-month": "Day of month (monthly, 1-28)",
|
||||
"backup-restore": "Obnovit",
|
||||
"backup-list": "List backups",
|
||||
"backup-datetime": "Date and time",
|
||||
"backup-path": "File path",
|
||||
"backup-restore-mode": "Restore mode",
|
||||
"backup-restore-add-missing": "Add missing data only",
|
||||
"backup-restore-replace-all": "Replace all data",
|
||||
"backup-restore-select-first": "Select a backup from the list first.",
|
||||
"backup-restore-confirm": "Restore the selected backup? Attachments/avatars are written to the filesystem and data to the database according to the chosen mode.",
|
||||
"gcs-project-id": "Project ID",
|
||||
"gcs-bucket": "Bucket",
|
||||
"gcs-key-filename": "Service account key file path",
|
||||
@@ -1986,8 +2051,6 @@
|
||||
"cpu-cores": "CPU Cores",
|
||||
"cpu-usage": "CPU Usage",
|
||||
"current-action": "Current Action",
|
||||
"database-migration": "Database Migration",
|
||||
"database-migration-description": "Updating database structure for improved functionality and performance. This process may take several minutes.",
|
||||
"database-migrations": "Database Migrations",
|
||||
"days-old": "Days Old",
|
||||
"duration": "Duration",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accept": "Derbyn",
|
||||
"activity-changedTitle": "changed title to %s of %s",
|
||||
"activity-changedDescription": "changed description of %s",
|
||||
"act-deleteCard": "deleted card __card__ from list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-removeBoard": "deleted board __board__",
|
||||
"act-removeList": "deleted list __list__ at board __board__",
|
||||
@@ -441,6 +442,7 @@
|
||||
"createCustomField": "Creu Maes",
|
||||
"createCustomFieldPopup-title": "Creu Maes",
|
||||
"current": "presennol",
|
||||
"custom-color": "Custom color",
|
||||
"custom-field-delete-pop": "Does dim dadwneud. Bydd hyn yn dileu'r maes arfer hwn o bob cerdyn ac yn dinistrio ei hanes.",
|
||||
"custom-field-checkbox": "Blwch ticio",
|
||||
"custom-field-currency": "Arian cyfred",
|
||||
@@ -508,6 +510,7 @@
|
||||
"error-csv-schema": "Nid yw eich CSV(Comma Separated Values)/TSV (Tab Separated Values) yn cynnwys y wybodaeth gywir yn y fformat cywir",
|
||||
"error-import-empty-board": "This export contains no board content (no swimlanes, lists or cards). It may have been created by an older WeKan version with a known export bug. Please re-export the board with the current version and import the new file.",
|
||||
"error-list-doesNotExist": "Nid yw'r rhestr hon yn bodoli",
|
||||
"error-linked-card-not-allowed": "You can only link to a normal card. Linking to another linked card, or to a card that links back to this board, is not allowed because it would make the cards inaccessible.",
|
||||
"error-user-disabled": "This user account is disabled",
|
||||
"error-user-doesNotExist": "Nid yw'r defnyddiwr hwn yn bodoli",
|
||||
"error-user-notAllowSelf": "Ni allwch wahodd eich hun",
|
||||
@@ -581,6 +584,7 @@
|
||||
"fullname": "Enw Llawn",
|
||||
"header-logo-title": "Ewch yn ôl i dudalen eich byrddau.",
|
||||
"show-activities": "Gweithgareddau Sioe",
|
||||
"hide-activities": "Hide Activities",
|
||||
"headerBarCreateBoardPopup-title": "Creu Bwrdd",
|
||||
"home": "Cartref",
|
||||
"import": "Mewnforio",
|
||||
@@ -894,7 +898,12 @@
|
||||
"no-name": "(Anhysbys)",
|
||||
"Node_version": "Fersiwn nod",
|
||||
"Meteor_version": "Fersiwn Meteor",
|
||||
"MongoDB_version": "Fersiwn MongoDB",
|
||||
"Database_type": "Database type",
|
||||
"Database_commit": "Database commit",
|
||||
"FerretDB_version": "FerretDB version",
|
||||
"FerretDB_commit": "FerretDB commit",
|
||||
"Reactivity_mode": "Reactivity mode (changeStreams / oplog / polling)",
|
||||
"MongoDB_version": "MongoDB compatible version",
|
||||
"MongoDB_storage_engine": "Peiriant storio MongoDB",
|
||||
"MongoDB_Oplog_enabled": "MongoDB Oplog wedi'i alluogi",
|
||||
"OS_Arch": "AO Arch",
|
||||
@@ -1015,6 +1024,17 @@
|
||||
"r-workflow-help": "Drag a trigger and an action into the builder, then add the rule. Drag an action onto an existing rule to change what it does.",
|
||||
"r-drop-trigger": "drop a trigger here",
|
||||
"r-drop-action": "drop an action here",
|
||||
"r-w-card-created": "Card is created",
|
||||
"r-w-card-archived": "Card is archived",
|
||||
"r-w-card-unarchived": "Card is unarchived",
|
||||
"r-w-label-added": "Any label is added",
|
||||
"r-w-label-removed": "Any label is removed",
|
||||
"r-w-member-added": "A member is added",
|
||||
"r-w-member-removed": "A member is removed",
|
||||
"r-w-checklist-added": "A checklist is added",
|
||||
"r-w-attachment-added": "An attachment is added",
|
||||
"r-w-every-day-at": "Every day at __time__",
|
||||
"r-w-set-received-now": "Set received date to now",
|
||||
"r-export-json": "Export rules to JSON",
|
||||
"r-export-csv": "Export rules to CSV",
|
||||
"r-import-json": "Import rules from JSON",
|
||||
@@ -1068,6 +1088,12 @@
|
||||
"r-mark-incomplete": "Mark card incomplete",
|
||||
"r-move-all-cards": "Move all cards in list",
|
||||
"r-set-date-relative": "Set date relative to now",
|
||||
"r-later": "later",
|
||||
"r-unit-minutes": "minute(s)",
|
||||
"r-unit-hours": "hour(s)",
|
||||
"r-unit-days": "day(s)",
|
||||
"r-unit-weeks": "week(s)",
|
||||
"r-unit-months": "month(s)",
|
||||
"r-trigger": "Sbardun",
|
||||
"r-action": "Gweithred",
|
||||
"r-when-a-card": "Pan fydd cerdyn",
|
||||
@@ -1809,6 +1835,45 @@
|
||||
"azure-container": "Container",
|
||||
"gcs-storage": "Google Cloud Storage",
|
||||
"gcs-storage-description": "Google Cloud Storage.",
|
||||
"database-migration": "Database migration",
|
||||
"database-migration-description": "Migrate text-based data (everything except attachments and avatars, which stay on the filesystem) between MongoDB and FerretDB v1 (SQLite). The other database must be running and reachable (default ports: FerretDB mongodb://127.0.0.1:27018, MongoDB mongodb://127.0.0.1:27019; override with WEKAN_FERRETDB_URL / WEKAN_MONGODB_URL). After it finishes, point MONGO_URL at the other database and restart WeKan (on Snap: snap set wekan database=ferretdb or =mongodb).",
|
||||
"database-migrate-to-ferretdb": "Migrate text data to FerretDB v1 (SQLite)",
|
||||
"database-migrate-to-mongodb": "Migrate text data to MongoDB",
|
||||
"database-migration-confirm": "Copy all text-based collections to __db__? Attachments and avatars on the filesystem are not affected. The target database must be running.",
|
||||
"database-migration-phase": "Phase",
|
||||
"database-migration-done": "Text-data migration completed.",
|
||||
"collections": "collections",
|
||||
"features": "Features",
|
||||
"features-performance": "Performance",
|
||||
"cards-loading": "Card loading",
|
||||
"cards-loading-all": "All cards (default)",
|
||||
"cards-loading-lazy": "Lazy (only visible cards)",
|
||||
"cards-loading-description": "How each board loads its cards. All cards: every card is loaded into the browser (simplest, all features work, but a board with thousands of cards sends a lot of data to each viewer). Lazy: each list loads only the cards currently visible (infinite scroll) plus a live count, so very large boards use less memory and bandwidth. Also set by the CARDS_LOADING environment variable.",
|
||||
"cards-loading-lazy-note": "Lazy card loading is experimental: card counters and WIP limits are accurate, but the Calendar/Table/Gantt views and multi-select reflect only the cards loaded so far. Reload open boards after changing this.",
|
||||
"backup": "Backup",
|
||||
"backup-description": "Back up Attachments, Avatars and/or Data (all text-based collections that are not attachments or avatars). The .zip is streamed directly to the selected storage — no temporary file — as backup/YYYY/MM/DD/HH_MM_SS/backup.zip, containing YYYY_MM_DD-HH_MM_SS/attachments, /avatars and /data. Cloud storages (S3/MinIO, Azure, GCS) use the credentials configured in their storage tabs.",
|
||||
"backup-data": "Data (text)",
|
||||
"backup-storage": "Save to storage",
|
||||
"backup-now": "Backup now",
|
||||
"backup-done": "Backup completed",
|
||||
"backup-schedule": "Scheduled backups",
|
||||
"backup-frequency": "Frequency",
|
||||
"backup-frequency-off": "Off",
|
||||
"backup-frequency-daily": "Daily",
|
||||
"backup-frequency-weekly": "Weekly",
|
||||
"backup-frequency-monthly": "Monthly",
|
||||
"backup-time": "Time (HH:MM)",
|
||||
"backup-day-of-week": "Day of week (weekly)",
|
||||
"backup-day-of-month": "Day of month (monthly, 1-28)",
|
||||
"backup-restore": "Adfer",
|
||||
"backup-list": "List backups",
|
||||
"backup-datetime": "Date and time",
|
||||
"backup-path": "File path",
|
||||
"backup-restore-mode": "Restore mode",
|
||||
"backup-restore-add-missing": "Add missing data only",
|
||||
"backup-restore-replace-all": "Replace all data",
|
||||
"backup-restore-select-first": "Select a backup from the list first.",
|
||||
"backup-restore-confirm": "Restore the selected backup? Attachments/avatars are written to the filesystem and data to the database according to the chosen mode.",
|
||||
"gcs-project-id": "Project ID",
|
||||
"gcs-bucket": "Bucket",
|
||||
"gcs-key-filename": "Service account key file path",
|
||||
@@ -1986,8 +2051,6 @@
|
||||
"cpu-cores": "Craidd CPU",
|
||||
"cpu-usage": "Defnydd CPU",
|
||||
"current-action": "Gweithredu Presennol",
|
||||
"database-migration": "Mudo Cronfa Ddata",
|
||||
"database-migration-description": "Diweddaru strwythur cronfa ddata i wella ymarferoldeb a pherfformiad. Gall y broses hon gymryd sawl munud.",
|
||||
"database-migrations": "Mudo Cronfa Ddata",
|
||||
"days-old": "Dyddiau Hen",
|
||||
"duration": "Hyd",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accept": "Derbyn",
|
||||
"activity-changedTitle": "changed title to %s of %s",
|
||||
"activity-changedDescription": "changed description of %s",
|
||||
"act-deleteCard": "deleted card __card__ from list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-removeBoard": "deleted board __board__",
|
||||
"act-removeList": "deleted list __list__ at board __board__",
|
||||
@@ -441,6 +442,7 @@
|
||||
"createCustomField": "Creu Maes",
|
||||
"createCustomFieldPopup-title": "Creu Maes",
|
||||
"current": "presennol",
|
||||
"custom-color": "Custom color",
|
||||
"custom-field-delete-pop": "Does dim dadwneud. Bydd hyn yn dileu'r maes arfer hwn o bob cerdyn ac yn dinistrio ei hanes.",
|
||||
"custom-field-checkbox": "Blwch ticio",
|
||||
"custom-field-currency": "Arian cyfred",
|
||||
@@ -508,6 +510,7 @@
|
||||
"error-csv-schema": "Nid yw eich CSV(Comma Separated Values)/TSV (Tab Separated Values) yn cynnwys y wybodaeth gywir yn y fformat cywir",
|
||||
"error-import-empty-board": "This export contains no board content (no swimlanes, lists or cards). It may have been created by an older WeKan version with a known export bug. Please re-export the board with the current version and import the new file.",
|
||||
"error-list-doesNotExist": "Nid yw'r rhestr hon yn bodoli",
|
||||
"error-linked-card-not-allowed": "You can only link to a normal card. Linking to another linked card, or to a card that links back to this board, is not allowed because it would make the cards inaccessible.",
|
||||
"error-user-disabled": "This user account is disabled",
|
||||
"error-user-doesNotExist": "Nid yw'r defnyddiwr hwn yn bodoli",
|
||||
"error-user-notAllowSelf": "Ni allwch wahodd eich hun",
|
||||
@@ -581,6 +584,7 @@
|
||||
"fullname": "Enw Llawn",
|
||||
"header-logo-title": "Ewch yn ôl i dudalen eich byrddau.",
|
||||
"show-activities": "Gweithgareddau Sioe",
|
||||
"hide-activities": "Hide Activities",
|
||||
"headerBarCreateBoardPopup-title": "Creu Bwrdd",
|
||||
"home": "Cartref",
|
||||
"import": "Mewnforio",
|
||||
@@ -894,7 +898,12 @@
|
||||
"no-name": "(Anhysbys)",
|
||||
"Node_version": "Fersiwn nod",
|
||||
"Meteor_version": "Fersiwn Meteor",
|
||||
"MongoDB_version": "Fersiwn MongoDB",
|
||||
"Database_type": "Database type",
|
||||
"Database_commit": "Database commit",
|
||||
"FerretDB_version": "FerretDB version",
|
||||
"FerretDB_commit": "FerretDB commit",
|
||||
"Reactivity_mode": "Reactivity mode (changeStreams / oplog / polling)",
|
||||
"MongoDB_version": "MongoDB compatible version",
|
||||
"MongoDB_storage_engine": "Peiriant storio MongoDB",
|
||||
"MongoDB_Oplog_enabled": "MongoDB Oplog wedi'i alluogi",
|
||||
"OS_Arch": "AO Arch",
|
||||
@@ -1015,6 +1024,17 @@
|
||||
"r-workflow-help": "Drag a trigger and an action into the builder, then add the rule. Drag an action onto an existing rule to change what it does.",
|
||||
"r-drop-trigger": "drop a trigger here",
|
||||
"r-drop-action": "drop an action here",
|
||||
"r-w-card-created": "Card is created",
|
||||
"r-w-card-archived": "Card is archived",
|
||||
"r-w-card-unarchived": "Card is unarchived",
|
||||
"r-w-label-added": "Any label is added",
|
||||
"r-w-label-removed": "Any label is removed",
|
||||
"r-w-member-added": "A member is added",
|
||||
"r-w-member-removed": "A member is removed",
|
||||
"r-w-checklist-added": "A checklist is added",
|
||||
"r-w-attachment-added": "An attachment is added",
|
||||
"r-w-every-day-at": "Every day at __time__",
|
||||
"r-w-set-received-now": "Set received date to now",
|
||||
"r-export-json": "Export rules to JSON",
|
||||
"r-export-csv": "Export rules to CSV",
|
||||
"r-import-json": "Import rules from JSON",
|
||||
@@ -1068,6 +1088,12 @@
|
||||
"r-mark-incomplete": "Mark card incomplete",
|
||||
"r-move-all-cards": "Move all cards in list",
|
||||
"r-set-date-relative": "Set date relative to now",
|
||||
"r-later": "later",
|
||||
"r-unit-minutes": "minute(s)",
|
||||
"r-unit-hours": "hour(s)",
|
||||
"r-unit-days": "day(s)",
|
||||
"r-unit-weeks": "week(s)",
|
||||
"r-unit-months": "month(s)",
|
||||
"r-trigger": "Sbardun",
|
||||
"r-action": "Gweithred",
|
||||
"r-when-a-card": "Pan fydd cerdyn",
|
||||
@@ -1809,6 +1835,45 @@
|
||||
"azure-container": "Container",
|
||||
"gcs-storage": "Google Cloud Storage",
|
||||
"gcs-storage-description": "Google Cloud Storage.",
|
||||
"database-migration": "Database migration",
|
||||
"database-migration-description": "Migrate text-based data (everything except attachments and avatars, which stay on the filesystem) between MongoDB and FerretDB v1 (SQLite). The other database must be running and reachable (default ports: FerretDB mongodb://127.0.0.1:27018, MongoDB mongodb://127.0.0.1:27019; override with WEKAN_FERRETDB_URL / WEKAN_MONGODB_URL). After it finishes, point MONGO_URL at the other database and restart WeKan (on Snap: snap set wekan database=ferretdb or =mongodb).",
|
||||
"database-migrate-to-ferretdb": "Migrate text data to FerretDB v1 (SQLite)",
|
||||
"database-migrate-to-mongodb": "Migrate text data to MongoDB",
|
||||
"database-migration-confirm": "Copy all text-based collections to __db__? Attachments and avatars on the filesystem are not affected. The target database must be running.",
|
||||
"database-migration-phase": "Phase",
|
||||
"database-migration-done": "Text-data migration completed.",
|
||||
"collections": "collections",
|
||||
"features": "Features",
|
||||
"features-performance": "Performance",
|
||||
"cards-loading": "Card loading",
|
||||
"cards-loading-all": "All cards (default)",
|
||||
"cards-loading-lazy": "Lazy (only visible cards)",
|
||||
"cards-loading-description": "How each board loads its cards. All cards: every card is loaded into the browser (simplest, all features work, but a board with thousands of cards sends a lot of data to each viewer). Lazy: each list loads only the cards currently visible (infinite scroll) plus a live count, so very large boards use less memory and bandwidth. Also set by the CARDS_LOADING environment variable.",
|
||||
"cards-loading-lazy-note": "Lazy card loading is experimental: card counters and WIP limits are accurate, but the Calendar/Table/Gantt views and multi-select reflect only the cards loaded so far. Reload open boards after changing this.",
|
||||
"backup": "Backup",
|
||||
"backup-description": "Back up Attachments, Avatars and/or Data (all text-based collections that are not attachments or avatars). The .zip is streamed directly to the selected storage — no temporary file — as backup/YYYY/MM/DD/HH_MM_SS/backup.zip, containing YYYY_MM_DD-HH_MM_SS/attachments, /avatars and /data. Cloud storages (S3/MinIO, Azure, GCS) use the credentials configured in their storage tabs.",
|
||||
"backup-data": "Data (text)",
|
||||
"backup-storage": "Save to storage",
|
||||
"backup-now": "Backup now",
|
||||
"backup-done": "Backup completed",
|
||||
"backup-schedule": "Scheduled backups",
|
||||
"backup-frequency": "Frequency",
|
||||
"backup-frequency-off": "Off",
|
||||
"backup-frequency-daily": "Daily",
|
||||
"backup-frequency-weekly": "Weekly",
|
||||
"backup-frequency-monthly": "Monthly",
|
||||
"backup-time": "Time (HH:MM)",
|
||||
"backup-day-of-week": "Day of week (weekly)",
|
||||
"backup-day-of-month": "Day of month (monthly, 1-28)",
|
||||
"backup-restore": "Adfer",
|
||||
"backup-list": "List backups",
|
||||
"backup-datetime": "Date and time",
|
||||
"backup-path": "File path",
|
||||
"backup-restore-mode": "Restore mode",
|
||||
"backup-restore-add-missing": "Add missing data only",
|
||||
"backup-restore-replace-all": "Replace all data",
|
||||
"backup-restore-select-first": "Select a backup from the list first.",
|
||||
"backup-restore-confirm": "Restore the selected backup? Attachments/avatars are written to the filesystem and data to the database according to the chosen mode.",
|
||||
"gcs-project-id": "Project ID",
|
||||
"gcs-bucket": "Bucket",
|
||||
"gcs-key-filename": "Service account key file path",
|
||||
@@ -1986,8 +2051,6 @@
|
||||
"cpu-cores": "Craidd CPU",
|
||||
"cpu-usage": "Defnydd CPU",
|
||||
"current-action": "Gweithredu Presennol",
|
||||
"database-migration": "Mudo Cronfa Ddata",
|
||||
"database-migration-description": "Diweddaru strwythur cronfa ddata i wella ymarferoldeb a pherfformiad. Gall y broses hon gymryd sawl munud.",
|
||||
"database-migrations": "Mudo Cronfa Ddata",
|
||||
"days-old": "Dyddiau Hen",
|
||||
"duration": "Hyd",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accept": "Accepter",
|
||||
"activity-changedTitle": "ændrede titel til %s af %s",
|
||||
"activity-changedDescription": "changed description of %s",
|
||||
"act-deleteCard": "slettede kortet __card__ fra listen __list__ i svømmebanen __swimlane__ på tavlen __board__",
|
||||
"act-removeBoard": "slettede tavlen __board__",
|
||||
"act-removeList": "slettede listen __list__ fra tavlen __board__",
|
||||
@@ -441,6 +442,7 @@
|
||||
"createCustomField": "Opret felt",
|
||||
"createCustomFieldPopup-title": "Opret felt",
|
||||
"current": "nuværende",
|
||||
"custom-color": "Custom color",
|
||||
"custom-field-delete-pop": "Du kan ikke fortryde handlingen. Dette vil fjerne dette brugerdefinerede felt fra alle kort og tilintetgøre dens historik.",
|
||||
"custom-field-checkbox": "Afkrydsningsfelt",
|
||||
"custom-field-currency": "Valuta",
|
||||
@@ -508,6 +510,7 @@
|
||||
"error-csv-schema": "Din CSV (Comma Separated Values)/TSV (Tab Separated Values) indeholder ikke gyldig information i korrekt format",
|
||||
"error-import-empty-board": "Denne eksport indeholder intet tavleindhold (ingen svømmebaner, lister eller kort). Den kan være oprettet af en ældre WeKan-version med en kendt eksportfejl. Eksporter tavlen igen med den aktuelle version, og importer den nye fil.",
|
||||
"error-list-doesNotExist": "Listen findes ikke",
|
||||
"error-linked-card-not-allowed": "You can only link to a normal card. Linking to another linked card, or to a card that links back to this board, is not allowed because it would make the cards inaccessible.",
|
||||
"error-user-disabled": "This user account is disabled",
|
||||
"error-user-doesNotExist": "Brugeren findes ikke",
|
||||
"error-user-notAllowSelf": "Du kan ikke invitere dig selv",
|
||||
@@ -581,6 +584,7 @@
|
||||
"fullname": "Fuldt navn",
|
||||
"header-logo-title": "Gå tilbage til siden med dine tavler",
|
||||
"show-activities": "Visa aktiviteter",
|
||||
"hide-activities": "Hide Activities",
|
||||
"headerBarCreateBoardPopup-title": "Opret tavle",
|
||||
"home": "Hjem",
|
||||
"import": "Importér",
|
||||
@@ -894,7 +898,12 @@
|
||||
"no-name": "(Ukendt)",
|
||||
"Node_version": "Node-version",
|
||||
"Meteor_version": "Meteor-version",
|
||||
"MongoDB_version": "MongoDB-version",
|
||||
"Database_type": "Database type",
|
||||
"Database_commit": "Database commit",
|
||||
"FerretDB_version": "FerretDB version",
|
||||
"FerretDB_commit": "FerretDB commit",
|
||||
"Reactivity_mode": "Reactivity mode (changeStreams / oplog / polling)",
|
||||
"MongoDB_version": "MongoDB compatible version",
|
||||
"MongoDB_storage_engine": "Lagringsmotor for MongoDB",
|
||||
"MongoDB_Oplog_enabled": "MongoDB Oplog er slået til",
|
||||
"OS_Arch": "OS - Arch",
|
||||
@@ -1015,6 +1024,17 @@
|
||||
"r-workflow-help": "Træk en trigger og en handling ind i byggeren, og tilføj derefter reglen. Træk en handling over på en eksisterende regel for at ændre, hvad den gør.",
|
||||
"r-drop-trigger": "træk en udløser hertil",
|
||||
"r-drop-action": "træk en handling hertil",
|
||||
"r-w-card-created": "Card is created",
|
||||
"r-w-card-archived": "Card is archived",
|
||||
"r-w-card-unarchived": "Card is unarchived",
|
||||
"r-w-label-added": "Any label is added",
|
||||
"r-w-label-removed": "Any label is removed",
|
||||
"r-w-member-added": "A member is added",
|
||||
"r-w-member-removed": "A member is removed",
|
||||
"r-w-checklist-added": "A checklist is added",
|
||||
"r-w-attachment-added": "An attachment is added",
|
||||
"r-w-every-day-at": "Every day at __time__",
|
||||
"r-w-set-received-now": "Set received date to now",
|
||||
"r-export-json": "Eksporter regler til JSON",
|
||||
"r-export-csv": "Eksporter regler til CSV",
|
||||
"r-import-json": "Importer regler fra JSON",
|
||||
@@ -1068,6 +1088,12 @@
|
||||
"r-mark-incomplete": "Markér kort som ikke fuldført",
|
||||
"r-move-all-cards": "Flyt alle kort i en liste",
|
||||
"r-set-date-relative": "Angiv dato i forhold til dags dato",
|
||||
"r-later": "later",
|
||||
"r-unit-minutes": "minute(s)",
|
||||
"r-unit-hours": "hour(s)",
|
||||
"r-unit-days": "day(s)",
|
||||
"r-unit-weeks": "week(s)",
|
||||
"r-unit-months": "month(s)",
|
||||
"r-trigger": "Udløser",
|
||||
"r-action": "Handling",
|
||||
"r-when-a-card": "Når et kort",
|
||||
@@ -1809,6 +1835,45 @@
|
||||
"azure-container": "Blobcontainer",
|
||||
"gcs-storage": "Google Cloud Storage",
|
||||
"gcs-storage-description": "Google Cloud Storage.",
|
||||
"database-migration": "Database migration",
|
||||
"database-migration-description": "Migrate text-based data (everything except attachments and avatars, which stay on the filesystem) between MongoDB and FerretDB v1 (SQLite). The other database must be running and reachable (default ports: FerretDB mongodb://127.0.0.1:27018, MongoDB mongodb://127.0.0.1:27019; override with WEKAN_FERRETDB_URL / WEKAN_MONGODB_URL). After it finishes, point MONGO_URL at the other database and restart WeKan (on Snap: snap set wekan database=ferretdb or =mongodb).",
|
||||
"database-migrate-to-ferretdb": "Migrate text data to FerretDB v1 (SQLite)",
|
||||
"database-migrate-to-mongodb": "Migrate text data to MongoDB",
|
||||
"database-migration-confirm": "Copy all text-based collections to __db__? Attachments and avatars on the filesystem are not affected. The target database must be running.",
|
||||
"database-migration-phase": "Phase",
|
||||
"database-migration-done": "Text-data migration completed.",
|
||||
"collections": "collections",
|
||||
"features": "Features",
|
||||
"features-performance": "Performance",
|
||||
"cards-loading": "Card loading",
|
||||
"cards-loading-all": "All cards (default)",
|
||||
"cards-loading-lazy": "Lazy (only visible cards)",
|
||||
"cards-loading-description": "How each board loads its cards. All cards: every card is loaded into the browser (simplest, all features work, but a board with thousands of cards sends a lot of data to each viewer). Lazy: each list loads only the cards currently visible (infinite scroll) plus a live count, so very large boards use less memory and bandwidth. Also set by the CARDS_LOADING environment variable.",
|
||||
"cards-loading-lazy-note": "Lazy card loading is experimental: card counters and WIP limits are accurate, but the Calendar/Table/Gantt views and multi-select reflect only the cards loaded so far. Reload open boards after changing this.",
|
||||
"backup": "Backup",
|
||||
"backup-description": "Back up Attachments, Avatars and/or Data (all text-based collections that are not attachments or avatars). The .zip is streamed directly to the selected storage — no temporary file — as backup/YYYY/MM/DD/HH_MM_SS/backup.zip, containing YYYY_MM_DD-HH_MM_SS/attachments, /avatars and /data. Cloud storages (S3/MinIO, Azure, GCS) use the credentials configured in their storage tabs.",
|
||||
"backup-data": "Data (text)",
|
||||
"backup-storage": "Save to storage",
|
||||
"backup-now": "Backup now",
|
||||
"backup-done": "Backup completed",
|
||||
"backup-schedule": "Scheduled backups",
|
||||
"backup-frequency": "Frequency",
|
||||
"backup-frequency-off": "Off",
|
||||
"backup-frequency-daily": "Daily",
|
||||
"backup-frequency-weekly": "Weekly",
|
||||
"backup-frequency-monthly": "Monthly",
|
||||
"backup-time": "Time (HH:MM)",
|
||||
"backup-day-of-week": "Day of week (weekly)",
|
||||
"backup-day-of-month": "Day of month (monthly, 1-28)",
|
||||
"backup-restore": "Genskab",
|
||||
"backup-list": "List backups",
|
||||
"backup-datetime": "Date and time",
|
||||
"backup-path": "File path",
|
||||
"backup-restore-mode": "Restore mode",
|
||||
"backup-restore-add-missing": "Add missing data only",
|
||||
"backup-restore-replace-all": "Replace all data",
|
||||
"backup-restore-select-first": "Select a backup from the list first.",
|
||||
"backup-restore-confirm": "Restore the selected backup? Attachments/avatars are written to the filesystem and data to the database according to the chosen mode.",
|
||||
"gcs-project-id": "Projekt ID",
|
||||
"gcs-bucket": "Bucket",
|
||||
"gcs-key-filename": "Sti til servicekontoens nøglefil",
|
||||
@@ -1986,8 +2051,6 @@
|
||||
"cpu-cores": "CPU-kärnor",
|
||||
"cpu-usage": "CPU-användning",
|
||||
"current-action": "Nuvarande åtgärd",
|
||||
"database-migration": "Databasmigrering",
|
||||
"database-migration-description": "Uppdaterar databasstrukturen för förbättrad funktionalitet och prestanda. Denna process kan ta flera minuter.",
|
||||
"database-migrations": "Databasmigreringar",
|
||||
"days-old": "Dagar gammal",
|
||||
"duration": "Varaktighet",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accept": "Akzeptieren",
|
||||
"activity-changedTitle": "changed title to %s of %s",
|
||||
"activity-changedDescription": "changed description of %s",
|
||||
"act-deleteCard": "deleted card __card__ from list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-removeBoard": "deleted board __board__",
|
||||
"act-removeList": "deleted list __list__ at board __board__",
|
||||
@@ -441,6 +442,7 @@
|
||||
"createCustomField": "Feld erstellen",
|
||||
"createCustomFieldPopup-title": "Feld erstellen",
|
||||
"current": "aktuell",
|
||||
"custom-color": "Custom color",
|
||||
"custom-field-delete-pop": "Dies wird das Feld aus allen Karten entfernen und den dazugehörigen Verlauf löschen. Die Aktion kann nicht rückgängig gemacht werden.",
|
||||
"custom-field-checkbox": "Kontrollkästchen",
|
||||
"custom-field-currency": "Währung",
|
||||
@@ -508,6 +510,7 @@
|
||||
"error-csv-schema": "Deine CSV(Kommatgetrennte Werte)/TSV (Tab getrennte Werte) enthält keine korrekten Informationen im geforderten Format ",
|
||||
"error-import-empty-board": "This export contains no board content (no swimlanes, lists or cards). It may have been created by an older WeKan version with a known export bug. Please re-export the board with the current version and import the new file.",
|
||||
"error-list-doesNotExist": "Diese Liste existiert nicht",
|
||||
"error-linked-card-not-allowed": "You can only link to a normal card. Linking to another linked card, or to a card that links back to this board, is not allowed because it would make the cards inaccessible.",
|
||||
"error-user-disabled": "This user account is disabled",
|
||||
"error-user-doesNotExist": "Dieser Nutzer existiert nicht",
|
||||
"error-user-notAllowSelf": "Sie können sich nicht selbst einladen.",
|
||||
@@ -581,6 +584,7 @@
|
||||
"fullname": "Vollständiger Name",
|
||||
"header-logo-title": "Zurück zur Board Seite.",
|
||||
"show-activities": "Aktivitäten anzeigen",
|
||||
"hide-activities": "Hide Activities",
|
||||
"headerBarCreateBoardPopup-title": "Board erstellen",
|
||||
"home": "Home",
|
||||
"import": "Importieren",
|
||||
@@ -894,7 +898,12 @@
|
||||
"no-name": "(Unbekannt)",
|
||||
"Node_version": "Node-Version",
|
||||
"Meteor_version": "Meteor-Version",
|
||||
"MongoDB_version": "MongoDB-Version",
|
||||
"Database_type": "Database type",
|
||||
"Database_commit": "Database commit",
|
||||
"FerretDB_version": "FerretDB version",
|
||||
"FerretDB_commit": "FerretDB commit",
|
||||
"Reactivity_mode": "Reactivity mode (changeStreams / oplog / polling)",
|
||||
"MongoDB_version": "MongoDB compatible version",
|
||||
"MongoDB_storage_engine": "MongoDB-Speicher-Engine",
|
||||
"MongoDB_Oplog_enabled": "MongoDB-Oplog aktiviert",
|
||||
"OS_Arch": "Betriebssystem-Architektur",
|
||||
@@ -1015,6 +1024,17 @@
|
||||
"r-workflow-help": "Drag a trigger and an action into the builder, then add the rule. Drag an action onto an existing rule to change what it does.",
|
||||
"r-drop-trigger": "drop a trigger here",
|
||||
"r-drop-action": "drop an action here",
|
||||
"r-w-card-created": "Card is created",
|
||||
"r-w-card-archived": "Card is archived",
|
||||
"r-w-card-unarchived": "Card is unarchived",
|
||||
"r-w-label-added": "Any label is added",
|
||||
"r-w-label-removed": "Any label is removed",
|
||||
"r-w-member-added": "A member is added",
|
||||
"r-w-member-removed": "A member is removed",
|
||||
"r-w-checklist-added": "A checklist is added",
|
||||
"r-w-attachment-added": "An attachment is added",
|
||||
"r-w-every-day-at": "Every day at __time__",
|
||||
"r-w-set-received-now": "Set received date to now",
|
||||
"r-export-json": "Export rules to JSON",
|
||||
"r-export-csv": "Export rules to CSV",
|
||||
"r-import-json": "Import rules from JSON",
|
||||
@@ -1068,6 +1088,12 @@
|
||||
"r-mark-incomplete": "Mark card incomplete",
|
||||
"r-move-all-cards": "Move all cards in list",
|
||||
"r-set-date-relative": "Set date relative to now",
|
||||
"r-later": "later",
|
||||
"r-unit-minutes": "minute(s)",
|
||||
"r-unit-hours": "hour(s)",
|
||||
"r-unit-days": "day(s)",
|
||||
"r-unit-weeks": "week(s)",
|
||||
"r-unit-months": "month(s)",
|
||||
"r-trigger": "Auslöser",
|
||||
"r-action": "Aktion",
|
||||
"r-when-a-card": "Wenn Karte",
|
||||
@@ -1809,6 +1835,45 @@
|
||||
"azure-container": "Container",
|
||||
"gcs-storage": "Google Cloud Storage",
|
||||
"gcs-storage-description": "Google Cloud Storage.",
|
||||
"database-migration": "Database migration",
|
||||
"database-migration-description": "Migrate text-based data (everything except attachments and avatars, which stay on the filesystem) between MongoDB and FerretDB v1 (SQLite). The other database must be running and reachable (default ports: FerretDB mongodb://127.0.0.1:27018, MongoDB mongodb://127.0.0.1:27019; override with WEKAN_FERRETDB_URL / WEKAN_MONGODB_URL). After it finishes, point MONGO_URL at the other database and restart WeKan (on Snap: snap set wekan database=ferretdb or =mongodb).",
|
||||
"database-migrate-to-ferretdb": "Migrate text data to FerretDB v1 (SQLite)",
|
||||
"database-migrate-to-mongodb": "Migrate text data to MongoDB",
|
||||
"database-migration-confirm": "Copy all text-based collections to __db__? Attachments and avatars on the filesystem are not affected. The target database must be running.",
|
||||
"database-migration-phase": "Phase",
|
||||
"database-migration-done": "Text-data migration completed.",
|
||||
"collections": "collections",
|
||||
"features": "Features",
|
||||
"features-performance": "Performance",
|
||||
"cards-loading": "Card loading",
|
||||
"cards-loading-all": "All cards (default)",
|
||||
"cards-loading-lazy": "Lazy (only visible cards)",
|
||||
"cards-loading-description": "How each board loads its cards. All cards: every card is loaded into the browser (simplest, all features work, but a board with thousands of cards sends a lot of data to each viewer). Lazy: each list loads only the cards currently visible (infinite scroll) plus a live count, so very large boards use less memory and bandwidth. Also set by the CARDS_LOADING environment variable.",
|
||||
"cards-loading-lazy-note": "Lazy card loading is experimental: card counters and WIP limits are accurate, but the Calendar/Table/Gantt views and multi-select reflect only the cards loaded so far. Reload open boards after changing this.",
|
||||
"backup": "Backup",
|
||||
"backup-description": "Back up Attachments, Avatars and/or Data (all text-based collections that are not attachments or avatars). The .zip is streamed directly to the selected storage — no temporary file — as backup/YYYY/MM/DD/HH_MM_SS/backup.zip, containing YYYY_MM_DD-HH_MM_SS/attachments, /avatars and /data. Cloud storages (S3/MinIO, Azure, GCS) use the credentials configured in their storage tabs.",
|
||||
"backup-data": "Data (text)",
|
||||
"backup-storage": "Save to storage",
|
||||
"backup-now": "Backup now",
|
||||
"backup-done": "Backup completed",
|
||||
"backup-schedule": "Scheduled backups",
|
||||
"backup-frequency": "Frequency",
|
||||
"backup-frequency-off": "Off",
|
||||
"backup-frequency-daily": "Daily",
|
||||
"backup-frequency-weekly": "Weekly",
|
||||
"backup-frequency-monthly": "Monthly",
|
||||
"backup-time": "Time (HH:MM)",
|
||||
"backup-day-of-week": "Day of week (weekly)",
|
||||
"backup-day-of-month": "Day of month (monthly, 1-28)",
|
||||
"backup-restore": "Wiederherstellen",
|
||||
"backup-list": "List backups",
|
||||
"backup-datetime": "Date and time",
|
||||
"backup-path": "File path",
|
||||
"backup-restore-mode": "Restore mode",
|
||||
"backup-restore-add-missing": "Add missing data only",
|
||||
"backup-restore-replace-all": "Replace all data",
|
||||
"backup-restore-select-first": "Select a backup from the list first.",
|
||||
"backup-restore-confirm": "Restore the selected backup? Attachments/avatars are written to the filesystem and data to the database according to the chosen mode.",
|
||||
"gcs-project-id": "Project ID",
|
||||
"gcs-bucket": "Bucket",
|
||||
"gcs-key-filename": "Service account key file path",
|
||||
@@ -1986,8 +2051,6 @@
|
||||
"cpu-cores": "CPU Cores",
|
||||
"cpu-usage": "CPU Usage",
|
||||
"current-action": "Laufender Vorgang",
|
||||
"database-migration": "Datenbankmigration",
|
||||
"database-migration-description": "Update der Datenbankstruktur für eine Verbesserung der Funktionalität und der Performanz. Das kann ein paar Minuten dauern.",
|
||||
"database-migrations": "Datenbankmigration",
|
||||
"days-old": "Tage alt",
|
||||
"duration": "Dauer",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accept": "Akzeptieren",
|
||||
"activity-changedTitle": "changed title to %s of %s",
|
||||
"activity-changedDescription": "changed description of %s",
|
||||
"act-deleteCard": "deleted card __card__ from list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-removeBoard": "deleted board __board__",
|
||||
"act-removeList": "deleted list __list__ at board __board__",
|
||||
@@ -441,6 +442,7 @@
|
||||
"createCustomField": "Feld erstellen",
|
||||
"createCustomFieldPopup-title": "Feld erstellen",
|
||||
"current": "aktuell",
|
||||
"custom-color": "Custom color",
|
||||
"custom-field-delete-pop": "Dies wird das Feld aus allen Karten entfernen und den dazugehörigen Verlauf löschen. Die Aktion kann nicht rückgängig gemacht werden.",
|
||||
"custom-field-checkbox": "Kontrollkästchen",
|
||||
"custom-field-currency": "Währung",
|
||||
@@ -508,6 +510,7 @@
|
||||
"error-csv-schema": "Deine CSV(Kommatgetrennte Werte)/TSV (Tab getrennte Werte) enthält keine korrekten Informationen im geforderten Format ",
|
||||
"error-import-empty-board": "This export contains no board content (no swimlanes, lists or cards). It may have been created by an older WeKan version with a known export bug. Please re-export the board with the current version and import the new file.",
|
||||
"error-list-doesNotExist": "Diese Liste existiert nicht",
|
||||
"error-linked-card-not-allowed": "You can only link to a normal card. Linking to another linked card, or to a card that links back to this board, is not allowed because it would make the cards inaccessible.",
|
||||
"error-user-disabled": "This user account is disabled",
|
||||
"error-user-doesNotExist": "Dieser Nutzer existiert nicht",
|
||||
"error-user-notAllowSelf": "Sie können sich nicht selbst einladen.",
|
||||
@@ -581,6 +584,7 @@
|
||||
"fullname": "Vollständiger Name",
|
||||
"header-logo-title": "Zurück zur Board Seite.",
|
||||
"show-activities": "Aktivitäten anzeigen",
|
||||
"hide-activities": "Hide Activities",
|
||||
"headerBarCreateBoardPopup-title": "Board erstellen",
|
||||
"home": "Home",
|
||||
"import": "Importieren",
|
||||
@@ -894,7 +898,12 @@
|
||||
"no-name": "(Unbekannt)",
|
||||
"Node_version": "Node-Version",
|
||||
"Meteor_version": "Meteor-Version",
|
||||
"MongoDB_version": "MongoDB-Version",
|
||||
"Database_type": "Database type",
|
||||
"Database_commit": "Database commit",
|
||||
"FerretDB_version": "FerretDB version",
|
||||
"FerretDB_commit": "FerretDB commit",
|
||||
"Reactivity_mode": "Reactivity mode (changeStreams / oplog / polling)",
|
||||
"MongoDB_version": "MongoDB compatible version",
|
||||
"MongoDB_storage_engine": "MongoDB-Speicher-Engine",
|
||||
"MongoDB_Oplog_enabled": "MongoDB-Oplog aktiviert",
|
||||
"OS_Arch": "Betriebssystem-Architektur",
|
||||
@@ -1015,6 +1024,17 @@
|
||||
"r-workflow-help": "Drag a trigger and an action into the builder, then add the rule. Drag an action onto an existing rule to change what it does.",
|
||||
"r-drop-trigger": "drop a trigger here",
|
||||
"r-drop-action": "drop an action here",
|
||||
"r-w-card-created": "Card is created",
|
||||
"r-w-card-archived": "Card is archived",
|
||||
"r-w-card-unarchived": "Card is unarchived",
|
||||
"r-w-label-added": "Any label is added",
|
||||
"r-w-label-removed": "Any label is removed",
|
||||
"r-w-member-added": "A member is added",
|
||||
"r-w-member-removed": "A member is removed",
|
||||
"r-w-checklist-added": "A checklist is added",
|
||||
"r-w-attachment-added": "An attachment is added",
|
||||
"r-w-every-day-at": "Every day at __time__",
|
||||
"r-w-set-received-now": "Set received date to now",
|
||||
"r-export-json": "Export rules to JSON",
|
||||
"r-export-csv": "Export rules to CSV",
|
||||
"r-import-json": "Import rules from JSON",
|
||||
@@ -1068,6 +1088,12 @@
|
||||
"r-mark-incomplete": "Mark card incomplete",
|
||||
"r-move-all-cards": "Move all cards in list",
|
||||
"r-set-date-relative": "Set date relative to now",
|
||||
"r-later": "later",
|
||||
"r-unit-minutes": "minute(s)",
|
||||
"r-unit-hours": "hour(s)",
|
||||
"r-unit-days": "day(s)",
|
||||
"r-unit-weeks": "week(s)",
|
||||
"r-unit-months": "month(s)",
|
||||
"r-trigger": "Auslöser",
|
||||
"r-action": "Aktion",
|
||||
"r-when-a-card": "Wenn Karte",
|
||||
@@ -1809,6 +1835,45 @@
|
||||
"azure-container": "Container",
|
||||
"gcs-storage": "Google Cloud Storage",
|
||||
"gcs-storage-description": "Google Cloud Storage.",
|
||||
"database-migration": "Database migration",
|
||||
"database-migration-description": "Migrate text-based data (everything except attachments and avatars, which stay on the filesystem) between MongoDB and FerretDB v1 (SQLite). The other database must be running and reachable (default ports: FerretDB mongodb://127.0.0.1:27018, MongoDB mongodb://127.0.0.1:27019; override with WEKAN_FERRETDB_URL / WEKAN_MONGODB_URL). After it finishes, point MONGO_URL at the other database and restart WeKan (on Snap: snap set wekan database=ferretdb or =mongodb).",
|
||||
"database-migrate-to-ferretdb": "Migrate text data to FerretDB v1 (SQLite)",
|
||||
"database-migrate-to-mongodb": "Migrate text data to MongoDB",
|
||||
"database-migration-confirm": "Copy all text-based collections to __db__? Attachments and avatars on the filesystem are not affected. The target database must be running.",
|
||||
"database-migration-phase": "Phase",
|
||||
"database-migration-done": "Text-data migration completed.",
|
||||
"collections": "collections",
|
||||
"features": "Features",
|
||||
"features-performance": "Performance",
|
||||
"cards-loading": "Card loading",
|
||||
"cards-loading-all": "All cards (default)",
|
||||
"cards-loading-lazy": "Lazy (only visible cards)",
|
||||
"cards-loading-description": "How each board loads its cards. All cards: every card is loaded into the browser (simplest, all features work, but a board with thousands of cards sends a lot of data to each viewer). Lazy: each list loads only the cards currently visible (infinite scroll) plus a live count, so very large boards use less memory and bandwidth. Also set by the CARDS_LOADING environment variable.",
|
||||
"cards-loading-lazy-note": "Lazy card loading is experimental: card counters and WIP limits are accurate, but the Calendar/Table/Gantt views and multi-select reflect only the cards loaded so far. Reload open boards after changing this.",
|
||||
"backup": "Backup",
|
||||
"backup-description": "Back up Attachments, Avatars and/or Data (all text-based collections that are not attachments or avatars). The .zip is streamed directly to the selected storage — no temporary file — as backup/YYYY/MM/DD/HH_MM_SS/backup.zip, containing YYYY_MM_DD-HH_MM_SS/attachments, /avatars and /data. Cloud storages (S3/MinIO, Azure, GCS) use the credentials configured in their storage tabs.",
|
||||
"backup-data": "Data (text)",
|
||||
"backup-storage": "Save to storage",
|
||||
"backup-now": "Backup now",
|
||||
"backup-done": "Backup completed",
|
||||
"backup-schedule": "Scheduled backups",
|
||||
"backup-frequency": "Frequency",
|
||||
"backup-frequency-off": "Off",
|
||||
"backup-frequency-daily": "Daily",
|
||||
"backup-frequency-weekly": "Weekly",
|
||||
"backup-frequency-monthly": "Monthly",
|
||||
"backup-time": "Time (HH:MM)",
|
||||
"backup-day-of-week": "Day of week (weekly)",
|
||||
"backup-day-of-month": "Day of month (monthly, 1-28)",
|
||||
"backup-restore": "Wiederherstellen",
|
||||
"backup-list": "List backups",
|
||||
"backup-datetime": "Date and time",
|
||||
"backup-path": "File path",
|
||||
"backup-restore-mode": "Restore mode",
|
||||
"backup-restore-add-missing": "Add missing data only",
|
||||
"backup-restore-replace-all": "Replace all data",
|
||||
"backup-restore-select-first": "Select a backup from the list first.",
|
||||
"backup-restore-confirm": "Restore the selected backup? Attachments/avatars are written to the filesystem and data to the database according to the chosen mode.",
|
||||
"gcs-project-id": "Project ID",
|
||||
"gcs-bucket": "Bucket",
|
||||
"gcs-key-filename": "Service account key file path",
|
||||
@@ -1986,8 +2051,6 @@
|
||||
"cpu-cores": "CPU Cores",
|
||||
"cpu-usage": "CPU Usage",
|
||||
"current-action": "Laufender Vorgang",
|
||||
"database-migration": "Datenbankmigration",
|
||||
"database-migration-description": "Update der Datenbankstruktur für eine Verbesserung der Funktionalität und der Performanz. Das kann ein paar Minuten dauern.",
|
||||
"database-migrations": "Datenbankmigration",
|
||||
"days-old": "Tage alt",
|
||||
"duration": "Dauer",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accept": "Akzeptieren",
|
||||
"activity-changedTitle": "changed title to %s of %s",
|
||||
"activity-changedDescription": "changed description of %s",
|
||||
"act-deleteCard": "deleted card __card__ from list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-removeBoard": "deleted board __board__",
|
||||
"act-removeList": "deleted list __list__ at board __board__",
|
||||
@@ -441,6 +442,7 @@
|
||||
"createCustomField": "Feld erstellen",
|
||||
"createCustomFieldPopup-title": "Feld erstellen",
|
||||
"current": "aktuell",
|
||||
"custom-color": "Custom color",
|
||||
"custom-field-delete-pop": "Dies wird das Feld aus allen Karten entfernen und den dazugehörigen Verlauf löschen. Die Aktion kann nicht rückgängig gemacht werden.",
|
||||
"custom-field-checkbox": "Kontrollkästchen",
|
||||
"custom-field-currency": "Währung",
|
||||
@@ -508,6 +510,7 @@
|
||||
"error-csv-schema": "Deine CSV(Kommatgetrennte Werte)/TSV (Tab getrennte Werte) enthält keine korrekten Informationen im geforderten Format ",
|
||||
"error-import-empty-board": "This export contains no board content (no swimlanes, lists or cards). It may have been created by an older WeKan version with a known export bug. Please re-export the board with the current version and import the new file.",
|
||||
"error-list-doesNotExist": "Diese Liste existiert nicht",
|
||||
"error-linked-card-not-allowed": "You can only link to a normal card. Linking to another linked card, or to a card that links back to this board, is not allowed because it would make the cards inaccessible.",
|
||||
"error-user-disabled": "This user account is disabled",
|
||||
"error-user-doesNotExist": "Dieser Nutzer existiert nicht",
|
||||
"error-user-notAllowSelf": "Sie können sich nicht selbst einladen.",
|
||||
@@ -581,6 +584,7 @@
|
||||
"fullname": "Vollständiger Name",
|
||||
"header-logo-title": "Zurück zur Board Seite.",
|
||||
"show-activities": "Aktivitäten anzeigen",
|
||||
"hide-activities": "Hide Activities",
|
||||
"headerBarCreateBoardPopup-title": "Board erstellen",
|
||||
"home": "Home",
|
||||
"import": "Importieren",
|
||||
@@ -894,7 +898,12 @@
|
||||
"no-name": "(Unbekannt)",
|
||||
"Node_version": "Node-Version",
|
||||
"Meteor_version": "Meteor-Version",
|
||||
"MongoDB_version": "MongoDB-Version",
|
||||
"Database_type": "Database type",
|
||||
"Database_commit": "Database commit",
|
||||
"FerretDB_version": "FerretDB version",
|
||||
"FerretDB_commit": "FerretDB commit",
|
||||
"Reactivity_mode": "Reactivity mode (changeStreams / oplog / polling)",
|
||||
"MongoDB_version": "MongoDB compatible version",
|
||||
"MongoDB_storage_engine": "MongoDB-Speicher-Engine",
|
||||
"MongoDB_Oplog_enabled": "MongoDB-Oplog aktiviert",
|
||||
"OS_Arch": "Betriebssystem-Architektur",
|
||||
@@ -1015,6 +1024,17 @@
|
||||
"r-workflow-help": "Drag a trigger and an action into the builder, then add the rule. Drag an action onto an existing rule to change what it does.",
|
||||
"r-drop-trigger": "drop a trigger here",
|
||||
"r-drop-action": "drop an action here",
|
||||
"r-w-card-created": "Card is created",
|
||||
"r-w-card-archived": "Card is archived",
|
||||
"r-w-card-unarchived": "Card is unarchived",
|
||||
"r-w-label-added": "Any label is added",
|
||||
"r-w-label-removed": "Any label is removed",
|
||||
"r-w-member-added": "A member is added",
|
||||
"r-w-member-removed": "A member is removed",
|
||||
"r-w-checklist-added": "A checklist is added",
|
||||
"r-w-attachment-added": "An attachment is added",
|
||||
"r-w-every-day-at": "Every day at __time__",
|
||||
"r-w-set-received-now": "Set received date to now",
|
||||
"r-export-json": "Export rules to JSON",
|
||||
"r-export-csv": "Export rules to CSV",
|
||||
"r-import-json": "Import rules from JSON",
|
||||
@@ -1068,6 +1088,12 @@
|
||||
"r-mark-incomplete": "Mark card incomplete",
|
||||
"r-move-all-cards": "Move all cards in list",
|
||||
"r-set-date-relative": "Set date relative to now",
|
||||
"r-later": "later",
|
||||
"r-unit-minutes": "minute(s)",
|
||||
"r-unit-hours": "hour(s)",
|
||||
"r-unit-days": "day(s)",
|
||||
"r-unit-weeks": "week(s)",
|
||||
"r-unit-months": "month(s)",
|
||||
"r-trigger": "Auslöser",
|
||||
"r-action": "Aktion",
|
||||
"r-when-a-card": "Wenn Karte",
|
||||
@@ -1809,6 +1835,45 @@
|
||||
"azure-container": "Container",
|
||||
"gcs-storage": "Google Cloud Storage",
|
||||
"gcs-storage-description": "Google Cloud Storage.",
|
||||
"database-migration": "Database migration",
|
||||
"database-migration-description": "Migrate text-based data (everything except attachments and avatars, which stay on the filesystem) between MongoDB and FerretDB v1 (SQLite). The other database must be running and reachable (default ports: FerretDB mongodb://127.0.0.1:27018, MongoDB mongodb://127.0.0.1:27019; override with WEKAN_FERRETDB_URL / WEKAN_MONGODB_URL). After it finishes, point MONGO_URL at the other database and restart WeKan (on Snap: snap set wekan database=ferretdb or =mongodb).",
|
||||
"database-migrate-to-ferretdb": "Migrate text data to FerretDB v1 (SQLite)",
|
||||
"database-migrate-to-mongodb": "Migrate text data to MongoDB",
|
||||
"database-migration-confirm": "Copy all text-based collections to __db__? Attachments and avatars on the filesystem are not affected. The target database must be running.",
|
||||
"database-migration-phase": "Phase",
|
||||
"database-migration-done": "Text-data migration completed.",
|
||||
"collections": "collections",
|
||||
"features": "Features",
|
||||
"features-performance": "Performance",
|
||||
"cards-loading": "Card loading",
|
||||
"cards-loading-all": "All cards (default)",
|
||||
"cards-loading-lazy": "Lazy (only visible cards)",
|
||||
"cards-loading-description": "How each board loads its cards. All cards: every card is loaded into the browser (simplest, all features work, but a board with thousands of cards sends a lot of data to each viewer). Lazy: each list loads only the cards currently visible (infinite scroll) plus a live count, so very large boards use less memory and bandwidth. Also set by the CARDS_LOADING environment variable.",
|
||||
"cards-loading-lazy-note": "Lazy card loading is experimental: card counters and WIP limits are accurate, but the Calendar/Table/Gantt views and multi-select reflect only the cards loaded so far. Reload open boards after changing this.",
|
||||
"backup": "Backup",
|
||||
"backup-description": "Back up Attachments, Avatars and/or Data (all text-based collections that are not attachments or avatars). The .zip is streamed directly to the selected storage — no temporary file — as backup/YYYY/MM/DD/HH_MM_SS/backup.zip, containing YYYY_MM_DD-HH_MM_SS/attachments, /avatars and /data. Cloud storages (S3/MinIO, Azure, GCS) use the credentials configured in their storage tabs.",
|
||||
"backup-data": "Data (text)",
|
||||
"backup-storage": "Save to storage",
|
||||
"backup-now": "Backup now",
|
||||
"backup-done": "Backup completed",
|
||||
"backup-schedule": "Scheduled backups",
|
||||
"backup-frequency": "Frequency",
|
||||
"backup-frequency-off": "Off",
|
||||
"backup-frequency-daily": "Daily",
|
||||
"backup-frequency-weekly": "Weekly",
|
||||
"backup-frequency-monthly": "Monthly",
|
||||
"backup-time": "Time (HH:MM)",
|
||||
"backup-day-of-week": "Day of week (weekly)",
|
||||
"backup-day-of-month": "Day of month (monthly, 1-28)",
|
||||
"backup-restore": "Wiederherstellen",
|
||||
"backup-list": "List backups",
|
||||
"backup-datetime": "Date and time",
|
||||
"backup-path": "File path",
|
||||
"backup-restore-mode": "Restore mode",
|
||||
"backup-restore-add-missing": "Add missing data only",
|
||||
"backup-restore-replace-all": "Replace all data",
|
||||
"backup-restore-select-first": "Select a backup from the list first.",
|
||||
"backup-restore-confirm": "Restore the selected backup? Attachments/avatars are written to the filesystem and data to the database according to the chosen mode.",
|
||||
"gcs-project-id": "Project ID",
|
||||
"gcs-bucket": "Bucket",
|
||||
"gcs-key-filename": "Service account key file path",
|
||||
@@ -1986,8 +2051,6 @@
|
||||
"cpu-cores": "CPU Cores",
|
||||
"cpu-usage": "CPU Usage",
|
||||
"current-action": "Laufender Vorgang",
|
||||
"database-migration": "Datenbankmigration",
|
||||
"database-migration-description": "Update der Datenbankstruktur für eine Verbesserung der Funktionalität und der Performanz. Das kann ein paar Minuten dauern.",
|
||||
"database-migrations": "Datenbankmigration",
|
||||
"days-old": "Tage alt",
|
||||
"duration": "Dauer",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accept": "Akzeptieren",
|
||||
"activity-changedTitle": "changed title to %s of %s",
|
||||
"activity-changedDescription": "changed description of %s",
|
||||
"act-deleteCard": "deleted card __card__ from list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-removeBoard": "deleted board __board__",
|
||||
"act-removeList": "deleted list __list__ at board __board__",
|
||||
@@ -441,6 +442,7 @@
|
||||
"createCustomField": "Feld erstellen",
|
||||
"createCustomFieldPopup-title": "Feld erstellen",
|
||||
"current": "aktuell",
|
||||
"custom-color": "Custom color",
|
||||
"custom-field-delete-pop": "Dies wird das Feld aus allen Karten entfernen und den dazugehörigen Verlauf löschen. Die Aktion kann nicht rückgängig gemacht werden.",
|
||||
"custom-field-checkbox": "Kontrollkästchen",
|
||||
"custom-field-currency": "Währung",
|
||||
@@ -508,6 +510,7 @@
|
||||
"error-csv-schema": "Deine CSV(Kommatgetrennte Werte)/TSV (Tab getrennte Werte) enthält keine korrekten Informationen im geforderten Format ",
|
||||
"error-import-empty-board": "This export contains no board content (no swimlanes, lists or cards). It may have been created by an older WeKan version with a known export bug. Please re-export the board with the current version and import the new file.",
|
||||
"error-list-doesNotExist": "Diese Liste existiert nicht",
|
||||
"error-linked-card-not-allowed": "You can only link to a normal card. Linking to another linked card, or to a card that links back to this board, is not allowed because it would make the cards inaccessible.",
|
||||
"error-user-disabled": "This user account is disabled",
|
||||
"error-user-doesNotExist": "Dieser Nutzer existiert nicht",
|
||||
"error-user-notAllowSelf": "Sie können sich nicht selbst einladen.",
|
||||
@@ -581,6 +584,7 @@
|
||||
"fullname": "Vollständiger Name",
|
||||
"header-logo-title": "Zurück zur Board Seite.",
|
||||
"show-activities": "Aktivitäten anzeigen",
|
||||
"hide-activities": "Hide Activities",
|
||||
"headerBarCreateBoardPopup-title": "Board erstellen",
|
||||
"home": "Home",
|
||||
"import": "Importieren",
|
||||
@@ -894,7 +898,12 @@
|
||||
"no-name": "(Unbekannt)",
|
||||
"Node_version": "Node-Version",
|
||||
"Meteor_version": "Meteor-Version",
|
||||
"MongoDB_version": "MongoDB-Version",
|
||||
"Database_type": "Database type",
|
||||
"Database_commit": "Database commit",
|
||||
"FerretDB_version": "FerretDB version",
|
||||
"FerretDB_commit": "FerretDB commit",
|
||||
"Reactivity_mode": "Reactivity mode (changeStreams / oplog / polling)",
|
||||
"MongoDB_version": "MongoDB compatible version",
|
||||
"MongoDB_storage_engine": "MongoDB-Speicher-Engine",
|
||||
"MongoDB_Oplog_enabled": "MongoDB-Oplog aktiviert",
|
||||
"OS_Arch": "Betriebssystem-Architektur",
|
||||
@@ -1015,6 +1024,17 @@
|
||||
"r-workflow-help": "Drag a trigger and an action into the builder, then add the rule. Drag an action onto an existing rule to change what it does.",
|
||||
"r-drop-trigger": "drop a trigger here",
|
||||
"r-drop-action": "drop an action here",
|
||||
"r-w-card-created": "Card is created",
|
||||
"r-w-card-archived": "Card is archived",
|
||||
"r-w-card-unarchived": "Card is unarchived",
|
||||
"r-w-label-added": "Any label is added",
|
||||
"r-w-label-removed": "Any label is removed",
|
||||
"r-w-member-added": "A member is added",
|
||||
"r-w-member-removed": "A member is removed",
|
||||
"r-w-checklist-added": "A checklist is added",
|
||||
"r-w-attachment-added": "An attachment is added",
|
||||
"r-w-every-day-at": "Every day at __time__",
|
||||
"r-w-set-received-now": "Set received date to now",
|
||||
"r-export-json": "Export rules to JSON",
|
||||
"r-export-csv": "Export rules to CSV",
|
||||
"r-import-json": "Import rules from JSON",
|
||||
@@ -1068,6 +1088,12 @@
|
||||
"r-mark-incomplete": "Mark card incomplete",
|
||||
"r-move-all-cards": "Move all cards in list",
|
||||
"r-set-date-relative": "Set date relative to now",
|
||||
"r-later": "later",
|
||||
"r-unit-minutes": "minute(s)",
|
||||
"r-unit-hours": "hour(s)",
|
||||
"r-unit-days": "day(s)",
|
||||
"r-unit-weeks": "week(s)",
|
||||
"r-unit-months": "month(s)",
|
||||
"r-trigger": "Auslöser",
|
||||
"r-action": "Aktion",
|
||||
"r-when-a-card": "Wenn Karte",
|
||||
@@ -1809,6 +1835,45 @@
|
||||
"azure-container": "Container",
|
||||
"gcs-storage": "Google Cloud Storage",
|
||||
"gcs-storage-description": "Google Cloud Storage.",
|
||||
"database-migration": "Database migration",
|
||||
"database-migration-description": "Migrate text-based data (everything except attachments and avatars, which stay on the filesystem) between MongoDB and FerretDB v1 (SQLite). The other database must be running and reachable (default ports: FerretDB mongodb://127.0.0.1:27018, MongoDB mongodb://127.0.0.1:27019; override with WEKAN_FERRETDB_URL / WEKAN_MONGODB_URL). After it finishes, point MONGO_URL at the other database and restart WeKan (on Snap: snap set wekan database=ferretdb or =mongodb).",
|
||||
"database-migrate-to-ferretdb": "Migrate text data to FerretDB v1 (SQLite)",
|
||||
"database-migrate-to-mongodb": "Migrate text data to MongoDB",
|
||||
"database-migration-confirm": "Copy all text-based collections to __db__? Attachments and avatars on the filesystem are not affected. The target database must be running.",
|
||||
"database-migration-phase": "Phase",
|
||||
"database-migration-done": "Text-data migration completed.",
|
||||
"collections": "collections",
|
||||
"features": "Features",
|
||||
"features-performance": "Performance",
|
||||
"cards-loading": "Card loading",
|
||||
"cards-loading-all": "All cards (default)",
|
||||
"cards-loading-lazy": "Lazy (only visible cards)",
|
||||
"cards-loading-description": "How each board loads its cards. All cards: every card is loaded into the browser (simplest, all features work, but a board with thousands of cards sends a lot of data to each viewer). Lazy: each list loads only the cards currently visible (infinite scroll) plus a live count, so very large boards use less memory and bandwidth. Also set by the CARDS_LOADING environment variable.",
|
||||
"cards-loading-lazy-note": "Lazy card loading is experimental: card counters and WIP limits are accurate, but the Calendar/Table/Gantt views and multi-select reflect only the cards loaded so far. Reload open boards after changing this.",
|
||||
"backup": "Backup",
|
||||
"backup-description": "Back up Attachments, Avatars and/or Data (all text-based collections that are not attachments or avatars). The .zip is streamed directly to the selected storage — no temporary file — as backup/YYYY/MM/DD/HH_MM_SS/backup.zip, containing YYYY_MM_DD-HH_MM_SS/attachments, /avatars and /data. Cloud storages (S3/MinIO, Azure, GCS) use the credentials configured in their storage tabs.",
|
||||
"backup-data": "Data (text)",
|
||||
"backup-storage": "Save to storage",
|
||||
"backup-now": "Backup now",
|
||||
"backup-done": "Backup completed",
|
||||
"backup-schedule": "Scheduled backups",
|
||||
"backup-frequency": "Frequency",
|
||||
"backup-frequency-off": "Off",
|
||||
"backup-frequency-daily": "Daily",
|
||||
"backup-frequency-weekly": "Weekly",
|
||||
"backup-frequency-monthly": "Monthly",
|
||||
"backup-time": "Time (HH:MM)",
|
||||
"backup-day-of-week": "Day of week (weekly)",
|
||||
"backup-day-of-month": "Day of month (monthly, 1-28)",
|
||||
"backup-restore": "Wiederherstellen",
|
||||
"backup-list": "List backups",
|
||||
"backup-datetime": "Date and time",
|
||||
"backup-path": "File path",
|
||||
"backup-restore-mode": "Restore mode",
|
||||
"backup-restore-add-missing": "Add missing data only",
|
||||
"backup-restore-replace-all": "Replace all data",
|
||||
"backup-restore-select-first": "Select a backup from the list first.",
|
||||
"backup-restore-confirm": "Restore the selected backup? Attachments/avatars are written to the filesystem and data to the database according to the chosen mode.",
|
||||
"gcs-project-id": "Project ID",
|
||||
"gcs-bucket": "Bucket",
|
||||
"gcs-key-filename": "Service account key file path",
|
||||
@@ -1986,8 +2051,6 @@
|
||||
"cpu-cores": "CPU Cores",
|
||||
"cpu-usage": "CPU Usage",
|
||||
"current-action": "Laufender Vorgang",
|
||||
"database-migration": "Datenbankmigration",
|
||||
"database-migration-description": "Update der Datenbankstruktur für eine Verbesserung der Funktionalität und der Performanz. Das kann ein paar Minuten dauern.",
|
||||
"database-migrations": "Datenbankmigration",
|
||||
"days-old": "Tage alt",
|
||||
"duration": "Dauer",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accept": "Αποδοχή",
|
||||
"activity-changedTitle": "changed title to %s of %s",
|
||||
"activity-changedDescription": "changed description of %s",
|
||||
"act-deleteCard": "deleted card __card__ from list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-removeBoard": "deleted board __board__",
|
||||
"act-removeList": "deleted list __list__ at board __board__",
|
||||
@@ -441,6 +442,7 @@
|
||||
"createCustomField": "Δημιουργία Πεδίου",
|
||||
"createCustomFieldPopup-title": "Δημιουργία Πεδίου",
|
||||
"current": "τρέχων",
|
||||
"custom-color": "Custom color",
|
||||
"custom-field-delete-pop": "Non potrai tornare indietro. Questa azione rimuoverà questo campo personalizzato da tutte le schede ed eliminerà ogni sua traccia.",
|
||||
"custom-field-checkbox": "Casella di scelta",
|
||||
"custom-field-currency": "Νόμισμα",
|
||||
@@ -508,6 +510,7 @@
|
||||
"error-csv-schema": "Il tuo file CSV (Comma Separated Values) / TSV (Tab Separated Values) non contiene le giuste informazioni nel formato corretto",
|
||||
"error-import-empty-board": "This export contains no board content (no swimlanes, lists or cards). It may have been created by an older WeKan version with a known export bug. Please re-export the board with the current version and import the new file.",
|
||||
"error-list-doesNotExist": "Η λίστα δεν υπάρχει",
|
||||
"error-linked-card-not-allowed": "You can only link to a normal card. Linking to another linked card, or to a card that links back to this board, is not allowed because it would make the cards inaccessible.",
|
||||
"error-user-disabled": "This user account is disabled",
|
||||
"error-user-doesNotExist": "Ο χρήστης δεν υπάρχει",
|
||||
"error-user-notAllowSelf": "Δε μπορείτε να αυτοπροσκληθείτε",
|
||||
@@ -581,6 +584,7 @@
|
||||
"fullname": "Πλήρες Όνομα",
|
||||
"header-logo-title": "Επιστροφή στη σελίδα των πινάκων σας.",
|
||||
"show-activities": "Mostra Attività",
|
||||
"hide-activities": "Hide Activities",
|
||||
"headerBarCreateBoardPopup-title": "Δημιουργία Πίνακα",
|
||||
"home": "Αρχική",
|
||||
"import": "Εισαγωγή",
|
||||
@@ -894,7 +898,12 @@
|
||||
"no-name": "(Άγνωστο)",
|
||||
"Node_version": "Έκδοση Node",
|
||||
"Meteor_version": "Έκδοση Meteor",
|
||||
"MongoDB_version": "Έκδοση MongoDB",
|
||||
"Database_type": "Database type",
|
||||
"Database_commit": "Database commit",
|
||||
"FerretDB_version": "FerretDB version",
|
||||
"FerretDB_commit": "FerretDB commit",
|
||||
"Reactivity_mode": "Reactivity mode (changeStreams / oplog / polling)",
|
||||
"MongoDB_version": "MongoDB compatible version",
|
||||
"MongoDB_storage_engine": "Storage engine di MongoDB",
|
||||
"MongoDB_Oplog_enabled": "MongoDB Oplog ενεργοποιημένο",
|
||||
"OS_Arch": "Architettura SO",
|
||||
@@ -1015,6 +1024,17 @@
|
||||
"r-workflow-help": "Drag a trigger and an action into the builder, then add the rule. Drag an action onto an existing rule to change what it does.",
|
||||
"r-drop-trigger": "drop a trigger here",
|
||||
"r-drop-action": "drop an action here",
|
||||
"r-w-card-created": "Card is created",
|
||||
"r-w-card-archived": "Card is archived",
|
||||
"r-w-card-unarchived": "Card is unarchived",
|
||||
"r-w-label-added": "Any label is added",
|
||||
"r-w-label-removed": "Any label is removed",
|
||||
"r-w-member-added": "A member is added",
|
||||
"r-w-member-removed": "A member is removed",
|
||||
"r-w-checklist-added": "A checklist is added",
|
||||
"r-w-attachment-added": "An attachment is added",
|
||||
"r-w-every-day-at": "Every day at __time__",
|
||||
"r-w-set-received-now": "Set received date to now",
|
||||
"r-export-json": "Export rules to JSON",
|
||||
"r-export-csv": "Export rules to CSV",
|
||||
"r-import-json": "Import rules from JSON",
|
||||
@@ -1068,6 +1088,12 @@
|
||||
"r-mark-incomplete": "Mark card incomplete",
|
||||
"r-move-all-cards": "Move all cards in list",
|
||||
"r-set-date-relative": "Set date relative to now",
|
||||
"r-later": "later",
|
||||
"r-unit-minutes": "minute(s)",
|
||||
"r-unit-hours": "hour(s)",
|
||||
"r-unit-days": "day(s)",
|
||||
"r-unit-weeks": "week(s)",
|
||||
"r-unit-months": "month(s)",
|
||||
"r-trigger": "Déclencheur",
|
||||
"r-action": "Ενέργεια",
|
||||
"r-when-a-card": "Όταν μία κάρτα",
|
||||
@@ -1809,6 +1835,45 @@
|
||||
"azure-container": "Container",
|
||||
"gcs-storage": "Google Cloud Storage",
|
||||
"gcs-storage-description": "Google Cloud Storage.",
|
||||
"database-migration": "Database migration",
|
||||
"database-migration-description": "Migrate text-based data (everything except attachments and avatars, which stay on the filesystem) between MongoDB and FerretDB v1 (SQLite). The other database must be running and reachable (default ports: FerretDB mongodb://127.0.0.1:27018, MongoDB mongodb://127.0.0.1:27019; override with WEKAN_FERRETDB_URL / WEKAN_MONGODB_URL). After it finishes, point MONGO_URL at the other database and restart WeKan (on Snap: snap set wekan database=ferretdb or =mongodb).",
|
||||
"database-migrate-to-ferretdb": "Migrate text data to FerretDB v1 (SQLite)",
|
||||
"database-migrate-to-mongodb": "Migrate text data to MongoDB",
|
||||
"database-migration-confirm": "Copy all text-based collections to __db__? Attachments and avatars on the filesystem are not affected. The target database must be running.",
|
||||
"database-migration-phase": "Phase",
|
||||
"database-migration-done": "Text-data migration completed.",
|
||||
"collections": "collections",
|
||||
"features": "Features",
|
||||
"features-performance": "Performance",
|
||||
"cards-loading": "Card loading",
|
||||
"cards-loading-all": "All cards (default)",
|
||||
"cards-loading-lazy": "Lazy (only visible cards)",
|
||||
"cards-loading-description": "How each board loads its cards. All cards: every card is loaded into the browser (simplest, all features work, but a board with thousands of cards sends a lot of data to each viewer). Lazy: each list loads only the cards currently visible (infinite scroll) plus a live count, so very large boards use less memory and bandwidth. Also set by the CARDS_LOADING environment variable.",
|
||||
"cards-loading-lazy-note": "Lazy card loading is experimental: card counters and WIP limits are accurate, but the Calendar/Table/Gantt views and multi-select reflect only the cards loaded so far. Reload open boards after changing this.",
|
||||
"backup": "Backup",
|
||||
"backup-description": "Back up Attachments, Avatars and/or Data (all text-based collections that are not attachments or avatars). The .zip is streamed directly to the selected storage — no temporary file — as backup/YYYY/MM/DD/HH_MM_SS/backup.zip, containing YYYY_MM_DD-HH_MM_SS/attachments, /avatars and /data. Cloud storages (S3/MinIO, Azure, GCS) use the credentials configured in their storage tabs.",
|
||||
"backup-data": "Data (text)",
|
||||
"backup-storage": "Save to storage",
|
||||
"backup-now": "Backup now",
|
||||
"backup-done": "Backup completed",
|
||||
"backup-schedule": "Scheduled backups",
|
||||
"backup-frequency": "Frequency",
|
||||
"backup-frequency-off": "Off",
|
||||
"backup-frequency-daily": "Daily",
|
||||
"backup-frequency-weekly": "Weekly",
|
||||
"backup-frequency-monthly": "Monthly",
|
||||
"backup-time": "Time (HH:MM)",
|
||||
"backup-day-of-week": "Day of week (weekly)",
|
||||
"backup-day-of-month": "Day of month (monthly, 1-28)",
|
||||
"backup-restore": "Επαναφορά",
|
||||
"backup-list": "List backups",
|
||||
"backup-datetime": "Date and time",
|
||||
"backup-path": "File path",
|
||||
"backup-restore-mode": "Restore mode",
|
||||
"backup-restore-add-missing": "Add missing data only",
|
||||
"backup-restore-replace-all": "Replace all data",
|
||||
"backup-restore-select-first": "Select a backup from the list first.",
|
||||
"backup-restore-confirm": "Restore the selected backup? Attachments/avatars are written to the filesystem and data to the database according to the chosen mode.",
|
||||
"gcs-project-id": "Project ID",
|
||||
"gcs-bucket": "Bucket",
|
||||
"gcs-key-filename": "Service account key file path",
|
||||
@@ -1986,8 +2051,6 @@
|
||||
"cpu-cores": "Cœurs du CPU ",
|
||||
"cpu-usage": "Utilisation du CPU",
|
||||
"current-action": "Current Action",
|
||||
"database-migration": "Migration base de données",
|
||||
"database-migration-description": "Updating database structure for improved functionality and performance. This process may take several minutes.",
|
||||
"database-migrations": "Migrations base de données",
|
||||
"days-old": "Jours d'ancienneté",
|
||||
"duration": "Durée",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accept": "Αποδοχή",
|
||||
"activity-changedTitle": "changed title to %s of %s",
|
||||
"activity-changedDescription": "changed description of %s",
|
||||
"act-deleteCard": "deleted card __card__ from list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-removeBoard": "deleted board __board__",
|
||||
"act-removeList": "deleted list __list__ at board __board__",
|
||||
@@ -441,6 +442,7 @@
|
||||
"createCustomField": "Δημιουργία Πεδίου",
|
||||
"createCustomFieldPopup-title": "Δημιουργία Πεδίου",
|
||||
"current": "τρέχων",
|
||||
"custom-color": "Custom color",
|
||||
"custom-field-delete-pop": "Non potrai tornare indietro. Questa azione rimuoverà questo campo personalizzato da tutte le schede ed eliminerà ogni sua traccia.",
|
||||
"custom-field-checkbox": "Casella di scelta",
|
||||
"custom-field-currency": "Νόμισμα",
|
||||
@@ -508,6 +510,7 @@
|
||||
"error-csv-schema": "Il tuo file CSV (Comma Separated Values) / TSV (Tab Separated Values) non contiene le giuste informazioni nel formato corretto",
|
||||
"error-import-empty-board": "This export contains no board content (no swimlanes, lists or cards). It may have been created by an older WeKan version with a known export bug. Please re-export the board with the current version and import the new file.",
|
||||
"error-list-doesNotExist": "Η λίστα δεν υπάρχει",
|
||||
"error-linked-card-not-allowed": "You can only link to a normal card. Linking to another linked card, or to a card that links back to this board, is not allowed because it would make the cards inaccessible.",
|
||||
"error-user-disabled": "This user account is disabled",
|
||||
"error-user-doesNotExist": "Ο χρήστης δεν υπάρχει",
|
||||
"error-user-notAllowSelf": "Δε μπορείτε να αυτοπροσκληθείτε",
|
||||
@@ -581,6 +584,7 @@
|
||||
"fullname": "Πλήρες Όνομα",
|
||||
"header-logo-title": "Επιστροφή στη σελίδα των πινάκων σας.",
|
||||
"show-activities": "Mostra Attività",
|
||||
"hide-activities": "Hide Activities",
|
||||
"headerBarCreateBoardPopup-title": "Δημιουργία Πίνακα",
|
||||
"home": "Αρχική",
|
||||
"import": "Εισαγωγή",
|
||||
@@ -894,7 +898,12 @@
|
||||
"no-name": "(Άγνωστο)",
|
||||
"Node_version": "Έκδοση Node",
|
||||
"Meteor_version": "Έκδοση Meteor",
|
||||
"MongoDB_version": "Έκδοση MongoDB",
|
||||
"Database_type": "Database type",
|
||||
"Database_commit": "Database commit",
|
||||
"FerretDB_version": "FerretDB version",
|
||||
"FerretDB_commit": "FerretDB commit",
|
||||
"Reactivity_mode": "Reactivity mode (changeStreams / oplog / polling)",
|
||||
"MongoDB_version": "MongoDB compatible version",
|
||||
"MongoDB_storage_engine": "Storage engine di MongoDB",
|
||||
"MongoDB_Oplog_enabled": "MongoDB Oplog ενεργοποιημένο",
|
||||
"OS_Arch": "Architettura SO",
|
||||
@@ -1015,6 +1024,17 @@
|
||||
"r-workflow-help": "Drag a trigger and an action into the builder, then add the rule. Drag an action onto an existing rule to change what it does.",
|
||||
"r-drop-trigger": "drop a trigger here",
|
||||
"r-drop-action": "drop an action here",
|
||||
"r-w-card-created": "Card is created",
|
||||
"r-w-card-archived": "Card is archived",
|
||||
"r-w-card-unarchived": "Card is unarchived",
|
||||
"r-w-label-added": "Any label is added",
|
||||
"r-w-label-removed": "Any label is removed",
|
||||
"r-w-member-added": "A member is added",
|
||||
"r-w-member-removed": "A member is removed",
|
||||
"r-w-checklist-added": "A checklist is added",
|
||||
"r-w-attachment-added": "An attachment is added",
|
||||
"r-w-every-day-at": "Every day at __time__",
|
||||
"r-w-set-received-now": "Set received date to now",
|
||||
"r-export-json": "Export rules to JSON",
|
||||
"r-export-csv": "Export rules to CSV",
|
||||
"r-import-json": "Import rules from JSON",
|
||||
@@ -1068,6 +1088,12 @@
|
||||
"r-mark-incomplete": "Mark card incomplete",
|
||||
"r-move-all-cards": "Move all cards in list",
|
||||
"r-set-date-relative": "Set date relative to now",
|
||||
"r-later": "later",
|
||||
"r-unit-minutes": "minute(s)",
|
||||
"r-unit-hours": "hour(s)",
|
||||
"r-unit-days": "day(s)",
|
||||
"r-unit-weeks": "week(s)",
|
||||
"r-unit-months": "month(s)",
|
||||
"r-trigger": "Déclencheur",
|
||||
"r-action": "Ενέργεια",
|
||||
"r-when-a-card": "Όταν μία κάρτα",
|
||||
@@ -1809,6 +1835,45 @@
|
||||
"azure-container": "Container",
|
||||
"gcs-storage": "Google Cloud Storage",
|
||||
"gcs-storage-description": "Google Cloud Storage.",
|
||||
"database-migration": "Database migration",
|
||||
"database-migration-description": "Migrate text-based data (everything except attachments and avatars, which stay on the filesystem) between MongoDB and FerretDB v1 (SQLite). The other database must be running and reachable (default ports: FerretDB mongodb://127.0.0.1:27018, MongoDB mongodb://127.0.0.1:27019; override with WEKAN_FERRETDB_URL / WEKAN_MONGODB_URL). After it finishes, point MONGO_URL at the other database and restart WeKan (on Snap: snap set wekan database=ferretdb or =mongodb).",
|
||||
"database-migrate-to-ferretdb": "Migrate text data to FerretDB v1 (SQLite)",
|
||||
"database-migrate-to-mongodb": "Migrate text data to MongoDB",
|
||||
"database-migration-confirm": "Copy all text-based collections to __db__? Attachments and avatars on the filesystem are not affected. The target database must be running.",
|
||||
"database-migration-phase": "Phase",
|
||||
"database-migration-done": "Text-data migration completed.",
|
||||
"collections": "collections",
|
||||
"features": "Features",
|
||||
"features-performance": "Performance",
|
||||
"cards-loading": "Card loading",
|
||||
"cards-loading-all": "All cards (default)",
|
||||
"cards-loading-lazy": "Lazy (only visible cards)",
|
||||
"cards-loading-description": "How each board loads its cards. All cards: every card is loaded into the browser (simplest, all features work, but a board with thousands of cards sends a lot of data to each viewer). Lazy: each list loads only the cards currently visible (infinite scroll) plus a live count, so very large boards use less memory and bandwidth. Also set by the CARDS_LOADING environment variable.",
|
||||
"cards-loading-lazy-note": "Lazy card loading is experimental: card counters and WIP limits are accurate, but the Calendar/Table/Gantt views and multi-select reflect only the cards loaded so far. Reload open boards after changing this.",
|
||||
"backup": "Backup",
|
||||
"backup-description": "Back up Attachments, Avatars and/or Data (all text-based collections that are not attachments or avatars). The .zip is streamed directly to the selected storage — no temporary file — as backup/YYYY/MM/DD/HH_MM_SS/backup.zip, containing YYYY_MM_DD-HH_MM_SS/attachments, /avatars and /data. Cloud storages (S3/MinIO, Azure, GCS) use the credentials configured in their storage tabs.",
|
||||
"backup-data": "Data (text)",
|
||||
"backup-storage": "Save to storage",
|
||||
"backup-now": "Backup now",
|
||||
"backup-done": "Backup completed",
|
||||
"backup-schedule": "Scheduled backups",
|
||||
"backup-frequency": "Frequency",
|
||||
"backup-frequency-off": "Off",
|
||||
"backup-frequency-daily": "Daily",
|
||||
"backup-frequency-weekly": "Weekly",
|
||||
"backup-frequency-monthly": "Monthly",
|
||||
"backup-time": "Time (HH:MM)",
|
||||
"backup-day-of-week": "Day of week (weekly)",
|
||||
"backup-day-of-month": "Day of month (monthly, 1-28)",
|
||||
"backup-restore": "Επαναφορά",
|
||||
"backup-list": "List backups",
|
||||
"backup-datetime": "Date and time",
|
||||
"backup-path": "File path",
|
||||
"backup-restore-mode": "Restore mode",
|
||||
"backup-restore-add-missing": "Add missing data only",
|
||||
"backup-restore-replace-all": "Replace all data",
|
||||
"backup-restore-select-first": "Select a backup from the list first.",
|
||||
"backup-restore-confirm": "Restore the selected backup? Attachments/avatars are written to the filesystem and data to the database according to the chosen mode.",
|
||||
"gcs-project-id": "Project ID",
|
||||
"gcs-bucket": "Bucket",
|
||||
"gcs-key-filename": "Service account key file path",
|
||||
@@ -1986,8 +2051,6 @@
|
||||
"cpu-cores": "Cœurs du CPU ",
|
||||
"cpu-usage": "Utilisation du CPU",
|
||||
"current-action": "Current Action",
|
||||
"database-migration": "Migration base de données",
|
||||
"database-migration-description": "Updating database structure for improved functionality and performance. This process may take several minutes.",
|
||||
"database-migrations": "Migrations base de données",
|
||||
"days-old": "Jours d'ancienneté",
|
||||
"duration": "Durée",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accept": "Accept",
|
||||
"activity-changedTitle": "changed title to %s of %s",
|
||||
"activity-changedDescription": "changed description of %s",
|
||||
"act-deleteCard": "deleted card __card__ from list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-removeBoard": "deleted board __board__",
|
||||
"act-removeList": "deleted list __list__ at board __board__",
|
||||
@@ -441,6 +442,7 @@
|
||||
"createCustomField": "Create Field",
|
||||
"createCustomFieldPopup-title": "Create Field",
|
||||
"current": "current",
|
||||
"custom-color": "Custom color",
|
||||
"custom-field-delete-pop": "There is no undo. This will remove this custom field from all cards and destroy its history.",
|
||||
"custom-field-checkbox": "Checkbox",
|
||||
"custom-field-currency": "Currency",
|
||||
@@ -508,6 +510,7 @@
|
||||
"error-csv-schema": "Your CSV(Comma Separated Values)/TSV (Tab Separated Values) does not include the proper information in the correct format ",
|
||||
"error-import-empty-board": "This export contains no board content (no swimlanes, lists or cards). It may have been created by an older WeKan version with a known export bug. Please re-export the board with the current version and import the new file.",
|
||||
"error-list-doesNotExist": "This list does not exist",
|
||||
"error-linked-card-not-allowed": "You can only link to a normal card. Linking to another linked card, or to a card that links back to this board, is not allowed because it would make the cards inaccessible.",
|
||||
"error-user-disabled": "This user account is disabled",
|
||||
"error-user-doesNotExist": "This user does not exist",
|
||||
"error-user-notAllowSelf": "You can not invite yourself",
|
||||
@@ -581,6 +584,7 @@
|
||||
"fullname": "Full Name",
|
||||
"header-logo-title": "Go back to your boards page.",
|
||||
"show-activities": "Show Activities",
|
||||
"hide-activities": "Hide Activities",
|
||||
"headerBarCreateBoardPopup-title": "Create Board",
|
||||
"home": "Home",
|
||||
"import": "Import",
|
||||
@@ -894,7 +898,12 @@
|
||||
"no-name": "(Unknown)",
|
||||
"Node_version": "Node version",
|
||||
"Meteor_version": "Meteor version",
|
||||
"MongoDB_version": "MongoDB version",
|
||||
"Database_type": "Database type",
|
||||
"Database_commit": "Database commit",
|
||||
"FerretDB_version": "FerretDB version",
|
||||
"FerretDB_commit": "FerretDB commit",
|
||||
"Reactivity_mode": "Reactivity mode (changeStreams / oplog / polling)",
|
||||
"MongoDB_version": "MongoDB compatible version",
|
||||
"MongoDB_storage_engine": "MongoDB storage engine",
|
||||
"MongoDB_Oplog_enabled": "MongoDB Oplog enabled",
|
||||
"OS_Arch": "OS Arch",
|
||||
@@ -1015,6 +1024,17 @@
|
||||
"r-workflow-help": "Drag a trigger and an action into the builder, then add the rule. Drag an action onto an existing rule to change what it does.",
|
||||
"r-drop-trigger": "drop a trigger here",
|
||||
"r-drop-action": "drop an action here",
|
||||
"r-w-card-created": "Card is created",
|
||||
"r-w-card-archived": "Card is archived",
|
||||
"r-w-card-unarchived": "Card is unarchived",
|
||||
"r-w-label-added": "Any label is added",
|
||||
"r-w-label-removed": "Any label is removed",
|
||||
"r-w-member-added": "A member is added",
|
||||
"r-w-member-removed": "A member is removed",
|
||||
"r-w-checklist-added": "A checklist is added",
|
||||
"r-w-attachment-added": "An attachment is added",
|
||||
"r-w-every-day-at": "Every day at __time__",
|
||||
"r-w-set-received-now": "Set received date to now",
|
||||
"r-export-json": "Export rules to JSON",
|
||||
"r-export-csv": "Export rules to CSV",
|
||||
"r-import-json": "Import rules from JSON",
|
||||
@@ -1068,6 +1088,12 @@
|
||||
"r-mark-incomplete": "Mark card incomplete",
|
||||
"r-move-all-cards": "Move all cards in list",
|
||||
"r-set-date-relative": "Set date relative to now",
|
||||
"r-later": "later",
|
||||
"r-unit-minutes": "minute(s)",
|
||||
"r-unit-hours": "hour(s)",
|
||||
"r-unit-days": "day(s)",
|
||||
"r-unit-weeks": "week(s)",
|
||||
"r-unit-months": "month(s)",
|
||||
"r-trigger": "Trigger",
|
||||
"r-action": "Action",
|
||||
"r-when-a-card": "When a card",
|
||||
@@ -1809,6 +1835,45 @@
|
||||
"azure-container": "Container",
|
||||
"gcs-storage": "Google Cloud Storage",
|
||||
"gcs-storage-description": "Google Cloud Storage.",
|
||||
"database-migration": "Database migration",
|
||||
"database-migration-description": "Migrate text-based data (everything except attachments and avatars, which stay on the filesystem) between MongoDB and FerretDB v1 (SQLite). The other database must be running and reachable (default ports: FerretDB mongodb://127.0.0.1:27018, MongoDB mongodb://127.0.0.1:27019; override with WEKAN_FERRETDB_URL / WEKAN_MONGODB_URL). After it finishes, point MONGO_URL at the other database and restart WeKan (on Snap: snap set wekan database=ferretdb or =mongodb).",
|
||||
"database-migrate-to-ferretdb": "Migrate text data to FerretDB v1 (SQLite)",
|
||||
"database-migrate-to-mongodb": "Migrate text data to MongoDB",
|
||||
"database-migration-confirm": "Copy all text-based collections to __db__? Attachments and avatars on the filesystem are not affected. The target database must be running.",
|
||||
"database-migration-phase": "Phase",
|
||||
"database-migration-done": "Text-data migration completed.",
|
||||
"collections": "collections",
|
||||
"features": "Features",
|
||||
"features-performance": "Performance",
|
||||
"cards-loading": "Card loading",
|
||||
"cards-loading-all": "All cards (default)",
|
||||
"cards-loading-lazy": "Lazy (only visible cards)",
|
||||
"cards-loading-description": "How each board loads its cards. All cards: every card is loaded into the browser (simplest, all features work, but a board with thousands of cards sends a lot of data to each viewer). Lazy: each list loads only the cards currently visible (infinite scroll) plus a live count, so very large boards use less memory and bandwidth. Also set by the CARDS_LOADING environment variable.",
|
||||
"cards-loading-lazy-note": "Lazy card loading is experimental: card counters and WIP limits are accurate, but the Calendar/Table/Gantt views and multi-select reflect only the cards loaded so far. Reload open boards after changing this.",
|
||||
"backup": "Backup",
|
||||
"backup-description": "Back up Attachments, Avatars and/or Data (all text-based collections that are not attachments or avatars). The .zip is streamed directly to the selected storage — no temporary file — as backup/YYYY/MM/DD/HH_MM_SS/backup.zip, containing YYYY_MM_DD-HH_MM_SS/attachments, /avatars and /data. Cloud storages (S3/MinIO, Azure, GCS) use the credentials configured in their storage tabs.",
|
||||
"backup-data": "Data (text)",
|
||||
"backup-storage": "Save to storage",
|
||||
"backup-now": "Backup now",
|
||||
"backup-done": "Backup completed",
|
||||
"backup-schedule": "Scheduled backups",
|
||||
"backup-frequency": "Frequency",
|
||||
"backup-frequency-off": "Off",
|
||||
"backup-frequency-daily": "Daily",
|
||||
"backup-frequency-weekly": "Weekly",
|
||||
"backup-frequency-monthly": "Monthly",
|
||||
"backup-time": "Time (HH:MM)",
|
||||
"backup-day-of-week": "Day of week (weekly)",
|
||||
"backup-day-of-month": "Day of month (monthly, 1-28)",
|
||||
"backup-restore": "Restore",
|
||||
"backup-list": "List backups",
|
||||
"backup-datetime": "Date and time",
|
||||
"backup-path": "File path",
|
||||
"backup-restore-mode": "Restore mode",
|
||||
"backup-restore-add-missing": "Add missing data only",
|
||||
"backup-restore-replace-all": "Replace all data",
|
||||
"backup-restore-select-first": "Select a backup from the list first.",
|
||||
"backup-restore-confirm": "Restore the selected backup? Attachments/avatars are written to the filesystem and data to the database according to the chosen mode.",
|
||||
"gcs-project-id": "Project ID",
|
||||
"gcs-bucket": "Bucket",
|
||||
"gcs-key-filename": "Service account key file path",
|
||||
@@ -1986,8 +2051,6 @@
|
||||
"cpu-cores": "CPU Cores",
|
||||
"cpu-usage": "CPU Usage",
|
||||
"current-action": "Current Action",
|
||||
"database-migration": "Database Migration",
|
||||
"database-migration-description": "Updating database structure for improved functionality and performance. This process may take several minutes.",
|
||||
"database-migrations": "Database Migrations",
|
||||
"days-old": "Days Old",
|
||||
"duration": "Duration",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accept": "Accept",
|
||||
"activity-changedTitle": "changed title to %s of %s",
|
||||
"activity-changedDescription": "changed description of %s",
|
||||
"act-deleteCard": "deleted card __card__ from list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-removeBoard": "deleted board __board__",
|
||||
"act-removeList": "deleted list __list__ at board __board__",
|
||||
@@ -441,6 +442,7 @@
|
||||
"createCustomField": "Create Field",
|
||||
"createCustomFieldPopup-title": "Create Field",
|
||||
"current": "current",
|
||||
"custom-color": "Custom color",
|
||||
"custom-field-delete-pop": "There is no undo. This will remove this custom field from all cards and destroy its history.",
|
||||
"custom-field-checkbox": "Checkbox",
|
||||
"custom-field-currency": "Currency",
|
||||
@@ -508,6 +510,7 @@
|
||||
"error-csv-schema": "Your CSV(Comma Separated Values)/TSV (Tab Separated Values) does not include the proper information in the correct format ",
|
||||
"error-import-empty-board": "This export contains no board content (no swimlanes, lists or cards). It may have been created by an older WeKan version with a known export bug. Please re-export the board with the current version and import the new file.",
|
||||
"error-list-doesNotExist": "This list does not exist",
|
||||
"error-linked-card-not-allowed": "You can only link to a normal card. Linking to another linked card, or to a card that links back to this board, is not allowed because it would make the cards inaccessible.",
|
||||
"error-user-disabled": "This user account is disabled",
|
||||
"error-user-doesNotExist": "This user does not exist",
|
||||
"error-user-notAllowSelf": "You can not invite yourself",
|
||||
@@ -581,6 +584,7 @@
|
||||
"fullname": "Full Name",
|
||||
"header-logo-title": "Go back to your boards page.",
|
||||
"show-activities": "Show Activities",
|
||||
"hide-activities": "Hide Activities",
|
||||
"headerBarCreateBoardPopup-title": "Create Board",
|
||||
"home": "Home",
|
||||
"import": "Import",
|
||||
@@ -894,7 +898,12 @@
|
||||
"no-name": "(Unknown)",
|
||||
"Node_version": "Node version",
|
||||
"Meteor_version": "Meteor version",
|
||||
"MongoDB_version": "MongoDB version",
|
||||
"Database_type": "Database type",
|
||||
"Database_commit": "Database commit",
|
||||
"FerretDB_version": "FerretDB version",
|
||||
"FerretDB_commit": "FerretDB commit",
|
||||
"Reactivity_mode": "Reactivity mode (changeStreams / oplog / polling)",
|
||||
"MongoDB_version": "MongoDB compatible version",
|
||||
"MongoDB_storage_engine": "MongoDB storage engine",
|
||||
"MongoDB_Oplog_enabled": "MongoDB Oplog enabled",
|
||||
"OS_Arch": "OS Arch",
|
||||
@@ -1015,6 +1024,17 @@
|
||||
"r-workflow-help": "Drag a trigger and an action into the builder, then add the rule. Drag an action onto an existing rule to change what it does.",
|
||||
"r-drop-trigger": "drop a trigger here",
|
||||
"r-drop-action": "drop an action here",
|
||||
"r-w-card-created": "Card is created",
|
||||
"r-w-card-archived": "Card is archived",
|
||||
"r-w-card-unarchived": "Card is unarchived",
|
||||
"r-w-label-added": "Any label is added",
|
||||
"r-w-label-removed": "Any label is removed",
|
||||
"r-w-member-added": "A member is added",
|
||||
"r-w-member-removed": "A member is removed",
|
||||
"r-w-checklist-added": "A checklist is added",
|
||||
"r-w-attachment-added": "An attachment is added",
|
||||
"r-w-every-day-at": "Every day at __time__",
|
||||
"r-w-set-received-now": "Set received date to now",
|
||||
"r-export-json": "Export rules to JSON",
|
||||
"r-export-csv": "Export rules to CSV",
|
||||
"r-import-json": "Import rules from JSON",
|
||||
@@ -1068,6 +1088,12 @@
|
||||
"r-mark-incomplete": "Mark card incomplete",
|
||||
"r-move-all-cards": "Move all cards in list",
|
||||
"r-set-date-relative": "Set date relative to now",
|
||||
"r-later": "later",
|
||||
"r-unit-minutes": "minute(s)",
|
||||
"r-unit-hours": "hour(s)",
|
||||
"r-unit-days": "day(s)",
|
||||
"r-unit-weeks": "week(s)",
|
||||
"r-unit-months": "month(s)",
|
||||
"r-trigger": "Trigger",
|
||||
"r-action": "Action",
|
||||
"r-when-a-card": "When a card",
|
||||
@@ -1809,6 +1835,45 @@
|
||||
"azure-container": "Container",
|
||||
"gcs-storage": "Google Cloud Storage",
|
||||
"gcs-storage-description": "Google Cloud Storage.",
|
||||
"database-migration": "Database migration",
|
||||
"database-migration-description": "Migrate text-based data (everything except attachments and avatars, which stay on the filesystem) between MongoDB and FerretDB v1 (SQLite). The other database must be running and reachable (default ports: FerretDB mongodb://127.0.0.1:27018, MongoDB mongodb://127.0.0.1:27019; override with WEKAN_FERRETDB_URL / WEKAN_MONGODB_URL). After it finishes, point MONGO_URL at the other database and restart WeKan (on Snap: snap set wekan database=ferretdb or =mongodb).",
|
||||
"database-migrate-to-ferretdb": "Migrate text data to FerretDB v1 (SQLite)",
|
||||
"database-migrate-to-mongodb": "Migrate text data to MongoDB",
|
||||
"database-migration-confirm": "Copy all text-based collections to __db__? Attachments and avatars on the filesystem are not affected. The target database must be running.",
|
||||
"database-migration-phase": "Phase",
|
||||
"database-migration-done": "Text-data migration completed.",
|
||||
"collections": "collections",
|
||||
"features": "Features",
|
||||
"features-performance": "Performance",
|
||||
"cards-loading": "Card loading",
|
||||
"cards-loading-all": "All cards (default)",
|
||||
"cards-loading-lazy": "Lazy (only visible cards)",
|
||||
"cards-loading-description": "How each board loads its cards. All cards: every card is loaded into the browser (simplest, all features work, but a board with thousands of cards sends a lot of data to each viewer). Lazy: each list loads only the cards currently visible (infinite scroll) plus a live count, so very large boards use less memory and bandwidth. Also set by the CARDS_LOADING environment variable.",
|
||||
"cards-loading-lazy-note": "Lazy card loading is experimental: card counters and WIP limits are accurate, but the Calendar/Table/Gantt views and multi-select reflect only the cards loaded so far. Reload open boards after changing this.",
|
||||
"backup": "Backup",
|
||||
"backup-description": "Back up Attachments, Avatars and/or Data (all text-based collections that are not attachments or avatars). The .zip is streamed directly to the selected storage — no temporary file — as backup/YYYY/MM/DD/HH_MM_SS/backup.zip, containing YYYY_MM_DD-HH_MM_SS/attachments, /avatars and /data. Cloud storages (S3/MinIO, Azure, GCS) use the credentials configured in their storage tabs.",
|
||||
"backup-data": "Data (text)",
|
||||
"backup-storage": "Save to storage",
|
||||
"backup-now": "Backup now",
|
||||
"backup-done": "Backup completed",
|
||||
"backup-schedule": "Scheduled backups",
|
||||
"backup-frequency": "Frequency",
|
||||
"backup-frequency-off": "Off",
|
||||
"backup-frequency-daily": "Daily",
|
||||
"backup-frequency-weekly": "Weekly",
|
||||
"backup-frequency-monthly": "Monthly",
|
||||
"backup-time": "Time (HH:MM)",
|
||||
"backup-day-of-week": "Day of week (weekly)",
|
||||
"backup-day-of-month": "Day of month (monthly, 1-28)",
|
||||
"backup-restore": "Restore",
|
||||
"backup-list": "List backups",
|
||||
"backup-datetime": "Date and time",
|
||||
"backup-path": "File path",
|
||||
"backup-restore-mode": "Restore mode",
|
||||
"backup-restore-add-missing": "Add missing data only",
|
||||
"backup-restore-replace-all": "Replace all data",
|
||||
"backup-restore-select-first": "Select a backup from the list first.",
|
||||
"backup-restore-confirm": "Restore the selected backup? Attachments/avatars are written to the filesystem and data to the database according to the chosen mode.",
|
||||
"gcs-project-id": "Project ID",
|
||||
"gcs-bucket": "Bucket",
|
||||
"gcs-key-filename": "Service account key file path",
|
||||
@@ -1986,8 +2051,6 @@
|
||||
"cpu-cores": "CPU Cores",
|
||||
"cpu-usage": "CPU Usage",
|
||||
"current-action": "Current Action",
|
||||
"database-migration": "Database Migration",
|
||||
"database-migration-description": "Updating database structure for improved functionality and performance. This process may take several minutes.",
|
||||
"database-migrations": "Database Migrations",
|
||||
"days-old": "Days Old",
|
||||
"duration": "Duration",
|
||||
|
||||
Loaded 100 of 371 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user