Compare commits
13 Commits
enh/dateSh
...
enh/attach
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
13d57f7a8d | ||
|
|
638e8c152e | ||
|
|
cbfee393fd | ||
|
|
90cf64481e | ||
|
|
d1db72fcf2 | ||
|
|
89efcdee62 | ||
|
|
96e9f8275f | ||
|
|
9082075949 | ||
|
|
9f708e54c9 | ||
|
|
84fbbafcfb | ||
|
|
da24176aec | ||
|
|
06a973ae02 | ||
|
|
2748b4a0b1 |
2
.github/workflows/app-code-check.yml
vendored
2
.github/workflows/app-code-check.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: PHP AppCode Check
|
||||
name: Nextcloud app code check
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
2
.github/workflows/appbuild.yml
vendored
2
.github/workflows/appbuild.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Build app package
|
||||
name: Package build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
89
.github/workflows/integration.yml
vendored
Normal file
89
.github/workflows/integration.yml
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
name: Integration tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- stable*
|
||||
|
||||
env:
|
||||
APP_NAME: deck
|
||||
|
||||
jobs:
|
||||
integration:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php-versions: ['7.4']
|
||||
databases: ['sqlite', 'mysql', 'pgsql']
|
||||
server-versions: ['master']
|
||||
|
||||
name: php${{ matrix.php-versions }}-${{ matrix.databases }}-${{ matrix.server-versions }}
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres
|
||||
ports:
|
||||
- 4445:5432/tcp
|
||||
env:
|
||||
POSTGRES_USER: root
|
||||
POSTGRES_PASSWORD: rootpassword
|
||||
POSTGRES_DB: nextcloud
|
||||
options: --health-cmd pg_isready --health-interval 5s --health-timeout 2s --health-retries 5
|
||||
mysql:
|
||||
image: mariadb
|
||||
ports:
|
||||
- 4444:3306/tcp
|
||||
env:
|
||||
MYSQL_ROOT_PASSWORD: rootpassword
|
||||
options: --health-cmd="mysqladmin ping" --health-interval 5s --health-timeout 2s --health-retries 5
|
||||
|
||||
steps:
|
||||
- name: Checkout server
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: nextcloud/server
|
||||
ref: ${{ matrix.server-versions }}
|
||||
|
||||
- name: Checkout submodules
|
||||
shell: bash
|
||||
run: |
|
||||
auth_header="$(git config --local --get http.https://github.com/.extraheader)"
|
||||
git submodule sync --recursive
|
||||
git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1
|
||||
|
||||
- name: Checkout app
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: apps/${{ env.APP_NAME }}
|
||||
|
||||
- name: Set up php ${{ matrix.php-versions }}
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
tools: phpunit
|
||||
extensions: mbstring, iconv, fileinfo, intl, sqlite, pdo_sqlite, mysql, pdo_mysql, pgsql, pdo_pgsql,
|
||||
coverage: none
|
||||
|
||||
- name: Set up PHPUnit
|
||||
working-directory: apps/${{ env.APP_NAME }}
|
||||
run: composer i
|
||||
|
||||
- name: Set up Nextcloud
|
||||
run: |
|
||||
if [ "${{ matrix.databases }}" = "mysql" ]; then
|
||||
export DB_PORT=4444
|
||||
elif [ "${{ matrix.databases }}" = "pgsql" ]; then
|
||||
export DB_PORT=4445
|
||||
fi
|
||||
mkdir data
|
||||
./occ maintenance:install --verbose --database=${{ matrix.databases }} --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass admin
|
||||
./occ app:enable --force ${{ env.APP_NAME }}
|
||||
php -S localhost:8080 &
|
||||
|
||||
- name: Run behat
|
||||
working-directory: apps/${{ env.APP_NAME }}/tests/integration
|
||||
run: ./run.sh
|
||||
2
.github/workflows/nightly.yml
vendored
2
.github/workflows/nightly.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Nightly build
|
||||
name: Package nightly
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
5
.github/workflows/nodejs.yml
vendored
5
.github/workflows/nodejs.yml
vendored
@@ -22,5 +22,8 @@ jobs:
|
||||
npm ci
|
||||
- name: build
|
||||
run: |
|
||||
npm run build --if-present
|
||||
mkdir -p js
|
||||
npm run build --if-present -- --profile --json | tail -n +6 > js/webpack-stats.json
|
||||
npx relative-ci-agent
|
||||
|
||||
|
||||
|
||||
161
.github/workflows/phpunit.yml
vendored
161
.github/workflows/phpunit.yml
vendored
@@ -10,82 +10,30 @@ on:
|
||||
env:
|
||||
APP_NAME: deck
|
||||
|
||||
|
||||
jobs:
|
||||
php:
|
||||
integration:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
# do not stop on another job's failure
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php-versions: ['7.4']
|
||||
databases: ['sqlite']
|
||||
server-versions: ['master']
|
||||
|
||||
name: php${{ matrix.php-versions }}-${{ matrix.databases }}-${{ matrix.server-versions }}
|
||||
|
||||
steps:
|
||||
- name: Checkout server
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: nextcloud/server
|
||||
ref: ${{ matrix.server-versions }}
|
||||
|
||||
- name: Checkout submodules
|
||||
shell: bash
|
||||
run: |
|
||||
auth_header="$(git config --local --get http.https://github.com/.extraheader)"
|
||||
git submodule sync --recursive
|
||||
git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1
|
||||
|
||||
- name: Checkout app
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: apps/${{ env.APP_NAME }}
|
||||
|
||||
- name: Set up php ${{ matrix.php-versions }}
|
||||
uses: shivammathur/setup-php@v1
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
tools: phpunit
|
||||
extensions: mbstring, iconv, fileinfo, intl, sqlite, pdo_sqlite
|
||||
coverage: none
|
||||
|
||||
- name: Set up PHPUnit
|
||||
working-directory: apps/${{ env.APP_NAME }}
|
||||
run: composer i
|
||||
|
||||
- name: Set up Nextcloud
|
||||
env:
|
||||
DB_PORT: 4444
|
||||
run: |
|
||||
mkdir data
|
||||
./occ maintenance:install --verbose --database=${{ matrix.databases }} --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass password
|
||||
./occ app:enable --force ${{ env.APP_NAME }}
|
||||
php -S localhost:8080 &
|
||||
|
||||
- name: PHPUnit
|
||||
working-directory: apps/${{ env.APP_NAME }}
|
||||
run: ./vendor/phpunit/phpunit/phpunit -c tests/phpunit.xml
|
||||
|
||||
- name: PHPUnit integration
|
||||
working-directory: apps/${{ env.APP_NAME }}
|
||||
run: ./vendor/phpunit/phpunit/phpunit -c tests/phpunit.integration.xml
|
||||
|
||||
mysql:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
# do not stop on another job's failure
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php-versions: ['7.3', '7.4']
|
||||
databases: ['mysql']
|
||||
databases: ['sqlite', 'mysql', 'pgsql']
|
||||
server-versions: ['master']
|
||||
|
||||
name: php${{ matrix.php-versions }}-${{ matrix.databases }}-${{ matrix.server-versions }}
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres
|
||||
ports:
|
||||
- 4445:5432/tcp
|
||||
env:
|
||||
POSTGRES_USER: root
|
||||
POSTGRES_PASSWORD: rootpassword
|
||||
POSTGRES_DB: nextcloud
|
||||
options: --health-cmd pg_isready --health-interval 5s --health-timeout 2s --health-retries 5
|
||||
mysql:
|
||||
image: mariadb
|
||||
ports:
|
||||
@@ -114,11 +62,11 @@ jobs:
|
||||
path: apps/${{ env.APP_NAME }}
|
||||
|
||||
- name: Set up php ${{ matrix.php-versions }}
|
||||
uses: shivammathur/setup-php@v1
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
tools: phpunit
|
||||
extensions: mbstring, iconv, fileinfo, intl, mysql, pdo_mysql
|
||||
extensions: mbstring, iconv, fileinfo, intl, sqlite, pdo_sqlite, mysql, pdo_mysql, pgsql, pdo_pgsql
|
||||
coverage: none
|
||||
|
||||
- name: Set up PHPUnit
|
||||
@@ -126,83 +74,14 @@ jobs:
|
||||
run: composer i
|
||||
|
||||
- name: Set up Nextcloud
|
||||
env:
|
||||
DB_PORT: 4444
|
||||
run: |
|
||||
if [ "${{ matrix.databases }}" = "mysql" ]; then
|
||||
export DB_PORT=4444
|
||||
elif [ "${{ matrix.databases }}" = "pgsql" ]; then
|
||||
export DB_PORT=4445
|
||||
fi
|
||||
mkdir data
|
||||
./occ maintenance:install --verbose --database=${{ matrix.databases }} --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass password
|
||||
./occ app:enable --force ${{ env.APP_NAME }}
|
||||
php -S localhost:8080 &
|
||||
|
||||
- name: PHPUnit
|
||||
working-directory: apps/${{ env.APP_NAME }}
|
||||
run: ./vendor/phpunit/phpunit/phpunit -c tests/phpunit.xml
|
||||
|
||||
- name: PHPUnit integration
|
||||
working-directory: apps/${{ env.APP_NAME }}
|
||||
run: ./vendor/phpunit/phpunit/phpunit -c tests/phpunit.integration.xml
|
||||
|
||||
pgsql:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
# do not stop on another job's failure
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php-versions: ['7.4']
|
||||
databases: ['pgsql']
|
||||
server-versions: ['master']
|
||||
|
||||
name: php${{ matrix.php-versions }}-${{ matrix.databases }}-${{ matrix.server-versions }}
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres
|
||||
ports:
|
||||
- 4444:5432/tcp
|
||||
env:
|
||||
POSTGRES_USER: root
|
||||
POSTGRES_PASSWORD: rootpassword
|
||||
POSTGRES_DB: nextcloud
|
||||
options: --health-cmd pg_isready --health-interval 5s --health-timeout 2s --health-retries 5
|
||||
|
||||
steps:
|
||||
- name: Checkout server
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: nextcloud/server
|
||||
ref: ${{ matrix.server-versions }}
|
||||
|
||||
- name: Checkout submodules
|
||||
shell: bash
|
||||
run: |
|
||||
auth_header="$(git config --local --get http.https://github.com/.extraheader)"
|
||||
git submodule sync --recursive
|
||||
git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1
|
||||
|
||||
- name: Checkout app
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: apps/${{ env.APP_NAME }}
|
||||
|
||||
- name: Set up php ${{ matrix.php-versions }}
|
||||
uses: shivammathur/setup-php@v1
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
tools: phpunit
|
||||
extensions: mbstring, iconv, fileinfo, intl, pgsql, pdo_pgsql
|
||||
coverage: none
|
||||
|
||||
- name: Set up PHPUnit
|
||||
working-directory: apps/${{ env.APP_NAME }}
|
||||
run: composer i
|
||||
|
||||
- name: Set up Nextcloud
|
||||
env:
|
||||
DB_PORT: 4444
|
||||
run: |
|
||||
mkdir data
|
||||
./occ maintenance:install --verbose --database=${{ matrix.databases }} --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass password
|
||||
./occ maintenance:install --verbose --database=${{ matrix.databases }} --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass admin
|
||||
./occ app:enable --force ${{ env.APP_NAME }}
|
||||
php -S localhost:8080 &
|
||||
|
||||
|
||||
7
.github/workflows/static-analysis.yml
vendored
7
.github/workflows/static-analysis.yml
vendored
@@ -1,6 +1,11 @@
|
||||
name: Static analysis
|
||||
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- stable*
|
||||
|
||||
jobs:
|
||||
static-psalm-analysis:
|
||||
|
||||
442
package-lock.json
generated
442
package-lock.json
generated
@@ -2717,6 +2717,25 @@
|
||||
"integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
|
||||
"dev": true
|
||||
},
|
||||
"@bundle-stats/utils": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@bundle-stats/utils/-/utils-2.4.0.tgz",
|
||||
"integrity": "sha512-JPM2q496ECnfbbVslXPm3aR0QaFth/XyqZSY7m/6/7EVkI0rO0Znx5jEZ/0RXtRuiU83eVsWrwoUabeLt5PO2g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"convert-units": "^2.3.4",
|
||||
"core-js": "^3.1.4",
|
||||
"superstruct": "^0.8.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"core-js": {
|
||||
"version": "3.8.1",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.8.1.tgz",
|
||||
"integrity": "sha512-9Id2xHY1W7m8hCl8NkhQn5CufmF/WuR30BTRewvCXc1aZd3kMECwNZ69ndLbekKfakw9Rf2Xyc+QR6E7Gg+obg==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@cnakazawa/watch": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.3.tgz",
|
||||
@@ -4365,6 +4384,182 @@
|
||||
"fastq": "^1.6.0"
|
||||
}
|
||||
},
|
||||
"@relative-ci/agent": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@relative-ci/agent/-/agent-1.4.0.tgz",
|
||||
"integrity": "sha512-bP2fFYK9rQZTs8bLHoyRjUzAYrRhcFHmfYZmyOu0lXoHe1tyXD7NxYKicLElG2RNjTlzbQS0bwsTU9xjK6wSGg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@bundle-stats/utils": "^2.1.1",
|
||||
"@relative-ci/env-ci": "^5.1.0",
|
||||
"core-js": "^3.6.4",
|
||||
"cosmiconfig": "^7.0.0",
|
||||
"debug": "^4.1.1",
|
||||
"dotenv": "^8.2.0",
|
||||
"fs-extra": "^9.0.0",
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
"lodash": "^4.17.15"
|
||||
},
|
||||
"dependencies": {
|
||||
"core-js": {
|
||||
"version": "3.8.1",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.8.1.tgz",
|
||||
"integrity": "sha512-9Id2xHY1W7m8hCl8NkhQn5CufmF/WuR30BTRewvCXc1aZd3kMECwNZ69ndLbekKfakw9Rf2Xyc+QR6E7Gg+obg==",
|
||||
"dev": true
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
|
||||
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ms": "2.1.2"
|
||||
}
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz",
|
||||
"integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"at-least-node": "^1.0.0",
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^6.0.1",
|
||||
"universalify": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"graceful-fs": {
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
|
||||
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
|
||||
"dev": true
|
||||
},
|
||||
"jsonfile": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
|
||||
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.6",
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"universalify": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
|
||||
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||
"dev": true
|
||||
},
|
||||
"universalify": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz",
|
||||
"integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@relative-ci/env-ci": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@relative-ci/env-ci/-/env-ci-5.1.0.tgz",
|
||||
"integrity": "sha512-7KI6izFxbx0Hyzk1kgpL/DaAmP9wEArSGJWaCo/B4cgy/D5Q2lOH1P/LkItTq2rnPl96bo1aCOB6z0/RmEBA2w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"execa": "^4.0.0",
|
||||
"java-properties": "^1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"path-key": "^3.1.0",
|
||||
"shebang-command": "^2.0.0",
|
||||
"which": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"execa": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz",
|
||||
"integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cross-spawn": "^7.0.0",
|
||||
"get-stream": "^5.0.0",
|
||||
"human-signals": "^1.1.1",
|
||||
"is-stream": "^2.0.0",
|
||||
"merge-stream": "^2.0.0",
|
||||
"npm-run-path": "^4.0.0",
|
||||
"onetime": "^5.1.0",
|
||||
"signal-exit": "^3.0.2",
|
||||
"strip-final-newline": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"get-stream": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
|
||||
"integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"pump": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"is-stream": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz",
|
||||
"integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==",
|
||||
"dev": true
|
||||
},
|
||||
"npm-run-path": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
|
||||
"integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"path-key": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"path-key": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
||||
"dev": true
|
||||
},
|
||||
"shebang-command": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"shebang-regex": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"shebang-regex": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
||||
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||
"dev": true
|
||||
},
|
||||
"which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"isexe": "^2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@sinonjs/commons": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz",
|
||||
@@ -5413,6 +5608,12 @@
|
||||
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
|
||||
"dev": true
|
||||
},
|
||||
"at-least-node": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
|
||||
"integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
|
||||
"dev": true
|
||||
},
|
||||
"atob": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
|
||||
@@ -7320,6 +7521,16 @@
|
||||
"safe-buffer": "~5.1.1"
|
||||
}
|
||||
},
|
||||
"convert-units": {
|
||||
"version": "2.3.4",
|
||||
"resolved": "https://registry.npmjs.org/convert-units/-/convert-units-2.3.4.tgz",
|
||||
"integrity": "sha512-ERHfdA0UhHJp1IpwE6PnFJx8LqG7B1ZjJ20UvVCmopEnVCfER68Tbe3kvN63dLbYXDA2xFWRE6zd4Wsf0w7POg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash.foreach": "2.3.x",
|
||||
"lodash.keys": "2.3.x"
|
||||
}
|
||||
},
|
||||
"cookie": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
|
||||
@@ -8163,6 +8374,12 @@
|
||||
"domelementtype": "1"
|
||||
}
|
||||
},
|
||||
"dotenv": {
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
|
||||
"integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==",
|
||||
"dev": true
|
||||
},
|
||||
"duplexify": {
|
||||
"version": "3.7.1",
|
||||
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
|
||||
@@ -11930,6 +12147,16 @@
|
||||
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
|
||||
"dev": true
|
||||
},
|
||||
"isomorphic-fetch": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz",
|
||||
"integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"node-fetch": "^2.6.1",
|
||||
"whatwg-fetch": "^3.4.1"
|
||||
}
|
||||
},
|
||||
"isstream": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
|
||||
@@ -12058,6 +12285,12 @@
|
||||
"istanbul-lib-report": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"java-properties": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/java-properties/-/java-properties-1.0.2.tgz",
|
||||
"integrity": "sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ==",
|
||||
"dev": true
|
||||
},
|
||||
"jed": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jed/-/jed-1.1.1.tgz",
|
||||
@@ -14757,17 +14990,190 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
|
||||
},
|
||||
"lodash._basebind": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash._basebind/-/lodash._basebind-2.3.0.tgz",
|
||||
"integrity": "sha1-K1vEUqDhBhQ7IYafIzvbWHQX0kg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash._basecreate": "~2.3.0",
|
||||
"lodash._setbinddata": "~2.3.0",
|
||||
"lodash.isobject": "~2.3.0"
|
||||
}
|
||||
},
|
||||
"lodash._basecreate": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-2.3.0.tgz",
|
||||
"integrity": "sha1-m4ioak3P97fzxh2Dovz8BnHsneA=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash._renative": "~2.3.0",
|
||||
"lodash.isobject": "~2.3.0",
|
||||
"lodash.noop": "~2.3.0"
|
||||
}
|
||||
},
|
||||
"lodash._basecreatecallback": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash._basecreatecallback/-/lodash._basecreatecallback-2.3.0.tgz",
|
||||
"integrity": "sha1-N7KrF1kaM56YjbMln81GAZ16w2I=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash._setbinddata": "~2.3.0",
|
||||
"lodash.bind": "~2.3.0",
|
||||
"lodash.identity": "~2.3.0",
|
||||
"lodash.support": "~2.3.0"
|
||||
}
|
||||
},
|
||||
"lodash._basecreatewrapper": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash._basecreatewrapper/-/lodash._basecreatewrapper-2.3.0.tgz",
|
||||
"integrity": "sha1-qgxhrZYETDkzN2ExSDqXWcNlEkc=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash._basecreate": "~2.3.0",
|
||||
"lodash._setbinddata": "~2.3.0",
|
||||
"lodash._slice": "~2.3.0",
|
||||
"lodash.isobject": "~2.3.0"
|
||||
}
|
||||
},
|
||||
"lodash._createwrapper": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash._createwrapper/-/lodash._createwrapper-2.3.0.tgz",
|
||||
"integrity": "sha1-0arhEC2t9EDo4G/BM6bt1/4UYHU=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash._basebind": "~2.3.0",
|
||||
"lodash._basecreatewrapper": "~2.3.0",
|
||||
"lodash.isfunction": "~2.3.0"
|
||||
}
|
||||
},
|
||||
"lodash._objecttypes": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.3.0.tgz",
|
||||
"integrity": "sha1-aj6jmH3W7rgCGy1cnDA1Scwrrh4=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash._renative": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash._renative/-/lodash._renative-2.3.0.tgz",
|
||||
"integrity": "sha1-d9jt1M7SbdWXH54Vpfdy5OMX+9M=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash._setbinddata": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash._setbinddata/-/lodash._setbinddata-2.3.0.tgz",
|
||||
"integrity": "sha1-5WEEkKzRMnfVmFjZW18nJ/FQjwQ=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash._renative": "~2.3.0",
|
||||
"lodash.noop": "~2.3.0"
|
||||
}
|
||||
},
|
||||
"lodash._shimkeys": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash._shimkeys/-/lodash._shimkeys-2.3.0.tgz",
|
||||
"integrity": "sha1-YR+TFJ4+bHIQlrSHae8pU3rai6k=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash._objecttypes": "~2.3.0"
|
||||
}
|
||||
},
|
||||
"lodash._slice": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash._slice/-/lodash._slice-2.3.0.tgz",
|
||||
"integrity": "sha1-FHGYEyhZly5GgMoppZkshVZpqlw=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.bind": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-2.3.0.tgz",
|
||||
"integrity": "sha1-wqjhi2jl7MFS4rFoJmEW/qWwFsw=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash._createwrapper": "~2.3.0",
|
||||
"lodash._renative": "~2.3.0",
|
||||
"lodash._slice": "~2.3.0"
|
||||
}
|
||||
},
|
||||
"lodash.foreach": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-2.3.0.tgz",
|
||||
"integrity": "sha1-CDQEyR6EbudyRf3512UZxosq8Wg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash._basecreatecallback": "~2.3.0",
|
||||
"lodash.forown": "~2.3.0"
|
||||
}
|
||||
},
|
||||
"lodash.forown": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.forown/-/lodash.forown-2.3.0.tgz",
|
||||
"integrity": "sha1-JPtKr4ANRfwtxgv+w84EyDajrX8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash._basecreatecallback": "~2.3.0",
|
||||
"lodash._objecttypes": "~2.3.0",
|
||||
"lodash.keys": "~2.3.0"
|
||||
}
|
||||
},
|
||||
"lodash.get": {
|
||||
"version": "4.4.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
|
||||
"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
|
||||
},
|
||||
"lodash.identity": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.identity/-/lodash.identity-2.3.0.tgz",
|
||||
"integrity": "sha1-awGiEMlIU1XCqRO0i2cRIZoXPe0=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.isfunction": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-2.3.0.tgz",
|
||||
"integrity": "sha1-aylz5HpkfPEucNZ2rqE2Q3BuUmc=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.isobject": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.3.0.tgz",
|
||||
"integrity": "sha1-LhbT/Fg9qYMZaJU/LY5tc0NPZ5k=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash._objecttypes": "~2.3.0"
|
||||
}
|
||||
},
|
||||
"lodash.keys": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.3.0.tgz",
|
||||
"integrity": "sha1-s1D0+Syqn0WkouzwGEVM8vKK4lM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash._renative": "~2.3.0",
|
||||
"lodash._shimkeys": "~2.3.0",
|
||||
"lodash.isobject": "~2.3.0"
|
||||
}
|
||||
},
|
||||
"lodash.noop": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.noop/-/lodash.noop-2.3.0.tgz",
|
||||
"integrity": "sha1-MFnWKNUbv5N80qC2/Dp/ISpmnCw=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.sortby": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
|
||||
"integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.support": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.support/-/lodash.support-2.3.0.tgz",
|
||||
"integrity": "sha1-fq8DivTw1qq3drRKptz8gDNMm/0=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash._renative": "~2.3.0"
|
||||
}
|
||||
},
|
||||
"lodash.throttle": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
|
||||
@@ -15591,6 +15997,12 @@
|
||||
"lodash": "^4.17.15"
|
||||
}
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
|
||||
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==",
|
||||
"dev": true
|
||||
},
|
||||
"node-forge": {
|
||||
"version": "0.10.0",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
|
||||
@@ -20243,6 +20655,24 @@
|
||||
"postcss": "^7.0.2"
|
||||
}
|
||||
},
|
||||
"superstruct": {
|
||||
"version": "0.8.4",
|
||||
"resolved": "https://registry.npmjs.org/superstruct/-/superstruct-0.8.4.tgz",
|
||||
"integrity": "sha512-48Ors8IVWZm/tMr8r0Si6+mJiB7mkD7jqvIzktjJ4+EnP5tBp0qOpiM1J8sCUorKx+TXWrfb3i1UcjdD1YK/wA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"kind-of": "^6.0.2",
|
||||
"tiny-invariant": "^1.0.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"kind-of": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
|
||||
"integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
@@ -20501,6 +20931,12 @@
|
||||
"setimmediate": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"tiny-invariant": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz",
|
||||
"integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==",
|
||||
"dev": true
|
||||
},
|
||||
"tinycolor2": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz",
|
||||
@@ -22243,6 +22679,12 @@
|
||||
"iconv-lite": "0.4.24"
|
||||
}
|
||||
},
|
||||
"whatwg-fetch": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.5.0.tgz",
|
||||
"integrity": "sha512-jXkLtsR42xhXg7akoDKvKWE40eJeI+2KZqcp2h3NsOrRnDvtWX36KcKl30dy+hxECivdk2BVUHVNrPtoMBUx6A==",
|
||||
"dev": true
|
||||
},
|
||||
"whatwg-mimetype": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz",
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"license": "agpl",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "NODE_ENV=production webpack --progress --hide-modules --config webpack.js",
|
||||
"build": "NODE_ENV=production webpack --progress --config webpack.js",
|
||||
"dev": "NODE_ENV=development webpack --progress --config webpack.js",
|
||||
"watch": "NODE_ENV=development webpack --progress --watch --config webpack.js",
|
||||
"lint": "eslint --ext .js,.vue src",
|
||||
@@ -75,6 +75,7 @@
|
||||
"@nextcloud/eslint-config": "^2.2.0",
|
||||
"@nextcloud/eslint-plugin": "^1.5.0",
|
||||
"@nextcloud/webpack-vue-config": "^1.4.1",
|
||||
"@relative-ci/agent": "^1.4.0",
|
||||
"@vue/test-utils": "^1.1.2",
|
||||
"acorn": "^8.0.4",
|
||||
"babel-eslint": "^10.1.0",
|
||||
|
||||
8
relativeci.config.js
Normal file
8
relativeci.config.js
Normal file
@@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
// Allow the agent to pick up the current commit message
|
||||
includeCommitMessage: true,
|
||||
webpack: {
|
||||
// Path to Webpack stats JSON file
|
||||
stats: './js/webpack-stats.json'
|
||||
}
|
||||
};
|
||||
410
src/components/Controls.vue
Normal file
410
src/components/Controls.vue
Normal file
@@ -0,0 +1,410 @@
|
||||
<!--
|
||||
* @copyright Copyright (c) 2018 Michael Weimann <mail@michael-weimann.eu>
|
||||
*
|
||||
* @author Michael Weimann <mail@michael-weimann.eu>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="controls">
|
||||
<div v-if="overviewName" class="board-title">
|
||||
<div class="board-bullet icon-calendar-dark" />
|
||||
<h2>{{ overviewName }}</h2>
|
||||
</div>
|
||||
<div v-else-if="board" class="board-title">
|
||||
<div :style="{backgroundColor: '#' + board.color}" class="board-bullet" />
|
||||
<h2>{{ board.title }}</h2>
|
||||
<p v-if="showArchived">
|
||||
({{ t('deck', 'Archived cards') }})
|
||||
</p>
|
||||
</div>
|
||||
<div v-if="board" class="board-actions">
|
||||
<div v-if="canManage && !showArchived && !board.archived"
|
||||
id="stack-add"
|
||||
v-click-outside="hideAddStack">
|
||||
<Actions v-if="!isAddStackVisible">
|
||||
<ActionButton icon="icon-add" @click.stop="showAddStack">
|
||||
{{ t('deck', 'Add list') }}
|
||||
</ActionButton>
|
||||
</Actions>
|
||||
<form v-else @submit.prevent="addNewStack()">
|
||||
<label for="new-stack-input-main" class="hidden-visually">{{ t('deck', 'Add list') }}</label>
|
||||
<input id="new-stack-input-main"
|
||||
v-model="newStackTitle"
|
||||
v-focus
|
||||
type="text"
|
||||
class="no-close"
|
||||
:placeholder="t('deck', 'List name')"
|
||||
required>
|
||||
<input v-tooltip="t('deck', 'Add list')"
|
||||
class="icon-confirm"
|
||||
type="submit"
|
||||
value="">
|
||||
</form>
|
||||
</div>
|
||||
<div class="board-action-buttons">
|
||||
<Popover @show="filterVisible=true" @hide="filterVisible=false">
|
||||
<Actions slot="trigger" :title="t('deck', 'Apply filter')">
|
||||
<ActionButton v-if="isFilterActive" icon="icon-filter_set" />
|
||||
<ActionButton v-else icon="icon-filter" />
|
||||
</Actions>
|
||||
|
||||
<template>
|
||||
<div v-if="filterVisible" class="filter">
|
||||
<h3>{{ t('deck', 'Filter by tag') }}</h3>
|
||||
<div v-for="label in labelsSorted" :key="label.id" class="filter--item">
|
||||
<input
|
||||
:id="label.id"
|
||||
v-model="filter.tags"
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
:value="label.id"
|
||||
@change="setFilter">
|
||||
<label :for="label.id"><span class="label" :style="labelStyle(label)">{{ label.title }}</span></label>
|
||||
</div>
|
||||
|
||||
<h3>{{ t('deck', 'Filter by assigned user') }}</h3>
|
||||
<div class="filter--item">
|
||||
<input
|
||||
id="unassigned"
|
||||
v-model="filter.unassigned"
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
value="unassigned"
|
||||
@change="setFilter"
|
||||
@click="beforeSetFilter">
|
||||
<label for="unassigned">{{ t('deck', 'Unassigned') }}</label>
|
||||
</div>
|
||||
<div v-for="user in board.users" :key="user.uid" class="filter--item">
|
||||
<input
|
||||
:id="user.uid"
|
||||
v-model="filter.users"
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
:value="user.uid"
|
||||
@change="setFilter">
|
||||
<label :for="user.uid"><Avatar :user="user.uid" :size="24" :disable-menu="true" /> {{ user.displayname }}</label>
|
||||
</div>
|
||||
|
||||
<h3>{{ t('deck', 'Filter by due date') }}</h3>
|
||||
|
||||
<div class="filter--item">
|
||||
<input
|
||||
id="overdue"
|
||||
v-model="filter.due"
|
||||
type="radio"
|
||||
class="radio"
|
||||
value="overdue"
|
||||
@change="setFilter"
|
||||
@click="beforeSetFilter">
|
||||
<label for="overdue">{{ t('deck', 'Overdue') }}</label>
|
||||
</div>
|
||||
|
||||
<div class="filter--item">
|
||||
<input
|
||||
id="dueToday"
|
||||
v-model="filter.due"
|
||||
type="radio"
|
||||
class="radio"
|
||||
value="dueToday"
|
||||
@change="setFilter"
|
||||
@click="beforeSetFilter">
|
||||
<label for="dueToday">{{ t('deck', 'Next 24 hours') }}</label>
|
||||
</div>
|
||||
|
||||
<div class="filter--item">
|
||||
<input
|
||||
id="dueWeek"
|
||||
v-model="filter.due"
|
||||
type="radio"
|
||||
class="radio"
|
||||
value="dueWeek"
|
||||
@change="setFilter"
|
||||
@click="beforeSetFilter">
|
||||
<label for="dueWeek">{{ t('deck', 'Next 7 days') }}</label>
|
||||
</div>
|
||||
|
||||
<div class="filter--item">
|
||||
<input
|
||||
id="dueMonth"
|
||||
v-model="filter.due"
|
||||
type="radio"
|
||||
class="radio"
|
||||
value="dueMonth"
|
||||
@change="setFilter"
|
||||
@click="beforeSetFilter">
|
||||
<label for="dueMonth">{{ t('deck', 'Next 30 days') }}</label>
|
||||
</div>
|
||||
|
||||
<div class="filter--item">
|
||||
<input
|
||||
id="noDue"
|
||||
v-model="filter.due"
|
||||
type="radio"
|
||||
class="radio"
|
||||
value="noDue"
|
||||
@change="setFilter"
|
||||
@click="beforeSetFilter">
|
||||
<label for="noDue">{{ t('deck', 'No due date') }}</label>
|
||||
</div>
|
||||
|
||||
<Button :disabled="!isFilterActive" @click="clearFilter">
|
||||
{{ t('deck', 'Clear filter') }}
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</Popover>
|
||||
|
||||
<Actions>
|
||||
<ActionButton
|
||||
icon="icon-archive"
|
||||
@click="toggleShowArchived">
|
||||
{{ showArchived ? t('deck', 'Hide archived cards') : t('deck', 'Show archived cards') }}
|
||||
</ActionButton>
|
||||
<ActionButton v-if="compactMode"
|
||||
icon="icon-toggle-compact-collapsed"
|
||||
@click="toggleCompactMode">
|
||||
{{ t('deck', 'Toggle compact mode') }}
|
||||
</ActionButton>
|
||||
<ActionButton v-else
|
||||
icon="icon-toggle-compact-expanded"
|
||||
@click="toggleCompactMode">
|
||||
{{ t('deck', 'Toggle compact mode') }}
|
||||
</ActionButton>
|
||||
</Actions>
|
||||
<!-- FIXME: ActionRouter currently doesn't work as an inline action -->
|
||||
<Actions :title="t('deck', 'Details')">
|
||||
<ActionButton icon="icon-menu-sidebar" @click="toggleDetailsView" />
|
||||
</Actions>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters } from 'vuex'
|
||||
import { Actions, ActionButton, Popover, Avatar } from '@nextcloud/vue'
|
||||
import labelStyle from '../mixins/labelStyle'
|
||||
|
||||
export default {
|
||||
name: 'Controls',
|
||||
components: {
|
||||
Actions, ActionButton, Popover, Avatar,
|
||||
},
|
||||
mixins: [labelStyle],
|
||||
props: {
|
||||
board: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
overviewName: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
newStackTitle: '',
|
||||
stack: '',
|
||||
filterVisible: false,
|
||||
showArchived: false,
|
||||
isAddStackVisible: false,
|
||||
filter: { tags: [], users: [], due: '', unassigned: false },
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'canEdit',
|
||||
'canManage',
|
||||
]),
|
||||
...mapState({
|
||||
compactMode: state => state.compactMode,
|
||||
}),
|
||||
detailsRoute() {
|
||||
return {
|
||||
name: 'board.details',
|
||||
}
|
||||
},
|
||||
isFilterActive() {
|
||||
if (this.filter.tags.length !== 0 || this.filter.users.length !== 0 || this.filter.due !== '') {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
labelsSorted() {
|
||||
return [...this.board.labels].sort((a, b) => (a.title < b.title) ? -1 : 1)
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
board(current, previous) {
|
||||
if (current?.id !== previous?.id) {
|
||||
this.clearFilter()
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
beforeSetFilter(e) {
|
||||
if (this.filter.due === e.target.value) {
|
||||
this.filter.due = ''
|
||||
this.$store.dispatch('setFilter', { ...this.filter })
|
||||
}
|
||||
if (e.target.value === 'unassigned') {
|
||||
this.filter.users = []
|
||||
}
|
||||
},
|
||||
setFilter() {
|
||||
if (this.filter.users.length > 0) {
|
||||
this.filter.unassigned = false
|
||||
}
|
||||
this.$nextTick(() => this.$store.dispatch('setFilter', { ...this.filter }))
|
||||
},
|
||||
toggleNav() {
|
||||
this.$store.dispatch('toggleNav')
|
||||
},
|
||||
toggleCompactMode() {
|
||||
this.$store.dispatch('toggleCompactMode')
|
||||
},
|
||||
toggleShowArchived() {
|
||||
this.$store.dispatch('toggleShowArchived')
|
||||
this.showArchived = !this.showArchived
|
||||
},
|
||||
addNewStack() {
|
||||
this.stack = { title: this.newStackTitle }
|
||||
this.$store.dispatch('createStack', this.stack)
|
||||
this.newStackTitle = ''
|
||||
this.stack = null
|
||||
this.isAddStackVisible = false
|
||||
},
|
||||
showAddStack() {
|
||||
this.isAddStackVisible = true
|
||||
},
|
||||
hideAddStack() {
|
||||
this.isAddStackVisible = false
|
||||
},
|
||||
toggleDetailsView() {
|
||||
if (this.$route.name === 'board.details') {
|
||||
this.$router.push({ name: 'board' })
|
||||
} else {
|
||||
this.$router.push({ name: 'board.details' })
|
||||
}
|
||||
},
|
||||
clearFilter() {
|
||||
const filterReset = { tags: [], users: [], due: '' }
|
||||
this.$store.dispatch('setFilter', { ...filterReset })
|
||||
this.filter = filterReset
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.controls {
|
||||
display: flex;
|
||||
padding: 3px;
|
||||
height: 44px;
|
||||
padding-left: 44px;
|
||||
|
||||
.board-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
h2 {
|
||||
margin: 0;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.board-bullet {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
background-color: transparent;
|
||||
margin: 12px;
|
||||
margin-left: -4px;
|
||||
}
|
||||
}
|
||||
|
||||
#stack-add form {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
#app-navigation-toggle-custom {
|
||||
position: static;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
cursor: pointer;
|
||||
opacity: 1;
|
||||
display: inline-block !important;
|
||||
}
|
||||
|
||||
.board-actions {
|
||||
flex-grow: 1;
|
||||
order: 100;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.board-action-buttons {
|
||||
display: flex;
|
||||
button {
|
||||
border: 0;
|
||||
width: 44px;
|
||||
margin: 0 0 0 -1px;
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.filter--item {
|
||||
input + label {
|
||||
display: block;
|
||||
padding: 6px 0;
|
||||
vertical-align: middle;
|
||||
.avatardiv {
|
||||
vertical-align: middle;
|
||||
margin-bottom: 2px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
.label {
|
||||
padding: 5px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filter {
|
||||
width: 250px;
|
||||
max-height: 80vh;
|
||||
overflow: auto;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.filter h3 {
|
||||
margin-top: 0px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss">
|
||||
.tooltip-inner.popover-inner {
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
@@ -29,7 +29,7 @@
|
||||
<div class="board-list-bullet" />
|
||||
</div>
|
||||
<div class="board-list-title-cell">
|
||||
{{ t('deck', 'Title') }}
|
||||
{{ t('deck', 'Board name') }}
|
||||
</div>
|
||||
<div class="board-list-avatars-cell">
|
||||
{{ t('deck', 'Members') }}
|
||||
|
||||
@@ -21,58 +21,65 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<AttachmentDragAndDrop :card-id="cardId" class="drop-upload--sidebar">
|
||||
<button class="icon-upload" @click="clickAddNewAttachmment()">
|
||||
{{ t('deck', 'Upload attachment') }}
|
||||
</button>
|
||||
<input ref="localAttachments"
|
||||
type="file"
|
||||
style="display: none;"
|
||||
multiple
|
||||
@change="handleUploadFile">
|
||||
<ul class="attachment-list">
|
||||
<li v-for="attachment in uploadQueue" :key="attachment.name" class="attachment">
|
||||
<a class="fileicon" :style="mimetypeForAttachment('none')" />
|
||||
<div class="details">
|
||||
<a>
|
||||
<div class="filename">
|
||||
<span class="basename">{{ attachment.name }}</span>
|
||||
</div>
|
||||
<progress :value="attachment.progress" max="100" />
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
<li v-for="attachment in attachments"
|
||||
:key="attachment.id"
|
||||
class="attachment">
|
||||
<a class="fileicon" :style="mimetypeForAttachment(attachment.extendedData.mimetype)" :href="attachmentUrl(attachment)" />
|
||||
<div class="details">
|
||||
<a :href="attachmentUrl(attachment)" target="_blank">
|
||||
<div class="filename">
|
||||
<span class="basename">{{ attachment.data }}</span>
|
||||
</div>
|
||||
<span class="filesize">{{ formattedFileSize(attachment.extendedData.filesize) }}</span>
|
||||
<span class="filedate">{{ relativeDate(attachment.createdAt*1000) }}</span>
|
||||
<span class="filedate">{{ attachment.createdBy }}</span>
|
||||
</a>
|
||||
</div>
|
||||
<Actions v-if="selectable">
|
||||
<ActionButton icon="icon-confirm" @click="$emit('selectAttachment', attachment)">
|
||||
{{ t('deck', 'Add this attachment') }}
|
||||
</ActionButton>
|
||||
</Actions>
|
||||
<Actions v-if="removable">
|
||||
<ActionButton v-if="attachment.deletedAt === 0" icon="icon-delete" @click="$emit('deleteAttachment', attachment)">
|
||||
{{ t('deck', 'Delete Attachment') }}
|
||||
</ActionButton>
|
||||
<div>
|
||||
<h5>
|
||||
{{ t('deck', 'Attachments') }}
|
||||
<Actions>
|
||||
<ActionButton icon="icon-upload" @click="clickAddNewAttachmment()">
|
||||
{{ t('deck', 'Upload attachment') }}
|
||||
</ActionButton>
|
||||
</Actions>
|
||||
</h5>
|
||||
<AttachmentDragAndDrop :card-id="cardId" class="drop-upload--sidebar">
|
||||
<input ref="localAttachments"
|
||||
type="file"
|
||||
style="display: none;"
|
||||
multiple
|
||||
@change="handleUploadFile">
|
||||
<ul class="attachment-list">
|
||||
<li v-for="attachment in uploadQueue" :key="attachment.name" class="attachment">
|
||||
<a class="fileicon" :style="mimetypeForAttachment('none')" />
|
||||
<div class="details">
|
||||
<a>
|
||||
<div class="filename">
|
||||
<span class="basename">{{ attachment.name }}</span>
|
||||
</div>
|
||||
<progress :value="attachment.progress" max="100" />
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
<li v-for="attachment in attachments"
|
||||
:key="attachment.id"
|
||||
class="attachment">
|
||||
<a class="fileicon" :style="mimetypeForAttachment(attachment.extendedData.mimetype)" :href="attachmentUrl(attachment)" />
|
||||
<div class="details">
|
||||
<a :href="attachmentUrl(attachment)" target="_blank">
|
||||
<div class="filename">
|
||||
<span class="basename">{{ attachment.data }}</span>
|
||||
</div>
|
||||
<span class="filesize">{{ formattedFileSize(attachment.extendedData.filesize) }}</span>
|
||||
<span class="filedate">{{ relativeDate(attachment.createdAt*1000) }}</span>
|
||||
<span class="filedate">{{ attachment.createdBy }}</span>
|
||||
</a>
|
||||
</div>
|
||||
<Actions v-if="selectable">
|
||||
<ActionButton icon="icon-confirm" @click="$emit('selectAttachment', attachment)">
|
||||
{{ t('deck', 'Add this attachment') }}
|
||||
</ActionButton>
|
||||
</Actions>
|
||||
<Actions v-if="removable">
|
||||
<ActionButton v-if="attachment.deletedAt === 0" icon="icon-delete" @click="$emit('deleteAttachment', attachment)">
|
||||
{{ t('deck', 'Delete Attachment') }}
|
||||
</ActionButton>
|
||||
|
||||
<ActionButton v-else icon="icon-history" @click="$emit('restoreAttachment', attachment)">
|
||||
{{ t('deck', 'Restore Attachment') }}
|
||||
</ActionButton>
|
||||
</Actions>
|
||||
</li>
|
||||
</ul>
|
||||
</AttachmentDragAndDrop>
|
||||
<ActionButton v-else icon="icon-history" @click="$emit('restoreAttachment', attachment)">
|
||||
{{ t('deck', 'Restore Attachment') }}
|
||||
</ActionButton>
|
||||
</Actions>
|
||||
</li>
|
||||
</ul>
|
||||
</AttachmentDragAndDrop>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -171,6 +178,21 @@ export default {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
h5 {
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
margin-top: 20px;
|
||||
margin-bottom: 5px;
|
||||
color: var(--color-text-maxcontrast);
|
||||
|
||||
.icon-upload {
|
||||
background-size: 16px;
|
||||
float: right;
|
||||
margin-top: -14px;
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.icon-upload {
|
||||
padding-left: 35px;
|
||||
background-position: 10px center;
|
||||
|
||||
@@ -46,13 +46,6 @@
|
||||
<CardSidebarTabDetails :card="currentCard" />
|
||||
</AppSidebarTab>
|
||||
|
||||
<AppSidebarTab id="attachments"
|
||||
:order="1"
|
||||
:name="t('deck', 'Attachments')"
|
||||
icon="icon-attach">
|
||||
<CardSidebarTabAttachments :card="currentCard" />
|
||||
</AppSidebarTab>
|
||||
|
||||
<AppSidebarTab
|
||||
id="comments"
|
||||
:order="2"
|
||||
@@ -75,7 +68,6 @@
|
||||
import { ActionButton, AppSidebar, AppSidebarTab } from '@nextcloud/vue'
|
||||
import { mapState, mapGetters } from 'vuex'
|
||||
import CardSidebarTabDetails from './CardSidebarTabDetails'
|
||||
import CardSidebarTabAttachments from './CardSidebarTabAttachments'
|
||||
import CardSidebarTabComments from './CardSidebarTabComments'
|
||||
import CardSidebarTabActivity from './CardSidebarTabActivity'
|
||||
import relativeDate from '../../mixins/relativeDate'
|
||||
@@ -90,7 +82,6 @@ export default {
|
||||
AppSidebar,
|
||||
AppSidebarTab,
|
||||
ActionButton,
|
||||
CardSidebarTabAttachments,
|
||||
CardSidebarTabComments,
|
||||
CardSidebarTabActivity,
|
||||
CardSidebarTabDetails,
|
||||
|
||||
@@ -101,7 +101,6 @@
|
||||
:lang="lang"
|
||||
:formatter="format"
|
||||
:disabled="saving || !canEdit"
|
||||
:shortcuts="shortcuts"
|
||||
confirm />
|
||||
<Actions v-if="canEdit">
|
||||
<ActionButton v-if="copiedCard.duedate" icon="icon-delete" @click="removeDue()">
|
||||
@@ -118,6 +117,12 @@
|
||||
type="deck-card" />
|
||||
</div>
|
||||
|
||||
<AttachmentList
|
||||
:card-id="card.id"
|
||||
:removable="true"
|
||||
@deleteAttachment="deleteAttachment"
|
||||
@restoreAttachment="restoreAttachment" />
|
||||
|
||||
<Description :key="card.id" :card="card" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -126,7 +131,7 @@
|
||||
import { mapState, mapGetters } from 'vuex'
|
||||
import moment from '@nextcloud/moment'
|
||||
import { Avatar, Actions, ActionButton, Multiselect, DatetimePicker } from '@nextcloud/vue'
|
||||
|
||||
import AttachmentList from './AttachmentList'
|
||||
import { CollectionList } from 'nextcloud-vue-collections'
|
||||
import Color from '../../mixins/color'
|
||||
import {
|
||||
@@ -147,6 +152,7 @@ export default {
|
||||
ActionButton,
|
||||
Avatar,
|
||||
CollectionList,
|
||||
AttachmentList,
|
||||
},
|
||||
mixins: [Color],
|
||||
props: {
|
||||
@@ -177,23 +183,6 @@ export default {
|
||||
stringify: this.stringify,
|
||||
parse: this.parse,
|
||||
},
|
||||
shortcuts: [
|
||||
{
|
||||
text: 'Today',
|
||||
onClick() {
|
||||
const date = new Date()
|
||||
return date
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Tomorrow',
|
||||
onClick() {
|
||||
const date = new Date()
|
||||
date.setTime(date.getTime() + 3600 * 1000 * 24)
|
||||
return date
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -252,6 +241,12 @@ export default {
|
||||
this.initialize()
|
||||
},
|
||||
methods: {
|
||||
deleteAttachment(attachment) {
|
||||
this.$store.dispatch('deleteAttachment', attachment)
|
||||
},
|
||||
restoreAttachment(attachment) {
|
||||
this.$store.dispatch('restoreAttachment', attachment)
|
||||
},
|
||||
async initialize() {
|
||||
if (!this.card) {
|
||||
return
|
||||
@@ -334,10 +329,6 @@ export default {
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.section-wrapper::v-deep .mx-datepicker-main .mx-datepicker-popup {
|
||||
left: 0 !important;
|
||||
}
|
||||
|
||||
.section-wrapper {
|
||||
display: flex;
|
||||
max-width: 100%;
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
type="checkbox"
|
||||
class="checkbox">
|
||||
<label for="toggle-modal">
|
||||
{{ t('deck', 'Use modal card view') }}
|
||||
{{ t('deck', 'Use bigger card view') }}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,22 +1,17 @@
|
||||
{
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.6",
|
||||
"behat/behat": "^3.0",
|
||||
"guzzlehttp/guzzle": "~5.0",
|
||||
"phpunit/phpunit": "~6.5",
|
||||
"behat/behat": "~3.8.0",
|
||||
"guzzlehttp/guzzle": "6.5.2",
|
||||
"jarnaiz/behat-junit-formatter": "^1.3",
|
||||
"sabre/dav": "3.2"
|
||||
"sabre/dav": "3.2.3",
|
||||
"symfony/event-dispatcher": "~4.4"
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"../../../../build/integration/features/bootstrap/Auth.php",
|
||||
"../../../../build/integration/features/bootstrap/Provisioning.php",
|
||||
"../../../../build/integration/features/bootstrap/Sharing.php",
|
||||
"../../../../build/integration/features/bootstrap/WebDav.php",
|
||||
"../../../../build/integration/features/bootstrap/Trashbin.php"
|
||||
],
|
||||
"psr-0": {
|
||||
"": [
|
||||
"features/bootstrap/"
|
||||
"features/bootstrap/",
|
||||
"../../../../build/integration/features/bootstrap/"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,451 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* @author Christoph Wurst <christoph@owncloud.com>
|
||||
* @author Joas Schilling <coding@schilljs.com>
|
||||
* @author Lukas Reschke <lukas@statuscode.ch>
|
||||
* @author Sergio Bertolin <sbertolin@solidgear.es>
|
||||
* @author Thomas Müller <thomas.mueller@tmit.eu>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Cookie\CookieJar;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use GuzzleHttp\Message\ResponseInterface;
|
||||
|
||||
require __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
trait BasicStructure {
|
||||
use Auth;
|
||||
|
||||
/** @var string */
|
||||
private $currentUser = '';
|
||||
|
||||
/** @var string */
|
||||
private $currentServer = '';
|
||||
|
||||
/** @var string */
|
||||
private $baseUrl = '';
|
||||
|
||||
/** @var int */
|
||||
private $apiVersion = 1;
|
||||
|
||||
/** @var ResponseInterface */
|
||||
private $response = null;
|
||||
|
||||
/** @var CookieJar */
|
||||
private $cookieJar;
|
||||
|
||||
/** @var string */
|
||||
private $requestToken;
|
||||
|
||||
public function __construct($baseUrl, $admin, $regular_user_password) {
|
||||
|
||||
// Initialize your context here
|
||||
$this->baseUrl = $baseUrl;
|
||||
$this->adminUser = $admin;
|
||||
$this->regularUser = $regular_user_password;
|
||||
$this->localBaseUrl = $this->baseUrl;
|
||||
$this->remoteBaseUrl = $this->baseUrl;
|
||||
$this->currentServer = 'LOCAL';
|
||||
$this->cookieJar = new CookieJar();
|
||||
|
||||
// in case of ci deployment we take the server url from the environment
|
||||
$testServerUrl = getenv('TEST_SERVER_URL');
|
||||
if ($testServerUrl !== false) {
|
||||
$this->baseUrl = $testServerUrl;
|
||||
$this->localBaseUrl = $testServerUrl;
|
||||
}
|
||||
|
||||
// federated server url from the environment
|
||||
$testRemoteServerUrl = getenv('TEST_SERVER_FED_URL');
|
||||
if ($testRemoteServerUrl !== false) {
|
||||
$this->remoteBaseUrl = $testRemoteServerUrl;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given /^using api version "(\d+)"$/
|
||||
* @param string $version
|
||||
*/
|
||||
public function usingApiVersion($version) {
|
||||
$this->apiVersion = (int) $version;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given /^As an "([^"]*)"$/
|
||||
* @param string $user
|
||||
*/
|
||||
public function asAn($user) {
|
||||
$this->currentUser = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given /^Using server "(LOCAL|REMOTE)"$/
|
||||
* @param string $server
|
||||
* @return string Previous used server
|
||||
*/
|
||||
public function usingServer($server) {
|
||||
$previousServer = $this->currentServer;
|
||||
if ($server === 'LOCAL') {
|
||||
$this->baseUrl = $this->localBaseUrl;
|
||||
$this->currentServer = 'LOCAL';
|
||||
return $previousServer;
|
||||
} else {
|
||||
$this->baseUrl = $this->remoteBaseUrl;
|
||||
$this->currentServer = 'REMOTE';
|
||||
return $previousServer;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^sending "([^"]*)" to "([^"]*)"$/
|
||||
* @param string $verb
|
||||
* @param string $url
|
||||
*/
|
||||
public function sendingTo($verb, $url) {
|
||||
$this->sendingToWith($verb, $url, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the xml answer to get ocs response which doesn't match with
|
||||
* http one in v1 of the api.
|
||||
* @param ResponseInterface $response
|
||||
* @return string
|
||||
*/
|
||||
public function getOCSResponse($response) {
|
||||
return $response->xml()->meta[0]->statuscode;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is needed to use a vertical fashion in the gherkin tables.
|
||||
* @param array $arrayOfArrays
|
||||
* @return array
|
||||
*/
|
||||
public function simplifyArray($arrayOfArrays) {
|
||||
$a = array_map(function ($subArray) {
|
||||
return $subArray[0];
|
||||
}, $arrayOfArrays);
|
||||
return $a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^sending "([^"]*)" to "([^"]*)" with$/
|
||||
* @param string $verb
|
||||
* @param string $url
|
||||
* @param \Behat\Gherkin\Node\TableNode $body
|
||||
*/
|
||||
public function sendingToWith($verb, $url, $body) {
|
||||
$fullUrl = $this->baseUrl . "v{$this->apiVersion}.php" . $url;
|
||||
$client = new Client();
|
||||
$options = [];
|
||||
if ($this->currentUser === 'admin') {
|
||||
$options['auth'] = $this->adminUser;
|
||||
} else {
|
||||
$options['auth'] = [$this->currentUser, $this->regularUser];
|
||||
}
|
||||
$options['headers'] = [
|
||||
'OCS_APIREQUEST' => 'true'
|
||||
];
|
||||
if ($body instanceof \Behat\Gherkin\Node\TableNode) {
|
||||
$fd = $body->getRowsHash();
|
||||
$options['body'] = $fd;
|
||||
}
|
||||
|
||||
// TODO: Fix this hack!
|
||||
if ($verb === 'PUT' && $body === null) {
|
||||
$options['body'] = [
|
||||
'foo' => 'bar',
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
$this->response = $client->send($client->createRequest($verb, $fullUrl, $options));
|
||||
} catch (ClientException $ex) {
|
||||
$this->response = $ex->getResponse();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^sending "([^"]*)" with exact url to "([^"]*)"$/
|
||||
* @param string $verb
|
||||
* @param string $url
|
||||
*/
|
||||
public function sendingToDirectUrl($verb, $url) {
|
||||
$this->sendingToWithDirectUrl($verb, $url, null);
|
||||
}
|
||||
|
||||
public function sendingToWithDirectUrl($verb, $url, $body) {
|
||||
$fullUrl = substr($this->baseUrl, 0, -5) . $url;
|
||||
$client = new Client();
|
||||
$options = [];
|
||||
if ($this->currentUser === 'admin') {
|
||||
$options['auth'] = $this->adminUser;
|
||||
} else {
|
||||
$options['auth'] = [$this->currentUser, $this->regularUser];
|
||||
}
|
||||
if ($body instanceof \Behat\Gherkin\Node\TableNode) {
|
||||
$fd = $body->getRowsHash();
|
||||
$options['body'] = $fd;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->response = $client->send($client->createRequest($verb, $fullUrl, $options));
|
||||
} catch (ClientException $ex) {
|
||||
$this->response = $ex->getResponse();
|
||||
}
|
||||
}
|
||||
|
||||
public function isExpectedUrl($possibleUrl, $finalPart) {
|
||||
$baseUrlChopped = substr($this->baseUrl, 0, -4);
|
||||
$endCharacter = strlen($baseUrlChopped) + strlen($finalPart);
|
||||
return (substr($possibleUrl,0,$endCharacter) == "$baseUrlChopped" . "$finalPart");
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^the OCS status code should be "([^"]*)"$/
|
||||
* @param int $statusCode
|
||||
*/
|
||||
public function theOCSStatusCodeShouldBe($statusCode) {
|
||||
PHPUnit_Framework_Assert::assertEquals($statusCode, $this->getOCSResponse($this->response));
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^the HTTP status code should be "([^"]*)"$/
|
||||
* @param int $statusCode
|
||||
*/
|
||||
public function theHTTPStatusCodeShouldBe($statusCode) {
|
||||
PHPUnit_Framework_Assert::assertEquals($statusCode, $this->response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^the Content-Type should be "([^"]*)"$/
|
||||
* @param string $contentType
|
||||
*/
|
||||
public function theContentTypeShouldbe($contentType) {
|
||||
PHPUnit_Framework_Assert::assertEquals($contentType, $this->response->getHeader('Content-Type'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ResponseInterface $response
|
||||
*/
|
||||
private function extracRequestTokenFromResponse(ResponseInterface $response) {
|
||||
$this->requestToken = substr(preg_replace('/(.*)data-requesttoken="(.*)">(.*)/sm', '\2', $response->getBody()->getContents()), 0, 89);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given Logging in using web as :user
|
||||
* @param string $user
|
||||
*/
|
||||
public function loggingInUsingWebAs($user) {
|
||||
$loginUrl = substr($this->baseUrl, 0, -5) . '/login';
|
||||
// Request a new session and extract CSRF token
|
||||
$client = new Client();
|
||||
$response = $client->get(
|
||||
$loginUrl,
|
||||
[
|
||||
'cookies' => $this->cookieJar,
|
||||
]
|
||||
);
|
||||
$this->extracRequestTokenFromResponse($response);
|
||||
|
||||
// Login and extract new token
|
||||
$password = ($user === 'admin') ? 'admin' : '123456';
|
||||
$client = new Client();
|
||||
$response = $client->post(
|
||||
$loginUrl,
|
||||
[
|
||||
'body' => [
|
||||
'user' => $user,
|
||||
'password' => $password,
|
||||
'requesttoken' => $this->requestToken,
|
||||
],
|
||||
'cookies' => $this->cookieJar,
|
||||
]
|
||||
);
|
||||
$this->extracRequestTokenFromResponse($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When Sending a :method to :url with requesttoken
|
||||
* @param string $method
|
||||
* @param string $url
|
||||
*/
|
||||
public function sendingAToWithRequesttoken($method, $url) {
|
||||
$baseUrl = substr($this->baseUrl, 0, -5);
|
||||
|
||||
$client = new Client();
|
||||
$request = $client->createRequest(
|
||||
$method,
|
||||
$baseUrl . $url,
|
||||
[
|
||||
'cookies' => $this->cookieJar,
|
||||
]
|
||||
);
|
||||
$request->addHeader('requesttoken', $this->requestToken);
|
||||
try {
|
||||
$this->response = $client->send($request);
|
||||
} catch (ClientException $e) {
|
||||
$this->response = $e->getResponse();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @When Sending a :method to :url without requesttoken
|
||||
* @param string $method
|
||||
* @param string $url
|
||||
*/
|
||||
public function sendingAToWithoutRequesttoken($method, $url) {
|
||||
$baseUrl = substr($this->baseUrl, 0, -5);
|
||||
|
||||
$client = new Client();
|
||||
$request = $client->createRequest(
|
||||
$method,
|
||||
$baseUrl . $url,
|
||||
[
|
||||
'cookies' => $this->cookieJar,
|
||||
]
|
||||
);
|
||||
try {
|
||||
$this->response = $client->send($request);
|
||||
} catch (ClientException $e) {
|
||||
$this->response = $e->getResponse();
|
||||
}
|
||||
}
|
||||
|
||||
public static function removeFile($path, $filename) {
|
||||
if (file_exists("$path" . "$filename")) {
|
||||
unlink("$path" . "$filename");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given User :user modifies text of :filename with text :text
|
||||
* @param string $user
|
||||
* @param string $filename
|
||||
* @param string $text
|
||||
*/
|
||||
public function modifyTextOfFile($user, $filename, $text) {
|
||||
self::removeFile("../../data/$user/files", "$filename");
|
||||
file_put_contents("../../data/$user/files" . "$filename", "$text");
|
||||
}
|
||||
|
||||
public function createFileSpecificSize($name, $size) {
|
||||
$file = fopen("work/" . "$name", 'w');
|
||||
fseek($file, $size - 1 ,SEEK_CUR);
|
||||
fwrite($file,'a'); // write a dummy char at SIZE position
|
||||
fclose($file);
|
||||
}
|
||||
|
||||
public function createFileWithText($name, $text) {
|
||||
$file = fopen("work/" . "$name", 'w');
|
||||
fwrite($file, $text);
|
||||
fclose($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given file :filename of size :size is created in local storage
|
||||
* @param string $filename
|
||||
* @param string $size
|
||||
*/
|
||||
public function fileIsCreatedInLocalStorageWithSize($filename, $size) {
|
||||
$this->createFileSpecificSize("local_storage/$filename", $size);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given file :filename with text :text is created in local storage
|
||||
* @param string $filename
|
||||
* @param string $text
|
||||
*/
|
||||
public function fileIsCreatedInLocalStorageWithText($filename, $text) {
|
||||
$this->createFileWithText("local_storage/$filename", $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When Sleep for :seconds seconds
|
||||
* @param int $seconds
|
||||
*/
|
||||
public function sleepForSeconds($seconds) {
|
||||
sleep((int)$seconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* @BeforeSuite
|
||||
*/
|
||||
public static function addFilesToSkeleton() {
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
file_put_contents("../../core/skeleton/" . "textfile" . "$i" . ".txt", "Nextcloud test text file\n");
|
||||
}
|
||||
if (!file_exists("../../core/skeleton/FOLDER")) {
|
||||
mkdir("../../core/skeleton/FOLDER", 0777, true);
|
||||
}
|
||||
if (!file_exists("../../core/skeleton/PARENT")) {
|
||||
mkdir("../../core/skeleton/PARENT", 0777, true);
|
||||
}
|
||||
file_put_contents("../../core/skeleton/PARENT/" . "parent.txt", "Nextcloud test text file\n");
|
||||
if (!file_exists("../../core/skeleton/PARENT/CHILD")) {
|
||||
mkdir("../../core/skeleton/PARENT/CHILD", 0777, true);
|
||||
}
|
||||
file_put_contents("../../core/skeleton/PARENT/CHILD/" . "child.txt", "Nextcloud test text file\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* @AfterSuite
|
||||
*/
|
||||
public static function removeFilesFromSkeleton() {
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
self::removeFile("../../core/skeleton/", "textfile" . "$i" . ".txt");
|
||||
}
|
||||
if (is_dir("../../core/skeleton/FOLDER")) {
|
||||
rmdir("../../core/skeleton/FOLDER");
|
||||
}
|
||||
self::removeFile("../../core/skeleton/PARENT/CHILD/", "child.txt");
|
||||
if (is_dir("../../core/skeleton/PARENT/CHILD")) {
|
||||
rmdir("../../core/skeleton/PARENT/CHILD");
|
||||
}
|
||||
self::removeFile("../../core/skeleton/PARENT/", "parent.txt");
|
||||
if (is_dir("../../core/skeleton/PARENT")) {
|
||||
rmdir("../../core/skeleton/PARENT");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @BeforeScenario @local_storage
|
||||
*/
|
||||
public static function removeFilesFromLocalStorageBefore() {
|
||||
$dir = "./work/local_storage/";
|
||||
$di = new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS);
|
||||
$ri = new RecursiveIteratorIterator($di, RecursiveIteratorIterator::CHILD_FIRST);
|
||||
foreach ($ri as $file) {
|
||||
$file->isDir() ? rmdir($file) : unlink($file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @AfterScenario @local_storage
|
||||
*/
|
||||
public static function removeFilesFromLocalStorageAfter() {
|
||||
$dir = "./work/local_storage/";
|
||||
$di = new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS);
|
||||
$ri = new RecursiveIteratorIterator($di, RecursiveIteratorIterator::CHILD_FIRST);
|
||||
foreach ($ri as $file) {
|
||||
$file->isDir() ? rmdir($file) : unlink($file);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,23 +28,7 @@ class FeatureContext implements Context {
|
||||
* @When Sending a :method to :url with JSON
|
||||
*/
|
||||
public function sendingAToWithJSON($method, $url, \Behat\Gherkin\Node\PyStringNode $data) {
|
||||
$baseUrl = substr($this->baseUrl, 0, -5);
|
||||
|
||||
$client = new Client;
|
||||
$request = $client->createRequest(
|
||||
$method,
|
||||
$baseUrl . $url,
|
||||
[
|
||||
'cookies' => $this->cookieJar,
|
||||
'json' => json_decode($data)
|
||||
]
|
||||
);
|
||||
$request->addHeader('requesttoken', $this->requestToken);
|
||||
try {
|
||||
$this->response = $client->send($request);
|
||||
} catch (ClientException $e) {
|
||||
$this->response = $e->getResponse();
|
||||
}
|
||||
$this->sendJSONrequest($method, $url, json_decode($data));
|
||||
}
|
||||
|
||||
|
||||
@@ -149,17 +133,18 @@ class FeatureContext implements Context {
|
||||
$baseUrl = substr($this->baseUrl, 0, -5);
|
||||
|
||||
$client = new Client;
|
||||
$request = $client->createRequest(
|
||||
$method,
|
||||
$baseUrl . $url,
|
||||
[
|
||||
'cookies' => $this->cookieJar,
|
||||
'json' => $data
|
||||
]
|
||||
);
|
||||
$request->addHeader('requesttoken', $this->requestToken);
|
||||
try {
|
||||
$this->response = $client->send($request);
|
||||
$this->response = $client->request(
|
||||
$method,
|
||||
$baseUrl . $url,
|
||||
[
|
||||
'cookies' => $this->cookieJar,
|
||||
'json' => $data,
|
||||
'headers' => [
|
||||
'requesttoken' => $this->requestToken
|
||||
]
|
||||
]
|
||||
);
|
||||
} catch (ClientException $e) {
|
||||
$this->response = $e->getResponse();
|
||||
}
|
||||
|
||||
@@ -19,6 +19,13 @@ const config = {
|
||||
'node_modules',
|
||||
],
|
||||
},
|
||||
stats: {
|
||||
context: path.resolve(__dirname, 'src'),
|
||||
assets: true,
|
||||
entrypoints: true,
|
||||
chunks: true,
|
||||
modules: true
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = merge(webpackConfig, config)
|
||||
|
||||
Reference in New Issue
Block a user