mirror of
https://github.com/wekan/wekan.git
synced 2026-11-08 11:25:19 -05:00
Compare commits
No files matched your search
@@ -24,7 +24,7 @@ updates:
|
||||
# * The only upgrades Dependabot/npm offer are build-breaking: a
|
||||
# @meteorjs/rspack downgrade, or @rspack/cli 2.0 / Rspack 2.0, which
|
||||
# needs Meteor 3.6 (see https://github.com/meteor/meteor/pull/14360).
|
||||
# WeKan currently runs METEOR@3.5-rc.1.
|
||||
# WeKan currently runs METEOR@3.5-rc.2.
|
||||
#
|
||||
# So pin them here until Meteor 3.6 + Rspack 2.0 land, then revisit.
|
||||
- dependency-name: "@meteorjs/rspack"
|
||||
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
run: meteor test --once --driver-package meteortesting:mocha
|
||||
|
||||
e2e-tests:
|
||||
name: Playwright E2E (${{ matrix.project }})
|
||||
name: Playwright E2E (${{ matrix.project }} ${{ matrix.shard }}/2)
|
||||
runs-on: ubuntu-24.04
|
||||
needs: unit-tests
|
||||
strategy:
|
||||
@@ -71,6 +71,12 @@ jobs:
|
||||
# Playwright bundles Chromium, Firefox and WebKit builds that all run
|
||||
# headless on the Linux runner (installed per-job below with --with-deps).
|
||||
project: [chromium, firefox, webkit]
|
||||
# Split each browser's ~245 specs across 2 parallel shards (6 jobs total)
|
||||
# to cut wall-clock time. Each shard job starts its own WeKan + Mongo, so
|
||||
# there is no shared-server contention between shards. Raise the shard
|
||||
# count (here and in the --shard flag below) once the baseline is green
|
||||
# and/or once E2E runs against a prebuilt bundle (cheaper per-job startup).
|
||||
shard: [1, 2]
|
||||
|
||||
services:
|
||||
mongodb:
|
||||
@@ -123,34 +129,54 @@ jobs:
|
||||
run: npx playwright install --with-deps ${{ matrix.project }}
|
||||
working-directory: tests/playwright
|
||||
|
||||
- name: Start WeKan in background
|
||||
- name: Build and start WeKan (production bundle)
|
||||
run: |
|
||||
# Run E2E against a PRODUCTION build instead of `meteor run` (dev mode).
|
||||
# In dev the client JS is served by the rspack dev server, which did not
|
||||
# serve the bundle reliably in headless CI: the browser received HTML for
|
||||
# the bundle ("Unexpected token '<'"), Meteor never initialised, and every
|
||||
# spec timed out. A production build bakes the client JS into the bundle,
|
||||
# served statically by the Meteor server at :3000.
|
||||
meteor build ./output --directory
|
||||
( cd output/bundle/programs/server && npm install )
|
||||
# WeKan's server/00checkStartup.js refuses to start unless WRITABLE_PATH
|
||||
# (used for attachment/file storage) is set and writable.
|
||||
mkdir -p "$PWD/wekan-writable"
|
||||
# Match the env the dev runner (rebuild-wekan.sh) and the specs expect:
|
||||
# WITH_API enables the REST API (specs 17/23/24/28), comment-editor
|
||||
# flag matches dev.
|
||||
MONGO_URL=mongodb://localhost:27017/wekan_test \
|
||||
ROOT_URL=http://localhost:3000 \
|
||||
PORT=3000 \
|
||||
meteor run --settings settings.json > wekan-run.log 2>&1 &
|
||||
# The first Meteor build is slow, so wait up to 20 minutes for the
|
||||
# server to start responding before giving up.
|
||||
for i in $(seq 1 120); do
|
||||
WRITABLE_PATH="$PWD/wekan-writable" \
|
||||
WITH_API=true \
|
||||
RICHER_CARD_COMMENT_EDITOR=false \
|
||||
node output/bundle/main.js > wekan-run.log 2>&1 &
|
||||
# The production server serves the client JS statically, so once :3000
|
||||
# responds the bundle is being served. Wait up to 10 minutes.
|
||||
for i in $(seq 1 60); do
|
||||
if curl -sf http://localhost:3000 > /dev/null; then
|
||||
echo "WeKan is up after $((i * 10))s"
|
||||
break
|
||||
fi
|
||||
echo "Waiting for WeKan... attempt $i/120"
|
||||
echo "Waiting for WeKan... attempt $i/60"
|
||||
sleep 10
|
||||
done
|
||||
if ! curl -sf http://localhost:3000 > /dev/null; then
|
||||
echo "WeKan failed to start. Last 200 log lines:"
|
||||
tail -n 200 wekan-run.log
|
||||
echo "WeKan failed to start. Last 250 log lines:"
|
||||
tail -n 250 wekan-run.log
|
||||
exit 1
|
||||
fi
|
||||
env:
|
||||
DISABLE_CAPTCHA: 'true'
|
||||
TOOL_NODE_FLAGS: --max-old-space-size=8192
|
||||
NODE_OPTIONS: --max-old-space-size=8192
|
||||
DISABLE_CAPTCHA: 'true'
|
||||
|
||||
- name: Run Playwright tests (${{ matrix.project }})
|
||||
- name: Run Playwright tests (${{ matrix.project }} shard ${{ matrix.shard }}/2)
|
||||
run: |
|
||||
npx playwright test \
|
||||
--project=${{ matrix.project }} \
|
||||
--shard=${{ matrix.shard }}/2 \
|
||||
--reporter=github
|
||||
working-directory: tests/playwright
|
||||
env:
|
||||
@@ -164,7 +190,7 @@ jobs:
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: playwright-report-${{ matrix.project }}
|
||||
name: playwright-report-${{ matrix.project }}-shard${{ matrix.shard }}
|
||||
path: tests/playwright/playwright-report/
|
||||
retention-days: 14
|
||||
|
||||
@@ -172,7 +198,7 @@ jobs:
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: test-results-${{ matrix.project }}
|
||||
name: test-results-${{ matrix.project }}-shard${{ matrix.shard }}
|
||||
path: tests/playwright/test-results/
|
||||
retention-days: 7
|
||||
|
||||
@@ -227,27 +253,40 @@ jobs:
|
||||
- name: Install Chromium
|
||||
run: sudo apt-get install -y chromium-browser
|
||||
|
||||
- name: Start WeKan in background
|
||||
- name: Build and start WeKan (production bundle)
|
||||
run: |
|
||||
# Run a PRODUCTION build, not `meteor run` (dev): exactly like the
|
||||
# Playwright e2e job, because the rspack dev server does not serve the
|
||||
# client JS bundle in headless CI, so Meteor never initialises and the
|
||||
# puppeteer regression test times out in waitForMeteorGlobals(). The
|
||||
# production bundle serves the client statically. WRITABLE_PATH is
|
||||
# required by server/00checkStartup.js; WITH_API mirrors the dev runner.
|
||||
meteor build ./output --directory
|
||||
( cd output/bundle/programs/server && npm install )
|
||||
mkdir -p "$PWD/wekan-writable"
|
||||
MONGO_URL=mongodb://localhost:27017/wekan_regression \
|
||||
ROOT_URL=http://localhost:3000 \
|
||||
PORT=3000 \
|
||||
meteor run > wekan-run.log 2>&1 &
|
||||
# The first Meteor build is slow, so wait up to 20 minutes.
|
||||
for i in $(seq 1 120); do
|
||||
WRITABLE_PATH="$PWD/wekan-writable" \
|
||||
WITH_API=true \
|
||||
RICHER_CARD_COMMENT_EDITOR=false \
|
||||
node output/bundle/main.js > wekan-run.log 2>&1 &
|
||||
for i in $(seq 1 60); do
|
||||
if curl -sf http://localhost:3000 > /dev/null; then
|
||||
echo "WeKan is up after $((i * 10))s"
|
||||
break
|
||||
fi
|
||||
echo "Waiting for WeKan... attempt $i/120"
|
||||
echo "Waiting for WeKan... attempt $i/60"
|
||||
sleep 10
|
||||
done
|
||||
if ! curl -sf http://localhost:3000 > /dev/null; then
|
||||
echo "WeKan failed to start. Last 200 log lines:"
|
||||
tail -n 200 wekan-run.log
|
||||
echo "WeKan failed to start. Last 250 log lines:"
|
||||
tail -n 250 wekan-run.log
|
||||
exit 1
|
||||
fi
|
||||
env:
|
||||
TOOL_NODE_FLAGS: --max-old-space-size=8192
|
||||
NODE_OPTIONS: --max-old-space-size=8192
|
||||
DISABLE_CAPTCHA: 'true'
|
||||
|
||||
- name: Run list-regressions e2e
|
||||
|
||||
@@ -64,7 +64,7 @@ permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
# Node.js major version that Meteor 3.5-beta bundles. Used for native module
|
||||
# Node.js major version that Meteor 3.5 bundles. Used for native module
|
||||
# rebuilds on every platform — it must match the amd64 build's Node ABI.
|
||||
env:
|
||||
NODE_VERSION: '24'
|
||||
@@ -250,7 +250,7 @@ jobs:
|
||||
./wekan/releases/release-charts.sh "${{ inputs.new_version }}"
|
||||
|
||||
# ─── 2. Build amd64 bundle (full Meteor 3 build) ────────────────────────────
|
||||
# Meteor 3.5-beta installer supports only Linux x86_64/aarch64, macOS, Windows.
|
||||
# Meteor 3.5 installer supports only Linux x86_64/aarch64, macOS, Windows.
|
||||
# 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:
|
||||
|
||||
+8
-8
@@ -6,7 +6,7 @@
|
||||
meteor-base@1.5.2
|
||||
|
||||
# Build system
|
||||
ecmascript@0.18.0
|
||||
ecmascript@0.18.1-rc350.0
|
||||
standard-minifier-js@3.2.0
|
||||
rspack@1.1.0
|
||||
|
||||
@@ -16,11 +16,11 @@ blaze@3.0.2
|
||||
aldeed:collection2
|
||||
aldeed:schema-index@4.0.0-beta.5
|
||||
reywood:publish-composite
|
||||
mongo@2.4.0-rc350.1
|
||||
mongo@2.4.0-rc350.2
|
||||
|
||||
# Account system
|
||||
accounts-password@3.3.0-rc350.1
|
||||
accounts-express@1.0.0-rc350.1
|
||||
accounts-password@3.3.0-rc350.2
|
||||
accounts-express@1.0.0-rc350.2
|
||||
communitypackages:core
|
||||
communitypackages:unstyled
|
||||
communitypackages:flow-routing
|
||||
@@ -37,7 +37,7 @@ wekan-accounts-oidc
|
||||
check@1.5.0
|
||||
random@1.2.2
|
||||
reactive-dict@1.3.2
|
||||
session@1.2.3-rc350.1
|
||||
session@1.2.3-rc350.2
|
||||
tracker@1.3.4
|
||||
audit-argument-checks@1.0.8
|
||||
|
||||
@@ -45,7 +45,7 @@ audit-argument-checks@1.0.8
|
||||
ostrio:i18n
|
||||
reactive-var@1.0.13
|
||||
shell-server@0.7.0
|
||||
email@3.2.0-rc350.1
|
||||
email@3.2.0-rc350.2
|
||||
dynamic-import@0.7.4
|
||||
wekan-markdown
|
||||
quave:synced-cron
|
||||
@@ -55,8 +55,8 @@ ostrio:files
|
||||
meteortesting:mocha@3.0.0
|
||||
matb33:collection-hooks
|
||||
service-configuration@1.3.5
|
||||
minifier-css@2.0.2-rc350.1
|
||||
ejson@1.2.0-rc350.1
|
||||
minifier-css@2.0.2-rc350.2
|
||||
ejson@1.2.0-rc350.2
|
||||
logging@1.3.6
|
||||
wekan-fullcalendar
|
||||
wekan-fontawesome
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
METEOR@3.5-rc.1
|
||||
METEOR@3.5-rc.2
|
||||
+28
-28
@@ -1,44 +1,44 @@
|
||||
accounts-base@3.3.0-rc350.1
|
||||
accounts-express@1.0.0-rc350.1
|
||||
accounts-oauth@1.4.7-rc350.1
|
||||
accounts-password@3.3.0-rc350.1
|
||||
accounts-base@3.3.0-rc350.2
|
||||
accounts-express@1.0.0-rc350.2
|
||||
accounts-oauth@1.4.7-rc350.2
|
||||
accounts-password@3.3.0-rc350.2
|
||||
aldeed:collection2@4.2.0
|
||||
aldeed:schema-index@4.0.0
|
||||
aldeed:simple-schema@1.13.1
|
||||
allow-deny@2.1.0
|
||||
audit-argument-checks@1.0.8
|
||||
autoupdate@2.0.1
|
||||
babel-compiler@7.14.0
|
||||
babel-compiler@7.14.1-rc350.0
|
||||
babel-runtime@1.5.2
|
||||
base64@1.0.14-rc350.1
|
||||
binary-heap@1.0.13-rc350.1
|
||||
base64@1.0.14-rc350.2
|
||||
binary-heap@1.0.13-rc350.2
|
||||
blaze@3.0.3
|
||||
blaze-tools@2.0.1
|
||||
boilerplate-generator@2.1.0
|
||||
caching-compiler@2.0.1
|
||||
caching-html-compiler@2.0.1
|
||||
callback-hook@1.8.0-rc350.1
|
||||
callback-hook@1.8.0-rc350.2
|
||||
check@1.5.0
|
||||
communitypackages:core@2.0.3
|
||||
communitypackages:flow-routing@2.1.0
|
||||
communitypackages:unstyled@2.0.1
|
||||
core-runtime@1.0.0
|
||||
ddp@1.4.2
|
||||
ddp-client@3.3.0-rc350.1
|
||||
ddp-common@1.4.5-rc350.1
|
||||
ddp-rate-limiter@1.3.0-rc350.1
|
||||
ddp-server@3.3.0-rc350.1
|
||||
ddp-client@3.3.0-rc350.2
|
||||
ddp-common@1.4.5-rc350.2
|
||||
ddp-rate-limiter@1.3.0-rc350.2
|
||||
ddp-server@3.3.0-rc350.2
|
||||
diff-sequence@1.1.3
|
||||
dynamic-import@0.7.4
|
||||
ecmascript@0.18.0
|
||||
ecmascript@0.18.1-rc350.0
|
||||
ecmascript-runtime@0.8.3
|
||||
ecmascript-runtime-client@0.13.0
|
||||
ecmascript-runtime-server@0.11.1
|
||||
ejson@1.2.0-rc350.1
|
||||
email@3.2.0-rc350.1
|
||||
ejson@1.2.0-rc350.2
|
||||
email@3.2.0-rc350.2
|
||||
es5-shim@4.8.1
|
||||
facts-base@1.0.2
|
||||
fetch@0.2.0-rc350.1
|
||||
fetch@0.2.0-rc350.2
|
||||
geojson-utils@1.0.12
|
||||
hot-code-push@1.0.5
|
||||
html-tools@2.0.1
|
||||
@@ -53,23 +53,23 @@ lai:collection-extensions@1.0.0
|
||||
localstorage@1.2.1
|
||||
logging@1.3.6
|
||||
matb33:collection-hooks@2.1.0
|
||||
meteor@2.3.1-rc350.1
|
||||
meteor@2.3.1-rc350.2
|
||||
meteor-base@1.5.2
|
||||
meteortesting:browser-tests@1.8.0
|
||||
meteortesting:mocha@3.3.0
|
||||
meteortesting:mocha-core@8.2.0
|
||||
minifier-css@2.0.2-rc350.1
|
||||
minifier-js@3.2.0
|
||||
minimongo@2.2.0-rc350.1
|
||||
minifier-css@2.0.2-rc350.2
|
||||
minifier-js@3.2.1-rc350.0
|
||||
minimongo@2.2.0-rc350.2
|
||||
modern-browsers@0.2.3
|
||||
modules@0.20.3
|
||||
modules-runtime@0.13.2
|
||||
mongo@2.4.0-rc350.1
|
||||
mongo@2.4.0-rc350.2
|
||||
mongo-decimal@0.2.0
|
||||
mongo-dev-server@1.1.1
|
||||
mongo-id@1.0.9
|
||||
npm-mongo@6.16.1
|
||||
oauth@3.0.4-rc350.1
|
||||
oauth@3.0.4-rc350.2
|
||||
oauth2@1.3.3
|
||||
observe-sequence@2.0.1
|
||||
ordered-dict@1.2.0
|
||||
@@ -82,7 +82,7 @@ promise@1.0.0
|
||||
quave:synced-cron@2.4.0
|
||||
raix:eventemitter@1.0.0
|
||||
random@1.2.2
|
||||
rate-limit@1.2.0-rc350.1
|
||||
rate-limit@1.2.0-rc350.2
|
||||
react-fast-refresh@0.3.0
|
||||
reactive-dict@1.3.2
|
||||
reactive-var@1.0.13
|
||||
@@ -92,10 +92,10 @@ reywood:publish-composite@1.9.0
|
||||
routepolicy@1.1.2
|
||||
rspack@1.1.0
|
||||
service-configuration@1.3.5
|
||||
session@1.2.3-rc350.1
|
||||
session@1.2.3-rc350.2
|
||||
sha@1.0.10
|
||||
shell-server@0.7.0
|
||||
socket-stream-client@0.7.0-rc350.1
|
||||
socket-stream-client@0.7.0-rc350.2
|
||||
spacebars@2.0.1
|
||||
spacebars-compiler@2.0.1
|
||||
standard-minifier-js@3.2.0
|
||||
@@ -105,9 +105,9 @@ templating-runtime@2.0.2
|
||||
templating-tools@2.0.1
|
||||
tools-core@1.1.0
|
||||
tracker@1.3.4
|
||||
typescript@5.10.0
|
||||
url@1.3.6-rc350.1
|
||||
webapp@2.2.0-rc350.1
|
||||
typescript@5.10.1-rc350.0
|
||||
url@1.3.6-rc350.2
|
||||
webapp@2.2.0-rc350.2
|
||||
webapp-hashing@1.1.2
|
||||
wekan-accounts-cas@0.2.0
|
||||
wekan-accounts-lockout@1.1.0
|
||||
|
||||
+494
-5
@@ -4,19 +4,18 @@ Newest WeKan at these platforms:
|
||||
|
||||
- [Upgrade WeKan](https://wekan.fi/upgrade/)
|
||||
- [bundle zip at releases](https://github.com/wekan/wekan/releases), docs at https://wekan.fi/docs/
|
||||
- [Linux amd64, arm64](https://github.com/wekan/wekan/blob/main/docs/Platforms/FOSS/RaspberryPi/Raspberry-Pi.md)
|
||||
- [Linux amd64, arm64, s390x, ppc64le](https://github.com/wekan/wekan/blob/main/docs/Platforms/FOSS/RaspberryPi/Raspberry-Pi.md)
|
||||
- [Windows amd64/arm64](https://github.com/wekan/wekan/blob/main/docs/Platforms/Propietary/Windows/Offline.md)
|
||||
- [Mac arm64](https://github.com/wekan/wekan/blob/main/docs/Platforms/Propietary/Mac.md)
|
||||
- https://wekan.fi/install/
|
||||
- Snap Beta amd64/arm64
|
||||
- Docker amd64/arm64
|
||||
- Kubernetes Docker amd64/arm64
|
||||
- Bitnami MongoDB Docker images do not exist anymore. [MongoDump/MongoRestore to groundhog2k MongoDB images](https://github.com/wekan/charts/issues/45)
|
||||
- Fixing other platforms In Progress:
|
||||
- [Windows amd64/arm64](https://github.com/wekan/wekan/blob/main/docs/Platforms/Propietary/Windows/Offline.md)
|
||||
- [Mac amd64, works also with Rosetta2 at Apple Silicon](https://github.com/wekan/wekan/blob/main/docs/Platforms/Propietary/Mac.md)
|
||||
|
||||
Versions:
|
||||
|
||||
- WeKan 8.75 and newer uses Meteor 3.5-beta.x, [Node.js 24.x](https://nodejs.org/dist/latest-v24.x/),
|
||||
- WeKan 8.75 and newer uses Meteor 3.5-rc.x, [Node.js 24.x](https://nodejs.org/dist/latest-v24.x/),
|
||||
MongoDB 7.x (or 6.x), or [FerretDB2/PostgreSQL](https://github.com/wekan/wekan/blob/main/docs/Databases/FerretDB2-PostgreSQL.md)
|
||||
- WeKan 8.43 upgraded to Meteor 3.x, huge thanks to harryadel:
|
||||
- https://harryadel.com/dev-diary-24/
|
||||
@@ -26,10 +25,500 @@ Versions:
|
||||
- WeKan 8.00-8.06 had wrong raw database directory setting /var/snap/wekan/common/wekan and some cards were not visible,
|
||||
it was fixed at WeKan 8.07 where database directory is back to /var/snap/wekan/common and all cards are visible.
|
||||
|
||||
# TODO Later
|
||||
|
||||
Carried to a future release — investigated this session but not finished, with findings recorded for whoever picks
|
||||
them up next.
|
||||
|
||||
- **Need a runtime reproduction before a safe fix:**
|
||||
[#5730](https://github.com/wekan/wekan/issues/5730) (board vanished after adding a user — no logs/repro).
|
||||
- **Need specific infrastructure we cannot reproduce/verify here (left for environment owners):**
|
||||
[#4560](https://github.com/wekan/wekan/issues/4560) (LDAP→OIDC), [#3707](https://github.com/wekan/wekan/issues/3707)
|
||||
& [#3700](https://github.com/wekan/wekan/issues/3700) (LDAP), [#3575](https://github.com/wekan/wekan/issues/3575)
|
||||
(WebHooks), [#1192](https://github.com/wekan/wekan/issues/1192) (Sandstorm).
|
||||
- **Feature requests / behaviour-by-design rather than bugs:**
|
||||
[#5547](https://github.com/wekan/wekan/issues/5547), [#3843](https://github.com/wekan/wekan/issues/3843),
|
||||
[#3823](https://github.com/wekan/wekan/issues/3823), [#3138](https://github.com/wekan/wekan/issues/3138),
|
||||
[#2204](https://github.com/wekan/wekan/issues/2204) (restrict permanent delete to the Admin role).
|
||||
|
||||
# v9.71 2026-06-23 WeKan ® release
|
||||
|
||||
This release adds the following features:
|
||||
|
||||
- [Sort, search and paginate the All Boards page](https://github.com/wekan/wekan/issues/5799),
|
||||
[#5799](https://github.com/wekan/wekan/issues/5799): the All Boards page only offered the per-user manual drag order
|
||||
with no obvious way to sort by name. Added a **Sort** button (Custom drag order / Title A→Z / Title Z→A, stored per
|
||||
user), a **board-name search** box that spans every category (Starred, Templates, Remaining and all
|
||||
(sub)workspaces), and **pagination of the board icons** — in the sorted modes the current page is computed on the
|
||||
server (`getAllBoardsPage`) so only that page is rendered and the page scales to thousands of boards. The search box
|
||||
and pagination controls are placed like Admin Panel > People. Custom (manual drag order) stays unpaginated so
|
||||
drag-reordering keeps working. All server-side filtering/sorting resolves against the effective current user, so it
|
||||
also works when a GlobalAdmin impersonates a user; added Playwright impersonation tests
|
||||
(`tests/playwright/specs/38-impersonation.e2e.js`). Note: "Stop impersonating" is still a follow-up — impersonation
|
||||
is connection-scoped and currently reverts on a full page reload.
|
||||
Fixed in [f74ccbc8f](https://github.com/wekan/wekan/commit/f74ccbc8faf1c19f06775ca70306cf320e442dfb) and
|
||||
[c1da7a4a3](https://github.com/wekan/wekan/commit/c1da7a4a332cd10bbd2ed3c93122fe0ea8864336).
|
||||
- [Fixed (same) width for all lists](https://github.com/wekan/wekan/issues/5729),
|
||||
[#5729](https://github.com/wekan/wekan/issues/5729): the Set width popup now has a "Same width for all lists" toggle.
|
||||
When enabled, every list on the board renders at one shared width for the current viewer, and dragging the resize
|
||||
handle of ANY list updates that single value so ALL lists change together. The setting is per-viewer/per-board and
|
||||
works for both logged-in users (stored in `profile.fixedListWidthBoards` / `profile.fixedListWidths`) and anonymous
|
||||
public-board users (stored in `localStorage` keys `wekan-fixed-list-width-enabled` / `wekan-fixed-list-width`).
|
||||
Enabling fixed width turns off auto-width (the two modes are mutually exclusive); widths below 270px are rejected.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Due date does not work when the language uses non-Latin (e.g. Persian/Farsi) digits](https://github.com/wekan/wekan/issues/5752),
|
||||
[#5752](https://github.com/wekan/wekan/issues/5752): in locales such as Persian/Farsi or Arabic, dates and times can be
|
||||
rendered with non-Latin digits (Persian/Extended Arabic-Indic ۰۱۲۳۴۵۶۷۸۹, U+06F0–U+06F9, or Arabic-Indic ٠١٢٣٤٥٦٧٨٩,
|
||||
U+0660–U+0669). The native JavaScript `Date` constructor only understands ASCII digits, so any such string fails to
|
||||
parse and produces an Invalid Date when setting/updating or comparing dates. Added a `normalizeDigits()` helper in
|
||||
`imports/lib/dateUtils.js` that converts those non-Latin digits to ASCII, applied it at every date-string parse
|
||||
boundary in `dateUtils.js` (via an internal `toDate()` wrapper) and in the date/time picker submit/change handlers in
|
||||
`client/lib/datepicker.js`. The normal ASCII path is unchanged.
|
||||
- [Impossible to create a Link to a whole BOARD if that board already has cards](https://github.com/wekan/wekan/issues/5715),
|
||||
[#5715](https://github.com/wekan/wekan/issues/5715): in the link-card popup, choosing a board used to auto-populate the
|
||||
List/Card sub-selects, with no way to clear them, so once the chosen board had cards you could only link to a card and
|
||||
no longer to the whole board. The Swimlane/List/Card selects now default to a blank "(none)" option and are no longer
|
||||
auto-selected when a board is chosen, and the popup's confirm ("link") button now falls back to creating a board-level
|
||||
link when a board is selected but the Card field is left blank (matching the previously-only-empty-board behaviour).
|
||||
Added `tests/linkCardPopup.test.js` covering the positive and negative cases.
|
||||
- [Internal Server Error (500) when attempting to reset a password](https://github.com/wekan/wekan/issues/5706),
|
||||
[#5706](https://github.com/wekan/wekan/issues/5706): on the Forgot Password page, clicking "Email reset link" could
|
||||
return a raw HTTP 500 instead of sending the reset email / showing success. This happens when the server's SMTP is not
|
||||
configured (no `MAIL_URL` / `MAIL_FROM`, or a bad mail server): Meteor's `Email.sendAsync` throws, and the exception
|
||||
propagated unhandled out of the `forgotPassword` method as an opaque 500. The reset-password / verify-email /
|
||||
enroll-account email-template builders are now hardened so they never throw (guarded user name/language lookups and a
|
||||
safe fallback if translation fails), and `Accounts.sendResetPasswordEmail` is wrapped so a send failure surfaces as a
|
||||
clean `Meteor.Error('email-fail', ...)` instead of a 500. Note: this makes the failure graceful, but to actually
|
||||
receive reset emails you must still configure SMTP (Admin Panel mail server, or `MAIL_URL` and `MAIL_FROM`). Logic
|
||||
extracted to `server/lib/resetPasswordEmail.js` and unit-tested in `tests/unit/resetPasswordEmail.test.js`.
|
||||
- [Changing the UI language had no effect for some languages (e.g. Chinese zh-CN, zh-Hans/zh-Hant, Arabic ar-*) while others (de, fr) worked](https://github.com/wekan/wekan/issues/5756),
|
||||
[#5756](https://github.com/wekan/wekan/issues/5756): the custom tap:i18n reimplementation registered each language's
|
||||
resource bundle under the raw Wekan tag, but i18next (with `cleanCode: true`) resolves and looks translations up under a
|
||||
normalised code. For region/script-tagged and legacy underscore tags (`af_ZA`, `en_AU`, …) the stored code and the
|
||||
lookup code disagreed, so the bundle was never found and the UI silently fell back to English. The loader now normalises
|
||||
every i18next call (`supportedLngs`, `addResourceBundle`, `changeLanguage`, `t`) through the same code — underscores
|
||||
converted to hyphens and run through i18next's `formatLanguageCode` — so storage and lookup always agree for ALL
|
||||
supported languages. Dynamic JSON imports that resolve to an ES-module namespace are now unwrapped safely (without
|
||||
mistaking the data's own `"default"` translation key for the module's default export), and the language-switch handlers
|
||||
in the user header and login form now surface a failed load instead of leaving the UI silently in English. Added
|
||||
positive and negative regression tests in `imports/i18n/i18n.test.js`.
|
||||
- [Card labels took two lines / double height on minicards, wasting vertical space](https://github.com/wekan/wekan/issues/6424),
|
||||
[#6424](https://github.com/wekan/wekan/issues/6424): each label's name is rendered inside a `.viewer`, whose global
|
||||
`min-height: 2.5vh` (intended for the full content editor) forced every minicard label to roughly double height. The
|
||||
minicard text labels now reset that `min-height`, render the viewer/paragraph inline and stay on a single compact line.
|
||||
Fixed in [afbabccd2](https://github.com/wekan/wekan/commit/afbabccd2685af77ffddc1038f62823882615b74).
|
||||
- [REST API: moving a card to another list (PUT .../cards/:cardId with listId) returned HTTP 500 — "fieldNames.includes is not a function"](https://github.com/wekan/wekan/issues/6423),
|
||||
[#6423](https://github.com/wekan/wekan/issues/6423): the list-move path called the `cardMove()` activity helper with
|
||||
`{ fieldName: 'listId' }` (a plain object) as its `fieldNames` argument, but `cardMove()` does
|
||||
`fieldNames.includes('boardId')`, so the object's missing `.includes` threw and the endpoint returned HTTP 500. The
|
||||
changed field names are now passed as an array (`['listId']`), matching the cross-board move path.
|
||||
Fixed in [69c899f1f](https://github.com/wekan/wekan/commit/69c899f1f99b0ea984d3b36f5d2a2aac67c4ff9a).
|
||||
- [Notification emails were sent in English despite the user's language setting](https://github.com/wekan/wekan/issues/5875),
|
||||
[#5875](https://github.com/wekan/wekan/issues/5875): on the server only the default (English) translation bundle is
|
||||
loaded at startup, so translating a notification to the user's language fell back to English because i18next never had
|
||||
that language's bundle. Added `TAPi18n.ensureLanguageLoaded()` (loads the bundle on demand) and await it before
|
||||
translating in the email notification, `EmailLocalization.sendEmail` and outgoing webhook paths.
|
||||
Fixed in [22f996903](https://github.com/wekan/wekan/commit/22f996903b09d35e3aa959a9aa65b30204e12202).
|
||||
|
||||
# v9.70 2026-06-23 WeKan ® release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- **Copying a card to or from a board with no labels threw.** `Card.copy()`'s cross-board label-remap did
|
||||
`oldBoard.labels.filter(...)` / `filterCopiedLabelIds(newBoard.labels, …)` without guarding a missing `labels` array.
|
||||
Most boards always have default labels, but a board **created via the REST API** (`POST /api/boards`) has no `labels`
|
||||
array — so copying a card to/from such a board (or copying the board itself, which copies its cards) threw
|
||||
`Cannot read properties of undefined (reading 'filter')` and returned HTTP 500. Both label lookups now fall back to
|
||||
`[]`. Cross-board copy regressions in `tests/playwright/specs/17-rest-api.e2e.js`.
|
||||
- [Copying a board did not copy its webhooks](https://github.com/wekan/wekan/issues/5592),
|
||||
[#5592](https://github.com/wekan/wekan/issues/5592): `board.copy()` duplicated swimlanes/lists/cards, custom fields
|
||||
and rules/triggers/actions, but had no loop for **Integrations** (outgoing webhooks), so a copied board lost all of
|
||||
them (even though they are per-board children — `boardRemover` deletes them by `boardId`). `copy()` now also copies
|
||||
the board's Integrations, remapping `boardId` (URL/token/activities carry over — the copying user is a board admin
|
||||
with access to them). Regression in `tests/playwright/specs/17-rest-api.e2e.js`.
|
||||
- [Disabled user accounts could be added to boards](https://github.com/wekan/wekan/issues/1894),
|
||||
[#1894](https://github.com/wekan/wekan/issues/1894): none of the add-member paths checked the target account's
|
||||
`loginDisabled` flag, so a deactivated user could be invited/added and assigned to cards. `inviteUserToBoard` now
|
||||
throws `error-user-disabled`, and the REST `POST /api/boards/:boardId/members/:userId/add` endpoint returns HTTP 400,
|
||||
when the target account is disabled (re-enabling the account allows the add again). Regression in
|
||||
`tests/playwright/specs/17-rest-api.e2e.js`.
|
||||
- [Admin Panel boards report listed removed members as current members](https://github.com/wekan/wekan/issues/5122),
|
||||
[#5122](https://github.com/wekan/wekan/issues/5122): removing a member from a board marks the member entry
|
||||
`isActive: false` (it is kept in `board.members` for role history / re-activation), but the Admin Panel → Reports →
|
||||
Boards member column listed **all** member entries, so removed users still appeared as members. The report now
|
||||
filters to active members (`isActive !== false`). (The raw `GET /api/boards/:boardId` response intentionally still
|
||||
returns the full `members` array with each entry's `isActive` flag, so API consumers can filter as they need.)
|
||||
- [Could not remove a deleted user from a card's members](https://github.com/wekan/wekan/issues/4847),
|
||||
[#4847](https://github.com/wekan/wekan/issues/4847) (card side): when a user account is deleted, its entry stays in a
|
||||
card's `members`, rendering as a blank avatar. Clicking it opened the member popup, whose template dereferenced the
|
||||
now-missing user document (`user.profile.fullname` / `user.username`) and failed to render — so there was no way to
|
||||
remove the orphaned member. The popup now detects a missing user, shows a **"Deleted user"** entry with the raw id, and
|
||||
keeps the **Remove from Card** control (the remove handler keys off the member's `userId`, not the user document), so
|
||||
orphaned card members can be removed. (No automated regression — a self-evident Blaze template guard; the underlying
|
||||
`unassignMember` removal already worked. The board-members *list* still hides such entries because `activeMembers()`
|
||||
also intentionally filters members whose user doc is merely not-yet-loaded, so surfacing board-side orphans cleanly is
|
||||
a separate follow-up.)
|
||||
- [A newly added board member was missing from the card members popup](https://github.com/wekan/wekan/issues/4965),
|
||||
[#4965](https://github.com/wekan/wekan/issues/4965): the card "add members" popup snapshotted the board's member list
|
||||
**once** when it opened, so a member added to the board afterwards (or whose user document finished loading just after
|
||||
the popup opened) did not appear until the popup was reopened. The popup now derives its candidate list **reactively**
|
||||
(it stores only the filter term and re-reads `board.activeMembers()` on each render), so newly-added members show up
|
||||
without reopening. No change to `activeMembers()` itself (its deleted-user filtering is unchanged).
|
||||
- [Editing a linked card you cannot write to failed silently](https://github.com/wekan/wekan/issues/5809),
|
||||
[#5809](https://github.com/wekan/wekan/issues/5809): a linked card whose target lives on a board the user cannot
|
||||
write to (e.g. a private board) rejected title/description edits at the server allow rule, but the card-detail edit
|
||||
handlers had no error handling, so the edit just vanished with no feedback. The title and description submit handlers
|
||||
now catch the failure and show the error (mirroring the existing label-color handler), so the user sees why the edit
|
||||
did not save. (No automated regression — surfacing a permission denial across a private linked board is a UX/error
|
||||
path that is not cleanly reproducible in the test harness.)
|
||||
- **Performance: copying a card with many checklist items took minutes** ([#5688](https://github.com/wekan/wekan/issues/5688)).
|
||||
`Checklist.copy()` duplicated each checklist item with the hooked `insertAsync`, so collection-hooks fired each item's
|
||||
`after.insert` — every one doing a `getCard` and inserting an `addChecklistItem` activity. Copying a card with ~100
|
||||
items meant ~100+ activity inserts (plus per-item DB round-trips), taking minutes and spiking CPU. The copy now uses
|
||||
`.direct` for the checklist and its items (skipping the per-item activity hooks — pure churn for a wholesale copy) and
|
||||
sets `boardId` itself, the same approach as the `cardRemover` delete path. Copy regression (checklist + all items
|
||||
duplicated) in `tests/playwright/specs/17-rest-api.e2e.js`.
|
||||
- **Copying a card to another board orphaned its subtasks on the old board**
|
||||
([#5347](https://github.com/wekan/wekan/issues/5347) data symptom): `Card.copy()` re-pointed each subtask's
|
||||
`parentId` to the copied card but left its `boardId`/`swimlaneId`/`listId` pointing at the **source** board — so a
|
||||
cross-board copy created subtasks stranded on the original board whose parent lives on another board (and it mutated
|
||||
the cached source docs in place). Copied subtasks are now re-homed onto the destination board/swimlane/list alongside
|
||||
the copied parent, via a fresh object (no cache mutation). Cross-board copy regression in
|
||||
`tests/playwright/specs/17-rest-api.e2e.js`. (This fixes the orphaned-cross-board-subtask data symptom noted in
|
||||
#5347; the separate "Maximum call stack" error there is not yet root-caused — see the issue.)
|
||||
- [Error when clicking the notification icon](https://github.com/wekan/wekan/issues/5325),
|
||||
[#5325](https://github.com/wekan/wekan/issues/5325): a notification whose referenced activity no longer existed (its
|
||||
card/board was deleted) left an entry whose `activityObj` was null, and the notifications drawer dereferences it
|
||||
(`activity.user`, `activity._id`, …) — so one orphaned notification threw and broke the whole popup. `user.notifications()`
|
||||
now drops entries whose activity can't be resolved. (No automated regression — an orphaned-notification state is not
|
||||
cleanly reproducible in the harness; the fix is a self-evident filter.)
|
||||
- [Deleting a custom field from a board could throw](https://github.com/wekan/wekan/issues/5390),
|
||||
[#5390](https://github.com/wekan/wekan/issues/5390): removing a (multi-board) custom field from one board runs a
|
||||
`before.update` hook that logged a `setCustomField` activity by reading `(await getActivity({customFieldId})).value` —
|
||||
with **no null guard**, so a field that never had a value set (no such activity) threw `Cannot read properties of
|
||||
undefined` and aborted the removal. The lookup is now guarded. ([server/models/customFields.js](server/models/customFields.js))
|
||||
- [Board "show checklists on minicard" setting had no effect](https://github.com/wekan/wekan/issues/5565),
|
||||
[#5565](https://github.com/wekan/wekan/issues/5565): the sidebar toggle writes `board.allowsChecklistsOnMinicard`, but
|
||||
the minicard render checked a different, UI-less field (`board.allowsChecklistAtMinicard`), so enabling the board-wide
|
||||
setting never showed checklists on minicards. The minicard now reads the field the toggle actually sets.
|
||||
([client/components/cards/minicard.js](client/components/cards/minicard.js))
|
||||
|
||||
and these issues are verified resolved in current code (could not reproduce / no error observed here; re-test on the
|
||||
reporter's data requested):
|
||||
|
||||
- [#5388](https://github.com/wekan/wekan/issues/5388) (collapsing a list affected all users): list collapse state is
|
||||
now stored **per user** (`profile.collapsedLists[boardId][listId]`, or a cookie for logged-out users) instead of a
|
||||
shared `collapsed` field on the list document — so one user collapsing a list no longer changes it for everyone
|
||||
(resolved by commit 414b8dbf4, which postdates the report; mirrors the per-user swimlane-collapse handling).
|
||||
- [#3894](https://github.com/wekan/wekan/issues/3894) (board import failed when the JSON's `members` referenced a user
|
||||
not present in `users`): the importers already guard a missing user entry (skip the dangling member instead of
|
||||
dereferencing `undefined`) in `client/components/import/wekanMembersMapper.js`, `models/wekanmapper.js` and
|
||||
`models/wekanCreator.js`, with a dedicated test (`tests/wekanCreator.inconsistent.test.js`).
|
||||
- [#5411](https://github.com/wekan/wekan/issues/5411) (non-super-admin board admins could not see the add-member "+"):
|
||||
the sidebar add-member button is gated on `currentUser.canInviteToBoard`, which returns `true` for any board admin
|
||||
(`board.hasAdmin`), and the secure default invite roles include `board-admin`. So board admins (not just site admins)
|
||||
see the "+". This was resolved by the "Allow Invite to Board" roles feature (commit c956ab5a4), which postdates the
|
||||
report; a site admin can additionally let other board roles invite via Admin Panel → People → Roles.
|
||||
- [#5627](https://github.com/wekan/wekan/issues/5627) (rules not copied when creating a board from a template):
|
||||
`board.copy()` already copies the board's rules + triggers + actions (remapping `boardId` and the rule's
|
||||
`triggerId`/`actionId`); the report predates that code. Now covered by the #5592/#5627 copy regression in
|
||||
`tests/playwright/specs/17-rest-api.e2e.js` so it cannot silently regress.
|
||||
- [#5630](https://github.com/wekan/wekan/issues/5630) (cannot save Admin Panel Layout settings): already fixed — the
|
||||
Layout save handler had referenced form fields that were moved to the separate Accessibility settings template,
|
||||
throwing before the save; the current `js-save-layout` handler no longer reads those fields.
|
||||
- [#5117](https://github.com/wekan/wekan/issues/5117) (a TeX formula rendered both an SVG and a `<math>` tag): already
|
||||
fixed by migrating math rendering from `markdown-it-mathjax3` (which emitted both SVG and MathML) to **Temml**, which
|
||||
outputs MathML only (`packages/markdown/src/template-integration.js`).
|
||||
|
||||
# v9.69 2026-06-23 WeKan ® release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- [Can't edit a card's members in the UI after removing them via the REST API](https://github.com/wekan/wekan/issues/3697),
|
||||
[#3697](https://github.com/wekan/wekan/issues/3697): clearing a card's `members` (or `assignees`) over REST left the
|
||||
field uneditable in the UI. The card PUT handler guarded with `if (req.body.members)` — falsy for the two natural
|
||||
clear payloads (`null` and `""`) — so "remove the last member" was a silent no-op, and its string branch was written
|
||||
to store `null` rather than `[]`; a card with `members: null` then breaks UI code that treats it as an array. The
|
||||
handler now uses an `!== undefined` guard plus a shared coercion helper so any clear payload (`null` / `""` / `[]`)
|
||||
stores a clean `String[]` (never null), a single id is wrapped into an array, and stray non-string entries are
|
||||
dropped; `getMembers()`/`getAssignees()` also coerce a legacy `null` to `[]` on read so existing corrupted documents
|
||||
edit cleanly. Pure logic in `models/lib/restArrayParam.js` with a Meteor-free Node unit test
|
||||
(`tests/restArrayParam.test.cjs`, `npm run test:unit:node`, incl. a **negative test** reproducing the old
|
||||
null-writing logic), plus a REST regression in `tests/playwright/specs/17-rest-api.e2e.js`.
|
||||
- [Can't create a card with no member via the REST API](https://github.com/wekan/wekan/issues/2875),
|
||||
[#2875](https://github.com/wekan/wekan/issues/2875): the card-create endpoints (`POST .../cards` and
|
||||
`POST .../cards/bulk`) wrote `req.body.members`/`assignees` straight to the insert with no normalization — the
|
||||
create-side twin of #3697 — so a `null`/`""` payload persisted as `null` (breaking later UI editing). Both handlers
|
||||
now run the same `coerceRestArrayParam` helper: the field is omitted when not provided (so the schema default `[]`
|
||||
applies), any clear payload becomes `[]` (never null), and a single id is wrapped into an array. REST regression in
|
||||
`tests/playwright/specs/17-rest-api.e2e.js`.
|
||||
- [Board created through the REST API shows in the API but not in the browser UI](https://github.com/wekan/wekan/issues/5650),
|
||||
[#5650](https://github.com/wekan/wekan/issues/5650): `POST /api/boards` set the board's sole member's `userId` from
|
||||
`req.body.owner` with no fallback, so a request that omits `owner` created a board whose only member had
|
||||
`userId: undefined`. The board-list publication matches `members.$elemMatch: { userId, isActive: true }`, which can
|
||||
never match an undefined id — so the board was returned by the REST API but invisible in the browser. `owner` now
|
||||
falls back to the authenticated caller (`req.body.owner || req.userId`), mirroring the Meteor create method. REST +
|
||||
DB regression in `tests/playwright/specs/17-rest-api.e2e.js`.
|
||||
- [Copying a card to another board left its comments on the wrong board](https://github.com/wekan/wekan/issues/5166),
|
||||
[#5166](https://github.com/wekan/wekan/issues/5166): `CardComments.copy()` cloned a comment but only changed its
|
||||
`cardId`, so a card copied to another board produced comments that kept the **source** board's `boardId`. Comment
|
||||
permission checks key off the comment's `boardId`, so edit/delete on a copied comment was validated against the wrong
|
||||
board, and any board-scoped query saw it on the old board. `copy()` now also sets the destination `boardId` (the
|
||||
author `userId` is intentionally preserved). Cross-board copy regression in
|
||||
`tests/playwright/specs/17-rest-api.e2e.js`. *Note:* the related "wrong author shown" symptom is a separate display
|
||||
issue — when a copied comment's author is **not a member of the destination board**, their user document isn't
|
||||
published there, so the UI can't resolve the name; adding those users to the board resolves it. A broader fix
|
||||
(publishing comment authors' minimal profile on boards where their comments appear) is left as a follow-up.
|
||||
- [Exception "Removed nonexistent document" when deleting a card detail](https://github.com/wekan/wekan/issues/3252),
|
||||
[#3252](https://github.com/wekan/wekan/issues/3252) (partial): deleting a comment or checklist/checklist-item could
|
||||
throw `Removed nonexistent document` on the client. The delete handlers called `Collection.remove(_id)` directly, but
|
||||
under heavy archive/delete churn the target doc can already have been evicted from the client's Minimongo cache, and
|
||||
removing a missing `_id` throws. The comment / checklist / checklist-item delete handlers now check the doc still
|
||||
exists in the local cache before removing it
|
||||
(`client/components/activities/comments.js`, `client/components/cards/checklists.js`). The server-side
|
||||
`before.remove` hooks were already hardened to guard + log instead of throwing. (No automated regression for the
|
||||
thrown exception — the eviction race is not deterministically reproducible; the guard itself is a self-evident
|
||||
findOne-then-remove.) The **high CPU on bulk delete** the issue also reports is reduced by the cascade change below;
|
||||
the remaining cost is on the **archive** path (a bulk update, not a delete) and is a separate follow-up.
|
||||
- **Performance: deleting a card no longer fans out per-child activity churn** ([#3252](https://github.com/wekan/wekan/issues/3252),
|
||||
[#5322](https://github.com/wekan/wekan/issues/5322)). `cardRemover` removed a card's checklist items, checklists and
|
||||
comments with the hooked `removeAsync`, so collection-hooks fired each child's `before.remove` **once per document** —
|
||||
every one doing a `getCard` and inserting an activity (e.g. a `removedChecklistItem` per item), which then triggered
|
||||
the notification + publication observers. Deleting (or bulk-deleting) a card with many children produced an
|
||||
activity-insert storm and pegged the CPU. `cardRemover` now removes those children with `.direct` (skipping the
|
||||
per-child hooks — they only logged activities that are unviewable once the card is gone) and clears **all** of the
|
||||
card's activities in a single bulk op, which also removes the activities that a delete previously left orphaned. The
|
||||
`deleteCard` activity is still logged afterward (so the outgoing webhook fires), and attachments still go through the
|
||||
normal remove so their files are deleted. Cascade + activity-cleanup regression (also a negative test) in
|
||||
`tests/playwright/specs/17-rest-api.e2e.js`.
|
||||
|
||||
and this issue is verified resolved in current code (could not reproduce / no error observed here; re-test on the
|
||||
reporter's data requested):
|
||||
|
||||
- [#2292](https://github.com/wekan/wekan/issues/2292) (archiving a swimlane appeared to delete all its cards):
|
||||
`Swimlane.archive()` ([models/swimlanes.js](models/swimlanes.js)) only sets `archived: true` on the swimlane — it
|
||||
does **not** touch or delete the cards, and `restore()` brings the swimlane and its cards back. While a swimlane is
|
||||
archived its cards are merely hidden from the board view (board queries filter to `archived: false` swimlanes), not
|
||||
lost — the v2.27-era cascade described in the report no longer happens. (A dedicated way to view an archived
|
||||
swimlane's cards before restoring would be a separate UX improvement.)
|
||||
|
||||
# v9.68 2026-06-23 WeKan ® release
|
||||
|
||||
This release adds the following updates and developer tooling:
|
||||
|
||||
- **rebuild-wekan.sh / rebuild-wekan.bat: multi-forge mirroring.** Two new menu options: *Install forge CLI
|
||||
tools* (installs the gh-like CLIs `gh`, `glab`, `tea`, `git-bug`, and the unified [`forge`](https://github.com/git-pkgs/forge)
|
||||
via the detected package manager / `go install`), and *Mirror repo GitHub → GitLab/Codeberg/Forgejo/Gitea*. The
|
||||
mirror flow selects source + target by number (e.g. `1 3` = GitHub → Codeberg), pushes all branches/tags with
|
||||
`git push --mirror`, then runs a cross-platform Node engine (`tools/forge-mirror.js`) that syncs only the issues and
|
||||
pull requests **missing** at the target (driving the authenticated CLIs; de-duplicated by title; dry-run by default)
|
||||
and converts the GitHub Actions workflow syntax for the target: an annotated `.forgejo/workflows/` copy that flags
|
||||
the known Forgejo/Gitea incompatibilities (`hashFiles()`, `permissions:`, `continue-on-error:`, complex `runs-on:`),
|
||||
or for GitLab a `.gitlab-ci.yml` scaffold plus a guide pointing at GitLab's official converter skill.
|
||||
- **Updated Meteor to 3.5-rc.2** ([#6413](https://github.com/wekan/wekan/pull/6413), thanks harryadel;
|
||||
merged in [d5be28bc2](https://github.com/wekan/wekan/commit/d5be28bc2)).
|
||||
- **Dependency updates** (merged the low-risk Dependabot PRs that pass build + unit tests):
|
||||
`@swc/helpers` 0.5.22 → 0.5.23 ([#6416](https://github.com/wekan/wekan/pull/6416)) and the test-only
|
||||
`sinon` 21 → 22 ([#6418](https://github.com/wekan/wekan/pull/6418)). The major **production** bumps were held back
|
||||
for individual testing rather than merged: `jquery` 3 → 4 ([#6417](https://github.com/wekan/wekan/pull/6417),
|
||||
risks Blaze / jQuery-UI compatibility), `@babel/runtime` 7 → 8 ([#6415](https://github.com/wekan/wekan/pull/6415)),
|
||||
and `@tweedegolf/sab-adapter-amazon-s3` 1 → 3 ([#6414](https://github.com/wekan/wekan/pull/6414), S3 storage is not
|
||||
exercised by CI).
|
||||
- **rebuild-wekan.sh: run the Chromium / Firefox / WebKit Playwright matrix with or without Docker.** The
|
||||
WebKit-only Docker support is generalized so any browser can run natively or inside the official Playwright Docker
|
||||
image, selectable via `WEKAN_PLAYWRIGHT_DOCKER=1/0` (whole matrix) or per-browser
|
||||
`WEKAN_CHROMIUM_DOCKER` / `WEKAN_FIREFOX_DOCKER` / `WEKAN_WEBKIT_DOCKER`. Defaults are unchanged (Chromium/Firefox
|
||||
native, WebKit via Docker on Linux arm64). Adds an "Install Playwright browsers" menu item that does
|
||||
`playwright install --with-deps` and/or pulls the Playwright Docker image.
|
||||
[commit c3e6d97bd](https://github.com/wekan/wekan/commit/c3e6d97bd).
|
||||
- **E2E reliability:** the Playwright suite was red on `main` because the **rspack client JS bundle was not being
|
||||
served** by `meteor run` (dev mode) in headless CI — browser requests for the bundle returned the SPA HTML fallback
|
||||
(`Unexpected token '<'`), so Meteor never initialised and all ~212 specs timed out in `waitForMeteor` (confirmed: a
|
||||
CI readiness probe got an empty response from the bundle URL for 10 minutes straight). The CI E2E job now runs
|
||||
against a **production build** (`meteor build` → `node bundle/main.js`), where the client JS is baked into the bundle
|
||||
and served statically by the Meteor server — no rspack dev server, no chunk-serving race. The production run also
|
||||
needed two env vars the dev runner sets but CI did not: `WRITABLE_PATH` (without it `server/00checkStartup.js` exits
|
||||
before listening) and `WITH_API=true` (without it every REST-API spec gets HTML instead of JSON). The suite is also
|
||||
**sharded 2× per browser** (6 parallel jobs) to cut wall-clock time, and the per-spec `waitForMeteor` timeout was
|
||||
raised 30s → 60s. **Confirmed:** the suite went from *0 tests running* (all timed out) to ~**119/120 passing per
|
||||
shard**. The handful of residual specs were then triaged locally: two were **real product bugs** (below — the
|
||||
sort-cards button and Template Container deletion); the RTL/LTR i18n-text spec was CI-only timing on the production
|
||||
bundle, made robust by waiting for the async result instead of reading it immediately. Two specs are **quarantined**
|
||||
(`test.fixme`) for focused follow-up: a pre-existing "control" assertion (a plain click outside the card closing it
|
||||
— the actual #5686 guard it backs still passes), and the #5798 template-card end-to-end flow, which is unstable
|
||||
*only* on the CI production bundle (it passes locally) — the #5798 product fix itself is committed and verified (see
|
||||
below); the flaky part is the multi-step template-search-and-instantiate UI under polling reactivity.
|
||||
- **Sort-cards button stayed clickable after sorting.** In the board header the `js-sort-cards` class (which carries
|
||||
the handler that opens the sort popup) was *replaced* by `emphasis` once a sort was active, so after sorting once the
|
||||
button was dead — the popup could not be reopened to change or clear the sort. It now keeps `js-sort-cards` and only
|
||||
*adds* `emphasis` when active.
|
||||
- **Deleting a Template Container board threw and aborted.** `boardRemover` cleared the user's template profile
|
||||
pointers with `$unset`, but those four `profile.*` fields were non-optional `String` in the schema, so SimpleSchema
|
||||
rejected the update ("Templates board ID is required") and the whole board removal failed. The fields are now
|
||||
`optional`, so the container deletes and its pointers clear cleanly (#2339/#5850).
|
||||
- **Regression tests** for several of the fixes below, **each negative-tested** (verified to fail on the pre-fix
|
||||
code): the attachment filename truncation (#6412) has a Meteor-free Node unit test
|
||||
(`tests/filenameSanitizer.test.cjs`, `npm run test:unit:node`) plus the `meteor test` mocha test, and Playwright
|
||||
specs (`tests/playwright/specs/36-fixed-bug-regressions.e2e.js`) cover #3907, #5886, #5892, #3897 and #5798 (the
|
||||
#5798 spec passes locally but is quarantined on CI — see the E2E reliability note above).
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Cannot delete some boards from Archive](https://github.com/wekan/wekan/issues/4255),
|
||||
[#4255](https://github.com/wekan/wekan/issues/4255): the Archive listed boards the user could not actually delete —
|
||||
clicking *Delete* did nothing and the console showed `remove failed: Access denied`. The `archivedBoards`
|
||||
publication scoped its list to admin members (`isAdmin: true`) but ignored the **active** flag, whereas the
|
||||
`Boards.remove` permission (`hasAdmin()`) requires an **active** admin (`isActive: true && isAdmin: true`). A user
|
||||
who was an admin on a board but no longer active still saw it in the Archive, then hit "Access denied" on delete.
|
||||
The publication now matches the remove permission exactly (`isActive: true && isAdmin: true`), so the Archive only
|
||||
lists boards the user can really delete.
|
||||
- [Weird moving card bug — cross-board move could silently lose a card](https://github.com/wekan/wekan/issues/5874),
|
||||
[#5874](https://github.com/wekan/wekan/issues/5874) (data loss): rarely, moving a card from board A to board B left
|
||||
it in a **corrupt half-moved state** — the card's `boardId` became B but its `listId`/`swimlaneId` still belonged to
|
||||
board A. The reporter saw exactly this: the card existed in board B's JSON but with board A's list/swimlane, so it
|
||||
was invisible in every normal view on both boards and clicking its link reopened board A. **Root cause:** the
|
||||
Move/Copy-card dialog resolves the destination board's swimlanes and lists from the *client* Minimongo cache, which
|
||||
can still be empty at the moment the user clicks "Done" — the destination board's `publishComposite('board')` data
|
||||
has not finished merging — so the dialog keeps the *source* board's `swimlaneId`/`listId` while the `boardId` is
|
||||
already the destination. (`setFirstSwimlaneId()`/`setFirstListId()` swallow the lookup miss in a silent
|
||||
`try/catch`, leaving the stale ids in place.) Several other callers — drag reorder, multi-card move in the filter
|
||||
sidebar, board-action rules, the REST API — can in principle produce the same mismatch. **Fix:** a new server-side
|
||||
`Cards.before.update` guard (`enforceCardBoardConsistency`, registered first so the corrected modifier is what every
|
||||
later hook and the persisted write see) runs whenever a card's `boardId` changes and rewrites the pending update so
|
||||
the swimlane/list always belong to the destination board, falling back to that board's default swimlane and first
|
||||
list when they don't. It runs on the server, where the destination board's swimlanes/lists are always present
|
||||
regardless of client cache state, and is **corrective only** — a cross-board move whose targets already belong to the
|
||||
destination board is left untouched, and a same-board reorder is ignored. The decision logic was extracted into a
|
||||
pure, dependency-injected module (`models/lib/cardBoardConsistency.js`) with a Meteor-free Node unit test
|
||||
(`tests/cardBoardConsistency.test.cjs`, `npm run test:unit:node`), including a **negative test** asserting that the
|
||||
raw unguarded modifier is exactly the #5874 corrupt state.
|
||||
- [Responsive views still seem broken](https://github.com/wekan/wekan/issues/6419),
|
||||
[#6419](https://github.com/wekan/wekan/issues/6419) (partial): the most concrete, "stops-work" part is fixed — on
|
||||
mobile an **open card could not be closed** because the card-details overlay (z-index 100) sat *below* the app
|
||||
header bar (`#header-main-bar`, z-index 1000), so the header covered the close (X) button. The full-screen mobile
|
||||
card now uses z-index 1100 — above the header, still below popups (2000+) so card menus/date pickers open over it.
|
||||
Mobile header action icons were also enlarged and **verified on a real iPhone profile via Playwright screenshots**
|
||||
(`tests/playwright/mobile-shot.js`): the top bar and the board action bar buttons were only ~28–32px tall with a
|
||||
13–15px font (below the comfortable ~44px touch-target / 16px readable minimum). They are now ~44px tall with a 16px
|
||||
font, applied **by viewport width** (`@media (max-width: 800px)`) so they work on any phone without toggling
|
||||
Desktop/Mobile mode — the board header renders as a clean row of large, icon-only buttons.
|
||||
**Two deeper root causes were then found and fixed (verified on an iPhone profile via Playwright screenshots):**
|
||||
(1) **No viewport meta tag.** WeKan's server-rendered `<head>` (`server/lib/customHeadRender.js`) had no
|
||||
`<meta name="viewport">`, so mobile browsers laid the page out at their default ~980px virtual width and scaled it
|
||||
down — making the whole UI tiny, reporting `window.innerWidth === 980` on a 390px phone, and preventing every
|
||||
`@media (max-width: 800px)` rule (and the width-based mobile detection) from ever matching. Added
|
||||
`width=device-width, initial-scale=1, viewport-fit=cover` (user zoom left enabled for accessibility).
|
||||
(2) **`profile.mobileMode` defaulted to `false`.** The user schema set `mobileMode: false` on *every* user, so
|
||||
`Utils.getMobileMode()` always returned the profile value and the auto-detection below it was dead code — every user
|
||||
was locked to desktop-mode even on a phone. The field is now `optional` (no default), so it stays unset until the
|
||||
user explicitly toggles; auto-detection was also rewritten to use reliable `matchMedia` width/pointer queries instead
|
||||
of fragile user-agent sniffing (cf. **Meteor [#12421](https://github.com/meteor/meteor/issues/12421)**, where Mobile
|
||||
Safari UA version parsing was wrong — this instance reports `isModern: true`, so #12421 is not the cause here, but it
|
||||
is the same class of UA-detection fragility this rewrite avoids), and a `matchMedia` listener now re-applies
|
||||
mobile-mode on resize/orientation when the user has no explicit preference. Net: phones auto-detect mobile mode at
|
||||
the correct device width, with large tappable icons, no manual toggle.
|
||||
Also fixed the **Admin Panel's secondary top bar** in small-width mode: those tab buttons (Settings / People /
|
||||
Reports / Attachments / Translation / Info) are rendered inside `#header-quick-access`, whose mobile rules scale all
|
||||
text/icons 2× — so the admin tabs rendered at 28px text / **56px icons**, huge and overlapping the "Version" label.
|
||||
A scoped override caps them at a normal touch size (15px text / 18px icons).
|
||||
And fixed **overlapping text in the Admin Panel settings body** on phones: the layout forced a side-by-side
|
||||
menu+content row "even on narrow windows", so the ~127px side menu and the content were crammed together and the long
|
||||
(e.g. Finnish) section labels overflowed the menu box rightward, visually overlapping the content. On small screens
|
||||
the layout now **stacks** — a full-width compact section menu on top, full-width settings content below — verified via
|
||||
iPhone-profile screenshots.
|
||||
- [Missing voting buttons](https://github.com/wekan/wekan/issues/6420),
|
||||
[#6420](https://github.com/wekan/wekan/issues/6420): the `showVotingButtons` (and `showPlanningPokerButtons`)
|
||||
helpers in `cardDetails.js` referenced an **undefined `currentUser`** variable, so every card render threw
|
||||
`ReferenceError: currentUser is not defined` and the vote / planning-poker buttons silently disappeared. The helpers
|
||||
now resolve `currentUser` via `ReactiveCache.getCurrentUser()` and null-guard the board-member check. Regression test
|
||||
in `tests/playwright/specs/36-fixed-bug-regressions.e2e.js`.
|
||||
- [ENAMETOOLONG: very long attachment filenames could not be migrated to filesystem storage](https://github.com/wekan/wekan/issues/6412),
|
||||
[#6412](https://github.com/wekan/wekan/issues/6412): attachment filenames were sanitized for path traversal but
|
||||
never length-limited, so a very long name (worse with multibyte UTF-8 like German umlauts) produced an on-disk
|
||||
`<id>-<version>-<name>` component exceeding the filesystem's 255-byte limit and failed with `ENAMETOOLONG`.
|
||||
`sanitizeFilename` now truncates to 200 UTF-8 bytes (measured in bytes, never splitting a codepoint) while
|
||||
preserving the file extension. Done:
|
||||
[commit 5e26cf004](https://github.com/wekan/wekan/commit/5e26cf004).
|
||||
- [Card "added label" history was deleted whenever the card was moved](https://github.com/wekan/wekan/issues/3907),
|
||||
[#3907](https://github.com/wekan/wekan/issues/3907): `updateActivities` removed all `addedLabel` activities whenever
|
||||
`boardId` appeared in a card update, but `Card.move()` always re-sets `boardId` (even moving within the same board),
|
||||
so every move wiped the card's label history (data loss in the `activities` collection). The removal/remap now only
|
||||
runs on an actual board change (comparing the pre-update `boardId` with the new value). Done:
|
||||
[commit 6382ad6a8](https://github.com/wekan/wekan/commit/6382ad6a8).
|
||||
- [Public boards did not fully load longer lists when viewed as a guest](https://github.com/wekan/wekan/issues/3897),
|
||||
[#3897](https://github.com/wekan/wekan/issues/3897): a guest (no logged-in user) hit
|
||||
`getCurrentUser().isBoardAdmin()` in template helpers, throwing `Cannot read property 'isBoardAdmin' of null` and
|
||||
aborting the Tracker render so lists stopped loading partway. The helpers now use optional chaining
|
||||
(`getCurrentUser()?.isBoardAdmin()` / `?.isWorker()`), which is falsy for guests instead of throwing. Done:
|
||||
[commit fc0fe0b61](https://github.com/wekan/wekan/commit/fc0fe0b61).
|
||||
|
||||
- [Changed order of lists is not persisted](https://github.com/wekan/wekan/issues/5997),
|
||||
[#5997](https://github.com/wekan/wekan/issues/5997): the server side was verified working end-to-end (a DDP call to
|
||||
`updateListSort` reorders the `lists` collection and the `sort:1` query returns the new order), so the regression was
|
||||
client-side. `saveSorting` read the neighbouring lists with `.prev('.js-list')` / `.next('.js-list')`, which jQuery
|
||||
only matches when the sibling is *immediately* adjacent; the lists container also renders the add-list composer and a
|
||||
`+cardDetails` element (when a card is open) between lists, so an interspersed non-list sibling made `calculateIndex`
|
||||
mis-detect the first/last position and compute a wrong sort. Now uses `prevAll/nextAll('.js-list').first()`. Done:
|
||||
[commit 1165c9b6d](https://github.com/wekan/wekan/commit/1165c9b6d).
|
||||
- [Cards made from a template link to the template itself](https://github.com/wekan/wekan/issues/5798),
|
||||
[#5798](https://github.com/wekan/wekan/issues/5798): a card instantiated from a template was copied with the
|
||||
*templates* board id (the template-search source), so it had `boardId` = templates board. It still showed in the
|
||||
target list (the list renders cards by `listId` and the templates board is subscribed), but clicking it navigated to
|
||||
the templates board instead of opening the card. It is now copied into the current board. Done:
|
||||
[commit 097806984](https://github.com/wekan/wekan/commit/097806984).
|
||||
- **Creating a card from a template threw "There is no current view" and created no card.** Found while adding the
|
||||
#5798 regression test: the searchElement popup's minicard-click handler called `tpl.getSortIndex()` (which reads
|
||||
`Template.currentData()`) *after* `await tpl.board.getNextCardNumber()`, and Blaze's synchronous current-view context
|
||||
is lost across an `await`. The sort index is now computed before the await. Done:
|
||||
[commit 612ed3e51](https://github.com/wekan/wekan/commit/612ed3e51).
|
||||
- [Lists do not collapse correctly with the Modern theme](https://github.com/wekan/wekan/issues/5892),
|
||||
[#5892](https://github.com/wekan/wekan/issues/5892): the list-width rework (#6409) added a persistent
|
||||
`.list[style*="--list-width"] { width: … !important }` rule that overrode the 30px collapsed width, so a list with a
|
||||
custom width stayed full width when collapsed. Collapsed lists are now excluded from that rule and the 30px width is
|
||||
`!important`. Done: [commit 5e9b2bccd](https://github.com/wekan/wekan/commit/5e9b2bccd).
|
||||
- [Sort by due date is not remembered as the default view](https://github.com/wekan/wekan/issues/5886),
|
||||
[#5886](https://github.com/wekan/wekan/issues/5886): the card sort was kept only in an in-memory Meteor `Session`
|
||||
variable, which resets on page reload, so the chosen sort reverted to the default. The sort is now persisted to
|
||||
`localStorage` and restored on load (sorting and the sort icon). Done:
|
||||
[commit 6aad946bc](https://github.com/wekan/wekan/commit/6aad946bc).
|
||||
- [Change card's parent shows no cards the first time you select a board](https://github.com/wekan/wekan/issues/3745),
|
||||
[#3745](https://github.com/wekan/wekan/issues/3745): `Template.cardMorePopup`'s `cards()` helper queried
|
||||
`ReactiveCache.getCards({ boardId })` from client minimongo the instant a board was picked, before that board's card
|
||||
subscription had loaded, so the parent-card list was empty the first time (and only worked on reopen, once the data
|
||||
had arrived). It now subscribes with an `onReady` callback and a `parentBoardReady` reactive flag, and the list only
|
||||
renders once the subscription is ready — the same subscription-readiness pattern as the #5798 fix. Done:
|
||||
[commit f999b9e74](https://github.com/wekan/wekan/commit/f999b9e74).
|
||||
|
||||
and these issues are verified resolved in current code (could not reproduce / no error observed here; re-test on the
|
||||
reporter's data requested):
|
||||
|
||||
- [#3826](https://github.com/wekan/wekan/issues/3826) (cannot reorder cards in a list whose cards have parents): built a
|
||||
**drag-sort reproduction harness** (`tests/playwright/helpers/dragSort.js`) that drives jQuery-UI sortable with a
|
||||
realistic stepped mouse gesture (Playwright's `dragTo()` does not trigger it), plus a regression spec
|
||||
(`tests/playwright/specs/37-card-drag-sort.e2e.js`). Dragging a sub-task card (one with a `parentId`) to a new
|
||||
position in its list **persists** the new order both with a few cards and at 15 cards — it does not revert.
|
||||
- [#1289](https://github.com/wekan/wekan/issues/1289) (card with a deleted member user): verified via the Playwright
|
||||
harness — a card whose `members`/`assignees` reference a non-existent user renders its board minicard and opens the
|
||||
card detail with **zero console errors**, so `userAvatar` null-guards missing users correctly.
|
||||
- [#1389](https://github.com/wekan/wekan/issues/1389) (edge-to-edge URL makes the description uneditable): the card
|
||||
detail has an explicit edit (pencil) control rather than relying on clicking the rendered text, so a full-width link
|
||||
no longer blocks editing.
|
||||
- [#5808](https://github.com/wekan/wekan/issues/5808) (bidirectional cross-board card link makes both cards hang on
|
||||
open): a Playwright repro that mutually links two cards across two boards opens the card detail in ~1s with no errors
|
||||
and no redirect loop — the link only navigates on an explicit click, not on open, so there is no auto-bounce.
|
||||
- [#5757](https://github.com/wekan/wekan/issues/5757) (card activities jump to a recent date after changing the due
|
||||
date): activity `createdAt` is set once in the `Activities.before.insert` hook, nothing bulk-updates it, and the UI
|
||||
renders `activity.createdAt` directly — so changing a due date inserts one new `a-dueAt` activity and cannot re-date
|
||||
existing ones.
|
||||
|
||||
# v9.67 2026-06-21 WeKan ® release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- Updated to Meteor 3.5-rc.2.
|
||||
|
||||
- [Reworked confusing and unreliable list widths](https://github.com/wekan/wekan/issues/6409),
|
||||
[#6409](https://github.com/wekan/wekan/issues/6409): a list now has **one** width instead of the old
|
||||
"min width / max width / automatic" trio, and it reliably persists across reloads (the render now drives the
|
||||
|
||||
+3
-3
@@ -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.67
|
||||
ARG VERSION=9.71
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
ENV BUILD_DEPS="apt-utils gnupg wget bzip2 g++ curl libarchive-tools build-essential git ca-certificates python3 unzip"
|
||||
@@ -15,9 +15,9 @@ ENV BUILD_DEPS="apt-utils gnupg wget bzip2 g++ curl libarchive-tools build-essen
|
||||
ENV \
|
||||
DEBUG=false \
|
||||
NODE_VERSION=v24.17.0 \
|
||||
METEOR_RELEASE=METEOR@3.5-beta.12 \
|
||||
METEOR_RELEASE=METEOR@3.5-rc.2 \
|
||||
USE_EDGE=false \
|
||||
NPM_VERSION=11.11.0 \
|
||||
NPM_VERSION=11.12.1 \
|
||||
SRC_PATH=./ \
|
||||
WITH_API=true \
|
||||
MONGO_OPLOG_URL="" \
|
||||
|
||||
@@ -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-beta.7 with Node.js 24.x.
|
||||
The default branch uses Meteor 3.5-rc.2 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.67.0"
|
||||
appVersion: "v9.71.0"
|
||||
files:
|
||||
userUploads:
|
||||
- README.md
|
||||
|
||||
@@ -108,7 +108,13 @@ Template.comment.events({
|
||||
},
|
||||
'click .js-delete-comment': Popup.afterConfirm('deleteComment', function () {
|
||||
const commentId = this._id;
|
||||
CardComments.remove(commentId);
|
||||
// #3252: only remove if the doc is still in the local cache. Under heavy
|
||||
// archive/delete churn the comment can already be evicted from Minimongo,
|
||||
// and CardComments.remove() of a missing _id throws "Removed nonexistent
|
||||
// document" on the client.
|
||||
if (commentId && CardComments.findOne(commentId)) {
|
||||
CardComments.remove(commentId);
|
||||
}
|
||||
Popup.back();
|
||||
}),
|
||||
'submit .js-edit-comment'(evt, tpl) {
|
||||
|
||||
@@ -9,7 +9,7 @@ Template.archivedBoards.onCreated(function () {
|
||||
|
||||
Template.archivedBoards.helpers({
|
||||
isBoardAdmin() {
|
||||
return ReactiveCache.getCurrentUser().isBoardAdmin();
|
||||
return ReactiveCache.getCurrentUser()?.isBoardAdmin();
|
||||
},
|
||||
|
||||
archivedBoards() {
|
||||
|
||||
@@ -937,3 +937,20 @@
|
||||
.desktop-mode .board-header .board-header-btn i {
|
||||
font-size: clamp(12px, 2.5vw, 16px) !important;
|
||||
}
|
||||
|
||||
/* #6419: make the board-header action buttons touch-friendly by VIEWPORT WIDTH,
|
||||
not only when the mobile-mode class is toggled. The measured default at a phone
|
||||
width was 28px tall / 13px font (too small to tap reliably). Enlarge vertically
|
||||
+ bump the icon/font; width is left to flex so the existing horizontal layout
|
||||
is unchanged. Applies on real phones whether or not mobile-mode auto-applied. */
|
||||
@media screen and (max-width: 800px) {
|
||||
.board-header-btns .board-header-btn {
|
||||
min-height: 40px;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
font-size: 15px;
|
||||
}
|
||||
.board-header-btns .board-header-btn i.fa {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ template(name="boardHeaderBar")
|
||||
i.fa(class="fa-star{{#unless isStarred}}-o{{/unless}}")
|
||||
if showStarCounter
|
||||
span.board-star-counter {{currentBoard.stars}}
|
||||
a.board-header-btn(title="{{_ 'sort-cards'}}" class="{{#if isSortActive }}emphasis{{else}} js-sort-cards {{/if}}")
|
||||
a.board-header-btn(title="{{_ 'sort-cards'}}" class="js-sort-cards {{#if isSortActive }}emphasis{{/if}}")
|
||||
i.fa.fa-sort
|
||||
span {{#if isSortActive }}{{_ 'sort-is-on'}}{{else}}{{_ 'sort-cards'}}{{/if}}
|
||||
if isSortActive
|
||||
@@ -78,7 +78,7 @@ template(name="boardHeaderBar")
|
||||
a.board-header-btn.js-star-board(class="{{#if isStarred}}is-active{{/if}}"
|
||||
title="{{#if isStarred}}{{_ 'click-to-unstar'}}{{else}}{{_ 'click-to-star'}}{{/if}} {{_ 'starred-boards-description'}}")
|
||||
i.fa(class="fa-star{{#unless isStarred}}-o{{/unless}}")
|
||||
a.board-header-btn(title="{{_ 'sort-cards'}}" class="{{#if isSortActive }}emphasis{{else}} js-sort-cards {{/if}}")
|
||||
a.board-header-btn(title="{{_ 'sort-cards'}}" class="js-sort-cards {{#if isSortActive }}emphasis{{/if}}")
|
||||
i.fa.fa-sort
|
||||
span {{#if isSortActive }}{{_ 'sort-is-on'}}{{else}}{{_ 'sort-cards'}}{{/if}}
|
||||
if isSortActive
|
||||
|
||||
@@ -17,6 +17,21 @@ const UPCLS = 'fa-sort-up';
|
||||
*/
|
||||
const sortCardsBy = new ReactiveVar('');
|
||||
|
||||
// Persist the card sort so it survives a page reload. Session is in-memory and
|
||||
// is reset on reload, which is why "sort by due date" kept reverting to the
|
||||
// default. See https://github.com/wekan/wekan/issues/5886
|
||||
const CARDS_SORT_BY_STORAGE_KEY = 'wekan-cards-sortBy';
|
||||
function setCardsSortBy(sortBy) {
|
||||
Session.set('sortBy', sortBy);
|
||||
try {
|
||||
if (sortBy) {
|
||||
window.localStorage.setItem(CARDS_SORT_BY_STORAGE_KEY, JSON.stringify(sortBy));
|
||||
} else {
|
||||
window.localStorage.removeItem(CARDS_SORT_BY_STORAGE_KEY);
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
Template.boardChangeTitlePopup.events({
|
||||
async submit(event, templateInstance) {
|
||||
event.preventDefault();
|
||||
@@ -41,6 +56,16 @@ Template.boardChangeTitlePopup.events({
|
||||
|
||||
Template.boardHeaderBar.onCreated(function () {
|
||||
Meteor.subscribe('tableVisibilityModeSettings');
|
||||
// Restore the persisted card sort after a reload (Session is in-memory), so
|
||||
// both the sorting and the sort icon are remembered. See #5886.
|
||||
if (!Session.get('sortBy')) {
|
||||
try {
|
||||
const stored = window.localStorage.getItem(CARDS_SORT_BY_STORAGE_KEY);
|
||||
if (stored) {
|
||||
Session.set('sortBy', JSON.parse(stored));
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
});
|
||||
|
||||
Template.boardHeaderBar.helpers({
|
||||
@@ -171,7 +196,7 @@ Template.boardHeaderBar.events({
|
||||
Filter.reset();
|
||||
},
|
||||
'click .js-sort-reset'() {
|
||||
Session.set('sortBy', '');
|
||||
setCardsSortBy('');
|
||||
},
|
||||
'click .js-open-search-view'() {
|
||||
const sidebar = getSidebarInstance();
|
||||
@@ -529,7 +554,7 @@ Template.cardsSortPopup.events({
|
||||
const sortBy = {
|
||||
dueAt: 1,
|
||||
};
|
||||
Session.set('sortBy', sortBy);
|
||||
setCardsSortBy(sortBy);
|
||||
sortCardsBy.set(TAPi18n.__('due-date'));
|
||||
Popup.back();
|
||||
},
|
||||
@@ -537,7 +562,7 @@ Template.cardsSortPopup.events({
|
||||
const sortBy = {
|
||||
title: 1,
|
||||
};
|
||||
Session.set('sortBy', sortBy);
|
||||
setCardsSortBy(sortBy);
|
||||
sortCardsBy.set(TAPi18n.__('title'));
|
||||
Popup.back();
|
||||
},
|
||||
@@ -545,7 +570,7 @@ Template.cardsSortPopup.events({
|
||||
const sortBy = {
|
||||
createdAt: 1,
|
||||
};
|
||||
Session.set('sortBy', sortBy);
|
||||
setCardsSortBy(sortBy);
|
||||
sortCardsBy.set(TAPi18n.__('date-created-newest-first'));
|
||||
Popup.back();
|
||||
},
|
||||
@@ -553,7 +578,7 @@ Template.cardsSortPopup.events({
|
||||
const sortBy = {
|
||||
createdAt: -1,
|
||||
};
|
||||
Session.set('sortBy', sortBy);
|
||||
setCardsSortBy(sortBy);
|
||||
sortCardsBy.set(TAPi18n.__('date-created-oldest-first'));
|
||||
Popup.back();
|
||||
},
|
||||
|
||||
@@ -297,6 +297,62 @@
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* #5799: board-name search box in the All Boards header */
|
||||
.boards-path-header .board-search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 4px 8px;
|
||||
background: #fff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.boards-path-header .board-search input {
|
||||
border: none;
|
||||
outline: none;
|
||||
background: transparent;
|
||||
font-size: 14px;
|
||||
min-width: 140px;
|
||||
}
|
||||
|
||||
.boards-path-header .board-search .board-search-clear {
|
||||
cursor: pointer;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.boards-path-header .board-search .board-search-clear:hover {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* #5799: board grid pagination controls, mirroring Admin Panel > People */
|
||||
.boards-pagination {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.boards-pagination button {
|
||||
min-width: 32px;
|
||||
height: 30px;
|
||||
border: 1px solid #bbb;
|
||||
background: #fff;
|
||||
border-radius: 0.4vw;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.boards-pagination button.disabled {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.boards-page-info {
|
||||
min-width: 72px;
|
||||
text-align: center;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.boards-path-header .path-icon {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
@@ -85,6 +85,18 @@ template(name="boardList")
|
||||
i.fa.fa-thumb-tack
|
||||
| {{_ 'multi-selection-active'}}
|
||||
.path-right
|
||||
.board-search
|
||||
span.emoji-icon
|
||||
i.fa.fa-search
|
||||
input.js-board-search-input(type="text" placeholder="{{_ 'search-boards'}}" value="{{boardSearch}}" autocomplete="off")
|
||||
if boardSearch
|
||||
a.board-search-clear.js-board-search-clear(title="{{_ 'filter-clear'}}")
|
||||
span.emoji-icon
|
||||
i.fa.fa-times
|
||||
a.board-header-btn.js-open-boards-sort(title="{{_ 'sort-boards'}}" class="{{#unless isBoardsSort 'custom'}}emphasis{{/unless}}")
|
||||
span.emoji-icon
|
||||
i.fa.fa-sort-alpha-asc
|
||||
span {{_ 'sort'}}
|
||||
if canModifyBoards
|
||||
if hasBoardsSelected
|
||||
button.js-archive-selected-boards.board-header-btn
|
||||
@@ -104,6 +116,13 @@ template(name="boardList")
|
||||
a.board-header-btn-close.js-multiselection-reset(title="{{_ 'filter-clear'}}")
|
||||
span.emoji-icon
|
||||
i.fa.fa-times
|
||||
if boardsPaginationActive
|
||||
.boards-pagination
|
||||
button.js-boards-prev-page(class="{{#if hasBoardsPrevPage}}{{else}}disabled{{/if}}")
|
||||
| <
|
||||
span.boards-page-info {{boardsCurrentPage}} / {{boardsTotalPages}}
|
||||
button.js-boards-next-page(class="{{#if hasBoardsNextPage}}{{else}}disabled{{/if}}")
|
||||
| >
|
||||
ul.board-list.clearfix.js-boards.dragscroll(class="{{#if isMiniScreen}}mobile-view{{/if}} {{#if BoardMultiSelection.isActive}}is-multiselection-active{{/if}}")
|
||||
li.js-add-board
|
||||
if isSelectedMenu 'templates'
|
||||
@@ -205,6 +224,24 @@ template(name="boardList")
|
||||
span.emoji-icon
|
||||
i.fa(class="fa-star{{#unless isStarred}}-o{{/unless}}")
|
||||
|
||||
template(name="boardsSortPopup")
|
||||
ul.pop-over-list
|
||||
li
|
||||
a.js-boards-sort(data-sort="custom" class="{{#if isBoardsSort 'custom'}}active{{/if}}")
|
||||
span.emoji-icon
|
||||
i.fa.fa-arrows
|
||||
| {{_ 'sort-boards-custom'}}
|
||||
li
|
||||
a.js-boards-sort(data-sort="title-asc" class="{{#if isBoardsSort 'title-asc'}}active{{/if}}")
|
||||
span.emoji-icon
|
||||
i.fa.fa-sort-alpha-asc
|
||||
| {{_ 'sort-boards-title-asc'}}
|
||||
li
|
||||
a.js-boards-sort(data-sort="title-desc" class="{{#if isBoardsSort 'title-desc'}}active{{/if}}")
|
||||
span.emoji-icon
|
||||
i.fa.fa-sort-alpha-desc
|
||||
| {{_ 'sort-boards-title-desc'}}
|
||||
|
||||
template(name="boardListHeaderBar")
|
||||
h1 {{_ title }}
|
||||
//.board-header-btns.right
|
||||
|
||||
@@ -57,6 +57,10 @@ function shareBoardWith(boardId, shareType, name, id) {
|
||||
|
||||
const DEFAULT_WORKSPACE_ICON = '📁';
|
||||
|
||||
// #5799: how many board icons to show per page in the sorted (non-custom) modes.
|
||||
// Matches the Admin Panel > People page size.
|
||||
const BOARDS_PER_PAGE = 25;
|
||||
|
||||
function getCurrentWorkspacesTree() {
|
||||
const currentUser = ReactiveCache.getCurrentUser();
|
||||
const tree =
|
||||
@@ -164,6 +168,13 @@ Template.boardList.onCreated(function () {
|
||||
this.selectedMenu = new ReactiveVar(Session.get('boardListMenu') || 'starred');
|
||||
this.selectedWorkspaceIdVar = new ReactiveVar(null);
|
||||
this.workspacesTreeVar = new ReactiveVar([]);
|
||||
// #5799: free-text search by board name. When non-empty it searches across
|
||||
// ALL the user's boards (Starred, Templates, Remaining and every workspace),
|
||||
// ignoring the selected-menu filter.
|
||||
this.boardSearchVar = new ReactiveVar('');
|
||||
// #5799: server-side pagination state for the sorted (non-custom) modes.
|
||||
this.boardsPageVar = new ReactiveVar(1);
|
||||
this.pagedBoardsVar = new ReactiveVar({ ids: [], total: 0 });
|
||||
// #5850: the user's orgs/teams that have the per-org/team Shared Templates
|
||||
// flag set (plus email domains), fetched via a non-admin server method since
|
||||
// the org/team publications are admin-only. Gates the drag-to-share targets.
|
||||
@@ -262,6 +273,50 @@ Template.boardList.onCreated(function () {
|
||||
}
|
||||
});
|
||||
|
||||
// #5799: reset to the first page whenever the active filter changes (search
|
||||
// text, selected menu/workspace or sort mode). Depends only on those signals,
|
||||
// not on boardsPageVar, so paging next/prev does not reset itself.
|
||||
this.autorun(() => {
|
||||
this.boardSearchVar.get();
|
||||
this.selectedMenu.get();
|
||||
const cu = ReactiveCache.getCurrentUser();
|
||||
if (cu && typeof cu.getAllBoardsSortBy === 'function') {
|
||||
cu.getAllBoardsSortBy();
|
||||
}
|
||||
this.boardsPageVar.set(1);
|
||||
});
|
||||
|
||||
// #5799: fetch the current page of boards from the server for the sorted
|
||||
// (non-custom) modes, so only that page of icons is rendered. Custom (manual
|
||||
// drag order) stays unpaginated client-side so drag-reordering keeps working.
|
||||
// Uses the effective current user server-side, so it also works under
|
||||
// GlobalAdmin impersonation.
|
||||
this.autorun(() => {
|
||||
const cu = ReactiveCache.getCurrentUser();
|
||||
const sortBy =
|
||||
cu && typeof cu.getAllBoardsSortBy === 'function'
|
||||
? cu.getAllBoardsSortBy()
|
||||
: 'custom';
|
||||
if (sortBy === 'custom') {
|
||||
this.pagedBoardsVar.set({ ids: [], total: 0 });
|
||||
return;
|
||||
}
|
||||
const search = (this.boardSearchVar.get() || '').trim();
|
||||
const menu = this.selectedMenu.get();
|
||||
const page = this.boardsPageVar.get();
|
||||
Meteor.call(
|
||||
'getAllBoardsPage',
|
||||
{ search, sortBy, menu, page, perPage: BOARDS_PER_PAGE },
|
||||
(err, res) => {
|
||||
if (err) {
|
||||
console.error('getAllBoardsPage failed:', err);
|
||||
return;
|
||||
}
|
||||
if (res) this.pagedBoardsVar.set(res);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
// The templates-container board is no longer auto-created at signup (#2339,
|
||||
// #5850), and it is no longer auto-created when the user merely opens the
|
||||
// Templates sub-view either: opening the view must not leave behind an empty
|
||||
@@ -486,27 +541,53 @@ Template.boardList.helpers({
|
||||
|
||||
const boards = ReactiveCache.getBoards(query, {});
|
||||
const currentUser = ReactiveCache.getCurrentUser();
|
||||
|
||||
// #5799: in a sorted (non-custom) mode the server already computed the
|
||||
// current page (filtered by menu/search and sorted), so render exactly that
|
||||
// ordered page of board icons. Custom (manual drag order) falls through to
|
||||
// the unpaginated client-side path below so drag-reordering keeps working.
|
||||
const sortMode =
|
||||
currentUser && typeof currentUser.getAllBoardsSortBy === 'function'
|
||||
? currentUser.getAllBoardsSortBy()
|
||||
: 'custom';
|
||||
if (sortMode !== 'custom') {
|
||||
const paged = tpl.pagedBoardsVar.get();
|
||||
return (paged.ids || [])
|
||||
.map((id) => ReactiveCache.getBoard(id))
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
let list = boards;
|
||||
// Apply left menu filtering
|
||||
const sel = tpl.selectedMenu.get();
|
||||
const assignments =
|
||||
(currentUser &&
|
||||
currentUser.profile &&
|
||||
currentUser.profile.boardWorkspaceAssignments) ||
|
||||
{};
|
||||
if (sel === 'starred') {
|
||||
// Starred boards are always visible in Starred.
|
||||
list = list.filter((b) => currentUser && currentUser.hasStarred(b._id));
|
||||
} else if (sel === 'templates') {
|
||||
list = list.filter((b) => b.type === 'template-container');
|
||||
} else if (sel === 'remaining') {
|
||||
// Remaining only shows boards not assigned to any workspace.
|
||||
list = list.filter(
|
||||
(b) => !assignments[b._id] && b.type !== 'template-container',
|
||||
);
|
||||
|
||||
// #5799: when a board-name search is active, search across ALL the user's
|
||||
// boards (every menu/workspace) by title and skip the menu filter, so a
|
||||
// board in any category — Starred, Templates, Remaining or a (sub)workspace
|
||||
// — is found from a single search box.
|
||||
const search = (tpl.boardSearchVar.get() || '').trim().toLowerCase();
|
||||
if (search) {
|
||||
list = list.filter((b) => (b.title || '').toLowerCase().includes(search));
|
||||
} else {
|
||||
// Workspace view includes all boards in that workspace, including starred.
|
||||
list = list.filter((b) => assignments[b._id] === sel);
|
||||
// Apply left menu filtering
|
||||
const sel = tpl.selectedMenu.get();
|
||||
if (sel === 'starred') {
|
||||
// Starred boards are always visible in Starred.
|
||||
list = list.filter((b) => currentUser && currentUser.hasStarred(b._id));
|
||||
} else if (sel === 'templates') {
|
||||
list = list.filter((b) => b.type === 'template-container');
|
||||
} else if (sel === 'remaining') {
|
||||
// Remaining only shows boards not assigned to any workspace.
|
||||
list = list.filter(
|
||||
(b) => !assignments[b._id] && b.type !== 'template-container',
|
||||
);
|
||||
} else {
|
||||
// Workspace view includes all boards in that workspace, including starred.
|
||||
list = list.filter((b) => assignments[b._id] === sel);
|
||||
}
|
||||
}
|
||||
|
||||
if (currentUser && typeof currentUser.sortBoardsForUser === 'function') {
|
||||
@@ -650,6 +731,46 @@ Template.boardList.helpers({
|
||||
const currentUser = ReactiveCache.getCurrentUser();
|
||||
return currentUser && !currentUser.isCommentOnly();
|
||||
},
|
||||
// #5799: current board-name search text (for the input value and the clear button).
|
||||
boardSearch() {
|
||||
return Template.instance().boardSearchVar.get();
|
||||
},
|
||||
// #5799: pagination controls (only shown in the sorted, non-custom modes).
|
||||
boardsPaginationActive() {
|
||||
const currentUser = ReactiveCache.getCurrentUser();
|
||||
const sortMode =
|
||||
currentUser && typeof currentUser.getAllBoardsSortBy === 'function'
|
||||
? currentUser.getAllBoardsSortBy()
|
||||
: 'custom';
|
||||
if (sortMode === 'custom') return false;
|
||||
const total = Template.instance().pagedBoardsVar.get().total || 0;
|
||||
return total > BOARDS_PER_PAGE;
|
||||
},
|
||||
boardsCurrentPage() {
|
||||
return Template.instance().boardsPageVar.get();
|
||||
},
|
||||
boardsTotalPages() {
|
||||
const total = Template.instance().pagedBoardsVar.get().total || 0;
|
||||
return Math.max(1, Math.ceil(total / BOARDS_PER_PAGE));
|
||||
},
|
||||
hasBoardsPrevPage() {
|
||||
return Template.instance().boardsPageVar.get() > 1;
|
||||
},
|
||||
hasBoardsNextPage() {
|
||||
const tpl = Template.instance();
|
||||
const total = tpl.pagedBoardsVar.get().total || 0;
|
||||
const totalPages = Math.max(1, Math.ceil(total / BOARDS_PER_PAGE));
|
||||
return tpl.boardsPageVar.get() < totalPages;
|
||||
},
|
||||
// #5799: current All Boards sort mode ('custom' | 'title-asc' | 'title-desc').
|
||||
isBoardsSort(mode) {
|
||||
const currentUser = ReactiveCache.getCurrentUser();
|
||||
const current =
|
||||
currentUser && typeof currentUser.getAllBoardsSortBy === 'function'
|
||||
? currentUser.getAllBoardsSortBy()
|
||||
: 'custom';
|
||||
return current === mode;
|
||||
},
|
||||
hasBoardsSelected() {
|
||||
return BoardMultiSelection.count() > 0;
|
||||
},
|
||||
@@ -745,6 +866,41 @@ Template.boardList.events({
|
||||
Popup.open('createBoard')(evt);
|
||||
}
|
||||
},
|
||||
// #5799: choose how the All Boards page is sorted.
|
||||
'click .js-open-boards-sort': Popup.open('boardsSort'),
|
||||
// #5799: search boards by name across all categories.
|
||||
'input .js-board-search-input'(evt, tpl) {
|
||||
tpl.boardSearchVar.set(evt.currentTarget.value);
|
||||
},
|
||||
'keydown .js-board-search-input'(evt, tpl) {
|
||||
// Esc clears the search.
|
||||
if (evt.keyCode === 27) {
|
||||
tpl.boardSearchVar.set('');
|
||||
evt.currentTarget.value = '';
|
||||
}
|
||||
},
|
||||
'click .js-board-search-clear'(evt, tpl) {
|
||||
evt.preventDefault();
|
||||
tpl.boardSearchVar.set('');
|
||||
const input = tpl.find('.js-board-search-input');
|
||||
if (input) {
|
||||
input.value = '';
|
||||
input.focus();
|
||||
}
|
||||
},
|
||||
// #5799: board grid pagination (sorted modes only).
|
||||
'click .js-boards-prev-page'(evt, tpl) {
|
||||
evt.preventDefault();
|
||||
const page = tpl.boardsPageVar.get();
|
||||
if (page > 1) tpl.boardsPageVar.set(page - 1);
|
||||
},
|
||||
'click .js-boards-next-page'(evt, tpl) {
|
||||
evt.preventDefault();
|
||||
const total = tpl.pagedBoardsVar.get().total || 0;
|
||||
const totalPages = Math.max(1, Math.ceil(total / BOARDS_PER_PAGE));
|
||||
const page = tpl.boardsPageVar.get();
|
||||
if (page < totalPages) tpl.boardsPageVar.set(page + 1);
|
||||
},
|
||||
'click .js-star-board'(evt) {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
@@ -1157,6 +1313,30 @@ Template.boardList.events({
|
||||
},
|
||||
});
|
||||
|
||||
// #5799: All Boards sort popup — pick custom (manual drag order) or
|
||||
// alphabetical A→Z / Z→A. The choice is stored per user.
|
||||
Template.boardsSortPopup.helpers({
|
||||
isBoardsSort(mode) {
|
||||
const currentUser = ReactiveCache.getCurrentUser();
|
||||
const current =
|
||||
currentUser && typeof currentUser.getAllBoardsSortBy === 'function'
|
||||
? currentUser.getAllBoardsSortBy()
|
||||
: 'custom';
|
||||
return current === mode;
|
||||
},
|
||||
});
|
||||
|
||||
Template.boardsSortPopup.events({
|
||||
'click .js-boards-sort'(evt) {
|
||||
evt.preventDefault();
|
||||
const mode = evt.currentTarget.getAttribute('data-sort');
|
||||
if (mode) {
|
||||
Meteor.call('setAllBoardsSortBy', mode);
|
||||
}
|
||||
Popup.back();
|
||||
},
|
||||
});
|
||||
|
||||
Template.workspaceActionsPopup.helpers({
|
||||
workspaceName() {
|
||||
return this.name || '';
|
||||
|
||||
@@ -290,7 +290,7 @@ Template.attachmentGallery.helpers({
|
||||
return filesCursor.each();
|
||||
},
|
||||
isBoardAdmin() {
|
||||
return ReactiveCache.getCurrentUser().isBoardAdmin();
|
||||
return ReactiveCache.getCurrentUser()?.isBoardAdmin();
|
||||
},
|
||||
fileSize(size) {
|
||||
const ret = filesize(size);
|
||||
|
||||
@@ -504,16 +504,21 @@ input[type="submit"].attachment-add-link-submit {
|
||||
inset-inline-start: 0 !important;
|
||||
inset-inline-end: 0 !important;
|
||||
bottom: 0 !important;
|
||||
z-index: 100 !important;
|
||||
/* #6419: the open card is a full-screen takeover on mobile, so it must sit
|
||||
ABOVE the app header bar (#header-main-bar is z-index 1000) — otherwise the
|
||||
header overlaps the top of the card and hides the close (X) button, making
|
||||
it impossible to close the card. Kept below popups (z-index 2000+) so card
|
||||
menus / date pickers still open over the card. */
|
||||
z-index: 1100 !important;
|
||||
height: 100vh !important;
|
||||
max-height: 100vh !important;
|
||||
border-radius: 0 !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* Ensure card details are above everything on mobile */
|
||||
/* Ensure card details are above the app header on mobile (#6419) */
|
||||
body.mobile-mode .card-details {
|
||||
z-index: 100 !important;
|
||||
z-index: 1100 !important;
|
||||
width: 100vw !important;
|
||||
inset-inline-start: 0 !important;
|
||||
inset-inline-end: 0 !important;
|
||||
@@ -601,7 +606,8 @@ body.mobile-mode .card-details {
|
||||
inset-inline-start: 0 !important;
|
||||
inset-inline-end: 0 !important;
|
||||
bottom: 0 !important;
|
||||
z-index: 100 !important;
|
||||
/* #6419: above the app header (#header-main-bar z-index 1000), below popups. */
|
||||
z-index: 1100 !important;
|
||||
height: 100vh !important;
|
||||
max-height: 100vh !important;
|
||||
border-radius: 0 !important;
|
||||
|
||||
@@ -627,18 +627,24 @@ Template.cardDetails.helpers({
|
||||
|
||||
showVotingButtons() {
|
||||
const card = Template.currentData();
|
||||
// #6420: currentUser was referenced but never defined here, so the helper
|
||||
// threw "ReferenceError: currentUser is not defined" on every card render and
|
||||
// the voting buttons disappeared. Define it and guard the board-member call.
|
||||
const currentUser = ReactiveCache.getCurrentUser();
|
||||
return (
|
||||
(currentUser.isBoardMember() ||
|
||||
(currentUser && card.voteAllowNonBoardMembers())) &&
|
||||
currentUser &&
|
||||
(currentUser.isBoardMember() || card.voteAllowNonBoardMembers()) &&
|
||||
!card.expiredVote()
|
||||
);
|
||||
},
|
||||
|
||||
showPlanningPokerButtons() {
|
||||
const card = Template.currentData();
|
||||
// #6420: same as showVotingButtons — currentUser was undefined here.
|
||||
const currentUser = ReactiveCache.getCurrentUser();
|
||||
return (
|
||||
(currentUser.isBoardMember() ||
|
||||
(currentUser && card.pokerAllowNonBoardMembers())) &&
|
||||
currentUser &&
|
||||
(currentUser.isBoardMember() || card.pokerAllowNonBoardMembers()) &&
|
||||
!card.expiredPoker()
|
||||
);
|
||||
},
|
||||
@@ -853,17 +859,25 @@ Template.cardDetails.events({
|
||||
event.preventDefault();
|
||||
const description = tpl.find('.js-new-description-input').value;
|
||||
const card = Template.currentData();
|
||||
await card.setDescription(description);
|
||||
// #5809: surface a visible error instead of failing silently — e.g. editing
|
||||
// a linked card whose target is on a board the user cannot write to gets a
|
||||
// permission denial that otherwise vanished with no feedback.
|
||||
try {
|
||||
await card.setDescription(description);
|
||||
} catch (error) {
|
||||
alert(error?.reason || error?.message || 'Failed to save description');
|
||||
}
|
||||
},
|
||||
async 'submit .js-card-details-title'(event, tpl) {
|
||||
event.preventDefault();
|
||||
const titleInput = tpl.find('.js-edit-card-title');
|
||||
const title = titleInput ? titleInput.value.trim() : '';
|
||||
const card = Template.currentData();
|
||||
if (title) {
|
||||
await card.setTitle(title);
|
||||
} else {
|
||||
await card.setTitle('');
|
||||
// #5809: surface a visible error instead of failing silently (see above).
|
||||
try {
|
||||
await card.setTitle(title || '');
|
||||
} catch (error) {
|
||||
alert(error?.reason || error?.message || 'Failed to save title');
|
||||
}
|
||||
},
|
||||
'submit .js-card-details-assigner'(event, tpl) {
|
||||
@@ -1402,7 +1416,7 @@ Template.cardDetailsActionsPopup.helpers({
|
||||
},
|
||||
|
||||
isBoardAdmin() {
|
||||
return ReactiveCache.getCurrentUser().isBoardAdmin();
|
||||
return ReactiveCache.getCurrentUser()?.isBoardAdmin();
|
||||
},
|
||||
|
||||
showListOnMinicard() {
|
||||
@@ -1492,9 +1506,13 @@ Template.editCardTitleForm.events({
|
||||
});
|
||||
|
||||
Template.cardMembersPopup.onCreated(function () {
|
||||
let currBoard = Utils.getCurrentBoard();
|
||||
let members = currBoard.activeMembers();
|
||||
this.members = new ReactiveVar(members);
|
||||
// #4965: only the filter term is stored reactively; the candidate list is
|
||||
// derived reactively in the members() helper (via filterMembers, which reads
|
||||
// board.activeMembers() on each call). Previously the list was snapshotted
|
||||
// once here, so a member added to the board after the popup opened — or whose
|
||||
// user document finished loading after open — was missing until the popup was
|
||||
// reopened.
|
||||
this.filterTerm = new ReactiveVar('');
|
||||
});
|
||||
|
||||
Template.cardMembersPopup.events({
|
||||
@@ -1506,8 +1524,7 @@ Template.cardMembersPopup.events({
|
||||
event.preventDefault();
|
||||
},
|
||||
'keyup .card-members-filter'(event) {
|
||||
const members = filterMembers(event.target.value);
|
||||
Template.instance().members.set(members);
|
||||
Template.instance().filterTerm.set(event.target.value);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1521,7 +1538,7 @@ Template.cardMembersPopup.helpers({
|
||||
},
|
||||
|
||||
members() {
|
||||
const members = Template.instance().members.get();
|
||||
const members = filterMembers(Template.instance().filterTerm.get());
|
||||
const uniqueMembers = uniqBy(members, 'userId');
|
||||
return [...uniqueMembers].sort((a, b) => {
|
||||
const userA = ReactiveCache.getUser(a.userId);
|
||||
@@ -2076,6 +2093,11 @@ Template.cardMorePopup.onCreated(function () {
|
||||
const cardId = getCardId();
|
||||
this.currentCard = Cards.findOne(cardId);
|
||||
this.parentBoard = new ReactiveVar(null);
|
||||
// #3745: tracks whether the selected parent board's cards have finished
|
||||
// loading. The card list stays empty until the subscription is ready, so it
|
||||
// is no longer blank the first time another board is picked (the cards()
|
||||
// helper queries minimongo, which was empty before the subscription arrived).
|
||||
this.parentBoardReady = new ReactiveVar(true);
|
||||
this.parentCard = this.currentCard?.parentCard();
|
||||
if (this.parentCard) {
|
||||
const list = $('.js-field-parent-card');
|
||||
@@ -2114,7 +2136,10 @@ Template.cardMorePopup.helpers({
|
||||
cards() {
|
||||
const tpl = Template.instance();
|
||||
const currentId = getCardId();
|
||||
if (tpl.parentBoard.get()) {
|
||||
// #3745: don't list cards until the selected board's subscription is ready,
|
||||
// otherwise the first open shows an empty list. Depending on parentBoardReady
|
||||
// also re-runs this helper once the data has arrived.
|
||||
if (tpl.parentBoard.get() && tpl.parentBoardReady.get()) {
|
||||
const ret = ReactiveCache.getCards({
|
||||
boardId: tpl.parentBoard.get(),
|
||||
_id: { $ne: currentId },
|
||||
@@ -2179,7 +2204,14 @@ Template.cardMorePopup.events({
|
||||
if (selection === 'none') {
|
||||
tpl.parentBoard.set(null);
|
||||
} else {
|
||||
Meteor.subscribe('board', $(event.currentTarget).val(), false);
|
||||
// #3745: wait for the board subscription to be ready before showing its
|
||||
// cards, so the parent-card list is populated on the first selection.
|
||||
tpl.parentBoardReady.set(false);
|
||||
Meteor.subscribe('board', selection, false, {
|
||||
onReady() {
|
||||
tpl.parentBoardReady.set(true);
|
||||
},
|
||||
});
|
||||
tpl.parentBoard.set(selection);
|
||||
list.prop('disabled', false);
|
||||
}
|
||||
|
||||
@@ -214,7 +214,9 @@ Template.checklists.events({
|
||||
'click .js-delete-checklist-item': Popup.afterConfirm('checklistItemDelete', function () {
|
||||
Popup.back();
|
||||
const item = this?.item || this;
|
||||
if (item && item._id) {
|
||||
// #3252: guard against removing a doc already evicted from Minimongo (heavy
|
||||
// archive/delete churn), which throws "Removed nonexistent document".
|
||||
if (item && item._id && ChecklistItems.findOne(item._id)) {
|
||||
ChecklistItems.remove(item._id);
|
||||
}
|
||||
}),
|
||||
@@ -258,7 +260,8 @@ Template.checklistActionsPopup.events({
|
||||
'click .js-delete-checklist': Popup.afterConfirm('checklistDelete', function () {
|
||||
Popup.back(2);
|
||||
const checklist = this.checklist;
|
||||
if (checklist && checklist._id) {
|
||||
// #3252: see js-delete-checklist-item — avoid "Removed nonexistent document".
|
||||
if (checklist && checklist._id && Checklists.findOne(checklist._id)) {
|
||||
Checklists.remove(checklist._id);
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -119,6 +119,22 @@
|
||||
margin-inline-end: 0.4vw;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
/* Keep text labels on a single compact line. The label name is rendered
|
||||
inside a `.viewer`, whose global `min-height: 2.5vh` otherwise forces every
|
||||
label to roughly double height on the minicard. */
|
||||
.minicard .minicard-labels .card-label {
|
||||
min-height: 0;
|
||||
padding: 1px 6px;
|
||||
line-height: 1.3;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.minicard .minicard-labels .card-label .viewer {
|
||||
min-height: 0;
|
||||
display: inline;
|
||||
}
|
||||
.minicard .minicard-labels .card-label .viewer p {
|
||||
display: inline;
|
||||
}
|
||||
.minicard .minicard-labels-no-text {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
@@ -209,7 +209,11 @@ Template.minicard.helpers({
|
||||
// Show checklist if either:
|
||||
// 1. Board-wide setting is enabled, OR
|
||||
// 2. This specific checklist has the setting enabled
|
||||
if (currentBoard.allowsChecklistAtMinicard || checklist.showChecklistAtMinicard) {
|
||||
// #5565: use the same board field the sidebar toggle writes
|
||||
// (`allowsChecklistsOnMinicard`); this previously checked a different,
|
||||
// UI-less field (`allowsChecklistAtMinicard`), so the board toggle to show
|
||||
// checklists on minicards had no effect.
|
||||
if (currentBoard.allowsChecklistsOnMinicard || checklist.showChecklistAtMinicard) {
|
||||
visibleChecklists.push(checklist);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -78,7 +78,7 @@ Template.subtasks.onCreated(function () {
|
||||
|
||||
Template.subtasks.helpers({
|
||||
isBoardAdmin() {
|
||||
return ReactiveCache.getCurrentUser().isBoardAdmin();
|
||||
return ReactiveCache.getCurrentUser()?.isBoardAdmin();
|
||||
},
|
||||
toggleDeleteDialog() {
|
||||
return Template.instance().toggleDeleteDialog;
|
||||
@@ -115,7 +115,7 @@ Template.subtaskItemDetail.events({
|
||||
|
||||
Template.subtaskActionsPopup.helpers({
|
||||
isBoardAdmin() {
|
||||
return ReactiveCache.getCurrentUser().isBoardAdmin();
|
||||
return ReactiveCache.getCurrentUser()?.isBoardAdmin();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -155,6 +155,6 @@ Template.editSubtaskItemForm.helpers({
|
||||
return ReactiveCache.getUser(this.userId);
|
||||
},
|
||||
isBoardAdmin() {
|
||||
return ReactiveCache.getCurrentUser().isBoardAdmin();
|
||||
return ReactiveCache.getCurrentUser()?.isBoardAdmin();
|
||||
},
|
||||
});
|
||||
@@ -135,10 +135,14 @@ body.list-resizing-active * {
|
||||
flex-direction: column !important;
|
||||
}
|
||||
|
||||
/* Ensure the width persists after resize is complete */
|
||||
.js-swimlane .list[style*="--list-width"],
|
||||
.dragscroll .list[style*="--list-width"],
|
||||
[id^="swimlane-"] .list[style*="--list-width"] {
|
||||
/* Ensure the width persists after resize is complete.
|
||||
Exclude collapsed lists: a collapsed list keeps its inline --list-width custom
|
||||
property, and without :not() this !important rule overrode the 30px collapsed
|
||||
width, so collapsing a list that had a custom width left it full width.
|
||||
See https://github.com/wekan/wekan/issues/5892 */
|
||||
.js-swimlane .list[style*="--list-width"]:not(.list-collapsed),
|
||||
.dragscroll .list[style*="--list-width"]:not(.list-collapsed),
|
||||
[id^="swimlane-"] .list[style*="--list-width"]:not(.list-collapsed) {
|
||||
/* Maintain the width after resize */
|
||||
width: var(--list-width, auto) !important;
|
||||
min-width: var(--list-width, auto) !important;
|
||||
@@ -253,10 +257,12 @@ body.list-resizing-active * {
|
||||
height: 15vh;
|
||||
}
|
||||
.list.list-collapsed {
|
||||
flex: none;
|
||||
min-width: 30px;
|
||||
max-width: 30px;
|
||||
width: 30px;
|
||||
flex: none !important;
|
||||
/* !important so a custom per-list --list-width never keeps a collapsed list
|
||||
full width (see https://github.com/wekan/wekan/issues/5892) */
|
||||
min-width: 30px !important;
|
||||
max-width: 30px !important;
|
||||
width: 30px !important;
|
||||
min-height: 60vh;
|
||||
height: 60vh;
|
||||
overflow: visible;
|
||||
|
||||
@@ -54,8 +54,78 @@ function readAnonListWidth(boardId, listId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// #5729 Fixed (same) width for all lists.
|
||||
//
|
||||
// A per-viewer, per-board toggle that makes EVERY list render at a single
|
||||
// shared value, and makes dragging any list update that one value (so all lists
|
||||
// change together). Stored exactly like the personal width:
|
||||
// * logged-in -> profile.fixedListWidthBoards / profile.fixedListWidths
|
||||
// * anonymous -> localStorage (mirrors readAnonListWidth)
|
||||
// ---------------------------------------------------------------------------
|
||||
function readAnonFixedListWidthEnabled(boardId) {
|
||||
try {
|
||||
const stored = localStorage.getItem('wekan-fixed-list-width-enabled');
|
||||
if (stored) {
|
||||
const flags = JSON.parse(stored);
|
||||
return flags[boardId] === true;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Error reading fixed list width flag from localStorage:', e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function readAnonFixedListWidth(boardId) {
|
||||
try {
|
||||
const stored = localStorage.getItem('wekan-fixed-list-width');
|
||||
if (stored) {
|
||||
const widths = JSON.parse(stored);
|
||||
const w = widths[boardId];
|
||||
if (typeof w === 'number' && w >= MIN_LIST_WIDTH) return w;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Error reading fixed list width from localStorage:', e);
|
||||
}
|
||||
return DEFAULT_LIST_WIDTH;
|
||||
}
|
||||
|
||||
// Whether the current viewer has fixed width mode enabled for this board.
|
||||
function isFixedListWidth(boardId) {
|
||||
const user = ReactiveCache.getCurrentUser();
|
||||
if (user) {
|
||||
return !!user.isFixedListWidth(boardId);
|
||||
}
|
||||
return readAnonFixedListWidthEnabled(boardId);
|
||||
}
|
||||
|
||||
// The single width every list should use when fixed width mode is enabled.
|
||||
function fixedListWidthValue(boardId) {
|
||||
const user = ReactiveCache.getCurrentUser();
|
||||
if (user) {
|
||||
return user.getFixedListWidth(boardId);
|
||||
}
|
||||
return readAnonFixedListWidth(boardId);
|
||||
}
|
||||
|
||||
// Persist the single fixed width for the whole board (anon localStorage).
|
||||
function saveAnonFixedListWidth(boardId, width) {
|
||||
try {
|
||||
const stored = localStorage.getItem('wekan-fixed-list-width');
|
||||
const widths = stored ? JSON.parse(stored) : {};
|
||||
widths[boardId] = width;
|
||||
localStorage.setItem('wekan-fixed-list-width', JSON.stringify(widths));
|
||||
} catch (e) {
|
||||
console.warn('Error saving fixed list width to localStorage:', e);
|
||||
}
|
||||
}
|
||||
|
||||
function effectiveListWidth(list) {
|
||||
if (!list) return DEFAULT_LIST_WIDTH;
|
||||
// #5729 In fixed width mode every list renders at the single shared value.
|
||||
if (isFixedListWidth(list.boardId)) {
|
||||
return fixedListWidthValue(list.boardId);
|
||||
}
|
||||
const shared =
|
||||
typeof list.width === 'number' && list.width >= MIN_LIST_WIDTH
|
||||
? list.width
|
||||
@@ -80,6 +150,9 @@ function effectiveListWidth(list) {
|
||||
function canResizeList(list) {
|
||||
if (!list) return false;
|
||||
if (effectiveAutoWidth(list.boardId)) return false;
|
||||
// #5729 Fixed width is the viewer's own per-board setting, so any viewer
|
||||
// (logged-in or anonymous) may drag-resize; the change applies to all lists.
|
||||
if (isFixedListWidth(list.boardId)) return true;
|
||||
if (isPersonalListWidth(list.boardId)) return true;
|
||||
return Utils.canModifyBoard();
|
||||
}
|
||||
@@ -90,6 +163,18 @@ function saveListWidth(list, width) {
|
||||
const boardId = list.boardId;
|
||||
const listId = list._id;
|
||||
const user = ReactiveCache.getCurrentUser();
|
||||
// #5729 In fixed width mode, persist to the single per-board value instead of
|
||||
// the per-list width, so EVERY list re-renders at the new width.
|
||||
if (isFixedListWidth(boardId)) {
|
||||
if (user) {
|
||||
Meteor.call('setFixedListWidth', boardId, width, error => {
|
||||
if (error) console.error('Error saving fixed list width:', error);
|
||||
});
|
||||
} else {
|
||||
saveAnonFixedListWidth(boardId, width);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (isPersonalListWidth(boardId)) {
|
||||
if (user) {
|
||||
Meteor.call('applyListWidthToStorage', boardId, listId, width, width);
|
||||
|
||||
@@ -324,7 +324,20 @@ Template.listBody.helpers({
|
||||
const tpl = Template.instance();
|
||||
const limit = tpl.cardlimit.get();
|
||||
const defaultSort = { sort: 1 };
|
||||
const sortBy = Session.get('sortBy') ? Session.get('sortBy') : defaultSort;
|
||||
// Session is reset on page reload; fall back to the persisted card sort so a
|
||||
// chosen sort (e.g. by due date) is remembered across reloads (#5886).
|
||||
let sortBy = Session.get('sortBy');
|
||||
if (!sortBy) {
|
||||
try {
|
||||
const stored = window.localStorage.getItem('wekan-cards-sortBy');
|
||||
if (stored) {
|
||||
sortBy = JSON.parse(stored);
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
if (!sortBy) {
|
||||
sortBy = defaultSort;
|
||||
}
|
||||
const selector = {
|
||||
listId: Template.currentData()._id,
|
||||
archived: false,
|
||||
@@ -743,11 +756,37 @@ Template.linkCardPopup.events({
|
||||
tpl.selectedListId.set($(evt.currentTarget).val());
|
||||
},
|
||||
async 'click .js-done'(evt, tpl) {
|
||||
// LINK CARD
|
||||
// LINK CARD (or, when no card is chosen, fall back to a board-level link
|
||||
// so a whole board can be linked even when that board already has cards).
|
||||
// https://github.com/wekan/wekan/issues/5715
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
const linkedId = $('.js-select-cards option:selected').val();
|
||||
if (!linkedId) {
|
||||
const boardId = $('.js-select-boards option:selected').val();
|
||||
// No board and no card selected: nothing to link.
|
||||
if (!boardId) {
|
||||
Popup.back();
|
||||
return;
|
||||
}
|
||||
// A linkedBoard card for this board already exists; avoid duplicates.
|
||||
if (ReactiveCache.getCard({ linkedId: boardId, archived: false })) {
|
||||
Popup.back();
|
||||
return;
|
||||
}
|
||||
const boardCardNumber = await tpl.board.getNextCardNumber();
|
||||
const boardSortIndex = tpl.getSortIndex();
|
||||
const boardCardId = Cards.insert({
|
||||
title: $('.js-select-boards option:selected').text(), //dummy
|
||||
listId: tpl.listId,
|
||||
swimlaneId: tpl.swimlaneId,
|
||||
boardId: tpl.boardId,
|
||||
sort: boardSortIndex,
|
||||
type: 'cardType-linkedBoard',
|
||||
linkedId: boardId,
|
||||
cardNumber: boardCardNumber,
|
||||
});
|
||||
Filter.addException(boardCardId);
|
||||
Popup.back();
|
||||
return;
|
||||
}
|
||||
@@ -924,13 +963,37 @@ Template.searchElementPopup.events({
|
||||
if (!tpl.isTemplateSearch || tpl.isCardTemplateSearch) {
|
||||
// Card insertion
|
||||
// 1. Common
|
||||
// Compute the sort index BEFORE the first await: getSortIndex() reads
|
||||
// Template.currentData(), and Blaze's current-view context is lost across
|
||||
// an await, so calling it afterwards threw "There is no current view" and
|
||||
// no card was created from the template. Capture it while still in the
|
||||
// synchronous event/view context.
|
||||
const sortIndex = tpl.getSortIndex();
|
||||
element.cardNumber = await tpl.board.getNextCardNumber();
|
||||
element.sort = tpl.getSortIndex();
|
||||
element.sort = sortIndex;
|
||||
// 1.A From template
|
||||
if (tpl.isTemplateSearch) {
|
||||
element.type = 'cardType-card';
|
||||
element.linkedId = '';
|
||||
_id = await element.copy(tpl.boardId, tpl.swimlaneId, tpl.listId);
|
||||
// #5798: instantiate the template into the CURRENT board, not the
|
||||
// templates board. For a template search tpl.boardId is the templates
|
||||
// board (it is the search source), so copying with it created the card
|
||||
// with boardId = templates board. The card still showed in the target
|
||||
// list (the list query is by listId and the templates board is
|
||||
// subscribed), but clicking it navigated to the templates board instead
|
||||
// of opening the card. The target list/swimlane were already resolved on
|
||||
// the current board.
|
||||
// Instantiate into the board that OWNS the target list (where the user
|
||||
// clicked "add card"). Deriving it from tpl.listId is reliable even when
|
||||
// Utils.getCurrentBoard() (route/session state) has not settled yet — as
|
||||
// on the slower CI production bundle, where falling back to tpl.boardId
|
||||
// wrongly created the card on the templates board.
|
||||
const targetList = ReactiveCache.getList(tpl.listId);
|
||||
const targetBoardId =
|
||||
(targetList || {}).boardId ||
|
||||
(Utils.getCurrentBoard() || {})._id ||
|
||||
tpl.boardId;
|
||||
_id = await element.copy(targetBoardId, tpl.swimlaneId, tpl.listId);
|
||||
// 1.B Linked card
|
||||
} else {
|
||||
_id = element.link(tpl.boardId, tpl.swimlaneId, tpl.listId);
|
||||
|
||||
@@ -262,6 +262,9 @@ template(name="setListWidthPopup")
|
||||
a.js-toggle-auto-width(title="{{#if isAutoWidth}}{{_ 'click-to-disable-auto-width'}}{{else}}{{_ 'click-to-enable-auto-width'}}{{/if}}")
|
||||
| {{#if isAutoWidth}}🗜️{{else}}📏{{/if}}
|
||||
span {{_ 'auto-list-width'}}
|
||||
a.js-toggle-fixed-list-width(title="{{#if isFixedListWidth}}{{_ 'click-to-disable-fixed-list-width'}}{{else}}{{_ 'click-to-enable-fixed-list-width'}}{{/if}}")
|
||||
| {{#if isFixedListWidth}}✅{{else}}⬜{{/if}}
|
||||
span {{_ 'fixed-list-width'}}
|
||||
p.list-width-scope-note {{ listWidthScopeNote }}
|
||||
|
||||
template(name="listWidthErrorPopup")
|
||||
|
||||
@@ -20,12 +20,12 @@ Template.listHeader.helpers({
|
||||
(!list.getWipLimit('enabled') ||
|
||||
list.getWipLimit('soft') ||
|
||||
!Template.instance().reachedWipLimit()) &&
|
||||
!ReactiveCache.getCurrentUser().isWorker()
|
||||
!ReactiveCache.getCurrentUser()?.isWorker()
|
||||
);
|
||||
},
|
||||
|
||||
isBoardAdmin() {
|
||||
return ReactiveCache.getCurrentUser().isBoardAdmin();
|
||||
return ReactiveCache.getCurrentUser()?.isBoardAdmin();
|
||||
},
|
||||
|
||||
starred() {
|
||||
@@ -218,7 +218,7 @@ Template.listHeader.events({
|
||||
|
||||
Template.listActionPopup.helpers({
|
||||
isBoardAdmin() {
|
||||
return ReactiveCache.getCurrentUser().isBoardAdmin();
|
||||
return ReactiveCache.getCurrentUser()?.isBoardAdmin();
|
||||
},
|
||||
|
||||
isWipLimitEnabled() {
|
||||
@@ -520,9 +520,77 @@ function isPersonalListWidth(boardId) {
|
||||
return !!(board && board.allowsPersonalListWidth);
|
||||
}
|
||||
|
||||
// #5729 Fixed (same) width for all lists is a per-viewer/per-board setting.
|
||||
// Logged-in users store it in their profile; anonymous (public board) users in
|
||||
// localStorage (mirrors the per-list anon storage in list.js).
|
||||
function readAnonFixedListWidthEnabled(boardId) {
|
||||
try {
|
||||
const stored = localStorage.getItem('wekan-fixed-list-width-enabled');
|
||||
if (stored) {
|
||||
const flags = JSON.parse(stored);
|
||||
return flags[boardId] === true;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Error reading fixed list width flag from localStorage:', e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function readAnonFixedListWidth(boardId) {
|
||||
try {
|
||||
const stored = localStorage.getItem('wekan-fixed-list-width');
|
||||
if (stored) {
|
||||
const widths = JSON.parse(stored);
|
||||
const w = widths[boardId];
|
||||
if (typeof w === 'number' && w >= 270) return w;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Error reading fixed list width from localStorage:', e);
|
||||
}
|
||||
return 272;
|
||||
}
|
||||
|
||||
function isFixedListWidth(boardId) {
|
||||
const user = ReactiveCache.getCurrentUser();
|
||||
if (user) return !!user.isFixedListWidth(boardId);
|
||||
return readAnonFixedListWidthEnabled(boardId);
|
||||
}
|
||||
|
||||
function fixedListWidthValue(boardId) {
|
||||
const user = ReactiveCache.getCurrentUser();
|
||||
if (user) return user.getFixedListWidth(boardId);
|
||||
return readAnonFixedListWidth(boardId);
|
||||
}
|
||||
|
||||
function setAnonFixedListWidthEnabled(boardId, enabled) {
|
||||
try {
|
||||
const stored = localStorage.getItem('wekan-fixed-list-width-enabled');
|
||||
const flags = stored ? JSON.parse(stored) : {};
|
||||
flags[boardId] = !!enabled;
|
||||
localStorage.setItem('wekan-fixed-list-width-enabled', JSON.stringify(flags));
|
||||
} catch (e) {
|
||||
console.warn('Error saving fixed list width flag to localStorage:', e);
|
||||
}
|
||||
}
|
||||
|
||||
function setAnonFixedListWidth(boardId, width) {
|
||||
try {
|
||||
const stored = localStorage.getItem('wekan-fixed-list-width');
|
||||
const widths = stored ? JSON.parse(stored) : {};
|
||||
widths[boardId] = width;
|
||||
localStorage.setItem('wekan-fixed-list-width', JSON.stringify(widths));
|
||||
} catch (e) {
|
||||
console.warn('Error saving fixed list width to localStorage:', e);
|
||||
}
|
||||
}
|
||||
|
||||
Template.setListWidthPopup.helpers({
|
||||
listWidthValue() {
|
||||
const list = Template.currentData();
|
||||
// #5729 In fixed width mode the input edits the single per-board value.
|
||||
if (isFixedListWidth(list.boardId)) {
|
||||
return fixedListWidthValue(list.boardId);
|
||||
}
|
||||
const shared =
|
||||
typeof list.width === 'number' && list.width >= 270 ? list.width : 272;
|
||||
if (!isPersonalListWidth(list.boardId)) {
|
||||
@@ -539,6 +607,10 @@ Template.setListWidthPopup.helpers({
|
||||
|
||||
listWidthScopeNote() {
|
||||
const list = Template.currentData();
|
||||
// #5729 In fixed width mode the note explains the all-lists behaviour.
|
||||
if (isFixedListWidth(list.boardId)) {
|
||||
return TAPi18n.__('fixed-list-width-note');
|
||||
}
|
||||
return isPersonalListWidth(list.boardId)
|
||||
? TAPi18n.__('list-width-personal-note')
|
||||
: TAPi18n.__('list-width-shared-note');
|
||||
@@ -564,6 +636,12 @@ Template.setListWidthPopup.helpers({
|
||||
}
|
||||
return Utils.canModifyBoard();
|
||||
},
|
||||
|
||||
// #5729 Whether "same width for all lists" mode is on for the current viewer.
|
||||
isFixedListWidth() {
|
||||
const list = Template.currentData();
|
||||
return isFixedListWidth(list.boardId);
|
||||
},
|
||||
});
|
||||
|
||||
Template.setListWidthPopup.events({
|
||||
@@ -581,6 +659,24 @@ Template.setListWidthPopup.events({
|
||||
}
|
||||
Popup.back();
|
||||
},
|
||||
// #5729 Toggle "same width for all lists" (fixed width) for the current
|
||||
// viewer. Enabling it turns off auto-width (the two modes are exclusive).
|
||||
'click .js-toggle-fixed-list-width'(event) {
|
||||
event.preventDefault();
|
||||
const list = Template.currentData();
|
||||
const boardId = list.boardId;
|
||||
const enabled = !isFixedListWidth(boardId);
|
||||
const user = ReactiveCache.getCurrentUser();
|
||||
if (user) {
|
||||
if (enabled && isPersonalListWidth(boardId) && user.isAutoWidth(boardId)) {
|
||||
user.toggleAutoWidth(boardId);
|
||||
}
|
||||
Meteor.call('setFixedListWidthEnabled', boardId, enabled);
|
||||
} else {
|
||||
setAnonFixedListWidthEnabled(boardId, enabled);
|
||||
}
|
||||
Popup.back();
|
||||
},
|
||||
'click .list-width-apply'(event, tpl) {
|
||||
const list = Template.currentData();
|
||||
const boardId = list.boardId;
|
||||
@@ -591,6 +687,16 @@ Template.setListWidthPopup.events({
|
||||
return;
|
||||
}
|
||||
const user = ReactiveCache.getCurrentUser();
|
||||
// #5729 In fixed width mode the input applies to the single per-board value.
|
||||
if (isFixedListWidth(boardId)) {
|
||||
if (user) {
|
||||
Meteor.call('setFixedListWidth', boardId, width);
|
||||
} else {
|
||||
setAnonFixedListWidth(boardId, width);
|
||||
}
|
||||
Popup.back();
|
||||
return;
|
||||
}
|
||||
if (isPersonalListWidth(boardId)) {
|
||||
if (user) {
|
||||
Meteor.call('applyListWidthToStorage', boardId, list._id, width, width);
|
||||
|
||||
@@ -496,15 +496,19 @@ body:not(.board-view) #header #header-main-bar h1 {
|
||||
@media screen and (max-width: 800px),
|
||||
screen and (max-device-width: 932px) and (-webkit-min-device-pixel-ratio: 3) {
|
||||
#header #header-main-bar {
|
||||
height: 40px;
|
||||
/* #6419: taller bar so the action icons can use ~44px touch targets. */
|
||||
height: 48px;
|
||||
}
|
||||
#header #header-main-bar .board-header-btns {
|
||||
margin-top: 0px;
|
||||
}
|
||||
#header #header-main-bar .board-header-btn {
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
font-size: 15px;
|
||||
/* #6419: 32px tall / 15px was below the comfortable mobile touch-target and
|
||||
readable-font minimum (~44px / 16px). Enlarged vertically + bigger font;
|
||||
width is unchanged so this does not worsen the horizontal icon crowding. */
|
||||
height: 44px;
|
||||
line-height: 44px;
|
||||
font-size: 16px;
|
||||
}
|
||||
#header #header-main-bar .board-header-btn i.fa {
|
||||
line-height: 32px;
|
||||
|
||||
@@ -267,7 +267,12 @@ Template.main.helpers({
|
||||
Template.userFormsLayout.events({
|
||||
'change .js-userform-set-language'(event) {
|
||||
const tag = $(event.currentTarget).val();
|
||||
TAPi18n.setLanguage(tag);
|
||||
// setLanguage is async; surface a failed load instead of silently leaving
|
||||
// the UI in English (#5756).
|
||||
Promise.resolve(TAPi18n.setLanguage(tag)).catch(error => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`Failed to switch language to ${tag}:`, error);
|
||||
});
|
||||
event.preventDefault();
|
||||
},
|
||||
'click #at-btn'(event, templateInstance) {
|
||||
|
||||
@@ -364,6 +364,10 @@ Template.boardsReport.helpers({
|
||||
},
|
||||
userNames(members) {
|
||||
const ret = (members || [])
|
||||
// #5122: removing a member from a board marks it `isActive: false` (the
|
||||
// entry stays in board.members for role history / re-activation). The
|
||||
// boards report must not list those removed members as current members.
|
||||
.filter(_member => _member.isActive !== false)
|
||||
.map(_member => {
|
||||
const _ret = ReactiveCache.getUser(_member.userId)?.username || _member.userId;
|
||||
return _ret;
|
||||
|
||||
@@ -278,3 +278,50 @@ li.has-error .form-group .wekan-form-control {
|
||||
width: 120px;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
/* #6419: on phones the admin settings forced a side-by-side menu+content layout
|
||||
(flex-direction: row "even on narrow windows"), so the ~127px side menu and the
|
||||
content were crammed together and the long (e.g. Finnish) menu labels overflowed
|
||||
the menu box rightward, visually overlapping the content. Stack them on small
|
||||
screens: a full-width compact menu on top, full-width content below. */
|
||||
@media screen and (max-width: 800px) {
|
||||
.setting-content {
|
||||
position: relative;
|
||||
height: auto;
|
||||
min-height: 100%;
|
||||
}
|
||||
.setting-content .content-body {
|
||||
flex-direction: column;
|
||||
flex-wrap: nowrap;
|
||||
gap: 8px;
|
||||
height: auto;
|
||||
padding-top: 1vh;
|
||||
}
|
||||
.setting-content .content-body .side-menu {
|
||||
width: 100%;
|
||||
flex: 0 0 auto;
|
||||
border-radius: 6px;
|
||||
}
|
||||
/* Menu items as a wrapping horizontal row so the menu stays compact on top. */
|
||||
.setting-content .content-body .side-menu ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
padding: 4px;
|
||||
}
|
||||
.setting-content .content-body .side-menu ul li {
|
||||
flex: 0 0 auto;
|
||||
margin: 0;
|
||||
}
|
||||
.setting-content .content-body .side-menu ul li a {
|
||||
width: auto;
|
||||
white-space: nowrap;
|
||||
padding: 10px 12px;
|
||||
}
|
||||
.setting-content .content-body .main-body {
|
||||
width: 100%;
|
||||
flex: 1 1 auto;
|
||||
min-height: 60vh;
|
||||
overflow-x: auto !important;
|
||||
}
|
||||
}
|
||||
@@ -49,3 +49,30 @@
|
||||
float: none;
|
||||
margin: 2px 0;
|
||||
}
|
||||
|
||||
/* #6419: the admin panel's 2nd top bar (settingHeaderBar) is rendered inside
|
||||
#header-quick-access, whose mobile rules scale ALL text/icons 2x
|
||||
(`#header-quick-access * { font-size: inherit }` + `.fa { font-size: 2em }`).
|
||||
That made the admin tab buttons 28px text / 56px icons — huge, overlapping the
|
||||
"Version" label and the content below. Cap them at a normal touch size on small
|
||||
screens. Selectors are more specific than the 2em rules (and repeated for the
|
||||
.iphone-device JS-detection fallback) so they win. */
|
||||
@media screen and (max-width: 800px) {
|
||||
#header-quick-access .setting-header-btns .setting-header-btn,
|
||||
#header #header-main-bar .setting-header-btns .setting-header-btn,
|
||||
#header-quick-access .setting-header-btns .setting-header-btn span,
|
||||
#header #header-main-bar .setting-header-btns .setting-header-btn span {
|
||||
font-size: 15px !important;
|
||||
}
|
||||
#header-quick-access .setting-header-btns .setting-header-btn i.fa,
|
||||
#header #header-main-bar .setting-header-btns .setting-header-btn i.fa {
|
||||
font-size: 18px !important;
|
||||
}
|
||||
}
|
||||
.iphone-device #header-quick-access .setting-header-btns .setting-header-btn,
|
||||
.iphone-device #header-quick-access .setting-header-btns .setting-header-btn span {
|
||||
font-size: 15px !important;
|
||||
}
|
||||
.iphone-device #header-quick-access .setting-header-btns .setting-header-btn i.fa {
|
||||
font-size: 18px !important;
|
||||
}
|
||||
@@ -261,9 +261,40 @@ body.grey-icons-enabled .sidebar .sidebar-content ul.sidebar-list li > a .fa.fa-
|
||||
padding: 0 10px 0 10px;
|
||||
}
|
||||
@media screen and (max-width: 800px) {
|
||||
.board-sidebar {
|
||||
width: 100%;
|
||||
/* On phones (iPhone 12 mini, etc.) the right sidebar should be a full-width
|
||||
panel at the top of the board, in BOTH mobile mode and desktop mode. The
|
||||
sidebar lives inside .board-wrapper, which is wider than the viewport on a
|
||||
phone, so an absolutely-positioned sidebar (inset-inline-end: 0) pins to the
|
||||
far right of the wide board and shows as a narrow strip. Pin it to the
|
||||
VIEWPORT instead (position: fixed + 100vw) so it spans the full screen
|
||||
width. At 100% zoom .board-wrapper has no transform, so fixed resolves to
|
||||
the viewport in desktop mode too. */
|
||||
.board-sidebar.sidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
inset-inline-start: 0;
|
||||
inset-inline-end: 0;
|
||||
width: 100vw;
|
||||
max-width: 100vw;
|
||||
z-index: 1000;
|
||||
}
|
||||
/* Normalise sidebar text on phones so the headings are not oversized and the
|
||||
small text is not tiny — keep them close to the board's text size so board
|
||||
and sidebar read at one consistent size. */
|
||||
.board-sidebar.sidebar .sidebar-content {
|
||||
font-size: 14px;
|
||||
}
|
||||
.board-sidebar.sidebar .sidebar-content h2,
|
||||
.board-sidebar.sidebar .sidebar-content h3 {
|
||||
font-size: 16px;
|
||||
}
|
||||
.board-sidebar.sidebar .sidebar-content ul.sidebar-list li > a,
|
||||
.board-sidebar.sidebar .sidebar-content .card-label,
|
||||
.board-sidebar.sidebar .sidebar-content .card-label .card-label-name,
|
||||
.board-sidebar.sidebar .sidebar-content p,
|
||||
.board-sidebar.sidebar .sidebar-content span {
|
||||
font-size: 14px;
|
||||
}
|
||||
.board-sidebar .sidebar-content .hide-btn {
|
||||
width: 40px;
|
||||
|
||||
@@ -294,7 +294,7 @@ Template.memberPopup.helpers({
|
||||
return ReactiveCache.getUser(this.userId);
|
||||
},
|
||||
isBoardAdmin() {
|
||||
return ReactiveCache.getCurrentUser().isBoardAdmin();
|
||||
return ReactiveCache.getCurrentUser()?.isBoardAdmin();
|
||||
},
|
||||
memberType() {
|
||||
const type = ReactiveCache.getUser(this.userId).isBoardAdmin() ? 'admin' : 'normal';
|
||||
@@ -433,7 +433,7 @@ Template.boardMenuPopup.onCreated(function() {
|
||||
|
||||
Template.boardMenuPopup.helpers({
|
||||
isBoardAdmin() {
|
||||
return ReactiveCache.getCurrentUser().isBoardAdmin();
|
||||
return ReactiveCache.getCurrentUser()?.isBoardAdmin();
|
||||
},
|
||||
withApi() {
|
||||
return Template.instance().apiEnabled.get();
|
||||
@@ -547,7 +547,7 @@ Template.membersWidget.helpers({
|
||||
}
|
||||
},
|
||||
isBoardAdmin() {
|
||||
return ReactiveCache.getCurrentUser().isBoardAdmin();
|
||||
return ReactiveCache.getCurrentUser()?.isBoardAdmin();
|
||||
},
|
||||
AtLeastOneOrgWasCreated(){
|
||||
let orgs = ReactiveCache.getOrgs({}, {sort: { createdAt: -1 }});
|
||||
@@ -919,7 +919,7 @@ Template.labelsWidget.events({
|
||||
|
||||
Template.labelsWidget.helpers({
|
||||
isBoardAdmin() {
|
||||
return ReactiveCache.getCurrentUser().isBoardAdmin();
|
||||
return ReactiveCache.getCurrentUser()?.isBoardAdmin();
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -215,7 +215,7 @@ function mapSelection(kind, _id) {
|
||||
|
||||
Template.multiselectionSidebar.helpers({
|
||||
isBoardAdmin() {
|
||||
return ReactiveCache.getCurrentUser().isBoardAdmin();
|
||||
return ReactiveCache.getCurrentUser()?.isBoardAdmin();
|
||||
},
|
||||
isCommentOnly() {
|
||||
return ReactiveCache.getCurrentUser().isCommentOnly();
|
||||
|
||||
@@ -41,7 +41,7 @@ Template.swimlaneHeader.events({
|
||||
|
||||
Template.swimlaneFixedHeader.helpers({
|
||||
isBoardAdmin() {
|
||||
return ReactiveCache.getCurrentUser().isBoardAdmin();
|
||||
return ReactiveCache.getCurrentUser()?.isBoardAdmin();
|
||||
},
|
||||
collapseSwimlane() {
|
||||
const swimlane = Template.currentData();
|
||||
|
||||
@@ -83,8 +83,15 @@
|
||||
}
|
||||
.swimlane .swimlane-header-wrap .swimlane-header-menu {
|
||||
position: absolute;
|
||||
/* Stretch the menu to the full height of the swimlane header and lay the
|
||||
buttons (collapse, swimlane menu, + new swimlane, drag handle) in a row that
|
||||
is vertically centered — instead of anchoring them to the top with per-icon
|
||||
top: calc(50% + Npx) hacks that centered against the menu's own height. */
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
inset-inline-start: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: 22px;
|
||||
@@ -93,7 +100,6 @@
|
||||
pointer-events: auto;
|
||||
}
|
||||
.swimlane .swimlane-header-wrap .swimlane-header-menu .js-open-swimlane-menu {
|
||||
top: calc(50% + 6px);
|
||||
padding: 5px;
|
||||
display: inline-block;
|
||||
margin-inline-start: 30px;
|
||||
@@ -107,20 +113,16 @@
|
||||
}
|
||||
}
|
||||
.swimlane .swimlane-header-wrap .swimlane-header-plus-icon {
|
||||
top: calc(50% + 6px);
|
||||
padding: 5px;
|
||||
margin-inline-start: 20px;
|
||||
font-size: 22px;
|
||||
color: #a6a6a6;
|
||||
}
|
||||
.swimlane .swimlane-header-wrap .swimlane-header-menu-icon {
|
||||
top: calc(50% + 6px);
|
||||
padding-inline-start: 5px;
|
||||
font-size: 22px;
|
||||
}
|
||||
.swimlane .swimlane-header-wrap .swimlane-header-handle {
|
||||
position: relative;
|
||||
top: calc(50% + 2px);
|
||||
padding: 2px 5px;
|
||||
font-size: clamp(16px, 3vw, 20px);
|
||||
display: inline-block;
|
||||
@@ -132,9 +134,7 @@
|
||||
line-height: 1.2;
|
||||
}
|
||||
.swimlane .swimlane-header-wrap .swimlane-header-miniscreen-handle {
|
||||
position: relative;
|
||||
padding: 2px 5px;
|
||||
top: calc(50% + 2px);
|
||||
font-size: 24px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
|
||||
@@ -18,8 +18,16 @@ function saveSorting(ui) {
|
||||
}
|
||||
// To attribute the new index number, we need to get the DOM element
|
||||
// of the previous and the following list -- if any.
|
||||
const prevListDom = ui.item.prev('.js-list').get(0);
|
||||
const nextListDom = ui.item.next('.js-list').get(0);
|
||||
// Use prevAll/nextAll (not prev/next) so non-list siblings between lists do
|
||||
// not break neighbour detection: the lists container also renders the
|
||||
// +addListForm composer and, when a card is open, a +cardDetails element.
|
||||
// jQuery's .prev('.js-list') only matches an *immediately* adjacent sibling,
|
||||
// so an interspersed composer/cardDetails made calculateIndex mis-detect the
|
||||
// first/last position and compute a wrong sort, so the reordered list landed
|
||||
// elsewhere and looked like it did not persist. See
|
||||
// https://github.com/wekan/wekan/issues/5997
|
||||
const prevListDom = ui.item.prevAll('.js-list:not(.js-list-composer)').get(0);
|
||||
const nextListDom = ui.item.nextAll('.js-list:not(.js-list-composer)').get(0);
|
||||
const sortIndex = calculateIndex(prevListDom, nextListDom, 1);
|
||||
|
||||
const listDomElement = ui.item.get(0);
|
||||
@@ -635,7 +643,7 @@ function initializeSwimlaneResize(tpl, retryCount = 0) {
|
||||
|
||||
Template.swimlane.helpers({
|
||||
canSeeAddList() {
|
||||
return ReactiveCache.getCurrentUser().isBoardAdmin();
|
||||
return ReactiveCache.getCurrentUser()?.isBoardAdmin();
|
||||
},
|
||||
lists() {
|
||||
const swimlane = this;
|
||||
|
||||
@@ -139,16 +139,26 @@ template(name="adminChangeAvatarPopup")
|
||||
template(name="cardMemberPopup")
|
||||
.board-member-menu
|
||||
.mini-profile-info
|
||||
+userAvatar(userId=user._id showEdit=true)
|
||||
.info
|
||||
h3= user.profile.fullname
|
||||
p.quiet @{{ user.username }}
|
||||
if user
|
||||
+userAvatar(userId=user._id showEdit=true)
|
||||
.info
|
||||
h3= user.profile.fullname
|
||||
p.quiet @{{ user.username }}
|
||||
else
|
||||
//- #4847: the member's user account was deleted. Still show the entry
|
||||
//- (with its id) and the remove control so the orphaned member can be
|
||||
//- removed from the card — the remove handler keys off userId, not the
|
||||
//- user document.
|
||||
.info
|
||||
h3 {{_ 'deleted-user'}}
|
||||
p.quiet {{ userId }}
|
||||
ul.pop-over-list
|
||||
unless noRemove
|
||||
if currentUser.isNotCommentOnly
|
||||
if currentUser.isNotWorker
|
||||
li: a.js-remove-member {{_ 'remove-member-from-card'}}
|
||||
|
||||
if $eq currentUser._id user._id
|
||||
with currentUser
|
||||
li: a.js-edit-profile {{_ 'edit-profile'}}
|
||||
if user
|
||||
if $eq currentUser._id user._id
|
||||
with currentUser
|
||||
li: a.js-edit-profile {{_ 'edit-profile'}}
|
||||
@@ -309,7 +309,12 @@ Template.changeLanguagePopup.events({
|
||||
'profile.language': this.tag,
|
||||
},
|
||||
});
|
||||
TAPi18n.setLanguage(this.tag);
|
||||
// setLanguage is async; surface a failed load instead of silently leaving
|
||||
// the UI in English (#5756).
|
||||
Promise.resolve(TAPi18n.setLanguage(this.tag)).catch(error => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`Failed to switch language to ${this.tag}:`, error);
|
||||
});
|
||||
event.preventDefault();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Template } from 'meteor/templating';
|
||||
import { ReactiveVar } from 'meteor/reactive-var';
|
||||
import { ReactiveCache } from '/imports/reactiveCache';
|
||||
import { getCurrentCardFromContext } from '/client/lib/currentCard';
|
||||
import { normalizeDigits } from '/imports/lib/dateUtils';
|
||||
|
||||
// Helper to check if a date is valid
|
||||
function isValidDate(date) {
|
||||
@@ -114,8 +115,9 @@ export function datePickerHelpers() {
|
||||
export function datePickerEvents({ storeDate, deleteDate }) {
|
||||
return {
|
||||
'change .js-date-field'(evt, tpl) {
|
||||
// Native HTML date input validation
|
||||
const dateValue = tpl.find('#date').value;
|
||||
// Native HTML date input validation. Normalize any non-Latin digits
|
||||
// (e.g. Persian/Arabic-Indic) so parsing works in those locales (#5752).
|
||||
const dateValue = normalizeDigits(tpl.find('#date').value);
|
||||
if (dateValue) {
|
||||
// HTML date input format is always YYYY-MM-DD
|
||||
const dateObj = new Date(dateValue + 'T12:00:00');
|
||||
@@ -127,8 +129,9 @@ export function datePickerEvents({ storeDate, deleteDate }) {
|
||||
}
|
||||
},
|
||||
'change .js-time-field'(evt, tpl) {
|
||||
// Native HTML time input validation
|
||||
const timeValue = tpl.find('#time').value;
|
||||
// Native HTML time input validation. Normalize any non-Latin digits
|
||||
// (e.g. Persian/Arabic-Indic) so parsing works in those locales (#5752).
|
||||
const timeValue = normalizeDigits(tpl.find('#time').value);
|
||||
if (timeValue) {
|
||||
// HTML time input format is always HH:mm
|
||||
const timeObj = new Date(`1970-01-01T${timeValue}:00`);
|
||||
@@ -142,8 +145,10 @@ export function datePickerEvents({ storeDate, deleteDate }) {
|
||||
'submit .edit-date'(evt, tpl) {
|
||||
evt.preventDefault();
|
||||
|
||||
const dateValue = evt.target.date.value;
|
||||
const timeValue = evt.target.time.value || '12:00'; // Default to 12:00 if no time given
|
||||
// Normalize any non-Latin digits (e.g. Persian/Arabic-Indic) before
|
||||
// parsing so due/start/end dates work in those locales (#5752).
|
||||
const dateValue = normalizeDigits(evt.target.date.value);
|
||||
const timeValue = normalizeDigits(evt.target.time.value) || '12:00'; // Default to 12:00 if no time given
|
||||
|
||||
if (!dateValue) {
|
||||
tpl.datePicker.error.set('invalid-date');
|
||||
|
||||
+41
-21
@@ -99,27 +99,23 @@ export const Utils = {
|
||||
return user.profile.mobileMode;
|
||||
}
|
||||
|
||||
// No explicit preference: pick a sensible default from the device.
|
||||
// Previously this returned true ONLY for iPhone/iPod, so every other phone
|
||||
// (Fairphone 4 postmarketOS Firefox, Ubuntu Touch Morph, Android Firefox,
|
||||
// ...) defaulted to desktop-mode. desktop-mode applies large desktop sizing
|
||||
// (see .desktop-mode rules in boardHeader.css) which on a small phone screen
|
||||
// looks roughly 2x too big. Default to mobile-mode for any phone-sized touch
|
||||
// device, not just iPhone, so non-iPhone phones render at the correct size.
|
||||
const ua = navigator.userAgent;
|
||||
const isIPhone = /iPhone|iPod/i.test(ua);
|
||||
const isPhoneUA =
|
||||
/Mobile|Android|BlackBerry|IEMobile|Opera Mini/i.test(ua) &&
|
||||
!/iPad/i.test(ua);
|
||||
const isUbuntuTouch = /Ubuntu/i.test(ua);
|
||||
const hasTouch =
|
||||
('maxTouchPoints' in navigator && navigator.maxTouchPoints > 0) ||
|
||||
(window.matchMedia && window.matchMedia('(pointer: coarse)').matches);
|
||||
// A small touch screen (phone-sized) — covers touch browsers whose UA does
|
||||
// not contain a phone token. Desktop browsers (no touch, or wide) stay in
|
||||
// desktop-mode.
|
||||
const isSmallTouchScreen = hasTouch && window.innerWidth <= 800;
|
||||
return isIPhone || isPhoneUA || isUbuntuTouch || isSmallTouchScreen;
|
||||
// No explicit preference: detect from the VIEWPORT, not the user-agent.
|
||||
// UA string parsing is unreliable across the many mobile browsers (Firefox,
|
||||
// Chrome, Safari, postmarketOS, Ubuntu Touch Morph, ...) and is exactly the
|
||||
// kind of fragile detection that bites projects — cf. Meteor #12421, where
|
||||
// Mobile Safari version parsing was wrong. matchMedia width/pointer queries
|
||||
// are well-supported and, importantly, match the CSS `@media (max-width:
|
||||
// 800px)` breakpoint, so the body.mobile-mode class and the responsive CSS
|
||||
// agree. A narrow screen, or a coarse (touch) pointer on a not-wide screen,
|
||||
// gets mobile mode; wide desktop windows (fine pointer) stay desktop.
|
||||
// NOTE: requires the width=device-width viewport meta (see
|
||||
// server/lib/customHeadRender.js); without it window.innerWidth reports ~980
|
||||
// on phones and this would never trigger.
|
||||
const mq = q =>
|
||||
typeof window !== 'undefined' && window.matchMedia && window.matchMedia(q).matches;
|
||||
const narrow = mq('(max-width: 800px)');
|
||||
const coarse = mq('(pointer: coarse)');
|
||||
return Boolean(narrow || (coarse && window.innerWidth <= 1024));
|
||||
},
|
||||
|
||||
setMobileMode(enabled) {
|
||||
@@ -262,6 +258,30 @@ export const Utils = {
|
||||
const mobileMode = Utils.getMobileMode();
|
||||
Utils.applyZoomLevel(zoomLevel);
|
||||
Utils.applyMobileMode(mobileMode);
|
||||
|
||||
// #6419: keep mobile-mode in sync with the viewport (resize / orientation /
|
||||
// responsive testing) when the user has NOT explicitly chosen a mode. Bound
|
||||
// once. An explicit toggle (localStorage or profile) must stick, so we skip
|
||||
// auto-switching in that case.
|
||||
if (
|
||||
typeof window !== 'undefined' &&
|
||||
window.matchMedia &&
|
||||
!Utils._mobileModeAutoBound
|
||||
) {
|
||||
Utils._mobileModeAutoBound = true;
|
||||
const reapply = () => {
|
||||
const stored = localStorage.getItem('wekan-mobile-mode');
|
||||
const user = ReactiveCache.getCurrentUser();
|
||||
const explicit =
|
||||
stored !== null ||
|
||||
(user && user.profile && user.profile.mobileMode !== undefined);
|
||||
if (!explicit) {
|
||||
Utils.applyMobileMode(Utils.getMobileMode());
|
||||
}
|
||||
};
|
||||
window.matchMedia('(max-width: 800px)').addEventListener('change', reapply);
|
||||
window.matchMedia('(pointer: coarse)').addEventListener('change', reapply);
|
||||
}
|
||||
},
|
||||
getCurrentList() {
|
||||
const listId = this.getCurrentListId();
|
||||
|
||||
+28
-10
@@ -191,6 +191,16 @@ AccountsTemplates.configureRoute('changePwd', {
|
||||
});
|
||||
|
||||
if (Meteor.isServer) {
|
||||
// #5706: Internal Server Error (500) when attempting to reset a password.
|
||||
// The reset-password email-template builders and the email send must never let
|
||||
// an unhandled exception bubble up through Meteor's `forgotPassword` method (it
|
||||
// surfaces to the client as an opaque HTTP 500). The pure helpers below are
|
||||
// unit-tested in tests/unit/resetPasswordEmail.test.js.
|
||||
const {
|
||||
buildEmailTemplateField,
|
||||
wrapSendResetPasswordEmail,
|
||||
} = require('/server/lib/resetPasswordEmail');
|
||||
|
||||
[
|
||||
'resetPassword-subject',
|
||||
'resetPassword-text',
|
||||
@@ -200,16 +210,24 @@ if (Meteor.isServer) {
|
||||
'enrollAccount-text',
|
||||
].forEach(str => {
|
||||
const [templateName, field] = str.split('-');
|
||||
Accounts.emailTemplates[templateName][field] = (user, url) => {
|
||||
return TAPi18n.__(
|
||||
`email-${str}`,
|
||||
{
|
||||
url,
|
||||
user: user.getName(),
|
||||
siteName: Accounts.emailTemplates.siteName,
|
||||
},
|
||||
user.getLanguage(),
|
||||
Accounts.emailTemplates[templateName][field] = (user, url) =>
|
||||
buildEmailTemplateField(
|
||||
str,
|
||||
(key, params, language) => TAPi18n.__(key, params, language),
|
||||
Accounts.emailTemplates.siteName,
|
||||
user,
|
||||
url,
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
// #5706: When SMTP is not configured (no MAIL_URL / MAIL_FROM, or a bad mail
|
||||
// server), Meteor's `Email.sendAsync` throws. That exception otherwise propagates
|
||||
// out of the `forgotPassword` method as a raw HTTP 500 ("Internal Server Error").
|
||||
// Wrap the reset-password email send so the failure surfaces as a clean,
|
||||
// actionable `Meteor.Error('email-fail', ...)` instead, matching how the rest of
|
||||
// Wekan reports email failures (see server/models/users.js, settings.js).
|
||||
Accounts.sendResetPasswordEmail = wrapSendResetPasswordEmail(
|
||||
Accounts.sendResetPasswordEmail,
|
||||
(code, message) => new Meteor.Error(code, message),
|
||||
);
|
||||
}
|
||||
+2
-2
@@ -171,7 +171,7 @@ services:
|
||||
# Leave it unset or empty to skip migration.
|
||||
#-------------------------------------------------------------------------------------
|
||||
#wekan-migrate:
|
||||
# image: node:24.15.0-slim
|
||||
# image: node:24.17.0-slim
|
||||
# container_name: wekan-migrate
|
||||
# restart: "no"
|
||||
# networks:
|
||||
@@ -324,7 +324,7 @@ services:
|
||||
#- 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
|
||||
# Use oplog,polling to fix performance until changeStreams is fixed at next release of Meteor 3.5 Beta:
|
||||
# Keep oplog/polling fallback available for deployments that cannot use Change Streams:
|
||||
# https://github.com/wekan/wekan/issues/6307#issuecomment-4299349231
|
||||
- METEOR_REACTIVITY_ORDER=changeStreams,oplog,polling
|
||||
- DDP_TRANSPORT=uws
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@ curl -H "Authorization: Bearer a6DM_gOPRwBdynfXaGBaiiEwTiAuigR_Fj_81QmNpnf" \
|
||||
|
||||
## Add/Remove Board Member and Change Role
|
||||
|
||||
[Add/Remove Board Member and Change Role admin/normal/nocomments/commentonly](REST-API-Role).
|
||||
[Add/Remove Board Member and Change Role admin/normal/nocomments/commentonly](Role.md).
|
||||
|
||||
## The admin takes the ownership of ALL boards of the user (archived and not archived) where the user is admin on.
|
||||
|
||||
@@ -53,4 +53,4 @@ curl -H "Authorization: Bearer t7iYB86mXoLfP_XsMegxF41oKT7iiA9lDYiKVtXcctl" \
|
||||
|
||||
## In Wekan code
|
||||
|
||||
If you believe that code is the best documentation, be our guest: [models/cards.js](https://github.com/wekan/wekan/blob/main/models/boards.js "Board API code")
|
||||
If you believe that code is the best documentation, be our guest: [models/cards.js](../../models/boards.js "Board API code")
|
||||
+1
-1
@@ -91,4 +91,4 @@ The card's ID is returned in the format:
|
||||
|
||||
# In Wekan code
|
||||
|
||||
If you believe that code is the best documentation, be our guest: [models/cards.js](https://github.com/wekan/wekan/blob/main/models/cards.js "Card API code")
|
||||
If you believe that code is the best documentation, be our guest: [models/cards.js](../../models/cards.js "Card API code")
|
||||
+5
-5
@@ -8,26 +8,26 @@ meteor add simple:json-routes
|
||||
|
||||
https://atmospherejs.com/simple/json-routes
|
||||
|
||||
https://github.com/wekan/wekan/blob/main/.meteor/packages#L139
|
||||
[packages](../../.meteor/packages#L139)
|
||||
|
||||
### 2) API code
|
||||
|
||||
At models directory:
|
||||
|
||||
https://github.com/wekan/wekan/blob/main/models/users.js#L2018
|
||||
[users.js](../../models/users.js#L2018)
|
||||
|
||||
### 3) Login to API as JSON
|
||||
|
||||
https://github.com/wekan/wekan/wiki/REST-API#example-call---as-json
|
||||
[REST API](REST-API.md#example-call---as-json)
|
||||
|
||||
Also see at wiki right menu about REST API.
|
||||
|
||||
### 4) Use Python3 CLI to login to API and do REST API action
|
||||
|
||||
https://github.com/wekan/wekan/blob/main/api.py
|
||||
[api.py](../../api.py)
|
||||
|
||||
### 4) API Docs
|
||||
|
||||
https://wekan.fi/api/
|
||||
|
||||
Building API Docs: https://github.com/wekan/wekan/blob/main/releases/rebuild-docs.sh
|
||||
Building API Docs: [rebuild-docs.sh](../../releases/rebuild-docs.sh)
|
||||
@@ -1,5 +1,5 @@
|
||||
1) Login as Admin user as Form Data to get Bearer token
|
||||
https://github.com/wekan/wekan/wiki/REST-API#example-call---as-form-data
|
||||
[REST API](REST-API.md#example-call---as-form-data)
|
||||
|
||||
2) There needs to be Custom Field added to board
|
||||
https://wekan.fi/api/v4.42/#wekan-rest-api-customfields
|
||||
|
||||
@@ -2,7 +2,7 @@ Wekan provides a python script to ease the call of the REST API from command lin
|
||||
|
||||
# Context
|
||||
|
||||
- [API Login to get Bearer token](REST-API#example-call---as-form-data)
|
||||
- [API Login to get Bearer token](REST-API.md#example-call---as-form-data)
|
||||
- [API docs and examples for various programming languages](https://wekan.fi/api/), there is Boards / Export for exporting board with API
|
||||
- In the right menu, scroll down REST API Docs etc links =====>
|
||||
- Wekan-Gogs integration with Node.js https://github.com/wekan/wekan-gogs
|
||||
|
||||
+12
-12
@@ -31,21 +31,21 @@ When calling a production Wekan server, ensure it is running via HTTPS and has a
|
||||
### Users
|
||||
| HTTP Method | Url | Short Description |
|
||||
| :--- | :--- | :--- |
|
||||
| `POST` | `/users/register` | [Register a new user.](REST-API-User#user-register) |
|
||||
| `POST` | `/api/users` | [Create a new user.](REST-API-User#user-create) |
|
||||
| `PUT` | `/api/users/:id` | [Disable an existing user.](REST-API-User#disable-a-user-the-user-is-not-allowed-to-login-and-his-login-tokens-are-purged) |
|
||||
| `PUT` | `/api/users/:id` | [Enable an existing user.](REST-API-User#enable-a-user) |
|
||||
| `PUT` | `/api/users/:id` | [Admin takes the ownership.](REST-API-User#the-admin-takes-the-ownership-of-all-boards-of-the-user-archived-and-not-archived-where-the-user-is-admin-on) |
|
||||
| `DELETE` | `/api/users/:id` | [Delete an existing user.](REST-API-User#user-delete) ([Warning](https://github.com/wekan/wekan/issues/1289))|
|
||||
| `GET` | `/api/users/:id` | [Gets a user's information.](REST-API-User#user-information) |
|
||||
| `GET` | `/api/users` | [All of the users.](REST-API-User#user-list) |
|
||||
| `GET` | `/api/user` | [Gets a logged-in user.](REST-API-User#user-logged-in) |
|
||||
| `POST` | `/users/register` | [Register a new user.](User.md#user-register) |
|
||||
| `POST` | `/api/users` | [Create a new user.](User.md#user-create) |
|
||||
| `PUT` | `/api/users/:id` | [Disable an existing user.](User.md#disable-a-user-the-user-is-not-allowed-to-login-and-his-login-tokens-are-purged) |
|
||||
| `PUT` | `/api/users/:id` | [Enable an existing user.](User.md#enable-a-user) |
|
||||
| `PUT` | `/api/users/:id` | [Admin takes the ownership.](User.md#the-admin-takes-the-ownership-of-all-boards-of-the-user-archived-and-not-archived-where-the-user-is-admin-on) |
|
||||
| `DELETE` | `/api/users/:id` | [Delete an existing user.](User.md#user-delete) ([Warning](https://github.com/wekan/wekan/issues/1289))|
|
||||
| `GET` | `/api/users/:id` | [Gets a user's information.](User.md#user-information) |
|
||||
| `GET` | `/api/users` | [All of the users.](User.md#user-list) |
|
||||
| `GET` | `/api/user` | [Gets a logged-in user.](User.md#user-logged-in) |
|
||||
### Cards
|
||||
| HTTP Method | Url | Short Description |
|
||||
| :--- | :--- | :--- |
|
||||
| `POST` | `/api/boards/:boardId/lists/:listId/cards` | [Add a card to a list, board, and swimlane.](REST-API-Cards#add-card-to-list-board-swimlane) |
|
||||
| `PUT` | `/api/boards/:boardId/lists/:fromListId/cards/:cardId` | [Update a card.](REST-API-Cards#update-a-card) |
|
||||
| `DELETE` | `/api/boards/:boardId/lists/:listId/cards/:cardId` | [Delete a card.](REST-API-Cards#update-a-card) |
|
||||
| `POST` | `/api/boards/:boardId/lists/:listId/cards` | [Add a card to a list, board, and swimlane.](Cards.md#add-card-to-list-board-swimlane) |
|
||||
| `PUT` | `/api/boards/:boardId/lists/:fromListId/cards/:cardId` | [Update a card.](Cards.md#update-a-card) |
|
||||
| `DELETE` | `/api/boards/:boardId/lists/:listId/cards/:cardId` | [Delete a card.](Cards.md#update-a-card) |
|
||||
### Board Domain Sharing
|
||||
| HTTP Method | Url | Short Description |
|
||||
| :--- | :--- | :--- |
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ BoardAdmin can change role of user at right sidebar / click avatar / select role
|
||||
|
||||
- https://wekan.fi/api/
|
||||
- api.py at https://github.com/wekan/wekan
|
||||
- Login to API https://github.com/wekan/wekan/wiki/REST-API#example-call---as-json
|
||||
- Login to API [REST API](REST-API.md#example-call---as-json)
|
||||
|
||||
# Disclaimer
|
||||
|
||||
|
||||
@@ -6,13 +6,13 @@ This page tries to be as up to date as possible. If you see something wrong here
|
||||
|
||||
Please somebody add example by looking this:
|
||||
|
||||
[Issue](https://github.com/wekan/wekan/issues/1934) and [code](https://github.com/wekan/wekan/pull/1944/commits/be42b8d4cbdfa547ca019ab2dc9a590a115cc0e2). Also add to [Cards page](REST-API-Cards)
|
||||
[Issue](https://github.com/wekan/wekan/issues/1934) and [code](https://github.com/wekan/wekan/pull/1944/commits/be42b8d4cbdfa547ca019ab2dc9a590a115cc0e2). Also add to [Cards page](Cards.md)
|
||||
|
||||
# Add Swimlane to Board
|
||||
|
||||
| API URL / Code Link | Requires Admin Auth | HTTP Method |
|
||||
| :--- | :--- | :--- |
|
||||
| [/api/boards/:boardId/swimlanes](https://github.com/wekan/wekan/blob/main/models/swimlanes.js#L223) | `yes` | `POST` |
|
||||
| [/api/boards/:boardId/swimlanes](../../models/swimlanes.js#L223) | `yes` | `POST` |
|
||||
|
||||
```shell
|
||||
curl -H "Authorization: Bearer t7iYB86mXoLfP_XsMegxF41oKT7iiA9lDYiKVtXcctl" \
|
||||
@@ -33,7 +33,7 @@ The new swimlane's ID is returned in the format:
|
||||
|
||||
| API URL / Code Link | Requires Admin Auth | HTTP Method |
|
||||
| :--- | :--- | :--- |
|
||||
| [/api/boards/:boardId/swimlanes/:swimlaneId](https://github.com/wekan/wekan/blob/main/models/swimlanes.js#L257) | `yes` | `DELETE` |
|
||||
| [/api/boards/:boardId/swimlanes/:swimlaneId](../../models/swimlanes.js#L257) | `yes` | `DELETE` |
|
||||
|
||||
```shell
|
||||
curl -H "Authorization: Bearer t7iYB86mXoLfP_XsMegxF41oKT7iiA9lDYiKVtXcctl" \
|
||||
@@ -51,4 +51,4 @@ The swimlane's ID is returned in the format:
|
||||
|
||||
# In Wekan code
|
||||
|
||||
If you believe that code is the best documentation, be our guest: [models/cards.js](https://github.com/wekan/wekan/blob/main/models/swimlanes.js "Swimlane API code")
|
||||
If you believe that code is the best documentation, be our guest: [models/cards.js](../../models/swimlanes.js "Swimlane API code")
|
||||
+1
-1
@@ -332,4 +332,4 @@ curl -H "Authorization: Bearer t7iYB86mXoLfP_XsMegxF41oKT7iiA9lDYiKVtXcctl" \
|
||||
|
||||
# In Wekan code
|
||||
|
||||
If you believe that code is the best documentation, be our guest: [models/cards.js](https://github.com/wekan/wekan/blob/main/models/users.js "User API code")
|
||||
If you believe that code is the best documentation, be our guest: [models/cards.js](../../models/users.js "User API code")
|
||||
@@ -1,4 +1,4 @@
|
||||
[Sandstorm](Sandstorm) - [Sandstorm Backup](Export-from-Wekan-Sandstorm-grain-.zip-file)
|
||||
[Sandstorm](../Platforms/FOSS/Sandstorm/Sandstorm.md) - [Sandstorm Backup](../Platforms/FOSS/Sandstorm/Export-from-Wekan-Sandstorm-grain-.zip-file.md)
|
||||
|
||||
# Related
|
||||
|
||||
@@ -104,7 +104,7 @@ sudo snap refresh wekan --channel=latest/candidate --amend
|
||||
```
|
||||
5. [Restore Snap](#restore-wekan-snap)
|
||||
6. Copy back files directory, if it is there: `sudo cp -pR /root/common/files /var/snap/wekan/common/`
|
||||
7. If you use [Caddy](https://github.com/wekan/wekan/blob/main/docs/Webserver/Caddy.md), that is included in WeKan, edit /var/snap/wekan/Caddyfile to new syntax:
|
||||
7. If you use [Caddy](../Webserver/Caddy.md), that is included in WeKan, edit /var/snap/wekan/Caddyfile to new syntax:
|
||||
```
|
||||
wekan.yourcompany.com {
|
||||
tls {
|
||||
@@ -211,7 +211,7 @@ https://nosqlbooster.com/downloads
|
||||
|
||||
For below scheduled backup scripts, no info from above of this wiki page is required. Backup scripts below have the required settings.
|
||||
|
||||
This does backup of [Wekan+RocketChat snap databases](OAuth2) and php website etc.
|
||||
This does backup of [Wekan+RocketChat snap databases](../Login/OAuth2.md) and php website etc.
|
||||
|
||||
If you need to backup some remote server or cloud, you can use scp, or read [rclone docs](https://rclone.org/docs/) about how to configure saving to some other remote server or cloud.
|
||||
|
||||
@@ -520,7 +520,7 @@ cat board.json | xclip -se c
|
||||
```
|
||||
Then paste to webbrowser Wekan Add Board / Import / From previous export.
|
||||
|
||||
You can [save all MongoDB database content as JSON files](Export-from-Wekan-Sandstorm-grain-.zip-file). Files are base64 encoded in JSON files.
|
||||
You can [save all MongoDB database content as JSON files](../Platforms/FOSS/Sandstorm/Export-from-Wekan-Sandstorm-grain-.zip-file.md). Files are base64 encoded in JSON files.
|
||||
|
||||
Export board to Wekan JSON, and import as Wekan JSON can make some part of board to load, but you should check is some data missing.
|
||||
|
||||
@@ -762,7 +762,7 @@ makesRestore $1
|
||||
|
||||
## Docker Backup and Restore
|
||||
|
||||
[Docker Backup and Restore](Export-Docker-Mongo-Data)
|
||||
[Docker Backup and Restore](../Platforms/FOSS/Docker/Export-Docker-Mongo-Data.md)
|
||||
|
||||
[Wekan Docker Upgrade](https://github.com/wekan/wekan-mongodb#backup-before-upgrading)
|
||||
|
||||
@@ -776,11 +776,11 @@ makesRestore $1
|
||||
|
||||
Download Wekan grain with arrow down download button to .zip file. You can restore it later.
|
||||
|
||||
[Export data from Wekan Sandstorm grain .zip file](Export-from-Wekan-Sandstorm-grain-.zip-file)
|
||||
[Export data from Wekan Sandstorm grain .zip file](../Platforms/FOSS/Sandstorm/Export-from-Wekan-Sandstorm-grain-.zip-file.md)
|
||||
|
||||
## <a name="cloudron">Cloudron
|
||||
|
||||
If those [Backup](Backup) ways are not easily found at [Cloudron](Cloudron), one way is to install [Redash](https://redash.io/) and then backup this way:
|
||||
If those [Backup](Backup.md) ways are not easily found at [Cloudron](../Platforms/Propietary/Cloud/Cloudron/Cloudron.md), one way is to install [Redash](https://redash.io/) and then backup this way:
|
||||
|
||||
Redash works with this kind of queries:
|
||||
```json
|
||||
@@ -841,8 +841,8 @@ but downloading with API script still works:
|
||||
|
||||
7) Repeat steps 1-4 and 6 for every collection/table like boards,cards, etc
|
||||
|
||||
8) Remove from downloaded .json files extra query related data, so that it is similar like [any other Wekan database backup JSON files](Export-from-Wekan-Sandstorm-grain-.zip-file)
|
||||
8) Remove from downloaded .json files extra query related data, so that it is similar like [any other Wekan database backup JSON files](../Platforms/FOSS/Sandstorm/Export-from-Wekan-Sandstorm-grain-.zip-file.md)
|
||||
|
||||
9) Insert data to some other Wekan install with nosqlbooster like mentioned at page [Backup](Backup)
|
||||
9) Insert data to some other Wekan install with nosqlbooster like mentioned at page [Backup](Backup.md)
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ Current working way to upgrade is:
|
||||
|
||||
## Docker
|
||||
|
||||
1. Create Mongodump of your database like at [Backup page](https://github.com/wekan/wekan/wiki/Backup)
|
||||
1. Create Mongodump of your database like at [Backup page](../Backup.md)
|
||||
2. Use newest WeKan docker-compose.yml from https://github.com/wekan/wekan with WRITABLE_PATH setup to Rclone/MinIO like below this page
|
||||
3. Mongorestore to MongoDB 6.0.3 database
|
||||
4. Use WeKan Admin Panel `Move all attachments to filesystem` button to move all attachments to MinIO.
|
||||
|
||||
@@ -52,7 +52,7 @@ apt install ntfs-3g
|
||||
```
|
||||
mount /dev/sdd1 /mnt
|
||||
```
|
||||
4. Stop WeKan Docker containers. If copying raw database files, WeKan and MongoDB should not be running, so that MongoDB would not be even more corrupted. (If MongoDB is running, and you have mongodump backup, you can [Backup with mongorestore](Backup)
|
||||
4. Stop WeKan Docker containers. If copying raw database files, WeKan and MongoDB should not be running, so that MongoDB would not be even more corrupted. (If MongoDB is running, and you have mongodump backup, you can [Backup with mongorestore](Backup.md)
|
||||
```
|
||||
docker stop wekan-db wekan-app
|
||||
```
|
||||
|
||||
@@ -3,11 +3,11 @@ If you know updates to this, please add new issue about it. Make changes to this
|
||||
```
|
||||
git clone https://github.com/wekan/wekan.wiki
|
||||
```
|
||||
There edit Browser-compatibility-matrix.md and attach it with .txt extension or in .zip file to https://github.com/wekan/wekan/wiki/issues
|
||||
There edit Browser-compatibility-matrix.md and attach it with .txt extension or in .zip file to https://github.com/wekan/wekan/issues
|
||||
|
||||
## Webkit based
|
||||
|
||||
Browser | [PWA](PWA) | Mobile | Desktop | OS
|
||||
Browser | [PWA](PWA.md) | Mobile | Desktop | OS
|
||||
------- | ----| ------ | ------- | -------
|
||||
Safari | iOS Yes | iOS Newest | Newest | iOS, macOS
|
||||
Iris | No | No | Newest | [Iris browser news](https://www.riscosopen.org/forum/forums/1/topics/19545), Download [RISC OS Direct](https://www.riscosdev.com/direct/) for Raspberry Pi etc. [Other versions of RISC OS for many ARM CPU devices](https://www.riscosopen.org/content/downloads) do not have Iris browser yet, but there could be some way to transfer Iris browser files from RISC OS Direct.
|
||||
@@ -16,7 +16,7 @@ Iris | No | No | Newest | [Iris browser news](https://www.riscosopen.org/forum/f
|
||||
|
||||
Any telemetry at any Firefox based browser can be additionally most locked down with arkenfox user JS.
|
||||
|
||||
Browser | [PWA](PWA) | Mobile | Desktop | OS
|
||||
Browser | [PWA](PWA.md) | Mobile | Desktop | OS
|
||||
------- | ----| ------ | ------- | -------
|
||||
[uWolf](https://open-store.io/app/uwolf.chromiumos-guy) | No | Yes | No | Ubuntu Touch webbrowser based of LibreWolf
|
||||
[LibreWolf](https://librewolf.net) | No | No | Yes | [Win/Mac/Linux](https://librewolf.net/installation/), [Repos](https://codeberg.org/librewolf)
|
||||
@@ -30,7 +30,7 @@ Firefox | No | Newest | Newest | iOS/Android/Win/Mac/Linux/BSD/RasPi. At iOS use
|
||||
|
||||
## Chromium based
|
||||
|
||||
Browser | [PWA](PWA) | Mobile | Desktop | OS
|
||||
Browser | [PWA](PWA.md) | Mobile | Desktop | OS
|
||||
------- | ----| ------ | ------- | -------
|
||||
Chrome | Android Yes | Android Newest | Newest | Win/Mac/Linux
|
||||
Chromium | Android Yes | Android Newest | Newest | Win/Mac/Linux/RasPi
|
||||
@@ -47,13 +47,13 @@ Morph Browser | No | Yes | Yes | [Ubuntu Touch](https://ubports.com) based on Ub
|
||||
|
||||
## EdgeHTML based
|
||||
|
||||
Browser | [PWA](PWA) | Mobile | Desktop | OS
|
||||
Browser | [PWA](PWA.md) | Mobile | Desktop | OS
|
||||
------- | ----| ------ | ------- | -------
|
||||
Legacy Edge | No | No | Not | Not compatible with newest WeKan. Worked with Wekan v4.19 and before.
|
||||
|
||||
## MSHTML based
|
||||
|
||||
Browser | [PWA](PWA) | Mobile | Desktop | OS
|
||||
Browser | [PWA](PWA.md) | Mobile | Desktop | OS
|
||||
------- | ----| ------ | ------- | -------
|
||||
Internet Explorer | No | No | No | No | No
|
||||
|
||||
@@ -73,7 +73,7 @@ Internet Explorer | No | No | No | No | No
|
||||
|
||||
## TUI webbrowsers, not yet supported
|
||||
|
||||
Browser | [PWA](PWA) | Mobile | Desktop | OS
|
||||
Browser | [PWA](PWA.md) | Mobile | Desktop | OS
|
||||
------- | ----| ------ | ------- | -------
|
||||
[Chawan](https://chawan.net) | No | No | Yes | Linux/BSD. [Release 0.2.0](https://chawan.net/news/chawan-0-2-0.html), [HN](https://news.ycombinator.com/item?id=44293260)
|
||||
Elinks | No | No | Yes | Win/Mac/Linux/BSD/DOS TUI.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[Browser compatibility matrix](../Browser/Browser-compatibility-matrix)
|
||||
[Browser compatibility matrix](Browser-compatibility-matrix.md)
|
||||
|
||||
## Install your Wekan server as an app
|
||||
|
||||
@@ -85,17 +85,17 @@
|
||||
|
||||
These are mostly just notes to WeKan maintainer xet7 itself, how xet7 did get official WeKan Android Play Store app working at fullscreen of Android phone and Android tablet. Requires a lot time to setup, not recommended.
|
||||
|
||||
Related, for creating apps to other appstores: https://github.com/wekan/wekan/wiki/Browser-compatibility-matrix
|
||||
Related, for creating apps to other appstores: [Browser compatibility matrix](Browser-compatibility-matrix.md)
|
||||
|
||||
1. https://www.pwabuilder.com to create Android app. Select fullscreen when creating app, also requires correctly installed assetlinks.json with below Caddy config to get fullscreen. When creating app, make your own signing key if you don't have one yet. Releasing Android app is at Play Console https://play.google.com/console/ .
|
||||
|
||||
2. PWABuilder has about 100+ downloadable icons etc webmanifest requirements like is at https://github.com/wekan/wekan/tree/main/public
|
||||
2. PWABuilder has about 100+ downloadable icons etc webmanifest requirements like is at [public](../../public)
|
||||
|
||||
3. Clone WeKan repo, add favicons etc from step 2 to wekan/public/ , and build WeKan bundle from source like at https://github.com/wekan/wekan/wiki/Emoji . Note: Currently WeKan does not have feature for custom favicons, it would require a lot of work for 100+ favicons etc customizations.
|
||||
3. Clone WeKan repo, add favicons etc from step 2 to wekan/public/ , and build WeKan bundle from source like at [Emoji](../Features/Emoji.md) . Note: Currently WeKan does not have feature for custom favicons, it would require a lot of work for 100+ favicons etc customizations.
|
||||
|
||||
4. Run bundle at server like https://github.com/wekan/wekan/wiki/Offline or https://github.com/wekan/wekan/wiki/Raspberry-Pi
|
||||
4. Run bundle at server like [Offline](../Platforms/Propietary/Windows/Offline.md) or [Raspberry Pi](../Platforms/FOSS/RaspberryPi/Raspberry-Pi.md)
|
||||
|
||||
5. Install Caddy like https://github.com/wekan/wekan/wiki/Caddy-Webserver-Config , with this config for PWA at https://boards.example.com , there add assetlinks.json details you got when you downloaded Android .zip file from https://pwabuilder.com , see assetlinks issue about where at Play Console those 2 SHA256 keys https://github.com/pwa-builder/PWABuilder/issues/3867#issuecomment-1450826565
|
||||
5. Install Caddy like [Caddy Webserver Config](../Webserver/Caddy.md) , with this config for PWA at https://boards.example.com , there add assetlinks.json details you got when you downloaded Android .zip file from https://pwabuilder.com , see assetlinks issue about where at Play Console those 2 SHA256 keys https://github.com/pwa-builder/PWABuilder/issues/3867#issuecomment-1450826565
|
||||
|
||||
6. At Play Console https://play.google.com/console/ there is `App Integrity` button in the sidemenu (highlighted blue in the screenshot) where you find required 2 SHA256 keys for Caddyfile:
|
||||
|
||||
|
||||
@@ -158,13 +158,13 @@ sudo systemctl enable wekan
|
||||
sudo systemctl start wekan
|
||||
```
|
||||
For SSL/TLS, I run Caddy at front of Node.js:
|
||||
https://github.com/wekan/wekan/blob/main/docs/Webserver/Caddy.md
|
||||
[Caddy](../Webserver/Caddy.md)
|
||||
|
||||
Related is docs about Raspberry Pi:
|
||||
https://github.com/wekan/wekan/blob/main/docs/Platforms/FOSS/RaspberryPi/Raspberry-Pi.md
|
||||
[Raspberry Pi](../Platforms/FOSS/RaspberryPi/Raspberry-Pi.md)
|
||||
|
||||
And also about Windows bundle:
|
||||
https://github.com/wekan/wekan/blob/main/docs/Platforms/Propietary/Windows/Offline.md
|
||||
[Offline](../Platforms/Propietary/Windows/Offline.md)
|
||||
|
||||
## Notes
|
||||
|
||||
|
||||
@@ -161,9 +161,9 @@ top -p $(pgrep node)
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [MongoDB-Oplog-Configuration.md](../docs/Databases/MongoDB-Oplog-Configuration.md) - Full setup guide
|
||||
- [AWS.md](../docs/Platforms/Propietary/Cloud/AWS.md) - AWS oplog configuration
|
||||
- [LDAP.md](../docs/Login/LDAP.md) - LDAP with oplog setup
|
||||
- [MongoDB-Oplog-Configuration.md](MongoDB-Oplog-Configuration.md) - Full setup guide
|
||||
- [AWS.md](../Platforms/Propietary/Cloud/AWS.md) - AWS oplog configuration
|
||||
- [LDAP.md](../Login/LDAP.md) - LDAP with oplog setup
|
||||
- [ToroDB-PostgreSQL](../docs/Databases/ToroDB-PostgreSQL/docker-compose.yml) - ToroDB oplog config
|
||||
|
||||
## Files Modified This Session
|
||||
|
||||
@@ -79,7 +79,7 @@ docker-compose up -d
|
||||
9) Wekan and databases bind to address 0.0.0.0 so could be also available to other
|
||||
computers in network. I have not tested this.
|
||||
|
||||
10) [Restore your MongoDB data](https://github.com/wekan/wekan/wiki/Export-Docker-Mongo-Data).
|
||||
10) [Restore your MongoDB data](../../Platforms/FOSS/Docker/Export-Docker-Mongo-Data.md).
|
||||
|
||||
## Feedback
|
||||
|
||||
|
||||
@@ -3,14 +3,14 @@ version: '2'
|
||||
# Note: Do not add single quotes '' to variables. Having spaces still works without quotes where required.
|
||||
#---------------------------------------------------------------------------------------------------------
|
||||
# ==== CREATING USERS AND LOGGING IN TO WEKAN ====
|
||||
# https://github.com/wekan/wekan/wiki/Adding-users
|
||||
# ../../Login/Adding-users.md
|
||||
#---------------------------------------------------------------------------------------------------------
|
||||
# ==== FORGOT PASSWORD ====
|
||||
# https://github.com/wekan/wekan/wiki/Forgot-Password
|
||||
# ../../Login/Forgot-Password.md
|
||||
#---------------------------------------------------------------------------------------------------------
|
||||
# ==== Upgrading Wekan to new version =====
|
||||
# NOTE: MongoDB has changed from 3.x to 4.x, in that case you need backup/restore with --noIndexRestore
|
||||
# see https://github.com/wekan/wekan/wiki/Backup
|
||||
# see ../../Backup/Backup.md
|
||||
# 1) Stop Wekan:
|
||||
# docker-compose stop
|
||||
# 2) Remove old Wekan app (wekan-app only, not that wekan-db container that has all your data)
|
||||
@@ -48,7 +48,7 @@ version: '2'
|
||||
# docker-compose stop
|
||||
# ----------------------------------------------------------------------------------
|
||||
# ===== INSIDE DOCKER CONTAINERS, AND BACKUP/RESTORE ====
|
||||
# https://github.com/wekan/wekan/wiki/Backup
|
||||
# ../../Backup/Backup.md
|
||||
# If really necessary, repair MongoDB: https://github.com/wekan/wekan-mongodb/issues/6#issuecomment-424004116
|
||||
# 1) Going inside containers:
|
||||
# a) Wekan app, does not contain data
|
||||
@@ -195,7 +195,7 @@ services:
|
||||
# ==== 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/wiki/Troubleshooting-Mail
|
||||
# see ../../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
|
||||
@@ -236,7 +236,7 @@ services:
|
||||
# - export KADIRA_OPTIONS_ENDPOINT=http://127.0.0.1:11011
|
||||
#---------------------------------------------------------------
|
||||
# ==== OPTIONAL: LOGS AND STATS ====
|
||||
# https://github.com/wekan/wekan/wiki/Logs
|
||||
# ../../Features/Logs.md
|
||||
#
|
||||
# Daily export of Wekan changes as JSON to Logstash and ElasticSearch / Kibana (ELK)
|
||||
# https://github.com/wekan/wekan-logstash
|
||||
@@ -256,7 +256,7 @@ services:
|
||||
#---------------------------------------------------------------
|
||||
# ==== WEKAN API AND EXPORT BOARD ====
|
||||
# Wekan Export Board works when WITH_API=true.
|
||||
# https://github.com/wekan/wekan/wiki/REST-API
|
||||
# ../../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
|
||||
@@ -361,7 +361,7 @@ services:
|
||||
#- MATOMO_WITH_USERNAME=false
|
||||
#-----------------------------------------------------------------
|
||||
# ==== METRICS ALLOWED IP ADDRESSES ====
|
||||
# https://github.com/wekan/wekan/wiki/Metrics
|
||||
# ../../Features/Metrics.md
|
||||
#- METRICS_ALLOWED_IP_ADDRESSES=192.168.0.100,192.168.0.200
|
||||
#-----------------------------------------------------------------
|
||||
# ==== BROWSER POLICY AND TRUSTED IFRAME URL ====
|
||||
@@ -378,7 +378,7 @@ services:
|
||||
#- WEBHOOKS_ATTRIBUTES=
|
||||
#-----------------------------------------------------------------
|
||||
# ==== AUTOLOGIN WITH OIDC/OAUTH2 ====
|
||||
# https://github.com/wekan/wekan/wiki/autologin
|
||||
# ../../Login/autologin.md
|
||||
#- OIDC_REDIRECTION_ENABLED=true
|
||||
#---------------------------------------------
|
||||
# ==== OAUTH2 ORACLE on premise identity manager OIM ====
|
||||
@@ -386,7 +386,7 @@ services:
|
||||
#-----------------------------------------------------------------
|
||||
# ==== OAUTH2 ONLY WITH OIDC AND DOORKEEPER AS INDENTITY PROVIDER
|
||||
# https://github.com/wekan/wekan/issues/1874
|
||||
# https://github.com/wekan/wekan/wiki/OAuth2
|
||||
# ../../Login/OAuth2.md
|
||||
# Enable the OAuth2 connection
|
||||
# example: OAUTH2_ENABLED=true
|
||||
#- OAUTH2_ENABLED=false
|
||||
@@ -396,7 +396,7 @@ services:
|
||||
#- OAUTH2_ADFS_ENABLED=false
|
||||
# Azure AD B2C. https://github.com/wekan/wekan/issues/5242
|
||||
#- OAUTH2_B2C_ENABLED=false
|
||||
# OAuth2 docs: https://github.com/wekan/wekan/wiki/OAuth2
|
||||
# OAuth2 docs: ../../Login/OAuth2.md
|
||||
# OAuth2 Client ID, for example from Rocket.Chat. Example: abcde12345
|
||||
# example: OAUTH2_CLIENT_ID=abcde12345
|
||||
#- OAUTH2_CLIENT_ID=
|
||||
@@ -417,7 +417,7 @@ services:
|
||||
#- OAUTH2_TOKEN_ENDPOINT=
|
||||
#-----------------------------------------------------------------
|
||||
# ==== LDAP ====
|
||||
# https://github.com/wekan/wekan/wiki/LDAP
|
||||
# ../../Login/LDAP.md
|
||||
# For Snap settings see https://github.com/wekan/wekan-snap/wiki/Supported-settings-keys
|
||||
# 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.
|
||||
|
||||
@@ -13,11 +13,11 @@ https://user-images.githubusercontent.com/70445835/184324324-b4bd466e-6873-4eef-
|
||||
|
||||
## <a name="Time"></a>Time
|
||||
|
||||
* [Due Date](Due-Date)
|
||||
* [Day of week start](Day-of-week-start)
|
||||
* [Due Date](Due-Date.md)
|
||||
* [Day of week start](Day-of-week-start.md)
|
||||
|
||||
## What features are missing
|
||||
|
||||
* [Calendar issues](https://github.com/wekan/wekan/issues?q=is%3Aissue+is%3Aopen+calendar+label%3AFeature%3ACalendar-view)
|
||||
* [Time Tracking](Time-Tracking)
|
||||
* [Gantt Chart](Gantt)
|
||||
* [Time Tracking](Time-Tracking.md)
|
||||
* [Gantt Chart](../Features/Gantt.md)
|
||||
@@ -15,7 +15,7 @@ sudo apt -y install ntpdate
|
||||
sudo ntpdate ntp.ubuntu.com
|
||||
```
|
||||
|
||||
5. Now local time shows correctly at webbrowser, like for example at Friend Desktop https://github.com/wekan/wekan/wiki/Friend
|
||||
5. Now local time shows correctly at webbrowser, like for example at Friend Desktop [Friend](../Platforms/FOSS/Friend/Friend.md)
|
||||
|
||||
For testing any other time related code, that it shows correct local time, compare time functions results to this:
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ https://www.youtube.com/watch?v=ke-mbnZM3zE
|
||||
- Translated to 70+ languages https://explore.transifex.com/wekan/wekan/
|
||||
- Used in most countries of the world https://snapcraft.io/wekan
|
||||
- Biggest user have about 30k users, using many pods at Kubernetes
|
||||
- Changelog https://github.com/wekan/wekan/blob/main/CHANGELOG.md
|
||||
- Changelog [CHANGELOG.md](../../CHANGELOG.md)
|
||||
- Commercial Support at https://wekan.fi/commercial-support/ . Currently looking who could sponsor more of maintenance and development.
|
||||
|
||||
## How much effort to create Meteor WeKan, in COCOMO model
|
||||
@@ -26,39 +26,39 @@ https://www.youtube.com/watch?v=ke-mbnZM3zE
|
||||
## Meteor WeKan Browser support
|
||||
|
||||
- Javascript enabled Desktop and Mobile Safari/Chromium/Firefox based browsers
|
||||
- Apps at Play Store, Windows Microsoft Store, Ubuntu Touch OpenStore https://github.com/wekan/wekan/wiki/Browser-compatibility-matrix
|
||||
- Alternatively, for self-hosted WeKan, create PWA icon https://github.com/wekan/wekan/wiki/PWA
|
||||
- Using WeKan with big touchscreen https://github.com/wekan/wekan/wiki/Touch-Screen-support
|
||||
- Apps at Play Store, Windows Microsoft Store, Ubuntu Touch OpenStore [Browser compatibility matrix](../Browsers/Browser-compatibility-matrix.md)
|
||||
- Alternatively, for self-hosted WeKan, create PWA icon [PWA](../Browsers/PWA.md)
|
||||
- Using WeKan with big touchscreen [Touch Screen support](../DragDrop/Touch-Screen-support.md)
|
||||
|
||||
## Meteor WeKan Server
|
||||
|
||||
- Meteor.js 2.x, Node.js 14.x https://github.com/wekan/node-v14-esm/releases/tag/v14.21.4 , MongoDB 6.x
|
||||
- CPU: amd64, arm64, s390x, Source Bundle at https://github.com/wekan/wekan/wiki/Raspberry-Pi
|
||||
- Windows On-Premise: https://github.com/wekan/wekan/wiki/Offline
|
||||
- Mac: Docker, or at https://github.com/wekan/wekan/wiki/Mac
|
||||
- CPU: amd64, arm64, s390x, Source Bundle at [Raspberry Pi](../Platforms/FOSS/RaspberryPi/Raspberry-Pi.md)
|
||||
- Windows On-Premise: [Offline](../Platforms/Propietary/Windows/Offline.md)
|
||||
- Mac: Docker, or at [Mac](../Platforms/Propietary/Mac.md)
|
||||
- Newest at platforms: Source Bundle, Snap Candidate, Docker, Kubernetes https://wekan.fi
|
||||
- Platforms that will be updated to be up-to-date: Snap Stable, Sandstorm, UCS https://wekan.fi
|
||||
|
||||
## Meteor WeKan Features
|
||||
|
||||
- [WeKan Design Principles](https://github.com/wekan/wekan/wiki/Design-Principles)
|
||||
- [WeKan Design Principles](Design-Principles.md)
|
||||
- Realtime UI. When someone makes change, like drag card or add text, everyone will see changes immediately. This is because Meteor listens to MongoDB change stream, and updates UI. For some rare cases, there needs to be reload of page, those need to be fixed.
|
||||
- Whitelabel: Change product name and product image at login screen and board left top corner. Not changing favicon, because there are 100+ favicons for different devices.
|
||||
- Drag Drop at Desktop and Mobile:
|
||||
- Board Icons, Swimlanes, Lists, Cards, Checklists, Checklist Items
|
||||
- Drag handles, per screen https://github.com/wekan/wekan/wiki/Drag-Drop
|
||||
- Drag handles, per screen [Drag Drop](../DragDrop/Drag-Drop.md)
|
||||
- Some features similar to Trello and Jira. Some features not in Trello and Jira.
|
||||
- IFTTT Rules like Trello Butler https://github.com/wekan/wekan/wiki/IFTTT , translated to all supported languages. Trello Butler is only In English.
|
||||
- IFTTT Rules like Trello Butler [IFTTT](../Features/IFTTT/IFTTT.md) , translated to all supported languages. Trello Butler is only In English.
|
||||
- Many Assignees like in Jira.
|
||||
- Change background image and Card Cover. Change Theme. Change color of Boards, Swimlane, List, Card, Label.
|
||||
- Markdown at: Board name and description, Swimlane, List, Card title and description, Checklist and Checklist Item, Subtask title, https://github.com/wekan/wekan/wiki/Wekan-Markdown
|
||||
- Emoji https://github.com/wekan/wekan/wiki/Emoji
|
||||
- Markdown at: Board name and description, Swimlane, List, Card title and description, Checklist and Checklist Item, Subtask title, [Wekan Markdown](../Features/Markdown/Markdown.md)
|
||||
- Emoji [Emoji](../Features/Emoji.md)
|
||||
- MathML
|
||||
- Preview card attachment image and PDF
|
||||
- Show Start at End Dates of card at Calendar of Swimlanes/Lists/Calendar dropdown menu
|
||||
- Custom Fields https://github.com/wekan/wekan/wiki/Custom-Fields
|
||||
- Subtasks https://github.com/wekan/wekan/wiki/Subtasks
|
||||
- Planning Poker / Scrum Poker at Card https://github.com/wekan/wekan/wiki/Planning-Poker . No other card games yet ;)
|
||||
- Custom Fields [Custom Fields](../API/Custom-Fields.md)
|
||||
- Subtasks [Subtasks](../Features/Subtasks.md)
|
||||
- Planning Poker / Scrum Poker at Card [Planning Poker](../Features/Planning-Poker.md) . No other card games yet ;)
|
||||
- For board actions, like move card etc, Per Board and Global Outgoing and 2-way webhooks to Chat (RocketChat, Slack, Discord etc), NodeRED, or PHP webhook receiver like https://github.com/wekan/webhook
|
||||
- At top right username menu:
|
||||
- Search All Boards, with search options
|
||||
@@ -69,10 +69,10 @@ https://www.youtube.com/watch?v=ke-mbnZM3zE
|
||||
- Filter cards, by user etc
|
||||
- Multi-Selection, checkmark and drag many cards at once
|
||||
- Sort current board
|
||||
- Python and PHP based additional features https://github.com/wekan/wekan/wiki/Python
|
||||
- Python and PHP based additional features [Python](../Features/Python.md)
|
||||
- Admin Panel:
|
||||
- Organizations/Teams/People. Impersonate User, logged to database when someone uses that feature.
|
||||
- Customize Translations https://github.com/wekan/wekan/wiki/Customize-Translations
|
||||
- Customize Translations [Customize Translations](../Translations/Customize-Translations.md)
|
||||
|
||||
## Technical details
|
||||
|
||||
@@ -82,38 +82,38 @@ https://www.youtube.com/watch?v=ke-mbnZM3zE
|
||||
- Big performance improvements https://github.com/wekan/wekan/issues/5000
|
||||
- Using same style of API for client and serverside
|
||||
- Use more ReactiveCache than MiniMongo
|
||||
- Optimizing and Debugging Meteor, reducing size, links to WeKan dependencies https://github.com/wekan/wekan/wiki/Debugging
|
||||
- Optimizing and Debugging Meteor, reducing size, links to WeKan dependencies [Debugging](../DeveloperDocs/Debugging.md)
|
||||
- Custom OIDC/OAuth2 login
|
||||
- Repo https://github.com/wekan/wekan/tree/main/packages/wekan-oidc
|
||||
- Repo [wekan-oidc](../../packages/wekan-oidc)
|
||||
- Features:
|
||||
- At Admin Panel, you can change text of login page button `Login with OIDC`
|
||||
- Auth0 https://github.com/wekan/wekan/wiki/OAuth2#auth0 . This did take about half year to implement by xet7, was not available anywhere else for Meteor.
|
||||
- Oracle OIM. https://github.com/wekan/wekan/wiki/Oracle-OIM . For this, customer provided some code. xet7 added fix to make login work even when some login field was missing data, by taking data from other field, all this while xet7 did not have access to customers Oracle OIM, just guessing about what code to add, and it worked.
|
||||
- GitLab https://github.com/wekan/wekan/wiki/OAuth2#gitlab-providing-oauth2-login-to-wekan
|
||||
- RocketChat https://github.com/wekan/wekan/wiki/OAuth2#rocketchat-providing-oauth2-login-to-wekan
|
||||
- Nextcloud https://github.com/wekan/wekan/wiki/Nextcloud
|
||||
- ADFS 4.0 using OAuth2 and OpenID https://github.com/wekan/wekan/wiki/ADFS
|
||||
- Azure https://github.com/wekan/wekan/wiki/Azure
|
||||
- Keycloak https://github.com/wekan/wekan/wiki/Keycloak
|
||||
- Google login https://github.com/wekan/wekan/wiki/Google-login
|
||||
- Auth0 [OAuth2](../Login/OAuth2.md#auth0) . This did take about half year to implement by xet7, was not available anywhere else for Meteor.
|
||||
- Oracle OIM. [Oracle OIM](../Login/Oracle-OIM.md) . For this, customer provided some code. xet7 added fix to make login work even when some login field was missing data, by taking data from other field, all this while xet7 did not have access to customers Oracle OIM, just guessing about what code to add, and it worked.
|
||||
- GitLab [OAuth2](../Login/OAuth2.md#gitlab-providing-oauth2-login-to-wekan)
|
||||
- RocketChat [OAuth2](../Login/OAuth2.md#rocketchat-providing-oauth2-login-to-wekan)
|
||||
- Nextcloud [Nextcloud](../Login/Nextcloud.md)
|
||||
- ADFS 4.0 using OAuth2 and OpenID [ADFS](../Login/ADFS.md)
|
||||
- Azure [Azure](../Login/Azure/Azure.md)
|
||||
- Keycloak [Keycloak](../Login/Keycloak/Keycloak.md)
|
||||
- Google login [Google login](../Login/Google-login.md)
|
||||
- Custom LDAP
|
||||
- LDAP https://github.com/wekan/wekan/wiki/LDAP
|
||||
- LDAP AD Simple Auth https://github.com/wekan/wekan/wiki/LDAP-AD-Simple-Auth
|
||||
- LDAP [LDAP](../Login/LDAP.md)
|
||||
- LDAP AD Simple Auth [LDAP AD Simple Auth](../Login/LDAP-AD-Simple-Auth.md)
|
||||
- Default board for users https://github.com/wekan/wekan/pull/5098
|
||||
- Brute Force Accounts Lockout https://github.com/wekan/wekan/wiki/Accounts-Lockout
|
||||
- Markdown, Emoji, MathML, sanitizing https://github.com/wekan/wekan/blob/main/packages/markdown/src/template-integration.js
|
||||
- Brute Force Accounts Lockout [Accounts Lockout](../Login/Accounts-Lockout.md)
|
||||
- Markdown, Emoji, MathML, sanitizing [template-integration.js](../../packages/markdown/src/template-integration.js)
|
||||
- Many security fixes from Responsible Disclosure https://wekan.fi/hall-of-fame/
|
||||
|
||||
## Upcoming Features
|
||||
|
||||
- CAS https://github.com/wekan/wekan/wiki/CAS
|
||||
- SAML https://github.com/wekan/wekan/wiki/SAML
|
||||
- CAS [CAS](../Login/CAS.md)
|
||||
- SAML [SAML](../Login/SAML.md)
|
||||
|
||||
## Video
|
||||
|
||||
- Creator of WeKan, mquandalle (Maxime Quandalle) https://www.youtube.com/watch?v=N3iMLwCNOro
|
||||
- https://github.com/wekan/wekan/wiki/FAQ#what-was-wekan-fork--wefork
|
||||
- Maintainer of WeKan since December 2016, xet7 (Lauri Ojansivu), about WeKan Multiverse at EU NGI Dapsi https://www.youtube.com/watch?v=BPPjiZHVeyM . More about Multiverse at https://github.com/wekan/wekan/wiki/WeKan-Multiverse-Roadmap
|
||||
- [FAQ](../FAQ/FAQ.md#what-was-wekan-fork--wefork)
|
||||
- Maintainer of WeKan since December 2016, xet7 (Lauri Ojansivu), about WeKan Multiverse at EU NGI Dapsi https://www.youtube.com/watch?v=BPPjiZHVeyM . More about Multiverse at [WeKan Multiverse Roadmap](Multiverse/WeKan-Multiverse-Roadmap.md)
|
||||
|
||||
## Maybe?
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
- Original WeKan Design:
|
||||
- Uses WeKan style confirm popups `Popup.afterConfirm(` , translations etc. Please look at existing code to keep same style.
|
||||
- Kanban, the good parts. Like was done when figuring out best design for Swimlanes https://github.com/wekan/wekan/issues/955
|
||||
- Intuitive, easy to figure out, prevent messing up something https://github.com/wekan/wekan/wiki/Monkey-Proof-Software
|
||||
- Intuitive, easy to figure out, prevent messing up something [Monkey Proof Software](Monkey-Proof-Software.md)
|
||||
- Polished UI/UX
|
||||
- Menu options are there where you would expect then to be.
|
||||
- Try to fix anything unexpected.
|
||||
@@ -14,24 +14,24 @@
|
||||
- Swimlanes
|
||||
- All the use cases of WeKan feature/fix contributors
|
||||
- Cross-platform. Support many CPU/OS/Browsers for Desktop and Mobile.
|
||||
- Support many browsers https://github.com/wekan/wekan/wiki/Browser-compatibility-matrix . Add support for more.
|
||||
- PWA https://github.com/wekan/wekan/wiki/PWA
|
||||
- Support many browsers [Browser compatibility matrix](../Browsers/Browser-compatibility-matrix.md) . Add support for more.
|
||||
- PWA [PWA](../Browsers/PWA.md)
|
||||
- On-Premise:
|
||||
- Linux amd64/arm64/s390x
|
||||
- https://wekan.fi/install/
|
||||
- https://github.com/wekan/wekan/wiki/Raspberry-Pi
|
||||
- Windows https://github.com/wekan/wekan/wiki/Offline
|
||||
- Mac https://github.com/wekan/wekan/wiki/Mac
|
||||
- [Raspberry Pi](../Platforms/FOSS/RaspberryPi/Raspberry-Pi.md)
|
||||
- Windows [Offline](../Platforms/Propietary/Windows/Offline.md)
|
||||
- Mac [Mac](../Platforms/Propietary/Mac.md)
|
||||
- Not like other software:
|
||||
- But note that design of all software changes often, when they are in active development.
|
||||
- Not bad parts of Trello:
|
||||
- Not the exact design of Trello https://github.com/wekan/wekan/wiki/FAQ#why-does-wekan-look-so-different-now-compared-to--v09
|
||||
- Not the exact design of Trello [FAQ](../FAQ/FAQ.md#why-does-wekan-look-so-different-now-compared-to--v09)
|
||||
- Not so many duplicate menus and texts like Trello
|
||||
- Not so much empty space
|
||||
- Some good parts of Trello:
|
||||
- Import from Trello
|
||||
- Shortcuts https://github.com/wekan/wekan/issues/1878
|
||||
- IFTTT Rules like Trello Butler https://github.com/wekan/wekan/wiki/IFTTT
|
||||
- IFTTT Rules like Trello Butler [IFTTT](../Features/IFTTT/IFTTT.md)
|
||||
- Progress bar of checklist
|
||||
- Some good parts of Jira:
|
||||
- Multiple assignees
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
Existing WeKan features
|
||||
|
||||
- 2025-04 Opened card has comments at right, copied from [original WeKan feature Maximize Card 2021-06-14](https://github.com/wekan/wekan/blob/main/CHANGELOG.md#v535-2021-06-14-wekan-release). Trello does not have WeKan feature Minimize Card.
|
||||
- 2025-04 Opened card has comments at right, copied from [original WeKan feature Maximize Card 2021-06-14](../../CHANGELOG.md#v535-2021-06-14-wekan-release). Trello does not have WeKan feature Minimize Card.
|
||||
- https://community.atlassian.com/forums/Trello-questions/comments-in-the-ticket-on-the-right/qaq-p/3029030
|
||||
- https://community.atlassian.com/forums/Trello-questions/Trello-card-comments-now-appear-in-a-side-tab-how-to-go-back-to/qaq-p/3003380
|
||||
- 2025-02 Mirror Card, copied from [original WeKan feature Linked Cards from 2018-04-18](https://github.com/wekan/wekan/pull/1592), discussed at [WeKan issue 5683](https://github.com/wekan/wekan/issues/5683)
|
||||
|
||||
@@ -57,7 +57,7 @@ Custom CSS and Javascript is very dangerous: https://github.com/wekan/wekan/issu
|
||||
|
||||
> I had to previously find a way [how to fix Custom Javascript in RocketChat](https://forums.rocket.chat/t/big-issue-with-custom-javascript/261/4?u=xet7) because it broke my RocketChat install.
|
||||
>
|
||||
> Also, previously I had to [fix XSS bug](https://github.com/wekan/wekan/blob/main/CHANGELOG.md#v385-2020-03-23-wekan-release) because adding Javascript to input fields did run that Javascript code.
|
||||
> Also, previously I had to [fix XSS bug](../../CHANGELOG.md#v385-2020-03-23-wekan-release) because adding Javascript to input fields did run that Javascript code.
|
||||
>
|
||||
> I'll try to find is there a safe way to do this.
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
## About AVX
|
||||
|
||||
If CPU does not have AVX, Snap Candidate Meteor 2 WeKan now uses qemu-user to run MongoDB, using Qemu AVX support.
|
||||
This was added at [WeKan v7.93](https://github.com/wekan/wekan/blob/main/CHANGELOG.md#v793-2025-07-18-wekan--release).
|
||||
This was added at [WeKan v7.93](../../../CHANGELOG.md#v793-2025-07-18-wekan--release).
|
||||
|
||||
## What is not Cross-Platform
|
||||
|
||||
@@ -46,7 +46,7 @@ This was added at [WeKan v7.93](https://github.com/wekan/wekan/blob/main/CHANGEL
|
||||
|
||||
## Future
|
||||
|
||||
- Will this happen? With what tech? See https://github.com/wekan/wekan/blob/main/FUTURE.md
|
||||
- Will this happen? With what tech? See [FUTURE.md](../../../FUTURE.md)
|
||||
- You can comment at https://github.com/wekan/wekan/issues/4578
|
||||
- You can help by sending PR to any prototype repo mentioned below
|
||||
|
||||
@@ -249,7 +249,7 @@ https://github.com/wekan/php/blob/main/page/allboardschar.php
|
||||
- Redbean amd64 binary works at Win/Mac/Linux/BSD/BIOS without any modifications, because it is based on Cosmopolitan https://github.com/jart/cosmopolitan
|
||||
- Uses HTMX https://htmx.org at UI
|
||||
- Saves to SQLite database
|
||||
- Petclinic fork, using Blink to run at s390x https://github.com/wekan/wekan/wiki/s390x#petclinic-s390x
|
||||
- Petclinic fork, using Blink to run at s390x [s390x](../../Platforms/FOSS/s390x.md#petclinic-s390x)
|
||||
|
||||
### PHP
|
||||
|
||||
@@ -290,7 +290,7 @@ https://github.com/wekan/php/blob/main/page/allboardschar.php
|
||||
- Deno also includes Node.js as Deno's Node.js compatibility layer.
|
||||
- Node.js supports many CPU/OS
|
||||
- Node.js is used in production and has traceability. https://changelog.com/jsparty/294
|
||||
- https://github.com/wekan/wekan/wiki/s390x
|
||||
- [s390x](../../Platforms/FOSS/s390x.md)
|
||||
- https://nodejs.org/dist/latest-v20.x/
|
||||
- https://github.com/wekan/node-v14-esm/releases/tag/v14.21.4
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
MIT:
|
||||
|
||||
- WeKan (MIT, Node.js/Meteor/MongoDB) https://github.com/wekan/wekan is maintained, major features being added, see https://github.com/wekan/wekan/wiki/Deep-Dive-Into-WeKan
|
||||
- WeKan (MIT, Node.js/Meteor/MongoDB) https://github.com/wekan/wekan is maintained, major features being added, see [Deep Dive Into WeKan](Deep-Dive-Into-WeKan.md)
|
||||
- Kanboard (MIT, PHP) https://github.com/kanboard/kanboard is at maintenance mode, no major new feature development. There is still security fixes etc.
|
||||
- 4gaBoards (MIT, Node.js/Sails.js/PostgreSQL) https://github.com/RARgames/4gaBoards is maintained fork of Planka
|
||||
- Godello (MIT, Godot) https://github.com/alfredbaudisch/Godello can save locally, maybe no multi-user support yet
|
||||
@@ -29,9 +29,9 @@ Other kanbans maybe listed at https://github.com/KanRule
|
||||
- Atlassian is retiring self-managed server offerings https://www.atlassian.com/blog/platform/atlassian-server-is-going-away-next-steps
|
||||
|
||||
|
||||
## Please [search from ChangeLog page](https://github.com/wekan/wekan/blob/main/CHANGELOG.md) instead
|
||||
## Please [search from ChangeLog page](../../CHANGELOG.md) instead
|
||||
|
||||
Please [search from ChangeLog page](https://github.com/wekan/wekan/blob/main/CHANGELOG.md) instead about does Wekan have some feature.
|
||||
Please [search from ChangeLog page](../../CHANGELOG.md) instead about does Wekan have some feature.
|
||||
|
||||
This comparison below is over one year old and very outdated. All of Wekan/Trello/Restyaboard have changed very much and have many new features and fixes.
|
||||
|
||||
@@ -39,11 +39,11 @@ This comparison below is over one year old and very outdated. All of Wekan/Trell
|
||||
|
||||
Features | Wekan | Trello | Restyaboard
|
||||
------------ | ------------- | ------------- | -------------
|
||||
Price | Free and Open Source, [MIT license](https://github.com/wekan/wekan/blob/main/LICENSE). Free for Commercial Use. | Free with limitations, Monthly payment, Annual Subscription, Quote-based | Open Core
|
||||
Price | Free and Open Source, [MIT license](../../LICENSE). Free for Commercial Use. | Free with limitations, Monthly payment, Annual Subscription, Quote-based | Open Core
|
||||
Whitelabeling | Yes. Admin Panel/Layout: Hide Logo, Custom Product Name. | No | $ Yes
|
||||
Theming | [Yes](Custom-CSS-themes) | No | $ Yes
|
||||
Theming | [Yes](../Theme/Custom-CSS-themes.md) | No | $ Yes
|
||||
Redistributing | Yes | No | $ Yes
|
||||
Hosting | [Self-host or SaaS provider](Platforms) | SaaS | Self-host
|
||||
Hosting | [Self-host or SaaS provider](../Platforms/FOSS/Platforms.md) | SaaS | Self-host
|
||||
|
||||
## Basic features: Board
|
||||
|
||||
@@ -56,7 +56,7 @@ Boards | Yes | Yes | Yes
|
||||
Closed boards listing | Yes, at Archive | Yes | Yes
|
||||
Starred boards listing | No, starred and non-starred at All Boards | No | Yes
|
||||
Add board with predefined templates | Personal templates, Import Board from Trello or Wekan | No | Yes
|
||||
Board stats | [Yes](Features#stats) | No | Yes
|
||||
Board stats | [Yes](../Features/Features.md#stats) | No | Yes
|
||||
Board - Add members | Yes | Yes | Yes
|
||||
Board - Remove members | Yes | Yes | Yes
|
||||
Close board | Yes, move to Archive | Yes | Yes
|
||||
@@ -189,12 +189,12 @@ Undo from activities | No | No | Yes
|
||||
Features | Wekan | Trello | Restyaboard
|
||||
------------ | ------------- | ------------- | -------------
|
||||
API explorer | No | No | Yes
|
||||
OpenAPI | [Yes](https://github.com/wekan/wekan/tree/main/openapi) [here](https://wekan.fi/api/) | ? | ?
|
||||
OpenAPI | [Yes](../../openapi) [here](https://wekan.fi/api/) | ? | ?
|
||||
Developer applications | Yes, using REST API | Yes | Yes
|
||||
Authorized OAuth applications | No, REST API [login as admin to get Bearer token](REST-API#example-call---as-form-data) | Yes | Yes
|
||||
Authorized OAuth applications | No, REST API [login as admin to get Bearer token](../API/REST-API.md#example-call---as-form-data) | Yes | Yes
|
||||
Webhooks | Yes, per board or global at Admin Panel | Yes | Yes
|
||||
Zapier (IFTTT like workflow automation with 500+ websites) | Yes | Yes | $ Yes
|
||||
Integrated IFTTT | [Yes](IFTTT) | No | No
|
||||
Integrated IFTTT | [Yes](../Features/IFTTT/IFTTT.md) | No | No
|
||||
|
||||
## Basic features: Email and Notifications
|
||||
|
||||
@@ -215,7 +215,7 @@ Features | Wekan | Trello | Restyaboard
|
||||
Additional settings | No in Web UI, most settings at CLI | Yes | Yes (Basic only)
|
||||
Profile | Yes | Yes | Yes
|
||||
Add a new email address | Register / Invite | Yes | No
|
||||
Roles management | On web board+[API](REST-API-Role) | No | Yes
|
||||
Roles management | On web board+[API](../API/Role.md) | No | Yes
|
||||
Settings management | Only some at Web UI, most settings at CLI | No | Yes
|
||||
Users management | Add/Remove Orgs/Teams/Users | Yes? | Yes
|
||||
Permanently delete your entire account forever? | No | Yes | Yes (Admin can delete)
|
||||
@@ -225,10 +225,10 @@ Permanently delete your entire account forever? | No | Yes | Yes (Admin can dele
|
||||
Features | Wekan | Trello | Restyaboard
|
||||
------------ | ------------- | ------------- | -------------
|
||||
Login with username or email | Yes | Yes | Yes
|
||||
LDAP login | Standalone: [Yes](LDAP). Sandstorm: Yes. | No | $ Yes
|
||||
LDAP login | Standalone: [Yes](../Login/LDAP.md). Sandstorm: Yes. | No | $ Yes
|
||||
SAML login | Standalone: [Not yet](https://github.com/wekan/wekan/issues/708). Sandstorm: Yes | No | No
|
||||
Google login | Not yet, needs fixes to [OAuth2](OAuth2) | Yes | No
|
||||
GitHub login | Standalone: Not yet, needs fixes to [OAuth2](OAuth2). Sandstorm: Yes. | No | No
|
||||
Google login | Not yet, needs fixes to [OAuth2](../Login/OAuth2.md) | Yes | No
|
||||
GitHub login | Standalone: Not yet, needs fixes to [OAuth2](../Login/OAuth2.md). Sandstorm: Yes. | No | No
|
||||
Passwordless email login | Standalone: No. Sandstorm: Yes. | No | No
|
||||
|
||||
## Apps for productivity: Import / Export
|
||||
@@ -257,7 +257,7 @@ Show attachments in board | No, only at each card | No | Yes
|
||||
Features | Wekan | Trello | Restyaboard
|
||||
------------ | ------------- | ------------- | -------------
|
||||
Apps (Power-ups) | All integrated in, no separate plugin/app install | Yes | Yes
|
||||
Custom Field App | [Yes](Custom-Fields) | Yes | $ Yes
|
||||
Custom Field App | [Yes](../API/Custom-Fields.md) | Yes | $ Yes
|
||||
Estimated Time Custom Field App / Time Tracking | Reveived/Start/End/Due/End Date, Spent time, Requested By, Assigned By. No reports. For more advanced, [Not yet](https://github.com/wekan/wekan/issues/812) | Yes, 3rd party Burndown, Harvest etc | $ Yes
|
||||
Analytics | [Magento integration](https://github.com/wekan/wekan-snap/wiki/Supported-settings-keys) | No | No
|
||||
Hide card additional information | Yes, card and board activities | Yes, card activities | Yes, card activities
|
||||
@@ -270,7 +270,7 @@ Introduction video | No | No | Yes
|
||||
List sorting by due date | No | No | Yes
|
||||
Home screen | No | No | Yes
|
||||
Apps Integration | All integrated in | Yes | Yes
|
||||
Chat | No. You could use [Rocket.Chat](OAuth2) | No | $ Yes
|
||||
Chat | No. You could use [Rocket.Chat](../Login/OAuth2.md) | No | $ Yes
|
||||
Dashboard Charts | [Not yet](https://github.com/wekan/wekan-dashing-go) | No | $ Yes
|
||||
Hide Card Created Date App | No | No | Yes
|
||||
Hide Card ID App | No | No | Yes
|
||||
@@ -278,9 +278,9 @@ Canned Response App | No | No | $ Yes
|
||||
Auto Archive Expired Cards App | No | No | $ Yes
|
||||
Support Desk | No | No | $ Yes
|
||||
Card Template App | Copy Checklist Template to Multiple Cards | Yes | $ Yes
|
||||
Slack | [Yes](Outgoing-Webhook-to-Discord) | Yes | $ Yes
|
||||
Slack | [Yes](../Webhooks/Discord/Outgoing-Webhook-to-Discord.md) | Yes | $ Yes
|
||||
Amazon Echo | No | No | $ Yes
|
||||
Collaborate/TogetherJS | [Not yet](Friend) | No | Yes
|
||||
Collaborate/TogetherJS | [Not yet](../Platforms/FOSS/Friend/Friend.md) | No | Yes
|
||||
Gmail Add-on | No | Yes | Yes
|
||||
Hangouts Chat bot | No | Yes | $ Yes
|
||||
Print board | No | No | $ Yes
|
||||
@@ -296,8 +296,8 @@ SEO Checklist | No | No | $ Yes
|
||||
|
||||
Features | Wekan | Trello | Restyaboard
|
||||
------------ | ------------- | ------------- | -------------
|
||||
iOS Mobile App | [Not yet](Friend). Mobile Chrome browser works. | Yes | Yes
|
||||
Android Mobile App | [Yes](Browser-compatibility-matrix) | Yes | Yes
|
||||
Windows Microsoft Store App | [Yes](Browser-compatibility-matrix) | ? | ?
|
||||
Ubuntu Touch OpenStore App | [Yes](Browser-compatibility-matrix) | ? | ?
|
||||
iOS Mobile App | [Not yet](../Platforms/FOSS/Friend/Friend.md). Mobile Chrome browser works. | Yes | Yes
|
||||
Android Mobile App | [Yes](../Browsers/Browser-compatibility-matrix.md) | Yes | Yes
|
||||
Windows Microsoft Store App | [Yes](../Browsers/Browser-compatibility-matrix.md) | ? | ?
|
||||
Ubuntu Touch OpenStore App | [Yes](../Browsers/Browser-compatibility-matrix.md) | ? | ?
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
Add all necessary settings that you find on code.
|
||||
|
||||
After doing all changes, [fix lint errors](Developer-Documentation#preventing-travis-ci-lint-errors-before-submitting-pull-requests) and if possible warnings too. You can silence errors if you really can't find solution.
|
||||
After doing all changes, [fix lint errors](Developer-Documentation.md#preventing-travis-ci-lint-errors-before-submitting-pull-requests) and if possible warnings too. You can silence errors if you really can't find solution.
|
||||
|
||||
Submit pull request to Wekan edge branch https://github.com/wekan/wekan/tree/edge
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ That does:
|
||||
- 2 builds wekan
|
||||
- 3 starts wekan in development mode with command `meteor`, so it can detect if some file changes and try to rebuild automatically.
|
||||
|
||||
And then [register and login](Adding-users) with webbrowser at http://localhost:4000
|
||||
And then [register and login](../Login/Adding-users.md) with webbrowser at http://localhost:4000
|
||||
|
||||
### 4. Make and test your changes
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ cd wekan
|
||||
```
|
||||
That does: 1 install dependencies, 2 builds wekan, 3 starts wekan in development mode with command `meteor`, so it can detect if some file changes and try to rebuild automatically and reload webbroser. But, still sometimes, it may need stopping with Ctrl-c and full build with option 2.
|
||||
|
||||
And then [register and login](Adding-users) at http://localhost:4000
|
||||
And then [register and login](../Login/Adding-users.md) at http://localhost:4000
|
||||
|
||||
### OPTIONAL, NOT NEEDED: 5. Add new plugin package
|
||||
```
|
||||
|
||||
@@ -9,19 +9,19 @@ This can happen, when there is too much or incompatible code at browserside, for
|
||||
To fix it:
|
||||
|
||||
1. Move ExcelJS from browserside to run at serverside https://github.com/wekan/wekan/pull/3871
|
||||
2. Use Bundle Visualizer to see what is the size of dependencies, and try what can be moved to serverside like at step 1, that bundle visualizer is used in this script https://github.com/wekan/wekan/blob/main/rebuild-wekan.sh
|
||||
2. Use Bundle Visualizer to see what is the size of dependencies, and try what can be moved to serverside like at step 1, that bundle visualizer is used in this script [rebuild-wekan.sh](../../rebuild-wekan.sh)
|
||||
```
|
||||
meteor run --exclude-archs web.browser.legacy,web.cordova --port 4000 --extra-packages bundle-visualizer --production 2>&1 | tee ../log.txt
|
||||
```
|
||||
3. Make dependencies smaller. For example, use only required files, and do not include all dependencies: https://github.com/wekan/wekan/commit/23e5e1e3bd081699ce39ce5887db7e612616014d . In that commit, package was forked to packages directory, then renamed, and added with `meteor add packagename`, where package name does not have character `:`
|
||||
4. Use Browserstack.com to see errors at browser / inspect / console, or use iOS or other device emulators, to see error messages. Testing at real device is more important, because they could work differently than emulators, emulators sometimes do not emulate all same features. Those error messages have file where error happened, and line number, like `something.js:301` . From there, scroll up a little, look at what function or what package dependency it is where it happened. If possible, try to move that package serverside, like at step 1. Or alternatively, look is it possible to remove or change to some other compatible dependency.
|
||||
5. See what are the dependencies at your Meteor based software, compared to WeKan dependencies that are usually already upgraded to newest Meteor, is there any differences where changing to correct dependencies could help you to upgrade to newest Meteor:
|
||||
- https://github.com/wekan/wekan/blob/main/package.json
|
||||
- https://github.com/wekan/wekan/blob/main/.meteor/packages
|
||||
- https://github.com/wekan/wekan/blob/main/.meteor/versions
|
||||
- https://github.com/wekan/wekan/blob/main/.meteor/release
|
||||
- [package.json](../../package.json)
|
||||
- [packages](../../.meteor/packages)
|
||||
- [versions](../../.meteor/versions)
|
||||
- [release](../../.meteor/release)
|
||||
5. If you get some errors, search are those same already fixed in WeKan/Meteor/RocketChat, could you fix them same way:
|
||||
- https://github.com/wekan/wekan/blob/main/CHANGELOG.md
|
||||
- [CHANGELOG.md](../../CHANGELOG.md)
|
||||
- https://github.com/wekan/wekan/issues
|
||||
- https://github.com/wekan/wekan/issues?q=is%3Aissue+is%3Aclosed
|
||||
- https://github.com/meteor/meteor/issues
|
||||
@@ -29,12 +29,12 @@ meteor run --exclude-archs web.browser.legacy,web.cordova --port 4000 --extra-pa
|
||||
- https://github.com/RocketChat/Rocket.Chat/issues
|
||||
- https://github.com/RocketChat/Rocket.Chat/issues?q=is%3Aissue+is%3Aclosed
|
||||
6. If you have some webserver providing SSL/TLS, check that you have websockets enabled:
|
||||
- https://github.com/wekan/wekan/wiki/Caddy-Webserver-Config
|
||||
- https://github.com/wekan/wekan/wiki/Nginx-Webserver-Config
|
||||
- https://github.com/wekan/wekan/wiki/Apache
|
||||
- [Caddy Webserver Config](../Webserver/Caddy.md)
|
||||
- [Nginx Webserver Config](../Webserver/Nginx.md)
|
||||
- [Apache](../Webserver/Apache.md)
|
||||
- OpenLiteSpeed https://github.com/wekan/wekan/issues/3334#issuecomment-723651328
|
||||
- https://github.com/wekan/wekan/wiki/Local-self-signed-TLS
|
||||
- https://github.com/wekan/wekan/wiki/Traefik-and-self-signed-SSL-certs
|
||||
- [Local self signed TLS](../Webserver/Local-self-signed-TLS.md)
|
||||
- [Traefik and self signed SSL certs](../Webserver/Traefik-and-self-signed-SSL-certs.md)
|
||||
|
||||
## OLD: TODO
|
||||
|
||||
@@ -70,23 +70,23 @@ meteor run --exclude-archs web.browser.legacy,web.cordova --port 4000 --extra-pa
|
||||
|
||||
1) Increase ulimit system wide to 100 000 in systemd config.
|
||||
|
||||
2) Wekan Javascript code has [increaded fiber poolsize](https://github.com/wekan/wekan/blob/main/server/authentication.js#L5-L9).
|
||||
2) Wekan Javascript code has [increaded fiber poolsize](../../server/authentication.js#L5-L9).
|
||||
|
||||
3) There is [on-going 100% CPU usage Meteor issue](https://github.com/meteor/meteor/issues/9796#issuecomment-400079380) and hopefully [fixes to Node.js will land in Node v8.12](https://github.com/nodejs/node/pull/21593#issuecomment-403636667) sometime. Node 8.12 is now released and official version included at Wekan.
|
||||
|
||||
## Scaling to thousands of users
|
||||
|
||||
[Production setup at AWS](AWS)
|
||||
[Production setup at AWS](../Platforms/Propietary/Cloud/AWS.md)
|
||||
|
||||
## Current versions of dependencies
|
||||
|
||||
[Dockerfile](https://github.com/wekan/wekan/blob/main/Dockerfile), versions of Meteor.js, Node etc listed at beginning.
|
||||
[Dockerfile](../../Dockerfile), versions of Meteor.js, Node etc listed at beginning.
|
||||
|
||||
[Included Meteor packages](https://github.com/wekan/wekan/blob/main/.meteor/packages)
|
||||
[Included Meteor packages](../../.meteor/packages)
|
||||
|
||||
[Included Meteor package versions](https://github.com/wekan/wekan/blob/main/.meteor/versions)
|
||||
[Included Meteor package versions](../../.meteor/versions)
|
||||
|
||||
[Added packages at package.json](https://github.com/wekan/wekan/blob/main/package.json)
|
||||
[Added packages at package.json](../../package.json)
|
||||
|
||||
## Build from source
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ Please read the meteor style guide before making any significant contribution.
|
||||
|
||||
## Building code and submitting pull request
|
||||
|
||||
- [Building code and submitting pull request](emoji#how-you-could-add-another-plugin)
|
||||
- [Building code and submitting pull request](../Features/Emoji.md#how-you-could-add-another-plugin)
|
||||
- When you start `meteor` command, it watches for changes to files in wekan directory and subdirectories, and if it detects changed code, it starts rebuilding bundle and then reloads webbrowser after that automatically
|
||||
- Also look at meteor changelog mentioned new feature `hot reload` how that could make reloading faster
|
||||
|
||||
@@ -43,10 +43,10 @@ When you run this, you see .jade template files where is search input box, and .
|
||||
|
||||
## Getting Started
|
||||
|
||||
- Currently Wekan development is done mainly at Ubuntu 20.10 64bit, but building Wekan does work on any Debian, Ubuntu, WSL Ubuntu 20.04, [Mac](Mac). Sametime maybe on [Windows](Windows) with `choco install -y meteor` and installing packages it then asks for with `meteor add ...` or `meteor npm install --save ...` and then running meteor again.
|
||||
- Currently Wekan development is done mainly at Ubuntu 20.10 64bit, but building Wekan does work on any Debian, Ubuntu, WSL Ubuntu 20.04, [Mac](../Platforms/Propietary/Mac.md). Sametime maybe on [Windows](../Platforms/Propietary/Windows/Windows.md) with `choco install -y meteor` and installing packages it then asks for with `meteor add ...` or `meteor npm install --save ...` and then running meteor again.
|
||||
- Look through old [pull requests](https://github.com/wekan/wekan/pulls)
|
||||
- Read Wekan source code, you can use some git history viewer GUI like gitk
|
||||
- Read [Meteor documentation](https://docs.meteor.com/) for [Meteor version](https://github.com/wekan/wekan/blob/main/.meteor/release) in use, other versions mentioned at [Dockerfile](https://github.com/wekan/wekan/blob/main/Dockerfile)
|
||||
- Read [Meteor documentation](https://docs.meteor.com/) for [Meteor version](../../.meteor/release) in use, other versions mentioned at [Dockerfile](../../Dockerfile)
|
||||
- Docker up-to-date way: You can clone wekan/wekan repo and update docker-compose.yml file ROOT_URL etc as documented at https://github.com/wekan/wekan commented `docker-compose.yml` file, install docker, and build from source with docker with `docker compose up -d --build`
|
||||
- Docker not up-to-date way: [Docker environment for Wekan development](https://github.com/wekan/wekan-dev).
|
||||
|
||||
@@ -95,7 +95,7 @@ It's possible to import your existing boards from Trello. Instructions [[here|mi
|
||||
|
||||
# Directory Structure Details
|
||||
|
||||
[Directory Structure](Directory-Structure)
|
||||
[Directory Structure](Directory-Structure.md)
|
||||
|
||||
# Chat
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Routing
|
||||
|
||||
We're using [FlowRouter](https://github.com/kadirahq/flow-router) client side router inside **[config/router.js](https://github.com/wekan/wekan/tree/main/config/router.js)**.
|
||||
For accounts there is [AccountsTemplates](https://github.com/meteor-useraccounts) configured in **[config/accounts.js](https://github.com/wekan/wekan/tree/main/config/accounts.js)**.
|
||||
We're using [FlowRouter](https://github.com/kadirahq/flow-router) client side router inside **[config/router.js](../../config/router.js)**.
|
||||
For accounts there is [AccountsTemplates](https://github.com/meteor-useraccounts) configured in **[config/accounts.js](../../config/accounts.js)**.
|
||||
|
||||
# Client
|
||||
|
||||
@@ -12,172 +12,172 @@ Files in this directory are served by meteor as-is to the client. It hosts some
|
||||
|
||||
## components
|
||||
|
||||
* **[activities](https://github.com/wekan/wekan/tree/main/client/components/activities)**:
|
||||
* **[activities.jade](https://github.com/wekan/wekan/tree/main/client/components/activities/activities.jade)**: `activities` template for the list of activities placed inside a `sidebar-content`; uses `boardActivities` or `cardActivities` depending on `mode`; <span style="color:red">*XXX: does this mean that sidebar should be visible in board list mode? when does the `board` activity gets shown?*</span>
|
||||
* **[comments.jade](https://github.com/wekan/wekan/tree/main/client/components/activities/comments.jade)**: `commentForm` template used in `card-details-canvas` for adding comments;
|
||||
* **[boards](https://github.com/wekan/wekan/tree/main/client/components/boards)**:
|
||||
* **[boardArchive.jade](https://github.com/wekan/wekan/tree/main/client/components/boards/boardArchive.jade)**: `archivedBoards` template for the modal dialog showing the list of archived boards that might be restored;
|
||||
* **[boardBody.jade](https://github.com/wekan/wekan/tree/main/client/components/boards/boardBody.jade)**: top level template for presenting a board is `board` and, based on screen size and current state, it uses either `cardDetails` or `boardBody` templates; `boardBody` is the one including the `sidebar`, each `list`, `cardDetails` for larger screens when a card is selected and the `addListForm` for adding a new list (also defined in this file);
|
||||
* **[boardHeader.jade](https://github.com/wekan/wekan/tree/main/client/components/boards/boardHeader.jade)**: `boardHeaderBar`, `boardMenuPopup`, `boardVisibilityList`, `boardChangeVisibilityPopup`, `boardChangeWatchPopup`, `boardChangeColorPopup`, `createBoard`, `chooseBoardSource`, `boardChangeTitlePopup`, `archiveBoardPopup`, `outgoingWebhooksPopup`;
|
||||
* **[boardsList.jade](https://github.com/wekan/wekan/tree/main/client/components/boards/boardsList.jade)**: `boardList` and `boardListHeaderBar` for the list of boards in the initial screen;
|
||||
* **[cards](https://github.com/wekan/wekan/tree/main/client/components/cards)**:
|
||||
* **[attachments.jade](https://github.com/wekan/wekan/tree/main/client/components/cards/attachments.jade)**: `cardAttachmentsPopup`, `previewClipboardImagePopup`, `previewAttachedImagePopup`, `attachmentDeletePopup`, `attachmentsGalery`;
|
||||
* **[cardDate.jade](https://github.com/wekan/wekan/tree/main/client/components/cards/cardDate.jade)**: `editCardDate` and `dateBadge` templates;
|
||||
* **[cardDetails.jade](https://github.com/wekan/wekan/tree/main/client/components/cards/cardDetails.jade)**: `boardsAndLists` is the usual layout for a board display with `boardLists` being used in sandstorm where each board is independent;`cardDetails`, `editCardTitleForm`, `cardDetailsActionsPopup`, `moveCardPopup`, `copyCardPopup`,`cardMembersPopup`,`cardMorePopup`, `cardDeletePopup`;
|
||||
* **[cardTime.jade](https://github.com/wekan/wekan/tree/main/client/components/cards/cardTime.jade)**: `editCardSpentTime` and `timeBadge` templates;
|
||||
* **[checklists.jade](https://github.com/wekan/wekan/tree/main/client/components/cards/checklists.jade)**: `checklists`, `checklistDetail`, `checklistDeleteDialog`, `addChecklistItemForm`, `editChecklistItemForm`, `checklistItems`, `itemDetail`;
|
||||
* **[labels.jade](https://github.com/wekan/wekan/tree/main/client/components/cards/labels.jade)**: `formLabel`, `createLabelPopup`, `editLabelPopup`, `deleteLabelPopup`, `cardLabelsPopup`;
|
||||
* **[minicard.jade](https://github.com/wekan/wekan/tree/main/client/components/cards/minicard.jade)**: has the `minicard` template
|
||||
* **[forms](https://github.com/wekan/wekan/tree/main/client/components/forms)**: **[inlinedform.jade](https://github.com/wekan/wekan/tree/main/client/components/forms/inlinedform.jade)** has the`inlinedForm` template;
|
||||
* **[import](https://github.com/wekan/wekan/tree/main/client/components/import)**:
|
||||
* **[import.jade](https://github.com/wekan/wekan/tree/main/client/components/import/import.jade)**: `importHeaderBar`, `import`, `importTextarea`, `importMapMembers`, `importMapMembersAddPopup` are all templates used for importing Trello (via **[trelloMembersMapper.js](https://github.com/wekan/wekan/tree/main/client/components/import/trelloMembersMapper.js)**) and Wekan (via **[wekanMembersMapper.js](https://github.com/wekan/wekan/tree/main/client/components/import/wekanMembersMapper.js)**) boards;
|
||||
* **[lists](https://github.com/wekan/wekan/tree/main/client/components/lists)**:
|
||||
* **[list.jade](https://github.com/wekan/wekan/tree/main/client/components/lists/list.jade)**: `list` is the simple, main template for lists;
|
||||
* **[listBody.jade](https://github.com/wekan/wekan/tree/main/client/components/lists/listBody.jade)**: `listBody`, `addCardForm`, `autocompleteLabelLine` templates;
|
||||
* **[listHeader.jade](https://github.com/wekan/wekan/tree/main/client/components/lists/listHeader.jade)**: `listHeader`, `editListTitleForm`, `listActionPopup`, `boardLists`, `listMorePopup`, `listDeletePopup`, `setWipLimitPopup`, `wipLimitErrorPopup` templates;
|
||||
* **[main](https://github.com/wekan/wekan/tree/main/client/components/main)**:
|
||||
* **[editor.jade](https://github.com/wekan/wekan/tree/main/client/components/main/editor.jade)**: `editor` and `viewer` templates;
|
||||
* **[header.jade](https://github.com/wekan/wekan/tree/main/client/components/main/header.jade)**: `header` and `offlineWarning` templates; if the user is connected we display a small "quick-access" top bar that list all starred boards with a link to go there (this is inspired by the Reddit "subreddit" bar); the first link goes to the boards page;
|
||||
* **[keyboardShortcuts.jade](https://github.com/wekan/wekan/tree/main/client/components/main/keyboardShortcuts.jade)**: `shortcutsHeaderBar`, `shortcutsModalTitle`, `keyboardShortcuts` - all for the shortcuts that are presented when you press `?`re implemented inhere;
|
||||
* **[layouts.jade](https://github.com/wekan/wekan/tree/main/client/components/main/layouts.jade)**: has the template for head portion of the html page and other general purpose templates: `userFormsLayout`, `defaultLayout`, `notFound`, `message`;
|
||||
* **[popup.tpl.jade](https://github.com/wekan/wekan/tree/main/client/components/main/popup.tpl.jade)**: tpl files only define a single template so there's no need to wrap content in a template tag; the name of the template is the base name of the file (`popup` in this case);
|
||||
* **[activities](../../client/components/activities)**:
|
||||
* **[activities.jade](../../client/components/activities/activities.jade)**: `activities` template for the list of activities placed inside a `sidebar-content`; uses `boardActivities` or `cardActivities` depending on `mode`; <span style="color:red">*XXX: does this mean that sidebar should be visible in board list mode? when does the `board` activity gets shown?*</span>
|
||||
* **[comments.jade](../../client/components/activities/comments.jade)**: `commentForm` template used in `card-details-canvas` for adding comments;
|
||||
* **[boards](../../client/components/boards)**:
|
||||
* **[boardArchive.jade](../../client/components/boards/boardArchive.jade)**: `archivedBoards` template for the modal dialog showing the list of archived boards that might be restored;
|
||||
* **[boardBody.jade](../../client/components/boards/boardBody.jade)**: top level template for presenting a board is `board` and, based on screen size and current state, it uses either `cardDetails` or `boardBody` templates; `boardBody` is the one including the `sidebar`, each `list`, `cardDetails` for larger screens when a card is selected and the `addListForm` for adding a new list (also defined in this file);
|
||||
* **[boardHeader.jade](../../client/components/boards/boardHeader.jade)**: `boardHeaderBar`, `boardMenuPopup`, `boardVisibilityList`, `boardChangeVisibilityPopup`, `boardChangeWatchPopup`, `boardChangeColorPopup`, `createBoard`, `chooseBoardSource`, `boardChangeTitlePopup`, `archiveBoardPopup`, `outgoingWebhooksPopup`;
|
||||
* **[boardsList.jade](../../client/components/boards/boardsList.jade)**: `boardList` and `boardListHeaderBar` for the list of boards in the initial screen;
|
||||
* **[cards](../../client/components/cards)**:
|
||||
* **[attachments.jade](../../client/components/cards/attachments.jade)**: `cardAttachmentsPopup`, `previewClipboardImagePopup`, `previewAttachedImagePopup`, `attachmentDeletePopup`, `attachmentsGalery`;
|
||||
* **[cardDate.jade](../../client/components/cards/cardDate.jade)**: `editCardDate` and `dateBadge` templates;
|
||||
* **[cardDetails.jade](../../client/components/cards/cardDetails.jade)**: `boardsAndLists` is the usual layout for a board display with `boardLists` being used in sandstorm where each board is independent;`cardDetails`, `editCardTitleForm`, `cardDetailsActionsPopup`, `moveCardPopup`, `copyCardPopup`,`cardMembersPopup`,`cardMorePopup`, `cardDeletePopup`;
|
||||
* **[cardTime.jade](../../client/components/cards/cardTime.jade)**: `editCardSpentTime` and `timeBadge` templates;
|
||||
* **[checklists.jade](../../client/components/cards/checklists.jade)**: `checklists`, `checklistDetail`, `checklistDeleteDialog`, `addChecklistItemForm`, `editChecklistItemForm`, `checklistItems`, `itemDetail`;
|
||||
* **[labels.jade](../../client/components/cards/labels.jade)**: `formLabel`, `createLabelPopup`, `editLabelPopup`, `deleteLabelPopup`, `cardLabelsPopup`;
|
||||
* **[minicard.jade](../../client/components/cards/minicard.jade)**: has the `minicard` template
|
||||
* **[forms](../../client/components/forms)**: **[inlinedform.jade](../../client/components/forms/inlinedform.jade)** has the`inlinedForm` template;
|
||||
* **[import](../../client/components/import)**:
|
||||
* **[import.jade](../../client/components/import/import.jade)**: `importHeaderBar`, `import`, `importTextarea`, `importMapMembers`, `importMapMembersAddPopup` are all templates used for importing Trello (via **[trelloMembersMapper.js](../../client/components/import/trelloMembersMapper.js)**) and Wekan (via **[wekanMembersMapper.js](../../client/components/import/wekanMembersMapper.js)**) boards;
|
||||
* **[lists](../../client/components/lists)**:
|
||||
* **[list.jade](../../client/components/lists/list.jade)**: `list` is the simple, main template for lists;
|
||||
* **[listBody.jade](../../client/components/lists/listBody.jade)**: `listBody`, `addCardForm`, `autocompleteLabelLine` templates;
|
||||
* **[listHeader.jade](../../client/components/lists/listHeader.jade)**: `listHeader`, `editListTitleForm`, `listActionPopup`, `boardLists`, `listMorePopup`, `listDeletePopup`, `setWipLimitPopup`, `wipLimitErrorPopup` templates;
|
||||
* **[main](../../client/components/main)**:
|
||||
* **[editor.jade](../../client/components/main/editor.jade)**: `editor` and `viewer` templates;
|
||||
* **[header.jade](../../client/components/main/header.jade)**: `header` and `offlineWarning` templates; if the user is connected we display a small "quick-access" top bar that list all starred boards with a link to go there (this is inspired by the Reddit "subreddit" bar); the first link goes to the boards page;
|
||||
* **[keyboardShortcuts.jade](../../client/components/main/keyboardShortcuts.jade)**: `shortcutsHeaderBar`, `shortcutsModalTitle`, `keyboardShortcuts` - all for the shortcuts that are presented when you press `?`re implemented inhere;
|
||||
* **[layouts.jade](../../client/components/main/layouts.jade)**: has the template for head portion of the html page and other general purpose templates: `userFormsLayout`, `defaultLayout`, `notFound`, `message`;
|
||||
* **[popup.tpl.jade](../../client/components/main/popup.tpl.jade)**: tpl files only define a single template so there's no need to wrap content in a template tag; the name of the template is the base name of the file (`popup` in this case);
|
||||
* **[spinner.tpl.jade](https://github.com/wekan/wekan/tree/main/client/components/main/spinner.tpl.jade)**: is the template for a "waiting" dialog;
|
||||
* **[settings](https://github.com/wekan/wekan/tree/main/client/components/settings)**:
|
||||
* **[informationBody.jade](https://github.com/wekan/wekan/tree/main/client/components/settings/informationBody.jade)**: `information`, `statistics` templates;
|
||||
* **[invitationCode.jade](https://github.com/wekan/wekan/tree/main/client/components/settings/invitationCode.jade)**: `invitationCode` template;
|
||||
* **[peopleBody.jade](https://github.com/wekan/wekan/tree/main/client/components/settings/peopleBody.jade)**: `people`, `peopleGeneral`, `peopleRow`, `editUserPopup`;
|
||||
* **[settingBody.jade](https://github.com/wekan/wekan/tree/main/client/components/settings/settingBody.jade)**: `setting`, `general`, `email`, `accountSettings`, `announcementSettings`
|
||||
* **[settingHeader.jade](https://github.com/wekan/wekan/tree/main/client/components/settings/settingHeader.jade)**: `settingHeaderBar` template;
|
||||
* **[sidebar](https://github.com/wekan/wekan/tree/main/client/components/sidebar)**:
|
||||
* **[sidebar.jade](https://github.com/wekan/wekan/tree/main/client/components/sidebar/sidebar.jade)**: `sidebar`, `homeSidebar`, `membersWidget`, `labelsWidget`, `memberPopup`, `removeMemberPopup`, `leaveBoardPopup`, `addMemberPopup`, `changePermissionsPopup`
|
||||
* **[sidebarArchives.jade](https://github.com/wekan/wekan/tree/main/client/components/sidebar/sidebarArchives.jade)**: `archivesSidebar`
|
||||
* **[sidebarFilters.jade](https://github.com/wekan/wekan/tree/main/client/components/sidebar/sidebarFilters.jade)**: `filterSidebar`, `multiselectionSidebar`, `disambiguateMultiLabelPopup`, `disambiguateMultiMemberPopup`, `moveSelectionPopup`;
|
||||
* **[users](https://github.com/wekan/wekan/tree/main/client/components/users)**:
|
||||
* **[userAvatar.jade](https://github.com/wekan/wekan/tree/main/client/components/users/userAvatar.jade)**: `userAvatar`, `userAvatarInitials`, `userPopup`, `memberName`, `changeAvatarPopup`, `cardMemberPopup`
|
||||
* **[userHeader.jade](https://github.com/wekan/wekan/tree/main/client/components/users/userHeader.jade)**: `headerUserBar`, `memberMenuPopup`, `editProfilePopup`, `editNotificationPopup`, `changePasswordPopup`, `changeLanguagePopup`, `changeSettingsPopup`;
|
||||
* **[settings](../../client/components/settings)**:
|
||||
* **[informationBody.jade](../../client/components/settings/informationBody.jade)**: `information`, `statistics` templates;
|
||||
* **[invitationCode.jade](../../client/components/settings/invitationCode.jade)**: `invitationCode` template;
|
||||
* **[peopleBody.jade](../../client/components/settings/peopleBody.jade)**: `people`, `peopleGeneral`, `peopleRow`, `editUserPopup`;
|
||||
* **[settingBody.jade](../../client/components/settings/settingBody.jade)**: `setting`, `general`, `email`, `accountSettings`, `announcementSettings`
|
||||
* **[settingHeader.jade](../../client/components/settings/settingHeader.jade)**: `settingHeaderBar` template;
|
||||
* **[sidebar](../../client/components/sidebar)**:
|
||||
* **[sidebar.jade](../../client/components/sidebar/sidebar.jade)**: `sidebar`, `homeSidebar`, `membersWidget`, `labelsWidget`, `memberPopup`, `removeMemberPopup`, `leaveBoardPopup`, `addMemberPopup`, `changePermissionsPopup`
|
||||
* **[sidebarArchives.jade](../../client/components/sidebar/sidebarArchives.jade)**: `archivesSidebar`
|
||||
* **[sidebarFilters.jade](../../client/components/sidebar/sidebarFilters.jade)**: `filterSidebar`, `multiselectionSidebar`, `disambiguateMultiLabelPopup`, `disambiguateMultiMemberPopup`, `moveSelectionPopup`;
|
||||
* **[users](../../client/components/users)**:
|
||||
* **[userAvatar.jade](../../client/components/users/userAvatar.jade)**: `userAvatar`, `userAvatarInitials`, `userPopup`, `memberName`, `changeAvatarPopup`, `cardMemberPopup`
|
||||
* **[userHeader.jade](../../client/components/users/userHeader.jade)**: `headerUserBar`, `memberMenuPopup`, `editProfilePopup`, `editNotificationPopup`, `changePasswordPopup`, `changeLanguagePopup`, `changeSettingsPopup`;
|
||||
* **[mixins](https://github.com/wekan/wekan/tree/main/client/components/mixins)**: [extends](http://www.meteorpedia.com/read/Infinite_Scrolling) **[infiniteScrolling.js](https://github.com/wekan/wekan/tree/main/client/components/mixins/infiniteScrolling.js)** for card details, sidebar and also extends **[perfectScrollbar.js](https://github.com/wekan/wekan/tree/main/client/components/mixins/perfectScrollbar.js)**;
|
||||
|
||||
## config
|
||||
|
||||
* **[blazeHelpers.js](https://github.com/wekan/wekan/tree/main/client/config/blazeHelpers.js)**: following [Blaze](http://blazejs.org/) helpers are registered here:`currentBoard()`, `currentCard()`, `getUser()` and `concat()`;
|
||||
* **[gecko-fix.js](https://github.com/wekan/wekan/tree/main/client/config/gecko-fix.js)**: removes [deprecated](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/watch)`watch` and `unwatch` from Firefox prior to version 58;
|
||||
* **[blazeHelpers.js](../../client/config/blazeHelpers.js)**: following [Blaze](http://blazejs.org/) helpers are registered here:`currentBoard()`, `currentCard()`, `getUser()` and `concat()`;
|
||||
* **[gecko-fix.js](../../client/config/gecko-fix.js)**: removes [deprecated](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/watch)`watch` and `unwatch` from Firefox prior to version 58;
|
||||
* **[presence.js](https://github.com/wekan/wekan/tree/main/client/config/presence.js)**: custom state function for [Presence](https://github.com/dburles/meteor-presence) that keeps track of current board;
|
||||
* **[reactiveTabs.js](https://github.com/wekan/wekan/tree/main/client/config/reactiveTabs.js)**: [ReactiveTabs](https://github.com/meteortemplates/tabs) are configured to use `basicTabs` template.
|
||||
|
||||
## lib
|
||||
|
||||
* **[accessibility.js](https://github.com/wekan/wekan/tree/main/client/lib/accessibility.js)**: define a set of DOM transformations that are specifically intended for blind screen readers;
|
||||
* **[cssEvents.js](https://github.com/wekan/wekan/tree/main/client/lib/cssEvents.js)**: the `CSSEvents`object has methods that select the name of the event based on the specific transitions and animations;
|
||||
* **[pasteImage.js](https://github.com/wekan/wekan/tree/main/client/lib/pasteImage.js)** and **[dropImage.js](https://github.com/wekan/wekan/tree/main/client/lib/dropImage.js)**: utility for pasting and dropping images on a web app; <span style="color:red">*XXX: add comments; not same style as the rest of the code*</span>
|
||||
* **[accessibility.js](../../client/lib/accessibility.js)**: define a set of DOM transformations that are specifically intended for blind screen readers;
|
||||
* **[cssEvents.js](../../client/lib/cssEvents.js)**: the `CSSEvents`object has methods that select the name of the event based on the specific transitions and animations;
|
||||
* **[pasteImage.js](../../client/lib/pasteImage.js)** and **[dropImage.js](../../client/lib/dropImage.js)**: utility for pasting and dropping images on a web app; <span style="color:red">*XXX: add comments; not same style as the rest of the code*</span>
|
||||
* **[emoji-values.js](https://github.com/wekan/wekan/tree/main/client/lib/emoji-values.js)**: sets Emoji.values;
|
||||
* **[escapeActions.js](https://github.com/wekan/wekan/tree/main/client/lib/escapeActions.js)**: defines the behavior (mostly canceling current edit) for escape keyboard key;
|
||||
* **[i18n.js](https://github.com/wekan/wekan/tree/main/client/lib/i18n.js)**: at startup we choose the language for the ui based on user profile or browser language;
|
||||
* **[inlinedform.js](https://github.com/wekan/wekan/tree/main/client/lib/inlinedform.js)**: forms for editing a single field (like adding a card); <span style="color:red">*XXX: comments in code suggest that a form that is not submitted will retain its value to prevent data loss using [unsavedEdits.js](https://github.com/wekan/wekan/tree/main/client/lib/unsavedEdits.js);bug?*</span>; <span style="color:red">*XXX: edit button to save and open*</span>;
|
||||
* **[keyboard.js](https://github.com/wekan/wekan/tree/main/client/lib/keyboard.js)**: the shortcuts that are presented when you press `?`re implemented inhere;
|
||||
* **[escapeActions.js](../../client/lib/escapeActions.js)**: defines the behavior (mostly canceling current edit) for escape keyboard key;
|
||||
* **[i18n.js](../../client/lib/i18n.js)**: at startup we choose the language for the ui based on user profile or browser language;
|
||||
* **[inlinedform.js](../../client/lib/inlinedform.js)**: forms for editing a single field (like adding a card); <span style="color:red">*XXX: comments in code suggest that a form that is not submitted will retain its value to prevent data loss using [unsavedEdits.js](../../client/lib/unsavedEdits.js);bug?*</span>; <span style="color:red">*XXX: edit button to save and open*</span>;
|
||||
* **[keyboard.js](../../client/lib/keyboard.js)**: the shortcuts that are presented when you press `?`re implemented inhere;
|
||||
* **[mixins.js](https://github.com/wekan/wekan/tree/main/client/lib/mixins.js)**: stub; no `Mixins` at this point; <span style="color:red">*XXX: what does `new class` do? exlint: missing () invoking a constructor*</span>
|
||||
* **[popup.js](https://github.com/wekan/wekan/tree/main/client/lib/popup.js)**: defines `Popup` class for things likes electing a date; <span style="color:red">*XXX: not a Blaze helper?*</span>
|
||||
* **[textComplete.js](https://github.com/wekan/wekan/tree/main/client/lib/textComplete.js)**: extends [jquery-textcomplete](https://yuku-t.com/jquery-textcomplete/) to integrate with the rest of the system (like escape actions, tab and enter key handling); <span style="color:red">*XXX: deprecated?*</span>
|
||||
* **[utils.js](https://github.com/wekan/wekan/tree/main/client/lib/utils.js)**: various methods all over the place (resize, screen size, sort, capitalize, navigate to board and card);
|
||||
* **[popup.js](../../client/lib/popup.js)**: defines `Popup` class for things likes electing a date; <span style="color:red">*XXX: not a Blaze helper?*</span>
|
||||
* **[textComplete.js](../../client/lib/textComplete.js)**: extends [jquery-textcomplete](https://yuku-t.com/jquery-textcomplete/) to integrate with the rest of the system (like escape actions, tab and enter key handling); <span style="color:red">*XXX: deprecated?*</span>
|
||||
* **[utils.js](../../client/lib/utils.js)**: various methods all over the place (resize, screen size, sort, capitalize, navigate to board and card);
|
||||
* **Blaze helpers**:
|
||||
* **[filter.js](https://github.com/wekan/wekan/tree/main/client/lib/filter.js)**: registers `Filter` [Blaze](http://blazejs.org/) helper to support filtering cards by labels and by members;
|
||||
* **[modal.js](https://github.com/wekan/wekan/tree/main/client/lib/modal.js)**: registers `Modal` [Blaze](http://blazejs.org/) helper to support showing modal windows like the one for archived boards;
|
||||
* **[multiSelection.js](https://github.com/wekan/wekan/tree/main/client/lib/multiSelection.js)**: registers `Modal` [Blaze](http://blazejs.org/) helper to support multiple selection mode;
|
||||
* **[unsavedEdits.js](https://github.com/wekan/wekan/tree/main/client/lib/unsavedEdits.js)**: registers `getUnsavedValue` and `hasUnsavedValue` [Blaze](http://blazejs.org/) helpers to preserve content entered in fields but not saved;
|
||||
* **[filter.js](../../client/lib/filter.js)**: registers `Filter` [Blaze](http://blazejs.org/) helper to support filtering cards by labels and by members;
|
||||
* **[modal.js](../../client/lib/modal.js)**: registers `Modal` [Blaze](http://blazejs.org/) helper to support showing modal windows like the one for archived boards;
|
||||
* **[multiSelection.js](../../client/lib/multiSelection.js)**: registers `Modal` [Blaze](http://blazejs.org/) helper to support multiple selection mode;
|
||||
* **[unsavedEdits.js](../../client/lib/unsavedEdits.js)**: registers `getUnsavedValue` and `hasUnsavedValue` [Blaze](http://blazejs.org/) helpers to preserve content entered in fields but not saved;
|
||||
|
||||
# Server
|
||||
|
||||
.js files in this directory are not available to the client.
|
||||
|
||||
* **[statistics.js](https://github.com/wekan/wekan/tree/main/server/statistics.js)** implements a Meteor server-only [method](https://guide.meteor.com/methods.html) for general-purpose information such as OS, memory, CPUs, PID of the process and so on.
|
||||
* **[statistics.js](../../server/statistics.js)** implements a Meteor server-only [method](https://guide.meteor.com/methods.html) for general-purpose information such as OS, memory, CPUs, PID of the process and so on.
|
||||
* **[migrations.js](https://github.com/wekan/wekan/tree/main/server/migrations.js)** is where code that update sold databases to new schema is located. Anytime the schema of one of the collection changes in a non-backward compatible way a migration needs to be written in this file.
|
||||
* **[authentication.js](https://github.com/wekan/wekan/tree/main/server/authentication.js)** add the `Authentication`object to Meteor that provides methods for checking access rights.
|
||||
* **[lib/utils.js](https://github.com/wekan/wekan/tree/main/server/lib/utils.js)** defines some checks used by [checklists.js](https://github.com/wekan/wekan/tree/main/models/checklists.js)** model. <span style="color:red">*XXX: these methods are defined in server-only code by are used in models, which are visible by the client (in Checklists.allow)?*</span>
|
||||
* **[notifications](https://github.com/wekan/wekan/tree/main/server/notifications)**
|
||||
* **[notifications.js](https://github.com/wekan/wekan/tree/main/server/notifications/notifications.js)**: defines the `Notifications` object that supports [Activities](models/activities.js) and holds a list of functions to call when its `notify()` method is called along with convenience methods to subscribe, unsubscribe and a way to filter recipients according to user settings for notification;
|
||||
* **[email.js](https://github.com/wekan/wekan/tree/main/server/notifications/email.js)**: makes use of the notification system to send an email to a user;
|
||||
* **[profile.js](https://github.com/wekan/wekan/tree/main/server/notifications/profile.js)**: *stub*; will allow associating notifications with user ids to be consumed by mobile apps;
|
||||
* **[notifications.js](https://github.com/wekan/wekan/tree/main/server/notifications/notifications.js)**: adds the `watch()` Meteor server-only [method](https://guide.meteor.com/methods.html) that may watch boards, lists or cards using [models/watchable.js](https://github.com/wekan/wekan/tree/main/models/watchable.js);
|
||||
* **[outgoing.js](https://github.com/wekan/wekan/tree/main/server/notifications/outgoing.js)**: adds the `outgoingWebhooks()` Meteor server-only [method](https://guide.meteor.com/methods.html) that can call external API <span style="color:red">*XXX: I guess*</span>
|
||||
* **[publications](https://github.com/wekan/wekan/tree/main/server/publications)** defines sets of records that are [published](https://docs.meteor.com/api/pubsub.html#Meteor-publish) by the server and how clients can subscribe to those:
|
||||
* **[accountSettings.js](https://github.com/wekan/wekan/tree/main/server/publications/accountSettings.js)**: [AccountSettings](models/accountSettings.js) collection;
|
||||
* **[activities.js](https://github.com/wekan/wekan/tree/main/server/publications/activities.js)**: [Activities](models/activities.js) collection filtered and paginated;
|
||||
* **[announcements.js](https://github.com/wekan/wekan/tree/main/server/publications/announcements.js)**: [Announcements](models/announcements.js) collection;
|
||||
* **[avatars.js](https://github.com/wekan/wekan/tree/main/server/publications/avatars.js)**: [Avatars](models/avatars.js) collection for current user;
|
||||
* **[boards.js](https://github.com/wekan/wekan/tree/main/server/publications/boards.js)**: [Boards](models/boards.js) collection for current user, archived boards collection and individual board as a [relation](https://atmospherejs.com/cottz/publish-relations);
|
||||
* **[cards.js](https://github.com/wekan/wekan/tree/main/server/publications/cards.js)**: a [Card](https://github.com/wekan/wekan/tree/main/models/cards.js) by its id;
|
||||
* **[authentication.js](../../server/authentication.js)** add the `Authentication`object to Meteor that provides methods for checking access rights.
|
||||
* **[lib/utils.js](../../server/lib/utils.js)** defines some checks used by [checklists.js](../../models/checklists.js)** model. <span style="color:red">*XXX: these methods are defined in server-only code by are used in models, which are visible by the client (in Checklists.allow)?*</span>
|
||||
* **[notifications](../../server/notifications)**
|
||||
* **[notifications.js](../../server/notifications/notifications.js)**: defines the `Notifications` object that supports [Activities](models/activities.js) and holds a list of functions to call when its `notify()` method is called along with convenience methods to subscribe, unsubscribe and a way to filter recipients according to user settings for notification;
|
||||
* **[email.js](../../server/notifications/email.js)**: makes use of the notification system to send an email to a user;
|
||||
* **[profile.js](../../server/notifications/profile.js)**: *stub*; will allow associating notifications with user ids to be consumed by mobile apps;
|
||||
* **[notifications.js](../../server/notifications/notifications.js)**: adds the `watch()` Meteor server-only [method](https://guide.meteor.com/methods.html) that may watch boards, lists or cards using [models/watchable.js](../../models/watchable.js);
|
||||
* **[outgoing.js](../../server/notifications/outgoing.js)**: adds the `outgoingWebhooks()` Meteor server-only [method](https://guide.meteor.com/methods.html) that can call external API <span style="color:red">*XXX: I guess*</span>
|
||||
* **[publications](../../server/publications)** defines sets of records that are [published](https://docs.meteor.com/api/pubsub.html#Meteor-publish) by the server and how clients can subscribe to those:
|
||||
* **[accountSettings.js](../../server/publications/accountSettings.js)**: [AccountSettings](models/accountSettings.js) collection;
|
||||
* **[activities.js](../../server/publications/activities.js)**: [Activities](models/activities.js) collection filtered and paginated;
|
||||
* **[announcements.js](../../server/publications/announcements.js)**: [Announcements](models/announcements.js) collection;
|
||||
* **[avatars.js](../../server/publications/avatars.js)**: [Avatars](models/avatars.js) collection for current user;
|
||||
* **[boards.js](../../server/publications/boards.js)**: [Boards](models/boards.js) collection for current user, archived boards collection and individual board as a [relation](https://atmospherejs.com/cottz/publish-relations);
|
||||
* **[cards.js](../../server/publications/cards.js)**: a [Card](../../models/cards.js) by its id;
|
||||
* **[fast-render.js](https://github.com/wekan/wekan/tree/main/server/publications/fast-render.js)**: configures [FastRender](https://github.com/kadirahq/fast-render) to use the board data; <span style="color:red">*XXX: FastRender docs say "Make sure you're using Meteor.subscribe and not this.subscribe"*</span>
|
||||
* **[people.js](https://github.com/wekan/wekan/tree/main/server/publications/people.js)**: [Users](models/users.js) collection;
|
||||
* **[settings.js](https://github.com/wekan/wekan/tree/main/server/publications/settings.js)**: [Settings](models/settings.js) collection and, separately, the mail server;
|
||||
* **[unsavedEdits.js](https://github.com/wekan/wekan/tree/main/server/publications/unsavedEdits.js)**: [UnsavedEdits](models/unsavedEdits.js) collection;
|
||||
* **[users.js](https://github.com/wekan/wekan/tree/main/server/publications/users.js)**: provides a "mini-profile" for individual users and a [way](https://docs.meteor.com/api/collections.html#fieldspecifiers) to check if current user is admin.
|
||||
* **[people.js](../../server/publications/people.js)**: [Users](models/users.js) collection;
|
||||
* **[settings.js](../../server/publications/settings.js)**: [Settings](models/settings.js) collection and, separately, the mail server;
|
||||
* **[unsavedEdits.js](../../server/publications/unsavedEdits.js)**: [UnsavedEdits](models/unsavedEdits.js) collection;
|
||||
* **[users.js](../../server/publications/users.js)**: provides a "mini-profile" for individual users and a [way](https://docs.meteor.com/api/collections.html#fieldspecifiers) to check if current user is admin.
|
||||
|
||||
# Models
|
||||
|
||||
The files in **[models](https://github.com/wekan/wekan/tree/main/models)** directory mainly define collections; most of them have [aldeed SimpleSchema](https://atmospherejs.com/aldeed/simple-schema) for automatic validation of insert and update of collections. This is also where helpers, mutations, methods, hooks and bootstrap code is to be found. [Server side code](https://docs.meteor.com/api/core.html#Meteor-isServer) also implements json REST API.
|
||||
The files in **[models](../../models)** directory mainly define collections; most of them have [aldeed SimpleSchema](https://atmospherejs.com/aldeed/simple-schema) for automatic validation of insert and update of collections. This is also where helpers, mutations, methods, hooks and bootstrap code is to be found. [Server side code](https://docs.meteor.com/api/core.html#Meteor-isServer) also implements json REST API.
|
||||
|
||||
Collections (mostly `Mongo.Collection` except as noted) are defined in:
|
||||
* **[accountSettings.js](https://github.com/wekan/wekan/tree/main/models/accountSettings.js)**;
|
||||
* **[activities.js](https://github.com/wekan/wekan/tree/main/models/activities.js)**: does not have a SimpleSchema;
|
||||
* **[announcements.js](https://github.com/wekan/wekan/tree/main/models/announcements.js)**;
|
||||
* **[attachments.js](https://github.com/wekan/wekan/tree/main/models/attachments.js)**: file-system collection;
|
||||
* **[avatars.js](https://github.com/wekan/wekan/tree/main/models/avatars.js)**: file-system collection;
|
||||
* **[boards.js](https://github.com/wekan/wekan/tree/main/models/boards.js)**;
|
||||
* **[cardComments.js](https://github.com/wekan/wekan/tree/main/models/cardComments.js)**;
|
||||
* **[cards.js](https://github.com/wekan/wekan/tree/main/models/cards.js)**;
|
||||
* **[checklists.js](https://github.com/wekan/wekan/tree/main/models/checklists.js)**;
|
||||
* **[integrations.js](https://github.com/wekan/wekan/tree/main/models/integrations.js)**;
|
||||
* **[invitationCodes.js](https://github.com/wekan/wekan/tree/main/models/invitationCodes.js)**;
|
||||
* **[lists.js](https://github.com/wekan/wekan/tree/main/models/lists.js)**;
|
||||
* **[settings.js](https://github.com/wekan/wekan/tree/main/models/settings.js)**;
|
||||
* **[unsavedEdits.js](https://github.com/wekan/wekan/tree/main/models/unsavedEdits.js)**;
|
||||
* **[users.js](https://github.com/wekan/wekan/tree/main/models/users.js)**: extends the `Meteor.users` collection.
|
||||
* **[accountSettings.js](../../models/accountSettings.js)**;
|
||||
* **[activities.js](../../models/activities.js)**: does not have a SimpleSchema;
|
||||
* **[announcements.js](../../models/announcements.js)**;
|
||||
* **[attachments.js](../../models/attachments.js)**: file-system collection;
|
||||
* **[avatars.js](../../models/avatars.js)**: file-system collection;
|
||||
* **[boards.js](../../models/boards.js)**;
|
||||
* **[cardComments.js](../../models/cardComments.js)**;
|
||||
* **[cards.js](../../models/cards.js)**;
|
||||
* **[checklists.js](../../models/checklists.js)**;
|
||||
* **[integrations.js](../../models/integrations.js)**;
|
||||
* **[invitationCodes.js](../../models/invitationCodes.js)**;
|
||||
* **[lists.js](../../models/lists.js)**;
|
||||
* **[settings.js](../../models/settings.js)**;
|
||||
* **[unsavedEdits.js](../../models/unsavedEdits.js)**;
|
||||
* **[users.js](../../models/users.js)**: extends the `Meteor.users` collection.
|
||||
|
||||
Other files:
|
||||
* **[watchable.js](https://github.com/wekan/wekan/tree/main/models/watchable.js)**: extends the schema, helpers and mutations of `Boards`, `Lists` and `Cards`.
|
||||
* **[export.js](https://github.com/wekan/wekan/tree/main/models/export.js)**: has some code to support the REST API.
|
||||
* **[import.js](https://github.com/wekan/wekan/tree/main/models/import.js)**: implements `importBoard()` method so that Trello (in **[trelloCreator.js](https://github.com/wekan/wekan/tree/main/models/trelloCreator.js)**) and Wekan (in **[wekanCreator.js](https://github.com/wekan/wekan/tree/main/models/wekanCreator.js)**) boards can be imported. <span style="color:red">*XXX: Solid candidates for a directory of their own.*</span>
|
||||
* **[watchable.js](../../models/watchable.js)**: extends the schema, helpers and mutations of `Boards`, `Lists` and `Cards`.
|
||||
* **[export.js](../../models/export.js)**: has some code to support the REST API.
|
||||
* **[import.js](../../models/import.js)**: implements `importBoard()` method so that Trello (in **[trelloCreator.js](../../models/trelloCreator.js)**) and Wekan (in **[wekanCreator.js](../../models/wekanCreator.js)**) boards can be imported. <span style="color:red">*XXX: Solid candidates for a directory of their own.*</span>
|
||||
|
||||
# Tools
|
||||
|
||||
* Git:
|
||||
* **.git**;
|
||||
* **[.gitignore](https://github.com/wekan/wekan/tree/main/.gitignore)**;
|
||||
* **[.gitignore](../../.gitignore)**;
|
||||
* Docker:
|
||||
* **[docker-compose.yml](https://github.com/wekan/wekan/tree/main/docker-compose.yml)**: the compose file is a YAML file defining services, networks and volumes;
|
||||
* **[Dockerfile](https://github.com/wekan/wekan/tree/main/Dockerfile)**;
|
||||
* **[docker-compose.yml](../../docker-compose.yml)**: the compose file is a YAML file defining services, networks and volumes;
|
||||
* **[Dockerfile](../../Dockerfile)**;
|
||||
* Snap:
|
||||
* **[snapcraft.yaml](https://github.com/wekan/wekan/tree/main/snapcraft.yaml)**: [Snapcraft](https://snapcraft.io/) packages any app for every Linux desktop, server, cloud or device, and deliver updates directly;
|
||||
* **[snap](https://github.com/wekan/wekan/tree/main/snap)**;
|
||||
* **[snap-src](https://github.com/wekan/wekan/tree/main/snap-src)**;
|
||||
* **[snapcraft.yaml](../../snapcraft.yaml)**: [Snapcraft](https://snapcraft.io/) packages any app for every Linux desktop, server, cloud or device, and deliver updates directly;
|
||||
* **[snap](../../snap)**;
|
||||
* **[snap-src](../../snap-src)**;
|
||||
* Sandstorm:
|
||||
* **[sandstorm.js](https://github.com/wekan/wekan/tree/main/sandstorm.js)**: [Sandstorm](https://sandstorm.io/) specific code;
|
||||
* **[sandstorm-pkgdef.capnp](https://github.com/wekan/wekan/tree/main/sandstorm-pkgdef.capnp)**: used the meteor-spk tool to generate a sandstorm package;
|
||||
* **[sandstorm.js](../../sandstorm.js)**: [Sandstorm](https://sandstorm.io/) specific code;
|
||||
* **[sandstorm-pkgdef.capnp](../../sandstorm-pkgdef.capnp)**: used the meteor-spk tool to generate a sandstorm package;
|
||||
* Node:
|
||||
* **[package.json](https://github.com/wekan/wekan/tree/main/package.json)**;
|
||||
* **[package.json](../../package.json)**;
|
||||
* **node_modules**
|
||||
* **[app.json](https://github.com/wekan/wekan/tree/main/app.json)**: is a manifest format for describing web apps (build requirements, environment variables, addons, and other information);
|
||||
* **[app.env](https://github.com/wekan/wekan/tree/main/app.env)**: environment variables;
|
||||
* **[app.json](../../app.json)**: is a manifest format for describing web apps (build requirements, environment variables, addons, and other information);
|
||||
* **[app.env](../../app.env)**: environment variables;
|
||||
* Meteor: is a full-stack JavaScript platform for developing modern web and mobile applications.
|
||||
* **[.meteor](https://github.com/wekan/wekan/tree/main/.meteor)**;
|
||||
* **[.meteor](../../.meteor)**;
|
||||
* Translation:
|
||||
* **[i18n](https://github.com/wekan/wekan/tree/main/i18n)** directory has one .json file for each supported language
|
||||
* **[.tx](https://github.com/wekan/wekan/tree/main/.tx)**: configuration for [Transifex](https://app.transifex.com/wekan/) tool used to manage translation;
|
||||
* **[.tx](../../.tx)**: configuration for [Transifex](https://app.transifex.com/wekan/) tool used to manage translation;
|
||||
* Text editors:
|
||||
* **[.vscode](https://github.com/wekan/wekan/tree/main/.vscode)**: [Visual Studio Code Editor](https://code.visualstudio.com/docs/getstarted/settings);
|
||||
* **[.editorconfig](https://github.com/wekan/wekan/tree/main/.editorconfig)**: [EditorConfig](http://EditorConfig.org) provides consistent coding styles between different editors and IDEs;
|
||||
* **[.github](https://github.com/wekan/wekan/tree/main/.github)**: hosts the issues template;
|
||||
* **[.eslintrc.json](https://github.com/wekan/wekan/tree/main/.eslintrc.json)**: [ESLint](https://eslint.org/docs/user-guide/configuring) configuration;
|
||||
* **[.travis.yml](https://github.com/wekan/wekan/tree/main/.travis.yml)**: configuration for [Travis CI](https://travis-ci.org/);
|
||||
* **[scalingo.json](https://github.com/wekan/wekan/tree/main/scalingo.json)**: [Scalingo](https://scalingo.com/) is a deploy solution;
|
||||
* **[.vscode](../../.vscode)**: [Visual Studio Code Editor](https://code.visualstudio.com/docs/getstarted/settings);
|
||||
* **[.editorconfig](../../.editorconfig)**: [EditorConfig](http://EditorConfig.org) provides consistent coding styles between different editors and IDEs;
|
||||
* **[.github](../../.github)**: hosts the issues template;
|
||||
* **[.eslintrc.json](../../.eslintrc.json)**: [ESLint](https://eslint.org/docs/user-guide/configuring) configuration;
|
||||
* **[.travis.yml](../../.travis.yml)**: configuration for [Travis CI](https://travis-ci.org/);
|
||||
* **[scalingo.json](../../scalingo.json)**: [Scalingo](https://scalingo.com/) is a deploy solution;
|
||||
* **[fix-download-unicode](https://github.com/wekan/wekan/tree/main/fix-download-unicode)**: `cfs_access-point.txt` from this folder is copied to `bundle/programs/server/packages/cfs_access-point.js` in Docker build and in snapcraft build; this is a monkey patch fix for [downloading files that have unicode in filename](https://github.com/wekan/wekan/issues/784).
|
||||
|
||||
# Info
|
||||
|
||||
* **[meta](https://github.com/wekan/wekan/tree/main/meta)**: binary signatures, project description, icons, screenshots and, oui, a French change-log;
|
||||
* **[CHANGELOG.md](https://github.com/wekan/wekan/tree/main/CHANGELOG.md)**;
|
||||
* **[meta](../../meta)**: binary signatures, project description, icons, screenshots and, oui, a French change-log;
|
||||
* **[CHANGELOG.md](../../CHANGELOG.md)**;
|
||||
* **[Contributing.md](https://github.com/wekan/wekan/tree/main/Contributing.md)**;
|
||||
* **[LICENSE](https://github.com/wekan/wekan/tree/main/LICENSE)**;
|
||||
* **[README.md](https://github.com/wekan/wekan/tree/main/README.md)**.
|
||||
* **[LICENSE](../../LICENSE)**;
|
||||
* **[README.md](../../README.md)**.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
- Read [Wekan new release ChangeLog](https://github.com/wekan/wekan/blob/main/CHANGELOG.md)
|
||||
- [Install Meteor.js](https://www.meteor.com/install). Note: Windows install is very slow, please use other option at [Windows wiki page](Windows)
|
||||
- Read [Wekan new release ChangeLog](../../CHANGELOG.md)
|
||||
- [Install Meteor.js](https://www.meteor.com/install). Note: Windows install is very slow, please use other option at [Windows wiki page](../Platforms/Propietary/Windows/Windows.md)
|
||||
- Download newest bundle from https://releases.wekan.team
|
||||
- Unarchive bundle .tar.gz file: `tar -zxvf wekan-VERSION.tar.gz`
|
||||
- `cd bundle`
|
||||
@@ -8,6 +8,6 @@
|
||||
|
||||
[Wekan for Meteor.js bundle bug reports and feature requests](https://github.com/wekan/wekan/issues)
|
||||
|
||||
[Build Meteor bundle from source](Source)
|
||||
[Build Meteor bundle from source](../Platforms/FOSS/Source/Source.md)
|
||||
|
||||
[Install from source without root](Install-from-source-without-root)
|
||||
[Install from source without root](../Platforms/FOSS/Source/Install-from-source-without-root.md)
|
||||
@@ -20,7 +20,7 @@
|
||||
- https://www.youtube.com/watch?v=ZTtXRJTUCIU
|
||||
- https://github.com/jamauro/pwa-kit
|
||||
- https://wekan.fi/app/
|
||||
- https://github.com/wekan/wekan/wiki/PWA
|
||||
- [PWA](../Browsers/PWA.md)
|
||||
- Over 100 icons for different platforms https://github.com/wekan/wekan/blob/main/public/site.webmanifest
|
||||
|
||||
## Tip to any programmers
|
||||
@@ -76,7 +76,7 @@ https://www.youtube.com/watch?v=8pDqJVdNa44
|
||||
- Look at progress of migrated packages https://forums.meteor.com/t/looking-for-help-migrating-packages-to-meteor-3-0/60985
|
||||
- Migrate from Fibers (works only at Meteor 2 and Node.js 14) to Async/Await (works with Meteor 3 and Node.js 20)
|
||||
- Helper tool https://forums.meteor.com/t/blaze-async-migration-helper-babel-plugin-tracker-async-enhance-for-the-client-side-released/60842
|
||||
- Info how Async/Await works, from Storyteller: https://github.com/wekan/wekan/wiki/AsyncAwait
|
||||
- Info how Async/Await works, from Storyteller: [AsyncAwait](AsyncAwait.md)
|
||||
- Forums posts about migrating: https://forums.meteor.com
|
||||
- ( Older version upgrade to newest 2.14 https://guide.meteor.com/2.14-migration )
|
||||
|
||||
@@ -99,8 +99,8 @@ From Meteor Discord chat from someone:
|
||||
|
||||
## Building WeKan
|
||||
|
||||
- https://github.com/wekan/wekan/wiki/Emoji
|
||||
- Docs: https://github.com/wekan/wekan/tree/main/docs
|
||||
- [Emoji](../Features/Emoji.md)
|
||||
- Docs: [docs](..)
|
||||
|
||||
## Similar technology
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
## Testers to ping with new Wekan Edge release
|
||||
- All those whose issues were fixed [at ChangeLog](https://github.com/wekan/wekan/blob/main/CHANGELOG.md)
|
||||
- All those whose issues were fixed [at ChangeLog](../../CHANGELOG.md)
|
||||
- @saschafoerster
|
||||
|
||||
Those should reply to new issue that has name `Test Edge Wekan (version-number-here)` at https://github.com/wekan/wekan/issues .
|
||||
@@ -14,7 +14,7 @@ https://github.com/wekan/wekan/issues/2811
|
||||
|
||||
## What usually happens when Wekan gets broken
|
||||
|
||||
1. There is many new issues about some same new feature or bug. Yes, also when some notices new feature that is already [at ChangeLog](https://github.com/wekan/wekan/blob/main/CHANGELOG.md), they add issue about it, usually asking "Can this feature be turned off or made optional?". This is usually if new feature changes some old workflow radically.
|
||||
1. There is many new issues about some same new feature or bug. Yes, also when some notices new feature that is already [at ChangeLog](../../CHANGELOG.md), they add issue about it, usually asking "Can this feature be turned off or made optional?". This is usually if new feature changes some old workflow radically.
|
||||
2. Many do tag @xet7 that are you aware of the issue.
|
||||
3. Someone yells at IRC that "this is too much, it's time to fork Wekan". Well, that's because he gets yelled at by all users of Wekan at his company.
|
||||
4. xet7 does not have time to answer those, he just fixes the issue - usually at the same day, makes new release, closes all related issues with last comment "Please try Wekan (version-number-here)".
|
||||
@@ -23,7 +23,7 @@ https://github.com/wekan/wekan/issues/2811
|
||||
|
||||
## ChangeLog
|
||||
|
||||
https://github.com/wekan/wekan/blob/main/CHANGELOG.md
|
||||
[CHANGELOG.md](../../CHANGELOG.md)
|
||||
|
||||
## Backup before testing. And backup daily in production.
|
||||
|
||||
@@ -31,7 +31,7 @@ Why? Well, Wekan has no undo yet. If you delete Swimlane/List/Card, it's gone.
|
||||
That's why it's harder to reach in submenu. It's better to Arhive those,
|
||||
and unarchive.
|
||||
|
||||
https://github.com/wekan/wekan/wiki/Backup
|
||||
[Backup](../Backup/Backup.md)
|
||||
|
||||
You could test at other computer than your production.
|
||||
|
||||
@@ -81,7 +81,7 @@ image: wekanteam/wekan:v3.55
|
||||
### How to change Docker version
|
||||
|
||||
1) Backup first
|
||||
https://github.com/wekan/wekan/wiki/Backup
|
||||
[Backup](../Backup/Backup.md)
|
||||
|
||||
```
|
||||
docker-compose stop
|
||||
@@ -137,7 +137,7 @@ docker-compose up -d --build
|
||||
1) Install local development version of Sandstorm to your laptop from https://sandstorm.io/install
|
||||
|
||||
2) Download and install experimental version to your local Sandstorm:
|
||||
https://github.com/wekan/wekan/wiki/Sandstorm
|
||||
[Sandstorm](../Platforms/FOSS/Sandstorm/Sandstorm.md)
|
||||
Local sandstorm is at http://local.sandstorm.io:6080/ .
|
||||
|
||||
3) Download your production Wekan grain .zip file with down arrow button. Upload to your dev local Sandstorm.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## Roles
|
||||
|
||||
It depends on role of user what drag drop is allowed https://github.com/wekan/wekan/wiki/REST-API-Role
|
||||
It depends on role of user what drag drop is allowed [REST API Role](../API/Role.md)
|
||||
|
||||
## Mobile Drag Drop
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
## Newest Wekan
|
||||
|
||||
In [Wekan v5.02](https://github.com/wekan/wekan/blob/main/CHANGELOG.md#v502-2021-03-02-wekan-release) is fix for STMP settings [that works with very happy feedback](https://github.com/wekan/wekan/issues/3529#issuecomment-789085999). It requires:
|
||||
In [Wekan v5.02](../../CHANGELOG.md#v502-2021-03-02-wekan-release) is fix for STMP settings [that works with very happy feedback](https://github.com/wekan/wekan/issues/3529#issuecomment-789085999). It requires:
|
||||
- **There is no email settings at Admin Panel anymore**
|
||||
- Email settings are made only with snap commands like `sudo snap set wekan mail-url....` or similar in Docker/Source etc `MAIL_URL=...`, more details below.
|
||||
- For any errors like SSLv3 and STARTTLS, check [newest AWS SES info](#example-aws-ses) and use similar settings, other SSLv3/STARTTLS info could be outdated.
|
||||
@@ -50,7 +50,7 @@ await Email.sendAsync({
|
||||
|
||||
## Debugging your SMTP mail server
|
||||
|
||||
If you can't get your email server working with smtp/stmps or MAIL_SERVICE, look is there more config options for your email server at https://nodemailer.com docs, and add new issue about your email server to https://github.com/wekan/wekan/issues or [pull request](Emoji#how-you-could-add-another-plugin).
|
||||
If you can't get your email server working with smtp/stmps or MAIL_SERVICE, look is there more config options for your email server at https://nodemailer.com docs, and add new issue about your email server to https://github.com/wekan/wekan/issues or [pull request](../Features/Emoji.md#how-you-could-add-another-plugin).
|
||||
|
||||
For example, for https://nodemailer.com/smtp/
|
||||
|
||||
@@ -82,7 +82,7 @@ let info = transporter.sendMail({
|
||||
```
|
||||
|
||||
|
||||
If with some code example sending email works, xet7 can add it to wekan, like in [Wekan v5.52](https://github.com/wekan/wekan/blob/main/CHANGELOG.md#v552-2021-08-26-wekan-release) xet7 added direct nodemailer usage for MAIL_SERVICE.
|
||||
If with some code example sending email works, xet7 can add it to wekan, like in [Wekan v5.52](../../CHANGELOG.md#v552-2021-08-26-wekan-release) xet7 added direct nodemailer usage for MAIL_SERVICE.
|
||||
|
||||
## MAIL_SERVICE (not currently in use)
|
||||
|
||||
@@ -304,7 +304,7 @@ Mail configuration if done by settings of `MAIL_URL` & `MAIL_FROM` environment p
|
||||
|
||||
## No mail server
|
||||
|
||||
You can choose to _NOT_ configure a mail server, by not providing the `MAIL_URL` & `MAIL_FROM` environment parameters. Instead the mail message will be send to the terminal output. See [FAQ](FAQ#show-mails-with-a-docker-image-without-mail-configuration) for more info.
|
||||
You can choose to _NOT_ configure a mail server, by not providing the `MAIL_URL` & `MAIL_FROM` environment parameters. Instead the mail message will be send to the terminal output. See [FAQ](../FAQ/FAQ.md#show-mails-with-a-docker-image-without-mail-configuration) for more info.
|
||||
|
||||
## Email servers: SMTP or Exchange
|
||||
|
||||
|
||||
+13
-13
@@ -18,10 +18,10 @@ that by providing one-click installation on various platforms.
|
||||
- Wekan is used in [most countries of the world](https://snapcraft.io/wekan).
|
||||
- Wekan largest user has 30k users using Wekan in their company.
|
||||
- Wekan has been [translated](https://app.transifex.com/wekan/) to about 70+ languages.
|
||||
- [Features](Features): Wekan has real-time user interface.
|
||||
- [Platforms](Platforms): Wekan supports many platforms.
|
||||
- [Features](../Features/Features.md): Wekan has real-time user interface.
|
||||
- [Platforms](../Platforms/FOSS/Platforms.md): Wekan supports many platforms.
|
||||
Wekan is critical part of new platforms Wekan is currently being integrated to.
|
||||
- [Integrations](Integrations): Current possible integrations and future plans.
|
||||
- [Integrations](../ImportExport/Integrations.md): Current possible integrations and future plans.
|
||||
|
||||
## About niche of WeKan
|
||||
|
||||
@@ -30,7 +30,7 @@ It's a very specific niche, with limited amount competitors, with all of this ap
|
||||
- Kanban with very many features, polish and usability
|
||||
- Permissive MIT license (if some other changes to GPL, Open Core, has https://sso.tax , is propietary etc, it's not in same niche anymore)
|
||||
- Self-hosted (or SaaS)
|
||||
- All code https://github.com/wekan/wekan and docs https://github.com/wekan/wekan/wiki Open Source
|
||||
- All code https://github.com/wekan/wekan and docs [Wekan documentation](../README.md) Open Source
|
||||
- Available for many OS and CPU platforms, listed at download/install section of https://wekan.fi
|
||||
- Translated to 70+ languages at https://app.transifex.com/wekan
|
||||
- Has optional affordable Commercial Support for Features/Fixes/Support/Hosting available https://wekan.fi/commercial-support/ that funds development of WeKan. Commercial Support provides private support chat with maintainer of WeKan.
|
||||
@@ -41,18 +41,18 @@ It's a very specific niche, with limited amount competitors, with all of this ap
|
||||
## Requirements
|
||||
|
||||
- 64bit: Linux [Snap](https://github.com/wekan/wekan-snap/wiki/Install) or [Sandstorm](https://sandstorm.io) /
|
||||
[Mac](Mac) / [Windows](Install-Wekan-from-source-on-Windows).
|
||||
[More Platforms](Platforms). [ARM progress](https://github.com/wekan/wekan/issues/1053#issuecomment-410919264).
|
||||
[Mac](../Platforms/Propietary/Mac.md) / [Windows](../Platforms/Propietary/Windows/Install-Wekan-from-source-on-Windows.md).
|
||||
[More Platforms](../Platforms/FOSS/Platforms.md). [ARM progress](https://github.com/wekan/wekan/issues/1053#issuecomment-410919264).
|
||||
- 1 GB RAM minimum free for Wekan. Production server should have miminum total 4 GB RAM.
|
||||
For thousands of users, for example with [Docker](https://github.com/wekan/wekan/blob/main/docker-compose.yml): 3 frontend servers,
|
||||
For thousands of users, for example with [Docker](../../docker-compose.yml): 3 frontend servers,
|
||||
each having 2 CPU and 2 wekan-app containers. One backend wekan-db server with many CPUs.
|
||||
- Enough disk space and alerts about low disk space. If you run out disk space, MongoDB database gets corrupted.
|
||||
- SECURITY: Updating to newest Wekan version very often. Please check you do not have automatic updates of Sandstorm or Snap turned off.
|
||||
Old versions have security issues because of old versions Node.js etc. Only newest Wekan is supported.
|
||||
Wekan on Sandstorm is not usually affected by any Standalone Wekan (Snap/Docker/Source) security issues.
|
||||
- [Reporting all new bugs immediately](https://github.com/wekan/wekan/issues).
|
||||
New features and fixes are added to Wekan [many times a day](https://github.com/wekan/wekan/blob/main/CHANGELOG.md).
|
||||
- [Backups](Backup) of Wekan database once a day miminum.
|
||||
New features and fixes are added to Wekan [many times a day](../../CHANGELOG.md).
|
||||
- [Backups](../Backup/Backup.md) of Wekan database once a day miminum.
|
||||
Bugs, updates, users deleting list or card, harddrive full, harddrive crash etc can eat your data. There is no undo yet.
|
||||
Some bug can cause Wekan board to not load at all, requiring manual fixing of database content.
|
||||
|
||||
@@ -65,7 +65,7 @@ It's a very specific niche, with limited amount competitors, with all of this ap
|
||||
[Lauri Ojansivu](https://github.com/xet7) is CEO at [WeKan Team](https://wekan.fi),
|
||||
Cross-Platform FOSS maintainer,
|
||||
Cloud Architect, Full-Stack Developer, SysAdmin and SysOp.
|
||||
He has experience of [having added and removed over 4 million lines of code](https://github.com/wekan/wekan/blob/main/releases/count-lines-of-code-per-committer.sh) to
|
||||
He has experience of [having added and removed over 4 million lines of code](../../releases/count-lines-of-code-per-committer.sh) to
|
||||
[Meteor Full-Stack Web Framework](https://www.meteor.com)
|
||||
based [WeKan Open Source kanban](https://wekan.fi),
|
||||
that has been [translated to 70+ languages](https://explore.transifex.com/wekan/wekan/),
|
||||
@@ -119,7 +119,7 @@ Usually:
|
||||
* Slowest: Once a month.
|
||||
* One release contains anything from one typo fix to many major features and bugfixes.
|
||||
|
||||
For [Wekan Platforms](Platforms), it means these choices:
|
||||
For [Wekan Platforms](../Platforms/FOSS/Platforms.md), it means these choices:
|
||||
|
||||
* Snap: You get updates automatically immediately, or updates automatically scheduled at midnight
|
||||
* Source/Docker/VirtualBox: You need to test and install new Wekan version yourself
|
||||
@@ -150,7 +150,7 @@ c) Develop feature yourself and submit it as pull requests to devel [Wekan repo]
|
||||
|
||||
For Sandstorm-specific features, have the feature enabled in Sandstorm by using environment variable isSandstorm = true like is at wekan/sandstorm.js .
|
||||
|
||||
In wiki there is [Developer Documentation](Developer-Documentation).
|
||||
In wiki there is [Developer Documentation](../DeveloperDocs/Developer-Documentation.md).
|
||||
|
||||
## Will you accept my pull request?
|
||||
We totally rely on pull requests for new features and bug fixes. If your pull request works, it's very likely to be accepted by xet7.
|
||||
@@ -176,7 +176,7 @@ Yes, Libreboard was the old project name, which superseded the even older projec
|
||||
After renaming it to Libreboard, a [new logo] was designed and the project continued to live on as Libreboard. Unfortunately it turned out, that the new logo was apparently ripped-off from a [concept] published at Dribbble, and so a new logo had to be found. There were a lot of [ideas from the community][logo-ticket], and at the end Maxime [proposed][wekan-proposal] a completely new name, Wekan, together with a design proposal for a new logo.
|
||||
|
||||
## What was Wekan fork / Wefork?
|
||||
After 2016-09-02 there were no pull requests reviewed and integrated for nearly 2 months. At 2016-10-20 Wekan community created fork and started merging many bugfixes and new features into Wefork. 2017-01-29 Wekan author mquandalle gave access to Wekan and at 2017-01-31 xet7 started merging Wefork back to Wekan. 2017-02-08 All of Wefork is now merged and moved back to official Wekan. Wefork will not accept any new issues and pull requests. All development happens on Wekan. [Wefork announcement and merging back](https://github.com/wekan/wekan/issues/640#issuecomment-276383458), more info: [Team](Team)
|
||||
After 2016-09-02 there were no pull requests reviewed and integrated for nearly 2 months. At 2016-10-20 Wekan community created fork and started merging many bugfixes and new features into Wefork. 2017-01-29 Wekan author mquandalle gave access to Wekan and at 2017-01-31 xet7 started merging Wefork back to Wekan. 2017-02-08 All of Wefork is now merged and moved back to official Wekan. Wefork will not accept any new issues and pull requests. All development happens on Wekan. [Wefork announcement and merging back](https://github.com/wekan/wekan/issues/640#issuecomment-276383458), more info: [Team](Team.md)
|
||||
|
||||
## What is the difference between Wekan and Trello?
|
||||
The main difference between the two is that Wekan is completely open source and available under the permissive MIT license. That makes it possible to host it on your own server (or your company's or organization's server) and you keep the full control over all data. No need to fear it will disappear some day, like a commercial service like Trello could.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[Other things that are not related to WeKan kanban](https://github.com/wekan/wekan/wiki/NOT-related-to-Wekan)
|
||||
[Other things that are not related to WeKan kanban](NOT-related-to-Wekan.md)
|
||||
|
||||
## Description
|
||||
|
||||
|
||||
+12
-12
@@ -4,7 +4,7 @@
|
||||
|
||||
### If you are in a hurry, please don't use IRC
|
||||
|
||||
Instead, [please search existing open and closed issues or add new issue to Wekan Feature Requests and Bugs](https://github.com/wekan/wekan/issues) (open issues have not been solved yet), or alternatively [read docs](https://github.io/wekan/wekan/wiki). Thanks!
|
||||
Instead, [please search existing open and closed issues or add new issue to Wekan Feature Requests and Bugs](https://github.com/wekan/wekan/issues) (open issues have not been solved yet), or alternatively [read docs](../README.md). Thanks!
|
||||
|
||||
### Required license to enter IRC channel
|
||||
|
||||
@@ -145,9 +145,9 @@ A:
|
||||
- Do you have time to be Wekan co-maintainer?
|
||||
- What is URL to your fork? Please send it to me with email to x@xet7.org . Thanks! Currently there is about 2200 forks of Wekan, it would be hard to find without exact URL.
|
||||
- What features and fixes have you implemented to Wekan?
|
||||
- [What usually happens when Wekan gets broken](Test-Edge#what-usually-happens-when-wekan-gets-broken).
|
||||
- [What usually happens when Wekan gets broken](../DeveloperDocs/Test-Edge.md#what-usually-happens-when-wekan-gets-broken).
|
||||
- [Benefits of contributing your features to upstream Wekan](https://wekan.fi/blog/2018/02/benefits-of-contributing-your-features-to-upstream-wekan/index.html).
|
||||
- [What was Wekan fork](FAQ#what-was-wekan-fork--wefork).
|
||||
- [What was Wekan fork](FAQ.md#what-was-wekan-fork--wefork).
|
||||
|
||||
***
|
||||
### Q: Other mobile browsers?
|
||||
@@ -312,10 +312,10 @@ Without that info it's kind of hard to debug.
|
||||
You could try export that board to Wekan JSON, and then import that Wekan JSON.
|
||||
|
||||
I do have server at AWS LightSail that has 4 GB RAM and 60 GB SSD,
|
||||
running [Snap versions of Wekan and RocketChat installed this way](OAuth2)
|
||||
running [Snap versions of Wekan and RocketChat installed this way](../Login/OAuth2.md)
|
||||
on same server, and it does not crash.
|
||||
|
||||
BTW, I did yesterday release [Wekan v2.95 with these new features](https://github.com/wekan/wekan/blob/main/CHANGELOG.md#v295-2019-07-01-wekan-release).
|
||||
BTW, I did yesterday release [Wekan v2.95 with these new features](../../CHANGELOG.md#v295-2019-07-01-wekan-release).
|
||||
|
||||
***
|
||||
### Q: Board cleanup?
|
||||
@@ -338,7 +338,7 @@ for answers a little later
|
||||
[02:25:24] <Rojola1> xet7?
|
||||
[02:25:27] <Rojola1> anyone?
|
||||
```
|
||||
A: I was at meeting or sleeping when you asked. When I came back to IRC you were not online. Anyway, you can [make backup](Backup) and do some [cleanup](https://github.com/wekan/wekan-cleanup) to delete 1,5 years worth of activities etc. Next time, if you have questions, you get faster answers at [GitHub issues](https://github.com/wekan/wekan/issues), because I get email notifications of all new issues and comments on issues.
|
||||
A: I was at meeting or sleeping when you asked. When I came back to IRC you were not online. Anyway, you can [make backup](../Backup/Backup.md) and do some [cleanup](https://github.com/wekan/wekan-cleanup) to delete 1,5 years worth of activities etc. Next time, if you have questions, you get faster answers at [GitHub issues](https://github.com/wekan/wekan/issues), because I get email notifications of all new issues and comments on issues.
|
||||
|
||||
***
|
||||
### Q: Rules as code?
|
||||
@@ -405,7 +405,7 @@ from one card to another.
|
||||
|
||||
### Q: LDAP?
|
||||
|
||||
A: [LDAP for Standalone is now available](LDAP).
|
||||
A: [LDAP for Standalone is now available](../Login/LDAP.md).
|
||||
|
||||
```
|
||||
[17:56:10] <regdude> Hi! I'm trying to setup Wekan with LDAP,
|
||||
@@ -442,7 +442,7 @@ A: Yes. Click right top your username / My Cards.
|
||||
[20:49:35] <Rojola> but, in reality I can not access it
|
||||
```
|
||||
|
||||
A: See 4) at https://github.com/wekan/wekan/wiki/Adding-users . This is Stanalone Wekan (Snap, Docker, Source) email setting. This has nothing to do with Sandstorm.
|
||||
A: See 4) at [Adding users](../Login/Adding-users.md) . This is Stanalone Wekan (Snap, Docker, Source) email setting. This has nothing to do with Sandstorm.
|
||||
|
||||
***
|
||||
|
||||
@@ -476,7 +476,7 @@ from Snap on Ubuntu 18.04.
|
||||
[17:38:51] <k_sze> Like, I get 502 Bad Gateway from my nginx reverse proxy
|
||||
```
|
||||
|
||||
A: Did you set your domain SPF records (as TXT records) and DKIM records on your domain that your SMTP server uses? Problem is not in Wekan, it's your SMTP server. For example AWS SES works. Also see [Troubleshooting Email](Troubleshooting-Mail).
|
||||
A: Did you set your domain SPF records (as TXT records) and DKIM records on your domain that your SMTP server uses? Problem is not in Wekan, it's your SMTP server. For example AWS SES works. Also see [Troubleshooting Email](../Email/Troubleshooting-Mail.md).
|
||||
|
||||
***
|
||||
|
||||
@@ -487,7 +487,7 @@ other applications
|
||||
[03:32:50] <ajay> i am want to integrate it in QGIS for task management
|
||||
[03:33:01] <ajay> *i want
|
||||
```
|
||||
A: Use [Wekan REST API](REST-API). For example, see [Wekan Gogs integration](https://github.com/wekan/wekan-gogs). You can also use [Outgoing Webhooks](Outgoing-Webhook-to-Discord) to send data to some Incoming Webhook. There is also [IFTTT Rules](IFTTT) for some automations.
|
||||
A: Use [Wekan REST API](../API/REST-API.md). For example, see [Wekan Gogs integration](https://github.com/wekan/wekan-gogs). You can also use [Outgoing Webhooks](../Webhooks/Discord/Outgoing-Webhook-to-Discord.md) to send data to some Incoming Webhook. There is also [IFTTT Rules](../Features/IFTTT/IFTTT.md) for some automations.
|
||||
|
||||
***
|
||||
|
||||
@@ -602,7 +602,7 @@ yogab> Hi! How are you today ? Can i ask
|
||||
a docker x reverse proxy x wekan question here ?
|
||||
```
|
||||
|
||||
A: Hmm, it seems you did not ask. Just guessing, maybe it's about [Traefik reverse proxy and Docker](Traefik-and-self-signed-SSL-certs).
|
||||
A: Hmm, it seems you did not ask. Just guessing, maybe it's about [Traefik reverse proxy and Docker](../Webserver/Traefik-and-self-signed-SSL-certs.md).
|
||||
|
||||
***
|
||||
|
||||
@@ -610,7 +610,7 @@ A: Hmm, it seems you did not ask. Just guessing, maybe it's about [Traefik rever
|
||||
|
||||
Wow, you are so cool ! You gonna be an expert guru soon.
|
||||
|
||||
Check out here for more wild stuff: https://github.com/wekan/wekan/wiki
|
||||
Check out here for more wild stuff: [Wekan documentation](../README.md)
|
||||
|
||||
There is:
|
||||
- Info about translations, dev stuff, changelog, and huge amount of Wekan features.
|
||||
@@ -1,4 +1,4 @@
|
||||
Also: [Hall of Shame: Fake companies](https://github.com/wekan/wekan/wiki/Hall-of-Shame)
|
||||
Also: [Hall of Shame: Fake companies](Hall-of-Shame.md)
|
||||
|
||||
## These are NOT in any way related to WeKan
|
||||
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ Maxime Quandalle, original author of Wekan
|
||||
|
||||
* [EthKan](https://github.com/EthKan)
|
||||
|
||||
* [Friend](Friend)
|
||||
* [Friend](../Platforms/FOSS/Friend/Friend.md)
|
||||
|
||||
# In Spanish
|
||||
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
Please read Etiquette at [FAQ](FAQ) and [Developer Documentation](Developer-Documentation).
|
||||
Please read Etiquette at [FAQ](FAQ.md) and [Developer Documentation](../DeveloperDocs/Developer-Documentation.md).
|
||||
|
||||
# New Roadmap
|
||||
|
||||
@@ -15,7 +15,7 @@ GitHub Username | Feature | Status | Sponsor
|
||||
[xet7](https://github.com/xet7) | [Calendar View](https://github.com/wekan/wekan/issues/808) | In Progress | [Bountysource](https://www.bountysource.com/issues/41780513-add-feature-calendar-view)
|
||||
[GhassenRjab](https://github.com/GhassenRjab) | [Search](https://github.com/wekan/wekan/issues/552) | In Progress | [Bountysource](https://www.bountysource.com/issues/32132712-feature-request-search-items) and nm11
|
||||
[Serubin](https://github.com/Serubin) | [Top-level projects](https://github.com/wekan/wekan/issues/641) | In Progress | [Bountysource](https://www.bountysource.com/issues/36035028-top-level-projects)
|
||||
[xet7](https://github.com/xet7), [thuanpq ](https://github.com/thuanpq) | [Teams/Organizations similar to Trello](https://github.com/wekan/wekan/issues/802) . Beginnings of user admin [already implemented](https://github.com/wekan/wekan/pull/1325) at [Wekan v0.55](https://github.com/wekan/wekan/blob/main/CHANGELOG.md#v055-2017-11-19-wekan-release) | In Progress | [FUUG.fi](https://fuug.fi/2017/wekan-kanban-taulun-perustoimintojen-kehitys-alkuun/) and [Bountysource](https://www.bountysource.com/issues/41780380-add-feature-teams-organizations-similar-to-trello)
|
||||
[xet7](https://github.com/xet7), [thuanpq ](https://github.com/thuanpq) | [Teams/Organizations similar to Trello](https://github.com/wekan/wekan/issues/802) . Beginnings of user admin [already implemented](https://github.com/wekan/wekan/pull/1325) at [Wekan v0.55](../../CHANGELOG.md#v055-2017-11-19-wekan-release) | In Progress | [FUUG.fi](https://fuug.fi/2017/wekan-kanban-taulun-perustoimintojen-kehitys-alkuun/) and [Bountysource](https://www.bountysource.com/issues/41780380-add-feature-teams-organizations-similar-to-trello)
|
||||
[andresmanelli](https://github.com/andresmanelli) | [Gogs issue/commit integration](https://github.com/wekan/wekan/issues/253) | [Part 1 in Wekan already](https://github.com/wekan/wekan/pull/1189) |
|
||||
[papoola](https://github.com/papoola) | [Custom Fields](https://github.com/wekan/wekan/issues/807) | In Progress |
|
||||
[mfshiu](https://github.com/mfshiu) | [Custom Fields, Moving card on mobile phone, Filter cards by keyword](https://github.com/wekan/wekan/issues/1022#issuecomment-337646110), Integrate Wekan with [RIOT](http://riot.im) so user can be notified immediately on cell phone when Wekan cards change | Works, no pull request yet
|
||||
|
||||
+2
-2
@@ -15,7 +15,7 @@ GitHub Username | Location | Position | Details
|
||||
|
||||
# Current most frequent Contributors
|
||||
|
||||
Also, see newest contributors at [ChangeLog](https://github.com/wekan/wekan/blob/main/CHANGELOG.md)
|
||||
Also, see newest contributors at [ChangeLog](../../CHANGELOG.md)
|
||||
|
||||
GitHub Username | Location | Position | Details
|
||||
------------ | ------------ | ------------ | ------------
|
||||
@@ -52,7 +52,7 @@ GitHub Username | Chat Username | Location | Position | Details
|
||||
[liske](https://github.com/liske) | Germany | Contributor | Added card REST API fix, card time fix etc.
|
||||
[rhelsing](https://github.com/rhelsing) | ryan.helsing | USA | Contributor | [Comment permissions](https://github.com/wekan/wekan/pull/1008)
|
||||
[BaobabCoder](https://github.com/BaobabCoder) | BaobabCoder | France | Contributor | [Delete List](https://github.com/wekan/wekan/pull/1050)
|
||||
[whodafly](https://github.com/whodafly) | superfly | USA | Contributor | [Troubleshooting Mail](Troubleshooting-Mail), [Import Checklists from Trello](https://github.com/wekan/wekan/pull/894)
|
||||
[whodafly](https://github.com/whodafly) | superfly | USA | Contributor | [Troubleshooting Mail](../Email/Troubleshooting-Mail.md), [Import Checklists from Trello](https://github.com/wekan/wekan/pull/894)
|
||||
[stephenmoloney](https://github.com/stephenmoloney) | - | Ireland | Contributor | Dockerfile and Travis config, Meteor 1.4 and Node 4.x port
|
||||
[GhassenRjab](https://github.com/GhassenRjab) | ghassen.rjab | Universe | Contributor | [Fix Export Board](https://github.com/wekan/wekan/pull/1067), [Fix "W" shortcut](https://github.com/wekan/wekan/pull/1066), [Sort languages](https://github.com/wekan/wekan/pull/1070), [Add Breton language name](https://github.com/wekan/wekan/pull/1071), [Edit card description with invalid markdown](https://github.com/wekan/wekan/pull/1073), [Fix importing Trello board](https://github.com/wekan/wekan/pull/1080), [Import due date from Trello](https://github.com/wekan/wekan/pull/1082), [Import Wekan board](https://github.com/wekan/wekan/pull/1117), [Export and import attachments as base64 encoded files](https://github.com/wekan/wekan/pull/1134), [Export and import checklists](https://github.com/wekan/wekan/pull/1140), [Export and import boards in Sandstorm](https://github.com/wekan/wekan/pull/1144), [FIX SECURITY ISSUE Files accessible without authentication](https://github.com/wekan/wekan/issues/1105)
|
||||
[nztqa](https://github.com/nztqa) | - | Universe | Contributor | [Delete card when deleting list](https://github.com/wekan/wekan/pull/1054), [Disable unnecessary link feature](https://github.com/wekan/wekan/pull/1042), [Fix IE 11 drag board to scroll](https://github.com/wekan/wekan/pull/1052), [Fix filter reset on moving between starred boards](https://github.com/wekan/wekan/pull/1074), [Fix Admin Panel link available to all users, only link is hidden](https://github.com/wekan/wekan/pull/1076), [Fix Case-sensitive email handling](https://github.com/wekan/wekan/issues/675), [Fix admin panel route for subfolder](https://github.com/wekan/wekan/pull/1084), [Fix i18n route for subfolder](https://github.com/wekan/wekan/pull/1085), [Fix incorrect attachment link with subfolder in the url](https://github.com/wekan/wekan/pull/1086), [Fix link to card](https://github.com/wekan/wekan/pull/1087), [Fix duplicate id generation](https://github.com/wekan/wekan/pull/1093), [When finished input of checklist item, open new checklist item](https://github.com/wekan/wekan/pull/1099), [Fix Possible to add empty item to checklist](https://github.com/wekan/wekan/pull/1107), [Improve UI design of checklist items](https://github.com/wekan/wekan/pull/1108), [Outgoing Webhooks](https://github.com/wekan/wekan/pull/1119), [Adding user to board causes node.js to crash](https://github.com/wekan/wekan/issues/1131), [Fix double shashes](https://github.com/wekan/wekan/issues/962), [GitHub Issue template](https://github.com/wekan/wekan/pull/1146), [Fix showing card activity history in IE11](https://github.com/wekan/wekan/pull/1152), [Add display Wekan version number and runtime environment to Admin Panel](https://github.com/wekan/wekan/pull/1156), [Change Email address](https://github.com/wekan/wekan/pull/1161)
|
||||
|
||||
@@ -98,7 +98,7 @@ and toggle keyboard shortcuts on or off from the board sidebar.
|
||||
Cross-cutting accessibility guarantees (page language, skip link, landmark roles,
|
||||
visible focus, dialog roles, accessible names on icon controls, and absence of
|
||||
duplicate element ids) are covered by the end-to-end test suite in
|
||||
[`tests/playwright/specs/19-accessibility.e2e.js`](https://github.com/wekan/wekan/blob/main/tests/playwright/specs/19-accessibility.e2e.js).
|
||||
[`tests/playwright/specs/19-accessibility.e2e.js`](../../../tests/playwright/specs/19-accessibility.e2e.js).
|
||||
|
||||
## Reporting accessibility issues
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ member menu (top right corner) when you are an admin.
|
||||
|
||||
## Admin Panel
|
||||
|
||||
On the Source and Docker platforms the [Admin Panel](https://github.com/wekan/wekan/blob/main/CHANGELOG.md#v0111-rc2-2017-03-05-wekan-prerelease)
|
||||
On the Source and Docker platforms the [Admin Panel](../../../CHANGELOG.md#v0111-rc2-2017-03-05-wekan-prerelease)
|
||||
lets you:
|
||||
|
||||
- Allow self-registration, or switch to **invite-only** and invite users to boards.
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
[Wekan v5.55 and newer](https://github.com/wekan/wekan/blob/main/CHANGELOG.md#v555-2021-08-31-wekan-release)
|
||||
[Wekan v5.55 and newer](../../CHANGELOG.md#v555-2021-08-31-wekan-release)
|
||||
|
||||
<img src="allow-private-boards-only.png" width="60%" alt="Allow private boards only" />
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## To translators, about the word Custom
|
||||
|
||||
See other feature for description of Custom https://github.com/wekan/wekan/wiki/Customize-Translations
|
||||
See other feature for description of Custom [Customize Translations](../../Translations/Customize-Translations.md)
|
||||
|
||||
## 1) Click: Board hamburger menu / Custom Fields
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ cd wekan
|
||||
```
|
||||
That does: 1 install dependencies, 2 builds wekan, 3 starts wekan in development mode with command `meteor`, so it can detect if some file changes and try to rebuild automatically and reload webbroser. But, still sometimes, it may need stopping with Ctrl-c and full build with option 2.
|
||||
|
||||
And then [register and login](Adding-users) at http://localhost:4000
|
||||
And then [register and login](../Login/Adding-users.md) at http://localhost:4000
|
||||
|
||||
### OPTIONAL, NOT NEEDED: 5. Add new plugin package
|
||||
```
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
This page is an index of WeKan features. It used to be one long page; it has been
|
||||
split into focused topic pages under [`docs/Features/`](.).
|
||||
|
||||
Not every feature from the [CHANGELOG](https://github.com/wekan/wekan/blob/main/CHANGELOG.md)
|
||||
Not every feature from the [CHANGELOG](../../CHANGELOG.md)
|
||||
is documented yet — contributions are welcome.
|
||||
|
||||
## Kanban basics
|
||||
@@ -89,10 +89,10 @@ is documented yet — contributions are welcome.
|
||||
## Versions of Meteor and Node
|
||||
|
||||
WeKan tracks current Meteor and Node.js releases. As of WeKan 8.75 and newer it uses
|
||||
**Meteor 3.5-beta.x** and **Node.js 24.x**, with **MongoDB 7.x (or 6.x)** or
|
||||
**Meteor 3.5-rc.x** and **Node.js 24.x**, with **MongoDB 7.x (or 6.x)** or
|
||||
[FerretDB2/PostgreSQL](../Databases/FerretDB2-PostgreSQL.md). WeKan 8.43 upgraded to
|
||||
Meteor 3.x. For the exact versions of each release, see the
|
||||
[CHANGELOG](https://github.com/wekan/wekan/blob/main/CHANGELOG.md).
|
||||
[CHANGELOG](../../CHANGELOG.md).
|
||||
|
||||
## Roadmap and feature requests
|
||||
|
||||
|
||||
@@ -18,4 +18,4 @@ Previous GPLv2 WeKan Gantt is deprecated https://github.com/wekan/wekan-gantt-gp
|
||||
|
||||
# UCS
|
||||
|
||||
[Gantt feature at UCS](UCS#gantt)
|
||||
[Gantt feature at UCS](../Platforms/FOSS/UCS/UCS.md#gantt)
|
||||
@@ -85,7 +85,7 @@ are embedded inline so each rule is self-contained.
|
||||
}
|
||||
```
|
||||
|
||||
Python helper ([`api.py`](https://github.com/wekan/wekan/blob/main/api.py)):
|
||||
Python helper ([`api.py`](../../../api.py)):
|
||||
|
||||
```bash
|
||||
python3 api.py listrules BOARDID
|
||||
|
||||
+20
-3
@@ -1,10 +1,27 @@
|
||||
[Original issue](https://github.com/wekan/wekan/issues/4589)
|
||||
# LaTeX math
|
||||
|
||||
[Commit](https://github.com/wekan/wekan/commit/82fa632197a0e3b88d26c557f1dc8cc18b05b18b)
|
||||
WeKan renders LaTeX math in any markdown field (card descriptions, comments,
|
||||
checklists, etc.).
|
||||
|
||||
## How it works
|
||||
|
||||
Math is rendered with [Temml](https://temml.org), which converts LaTeX to
|
||||
**native [MathML](https://developer.mozilla.org/docs/Web/MathML)**. Modern
|
||||
browsers display MathML themselves, so no heavy client-side math engine is
|
||||
shipped in the bundle. The markdown ↔ math bridge is
|
||||
[markdown-it-math](https://www.npmjs.com/package/markdown-it-math), and the
|
||||
generated MathML is allow-listed in WeKan's DOMPurify sanitizer so it is not
|
||||
stripped.
|
||||
|
||||
Earlier WeKan versions used `markdown-it-mathjax3` (which bundled all of MathJax
|
||||
and had a "renders twice" bug). Math rendering was then accidentally dropped
|
||||
during the Meteor 3 refactor and has now been restored on Temml.
|
||||
|
||||
- [Original issue](https://github.com/wekan/wekan/issues/4589)
|
||||
|
||||
## Examples
|
||||
|
||||
[Examples From NPM package](https://www.npmjs.com/package/markdown-it-mathjax3)
|
||||
[Temml supported functions](https://temml.org/docs/en/supported)
|
||||
|
||||
**Inline**
|
||||
|
||||
|
||||
Loaded 100 of 393 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user