Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b8939c02e | ||
|
|
86e5356b0f | ||
|
|
c0f748bcb5 | ||
|
|
0acb8f5d69 | ||
|
|
37db870475 | ||
|
|
0d5f33299e | ||
|
|
78f818bbb0 | ||
|
|
edafb6a6d2 | ||
|
|
c0984aa740 | ||
|
|
6253bbdc0b | ||
|
|
254a9abad2 | ||
|
|
83e77f8d82 | ||
|
|
2726643586 | ||
|
|
b8f2a468d8 | ||
|
|
fb771d46dd | ||
|
|
ecd4c7fb02 | ||
|
|
da2be59185 | ||
|
|
93a3fa9c75 | ||
|
|
cf546c71e3 | ||
|
|
2791b7da22 | ||
|
|
48fc442b85 | ||
|
|
2e6b18dfa7 | ||
|
|
7f0da49e0a | ||
|
|
b5124d0f6a | ||
|
|
e70a1f0a60 | ||
|
|
7e8475e64d | ||
|
|
e161ada214 | ||
|
|
409b8559d7 | ||
|
|
d74dc92681 | ||
|
|
5f62d7307f | ||
|
|
3866ed3196 | ||
|
|
8059856c39 | ||
|
|
a3518a3bcf | ||
|
|
7b8d67de63 | ||
|
|
7a5aa469cd | ||
|
|
1e415b38d2 | ||
|
|
ff626fb559 |
No files matched your search
+1
-1
@@ -72,7 +72,7 @@ horka:swipebox@1.0.2
|
||||
hot-code-push@1.0.4
|
||||
html-tools@1.0.11
|
||||
htmljs@1.0.11
|
||||
http@1.4.2
|
||||
http@1.4.3
|
||||
id-map@1.1.0
|
||||
idmontie:migrations@1.0.3
|
||||
inter-process-messaging@0.1.1
|
||||
|
||||
@@ -1,3 +1,31 @@
|
||||
# v4.80 2021-01-18 Wekan release
|
||||
|
||||
This release adds the following improvements:
|
||||
|
||||
- [Global Search fixes and updates](https://github.com/wekan/wekan/pulls/3437).
|
||||
Thanks to jrsupplee.
|
||||
- [Use table-cell instead of inline-block in my-cards-list-wrapper CSS](https://github.com/wekan/wekan/commit/3866ed31965eb5b722e88c4d3e7628d516375088).
|
||||
Thanks to johappel and xet7.
|
||||
- [Use multi stage build based on Node images](https://github.com/wekan/wekan/pull/3438).
|
||||
Thanks to GavinLilly.
|
||||
- [Try to use buster base images, because when using Wekan Alpine, registering new user of Wekan does not work,
|
||||
maybe because of glibc/musl](https://github.com/wekan/wekan/commit/254a9abad2dec620d95c02ac9209e9f569407986).
|
||||
Thanks to GavinLilly and xet7.
|
||||
- [Use MongoDB setFeatureCompatibilityVersion 4.2 on Snap. TODO: Docker](https://github.com/wekan/wekan/commit/2791b7da22ddb0ff5588eca56f1dc90ff5ffdd2d).
|
||||
Thanks to GuidoDr and xet7.
|
||||
|
||||
and adds the following updates:
|
||||
|
||||
- [Update dependencies](https://github.com/wekan/wekan/commit/c0f748bcb5dfebe7fa90be647a1ed23f0edcc304).
|
||||
Thanks to developers of dependencies.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Fixed Linked card makes board not load when CustomField definition is undefined](https://github.com/wekan/wekan/commit/0d5f33299ee25e1bee4ca4fc3b3c2483c29e367c).
|
||||
Thanks to olivierlambert and xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v4.79 2021-01-17 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
+45
-193
@@ -1,26 +1,46 @@
|
||||
FROM quay.io/wekan/ubuntu:groovy-20201125.2
|
||||
FROM quay.io/wekan/node:12.20.1-buster AS builder
|
||||
# This is our builder image. It will take the Wekan meteor code and
|
||||
# build it into a standalone NodeJS package
|
||||
|
||||
LABEL maintainer="wekan"
|
||||
|
||||
# 2020-12-03:
|
||||
# - Above Ubuntu base image copied from Docker Hub ubuntu:groovy-20201125.2
|
||||
# to Quay to avoid Docker Hub rate limits.
|
||||
# Allow Meteor to run inside the container as container runs as root
|
||||
ENV METEOR_ALLOW_SUPERUSER=true
|
||||
|
||||
# Set the environment variables (defaults where required)
|
||||
# DOES NOT WORK: paxctl fix for alpine linux: https://github.com/wekan/wekan/issues/1303
|
||||
# ENV BUILD_DEPS="paxctl"
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
# Copy our source code into the container
|
||||
COPY . /usr/src/app
|
||||
|
||||
ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-essential git ca-certificates python3" \
|
||||
DEBUG=false \
|
||||
NODE_VERSION=v12.20.1 \
|
||||
METEOR_RELEASE=1.10.2 \
|
||||
USE_EDGE=false \
|
||||
METEOR_EDGE=1.5-beta.17 \
|
||||
NPM_VERSION=latest \
|
||||
FIBERS_VERSION=4.0.1 \
|
||||
ARCHITECTURE=linux-x64 \
|
||||
SRC_PATH=./ \
|
||||
WITH_API=true \
|
||||
# Allow the user accounts package to work with more Meteor versions
|
||||
RUN sed -i 's/api\.versionsFrom/\/\/api.versionsFrom/' /usr/src/app/packages/meteor-useraccounts-core/package.js
|
||||
|
||||
# Move our current working directory
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Install our NPM packages
|
||||
RUN npm install
|
||||
|
||||
# Install Meteor
|
||||
RUN curl "https://install.meteor.com/" | sh
|
||||
|
||||
# Build our Meteor package into a standalone NodeJS package
|
||||
RUN meteor build --directory /build
|
||||
|
||||
# Include the fix for CFS
|
||||
RUN cp /usr/src/app/fix-download-unicode/cfs_access-point.txt /build/bundle/programs/server/packages/cfs_access-point.js
|
||||
|
||||
# Install server side NPM depedencies
|
||||
RUN cd /build/bundle/programs/server/ && \
|
||||
npm install
|
||||
|
||||
# Remove the legacy web browser bundle, so that Wekan works in Android Firefox, iOS Safari, etc.
|
||||
RUN rm -rf /build/bundle/programs/web.browser.legacy
|
||||
|
||||
|
||||
FROM quay.io/wekan/node:12.20.1-buster-slim as app
|
||||
# This is our runtime image. It takes the built NodeJS package and runs it using
|
||||
# the lighter Alpine NodeJS base image to keep image size down
|
||||
|
||||
ENV WITH_API=true \
|
||||
ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURES_BEFORE=3 \
|
||||
ACCOUNTS_LOCKOUT_KNOWN_USERS_PERIOD=60 \
|
||||
ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURE_WINDOW=15 \
|
||||
@@ -138,182 +158,14 @@ ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-
|
||||
SAML_ATTRIBUTES="" \
|
||||
ORACLE_OIM_ENABLED=false
|
||||
|
||||
# Copy the app to the image
|
||||
COPY ${SRC_PATH} /home/wekan/app
|
||||
# Copy our built NodeJS package to this image
|
||||
COPY --from=builder /build/bundle /bundle
|
||||
|
||||
RUN \
|
||||
set -o xtrace && \
|
||||
# Add non-root user wekan
|
||||
useradd --user-group --system --home-dir /home/wekan wekan && \
|
||||
\
|
||||
# OS dependencies
|
||||
apt-get update -y && apt-get install -y --no-install-recommends ${BUILD_DEPS} && \
|
||||
#pip3 install -U pip setuptools wheel && \
|
||||
\
|
||||
# Meteor installer doesn't work with the default tar binary, so using bsdtar while installing.
|
||||
# https://github.com/coreos/bugs/issues/1095#issuecomment-350574389
|
||||
cp $(which tar) $(which tar)~ && \
|
||||
ln -sf $(which bsdtar) $(which tar) && \
|
||||
\
|
||||
# Download nodejs
|
||||
wget https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-${ARCHITECTURE}.tar.gz && \
|
||||
wget https://nodejs.org/dist/${NODE_VERSION}/SHASUMS256.txt.asc && \
|
||||
#---------------------------------------------------------------------------------------------
|
||||
# Node Fibers 100% CPU usage issue:
|
||||
# https://github.com/wekan/wekan-mongodb/issues/2#issuecomment-381453161
|
||||
# https://github.com/meteor/meteor/issues/9796#issuecomment-381676326
|
||||
# https://github.com/sandstorm-io/sandstorm/blob/0f1fec013fe7208ed0fd97eb88b31b77e3c61f42/shell/server/00-startup.js#L99-L129
|
||||
# Also see beginning of wekan/server/authentication.js
|
||||
# import Fiber from "fibers";
|
||||
# Fiber.poolSize = 1e9;
|
||||
# OLD: Download node version 8.12.0 prerelease that has fix included, => Official 8.12.0 has been released
|
||||
# Description at https://releases.wekan.team/node.txt
|
||||
#wget https://releases.wekan.team/node-${NODE_VERSION}-${ARCHITECTURE}.tar.gz && \
|
||||
#echo "1ed54adb8497ad8967075a0b5d03dd5d0a502be43d4a4d84e5af489c613d7795 node-v8.12.0-linux-x64.tar.gz" >> SHASUMS256.txt.asc && \
|
||||
\
|
||||
# Verify nodejs authenticity
|
||||
grep ${NODE_VERSION}-${ARCHITECTURE}.tar.gz SHASUMS256.txt.asc | shasum -a 256 -c - && \
|
||||
#export GNUPGHOME="$(mktemp -d)" && \
|
||||
#\
|
||||
# Try other key servers if ha.pool.sks-keyservers.net is unreachable
|
||||
# Code from https://github.com/chorrell/docker-node/commit/2b673e17547c34f17f24553db02beefbac98d23c
|
||||
# gpg keys listed at https://github.com/nodejs/node#release-team
|
||||
# and keys listed here from previous version of this Dockerfile
|
||||
#for key in \
|
||||
#9554F04D7259F04124DE6B476D5A82AC7E37093B \
|
||||
#94AE36675C464D64BAFA68DD7434390BDBE9B9C5 \
|
||||
#FD3A5288F042B6850C66B31F09FE44734EB7990E \
|
||||
#71DCFD284A79C3B38668286BC97EC7A07EDE3FC1 \
|
||||
#DD8F2338BAE7501E3DD5AC78C273792F7D83545D \
|
||||
#C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 \
|
||||
#B9AE9905FFD7803F25714661B63B535A4C206CA9 \
|
||||
#; do \
|
||||
#gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$key" || \
|
||||
#gpg --keyserver pgp.mit.edu --recv-keys "$key" || \
|
||||
#gpg --keyserver keyserver.pgp.com --recv-keys "$key" ; \
|
||||
#done && \
|
||||
#gpg --verify SHASUMS256.txt.asc && \
|
||||
# Ignore socket files then delete files then delete directories
|
||||
#find "$GNUPGHOME" -type f | xargs rm -f && \
|
||||
#find "$GNUPGHOME" -type d | xargs rm -fR && \
|
||||
rm -f SHASUMS256.txt.asc && \
|
||||
\
|
||||
# Install Node
|
||||
tar xvzf node-${NODE_VERSION}-${ARCHITECTURE}.tar.gz && \
|
||||
rm node-${NODE_VERSION}-${ARCHITECTURE}.tar.gz && \
|
||||
mv node-${NODE_VERSION}-${ARCHITECTURE} /opt/nodejs && \
|
||||
ln -s /opt/nodejs/bin/node /usr/bin/node && \
|
||||
ln -s /opt/nodejs/bin/npm /usr/bin/npm && \
|
||||
mkdir -p /opt/nodejs/lib/node_modules/fibers/.node-gyp /root/.node-gyp/8.16.1 /home/wekan/.config && \
|
||||
chown wekan --recursive /home/wekan/.config && \
|
||||
\
|
||||
#DOES NOT WORK: paxctl fix for alpine linux: https://github.com/wekan/wekan/issues/1303
|
||||
#paxctl -mC `which node` && \
|
||||
\
|
||||
# Install Node dependencies. Python path for node-gyp.
|
||||
npm install -g npm@${NPM_VERSION} && \
|
||||
#npm config set python python2.7 && \
|
||||
#npm install -g node-gyp && \
|
||||
#npm install -g fibers@${FIBERS_VERSION} && \
|
||||
\
|
||||
# Change user to wekan and install meteor
|
||||
cd /home/wekan/ && \
|
||||
chown wekan --recursive /home/wekan && \
|
||||
#curl "https://install.meteor.com" -o /home/wekan/install_meteor.sh && \
|
||||
#curl "https://install.meteor.com/?release=${METEOR_RELEASE}" -o /home/wekan/install_meteor.sh && \
|
||||
# OLD: sed -i "s|RELEASE=.*|RELEASE=${METEOR_RELEASE}\"\"|g" ./install_meteor.sh && \
|
||||
# Install Meteor forcing its progress
|
||||
#sed -i 's/VERBOSITY="--silent"/VERBOSITY="--progress-bar"/' ./install_meteor.sh && \
|
||||
echo "Starting meteor ${METEOR_RELEASE} installation... \n" && \
|
||||
gosu wekan:wekan curl https://install.meteor.com/ | /bin/sh && \
|
||||
mv /root/.meteor /home/wekan/ && \
|
||||
chown wekan --recursive /home/wekan/.meteor && \
|
||||
\
|
||||
# Check if opting for a release candidate instead of major release
|
||||
#if [ "$USE_EDGE" = false ]; then \
|
||||
#gosu wekan:wekan sh /home/wekan/install_meteor.sh; \
|
||||
# gosu wekan:wekan curl https://install.meteor.com/ | sh; \
|
||||
#else \
|
||||
# gosu wekan:wekan git clone --recursive --depth 1 -b release/METEOR@${METEOR_EDGE} https://github.com/meteor/meteor.git /home/wekan/.meteor; \
|
||||
#fi; \
|
||||
#\
|
||||
# Get additional packages
|
||||
#mkdir -p /home/wekan/app/packages && \
|
||||
#chown wekan:wekan --recursive /home/wekan && \
|
||||
# REPOS BELOW ARE INCLUDED TO WEKAN REPO
|
||||
#cd /home/wekan/app/packages && \
|
||||
#gosu wekan:wekan git clone --depth 1 -b master https://github.com/wekan/flow-router.git kadira-flow-router && \
|
||||
#gosu wekan:wekan git clone --depth 1 -b master https://github.com/meteor-useraccounts/core.git meteor-useraccounts-core && \
|
||||
#gosu wekan:wekan git clone --depth 1 -b master https://github.com/wekan/meteor-accounts-cas.git && \
|
||||
#gosu wekan:wekan git clone --depth 1 -b master https://github.com/wekan/wekan-ldap.git && \
|
||||
#gosu wekan:wekan git clone --depth 1 -b master https://github.com/wekan/wekan-scrollbar.git && \
|
||||
#gosu wekan:wekan git clone --depth 1 -b master https://github.com/wekan/meteor-accounts-oidc.git && \
|
||||
#gosu wekan:wekan git clone --depth 1 -b master --recurse-submodules https://github.com/wekan/markdown.git && \
|
||||
#gosu wekan:wekan mv meteor-accounts-oidc/packages/switch_accounts-oidc wekan-accounts-oidc && \
|
||||
#gosu wekan:wekan mv meteor-accounts-oidc/packages/switch_oidc wekan-oidc && \
|
||||
#gosu wekan:wekan rm -rf meteor-accounts-oidc && \
|
||||
sed -i 's/api\.versionsFrom/\/\/api.versionsFrom/' /home/wekan/app/packages/meteor-useraccounts-core/package.js && \
|
||||
cd /home/wekan/.meteor && \
|
||||
gosu wekan:wekan /home/wekan/.meteor/meteor -- help; \
|
||||
\
|
||||
# extract the OpenAPI specification
|
||||
#npm install -g api2html@0.3.3 && \
|
||||
#mkdir -p /home/wekan/python && \
|
||||
#chown wekan --recursive /home/wekan/python && \
|
||||
#cd /home/wekan/python && \
|
||||
#gosu wekan:wekan git clone --depth 1 -b master https://github.com/Kronuz/esprima-python && \
|
||||
#cd /home/wekan/python/esprima-python && \
|
||||
#python3 setup.py install --record files.txt && \
|
||||
#cd /home/wekan/app && \
|
||||
#mkdir -p /home/wekan/app/public/api && \
|
||||
#chown wekan --recursive /home/wekan/app && \
|
||||
#gosu wekan:wekan python3 ./openapi/generate_openapi.py --release $(git describe --tags --abbrev=0) > ./public/api/wekan.yml && \
|
||||
#gosu wekan:wekan /opt/nodejs/bin/api2html -c ./public/logo-header.png -o ./public/api/wekan.html ./public/api/wekan.yml; \
|
||||
# Build app
|
||||
cd /home/wekan/app && \
|
||||
mkdir -p /home/wekan/.npm && \
|
||||
chown wekan --recursive /home/wekan/.npm /home/wekan/.config /home/wekan/.meteor && \
|
||||
#gosu wekan:wekan /home/wekan/.meteor/meteor add standard-minifier-js && \
|
||||
gosu wekan:wekan npm install && \
|
||||
gosu wekan:wekan /home/wekan/.meteor/meteor build --directory /home/wekan/app_build && \
|
||||
cp /home/wekan/app/fix-download-unicode/cfs_access-point.txt /home/wekan/app_build/bundle/programs/server/packages/cfs_access-point.js && \
|
||||
#rm /home/wekan/app_build/bundle/programs/server/npm/node_modules/meteor/rajit_bootstrap3-datepicker/lib/bootstrap-datepicker/node_modules/phantomjs-prebuilt/lib/phantom/bin/phantomjs && \
|
||||
chown wekan /home/wekan/app_build/bundle/programs/server/packages/cfs_access-point.js && \
|
||||
#Removed binary version of bcrypt because of security vulnerability that is not fixed yet.
|
||||
#https://github.com/wekan/wekan/commit/4b2010213907c61b0e0482ab55abb06f6a668eac
|
||||
#https://github.com/wekan/wekan/commit/7eeabf14be3c63fae2226e561ef8a0c1390c8d3c
|
||||
#cd /home/wekan/app_build/bundle/programs/server/npm/node_modules/meteor/npm-bcrypt && \
|
||||
#gosu wekan:wekan rm -rf node_modules/bcrypt && \
|
||||
#gosu wekan:wekan npm install bcrypt && \
|
||||
#
|
||||
# Delete phantomjs
|
||||
#cd /home/wekan/app_build/bundle && \
|
||||
#find . -name "*phantomjs*" | xargs rm -rf && \
|
||||
#
|
||||
cd /home/wekan/app_build/bundle/programs/server/ && \
|
||||
gosu wekan:wekan npm install && \
|
||||
#gosu wekan:wekan npm install bcrypt && \
|
||||
# Remove legacy webbroser bundle, so that Wekan works also at Android Firefox, iOS Safari, etc.
|
||||
rm -rf /home/wekan/app_build/bundle/programs/web.browser.legacy && \
|
||||
mv /home/wekan/app_build/bundle /build && \
|
||||
\
|
||||
# Put back the original tar
|
||||
mv $(which tar)~ $(which tar) && \
|
||||
\
|
||||
# Cleanup
|
||||
apt-get remove --purge -y ${BUILD_DEPS} && \
|
||||
apt-get autoremove -y && \
|
||||
npm uninstall -g api2html &&\
|
||||
rm -R /var/lib/apt/lists/* && \
|
||||
rm -R /home/wekan/.meteor && \
|
||||
rm -R /home/wekan/app && \
|
||||
rm -R /home/wekan/app_build
|
||||
#cat /home/wekan/python/esprima-python/files.txt | xargs rm -R && \
|
||||
#rm -R /home/wekan/python
|
||||
#rm /home/wekan/install_meteor.sh
|
||||
WORKDIR /bundle
|
||||
|
||||
ENV PORT=8080
|
||||
EXPOSE $PORT
|
||||
USER wekan
|
||||
|
||||
CMD ["node", "/build/main.js"]
|
||||
USER node
|
||||
|
||||
CMD ["node", "/bundle/main.js"]
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
appId: wekan-public/apps/77b94f60-dec9-0136-304e-16ff53095928
|
||||
appVersion: "v4.79.0"
|
||||
appVersion: "v4.80.0"
|
||||
files:
|
||||
userUploads:
|
||||
- README.md
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
template(name="resultCard")
|
||||
.result-card-wrapper
|
||||
a.minicard-wrapper.card-title(href=card.absoluteUrl)
|
||||
a.minicard-wrapper.card-title(href=absoluteUrl)
|
||||
+minicard(this)
|
||||
//= card.title
|
||||
ul.result-card-context-list
|
||||
|
||||
Executable → Regular
File mode changed.
@@ -14,52 +14,26 @@ template(name="globalSearch")
|
||||
if currentUser
|
||||
.wrapper
|
||||
form.global-search-instructions.js-search-query-form
|
||||
input.global-search-query-input(type="text" name="searchQuery" placeholder="{{_ 'search-example'}}" autofocus dir="auto")
|
||||
input.global-search-query-input(type="text" name="searchQuery" placeholder="{{_ 'search-example'}}" value="{{ query.get }}" autofocus dir="auto")
|
||||
if searching.get
|
||||
+spinner
|
||||
else if hasResults.get
|
||||
.global-search-dueat-list-wrapper
|
||||
h1
|
||||
if $eq resultsCount.get 0
|
||||
| {{_ 'no-cards-found' }}
|
||||
else if $eq resultsCount.get 1
|
||||
| {{_ 'one-card-found' }}
|
||||
else if $eq resultsCount.get totalHits.get
|
||||
| {{_ 'n-cards-found' resultsCount.get }}
|
||||
else
|
||||
| {{_ 'n-n-of-n-cards-found' 1 resultsCount.get totalHits.get }}
|
||||
if queryErrors.get
|
||||
.global-search-results-list-wrapper
|
||||
if hasQueryErrors.get
|
||||
div
|
||||
each msg in errorMessages
|
||||
span.global-search-error-messages
|
||||
| {{_ msg.tag msg.value }}
|
||||
each card in results
|
||||
a.minicard-wrapper(href=card.absoluteUrl)
|
||||
else
|
||||
h1
|
||||
= resultsHeading.get
|
||||
a.fa.fa-link(title="{{_ 'link-to-search' }}" href="{{ getSearchHref }}")
|
||||
each card in results
|
||||
+resultCard(card)
|
||||
else
|
||||
.global-search-instructions
|
||||
h1 Search Operators
|
||||
+viewer
|
||||
= 'Searches can include operators to refine the search. Operators are specified by writing the operator'
|
||||
= 'name and value separated by a colon. For example, an operator specification of `list:Blocked` would limit the search'
|
||||
= 'to cards that are contained in a list named *Blocked*. If the value contains spaces or special characters'
|
||||
= 'it must be enclosed in quotation marks (e.g. `list:"To Review"`).\n'
|
||||
= 'Available operators are:\n'
|
||||
= '* `board:title` - cards in boards matching the specified title\n'
|
||||
= '* `list:title` - cards in lists matching the specified title\n'
|
||||
= '* `swimlane:title` - cards in swimlanes matching the specified title\n'
|
||||
= '* `label:color` - cards that have a label matching the given color\n'
|
||||
= '* `label:name` - cards that have a label matching the given name\n'
|
||||
= '* `user:username` - cards where the specified user is a member or assignee\n'
|
||||
= '* `@username` - shorthand for `user:username`\n'
|
||||
= '* `#label` - shorthand for `label:color-or-name`\n'
|
||||
= '## Notes\n'
|
||||
= '* Multiple operators may be specified.\n'
|
||||
= '* Similar operators are *OR*ed together. Cards that match any of the conditions will be returned.\n'
|
||||
= ' `list:Available list:Blocked` would return cards contained in any list named *Blocked* or *Available*.\n'
|
||||
= '* Differing operators are *AND*ed together. Only cards that match all of the differing operators are returned.\n'
|
||||
= '`list:Available label:red` returns only cards in the list *Available* with a *red* label.\n'
|
||||
= '* Text searches are case insensitive.\n'
|
||||
= searchInstructions
|
||||
|
||||
template(name="globalSearchViewChangePopup")
|
||||
if currentUser
|
||||
|
||||
@@ -36,164 +36,300 @@ BlazeComponent.extendComponent({
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.isPageReady = new ReactiveVar(true);
|
||||
this.searching = new ReactiveVar(false);
|
||||
this.hasResults = new ReactiveVar(false);
|
||||
this.hasQueryErrors = new ReactiveVar(false);
|
||||
this.query = new ReactiveVar('');
|
||||
this.resultsHeading = new ReactiveVar('');
|
||||
this.searchLink = new ReactiveVar(null);
|
||||
this.queryParams = null;
|
||||
this.resultsCount = new ReactiveVar(0);
|
||||
this.totalHits = new ReactiveVar(0);
|
||||
this.queryErrors = new ReactiveVar(null);
|
||||
this.parsingErrors = [];
|
||||
this.resultsCount = 0;
|
||||
this.totalHits = 0;
|
||||
this.queryErrors = null;
|
||||
Meteor.subscribe('setting');
|
||||
if (Session.get('globalQuery')) {
|
||||
this.searchAllBoards(Session.get('globalQuery'));
|
||||
}
|
||||
},
|
||||
|
||||
resetSearch() {
|
||||
this.searching.set(false);
|
||||
this.hasResults.set(false);
|
||||
this.hasQueryErrors.set(false);
|
||||
this.resultsHeading.set('');
|
||||
this.parsingErrors = [];
|
||||
this.resultsCount = 0;
|
||||
this.totalHits = 0;
|
||||
this.queryErrors = null;
|
||||
},
|
||||
|
||||
results() {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('getting results');
|
||||
if (this.queryParams) {
|
||||
const results = Cards.globalSearch(this.queryParams);
|
||||
this.queryErrors = results.errors;
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('user:', Meteor.user());
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('user:', Meteor.user().sessionData);
|
||||
// console.log('errors:', results.errors);
|
||||
this.totalHits.set(Meteor.user().sessionData.totalHits);
|
||||
this.resultsCount.set(results.cards.count());
|
||||
this.queryErrors.set(results.errors);
|
||||
return results.cards;
|
||||
// console.log('errors:', this.queryErrors);
|
||||
if (this.errorMessages().length) {
|
||||
this.hasQueryErrors.set(true);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (results.cards) {
|
||||
const sessionData = SessionData.findOne({ userId: Meteor.userId() });
|
||||
this.totalHits = sessionData.totalHits;
|
||||
this.resultsCount = results.cards.count();
|
||||
this.resultsHeading.set(this.getResultsHeading());
|
||||
return results.cards;
|
||||
}
|
||||
}
|
||||
this.resultsCount.set(0);
|
||||
this.resultsCount = 0;
|
||||
return [];
|
||||
},
|
||||
|
||||
errorMessages() {
|
||||
const errors = this.queryErrors.get();
|
||||
const messages = [];
|
||||
|
||||
errors.notFound.boards.forEach(board => {
|
||||
messages.push({ tag: 'board-title-not-found', value: board });
|
||||
});
|
||||
errors.notFound.swimlanes.forEach(swim => {
|
||||
messages.push({ tag: 'swimlane-title-not-found', value: swim });
|
||||
});
|
||||
errors.notFound.lists.forEach(list => {
|
||||
messages.push({ tag: 'list-title-not-found', value: list });
|
||||
});
|
||||
errors.notFound.users.forEach(user => {
|
||||
messages.push({ tag: 'user-username-not-found', value: user });
|
||||
});
|
||||
if (this.queryErrors) {
|
||||
this.queryErrors.notFound.boards.forEach(board => {
|
||||
messages.push({ tag: 'board-title-not-found', value: board });
|
||||
});
|
||||
this.queryErrors.notFound.swimlanes.forEach(swim => {
|
||||
messages.push({ tag: 'swimlane-title-not-found', value: swim });
|
||||
});
|
||||
this.queryErrors.notFound.lists.forEach(list => {
|
||||
messages.push({ tag: 'list-title-not-found', value: list });
|
||||
});
|
||||
this.queryErrors.notFound.labels.forEach(label => {
|
||||
messages.push({ tag: 'label-not-found', value: label });
|
||||
});
|
||||
this.queryErrors.notFound.users.forEach(user => {
|
||||
messages.push({ tag: 'user-username-not-found', value: user });
|
||||
});
|
||||
this.queryErrors.notFound.members.forEach(user => {
|
||||
messages.push({ tag: 'user-username-not-found', value: user });
|
||||
});
|
||||
this.queryErrors.notFound.assignees.forEach(user => {
|
||||
messages.push({ tag: 'user-username-not-found', value: user });
|
||||
});
|
||||
}
|
||||
|
||||
if (this.parsingErrors.length) {
|
||||
this.parsingErrors.forEach(err => {
|
||||
messages.push(err);
|
||||
});
|
||||
}
|
||||
|
||||
return messages;
|
||||
},
|
||||
|
||||
searchAllBoards(query) {
|
||||
this.query.set(query);
|
||||
|
||||
this.resetSearch();
|
||||
|
||||
if (!query) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.searching.set(true);
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('query:', query);
|
||||
|
||||
const reOperator1 = /^((?<operator>\w+):|(?<abbrev>[#@]))(?<value>\w+)(\s+|$)/;
|
||||
const reOperator2 = /^((?<operator>\w+):|(?<abbrev>[#@]))(?<quote>["']*)(?<value>.*?)\k<quote>(\s+|$)/;
|
||||
const reText = /^(?<text>\S+)(\s+|$)/;
|
||||
const reQuotedText = /^(?<quote>["'])(?<text>\w+)\k<quote>(\s+|$)/;
|
||||
|
||||
const operatorMap = {};
|
||||
operatorMap[TAPi18n.__('operator-board')] = 'boards';
|
||||
operatorMap[TAPi18n.__('operator-board-abbrev')] = 'boards';
|
||||
operatorMap[TAPi18n.__('operator-swimlane')] = 'swimlanes';
|
||||
operatorMap[TAPi18n.__('operator-swimlane-abbrev')] = 'swimlanes';
|
||||
operatorMap[TAPi18n.__('operator-list')] = 'lists';
|
||||
operatorMap[TAPi18n.__('operator-list-abbrev')] = 'lists';
|
||||
operatorMap[TAPi18n.__('operator-label')] = 'labels';
|
||||
operatorMap[TAPi18n.__('operator-label-abbrev')] = 'labels';
|
||||
operatorMap[TAPi18n.__('operator-user')] = 'users';
|
||||
operatorMap[TAPi18n.__('operator-user-abbrev')] = 'users';
|
||||
operatorMap[TAPi18n.__('operator-member')] = 'members';
|
||||
operatorMap[TAPi18n.__('operator-member-abbrev')] = 'members';
|
||||
operatorMap[TAPi18n.__('operator-assignee')] = 'assignees';
|
||||
operatorMap[TAPi18n.__('operator-assignee-abbrev')] = 'assignees';
|
||||
operatorMap[TAPi18n.__('operator-is')] = 'is';
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('operatorMap:', operatorMap);
|
||||
const params = {
|
||||
boards: [],
|
||||
swimlanes: [],
|
||||
lists: [],
|
||||
users: [],
|
||||
members: [],
|
||||
assignees: [],
|
||||
labels: [],
|
||||
is: [],
|
||||
};
|
||||
|
||||
let text = '';
|
||||
while (query) {
|
||||
m = query.match(reOperator1);
|
||||
if (!m) {
|
||||
m = query.match(reOperator2);
|
||||
if (m) {
|
||||
query = query.replace(reOperator2, '');
|
||||
}
|
||||
} else {
|
||||
query = query.replace(reOperator1, '');
|
||||
}
|
||||
if (m) {
|
||||
let op;
|
||||
if (m.groups.operator) {
|
||||
op = m.groups.operator.toLowerCase();
|
||||
} else {
|
||||
op = m.groups.abbrev;
|
||||
}
|
||||
if (op in operatorMap) {
|
||||
params[operatorMap[op]].push(m.groups.value);
|
||||
} else {
|
||||
this.parsingErrors.push({
|
||||
tag: 'operator-unknown-error',
|
||||
value: op,
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
m = query.match(reQuotedText);
|
||||
if (!m) {
|
||||
m = query.match(reText);
|
||||
if (m) {
|
||||
query = query.replace(reText, '');
|
||||
}
|
||||
} else {
|
||||
query = query.replace(reQuotedText, '');
|
||||
}
|
||||
if (m) {
|
||||
text += (text ? ' ' : '') + m.groups.text;
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('text:', text);
|
||||
params.text = text;
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('params:', params);
|
||||
|
||||
this.queryParams = params;
|
||||
|
||||
this.autorun(() => {
|
||||
const handle = subManager.subscribe('globalSearch', params);
|
||||
Tracker.nonreactive(() => {
|
||||
Tracker.autorun(() => {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('ready:', handle.ready());
|
||||
if (handle.ready()) {
|
||||
this.searching.set(false);
|
||||
this.hasResults.set(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
getResultsHeading() {
|
||||
if (this.resultsCount === 0) {
|
||||
return TAPi18n.__('no-cards-found');
|
||||
} else if (this.resultsCount === 1) {
|
||||
return TAPi18n.__('one-card-found');
|
||||
} else if (this.resultsCount === this.totalHits) {
|
||||
return TAPi18n.__('n-cards-found', this.resultsCount);
|
||||
}
|
||||
|
||||
return TAPi18n.__('n-n-of-n-cards-found', {
|
||||
start: 1,
|
||||
end: this.resultsCount,
|
||||
total: this.totalHits,
|
||||
});
|
||||
},
|
||||
|
||||
getSearchHref() {
|
||||
const baseUrl = window.location.href.replace(/([?#].*$|\s*$)/, '');
|
||||
return `${baseUrl}?q=${encodeURIComponent(this.query.get())}`;
|
||||
},
|
||||
|
||||
searchInstructions() {
|
||||
tags = {
|
||||
operator_board: TAPi18n.__('operator-board'),
|
||||
operator_list: TAPi18n.__('operator-list'),
|
||||
operator_swimlane: TAPi18n.__('operator-swimlane'),
|
||||
operator_label: TAPi18n.__('operator-label'),
|
||||
operator_label_abbrev: TAPi18n.__('operator-label-abbrev'),
|
||||
operator_user: TAPi18n.__('operator-user'),
|
||||
operator_user_abbrev: TAPi18n.__('operator-user-abbrev'),
|
||||
operator_member: TAPi18n.__('operator-member'),
|
||||
operator_member_abbrev: TAPi18n.__('operator-member-abbrev'),
|
||||
operator_assignee: TAPi18n.__('operator-assignee'),
|
||||
operator_assignee_abbrev: TAPi18n.__('operator-assignee-abbrev'),
|
||||
};
|
||||
|
||||
text = `# ${TAPi18n.__('globalSearch-instructions-heading')}`;
|
||||
text += `\n${TAPi18n.__('globalSearch-instructions-description', tags)}`;
|
||||
text += `\n${TAPi18n.__('globalSearch-instructions-operators', tags)}`;
|
||||
text += `\n* ${TAPi18n.__(
|
||||
'globalSearch-instructions-operator-board',
|
||||
tags,
|
||||
)}`;
|
||||
text += `\n* ${TAPi18n.__(
|
||||
'globalSearch-instructions-operator-list',
|
||||
tags,
|
||||
)}`;
|
||||
text += `\n* ${TAPi18n.__(
|
||||
'globalSearch-instructions-operator-swimlane',
|
||||
tags,
|
||||
)}`;
|
||||
text += `\n* ${TAPi18n.__(
|
||||
'globalSearch-instructions-operator-label',
|
||||
tags,
|
||||
)}`;
|
||||
text += `\n* ${TAPi18n.__(
|
||||
'globalSearch-instructions-operator-hash',
|
||||
tags,
|
||||
)}`;
|
||||
text += `\n* ${TAPi18n.__(
|
||||
'globalSearch-instructions-operator-user',
|
||||
tags,
|
||||
)}`;
|
||||
text += `\n* ${TAPi18n.__('globalSearch-instructions-operator-at', tags)}`;
|
||||
text += `\n* ${TAPi18n.__(
|
||||
'globalSearch-instructions-operator-member',
|
||||
tags,
|
||||
)}`;
|
||||
text += `\n* ${TAPi18n.__(
|
||||
'globalSearch-instructions-operator-assignee',
|
||||
tags,
|
||||
)}`;
|
||||
|
||||
text += `\n## ${TAPi18n.__('heading-notes')}`;
|
||||
text += `\n* ${TAPi18n.__('globalSearch-instructions-notes-1', tags)}`;
|
||||
text += `\n* ${TAPi18n.__('globalSearch-instructions-notes-2', tags)}`;
|
||||
text += `\n* ${TAPi18n.__('globalSearch-instructions-notes-3', tags)}`;
|
||||
text += `\n* ${TAPi18n.__('globalSearch-instructions-notes-4', tags)}`;
|
||||
text += `\n* ${TAPi18n.__('globalSearch-instructions-notes-5', tags)}`;
|
||||
|
||||
return text;
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'submit .js-search-query-form'(evt) {
|
||||
evt.preventDefault();
|
||||
this.query.set(evt.target.searchQuery.value);
|
||||
this.queryErrors.set(null);
|
||||
|
||||
if (!this.query.get()) {
|
||||
this.searching.set(false);
|
||||
this.hasResults.set(false);
|
||||
return;
|
||||
}
|
||||
|
||||
this.searching.set(true);
|
||||
this.hasResults.set(false);
|
||||
|
||||
let query = this.query.get();
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('query:', query);
|
||||
|
||||
const reOperator1 = /^((?<operator>\w+):|(?<abbrev>[#@]))(?<value>\w+)(\s+|$)/;
|
||||
const reOperator2 = /^((?<operator>\w+):|(?<abbrev>[#@]))(?<quote>["']*)(?<value>.*?)\k<quote>(\s+|$)/;
|
||||
const reText = /^(?<text>\S+)(\s+|$)/;
|
||||
const reQuotedText = /^(?<quote>["'])(?<text>\w+)\k<quote>(\s+|$)/;
|
||||
|
||||
const operatorMap = {};
|
||||
operatorMap[TAPi18n.__('operator-board')] = 'boards';
|
||||
operatorMap[TAPi18n.__('operator-board-abbrev')] = 'boards';
|
||||
operatorMap[TAPi18n.__('operator-swimlane')] = 'swimlanes';
|
||||
operatorMap[TAPi18n.__('operator-swimlane-abbrev')] = 'swimlanes';
|
||||
operatorMap[TAPi18n.__('operator-list')] = 'lists';
|
||||
operatorMap[TAPi18n.__('operator-list-abbrev')] = 'lists';
|
||||
operatorMap[TAPi18n.__('operator-label')] = 'labels';
|
||||
operatorMap[TAPi18n.__('operator-label-abbrev')] = 'labels';
|
||||
operatorMap[TAPi18n.__('operator-user')] = 'users';
|
||||
operatorMap[TAPi18n.__('operator-user-abbrev')] = 'users';
|
||||
operatorMap[TAPi18n.__('operator-is')] = 'is';
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('operatorMap:', operatorMap);
|
||||
const params = {
|
||||
boards: [],
|
||||
swimlanes: [],
|
||||
lists: [],
|
||||
users: [],
|
||||
labels: [],
|
||||
is: [],
|
||||
};
|
||||
|
||||
let text = '';
|
||||
while (query) {
|
||||
m = query.match(reOperator1);
|
||||
if (!m) {
|
||||
m = query.match(reOperator2);
|
||||
if (m) {
|
||||
query = query.replace(reOperator2, '');
|
||||
}
|
||||
} else {
|
||||
query = query.replace(reOperator1, '');
|
||||
}
|
||||
if (m) {
|
||||
let op;
|
||||
if (m.groups.operator) {
|
||||
op = m.groups.operator.toLowerCase();
|
||||
} else {
|
||||
op = m.groups.abbrev;
|
||||
}
|
||||
if (op in operatorMap) {
|
||||
params[operatorMap[op]].push(m.groups.value);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
m = query.match(reQuotedText);
|
||||
if (!m) {
|
||||
m = query.match(reText);
|
||||
if (m) {
|
||||
query = query.replace(reText, '');
|
||||
}
|
||||
} else {
|
||||
query = query.replace(reQuotedText, '');
|
||||
}
|
||||
if (m) {
|
||||
text += (text ? ' ' : '') + m.groups.text;
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('text:', text);
|
||||
params.text = text;
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('params:', params);
|
||||
|
||||
this.queryParams = params;
|
||||
|
||||
this.autorun(() => {
|
||||
const handle = subManager.subscribe('globalSearch', params);
|
||||
Tracker.nonreactive(() => {
|
||||
Tracker.autorun(() => {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('ready:', handle.ready());
|
||||
if (handle.ready()) {
|
||||
this.searching.set(false);
|
||||
this.hasResults.set(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
this.searchAllBoards(evt.target.searchQuery.value);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
margin-top: 0
|
||||
margin-bottom: 10px
|
||||
|
||||
.global-search-dueat-list-wrapper
|
||||
.global-search-results-list-wrapper
|
||||
max-width: 500px
|
||||
margin-right: auto
|
||||
margin-left: auto
|
||||
@@ -91,7 +91,7 @@
|
||||
font-style: italic
|
||||
|
||||
code
|
||||
color: white
|
||||
background-color: grey
|
||||
color: black
|
||||
background-color: lightgrey
|
||||
padding: 0.1rem !important
|
||||
font-size: 0.7rem !important
|
||||
@@ -43,7 +43,7 @@
|
||||
border-radius: 5px
|
||||
padding: 1.5rem
|
||||
padding-top: 0.75rem
|
||||
display: inline-block
|
||||
display: table-cell
|
||||
min-width: 250px
|
||||
max-width: 350px
|
||||
|
||||
|
||||
@@ -158,7 +158,14 @@ FlowRouter.route('/global-search', {
|
||||
|
||||
Utils.manageCustomUI();
|
||||
Utils.manageMatomo();
|
||||
DocHead.setTitle(TAPi18n.__('globalSearch-title'));
|
||||
|
||||
if (FlowRouter.getQueryParam('q')) {
|
||||
Session.set(
|
||||
'globalQuery',
|
||||
decodeURIComponent(FlowRouter.getQueryParam('q')),
|
||||
);
|
||||
}
|
||||
BlazeLayout.render('defaultLayout', {
|
||||
headerBar: 'globalSearchHeaderBar',
|
||||
content: 'globalSearch',
|
||||
|
||||
@@ -131,14 +131,6 @@ services:
|
||||
#build:
|
||||
# context: .
|
||||
# dockerfile: Dockerfile
|
||||
# args:
|
||||
# - NODE_VERSION=${NODE_VERSION}
|
||||
# - METEOR_RELEASE=${METEOR_RELEASE}
|
||||
# - NPM_VERSION=${NPM_VERSION}
|
||||
# - ARCHITECTURE=${ARCHITECTURE}
|
||||
# - SRC_PATH=${SRC_PATH}
|
||||
# - METEOR_EDGE=${METEOR_EDGE}
|
||||
# - USE_EDGE=${USE_EDGE}
|
||||
#-------------------------------------------------------------------------------------
|
||||
ports:
|
||||
# Docker outsideport:insideport. Do not add anything extra here.
|
||||
|
||||
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+27
-2
@@ -868,12 +868,13 @@
|
||||
"board-title-not-found": "Board „%s“ nicht gefunden.",
|
||||
"swimlane-title-not-found": "Swimlane „%s“ nicht gefunden.",
|
||||
"list-title-not-found": "Liste „%s“ nicht gefunden.",
|
||||
"label-not-found": "Label „%s“ nicht gefunden.",
|
||||
"user-username-not-found": "Nutzername „%s“ nicht gefunden.",
|
||||
"globalSearch-title": "Alle Boards durchsuchen",
|
||||
"no-cards-found": "Keine Karten gefunden",
|
||||
"one-card-found": "Eine Karte gefunden",
|
||||
"n-cards-found": "%s Karten gefunden",
|
||||
"n-n-of-n-cards-found": "%s–%s von %s Karten gefunden",
|
||||
"n-n-of-n-cards-found": "__start__–__end__ von __total__ Karten gefunden",
|
||||
"operator-board": "Board",
|
||||
"operator-board-abbrev": "b",
|
||||
"operator-swimlane": "Swimlane",
|
||||
@@ -884,5 +885,29 @@
|
||||
"operator-label-abbrev": "#",
|
||||
"operator-user": "Nutzer",
|
||||
"operator-user-abbrev": "@",
|
||||
"operator-is": "wird"
|
||||
"operator-member": "Mitglied",
|
||||
"operator-member-abbrev": "m",
|
||||
"operator-assignee": "Beauftragter",
|
||||
"operator-assignee-abbrev": "a",
|
||||
"operator-is": "wird",
|
||||
"operator-unknown-error": "„%s“ ist kein Operator",
|
||||
"heading-notes": "Bemerkungen",
|
||||
"globalSearch-instructions-heading": "Hinweise zur Suche",
|
||||
"globalSearch-instructions-description": "Suchanfragen können Operatoren enthalten, um die Suche zu verfeinern. Operatoren bestehen aus ihrem Namen und ihrem Wert, getrennt durch einen Doppelpunkt. Beispielsweise würde die Operatorangabe `list:Blockiert` die Suche beschränken auf Karten in einer Liste namens *Blockiert*. Wenn der Wet Leerschritte oder andere Spezialzeichen enthält, muss er in Anführungszeichen gesetzt sein (z.B. `__operator_list__:\"Im Review\"`).",
|
||||
"globalSearch-instructions-operators": "Mögliche Operatoren:",
|
||||
"globalSearch-instructions-operator-board": "`__operator_board__:Titel` – Karten in Boards mit dem angegebenen Titel",
|
||||
"globalSearch-instructions-operator-list": "`__operator_list__:Titel` – Karten in Listen, auf die der angegebene Titel passt",
|
||||
"globalSearch-instructions-operator-swimlane": "`__operator_swimlane__:Titel` – Karten in Swimlanes, auf die der angegebene Titel passt",
|
||||
"globalSearch-instructions-operator-label": "`__operator_label__:Farbe` `__operator_label__:Name` – Karten, die ein Label haben, auf das die angegebene Farbe oder Name passt",
|
||||
"globalSearch-instructions-operator-hash": "`__operator_label_abbrev__Label` – Kurzform von `__operator_label__:Label`",
|
||||
"globalSearch-instructions-operator-user": "`__operator_user__:Nutzername` – Karten, für die der angegebene Nutzer ein *Mitglied* oder ein *Beauftragter* ist",
|
||||
"globalSearch-instructions-operator-at": "`__operator_user_abbrev__Nutzername` – Kurzform von `user:Nutzername`",
|
||||
"globalSearch-instructions-operator-member": "`__operator_member__:Nutzername` – Karten, für die der angegebene Nutzer ein *Mitglied* ist",
|
||||
"globalSearch-instructions-operator-assignee": "`__operator_member__:Nutzername` – Karten, für die der angegebene Nutzer ein *Beauftragter* ist",
|
||||
"globalSearch-instructions-notes-1": "Mehrere Operatoren können angegeben werden.",
|
||||
"globalSearch-instructions-notes-2": "Similar operators are *OR*ed together. Cards that match any of the conditions will be returned.\n`__operator_list__:Available __operator_list__:Blocked` would return cards contained in any list named *Blocked* or *Available*.",
|
||||
"globalSearch-instructions-notes-3": "Differing operators are *AND*ed together. Only cards that match all of the differing operators are returned.\n`__operator_list__:Available __operator_label__:red` returns only cards in the list *Available* with a *red* label.",
|
||||
"globalSearch-instructions-notes-4": "Text searches are case insensitive.",
|
||||
"globalSearch-instructions-notes-5": "Currently archived cards are not searched.",
|
||||
"link-to-search": "Link to this search"
|
||||
}
|
||||
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+27
-2
@@ -868,12 +868,13 @@
|
||||
"board-title-not-found": "Board '%s' not found.",
|
||||
"swimlane-title-not-found": "Swimlane '%s' not found.",
|
||||
"list-title-not-found": "List '%s' not found.",
|
||||
"label-not-found": "Label '%s' not found.",
|
||||
"user-username-not-found": "Username '%s' not found.",
|
||||
"globalSearch-title": "Search All Boards",
|
||||
"no-cards-found": "No Cards Found",
|
||||
"one-card-found": "One Card Found",
|
||||
"n-cards-found": "%s Cards Found",
|
||||
"n-n-of-n-cards-found": "%s-%s of %s Cards Found",
|
||||
"n-n-of-n-cards-found": "__start__-__end__ of __total__ Cards Found",
|
||||
"operator-board": "board",
|
||||
"operator-board-abbrev": "b",
|
||||
"operator-swimlane": "swimlane",
|
||||
@@ -884,5 +885,29 @@
|
||||
"operator-label-abbrev": "#",
|
||||
"operator-user": "user",
|
||||
"operator-user-abbrev": "@",
|
||||
"operator-is": "is"
|
||||
"operator-member": "member",
|
||||
"operator-member-abbrev": "m",
|
||||
"operator-assignee": "assignee",
|
||||
"operator-assignee-abbrev": "a",
|
||||
"operator-is": "is",
|
||||
"operator-unknown-error": "%s is not an operator",
|
||||
"heading-notes": "Notes",
|
||||
"globalSearch-instructions-heading": "Search Instructions",
|
||||
"globalSearch-instructions-description": "Searches can include operators to refine the search. Operators are specified by writing the operator name and value separated by a colon. For example, an operator specification of `list:Blocked` would limit the search to cards that are contained in a list named *Blocked*. If the value contains spaces or special characters it must be enclosed in quotation marks (e.g. `__operator_list__:\"To Review\"`).",
|
||||
"globalSearch-instructions-operators": "Available operators:",
|
||||
"globalSearch-instructions-operator-board": "`__operator_board__:title` - cards in boards matching the specified title",
|
||||
"globalSearch-instructions-operator-list": "`__operator_list__:title` - cards in lists matching the specified title",
|
||||
"globalSearch-instructions-operator-swimlane": "`__operator_swimlane__:title` - cards in swimlanes matching the specified title",
|
||||
"globalSearch-instructions-operator-label": "`__operator_label__:color` `__operator_label__:name` - cards that have a label matching the given color or name",
|
||||
"globalSearch-instructions-operator-hash": "`__operator_label_abbrev__label` - shorthand for `__operator_label__:label`",
|
||||
"globalSearch-instructions-operator-user": "`__operator_user__:username` - cards where the specified user is a *member* or *assignee*",
|
||||
"globalSearch-instructions-operator-at": "`__operator_user_abbrev__username` - shorthand for `user:username`",
|
||||
"globalSearch-instructions-operator-member": "`__operator_member__:username` - cards where the specified user is a *member*",
|
||||
"globalSearch-instructions-operator-assignee": "`__operator_assignee__:username` - cards where the specified user is an *assignee*",
|
||||
"globalSearch-instructions-notes-1": "Multiple operators may be specified.",
|
||||
"globalSearch-instructions-notes-2": "Similar operators are *OR*ed together. Cards that match any of the conditions will be returned.\n`__operator_list__:Available __operator_list__:Blocked` would return cards contained in any list named *Blocked* or *Available*.",
|
||||
"globalSearch-instructions-notes-3": "Differing operators are *AND*ed together. Only cards that match all of the differing operators are returned.\n`__operator_list__:Available __operator_label__:red` returns only cards in the list *Available* with a *red* label.",
|
||||
"globalSearch-instructions-notes-4": "Text searches are case insensitive.",
|
||||
"globalSearch-instructions-notes-5": "Currently archived cards are not searched.",
|
||||
"link-to-search": "Link to this search"
|
||||
}
|
||||
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+27
-2
@@ -868,12 +868,13 @@
|
||||
"board-title-not-found": "הלוח ‚%s’ לא נמצא.",
|
||||
"swimlane-title-not-found": "המסלול ‚%s’ לא נמצא.",
|
||||
"list-title-not-found": "הרשימה ‚%s’ לא נמצאה.",
|
||||
"label-not-found": "Label '%s' not found.",
|
||||
"user-username-not-found": "שם המשתמש ‚%s’ לא נמצא.",
|
||||
"globalSearch-title": "חיפוש בכל הלוחות",
|
||||
"no-cards-found": "לא נמצאו כרטיסים",
|
||||
"one-card-found": "נמצא כרטיס אחד",
|
||||
"n-cards-found": "נמצאו %s כרטיסים",
|
||||
"n-n-of-n-cards-found": "נמצאו %s-%s מתוך %s כרטיסים",
|
||||
"n-n-of-n-cards-found": "__start__-__end__ of __total__ Cards Found",
|
||||
"operator-board": "לוח",
|
||||
"operator-board-abbrev": "ל",
|
||||
"operator-swimlane": "מסלול",
|
||||
@@ -884,5 +885,29 @@
|
||||
"operator-label-abbrev": "#",
|
||||
"operator-user": "משתמש",
|
||||
"operator-user-abbrev": "@",
|
||||
"operator-is": "הוא"
|
||||
"operator-member": "חבר",
|
||||
"operator-member-abbrev": "m",
|
||||
"operator-assignee": "assignee",
|
||||
"operator-assignee-abbrev": "a",
|
||||
"operator-is": "הוא",
|
||||
"operator-unknown-error": "%s is not an operator",
|
||||
"heading-notes": "Notes",
|
||||
"globalSearch-instructions-heading": "Search Instructions",
|
||||
"globalSearch-instructions-description": "Searches can include operators to refine the search. Operators are specified by writing the operator name and value separated by a colon. For example, an operator specification of `list:Blocked` would limit the search to cards that are contained in a list named *Blocked*. If the value contains spaces or special characters it must be enclosed in quotation marks (e.g. `__operator_list__:\"To Review\"`).",
|
||||
"globalSearch-instructions-operators": "Available operators:",
|
||||
"globalSearch-instructions-operator-board": "`__operator_board__:title` - cards in boards matching the specified title",
|
||||
"globalSearch-instructions-operator-list": "`__operator_list__:title` - cards in lists matching the specified title",
|
||||
"globalSearch-instructions-operator-swimlane": "`__operator_swimlane__:title` - cards in swimlanes matching the specified title",
|
||||
"globalSearch-instructions-operator-label": "`__operator_label__:color` `__operator_label__:name` - cards that have a label matching the given color or name",
|
||||
"globalSearch-instructions-operator-hash": "`__operator_label_abbrev__label` - shorthand for `__operator_label__:label`",
|
||||
"globalSearch-instructions-operator-user": "`__operator_user__:username` - cards where the specified user is a *member* or *assignee*",
|
||||
"globalSearch-instructions-operator-at": "`__operator_user_abbrev__username` - shorthand for `user:username`",
|
||||
"globalSearch-instructions-operator-member": "`__operator_member__:username` - cards where the specified user is a *member*",
|
||||
"globalSearch-instructions-operator-assignee": "`__operator_assignee__:username` - cards where the specified user is an *assignee*",
|
||||
"globalSearch-instructions-notes-1": "Multiple operators may be specified.",
|
||||
"globalSearch-instructions-notes-2": "Similar operators are *OR*ed together. Cards that match any of the conditions will be returned.\n`__operator_list__:Available __operator_list__:Blocked` would return cards contained in any list named *Blocked* or *Available*.",
|
||||
"globalSearch-instructions-notes-3": "Differing operators are *AND*ed together. Only cards that match all of the differing operators are returned.\n`__operator_list__:Available __operator_label__:red` returns only cards in the list *Available* with a *red* label.",
|
||||
"globalSearch-instructions-notes-4": "Text searches are case insensitive.",
|
||||
"globalSearch-instructions-notes-5": "Currently archived cards are not searched.",
|
||||
"link-to-search": "Link to this search"
|
||||
}
|
||||
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+27
-2
@@ -868,12 +868,13 @@
|
||||
"board-title-not-found": "Доска '%s' не найдена.",
|
||||
"swimlane-title-not-found": "Дорожка '%s' не найдена.",
|
||||
"list-title-not-found": "Список '%s' не найден.",
|
||||
"label-not-found": "Label '%s' not found.",
|
||||
"user-username-not-found": "Имя пользователя '%s' не найдено.",
|
||||
"globalSearch-title": "Искать на всех досках",
|
||||
"no-cards-found": "Ни одной карточки не найдено",
|
||||
"one-card-found": "Найдена одна карточка",
|
||||
"n-cards-found": "Карточек найдено %s",
|
||||
"n-n-of-n-cards-found": "Карточки %s - %s из %s найденных",
|
||||
"n-n-of-n-cards-found": "__start__-__end__ of __total__ Cards Found",
|
||||
"operator-board": "доска",
|
||||
"operator-board-abbrev": "д",
|
||||
"operator-swimlane": "дорожка",
|
||||
@@ -884,5 +885,29 @@
|
||||
"operator-label-abbrev": "#",
|
||||
"operator-user": "пользователь",
|
||||
"operator-user-abbrev": "@",
|
||||
"operator-is": " "
|
||||
"operator-member": "участника",
|
||||
"operator-member-abbrev": "m",
|
||||
"operator-assignee": "assignee",
|
||||
"operator-assignee-abbrev": "a",
|
||||
"operator-is": " ",
|
||||
"operator-unknown-error": "%s is not an operator",
|
||||
"heading-notes": "Notes",
|
||||
"globalSearch-instructions-heading": "Search Instructions",
|
||||
"globalSearch-instructions-description": "Searches can include operators to refine the search. Operators are specified by writing the operator name and value separated by a colon. For example, an operator specification of `list:Blocked` would limit the search to cards that are contained in a list named *Blocked*. If the value contains spaces or special characters it must be enclosed in quotation marks (e.g. `__operator_list__:\"To Review\"`).",
|
||||
"globalSearch-instructions-operators": "Available operators:",
|
||||
"globalSearch-instructions-operator-board": "`__operator_board__:title` - cards in boards matching the specified title",
|
||||
"globalSearch-instructions-operator-list": "`__operator_list__:title` - cards in lists matching the specified title",
|
||||
"globalSearch-instructions-operator-swimlane": "`__operator_swimlane__:title` - cards in swimlanes matching the specified title",
|
||||
"globalSearch-instructions-operator-label": "`__operator_label__:color` `__operator_label__:name` - cards that have a label matching the given color or name",
|
||||
"globalSearch-instructions-operator-hash": "`__operator_label_abbrev__label` - shorthand for `__operator_label__:label`",
|
||||
"globalSearch-instructions-operator-user": "`__operator_user__:username` - cards where the specified user is a *member* or *assignee*",
|
||||
"globalSearch-instructions-operator-at": "`__operator_user_abbrev__username` - shorthand for `user:username`",
|
||||
"globalSearch-instructions-operator-member": "`__operator_member__:username` - cards where the specified user is a *member*",
|
||||
"globalSearch-instructions-operator-assignee": "`__operator_assignee__:username` - cards where the specified user is an *assignee*",
|
||||
"globalSearch-instructions-notes-1": "Multiple operators may be specified.",
|
||||
"globalSearch-instructions-notes-2": "Similar operators are *OR*ed together. Cards that match any of the conditions will be returned.\n`__operator_list__:Available __operator_list__:Blocked` would return cards contained in any list named *Blocked* or *Available*.",
|
||||
"globalSearch-instructions-notes-3": "Differing operators are *AND*ed together. Only cards that match all of the differing operators are returned.\n`__operator_list__:Available __operator_label__:red` returns only cards in the list *Available* with a *red* label.",
|
||||
"globalSearch-instructions-notes-4": "Text searches are case insensitive.",
|
||||
"globalSearch-instructions-notes-5": "Currently archived cards are not searched.",
|
||||
"link-to-search": "Link to this search"
|
||||
}
|
||||
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+71
-24
@@ -693,7 +693,7 @@ Cards.helpers({
|
||||
const definition = definitions.find(definition => {
|
||||
return definition._id === customField._id;
|
||||
});
|
||||
if (!definition) {
|
||||
if (definition === undefined) {
|
||||
return {};
|
||||
}
|
||||
//search for "True Value" which is for DropDowns other then the Value (which is the id)
|
||||
@@ -1866,16 +1866,29 @@ Cards.globalSearch = queryParams => {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('userId:', userId);
|
||||
|
||||
const errors = {
|
||||
notFound: {
|
||||
boards: [],
|
||||
swimlanes: [],
|
||||
lists: [],
|
||||
labels: [],
|
||||
users: [],
|
||||
is: [],
|
||||
},
|
||||
};
|
||||
const errors = new (class {
|
||||
constructor() {
|
||||
this.notFound = {
|
||||
boards: [],
|
||||
swimlanes: [],
|
||||
lists: [],
|
||||
labels: [],
|
||||
users: [],
|
||||
members: [],
|
||||
assignees: [],
|
||||
is: [],
|
||||
};
|
||||
}
|
||||
|
||||
hasErrors() {
|
||||
for (const prop in this.notFound) {
|
||||
if (this.notFound[prop].length) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
})();
|
||||
|
||||
const selector = {
|
||||
archived: false,
|
||||
@@ -1939,25 +1952,63 @@ Cards.globalSearch = queryParams => {
|
||||
selector.listId.$in = queryLists;
|
||||
}
|
||||
|
||||
const queryMembers = [];
|
||||
const queryAssignees = [];
|
||||
if (queryParams.users.length) {
|
||||
const queryUsers = [];
|
||||
queryParams.users.forEach(query => {
|
||||
const users = Users.find({
|
||||
username: query,
|
||||
});
|
||||
if (users.count()) {
|
||||
users.forEach(user => {
|
||||
queryUsers.push(user._id);
|
||||
queryMembers.push(user._id);
|
||||
queryAssignees.push(user._id);
|
||||
});
|
||||
} else {
|
||||
errors.notFound.users.push(query);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (queryParams.members.length) {
|
||||
queryParams.members.forEach(query => {
|
||||
const users = Users.find({
|
||||
username: query,
|
||||
});
|
||||
if (users.count()) {
|
||||
users.forEach(user => {
|
||||
queryMembers.push(user._id);
|
||||
});
|
||||
} else {
|
||||
errors.notFound.members.push(query);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (queryParams.assignees.length) {
|
||||
queryParams.assignees.forEach(query => {
|
||||
const users = Users.find({
|
||||
username: query,
|
||||
});
|
||||
if (users.count()) {
|
||||
users.forEach(user => {
|
||||
queryAssignees.push(user._id);
|
||||
});
|
||||
} else {
|
||||
errors.notFound.assignees.push(query);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (queryMembers.length && queryAssignees.length) {
|
||||
selector.$or = [
|
||||
{ members: { $in: queryUsers } },
|
||||
{ assignees: { $in: queryUsers } },
|
||||
{ members: { $in: queryMembers } },
|
||||
{ assignees: { $in: queryAssignees } },
|
||||
];
|
||||
} else if (queryMembers.length) {
|
||||
selector.members = { $in: queryMembers };
|
||||
} else if (queryAssignees.length) {
|
||||
selector.assignees = { $in: queryAssignees };
|
||||
}
|
||||
|
||||
if (queryParams.labels.length) {
|
||||
@@ -2003,7 +2054,7 @@ Cards.globalSearch = queryParams => {
|
||||
});
|
||||
});
|
||||
} else {
|
||||
errors.notFound.labels.push({ tag: 'label', value: label });
|
||||
errors.notFound.labels.push(label);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2011,6 +2062,10 @@ Cards.globalSearch = queryParams => {
|
||||
});
|
||||
}
|
||||
|
||||
if (errors.hasErrors()) {
|
||||
return { cards: null, errors };
|
||||
}
|
||||
|
||||
if (queryParams.text) {
|
||||
const regex = new RegExp(queryParams.text, 'i');
|
||||
|
||||
@@ -2045,14 +2100,6 @@ Cards.globalSearch = queryParams => {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('count:', cards.count());
|
||||
|
||||
if (Meteor.isServer) {
|
||||
Users.update(userId, {
|
||||
$set: {
|
||||
'sessionData.totalHits': cards.count(),
|
||||
'sessionData.lastHit': cards.count() > 50 ? 50 : cards.count(),
|
||||
},
|
||||
});
|
||||
}
|
||||
return { cards, errors };
|
||||
};
|
||||
|
||||
|
||||
@@ -377,6 +377,14 @@ Users.initEasySearch(searchInFields, {
|
||||
returnFields: [...searchInFields, 'profile.avatarUrl'],
|
||||
});
|
||||
|
||||
Users.safeFields = {
|
||||
_id: 1,
|
||||
username: 1,
|
||||
'profile.fullname': 1,
|
||||
'profile.avatarUrl': 1,
|
||||
'profile.initials': 1,
|
||||
};
|
||||
|
||||
if (Meteor.isClient) {
|
||||
Users.helpers({
|
||||
isBoardMember() {
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
SessionData = new Mongo.Collection('sessiondata');
|
||||
|
||||
/**
|
||||
* A UserSessionData in Wekan. Organization in Trello.
|
||||
*/
|
||||
SessionData.attachSchema(
|
||||
new SimpleSchema({
|
||||
_id: {
|
||||
/**
|
||||
* the organization id
|
||||
*/
|
||||
type: Number,
|
||||
optional: true,
|
||||
// eslint-disable-next-line consistent-return
|
||||
autoValue() {
|
||||
if (this.isInsert && !this.isSet) {
|
||||
return incrementCounter('counters', 'orgId', 1);
|
||||
}
|
||||
},
|
||||
},
|
||||
userId: {
|
||||
/**
|
||||
* userId of the user
|
||||
*/
|
||||
type: String,
|
||||
optional: false,
|
||||
},
|
||||
totalHits: {
|
||||
/**
|
||||
* total number of hits in the last report query
|
||||
*/
|
||||
type: Number,
|
||||
optional: true,
|
||||
},
|
||||
lastHit: {
|
||||
/**
|
||||
* the last hit returned from a report query
|
||||
*/
|
||||
type: Number,
|
||||
optional: true,
|
||||
},
|
||||
createdAt: {
|
||||
/**
|
||||
* creation date of the team
|
||||
*/
|
||||
type: Date,
|
||||
// eslint-disable-next-line consistent-return
|
||||
autoValue() {
|
||||
if (this.isInsert) {
|
||||
return new Date();
|
||||
} else if (this.isUpsert) {
|
||||
return { $setOnInsert: new Date() };
|
||||
} else {
|
||||
this.unset();
|
||||
}
|
||||
},
|
||||
},
|
||||
modifiedAt: {
|
||||
type: Date,
|
||||
denyUpdate: false,
|
||||
// eslint-disable-next-line consistent-return
|
||||
autoValue() {
|
||||
if (this.isInsert || this.isUpsert || this.isUpdate) {
|
||||
return new Date();
|
||||
} else {
|
||||
this.unset();
|
||||
}
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
export default SessionData;
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "wekan",
|
||||
"version": "v4.79.0",
|
||||
"version": "v4.80.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "wekan",
|
||||
"version": "v4.79.0",
|
||||
"version": "v4.80.0",
|
||||
"description": "Open-Source kanban",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -1524,7 +1524,7 @@ var n=this.pipeline.run(e.tokenizer(t)),r=new e.Vector,i=[],o=this._fields.reduc
|
||||
<ul class="toc-list-h1">
|
||||
|
||||
<li>
|
||||
<a href="#wekan-rest-api" class="toc-h1 toc-link" data-title="Wekan REST API v4.79">Wekan REST API v4.79</a>
|
||||
<a href="#wekan-rest-api" class="toc-h1 toc-link" data-title="Wekan REST API v4.80">Wekan REST API v4.80</a>
|
||||
|
||||
</li>
|
||||
|
||||
@@ -2037,7 +2037,7 @@ var n=this.pipeline.run(e.tokenizer(t)),r=new e.Vector,i=[],o=this._fields.reduc
|
||||
<div class="page-wrapper">
|
||||
<div class="dark-box"></div>
|
||||
<div class="content">
|
||||
<h1 id="wekan-rest-api">Wekan REST API v4.79</h1>
|
||||
<h1 id="wekan-rest-api">Wekan REST API v4.80</h1>
|
||||
<blockquote>
|
||||
<p>Scroll down for code samples, example requests and responses. Select a language for code samples from the tabs above or the mobile navigation menu.</p>
|
||||
</blockquote>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
swagger: '2.0'
|
||||
info:
|
||||
title: Wekan REST API
|
||||
version: v4.79
|
||||
version: v4.80
|
||||
description: |
|
||||
The REST API allows you to control and extend Wekan with ease.
|
||||
|
||||
|
||||
@@ -22,10 +22,10 @@ const pkgdef :Spk.PackageDefinition = (
|
||||
appTitle = (defaultText = "Wekan"),
|
||||
# The name of the app as it is displayed to the user.
|
||||
|
||||
appVersion = 479,
|
||||
appVersion = 480,
|
||||
# Increment this for every release.
|
||||
|
||||
appMarketingVersion = (defaultText = "4.79.0~2021-01-17"),
|
||||
appMarketingVersion = (defaultText = "4.80.0~2021-01-18"),
|
||||
# Human-readable presentation of the app version.
|
||||
|
||||
minUpgradableAppVersion = 0,
|
||||
|
||||
@@ -72,18 +72,7 @@ Meteor.publish('myCards', function() {
|
||||
Boards.find({ _id: { $in: boards } }),
|
||||
Swimlanes.find({ _id: { $in: swimlanes } }),
|
||||
Lists.find({ _id: { $in: lists } }),
|
||||
Users.find(
|
||||
{ _id: { $in: users } },
|
||||
{
|
||||
fields: {
|
||||
_id: 1,
|
||||
username: 1,
|
||||
'profile.fullname': 1,
|
||||
'profile.avatarUrl': 1,
|
||||
'profile.initials': 1,
|
||||
},
|
||||
},
|
||||
),
|
||||
Users.find({ _id: { $in: users } }, { fields: Users.safeFields }),
|
||||
];
|
||||
});
|
||||
|
||||
@@ -93,18 +82,7 @@ Meteor.publish('dueCards', function(allUsers = false) {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('all users:', allUsers);
|
||||
|
||||
const user = Users.findOne(
|
||||
{ _id: this.userId },
|
||||
{
|
||||
fields: {
|
||||
_id: 1,
|
||||
username: 1,
|
||||
'profile.fullname': 1,
|
||||
'profile.avatarUrl': 1,
|
||||
'profile.initials': 1,
|
||||
},
|
||||
},
|
||||
);
|
||||
const user = Users.findOne({ _id: this.userId });
|
||||
|
||||
const archivedBoards = [];
|
||||
Boards.find({ archived: true }).forEach(board => {
|
||||
@@ -115,14 +93,12 @@ Meteor.publish('dueCards', function(allUsers = false) {
|
||||
let selector = {
|
||||
archived: false,
|
||||
};
|
||||
// for admins and users, allow her to see cards only from boards where
|
||||
// she is a member
|
||||
//if (!user.isAdmin) {
|
||||
|
||||
selector.$or = [
|
||||
{ permission: 'public' },
|
||||
{ members: { $elemMatch: { userId: user._id, isActive: true } } },
|
||||
];
|
||||
//}
|
||||
|
||||
Boards.find(selector).forEach(board => {
|
||||
permiitedBoards.push(board._id);
|
||||
});
|
||||
@@ -193,18 +169,7 @@ Meteor.publish('dueCards', function(allUsers = false) {
|
||||
Boards.find({ _id: { $in: boards } }),
|
||||
Swimlanes.find({ _id: { $in: swimlanes } }),
|
||||
Lists.find({ _id: { $in: lists } }),
|
||||
Users.find(
|
||||
{ _id: { $in: users } },
|
||||
{
|
||||
fields: {
|
||||
_id: 1,
|
||||
username: 1,
|
||||
'profile.fullname': 1,
|
||||
'profile.avatarUrl': 1,
|
||||
'profile.initials': 1,
|
||||
},
|
||||
},
|
||||
),
|
||||
Users.find({ _id: { $in: users } }, { fields: Users.safeFields }),
|
||||
];
|
||||
});
|
||||
|
||||
@@ -216,6 +181,20 @@ Meteor.publish('globalSearch', function(queryParams) {
|
||||
|
||||
const cards = Cards.globalSearch(queryParams).cards;
|
||||
|
||||
if (!cards) {
|
||||
return [];
|
||||
}
|
||||
|
||||
SessionData.upsert(
|
||||
{ userId: this.userId },
|
||||
{
|
||||
$set: {
|
||||
totalHits: cards.count(),
|
||||
lastHit: cards.count() > 50 ? 50 : cards.count(),
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const boards = [];
|
||||
const swimlanes = [];
|
||||
const lists = [];
|
||||
@@ -244,34 +223,21 @@ Meteor.publish('globalSearch', function(queryParams) {
|
||||
Boards.find({ _id: { $in: boards } }),
|
||||
Swimlanes.find({ _id: { $in: swimlanes } }),
|
||||
Lists.find({ _id: { $in: lists } }),
|
||||
Users.find({ _id: { $in: users } }),
|
||||
Users.find({ _id: { $in: users } }, { fields: Users.safeFields }),
|
||||
SessionData.find({ userId: this.userId }),
|
||||
];
|
||||
});
|
||||
|
||||
Meteor.publish('brokenCards', function() {
|
||||
const user = Users.findOne(
|
||||
{ _id: this.userId },
|
||||
{
|
||||
fields: {
|
||||
_id: 1,
|
||||
username: 1,
|
||||
'profile.fullname': 1,
|
||||
'profile.avatarUrl': 1,
|
||||
'profile.initials': 1,
|
||||
},
|
||||
},
|
||||
);
|
||||
const user = Users.findOne({ _id: this.userId });
|
||||
|
||||
const permiitedBoards = [null];
|
||||
let selector = {};
|
||||
// for admins and users, if user is not an admin allow her to see cards only from boards where
|
||||
// she is a member
|
||||
//if (!user.isAdmin) {
|
||||
selector.$or = [
|
||||
{ permission: 'public' },
|
||||
{ members: { $elemMatch: { userId: user._id, isActive: true } } },
|
||||
];
|
||||
//}
|
||||
|
||||
Boards.find(selector).forEach(board => {
|
||||
permiitedBoards.push(board._id);
|
||||
});
|
||||
@@ -328,17 +294,6 @@ Meteor.publish('brokenCards', function() {
|
||||
Boards.find({ _id: { $in: boards } }),
|
||||
Swimlanes.find({ _id: { $in: swimlanes } }),
|
||||
Lists.find({ _id: { $in: lists } }),
|
||||
Users.find(
|
||||
{ _id: { $in: users } },
|
||||
{
|
||||
fields: {
|
||||
_id: 1,
|
||||
username: 1,
|
||||
'profile.fullname': 1,
|
||||
'profile.avatarUrl': 1,
|
||||
'profile.initials': 1,
|
||||
},
|
||||
},
|
||||
),
|
||||
Users.find({ _id: { $in: users } }, { fields: Users.safeFields }),
|
||||
];
|
||||
});
|
||||
@@ -95,6 +95,6 @@ else
|
||||
|
||||
# Drop indexes on database upgrade, when starting MongoDB
|
||||
#mongo wekan --eval "db.getCollectionNames().forEach(function(col_name) { var coll = db.getCollection(col_name); coll.dropIndexes(); });" $BIND_OPTIONS
|
||||
mongo wekan --eval 'db.adminCommand({ setFeatureCompatibilityVersion: "4.4" });' $BIND_OPTIONS
|
||||
mongo wekan --eval 'db.adminCommand({ setFeatureCompatibilityVersion: "4.2" });' $BIND_OPTIONS
|
||||
|
||||
fi
|
||||
Reference in new issue
Block a user