Compare commits
340 Commits
v1.8.5
...
backport/4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9932a7b972 | ||
|
|
e10ab29f28 | ||
|
|
0ac494df9d | ||
|
|
a8ce89d487 | ||
|
|
2fcbbbbc23 | ||
|
|
4f673fe598 | ||
|
|
02ba8f85ce | ||
|
|
f88d53d7f1 | ||
|
|
6a5eecea76 | ||
|
|
b61667c90b | ||
|
|
6864846d84 | ||
|
|
962256acb0 | ||
|
|
9db8c9af80 | ||
|
|
88bd868e76 | ||
|
|
3c88551ad5 | ||
|
|
0872b1b606 | ||
|
|
65242c2b6c | ||
|
|
a3527542f9 | ||
|
|
28e4b12428 | ||
|
|
628cd333f1 | ||
|
|
ccfaa4886a | ||
|
|
0ef238da0e | ||
|
|
333b904335 | ||
|
|
32a956af02 | ||
|
|
b7dad3134d | ||
|
|
42f76117f2 | ||
|
|
be1c646bd7 | ||
|
|
137fece0b8 | ||
|
|
81e5922dfd | ||
|
|
ccc216d388 | ||
|
|
3f7ab93df9 | ||
|
|
a621aacac0 | ||
|
|
bb7ed1bc31 | ||
|
|
4d0f7ddef3 | ||
|
|
bd13cc09d8 | ||
|
|
cb3925faeb | ||
|
|
ac11571a7d | ||
|
|
fd4564450b | ||
|
|
acbd42d6a4 | ||
|
|
458831dfc7 | ||
|
|
8a5098951c | ||
|
|
99260ae966 | ||
|
|
ea592251f9 | ||
|
|
6c80674ce0 | ||
|
|
060e8cd7db | ||
|
|
f05bd86be9 | ||
|
|
1ce7856cae | ||
|
|
678e4c9569 | ||
|
|
4fe4216cb5 | ||
|
|
85e7305d8d | ||
|
|
090378580c | ||
|
|
3f8efb5368 | ||
|
|
c1f2fd452b | ||
|
|
576c47bdef | ||
|
|
e907aeb8c0 | ||
|
|
c51119d051 | ||
|
|
ee72cb4660 | ||
|
|
d4f2ed263f | ||
|
|
e6f8796b63 | ||
|
|
846443d003 | ||
|
|
144c27d403 | ||
|
|
f374084989 | ||
|
|
0656e53508 | ||
|
|
7506126ef7 | ||
|
|
5214721875 | ||
|
|
34f7b726cd | ||
|
|
ea5baa5339 | ||
|
|
7c3aa301ac | ||
|
|
313bc90ce9 | ||
|
|
be06c7ab70 | ||
|
|
5964da382e | ||
|
|
eaa2facc29 | ||
|
|
13e836760a | ||
|
|
73815827e1 | ||
|
|
34107ad06f | ||
|
|
90535b3c30 | ||
|
|
312376e596 | ||
|
|
04dbe45f29 | ||
|
|
bc38340daf | ||
|
|
9b89897d74 | ||
|
|
f411181dcf | ||
|
|
080465d48a | ||
|
|
2358645037 | ||
|
|
c6545c7c00 | ||
|
|
3baa2fc1a0 | ||
|
|
fd297dc92e | ||
|
|
4608e4b77b | ||
|
|
524b8ba90e | ||
|
|
569e568136 | ||
|
|
d9c563d5bf | ||
|
|
f51956891e | ||
|
|
053ecdacee | ||
|
|
8f755ee3eb | ||
|
|
fa6d819409 | ||
|
|
fa17dfac3a | ||
|
|
92b20efa89 | ||
|
|
2849177998 | ||
|
|
0bbeedb7e3 | ||
|
|
918f22f7a6 | ||
|
|
ecac9b772b | ||
|
|
f0a4469be5 | ||
|
|
f47f4983d9 | ||
|
|
c9b8735804 | ||
|
|
c9d7647200 | ||
|
|
4a8410f0fc | ||
|
|
4fbb383c0f | ||
|
|
98361ff096 | ||
|
|
31f2ca49d5 | ||
|
|
e0e6f86801 | ||
|
|
0bc4e1fa7b | ||
|
|
0d137a372c | ||
|
|
2d1ad75f35 | ||
|
|
e381e021d6 | ||
|
|
59f0813aa5 | ||
|
|
90d14c544f | ||
|
|
02ca7acf4f | ||
|
|
0725b55773 | ||
|
|
1219c0aed0 | ||
|
|
da57b9c7e1 | ||
|
|
e67822167b | ||
|
|
20e3729b86 | ||
|
|
5c3d88dae0 | ||
|
|
2fb86b11ec | ||
|
|
5d9c504842 | ||
|
|
1610968ed7 | ||
|
|
aad4aafc03 | ||
|
|
40e3987ae6 | ||
|
|
d3a77e4a11 | ||
|
|
c93189ff9a | ||
|
|
bca7b3386c | ||
|
|
3c7cc0b09c | ||
|
|
8e35b67a0d | ||
|
|
bfb292c54d | ||
|
|
ab6c775f5c | ||
|
|
e3e9e4596a | ||
|
|
fefe77c245 | ||
|
|
42108cb223 | ||
|
|
72d7a245d2 | ||
|
|
e187a665d5 | ||
|
|
01ab23dc82 | ||
|
|
004814ace7 | ||
|
|
326a38557b | ||
|
|
6e43ed5fce | ||
|
|
8b92610f67 | ||
|
|
6cc919b62d | ||
|
|
3ff47580ad | ||
|
|
a1a2120d43 | ||
|
|
273222bb21 | ||
|
|
1255e5a6c9 | ||
|
|
dace366b6c | ||
|
|
a2f5414c48 | ||
|
|
1887527197 | ||
|
|
6d35c2f1c1 | ||
|
|
51626698c4 | ||
|
|
5ed2b23f76 | ||
|
|
502b5acb69 | ||
|
|
03b5257fea | ||
|
|
39b158f92d | ||
|
|
3163136b73 | ||
|
|
9791444167 | ||
|
|
77acf95106 | ||
|
|
6505cc99ce | ||
|
|
54d1a40eeb | ||
|
|
ab577a95eb | ||
|
|
8537d4acef | ||
|
|
d0fda8a546 | ||
|
|
031f69523d | ||
|
|
3864f29bbd | ||
|
|
ee1b32fcd7 | ||
|
|
6ad6b1680f | ||
|
|
b158d79403 | ||
|
|
2d823e3321 | ||
|
|
9a153356c2 | ||
|
|
eaf5e6ff29 | ||
|
|
585a02a3d2 | ||
|
|
30acd27c94 | ||
|
|
71db398e9a | ||
|
|
d8f44e2c94 | ||
|
|
20c8cb12e3 | ||
|
|
60eadb7035 | ||
|
|
17e71c5c9a | ||
|
|
289c0f72dd | ||
|
|
c2a326d538 | ||
|
|
438bb8cb0d | ||
|
|
b98f8e3b07 | ||
|
|
333206c549 | ||
|
|
5359ec34ef | ||
|
|
ab7da85964 | ||
|
|
7ecb215957 | ||
|
|
df89839b51 | ||
|
|
03ed40df67 | ||
|
|
ad1429b4a2 | ||
|
|
eb03cd6814 | ||
|
|
566846fe4d | ||
|
|
f4c09fefd9 | ||
|
|
bd6e632055 | ||
|
|
75ddc058b8 | ||
|
|
8558ce432a | ||
|
|
7586084980 | ||
|
|
863de715a4 | ||
|
|
04abf47653 | ||
|
|
45c9a8ba27 | ||
|
|
3d10521524 | ||
|
|
ef616f37bd | ||
|
|
e64c36ffce | ||
|
|
837d925536 | ||
|
|
feaeb90f76 | ||
|
|
2ea94a12ad | ||
|
|
c8cf8238b8 | ||
|
|
16f41f74b3 | ||
|
|
3a78eea105 | ||
|
|
8e158972f4 | ||
|
|
b12a1306eb | ||
|
|
1175c8b605 | ||
|
|
4b06ef3ac0 | ||
|
|
5f59f485f2 | ||
|
|
2f28015b91 | ||
|
|
675aff612d | ||
|
|
9ad65af60e | ||
|
|
2210c05e28 | ||
|
|
a189139a04 | ||
|
|
c6a209c63a | ||
|
|
1968d8493b | ||
|
|
a94eb0cd31 | ||
|
|
b2bbf31f46 | ||
|
|
2b959c10dd | ||
|
|
81669635c5 | ||
|
|
7350a2a811 | ||
|
|
e677cbcdac | ||
|
|
6d7461d0d1 | ||
|
|
86d94166f0 | ||
|
|
a664545ebf | ||
|
|
360bd4192b | ||
|
|
da3f2b8ee4 | ||
|
|
78aa411e09 | ||
|
|
2d318c0ac1 | ||
|
|
b58f598044 | ||
|
|
bad2a19be6 | ||
|
|
a89bdab5e3 | ||
|
|
5b2c03f733 | ||
|
|
552e10d79d | ||
|
|
40f39fa21a | ||
|
|
d5c1833638 | ||
|
|
36dd98c93e | ||
|
|
ccbec7f5ac | ||
|
|
0359e89b22 | ||
|
|
e9f8634334 | ||
|
|
1889c0c4f9 | ||
|
|
1ac501db79 | ||
|
|
c9ddd4f997 | ||
|
|
e8a0d7c47c | ||
|
|
8ace53bfe2 | ||
|
|
89c3490015 | ||
|
|
72bc2f438a | ||
|
|
1183459608 | ||
|
|
a4e6c7b746 | ||
|
|
b99fb6e8f6 | ||
|
|
f613a60f7c | ||
|
|
7c77cde669 | ||
|
|
9a7bde4cbd | ||
|
|
b807a71f1f | ||
|
|
33942236f0 | ||
|
|
3dd3301ffc | ||
|
|
0d310d317e | ||
|
|
82515e5731 | ||
|
|
4cd4707559 | ||
|
|
90c14861c4 | ||
|
|
17a51501ec | ||
|
|
86707f18dd | ||
|
|
a28cd746f1 | ||
|
|
10ad995cd0 | ||
|
|
856c5f50c4 | ||
|
|
0a9d96d964 | ||
|
|
1d15921a57 | ||
|
|
b9826cb29f | ||
|
|
94c13bbbdb | ||
|
|
549f9c9045 | ||
|
|
ac16521404 | ||
|
|
4be298f537 | ||
|
|
10dead9d9f | ||
|
|
f9cb601a99 | ||
|
|
efdd3b0056 | ||
|
|
a18f550dc1 | ||
|
|
d808b9fef7 | ||
|
|
f4742259f2 | ||
|
|
3fe5da0ee1 | ||
|
|
17ae3a9a39 | ||
|
|
1ff588a15a | ||
|
|
2bb04ef298 | ||
|
|
055dda909e | ||
|
|
f364709318 | ||
|
|
84de1c0198 | ||
|
|
413017d333 | ||
|
|
5c069e5413 | ||
|
|
9d9cdb0f95 | ||
|
|
74f45e2e54 | ||
|
|
f82c48ad29 | ||
|
|
ece052a20d | ||
|
|
8a9a36ed10 | ||
|
|
82f2a2c363 | ||
|
|
3016659c81 | ||
|
|
b9ab0f14ab | ||
|
|
2d90ed7414 | ||
|
|
5d87f05994 | ||
|
|
9ef183bcaa | ||
|
|
34f18fbd7d | ||
|
|
27fa2bbc54 | ||
|
|
03a88327a5 | ||
|
|
67ac21e688 | ||
|
|
e90f32ca32 | ||
|
|
b21e65027c | ||
|
|
b628532def | ||
|
|
e0b2755b7c | ||
|
|
9e1fbb9852 | ||
|
|
047ca3e203 | ||
|
|
bfcd5357e3 | ||
|
|
461ed6b81f | ||
|
|
ee2969fc5b | ||
|
|
ca32ebd9cb | ||
|
|
4a29617fed | ||
|
|
8eab97a92a | ||
|
|
f453f69cce | ||
|
|
8ca84147e8 | ||
|
|
5e3ddd83fc | ||
|
|
6b985463d8 | ||
|
|
25e0ae2670 | ||
|
|
614c6fbdba | ||
|
|
8efe415558 | ||
|
|
f3ad2a3709 | ||
|
|
25dd71ba04 | ||
|
|
a514e6e168 | ||
|
|
cb5553ea94 | ||
|
|
335ee31c7c | ||
|
|
9b4379727d | ||
|
|
a5fe2f59be | ||
|
|
1b58f7854e | ||
|
|
b6effa468f | ||
|
|
2b512b88c4 | ||
|
|
5cb61cba90 | ||
|
|
0657efe239 |
@@ -3,10 +3,7 @@ root = true
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = tab
|
||||
indent_style = tab
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.{js,vue}]
|
||||
indent_style = tab
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: [
|
||||
'@nextcloud',
|
||||
],
|
||||
@@ -8,7 +7,6 @@ module.exports = {
|
||||
'jsdoc/require-param-type': ['off'],
|
||||
'jsdoc/check-param-names': ['off'],
|
||||
'jsdoc/no-undefined-types': ['off'],
|
||||
'jsdoc/require-property-description': ['off'],
|
||||
'import/no-named-as-default-member': ['off']
|
||||
'jsdoc/require-property-description' : ['off']
|
||||
},
|
||||
}
|
||||
|
||||
8
.github/workflows/appbuild.yml
vendored
@@ -1,11 +1,7 @@
|
||||
name: Package build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
- stable*
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -24,7 +20,7 @@ jobs:
|
||||
- name: Set up npm7
|
||||
run: npm i -g npm@7
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@2.21.2
|
||||
uses: shivammathur/setup-php@2.18.0
|
||||
with:
|
||||
php-version: '7.4'
|
||||
tools: composer
|
||||
|
||||
2
.github/workflows/appstore-build-publish.yml
vendored
@@ -66,7 +66,7 @@ jobs:
|
||||
run: npm i -g npm@"${{ steps.versions.outputs.npmVersion }}"
|
||||
|
||||
- name: Set up php ${{ env.PHP_VERSION }}
|
||||
uses: shivammathur/setup-php@2.21.2
|
||||
uses: shivammathur/setup-php@2.18.0
|
||||
with:
|
||||
php-version: ${{ env.PHP_VERSION }}
|
||||
coverage: none
|
||||
|
||||
7
.github/workflows/command-rebase.yml
vendored
@@ -9,14 +9,9 @@ on:
|
||||
issue_comment:
|
||||
types: created
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
rebase:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: none
|
||||
|
||||
# On pull requests and if the comment starts with `/rebase`
|
||||
if: github.event.issue.pull_request != '' && startsWith(github.event.comment.body, '/rebase')
|
||||
@@ -37,7 +32,7 @@ jobs:
|
||||
token: ${{ secrets.COMMAND_BOT_PAT }}
|
||||
|
||||
- name: Automatic Rebase
|
||||
uses: cirrus-actions/rebase@1.7
|
||||
uses: cirrus-actions/rebase@1.5
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.COMMAND_BOT_PAT }}
|
||||
|
||||
|
||||
112
.github/workflows/cypress.yml
vendored
@@ -1,112 +0,0 @@
|
||||
name: Cypress
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- stable*
|
||||
|
||||
env:
|
||||
APP_NAME: deck
|
||||
CYPRESS_baseUrl: http://localhost:8081/index.php
|
||||
|
||||
jobs:
|
||||
cypress:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node-version: [14.x]
|
||||
# containers: [1, 2, 3]
|
||||
php-versions: [ '7.4' ]
|
||||
databases: [ 'sqlite' ]
|
||||
server-versions: [ 'stable25' ]
|
||||
|
||||
steps:
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Set up npm7
|
||||
run: npm i -g npm@7
|
||||
|
||||
- name: Checkout server
|
||||
uses: actions/checkout@v3
|
||||
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 ${{ env.APP_NAME }}
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: apps/${{ env.APP_NAME }}
|
||||
|
||||
- name: Set up php ${{ matrix.php-versions }}
|
||||
uses: shivammathur/setup-php@2.21.2
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
extensions: mbstring, iconv, fileinfo, intl, sqlite, pdo_sqlite, zip, gd, apcu
|
||||
ini-values:
|
||||
apc.enable_cli=on
|
||||
coverage: none
|
||||
|
||||
- name: Set up Nextcloud
|
||||
env:
|
||||
DB_PORT: 4444
|
||||
PHP_CLI_SERVER_WORKERS: 10
|
||||
run: |
|
||||
mkdir data
|
||||
php 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
|
||||
php occ config:system:set memcache.local --value="\\OC\\Memcache\\APCu"
|
||||
php occ config:system:set debug --value=true --type=boolean
|
||||
php -f index.php
|
||||
php -S 0.0.0.0:8081 &
|
||||
export OC_PASS=1234561
|
||||
php occ user:add --password-from-env user1
|
||||
php occ user:add --password-from-env user2
|
||||
php occ app:enable deck
|
||||
php occ app:list
|
||||
cd apps/deck
|
||||
composer install --no-dev
|
||||
npm ci
|
||||
npm run build
|
||||
cd ../../
|
||||
curl -v http://localhost:8081/index.php/login
|
||||
|
||||
- name: Cypress run
|
||||
uses: cypress-io/github-action@v4
|
||||
with:
|
||||
record: true
|
||||
parallel: false
|
||||
wait-on: '${{ env.CYPRESS_baseUrl }}'
|
||||
working-directory: 'apps/${{ env.APP_NAME }}'
|
||||
config: defaultCommandTimeout=10000,video=false
|
||||
env:
|
||||
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||
npm_package_name: ${{ env.APP_NAME }}
|
||||
|
||||
- name: Upload test failure screenshots
|
||||
uses: actions/upload-artifact@v2
|
||||
if: failure()
|
||||
with:
|
||||
name: Upload screenshots
|
||||
path: apps/${{ env.APP_NAME }}/cypress/screenshots/
|
||||
retention-days: 5
|
||||
|
||||
- name: Upload nextcloud logs
|
||||
uses: actions/upload-artifact@v2
|
||||
if: failure()
|
||||
with:
|
||||
name: Upload nextcloud log
|
||||
path: data/nextcloud.log
|
||||
retention-days: 5
|
||||
@@ -8,20 +8,13 @@ name: Dependabot
|
||||
on:
|
||||
pull_request_target:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
- stable*
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
auto-approve-merge:
|
||||
if: github.actor == 'dependabot[bot]'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
# for hmarr/auto-approve-action to approve PRs
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
# Github actions bot approve
|
||||
|
||||
14
.github/workflows/integration.yml
vendored
@@ -2,14 +2,6 @@ name: Integration tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/integration.yml'
|
||||
- 'appinfo/**'
|
||||
- 'lib/**'
|
||||
- 'templates/**'
|
||||
- 'tests/**'
|
||||
- 'composer.json'
|
||||
- 'composer.lock'
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
@@ -27,7 +19,7 @@ jobs:
|
||||
matrix:
|
||||
php-versions: ['7.4']
|
||||
databases: ['sqlite', 'mysql', 'pgsql']
|
||||
server-versions: ['stable25']
|
||||
server-versions: ['stable24']
|
||||
|
||||
name: php${{ matrix.php-versions }}-${{ matrix.databases }}-${{ matrix.server-versions }}
|
||||
|
||||
@@ -62,7 +54,7 @@ jobs:
|
||||
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
|
||||
cd build/integration && composer require --dev phpunit/phpunit:~9
|
||||
cd build/integration && composer require --dev phpunit/phpunit:~8
|
||||
|
||||
- name: Checkout app
|
||||
uses: actions/checkout@v3
|
||||
@@ -70,7 +62,7 @@ jobs:
|
||||
path: apps/${{ env.APP_NAME }}
|
||||
|
||||
- name: Set up php ${{ matrix.php-versions }}
|
||||
uses: shivammathur/setup-php@2.21.2
|
||||
uses: shivammathur/setup-php@2.18.0
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
tools: phpunit
|
||||
|
||||
4
.github/workflows/lint.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up php${{ matrix.php-versions }}
|
||||
uses: shivammathur/setup-php@2.21.2
|
||||
uses: shivammathur/setup-php@2.18.0
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
coverage: none
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Set up php
|
||||
uses: shivammathur/setup-php@2.21.2
|
||||
uses: shivammathur/setup-php@2.18.0
|
||||
with:
|
||||
php-version: 7.4
|
||||
coverage: none
|
||||
|
||||
2
.github/workflows/nightly.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
||||
- name: Set up npm7
|
||||
run: npm i -g npm@7
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@2.21.2
|
||||
uses: shivammathur/setup-php@2.18.0
|
||||
with:
|
||||
php-version: '7.4'
|
||||
tools: composer
|
||||
|
||||
12
.github/workflows/phpunit.yml
vendored
@@ -2,14 +2,6 @@ name: PHPUnit
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/phpunit.yml'
|
||||
- 'appinfo/**'
|
||||
- 'lib/**'
|
||||
- 'templates/**'
|
||||
- 'tests/**'
|
||||
- 'composer.json'
|
||||
- 'composer.lock'
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
@@ -28,7 +20,7 @@ jobs:
|
||||
matrix:
|
||||
php-versions: ['7.4', '8.0', '8.1']
|
||||
databases: ['sqlite', 'mysql', 'pgsql']
|
||||
server-versions: ['stable25']
|
||||
server-versions: ['stable24']
|
||||
|
||||
name: php${{ matrix.php-versions }}-${{ matrix.databases }}-${{ matrix.server-versions }}
|
||||
|
||||
@@ -70,7 +62,7 @@ jobs:
|
||||
path: apps/${{ env.APP_NAME }}
|
||||
|
||||
- name: Set up php ${{ matrix.php-versions }}
|
||||
uses: shivammathur/setup-php@2.21.2
|
||||
uses: shivammathur/setup-php@2.18.0
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
tools: phpunit
|
||||
|
||||
129
CHANGELOG.md
@@ -1,106 +1,51 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## 1.8.5
|
||||
## 1.7.4
|
||||
|
||||
### Fixed
|
||||
|
||||
- fix: Properly overwrite z-index of datepicker above modal [#4667](https://github.com/nextcloud/deck/pull/4667)
|
||||
- Gracefully handle not found card for a share [#4569](https://github.com/nextcloud/deck/pull/4569)
|
||||
- fix: Use passed userid when getting attachment folder [#4541](https://github.com/nextcloud/deck/pull/4541)
|
||||
- fix: Append datetime picker to body to avoid cut off [#4646](https://github.com/nextcloud/deck/pull/4646)
|
||||
- Permanently delete deck cards marked as deleted after 5 min in a cron job [#4302](https://github.com/nextcloud/deck/pull/4302)
|
||||
- Fix : Overlapping expiry dates on tags [#4538](https://github.com/nextcloud/deck/pull/4538)
|
||||
- Update dependencies
|
||||
|
||||
|
||||
## 1.8.4
|
||||
## 1.7.3
|
||||
|
||||
### Fixed
|
||||
|
||||
- fix: Use passed userid when getting attachment folder [#4540](https://github.com/nextcloud/deck/pull/4540)
|
||||
- fix: Adapt NcEmptyContent usages to new slots [#4563](https://github.com/nextcloud/deck/pull/4563)
|
||||
- Gracefully handle not found card for a share [#4568](https://github.com/nextcloud/deck/pull/4568)
|
||||
- allow user to toggle visibility of the calendar for a deck board [#4626](https://github.com/nextcloud/deck/pull/4626)
|
||||
- fix: Append datetime picker to body to avoid cut off [#4645](https://github.com/nextcloud/deck/pull/4645)
|
||||
- Fix : Overlapping expiry dates on tags [#4536](https://github.com/nextcloud/deck/pull/4536)
|
||||
- Better display of card dates (creation and change dates) [#4620](https://github.com/nextcloud/deck/pull/4620)
|
||||
- Dependency updates
|
||||
- feat: add validators to check values in services @juliushaertl [#4176](https://github.com/nextcloud/deck/pull/4176)
|
||||
- Add integration test for attachment handling on cards [#4178](https://github.com/nextcloud/deck/pull/4178)
|
||||
- disables autocomplete on card creation @juliushaertl [#4182](https://github.com/nextcloud/deck/pull/4182)
|
||||
- minor style fixes [#4202](https://github.com/nextcloud/deck/pull/4202)
|
||||
|
||||
## 1.8.3
|
||||
|
||||
## 1.7.2
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix component renaming so that acl works on shares again [#4328](https://github.com/nextcloud/deck/pull/4328)
|
||||
- Permanently delete deck cards marked as deleted after 5 min in a cron job [#4301](https://github.com/nextcloud/deck/pull/4301)
|
||||
- Dependency updates
|
||||
- Cache user membership for circles [#4132](https://github.com/nextcloud/deck/pull/4132)
|
||||
- Set event link also for notifications that get emitted from activities [#4118](https://github.com/nextcloud/deck/pull/4118)
|
||||
- Fix Card menu not displaying when description is not set [#4103](https://github.com/nextcloud/deck/pull/4103)
|
||||
- disable Create card button while no stack is chosen [#4019](https://github.com/nextcloud/deck/pull/4019)
|
||||
- to nextcloud/OCP package in stable24 [#4093](https://github.com/nextcloud/deck/pull/4093)
|
||||
- Fix attachment creator name: show display name [#4037](https://github.com/nextcloud/deck/pull/4037)
|
||||
- Use capped memory cache for board permissions [#3997](https://github.com/nextcloud/deck/pull/3997)
|
||||
- Improve CalDAV integration performance [#3995](https://github.com/nextcloud/deck/pull/3995)
|
||||
- Fetch attachment folder for the correct user during cron job [#3959](https://github.com/nextcloud/deck/pull/3959)
|
||||
- Switch to 'markdown-it-task-checkbox' for rendering of task lists [#3925](https://github.com/nextcloud/deck/pull/3925)
|
||||
- Prevent opening card and applyLabelFilter on card drag end [#3917](https://github.com/nextcloud/deck/pull/3917)
|
||||
- Fix for issue #3637 [#3901](https://github.com/nextcloud/deck/pull/3901)
|
||||
- Fix z-index for deck sidebar [#3885](https://github.com/nextcloud/deck/pull/3885)
|
||||
|
||||
|
||||
## 1.8.2
|
||||
## 1.7.1
|
||||
|
||||
### Fixed
|
||||
|
||||
- minor style fixes [#4201](https://github.com/nextcloud/deck/pull/4201)
|
||||
- feat: add validators to check values in services [#4174](https://github.com/nextcloud/deck/pull/4174)
|
||||
- Add integration test for attachment handling on cards [#4179](https://github.com/nextcloud/deck/pull/4179)
|
||||
- Add missing userId property [#4198](https://github.com/nextcloud/deck/pull/4198)
|
||||
|
||||
## 1.8.1
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix Duedate activity @nickvergessen [#4155](https://github.com/nextcloud/deck/pull/4155)
|
||||
|
||||
## 1.8.0
|
||||
|
||||
### Added
|
||||
|
||||
- Nextcloud 25 compatibility
|
||||
- Performance improvements
|
||||
- Use capped memory cache for board permissions @juliushaertl [#3980](https://github.com/nextcloud/deck/pull/3980)
|
||||
- Improve CalDAV integration performance @juliushaertl [#3982](https://github.com/nextcloud/deck/pull/3982)
|
||||
- Simpify query for getting shared files @juliushaertl [#3983](https://github.com/nextcloud/deck/pull/3983)
|
||||
- Accessibility improvements
|
||||
- Add a11y label for sidebar button @marcelklehr [#3986](https://github.com/nextcloud/deck/pull/3986)
|
||||
- Improve filter popover accessibility @juliushaertl [#3820](https://github.com/nextcloud/deck/pull/3820)
|
||||
- Set ids to skip to content/navigation @juliushaertl [#3924](https://github.com/nextcloud/deck/pull/3924)
|
||||
- Invert icons properly in dark mode @juliushaertl [#3939](https://github.com/nextcloud/deck/pull/3939)
|
||||
- Implement card reference widget @eneiluj [#4031](https://github.com/nextcloud/deck/pull/4031)
|
||||
- Implement new dashboard widget interfaces @eneiluj [#4033](https://github.com/nextcloud/deck/pull/4033)
|
||||
- Add related resources panel to board sharing tab sidebar @Pytal [#4000](https://github.com/nextcloud/deck/pull/4000)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix sorting stacks [#4116](https://github.com/nextcloud/deck/pull/4116)
|
||||
- Fix issue with duedate format [#4140](https://github.com/nextcloud/deck/pull/4140)
|
||||
- Fix missing icon for activity rendering [#4090](https://github.com/nextcloud/deck/pull/4090)
|
||||
- disables autocomplete on card creation [#4142](https://github.com/nextcloud/deck/pull/4142)
|
||||
- Set event link also for notifications that get emitted from activities [#4117](https://github.com/nextcloud/deck/pull/4117)
|
||||
- Fix attachment creator name: show display name @eneiluj [#4036](https://github.com/nextcloud/deck/pull/4036)
|
||||
- Fix reference provider when caching @eneiluj [#4056](https://github.com/nextcloud/deck/pull/4056)
|
||||
- Use global import for nextcloud-vue [#4072](https://github.com/nextcloud/deck/pull/4072)
|
||||
- Disable Create card button while no stack is chosen @icewind1991 [#4014](https://github.com/nextcloud/deck/pull/4014)
|
||||
- Adjust testing matrix for Nextcloud 25 on stable25 @nickvergessen [#4068](https://github.com/nextcloud/deck/pull/4068)
|
||||
- Fix Card menu not displaying when description is not set @marcelklehr [#4105](https://github.com/nextcloud/deck/pull/4105)
|
||||
- Reference widget adjustments for Text [#4075](https://github.com/nextcloud/deck/pull/4075)
|
||||
- use OCP\Collaboration\Reference\Reference [#4078](https://github.com/nextcloud/deck/pull/4078)
|
||||
- Cache user membership for circles [#4141](https://github.com/nextcloud/deck/pull/4141)
|
||||
- set last modified when the card was found. Fixes #3763 @ylebre [#3796](https://github.com/nextcloud/deck/pull/3796)
|
||||
- Increase file count after sharing @luka-nextcloud [#3682](https://github.com/nextcloud/deck/pull/3682)
|
||||
- Align Duedate-delete icon properly - fixes nextcloud/deck#3791 @Ben-Ro [#3811](https://github.com/nextcloud/deck/pull/3811)
|
||||
- Fix for issue #3637 @flummer [#3833](https://github.com/nextcloud/deck/pull/3833)
|
||||
- Switch to 'markdown-it-task-checkbox' for rendering of task lists @q-wertz [#3898](https://github.com/nextcloud/deck/pull/3898)
|
||||
- Make rename functions accessibly by keyboard navigation @juliushaertl [#3813](https://github.com/nextcloud/deck/pull/3813)
|
||||
- Prevent opening card and applyLabelFilter on card drag end @eneiluj [#3916](https://github.com/nextcloud/deck/pull/3916)
|
||||
- Inserted required property in the rename list field, to prevent the l… @mstolf [#3862](https://github.com/nextcloud/deck/pull/3862)
|
||||
- Fix share provider for master changes @nickvergessen [#3942](https://github.com/nextcloud/deck/pull/3942)
|
||||
- Fetch attachment folder for the correct user during cron job @juliushaertl [#3952](https://github.com/nextcloud/deck/pull/3952)
|
||||
- Fix z-index for deck sidebar @Raudius [#3884](https://github.com/nextcloud/deck/pull/3884)
|
||||
|
||||
### Other
|
||||
|
||||
- Switch from OC::$server->get to OCP\Server::get @CarlSchwan [#3801](https://github.com/nextcloud/deck/pull/3801)
|
||||
- Add performance section in README @eneiluj [#3830](https://github.com/nextcloud/deck/pull/3830)
|
||||
- Fix static analysis by stubbing more circle methods @juliushaertl [#3900](https://github.com/nextcloud/deck/pull/3900)
|
||||
- fix(docs): fix links to JSON schemas for Trello @wiktor2200 [#3872](https://github.com/nextcloud/deck/pull/3872)
|
||||
- Move to OCP\Collaboration\Resources\LoadAdditionalScriptsEvent @juliushaertl [#3818](https://github.com/nextcloud/deck/pull/3818)
|
||||
- Rename settings to deck settings @PVince81 [#3928](https://github.com/nextcloud/deck/pull/3928)
|
||||
- SCSS cleanup @juliushaertl [#3803](https://github.com/nextcloud/deck/pull/3803)
|
||||
- Hide deprecated projects in sidebar and card details by default @Pytal [#3984](https://github.com/nextcloud/deck/pull/3984)
|
||||
- Align Duedate-delete icon properly - fixes nextcloud/deck#3791 [#3817](https://github.com/nextcloud/deck/pull/3817)
|
||||
- Increase file count after sharing [#3806](https://github.com/nextcloud/deck/pull/3806)
|
||||
- Fetch full board data after cloning [#3781](https://github.com/nextcloud/deck/pull/3781)
|
||||
|
||||
## 1.7.0
|
||||
|
||||
@@ -152,7 +97,7 @@ All notable changes to this project will be documented in this file.
|
||||
- Adapt the card modal to upstream changes [#3764](https://github.com/nextcloud/deck/pull/3764)
|
||||
- Fix text selection in dark mode and modal view [#3765](https://github.com/nextcloud/deck/pull/3765)
|
||||
- Add missing indices [#3754](https://github.com/nextcloud/deck/pull/3754)
|
||||
|
||||
- Handle qb mapper exception messages properly @juliushaertl [#3769](https://github.com/nextcloud/deck/pull/3769)
|
||||
|
||||
## 1.6.0-beta1
|
||||
|
||||
@@ -523,7 +468,7 @@ Android app team for helping to improve our REST API:
|
||||
- Fix comment activities on Nextcloud 15
|
||||
- Fix issues with Edge
|
||||
- API: Fix numeric types that were returned as strings
|
||||
- API: Fix If-Modified-Since header parsing
|
||||
- API: Fix If-Modified-Since header parsing
|
||||
|
||||
|
||||
## 0.5.1 - 2018-12-05
|
||||
@@ -650,7 +595,7 @@ Android app team for helping to improve our REST API:
|
||||
### Fixed
|
||||
- Various frontend fixes
|
||||
- Fix sidebar drag issues
|
||||
- Improvements for IE11
|
||||
- Improvements for IE11
|
||||
- Fix bug when draging a card to an empty stack
|
||||
|
||||
## 0.2.1 - 2017-07-04
|
||||
@@ -724,7 +669,7 @@ Android app team for helping to improve our REST API:
|
||||
|
||||
### Fixed
|
||||
- Various styling improvements
|
||||
- Fix problems with MySQL and PostgreSQL
|
||||
- Fix problems with MySQL and PostgreSQL
|
||||
- Select first color by default when creating boards
|
||||
- Fix error when changing board permissions
|
||||
|
||||
@@ -732,9 +677,9 @@ Android app team for helping to improve our REST API:
|
||||
|
||||
### Added
|
||||
- Sharing boards with other users
|
||||
- Create and manage boards
|
||||
- Create and manage boards
|
||||
- Sort cards on stacks by drag-and-drop
|
||||
- Assign labels
|
||||
- Markdown notes for each card
|
||||
- Archive cards
|
||||
- Archive cards
|
||||
|
||||
|
||||
17
README.md
@@ -24,9 +24,9 @@ Deck is a kanban style organization tool aimed at personal planning and project
|
||||
### 3rd-Party Integrations
|
||||
|
||||
- [trello-to-deck](https://github.com/maxammann/trello-to-deck) - Migrates cards from Trello
|
||||
- [mail2deck](https://github.com/newroco/mail2deck) - Provides an "email in" solution
|
||||
- [mail2deck](https://github.com/newroco/mail2deck) - Provides an "email in" solution
|
||||
- [A-deck](https://github.com/leoossa/A-deck) - Chrome Extension that allows to create new card in selected stack based on current tab
|
||||
|
||||
|
||||
## Installation/Update
|
||||
|
||||
This app is supposed to work on the two latest Nextcloud versions.
|
||||
@@ -52,17 +52,6 @@ Please make sure you have installed the following dependencies: `make, which, ta
|
||||
|
||||
Instead of setting everything up manually, you can just [download the nightly build](https://github.com/nextcloud/deck/releases/tag/nightly) instead. These builds are updated every 24 hours, and are pre-configured with all the needed dependencies.
|
||||
|
||||
## Performance limitations
|
||||
|
||||
Deck is not yet ready for intensive usage.
|
||||
A lot of database queries are generated when the number of boards, cards and attachments is high.
|
||||
For example, a user having access to 13 boards, with each board having on average 100 cards,
|
||||
and each card having on average 5 attachments,
|
||||
would generate 6500 database queries when doing the file related queries
|
||||
which would increase the page loading time significantly.
|
||||
|
||||
Improvements on Nextcloud server and Deck itself will improve the situation.
|
||||
|
||||
## Developing
|
||||
|
||||
### PHP
|
||||
@@ -71,8 +60,6 @@ Nothing to prepare, just dig into the code.
|
||||
|
||||
### JavaScript
|
||||
|
||||
This requires at least Node 14 and npm 7 to be installed.
|
||||
|
||||
Deck requires running a `make build-js` to install npm dependencies and build the JavaScript code using webpack. While developing you can also use `make watch` to rebuild everytime the code changes.
|
||||
|
||||
#### Hot reloading
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
- 🚀 Get your project organized
|
||||
|
||||
</description>
|
||||
<version>1.8.5</version>
|
||||
<version>1.7.4</version>
|
||||
<licence>agpl</licence>
|
||||
<author>Julius Härtl</author>
|
||||
<namespace>Deck</namespace>
|
||||
@@ -34,7 +34,7 @@
|
||||
<database min-version="9.4">pgsql</database>
|
||||
<database>sqlite</database>
|
||||
<database min-version="8.0">mysql</database>
|
||||
<nextcloud min-version="25" max-version="25"/>
|
||||
<nextcloud min-version="24" max-version="24"/>
|
||||
</dependencies>
|
||||
<background-jobs>
|
||||
<job>OCA\Deck\Cron\DeleteCron</job>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"symfony/event-dispatcher": "^4.0",
|
||||
"vimeo/psalm": "^4.3",
|
||||
"php-parallel-lint/php-parallel-lint": "^1.2",
|
||||
"nextcloud/ocp": "dev-stable25"
|
||||
"nextcloud/ocp": "dev-stable24"
|
||||
},
|
||||
"config": {
|
||||
"optimize-autoloader": true,
|
||||
@@ -36,7 +36,6 @@
|
||||
"cs:check": "php-cs-fixer fix --dry-run --diff",
|
||||
"cs:fix": "php-cs-fixer fix",
|
||||
"psalm": "psalm",
|
||||
"psalm:update-baseline": "psalm --update-baseline",
|
||||
"psalm:fix": "psalm --alter --issues=InvalidReturnType,InvalidNullableReturnType,MismatchingDocblockParamType,MismatchingDocblockReturnType,MissingParamType,InvalidFalsableReturnType",
|
||||
"test": [
|
||||
"@test:unit",
|
||||
|
||||
369
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "445858d371d9a1c7057d0603c566966a",
|
||||
"content-hash": "fdd039ec52f9c829403494423b527e10",
|
||||
"packages": [
|
||||
{
|
||||
"name": "cogpowered/finediff",
|
||||
@@ -63,16 +63,16 @@
|
||||
},
|
||||
{
|
||||
"name": "justinrainbow/json-schema",
|
||||
"version": "5.2.12",
|
||||
"version": "5.2.11",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/justinrainbow/json-schema.git",
|
||||
"reference": "ad87d5a5ca981228e0e205c2bc7dfb8e24559b60"
|
||||
"reference": "2ab6744b7296ded80f8cc4f9509abbff393399aa"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/ad87d5a5ca981228e0e205c2bc7dfb8e24559b60",
|
||||
"reference": "ad87d5a5ca981228e0e205c2bc7dfb8e24559b60",
|
||||
"url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/2ab6744b7296ded80f8cc4f9509abbff393399aa",
|
||||
"reference": "2ab6744b7296ded80f8cc4f9509abbff393399aa",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -127,9 +127,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/justinrainbow/json-schema/issues",
|
||||
"source": "https://github.com/justinrainbow/json-schema/tree/5.2.12"
|
||||
"source": "https://github.com/justinrainbow/json-schema/tree/5.2.11"
|
||||
},
|
||||
"time": "2022-04-13T08:02:27+00:00"
|
||||
"time": "2021-07-22T09:24:00+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
@@ -445,16 +445,16 @@
|
||||
},
|
||||
{
|
||||
"name": "composer/semver",
|
||||
"version": "3.3.2",
|
||||
"version": "3.2.9",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/composer/semver.git",
|
||||
"reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9"
|
||||
"reference": "a951f614bd64dcd26137bc9b7b2637ddcfc57649"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9",
|
||||
"reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9",
|
||||
"url": "https://api.github.com/repos/composer/semver/zipball/a951f614bd64dcd26137bc9b7b2637ddcfc57649",
|
||||
"reference": "a951f614bd64dcd26137bc9b7b2637ddcfc57649",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -506,7 +506,7 @@
|
||||
"support": {
|
||||
"irc": "irc://irc.freenode.org/composer",
|
||||
"issues": "https://github.com/composer/semver/issues",
|
||||
"source": "https://github.com/composer/semver/tree/3.3.2"
|
||||
"source": "https://github.com/composer/semver/tree/3.2.9"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -522,7 +522,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-04-01T19:23:25+00:00"
|
||||
"time": "2022-02-04T13:58:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "composer/xdebug-handler",
|
||||
@@ -896,16 +896,16 @@
|
||||
},
|
||||
{
|
||||
"name": "felixfbecker/language-server-protocol",
|
||||
"version": "v1.5.2",
|
||||
"version": "1.5.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/felixfbecker/php-language-server-protocol.git",
|
||||
"reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842"
|
||||
"reference": "9d846d1f5cf101deee7a61c8ba7caa0a975cd730"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/6e82196ffd7c62f7794d778ca52b69feec9f2842",
|
||||
"reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842",
|
||||
"url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/9d846d1f5cf101deee7a61c8ba7caa0a975cd730",
|
||||
"reference": "9d846d1f5cf101deee7a61c8ba7caa0a975cd730",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -946,9 +946,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/felixfbecker/php-language-server-protocol/issues",
|
||||
"source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.2"
|
||||
"source": "https://github.com/felixfbecker/php-language-server-protocol/tree/1.5.1"
|
||||
},
|
||||
"time": "2022-03-02T22:36:06+00:00"
|
||||
"time": "2021-02-22T14:02:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "friendsofphp/php-cs-fixer",
|
||||
@@ -1192,16 +1192,16 @@
|
||||
},
|
||||
{
|
||||
"name": "nextcloud/ocp",
|
||||
"version": "dev-stable25",
|
||||
"version": "dev-stable24",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nextcloud-deps/ocp.git",
|
||||
"reference": "91e61aba842365691d63a086dba68c3f3ccc3689"
|
||||
"reference": "0b89697ba1146d48506132c452cf548adb2a9cb8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/91e61aba842365691d63a086dba68c3f3ccc3689",
|
||||
"reference": "91e61aba842365691d63a086dba68c3f3ccc3689",
|
||||
"url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/0b89697ba1146d48506132c452cf548adb2a9cb8",
|
||||
"reference": "0b89697ba1146d48506132c452cf548adb2a9cb8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1213,7 +1213,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "26.0.0-dev"
|
||||
"dev-master": "24.0.0-dev"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
@@ -1229,22 +1229,22 @@
|
||||
"description": "Composer package containing Nextcloud's public API (classes, interfaces)",
|
||||
"support": {
|
||||
"issues": "https://github.com/nextcloud-deps/ocp/issues",
|
||||
"source": "https://github.com/nextcloud-deps/ocp/tree/stable25"
|
||||
"source": "https://github.com/nextcloud-deps/ocp/tree/stable24"
|
||||
},
|
||||
"time": "2023-04-13T00:33:11+00:00"
|
||||
"time": "2023-03-16T00:40:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nikic/php-parser",
|
||||
"version": "v4.14.0",
|
||||
"version": "v4.13.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nikic/PHP-Parser.git",
|
||||
"reference": "34bea19b6e03d8153165d8f30bba4c3be86184c1"
|
||||
"reference": "210577fe3cf7badcc5814d99455df46564f3c077"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/34bea19b6e03d8153165d8f30bba4c3be86184c1",
|
||||
"reference": "34bea19b6e03d8153165d8f30bba4c3be86184c1",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/210577fe3cf7badcc5814d99455df46564f3c077",
|
||||
"reference": "210577fe3cf7badcc5814d99455df46564f3c077",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1285,9 +1285,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v4.14.0"
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v4.13.2"
|
||||
},
|
||||
"time": "2022-05-31T20:59:12+00:00"
|
||||
"time": "2021-11-30T19:35:32+00:00"
|
||||
},
|
||||
{
|
||||
"name": "openlss/lib-array2xml",
|
||||
@@ -1723,24 +1723,91 @@
|
||||
"time": "2022-03-15T21:29:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
"version": "9.2.17",
|
||||
"name": "phpspec/prophecy",
|
||||
"version": "v1.15.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||
"reference": "aa94dc41e8661fe90c7316849907cba3007b10d8"
|
||||
"url": "https://github.com/phpspec/prophecy.git",
|
||||
"reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/aa94dc41e8661fe90c7316849907cba3007b10d8",
|
||||
"reference": "aa94dc41e8661fe90c7316849907cba3007b10d8",
|
||||
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/bbcd7380b0ebf3961ee21409db7b38bc31d69a13",
|
||||
"reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"doctrine/instantiator": "^1.2",
|
||||
"php": "^7.2 || ~8.0, <8.2",
|
||||
"phpdocumentor/reflection-docblock": "^5.2",
|
||||
"sebastian/comparator": "^3.0 || ^4.0",
|
||||
"sebastian/recursion-context": "^3.0 || ^4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpspec/phpspec": "^6.0 || ^7.0",
|
||||
"phpunit/phpunit": "^8.0 || ^9.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Prophecy\\": "src/Prophecy"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Konstantin Kudryashov",
|
||||
"email": "ever.zet@gmail.com",
|
||||
"homepage": "http://everzet.com"
|
||||
},
|
||||
{
|
||||
"name": "Marcello Duarte",
|
||||
"email": "marcello.duarte@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "Highly opinionated mocking framework for PHP 5.3+",
|
||||
"homepage": "https://github.com/phpspec/prophecy",
|
||||
"keywords": [
|
||||
"Double",
|
||||
"Dummy",
|
||||
"fake",
|
||||
"mock",
|
||||
"spy",
|
||||
"stub"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/phpspec/prophecy/issues",
|
||||
"source": "https://github.com/phpspec/prophecy/tree/v1.15.0"
|
||||
},
|
||||
"time": "2021-12-08T12:19:24+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
"version": "9.2.15",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||
"reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2e9da11878c4202f97915c1cb4bb1ca318a63f5f",
|
||||
"reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*",
|
||||
"ext-libxml": "*",
|
||||
"ext-xmlwriter": "*",
|
||||
"nikic/php-parser": "^4.14",
|
||||
"nikic/php-parser": "^4.13.0",
|
||||
"php": ">=7.3",
|
||||
"phpunit/php-file-iterator": "^3.0.3",
|
||||
"phpunit/php-text-template": "^2.0.2",
|
||||
@@ -1789,7 +1856,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.17"
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.15"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -1797,7 +1864,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2022-08-30T12:24:04+00:00"
|
||||
"time": "2022-03-07T09:28:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-file-iterator",
|
||||
@@ -2042,16 +2109,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "9.5.24",
|
||||
"version": "9.5.20",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "d0aa6097bef9fd42458a9b3c49da32c6ce6129c5"
|
||||
"reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d0aa6097bef9fd42458a9b3c49da32c6ce6129c5",
|
||||
"reference": "d0aa6097bef9fd42458a9b3c49da32c6ce6129c5",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba",
|
||||
"reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2066,6 +2133,7 @@
|
||||
"phar-io/manifest": "^2.0.3",
|
||||
"phar-io/version": "^3.0.2",
|
||||
"php": ">=7.3",
|
||||
"phpspec/prophecy": "^1.12.1",
|
||||
"phpunit/php-code-coverage": "^9.2.13",
|
||||
"phpunit/php-file-iterator": "^3.0.5",
|
||||
"phpunit/php-invoker": "^3.1.1",
|
||||
@@ -2080,9 +2148,13 @@
|
||||
"sebastian/global-state": "^5.0.1",
|
||||
"sebastian/object-enumerator": "^4.0.3",
|
||||
"sebastian/resource-operations": "^3.0.3",
|
||||
"sebastian/type": "^3.1",
|
||||
"sebastian/type": "^3.0",
|
||||
"sebastian/version": "^3.0.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-pdo": "*",
|
||||
"phpspec/prophecy-phpunit": "^2.0.1"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-soap": "*",
|
||||
"ext-xdebug": "*"
|
||||
@@ -2124,7 +2196,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.24"
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -2136,7 +2208,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2022-08-30T07:42:16+00:00"
|
||||
"time": "2022-04-01T12:37:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/cache",
|
||||
@@ -3133,16 +3205,16 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/environment",
|
||||
"version": "5.1.4",
|
||||
"version": "5.1.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/environment.git",
|
||||
"reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7"
|
||||
"reference": "388b6ced16caa751030f6a69e588299fa09200ac"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7",
|
||||
"reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/388b6ced16caa751030f6a69e588299fa09200ac",
|
||||
"reference": "388b6ced16caa751030f6a69e588299fa09200ac",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3184,7 +3256,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/environment/issues",
|
||||
"source": "https://github.com/sebastianbergmann/environment/tree/5.1.4"
|
||||
"source": "https://github.com/sebastianbergmann/environment/tree/5.1.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -3192,7 +3264,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2022-04-03T09:37:03+00:00"
|
||||
"time": "2020-09-28T05:52:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/exporter",
|
||||
@@ -3624,16 +3696,16 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/type",
|
||||
"version": "3.1.0",
|
||||
"version": "3.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/type.git",
|
||||
"reference": "fb44e1cc6e557418387ad815780360057e40753e"
|
||||
"reference": "b233b84bc4465aff7b57cf1c4bc75c86d00d6dad"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/type/zipball/fb44e1cc6e557418387ad815780360057e40753e",
|
||||
"reference": "fb44e1cc6e557418387ad815780360057e40753e",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b233b84bc4465aff7b57cf1c4bc75c86d00d6dad",
|
||||
"reference": "b233b84bc4465aff7b57cf1c4bc75c86d00d6dad",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3645,7 +3717,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.1-dev"
|
||||
"dev-master": "3.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -3668,7 +3740,7 @@
|
||||
"homepage": "https://github.com/sebastianbergmann/type",
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/type/issues",
|
||||
"source": "https://github.com/sebastianbergmann/type/tree/3.1.0"
|
||||
"source": "https://github.com/sebastianbergmann/type/tree/3.0.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -3676,7 +3748,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2022-08-29T06:55:37+00:00"
|
||||
"time": "2022-03-15T09:54:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/version",
|
||||
@@ -3733,16 +3805,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v5.4.12",
|
||||
"version": "v5.4.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "c072aa8f724c3af64e2c7a96b796a4863d24dba1"
|
||||
"reference": "d8111acc99876953f52fe16d4c50eb60940d49ad"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/c072aa8f724c3af64e2c7a96b796a4863d24dba1",
|
||||
"reference": "c072aa8f724c3af64e2c7a96b796a4863d24dba1",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/d8111acc99876953f52fe16d4c50eb60940d49ad",
|
||||
"reference": "d8111acc99876953f52fe16d4c50eb60940d49ad",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3812,7 +3884,7 @@
|
||||
"terminal"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/console/tree/v5.4.12"
|
||||
"source": "https://github.com/symfony/console/tree/v5.4.5"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -3828,20 +3900,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-08-17T13:18:05+00:00"
|
||||
"time": "2022-02-24T12:45:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/deprecation-contracts",
|
||||
"version": "v2.5.2",
|
||||
"version": "v2.5.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/deprecation-contracts.git",
|
||||
"reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66"
|
||||
"reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
|
||||
"reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
|
||||
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/6f981ee24cf69ee7ce9736146d1c57c2780598a8",
|
||||
"reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3879,7 +3951,7 @@
|
||||
"description": "A generic function and convention to trigger deprecation notices",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.2"
|
||||
"source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -3895,7 +3967,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-01-02T09:53:40+00:00"
|
||||
"time": "2021-07-12T14:48:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/event-dispatcher",
|
||||
@@ -3983,16 +4055,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/event-dispatcher-contracts",
|
||||
"version": "v1.1.13",
|
||||
"version": "v1.1.11",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/event-dispatcher-contracts.git",
|
||||
"reference": "1d5cd762abaa6b2a4169d3e77610193a7157129e"
|
||||
"reference": "01e9a4efac0ee33a05dfdf93b346f62e7d0e998c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/1d5cd762abaa6b2a4169d3e77610193a7157129e",
|
||||
"reference": "1d5cd762abaa6b2a4169d3e77610193a7157129e",
|
||||
"url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/01e9a4efac0ee33a05dfdf93b346f62e7d0e998c",
|
||||
"reference": "01e9a4efac0ee33a05dfdf93b346f62e7d0e998c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4042,7 +4114,7 @@
|
||||
"standards"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/event-dispatcher-contracts/tree/v1.1.13"
|
||||
"source": "https://github.com/symfony/event-dispatcher-contracts/tree/v1.1.11"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -4058,7 +4130,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-01-02T09:41:36+00:00"
|
||||
"time": "2021-03-23T15:25:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/filesystem",
|
||||
@@ -4258,16 +4330,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.26.0",
|
||||
"version": "v1.25.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||
"reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4"
|
||||
"reference": "30885182c981ab175d4d034db0f6f469898070ab"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4",
|
||||
"reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab",
|
||||
"reference": "30885182c981ab175d4d034db0f6f469898070ab",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4282,7 +4354,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.26-dev"
|
||||
"dev-main": "1.23-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
@@ -4320,7 +4392,7 @@
|
||||
"portable"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0"
|
||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.25.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -4336,20 +4408,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-05-24T11:49:31+00:00"
|
||||
"time": "2021-10-20T20:35:02+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-intl-grapheme",
|
||||
"version": "v1.26.0",
|
||||
"version": "v1.25.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
|
||||
"reference": "433d05519ce6990bf3530fba6957499d327395c2"
|
||||
"reference": "81b86b50cf841a64252b439e738e97f4a34e2783"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/433d05519ce6990bf3530fba6957499d327395c2",
|
||||
"reference": "433d05519ce6990bf3530fba6957499d327395c2",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/81b86b50cf841a64252b439e738e97f4a34e2783",
|
||||
"reference": "81b86b50cf841a64252b439e738e97f4a34e2783",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4361,7 +4433,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.26-dev"
|
||||
"dev-main": "1.23-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
@@ -4401,7 +4473,7 @@
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.26.0"
|
||||
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.25.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -4417,20 +4489,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-05-24T11:49:31+00:00"
|
||||
"time": "2021-11-23T21:10:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-intl-normalizer",
|
||||
"version": "v1.26.0",
|
||||
"version": "v1.25.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
|
||||
"reference": "219aa369ceff116e673852dce47c3a41794c14bd"
|
||||
"reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/219aa369ceff116e673852dce47c3a41794c14bd",
|
||||
"reference": "219aa369ceff116e673852dce47c3a41794c14bd",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8",
|
||||
"reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4442,7 +4514,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.26-dev"
|
||||
"dev-main": "1.23-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
@@ -4485,7 +4557,7 @@
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.26.0"
|
||||
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.25.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -4501,20 +4573,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-05-24T11:49:31+00:00"
|
||||
"time": "2021-02-19T12:13:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
"version": "v1.26.0",
|
||||
"version": "v1.25.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||
"reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e"
|
||||
"reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
|
||||
"reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0abb51d2f102e00a4eefcf46ba7fec406d245825",
|
||||
"reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4529,7 +4601,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.26-dev"
|
||||
"dev-main": "1.23-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
@@ -4568,7 +4640,7 @@
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0"
|
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.25.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -4584,20 +4656,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-05-24T11:49:31+00:00"
|
||||
"time": "2021-11-30T18:21:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php73",
|
||||
"version": "v1.26.0",
|
||||
"version": "v1.25.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php73.git",
|
||||
"reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85"
|
||||
"reference": "cc5db0e22b3cb4111010e48785a97f670b350ca5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/e440d35fa0286f77fb45b79a03fedbeda9307e85",
|
||||
"reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/cc5db0e22b3cb4111010e48785a97f670b350ca5",
|
||||
"reference": "cc5db0e22b3cb4111010e48785a97f670b350ca5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4606,7 +4678,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.26-dev"
|
||||
"dev-main": "1.23-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
@@ -4647,7 +4719,7 @@
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php73/tree/v1.26.0"
|
||||
"source": "https://github.com/symfony/polyfill-php73/tree/v1.25.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -4663,20 +4735,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-05-24T11:49:31+00:00"
|
||||
"time": "2021-06-05T21:20:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php80",
|
||||
"version": "v1.26.0",
|
||||
"version": "v1.25.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php80.git",
|
||||
"reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace"
|
||||
"reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace",
|
||||
"reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/4407588e0d3f1f52efb65fbe92babe41f37fe50c",
|
||||
"reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4685,7 +4757,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.26-dev"
|
||||
"dev-main": "1.23-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
@@ -4730,7 +4802,7 @@
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0"
|
||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.25.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -4746,7 +4818,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-05-10T07:21:04+00:00"
|
||||
"time": "2022-03-04T08:16:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php81",
|
||||
@@ -4891,22 +4963,22 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/service-contracts",
|
||||
"version": "v2.5.2",
|
||||
"version": "v2.5.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/service-contracts.git",
|
||||
"reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c"
|
||||
"reference": "1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/4b426aac47d6427cc1a1d0f7e2ac724627f5966c",
|
||||
"reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c",
|
||||
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc",
|
||||
"reference": "1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2.5",
|
||||
"psr/container": "^1.1",
|
||||
"symfony/deprecation-contracts": "^2.1|^3"
|
||||
"symfony/deprecation-contracts": "^2.1"
|
||||
},
|
||||
"conflict": {
|
||||
"ext-psr": "<1.1|>=2"
|
||||
@@ -4954,7 +5026,7 @@
|
||||
"standards"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/service-contracts/tree/v2.5.2"
|
||||
"source": "https://github.com/symfony/service-contracts/tree/v2.5.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -4970,7 +5042,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-05-30T19:17:29+00:00"
|
||||
"time": "2021-11-04T16:48:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/stopwatch",
|
||||
@@ -5036,16 +5108,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/string",
|
||||
"version": "v5.4.12",
|
||||
"version": "v5.4.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/string.git",
|
||||
"reference": "2fc515e512d721bf31ea76bd02fe23ada4640058"
|
||||
"reference": "92043b7d8383e48104e411bc9434b260dbeb5a10"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/2fc515e512d721bf31ea76bd02fe23ada4640058",
|
||||
"reference": "2fc515e512d721bf31ea76bd02fe23ada4640058",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/92043b7d8383e48104e411bc9434b260dbeb5a10",
|
||||
"reference": "92043b7d8383e48104e411bc9434b260dbeb5a10",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -5102,7 +5174,7 @@
|
||||
"utf8"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/string/tree/v5.4.12"
|
||||
"source": "https://github.com/symfony/string/tree/v5.4.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -5118,7 +5190,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-08-12T17:03:11+00:00"
|
||||
"time": "2022-01-02T09:53:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "theseer/tokenizer",
|
||||
@@ -5172,16 +5244,16 @@
|
||||
},
|
||||
{
|
||||
"name": "vimeo/psalm",
|
||||
"version": "4.27.0",
|
||||
"version": "4.22.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/vimeo/psalm.git",
|
||||
"reference": "faf106e717c37b8c81721845dba9de3d8deed8ff"
|
||||
"reference": "fc2c6ab4d5fa5d644d8617089f012f3bb84b8703"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/vimeo/psalm/zipball/faf106e717c37b8c81721845dba9de3d8deed8ff",
|
||||
"reference": "faf106e717c37b8c81721845dba9de3d8deed8ff",
|
||||
"url": "https://api.github.com/repos/vimeo/psalm/zipball/fc2c6ab4d5fa5d644d8617089f012f3bb84b8703",
|
||||
"reference": "fc2c6ab4d5fa5d644d8617089f012f3bb84b8703",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -5206,7 +5278,6 @@
|
||||
"php": "^7.1|^8",
|
||||
"sebastian/diff": "^3.0 || ^4.0",
|
||||
"symfony/console": "^3.4.17 || ^4.1.6 || ^5.0 || ^6.0",
|
||||
"symfony/polyfill-php80": "^1.25",
|
||||
"webmozart/path-util": "^2.3"
|
||||
},
|
||||
"provide": {
|
||||
@@ -5273,27 +5344,27 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/vimeo/psalm/issues",
|
||||
"source": "https://github.com/vimeo/psalm/tree/4.27.0"
|
||||
"source": "https://github.com/vimeo/psalm/tree/4.22.0"
|
||||
},
|
||||
"time": "2022-08-31T13:47:09+00:00"
|
||||
"time": "2022-02-24T20:34:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "webmozart/assert",
|
||||
"version": "1.11.0",
|
||||
"version": "1.10.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/webmozarts/assert.git",
|
||||
"reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991"
|
||||
"reference": "6964c76c7804814a842473e0c8fd15bab0f18e25"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991",
|
||||
"reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991",
|
||||
"url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25",
|
||||
"reference": "6964c76c7804814a842473e0c8fd15bab0f18e25",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-ctype": "*",
|
||||
"php": "^7.2 || ^8.0"
|
||||
"php": "^7.2 || ^8.0",
|
||||
"symfony/polyfill-ctype": "^1.8"
|
||||
},
|
||||
"conflict": {
|
||||
"phpstan/phpstan": "<0.12.20",
|
||||
@@ -5331,9 +5402,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/webmozarts/assert/issues",
|
||||
"source": "https://github.com/webmozarts/assert/tree/1.11.0"
|
||||
"source": "https://github.com/webmozarts/assert/tree/1.10.0"
|
||||
},
|
||||
"time": "2022-06-03T18:03:27+00:00"
|
||||
"time": "2021-03-09T10:59:23+00:00"
|
||||
},
|
||||
{
|
||||
"name": "webmozart/path-util",
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
.icon-deck {
|
||||
background-image: url(../img/deck-dark.svg);
|
||||
filter: var(--background-invert-if-dark);
|
||||
}
|
||||
|
||||
.icon-deck-white, .icon-deck.icon-white {
|
||||
background-image: url(../img/deck.svg);
|
||||
filter: var(--background-invert-if-dark);
|
||||
}
|
||||
1
css/deck.scss
Normal file
@@ -0,0 +1 @@
|
||||
@include icon-black-white('deck', 'deck', 1);
|
||||
@@ -1,8 +1,11 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2022 Raul Ferreira Fuentes <raul@nextcloud.com>
|
||||
/*
|
||||
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @author Raul Ferreira Fuentes <raul@nextcloud.com>
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
* @author Artem Anufrij <artem.anufrij@live.de>
|
||||
* @author Marin Treselj <marin@pixelipo.com>
|
||||
* @author Oskar Kurz <oskar.kurz@gmail.com>
|
||||
* @author Ryan Fletcher <ryan.fletcher@codepassion.ca>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
@@ -20,26 +23,6 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
namespace OCA\Deck\Model;
|
||||
|
||||
use OCA\Deck\Db\Board;
|
||||
|
||||
class BoardSummary extends Board {
|
||||
private Board $board;
|
||||
|
||||
public function __construct(Board $board) {
|
||||
parent::__construct();
|
||||
$this->board = $board;
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array {
|
||||
return [
|
||||
'id' => $this->getId(),
|
||||
'title' => $this->getTitle()
|
||||
];
|
||||
}
|
||||
|
||||
public function __call($name, $arguments) {
|
||||
return $this->board->__call($name, $arguments);
|
||||
}
|
||||
}
|
||||
@import 'icons';
|
||||
@import 'print';
|
||||
41
css/icons.scss
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Custom icons
|
||||
*/
|
||||
@include icon-black-white('deck', 'deck', 1);
|
||||
@include icon-black-white('archive', 'deck', 1);
|
||||
@include icon-black-white('circles', 'deck', 1);
|
||||
@include icon-black-white('clone', 'deck', 1);
|
||||
@include icon-black-white('filter', 'deck', 1);
|
||||
@include icon-black-white('filter_set', 'deck', 1);
|
||||
@include icon-black-white('attach', 'deck', 1);
|
||||
@include icon-black-white('reply', 'deck', 1);
|
||||
@include icon-black-white('notifications-dark', 'deck', 1);
|
||||
@include icon-black-white('description', 'deck', 1);
|
||||
|
||||
.icon-toggle-compact-collapsed {
|
||||
@include icon-color('toggle-view-expand', 'deck', $color-black);
|
||||
}
|
||||
|
||||
.icon-toggle-compact-expanded {
|
||||
@include icon-color('toggle-view-collapse', 'deck', $color-black);
|
||||
}
|
||||
.icon-activity {
|
||||
@include icon-color('activity-dark', 'activity', $color-black);
|
||||
}
|
||||
.icon-comment--unread {
|
||||
@include icon-color('comment', 'actions', $color-primary, 1, true);
|
||||
}
|
||||
|
||||
.avatardiv.circles {
|
||||
background: var(--color-primary);
|
||||
}
|
||||
|
||||
.icon-circles {
|
||||
opacity: 1;
|
||||
background-size: 20px;
|
||||
background-position: center center;
|
||||
}
|
||||
|
||||
.icon-colorpicker {
|
||||
background-image: url('../img/color_picker.svg');
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
const { defineConfig } = require('cypress')
|
||||
|
||||
module.exports = defineConfig({
|
||||
projectId: '1s7wkc',
|
||||
viewportWidth: 1280,
|
||||
viewportHeight: 720,
|
||||
e2e: {
|
||||
// We've imported your old cypress plugins here.
|
||||
// You may want to clean this up later by importing these.
|
||||
setupNodeEvents(on, config) {
|
||||
return require('./cypress/plugins/index.js')(on, config)
|
||||
},
|
||||
baseUrl: 'http://nextcloud.local/index.php',
|
||||
experimentalSessionAndOrigin: true,
|
||||
specPattern: 'cypress/e2e/**/*.{js,jsx,ts,tsx}',
|
||||
},
|
||||
})
|
||||
@@ -1,41 +0,0 @@
|
||||
import { randHash } from '../utils'
|
||||
const randUser = randHash()
|
||||
|
||||
describe('Board', function() {
|
||||
const password = 'pass123'
|
||||
|
||||
before(function() {
|
||||
cy.nextcloudCreateUser(randUser, password)
|
||||
})
|
||||
|
||||
beforeEach(function() {
|
||||
cy.login(randUser, password)
|
||||
})
|
||||
|
||||
it('Can create a board', function() {
|
||||
const board = 'TestBoard'
|
||||
|
||||
cy.intercept({
|
||||
method: 'POST',
|
||||
url: '/index.php/apps/deck/boards',
|
||||
}).as('createBoardRequest')
|
||||
|
||||
// Click "Add board"
|
||||
cy.openLeftSidebar()
|
||||
cy.get('#app-navigation-vue .app-navigation__list .app-navigation-entry')
|
||||
.eq(3).find('a').first().click({ force: true })
|
||||
|
||||
// Type the board title
|
||||
cy.get('.board-create form input[type=text]')
|
||||
.type(board, { force: true })
|
||||
|
||||
// Submit
|
||||
cy.get('.board-create form input[type=submit]')
|
||||
.first().click({ force: true })
|
||||
|
||||
cy.wait('@createBoardRequest').its('response.statusCode').should('equal', 200)
|
||||
|
||||
cy.get('.app-navigation__list .app-navigation-entry__children .app-navigation-entry')
|
||||
.contains(board).should('be.visible')
|
||||
})
|
||||
})
|
||||
@@ -1,67 +0,0 @@
|
||||
import { randHash } from '../utils'
|
||||
const randUser = randHash()
|
||||
|
||||
const testBoardData = {
|
||||
title: 'MyBoardTest',
|
||||
color: '00ff00',
|
||||
stacks: [
|
||||
{
|
||||
title: 'TestList',
|
||||
cards: [
|
||||
{
|
||||
title: 'Hello world',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
describe('Card', function() {
|
||||
before(function() {
|
||||
cy.nextcloudCreateUser(randUser, randUser)
|
||||
cy.createExampleBoard({
|
||||
user: randUser,
|
||||
password: randUser,
|
||||
board: testBoardData,
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(function() {
|
||||
cy.login(randUser, randUser)
|
||||
})
|
||||
|
||||
it('Can show card details modal', function() {
|
||||
cy.openLeftSidebar()
|
||||
cy.getNavigationEntry(testBoardData.title)
|
||||
.first().click({ force: true })
|
||||
|
||||
cy.get('.board .stack').eq(0).within(() => {
|
||||
cy.get('.card:contains("Hello world")').should('be.visible').click()
|
||||
})
|
||||
|
||||
cy.get('.modal__card').should('be.visible')
|
||||
cy.get('.app-sidebar-header__maintitle').contains('Hello world')
|
||||
})
|
||||
|
||||
it('Can add a card', function() {
|
||||
const newCardTitle = 'Write some cypress tests'
|
||||
|
||||
cy.openLeftSidebar()
|
||||
cy.getNavigationEntry(testBoardData.title)
|
||||
.first().click({ force: true })
|
||||
|
||||
cy.get('.board .stack').eq(0).within(() => {
|
||||
cy.get('.card:contains("Hello world")').should('be.visible')
|
||||
|
||||
cy.get('.button-vue[aria-label*="Add card"]')
|
||||
.first().click()
|
||||
|
||||
cy.get('.stack__card-add form input#new-stack-input-main')
|
||||
.type(newCardTitle)
|
||||
cy.get('.stack__card-add form input[type=submit]')
|
||||
.first().click()
|
||||
cy.get(`.card:contains("${newCardTitle}")`).should('be.visible')
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
@@ -1,31 +0,0 @@
|
||||
import { randHash } from '../utils'
|
||||
const randUser = randHash()
|
||||
|
||||
describe('Deck dashboard', function() {
|
||||
const password = 'pass123'
|
||||
|
||||
before(function() {
|
||||
cy.nextcloudCreateUser(randUser, password)
|
||||
})
|
||||
|
||||
beforeEach(function() {
|
||||
cy.login(randUser, password)
|
||||
})
|
||||
|
||||
it('Can show the right title on the dashboard', function() {
|
||||
cy.get('.board-title h2')
|
||||
.should('have.length', 1).first()
|
||||
.should('have.text', 'Upcoming cards')
|
||||
})
|
||||
|
||||
it('Can see the default "Personal Board" created for user by default', function() {
|
||||
const defaultBoard = 'Personal'
|
||||
|
||||
cy.openLeftSidebar()
|
||||
cy.get('.app-navigation-entry-wrapper[icon=icon-deck]')
|
||||
.find('ul.app-navigation-entry__children .app-navigation-entry:contains(' + defaultBoard + ')')
|
||||
.first()
|
||||
.contains(defaultBoard)
|
||||
.should('be.visible')
|
||||
})
|
||||
})
|
||||
@@ -1,30 +0,0 @@
|
||||
import { randHash } from '../utils'
|
||||
const randUser = randHash()
|
||||
|
||||
describe('Stack', function() {
|
||||
const board = 'TestBoard'
|
||||
const password = 'pass123'
|
||||
const stack = 'List 1'
|
||||
|
||||
before(function() {
|
||||
cy.nextcloudCreateUser(randUser, password)
|
||||
cy.deckCreateBoard({ user: randUser, password }, board)
|
||||
})
|
||||
|
||||
beforeEach(function() {
|
||||
cy.logout()
|
||||
cy.login(randUser, password)
|
||||
})
|
||||
|
||||
it('Can create a stack', function() {
|
||||
cy.openLeftSidebar()
|
||||
cy.getNavigationEntry(board)
|
||||
.click({ force: true })
|
||||
|
||||
cy.get('#stack-add button').first().click()
|
||||
cy.get('#stack-add form input#new-stack-input-main').type(stack)
|
||||
cy.get('#stack-add form input[type=submit]').first().click()
|
||||
|
||||
cy.get('.board .stack').eq(0).contains(stack).should('be.visible')
|
||||
})
|
||||
})
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"name": "Using fixtures to represent data",
|
||||
"email": "hello@cypress.io",
|
||||
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
/// <reference types="cypress" />
|
||||
// ***********************************************************
|
||||
// This example plugins/index.js can be used to load plugins
|
||||
//
|
||||
// You can change the location of this file or turn off loading
|
||||
// the plugins file with the 'pluginsFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/plugins-guide
|
||||
// ***********************************************************
|
||||
|
||||
// This function is called when a project is opened or re-opened (e.g. due to
|
||||
// the project's config changing)
|
||||
|
||||
/**
|
||||
* @type {Cypress.PluginConfig}
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
module.exports = (on, config) => {
|
||||
// `on` is used to hook into various events Cypress emits
|
||||
// `config` is the resolved Cypress config
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
/**
|
||||
* @copyright Copyright (c) 2019 John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
const url = Cypress.config('baseUrl').replace(/\/index.php\/?$/g, '')
|
||||
Cypress.env('baseUrl', url)
|
||||
|
||||
Cypress.Commands.add('login', (user, password, route = '/apps/deck/') => {
|
||||
const session = `${user}-${Date.now()}`
|
||||
cy.session(session, function() {
|
||||
cy.visit(route)
|
||||
cy.get('input[name=user]').type(user)
|
||||
cy.get('input[name=password]').type(password)
|
||||
cy.get('form[name=login] [type=submit]').click()
|
||||
cy.url().should('include', route)
|
||||
})
|
||||
cy.visit(route)
|
||||
})
|
||||
|
||||
Cypress.Commands.add('logout', (route = '/') => {
|
||||
cy.session('_guest', function() {})
|
||||
})
|
||||
|
||||
Cypress.Commands.add('nextcloudCreateUser', (user, password) => {
|
||||
cy.clearCookies()
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: `${Cypress.env('baseUrl')}/ocs/v1.php/cloud/users?format=json`,
|
||||
form: true,
|
||||
body: {
|
||||
userid: user,
|
||||
password,
|
||||
},
|
||||
auth: { user: 'admin', pass: 'admin' },
|
||||
headers: {
|
||||
'OCS-ApiRequest': 'true',
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
}).then((response) => {
|
||||
cy.log(`Created user ${user}`, response.status)
|
||||
})
|
||||
})
|
||||
|
||||
Cypress.Commands.add('nextcloudUpdateUser', (user, password, key, value) => {
|
||||
cy.request({
|
||||
method: 'PUT',
|
||||
url: `${Cypress.env('baseUrl')}/ocs/v2.php/cloud/users/${user}`,
|
||||
form: true,
|
||||
body: { key, value },
|
||||
auth: { user, pass: password },
|
||||
headers: {
|
||||
'OCS-ApiRequest': 'true',
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
}).then((response) => {
|
||||
cy.log(`Updated user ${user} ${key} to ${value}`, response.status)
|
||||
})
|
||||
})
|
||||
|
||||
Cypress.Commands.add('openLeftSidebar', () => {
|
||||
cy.get('.app-navigation button.app-navigation-toggle').click()
|
||||
})
|
||||
|
||||
Cypress.Commands.add('deckCreateBoard', ({ user, password }, title) => {
|
||||
cy.login(user, password)
|
||||
|
||||
cy.get('.app-navigation button.app-navigation-toggle').click()
|
||||
cy.get('#app-navigation-vue .app-navigation__list .app-navigation-entry')
|
||||
.eq(3)
|
||||
.find('a')
|
||||
.first()
|
||||
.click({ force: true })
|
||||
|
||||
cy.get('.board-create form input[type=text]').type(title, { force: true })
|
||||
|
||||
cy.get('.board-create form input[type=submit]')
|
||||
.first()
|
||||
.click({ force: true })
|
||||
})
|
||||
|
||||
Cypress.Commands.add('deckCreateList', ({ user, password }, title) => {
|
||||
cy.login(user, password)
|
||||
|
||||
cy.get('.app-navigation button.app-navigation-toggle').click()
|
||||
cy.get('#app-navigation-vue .app-navigation__list .app-navigation-entry')
|
||||
.eq(3)
|
||||
.find('a.app-navigation-entry-link')
|
||||
.first()
|
||||
.click({ force: true })
|
||||
|
||||
cy.get('#stack-add button').first().click()
|
||||
cy.get('#stack-add form input#new-stack-input-main').type(title)
|
||||
cy.get('#stack-add form input[type=submit]').first().click()
|
||||
})
|
||||
|
||||
Cypress.Commands.add('createExampleBoard', ({ user, password, board }) => {
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: `${Cypress.env('baseUrl')}/index.php/apps/deck/api/v1.0/boards`,
|
||||
auth: {
|
||||
user,
|
||||
password,
|
||||
},
|
||||
body: { title: board.title, color: board.color ?? 'ff0000' },
|
||||
}).then((boardResponse) => {
|
||||
expect(boardResponse.status).to.eq(200)
|
||||
const boardData = boardResponse.body
|
||||
for (const stackIndex in board.stacks) {
|
||||
const stack = board.stacks[stackIndex]
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: `${Cypress.env('baseUrl')}/index.php/apps/deck/api/v1.0/boards/${boardData.id}/stacks`,
|
||||
auth: {
|
||||
user,
|
||||
password,
|
||||
},
|
||||
body: { title: stack.title, order: 0 },
|
||||
}).then((stackResponse) => {
|
||||
const stackData = stackResponse.body
|
||||
for (const cardIndex in stack.cards) {
|
||||
const card = stack.cards[cardIndex]
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: `${Cypress.env('baseUrl')}/index.php/apps/deck/api/v1.0/boards/${boardData.id}/stacks/${stackData.id}/cards`,
|
||||
auth: {
|
||||
user,
|
||||
password,
|
||||
},
|
||||
body: { title: card.title },
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Cypress.Commands.add('getNavigationEntry', (boardTitle) => {
|
||||
return cy.get('.app-navigation-entry-wrapper[icon=icon-deck]')
|
||||
.find('ul.app-navigation-entry__children .app-navigation-entry:contains(' + boardTitle + ')')
|
||||
.find('a.app-navigation-entry-link')
|
||||
})
|
||||
@@ -1,20 +0,0 @@
|
||||
// ***********************************************************
|
||||
// This example support/index.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import './commands'
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
||||
@@ -1 +0,0 @@
|
||||
export const randHash = () => Math.random().toString(36).replace(/[^a-z]+/g, '').slice(0, 10)
|
||||
@@ -90,7 +90,7 @@ Steps:
|
||||
* Create the configuration file
|
||||
* Execute the import informing the import file path, data file and source as `Trello JSON`
|
||||
|
||||
Create the configuration file respecting the [JSON Schema](https://github.com/nextcloud/deck/blob/master/lib/Service/Importer/fixtures/config-trelloJson-schema.json) for import `Trello JSON`
|
||||
Create the configuration file respecting the [JSON Schema](https://github.com/nextcloud/deck/blob/master/lib/Service/fixtures/config-trelloJson-schema.json) for import `Trello JSON`
|
||||
|
||||
Example configuration file:
|
||||
```json
|
||||
@@ -120,7 +120,7 @@ https://api.trello.com/1/members/me/boards?key={yourKey}&token={yourToken}&field
|
||||
This ID you will use in the configuration file in the `board` property
|
||||
* Create the configuration file
|
||||
|
||||
Create the configuration file respecting the [JSON Schema](https://github.com/nextcloud/deck/blob/master/lib/Service/Importer/fixtures/config-trelloApi-schema.json) for import `Trello JSON`
|
||||
Create the configuration file respecting the [JSON Schema](https://github.com/nextcloud/deck/blob/master/lib/Service/fixtures/config-trelloApi-schema.json) for import `Trello JSON`
|
||||
|
||||
Example configuration file:
|
||||
```json
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="32" width="32" version="1.0" viewbox="0 0 32 32">
|
||||
<path d="m16 1-10 18h11l-1 12 10-18h-11z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 205 B |
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="32" width="32" version="1.0" viewBox="0 0 32 32">
|
||||
<path d="m16 1-10 18h11l-1 12 10-18h-11z" fill="#FFF"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 217 B |
1
img/archive-white.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><g transform="translate(0 -1036.362)" fill="#fff"><path d="M1.93 1041.296c-.185 0-.336.138-.336.31v9.842c0 .172.15.313.336.313h12.517c.185 0 .333-.14.333-.313v-9.842c0-.172-.148-.31-.333-.31H1.93zm4.124 1.507h4.223c.39 0 .705.314.705.704v.43c0 .39-.315.705-.705.705H6.054a.703.703 0 0 1-.705-.705v-.43c0-.39.314-.704.705-.704z"/><rect width="15.742" height="2.296" x=".136" y="1037.543" ry="0"/></g></svg>
|
||||
|
After Width: | Height: | Size: 488 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 58 58" width="512" height="512"><g fill="#000"><path d="M54.319 37.839C54.762 35.918 55 33.96 55 32c0-9.095-4.631-17.377-12.389-22.153a1 1 0 1 0-1.049 1.703C48.724 15.96 53 23.604 53 32c0 1.726-.2 3.451-.573 5.147A6.992 6.992 0 0 0 51 37c-3.86 0-7 3.141-7 7s3.14 7 7 7 7-3.141 7-7a7.006 7.006 0 0 0-3.681-6.161zM38.171 54.182A23.867 23.867 0 0 1 29 56a24.047 24.047 0 0 1-17.017-7.092A6.974 6.974 0 0 0 14 44c0-3.859-3.14-7-7-7s-7 3.141-7 7 3.14 7 7 7a6.952 6.952 0 0 0 3.381-.875C15.26 55.136 21.994 58 29 58c3.435 0 6.778-.663 9.936-1.971.51-.211.753-.796.542-1.307a1.001 1.001 0 0 0-1.307-.54zM4 31.213a1 1 0 0 0 1.068-.927c.712-10.089 7.586-18.52 17.22-21.314C23.142 11.874 25.825 14 29 14c3.86 0 7-3.141 7-7s-3.14-7-7-7c-3.851 0-6.985 3.127-6.999 6.975C11.42 9.922 3.851 19.12 3.073 30.146A.999.999 0 0 0 4 31.213z"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 885 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 58 58" width="512" height="512"><g fill="#fff"><path d="M54.319 37.839C54.762 35.918 55 33.96 55 32c0-9.095-4.631-17.377-12.389-22.153a1 1 0 1 0-1.049 1.703C48.724 15.96 53 23.604 53 32c0 1.726-.2 3.451-.573 5.147A6.992 6.992 0 0 0 51 37c-3.86 0-7 3.141-7 7s3.14 7 7 7 7-3.141 7-7a7.006 7.006 0 0 0-3.681-6.161zM38.171 54.182A23.867 23.867 0 0 1 29 56a24.047 24.047 0 0 1-17.017-7.092A6.974 6.974 0 0 0 14 44c0-3.859-3.14-7-7-7s-7 3.141-7 7 3.14 7 7 7a6.952 6.952 0 0 0 3.381-.875C15.26 55.136 21.994 58 29 58c3.435 0 6.778-.663 9.936-1.971.51-.211.753-.796.542-1.307a1.001 1.001 0 0 0-1.307-.54zM4 31.213a1 1 0 0 0 1.068-.927c.712-10.089 7.586-18.52 17.22-21.314C23.142 11.874 25.825 14 29 14c3.86 0 7-3.141 7-7s-3.14-7-7-7c-3.851 0-6.985 3.127-6.999 6.975C11.42 9.922 3.851 19.12 3.073 30.146A.999.999 0 0 0 4 31.213z"/></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 58 58" width="512" height="512"><g fill="#000"><path d="M54.319 37.839C54.762 35.918 55 33.96 55 32c0-9.095-4.631-17.377-12.389-22.153a1 1 0 1 0-1.049 1.703C48.724 15.96 53 23.604 53 32c0 1.726-.2 3.451-.573 5.147A6.992 6.992 0 0 0 51 37c-3.86 0-7 3.141-7 7s3.14 7 7 7 7-3.141 7-7a7.006 7.006 0 0 0-3.681-6.161zM38.171 54.182A23.867 23.867 0 0 1 29 56a24.047 24.047 0 0 1-17.017-7.092A6.974 6.974 0 0 0 14 44c0-3.859-3.14-7-7-7s-7 3.141-7 7 3.14 7 7 7a6.952 6.952 0 0 0 3.381-.875C15.26 55.136 21.994 58 29 58c3.435 0 6.778-.663 9.936-1.971.51-.211.753-.796.542-1.307a1.001 1.001 0 0 0-1.307-.54zM4 31.213a1 1 0 0 0 1.068-.927c.712-10.089 7.586-18.52 17.22-21.314C23.142 11.874 25.825 14 29 14c3.86 0 7-3.141 7-7s-3.14-7-7-7c-3.851 0-6.985 3.127-6.999 6.975C11.42 9.922 3.851 19.12 3.073 30.146A.999.999 0 0 0 4 31.213z"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 885 B After Width: | Height: | Size: 885 B |
1
img/clone.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="16" height="16" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M11.8 13.8H2.2V4.2h9.6m1.2 0c0-.67-.53-1.2-1.2-1.2H2.2C1.53 3 1 3.53 1 4.2v9.6c0 .67.53 1.2 1.2 1.2h9.6c.67 0 1.2-.53 1.2-1.2"/><path d="m4.2 1c-0.67 0-1.2 0.54-1.2 1.2h10.8v10.8c0.67 0 1.2-0.53 1.2-1.2v-9.6c0-0.67-0.53-1.2-1.2-1.2z"/></svg>
|
||||
|
After Width: | Height: | Size: 327 B |
1
img/reply.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16"><path d="M15 15s-.4-7.8-7-10V1L1 8l7 7v-4c5.1 0 7 4 7 4z"/></svg>
|
||||
|
After Width: | Height: | Size: 128 B |
50
l10n/ast.js
@@ -1,50 +0,0 @@
|
||||
OC.L10N.register(
|
||||
"deck",
|
||||
{
|
||||
"Deck" : "Deck",
|
||||
"Personal" : "Personal",
|
||||
"%s on %s" : "%s en %s",
|
||||
"Finished" : "Finó",
|
||||
"Action needed" : "Precísase aición",
|
||||
"Later" : "Más sero",
|
||||
"Done" : "Fecho",
|
||||
"The file was uploaded" : "Xubióse'l ficheru",
|
||||
"The uploaded file exceeds the upload_max_filesize directive in php.ini" : "El ficheru xubíu perpasa la direutiva de xuba upload_max_filesize en php.ini",
|
||||
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" : "El ficheru xubíu perpasa la direutiva \"MAX_FILE_SIZE\" especificada nel formulariu HTML",
|
||||
"No file was uploaded" : "Nun se xubieron fichjeros",
|
||||
"Missing a temporary folder" : "Falta un direutoriu temporal",
|
||||
"Could not write file to disk" : "Nun pudo escribise nel discu'l ficheru",
|
||||
"A PHP extension stopped the file upload" : "Una estensión de PHP paró la xuba de ficheros",
|
||||
"Invalid date, date format must be YYYY-MM-DD" : "Data non válida, el formatu ha ser AAAA-MM-DD",
|
||||
"Cancel" : "Encaboxar",
|
||||
"Close" : "Zarrar",
|
||||
"File already exists" : "Yá esiste'l ficheru",
|
||||
"Show archived cards" : "Amosar tarxetes archivaes",
|
||||
"Details" : "Detalles",
|
||||
"Sharing" : "Compartiendo",
|
||||
"Tags" : "Etiquetes",
|
||||
"Undo" : "Desfacer",
|
||||
"Can edit" : "Can edit",
|
||||
"Can share" : "Can share",
|
||||
"Owner" : "Owner",
|
||||
"Delete" : "Desaniciar",
|
||||
"Edit" : "Editar",
|
||||
"Members" : "Miembros",
|
||||
"Download" : "Baxar",
|
||||
"Attachments" : "Axuntos",
|
||||
"Comments" : "Comentarios",
|
||||
"Modified" : "Modificóse'l",
|
||||
"Created" : "Creóse",
|
||||
"Today" : "Güei",
|
||||
"Tomorrow" : "Mañana",
|
||||
"Save" : "Guardar",
|
||||
"Reply" : "Rempuesta",
|
||||
"Update" : "Anovar",
|
||||
"Description" : "Descripción",
|
||||
"(group)" : "(grupu)",
|
||||
"seconds ago" : "hai segundos",
|
||||
"Shared with you" : "Shared with you",
|
||||
"No notifications" : "Ensin avisos",
|
||||
"Share" : "Share"
|
||||
},
|
||||
"nplurals=2; plural=(n != 1);");
|
||||
@@ -1,48 +0,0 @@
|
||||
{ "translations": {
|
||||
"Deck" : "Deck",
|
||||
"Personal" : "Personal",
|
||||
"%s on %s" : "%s en %s",
|
||||
"Finished" : "Finó",
|
||||
"Action needed" : "Precísase aición",
|
||||
"Later" : "Más sero",
|
||||
"Done" : "Fecho",
|
||||
"The file was uploaded" : "Xubióse'l ficheru",
|
||||
"The uploaded file exceeds the upload_max_filesize directive in php.ini" : "El ficheru xubíu perpasa la direutiva de xuba upload_max_filesize en php.ini",
|
||||
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" : "El ficheru xubíu perpasa la direutiva \"MAX_FILE_SIZE\" especificada nel formulariu HTML",
|
||||
"No file was uploaded" : "Nun se xubieron fichjeros",
|
||||
"Missing a temporary folder" : "Falta un direutoriu temporal",
|
||||
"Could not write file to disk" : "Nun pudo escribise nel discu'l ficheru",
|
||||
"A PHP extension stopped the file upload" : "Una estensión de PHP paró la xuba de ficheros",
|
||||
"Invalid date, date format must be YYYY-MM-DD" : "Data non válida, el formatu ha ser AAAA-MM-DD",
|
||||
"Cancel" : "Encaboxar",
|
||||
"Close" : "Zarrar",
|
||||
"File already exists" : "Yá esiste'l ficheru",
|
||||
"Show archived cards" : "Amosar tarxetes archivaes",
|
||||
"Details" : "Detalles",
|
||||
"Sharing" : "Compartiendo",
|
||||
"Tags" : "Etiquetes",
|
||||
"Undo" : "Desfacer",
|
||||
"Can edit" : "Can edit",
|
||||
"Can share" : "Can share",
|
||||
"Owner" : "Owner",
|
||||
"Delete" : "Desaniciar",
|
||||
"Edit" : "Editar",
|
||||
"Members" : "Miembros",
|
||||
"Download" : "Baxar",
|
||||
"Attachments" : "Axuntos",
|
||||
"Comments" : "Comentarios",
|
||||
"Modified" : "Modificóse'l",
|
||||
"Created" : "Creóse",
|
||||
"Today" : "Güei",
|
||||
"Tomorrow" : "Mañana",
|
||||
"Save" : "Guardar",
|
||||
"Reply" : "Rempuesta",
|
||||
"Update" : "Anovar",
|
||||
"Description" : "Descripción",
|
||||
"(group)" : "(grupu)",
|
||||
"seconds ago" : "hai segundos",
|
||||
"Shared with you" : "Shared with you",
|
||||
"No notifications" : "Ensin avisos",
|
||||
"Share" : "Share"
|
||||
},"pluralForm" :"nplurals=2; plural=(n != 1);"
|
||||
}
|
||||
@@ -81,6 +81,7 @@ OC.L10N.register(
|
||||
"Deck board" : "Доска",
|
||||
"Owned by %1$s" : "Владелец: %1$s",
|
||||
"Deck boards, cards and comments" : "Доски, карточки и комментарии",
|
||||
"From %1$s, in %2$s/%3$s, owned by %4$s" : "Из %1$s, в %2$s/%3$s, принадлежит %4$s",
|
||||
"Card comments" : "Комментарии карточки",
|
||||
"%s on %s" : "%s на %s",
|
||||
"Deck boards and cards" : "Доски и карточки",
|
||||
@@ -156,6 +157,7 @@ OC.L10N.register(
|
||||
"Toggle compact mode" : "Выбор компактного или обычного режима просмотра",
|
||||
"Open details" : "Открыть подробности",
|
||||
"Details" : "Свойства",
|
||||
"Currently present people" : "Присутствующие в настоящее время люди",
|
||||
"Loading board" : "Загрузка доски",
|
||||
"No lists available" : "Нет ни одного списка",
|
||||
"Create a new list to add cards to this board" : "Создайте список чтобы добавить карточки на эту доску",
|
||||
@@ -296,10 +298,12 @@ OC.L10N.register(
|
||||
"Deck board {name}\n* Last modified on {lastMod}" : "Доска «{name}»\n* Последнее изменение: {lastMod}",
|
||||
"{stack} in {board}" : "«{stack}» с доски «{board}»",
|
||||
"Click to expand description" : "Нажмите, чтобы развернуть поле описания",
|
||||
"* Created on {created}\n* Last modified on {lastMod}\n* {nbAttachments} attachments\n* {nbComments} comments" : "* Создано {created}\n* Последнее изменение {lastMod}\n* {nbAttachments} вложений\n* {nbComments} комментариев",
|
||||
"{nbCards} cards" : "карточек: {nbCards}",
|
||||
"Click to expand comment" : "Нажмите, чтобы развернуть комментарии",
|
||||
"No upcoming cards" : "Отсутствуют карточки, ожидающие выполнения",
|
||||
"upcoming cards" : "карточки, ожидающие выполнения",
|
||||
"New card" : "Новая карточка",
|
||||
"Due on {date}" : "Дата исполнения: {date}",
|
||||
"Link to a board" : "Ссылка на доску",
|
||||
"Link to a card" : "Ссылка на карточку",
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
"Deck board" : "Доска",
|
||||
"Owned by %1$s" : "Владелец: %1$s",
|
||||
"Deck boards, cards and comments" : "Доски, карточки и комментарии",
|
||||
"From %1$s, in %2$s/%3$s, owned by %4$s" : "Из %1$s, в %2$s/%3$s, принадлежит %4$s",
|
||||
"Card comments" : "Комментарии карточки",
|
||||
"%s on %s" : "%s на %s",
|
||||
"Deck boards and cards" : "Доски и карточки",
|
||||
@@ -154,6 +155,7 @@
|
||||
"Toggle compact mode" : "Выбор компактного или обычного режима просмотра",
|
||||
"Open details" : "Открыть подробности",
|
||||
"Details" : "Свойства",
|
||||
"Currently present people" : "Присутствующие в настоящее время люди",
|
||||
"Loading board" : "Загрузка доски",
|
||||
"No lists available" : "Нет ни одного списка",
|
||||
"Create a new list to add cards to this board" : "Создайте список чтобы добавить карточки на эту доску",
|
||||
@@ -294,10 +296,12 @@
|
||||
"Deck board {name}\n* Last modified on {lastMod}" : "Доска «{name}»\n* Последнее изменение: {lastMod}",
|
||||
"{stack} in {board}" : "«{stack}» с доски «{board}»",
|
||||
"Click to expand description" : "Нажмите, чтобы развернуть поле описания",
|
||||
"* Created on {created}\n* Last modified on {lastMod}\n* {nbAttachments} attachments\n* {nbComments} comments" : "* Создано {created}\n* Последнее изменение {lastMod}\n* {nbAttachments} вложений\n* {nbComments} комментариев",
|
||||
"{nbCards} cards" : "карточек: {nbCards}",
|
||||
"Click to expand comment" : "Нажмите, чтобы развернуть комментарии",
|
||||
"No upcoming cards" : "Отсутствуют карточки, ожидающие выполнения",
|
||||
"upcoming cards" : "карточки, ожидающие выполнения",
|
||||
"New card" : "Новая карточка",
|
||||
"Due on {date}" : "Дата исполнения: {date}",
|
||||
"Link to a board" : "Ссылка на доску",
|
||||
"Link to a card" : "Ссылка на карточку",
|
||||
|
||||
14
l10n/sv.js
@@ -78,7 +78,7 @@ OC.L10N.register(
|
||||
"{user} has mentioned you in a comment on {deck-card}." : "{user} har nämnt dig i en kommentar i {deck-card}.",
|
||||
"The board \"%s\" has been shared with you by %s." : "Tavlan \"%s\" har delats med dig av %s.",
|
||||
"{user} has shared {deck-board} with you." : "{user} har delat {deck-board} med dig.",
|
||||
"Deck board" : "Deck-plank",
|
||||
"Deck board" : "Deck-tavla",
|
||||
"Owned by %1$s" : "Ägd av %1$s",
|
||||
"Deck boards, cards and comments" : "Deck tavlor, kort och kommentarer",
|
||||
"From %1$s, in %2$s/%3$s, owned by %4$s" : "Från %1$s, i %2$s/%3$s, ägd av %4$s",
|
||||
@@ -92,7 +92,7 @@ OC.L10N.register(
|
||||
"Later" : "Senare",
|
||||
"copy" : "kopiera",
|
||||
"To do" : "Att göra",
|
||||
"Doing" : "Gör",
|
||||
"Doing" : "Pågående",
|
||||
"Done" : "Klart",
|
||||
"Example Task 3" : "Exempeluppgift 3",
|
||||
"Example Task 2" : "Exempeluppgift 2",
|
||||
@@ -249,7 +249,7 @@ OC.L10N.register(
|
||||
"Write a description …" : "Ange en beskrivning ...",
|
||||
"Choose attachment" : "Välj bilaga",
|
||||
"(group)" : " (grupp)",
|
||||
"Todo items" : "Todo saker",
|
||||
"Todo items" : "Att göra saker",
|
||||
"{count} comments, {unread} unread" : "{count} kommentarer, {unread} olästa",
|
||||
"Edit card title" : "Ändra korttitel",
|
||||
"Assign to me" : "Tilldela till mig",
|
||||
@@ -289,7 +289,7 @@ OC.L10N.register(
|
||||
"Only assigned cards" : "Bara tilldelade kort",
|
||||
"No reminder" : "Ingen påminnelse",
|
||||
"An error occurred" : "Ett fel uppstod",
|
||||
"Are you sure you want to delete the board {title}? This will delete all the data of this board including archived cards." : "Är du säker på att du vill radera brädet {title}? Detta kommer radera all data som tillhör brädet inklusive arkiverade kort.",
|
||||
"Are you sure you want to delete the board {title}? This will delete all the data of this board including archived cards." : "Är du säker på att du vill radera tavla {title}? Detta kommer radera all data som tillhör tavlan inklusive arkiverade kort.",
|
||||
"Delete the board?" : "Ta bort tavlan?",
|
||||
"Loading filtered view" : "Laddar filtrerad vy",
|
||||
"No due" : "Inget slut",
|
||||
@@ -316,9 +316,9 @@ OC.L10N.register(
|
||||
"Share with a Deck card" : "Dela med ett Deck-kort",
|
||||
"Share {file} with a Deck card" : "Dela {file} med ett Deck-kort",
|
||||
"Share" : "Dela",
|
||||
"Are you sure you want to transfer the board {title} for {user}?" : "Är du säker på att du vill överföra brädet {title} för {user}?",
|
||||
"Transfer the board for {user} successfully" : "Överförde brädet för {user}",
|
||||
"Failed to transfer the board for {user}" : "Misslyckades med att överföra brädet för {user}",
|
||||
"Are you sure you want to transfer the board {title} for {user}?" : "Är du säker på att du vill överföra tavla {title} för {user}?",
|
||||
"Transfer the board for {user} successfully" : "Överförde tavlan för {user}",
|
||||
"Failed to transfer the board for {user}" : "Misslyckades med att överföra tavlan för {user}",
|
||||
"Add a new list" : "Lägg till en ny lista",
|
||||
"Are you sure you want to delete the board {title}? This will delete all the data of this board." : "Är du säker på att du vill radera tavla {title}? Detta kommer att radera all information från denna tavla."
|
||||
},
|
||||
|
||||
14
l10n/sv.json
@@ -76,7 +76,7 @@
|
||||
"{user} has mentioned you in a comment on {deck-card}." : "{user} har nämnt dig i en kommentar i {deck-card}.",
|
||||
"The board \"%s\" has been shared with you by %s." : "Tavlan \"%s\" har delats med dig av %s.",
|
||||
"{user} has shared {deck-board} with you." : "{user} har delat {deck-board} med dig.",
|
||||
"Deck board" : "Deck-plank",
|
||||
"Deck board" : "Deck-tavla",
|
||||
"Owned by %1$s" : "Ägd av %1$s",
|
||||
"Deck boards, cards and comments" : "Deck tavlor, kort och kommentarer",
|
||||
"From %1$s, in %2$s/%3$s, owned by %4$s" : "Från %1$s, i %2$s/%3$s, ägd av %4$s",
|
||||
@@ -90,7 +90,7 @@
|
||||
"Later" : "Senare",
|
||||
"copy" : "kopiera",
|
||||
"To do" : "Att göra",
|
||||
"Doing" : "Gör",
|
||||
"Doing" : "Pågående",
|
||||
"Done" : "Klart",
|
||||
"Example Task 3" : "Exempeluppgift 3",
|
||||
"Example Task 2" : "Exempeluppgift 2",
|
||||
@@ -247,7 +247,7 @@
|
||||
"Write a description …" : "Ange en beskrivning ...",
|
||||
"Choose attachment" : "Välj bilaga",
|
||||
"(group)" : " (grupp)",
|
||||
"Todo items" : "Todo saker",
|
||||
"Todo items" : "Att göra saker",
|
||||
"{count} comments, {unread} unread" : "{count} kommentarer, {unread} olästa",
|
||||
"Edit card title" : "Ändra korttitel",
|
||||
"Assign to me" : "Tilldela till mig",
|
||||
@@ -287,7 +287,7 @@
|
||||
"Only assigned cards" : "Bara tilldelade kort",
|
||||
"No reminder" : "Ingen påminnelse",
|
||||
"An error occurred" : "Ett fel uppstod",
|
||||
"Are you sure you want to delete the board {title}? This will delete all the data of this board including archived cards." : "Är du säker på att du vill radera brädet {title}? Detta kommer radera all data som tillhör brädet inklusive arkiverade kort.",
|
||||
"Are you sure you want to delete the board {title}? This will delete all the data of this board including archived cards." : "Är du säker på att du vill radera tavla {title}? Detta kommer radera all data som tillhör tavlan inklusive arkiverade kort.",
|
||||
"Delete the board?" : "Ta bort tavlan?",
|
||||
"Loading filtered view" : "Laddar filtrerad vy",
|
||||
"No due" : "Inget slut",
|
||||
@@ -314,9 +314,9 @@
|
||||
"Share with a Deck card" : "Dela med ett Deck-kort",
|
||||
"Share {file} with a Deck card" : "Dela {file} med ett Deck-kort",
|
||||
"Share" : "Dela",
|
||||
"Are you sure you want to transfer the board {title} for {user}?" : "Är du säker på att du vill överföra brädet {title} för {user}?",
|
||||
"Transfer the board for {user} successfully" : "Överförde brädet för {user}",
|
||||
"Failed to transfer the board for {user}" : "Misslyckades med att överföra brädet för {user}",
|
||||
"Are you sure you want to transfer the board {title} for {user}?" : "Är du säker på att du vill överföra tavla {title} för {user}?",
|
||||
"Transfer the board for {user} successfully" : "Överförde tavlan för {user}",
|
||||
"Failed to transfer the board for {user}" : "Misslyckades med att överföra tavlan för {user}",
|
||||
"Add a new list" : "Lägg till en ny lista",
|
||||
"Are you sure you want to delete the board {title}? This will delete all the data of this board." : "Är du säker på att du vill radera tavla {title}? Detta kommer att radera all information från denna tavla."
|
||||
},"pluralForm" :"nplurals=2; plural=(n != 1);"
|
||||
|
||||
@@ -173,7 +173,7 @@ OC.L10N.register(
|
||||
"Can manage" : "可以管理",
|
||||
"Owner" : "所有者",
|
||||
"Delete" : "删除",
|
||||
"Failed to create share with {displayName}" : "用 {displayName} 创建分享失败",
|
||||
"Failed to create share with {displayName}" : "用{displayName}创建分享失败",
|
||||
"Transfer" : "传输",
|
||||
"Archive all cards" : "归档所有卡片",
|
||||
"Delete list" : "删除列表",
|
||||
@@ -285,7 +285,7 @@ OC.L10N.register(
|
||||
"Create a card" : "创建一张卡片",
|
||||
"Message from {author} in {conversationName}" : "{conversationName} 会话中来自 {author} 的消息",
|
||||
"Something went wrong" : "发生了错误",
|
||||
"Failed to upload {name}" : "未能上传 {name}",
|
||||
"Failed to upload {name}" : "未能上传{name}",
|
||||
"Maximum file size of {size} exceeded" : "文件容量已超过 {size} 的上限",
|
||||
"Error creating the share" : "创建分享出错",
|
||||
"Share with a Deck card" : "分享给一张看板卡片",
|
||||
|
||||
@@ -171,7 +171,7 @@
|
||||
"Can manage" : "可以管理",
|
||||
"Owner" : "所有者",
|
||||
"Delete" : "删除",
|
||||
"Failed to create share with {displayName}" : "用 {displayName} 创建分享失败",
|
||||
"Failed to create share with {displayName}" : "用{displayName}创建分享失败",
|
||||
"Transfer" : "传输",
|
||||
"Archive all cards" : "归档所有卡片",
|
||||
"Delete list" : "删除列表",
|
||||
@@ -283,7 +283,7 @@
|
||||
"Create a card" : "创建一张卡片",
|
||||
"Message from {author} in {conversationName}" : "{conversationName} 会话中来自 {author} 的消息",
|
||||
"Something went wrong" : "发生了错误",
|
||||
"Failed to upload {name}" : "未能上传 {name}",
|
||||
"Failed to upload {name}" : "未能上传{name}",
|
||||
"Maximum file size of {size} exceeded" : "文件容量已超过 {size} 的上限",
|
||||
"Error creating the share" : "创建分享出错",
|
||||
"Share with a Deck card" : "分享给一张看板卡片",
|
||||
|
||||
@@ -45,9 +45,7 @@ use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
||||
use OCP\Comments\IComment;
|
||||
use OCP\IUser;
|
||||
use OCP\Server;
|
||||
use OCP\L10N\IFactory;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class ActivityManager {
|
||||
public const DECK_NOAUTHOR_COMMENT_SYSTEM_ENFORCED = 'DECK_NOAUTHOR_COMMENT_SYSTEM_ENFORCED';
|
||||
@@ -55,14 +53,14 @@ class ActivityManager {
|
||||
public const SUBJECT_PARAMS_MAX_LENGTH = 4000;
|
||||
public const SHORTENED_DESCRIPTION_MAX_LENGTH = 2000;
|
||||
|
||||
private IManager $manager;
|
||||
private ?string $userId;
|
||||
private PermissionService $permissionService;
|
||||
private BoardMapper $boardMapper;
|
||||
private CardMapper $cardMapper;
|
||||
private AclMapper $aclMapper;
|
||||
private StackMapper $stackMapper;
|
||||
private IFactory $l10nFactory;
|
||||
private $manager;
|
||||
private $userId;
|
||||
private $permissionService;
|
||||
private $boardMapper;
|
||||
private $cardMapper;
|
||||
private $aclMapper;
|
||||
private $stackMapper;
|
||||
private $l10nFactory;
|
||||
|
||||
public const DECK_OBJECT_BOARD = 'deck_board';
|
||||
public const DECK_OBJECT_CARD = 'deck_card';
|
||||
@@ -116,7 +114,7 @@ class ActivityManager {
|
||||
StackMapper $stackMapper,
|
||||
AclMapper $aclMapper,
|
||||
IFactory $l10nFactory,
|
||||
?string $userId
|
||||
$userId
|
||||
) {
|
||||
$this->manager = $manager;
|
||||
$this->permissionService = $permissionsService;
|
||||
@@ -312,10 +310,10 @@ class ActivityManager {
|
||||
try {
|
||||
$object = $this->findObjectForEntity($objectType, $entity);
|
||||
} catch (DoesNotExistException $e) {
|
||||
Server::get(LoggerInterface::class)->error('Could not create activity entry for ' . $subject . '. Entity not found.', (array)$entity);
|
||||
\OC::$server->getLogger()->error('Could not create activity entry for ' . $subject . '. Entity not found.', (array)$entity);
|
||||
return null;
|
||||
} catch (MultipleObjectsReturnedException $e) {
|
||||
Server::get(LoggerInterface::class)->error('Could not create activity entry for ' . $subject . '. Entity not found.', (array)$entity);
|
||||
\OC::$server->getLogger()->error('Could not create activity entry for ' . $subject . '. Entity not found.', (array)$entity);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -367,15 +365,7 @@ class ActivityManager {
|
||||
case self::SUBJECT_CARD_USER_ASSIGN:
|
||||
case self::SUBJECT_CARD_USER_UNASSIGN:
|
||||
$subjectParams = $this->findDetailsForCard($entity->getId(), $subject);
|
||||
|
||||
if (isset($additionalParams['after']) && $additionalParams['after'] instanceof \DateTimeInterface) {
|
||||
$additionalParams['after'] = $additionalParams['after']->format('c');
|
||||
}
|
||||
if (isset($additionalParams['before']) && $additionalParams['before'] instanceof \DateTimeInterface) {
|
||||
$additionalParams['before'] = $additionalParams['before']->format('c');
|
||||
}
|
||||
|
||||
break;
|
||||
break;
|
||||
case self::SUBJECT_ATTACHMENT_CREATE:
|
||||
case self::SUBJECT_ATTACHMENT_UPDATE:
|
||||
case self::SUBJECT_ATTACHMENT_DELETE:
|
||||
|
||||
@@ -312,19 +312,12 @@ class DeckProvider implements IProvider {
|
||||
$userLanguage = $this->config->getUserValue($event->getAuthor(), 'core', 'lang', $this->l10nFactory->findLanguage());
|
||||
$userLocale = $this->config->getUserValue($event->getAuthor(), 'core', 'locale', $this->l10nFactory->findLocale());
|
||||
$l10n = $this->l10nFactory->get('deck', $userLanguage, $userLocale);
|
||||
if (is_array($subjectParams['after'])) {
|
||||
// Unluckily there was a time when we stored jsonSerialized date objects in the database
|
||||
// Broken in 1.8.0 and fixed again in 1.8.1
|
||||
$date = new \DateTime($subjectParams['after']['date']);
|
||||
$date->setTimezone(new \DateTimeZone(\date_default_timezone_get()));
|
||||
} else {
|
||||
$date = new \DateTime($subjectParams['after']);
|
||||
$date->setTimezone(new \DateTimeZone(\date_default_timezone_get()));
|
||||
}
|
||||
$date = new \DateTime($subjectParams['after']);
|
||||
$date->setTimezone(new \DateTimeZone(\date_default_timezone_get()));
|
||||
$params['after'] = [
|
||||
'type' => 'highlight',
|
||||
'id' => 'dt:' . $subjectParams['after'],
|
||||
'name' => $l10n->l('datetime', $date),
|
||||
'name' => $l10n->l('datetime', $date)
|
||||
];
|
||||
}
|
||||
return $params;
|
||||
|
||||
@@ -47,7 +47,6 @@ use OCA\Deck\Listeners\FullTextSearchEventListener;
|
||||
use OCA\Deck\Middleware\DefaultBoardMiddleware;
|
||||
use OCA\Deck\Middleware\ExceptionMiddleware;
|
||||
use OCA\Deck\Notification\Notifier;
|
||||
use OCA\Deck\Reference\CardReferenceProvider;
|
||||
use OCA\Deck\Search\CardCommentProvider;
|
||||
use OCA\Deck\Search\DeckProvider;
|
||||
use OCA\Deck\Service\PermissionService;
|
||||
@@ -58,22 +57,20 @@ use OCP\AppFramework\Bootstrap\IBootContext;
|
||||
use OCP\AppFramework\Bootstrap\IBootstrap;
|
||||
use OCP\AppFramework\Bootstrap\IRegistrationContext;
|
||||
use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent;
|
||||
use OCP\Collaboration\Reference\RenderReferenceEvent;
|
||||
use OCP\Collaboration\Resources\IProviderManager;
|
||||
use OCP\Comments\CommentsEntityEvent;
|
||||
use OCP\Comments\ICommentsManager;
|
||||
use OCP\EventDispatcher\Event;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\Group\Events\GroupDeletedEvent;
|
||||
use OCP\IConfig;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\IGroup;
|
||||
use OCP\IGroupManager;
|
||||
use OCP\IRequest;
|
||||
use OCP\Server;
|
||||
use OCP\IServerContainer;
|
||||
use OCP\IUser;
|
||||
use OCP\IUserManager;
|
||||
use OCP\Notification\IManager as NotificationManager;
|
||||
use OCP\Share\IManager;
|
||||
use OCP\User\Events\UserDeletedEvent;
|
||||
use OCP\Util;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
@@ -82,16 +79,13 @@ class Application extends App implements IBootstrap {
|
||||
|
||||
public const COMMENT_ENTITY_TYPE = 'deckCard';
|
||||
|
||||
/** @var IServerContainer */
|
||||
private $server;
|
||||
|
||||
public function __construct(array $urlParams = []) {
|
||||
parent::__construct(self::APP_ID, $urlParams);
|
||||
|
||||
// TODO move this back to ::register after fixing the autoload issue
|
||||
// (and use a listener class)
|
||||
$container = $this->getContainer();
|
||||
$eventDispatcher = $container->get(IEventDispatcher::class);
|
||||
$eventDispatcher->addListener(RenderReferenceEvent::class, function () {
|
||||
Util::addScript(self::APP_ID, self::APP_ID . '-card-reference');
|
||||
});
|
||||
$this->server = \OC::$server;
|
||||
}
|
||||
|
||||
public function boot(IBootContext $context): void {
|
||||
@@ -130,12 +124,8 @@ class Application extends App implements IBootstrap {
|
||||
$context->registerSearchProvider(CardCommentProvider::class);
|
||||
$context->registerDashboardWidget(DeckWidget::class);
|
||||
|
||||
// reference widget
|
||||
$context->registerReferenceProvider(CardReferenceProvider::class);
|
||||
// $context->registerEventListener(RenderReferenceEvent::class, CardReferenceListener::class);
|
||||
|
||||
$context->registerEventListener(BeforeTemplateRenderedEvent::class, BeforeTemplateRenderedListener::class);
|
||||
|
||||
|
||||
// Event listening for full text search indexing
|
||||
$context->registerEventListener(CardCreatedEvent::class, FullTextSearchEventListener::class);
|
||||
$context->registerEventListener(CardUpdatedEvent::class, FullTextSearchEventListener::class);
|
||||
@@ -151,43 +141,33 @@ class Application extends App implements IBootstrap {
|
||||
|
||||
private function registerUserGroupHooks(IUserManager $userManager, IGroupManager $groupManager): void {
|
||||
$container = $this->getContainer();
|
||||
/** @var IEventDispatcher $eventDispatcher */
|
||||
$eventDispatcher = $container->get(IEventDispatcher::class);
|
||||
// Delete user/group acl entries when they get deleted
|
||||
$eventDispatcher->addListener(UserDeletedEvent::class, static function (Event $event) use ($container): void {
|
||||
if (!($event instanceof UserDeletedEvent)) {
|
||||
return;
|
||||
}
|
||||
$user = $event->getUser();
|
||||
$userManager->listen('\OC\User', 'postDelete', static function (IUser $user) use ($container) {
|
||||
// delete existing acl entries for deleted user
|
||||
/** @var AclMapper $aclMapper */
|
||||
$aclMapper = $container->get(AclMapper::class);
|
||||
$aclMapper = $container->query(AclMapper::class);
|
||||
$acls = $aclMapper->findByParticipant(Acl::PERMISSION_TYPE_USER, $user->getUID());
|
||||
foreach ($acls as $acl) {
|
||||
$aclMapper->delete($acl);
|
||||
}
|
||||
// delete existing user assignments
|
||||
$assignmentMapper = $container->get(AssignmentMapper::class);
|
||||
$assignmentMapper = $container->query(AssignmentMapper::class);
|
||||
$assignments = $assignmentMapper->findByParticipant($user->getUID());
|
||||
foreach ($assignments as $assignment) {
|
||||
$assignmentMapper->delete($assignment);
|
||||
}
|
||||
|
||||
/** @var BoardMapper $boardMapper */
|
||||
$boardMapper = $container->get(BoardMapper::class);
|
||||
$boardMapper = $container->query(BoardMapper::class);
|
||||
$boards = $boardMapper->findAllByOwner($user->getUID());
|
||||
foreach ($boards as $board) {
|
||||
$boardMapper->delete($board);
|
||||
}
|
||||
});
|
||||
|
||||
$eventDispatcher->addListener(GroupDeletedEvent::class, static function (Event $event) use ($container): void {
|
||||
if (!($event instanceof GroupDeletedEvent)) {
|
||||
return;
|
||||
}
|
||||
$group = $event->getGroup();
|
||||
$groupManager->listen('\OC\Group', 'postDelete', static function (IGroup $group) use ($container) {
|
||||
/** @var AclMapper $aclMapper */
|
||||
$aclMapper = $container->get(AclMapper::class);
|
||||
$aclMapper = $container->query(AclMapper::class);
|
||||
$aclMapper->findByParticipant(Acl::PERMISSION_TYPE_GROUP, $group->getGID());
|
||||
$acls = $aclMapper->findByParticipant(Acl::PERMISSION_TYPE_GROUP, $group->getGID());
|
||||
foreach ($acls as $acl) {
|
||||
@@ -201,7 +181,6 @@ class Application extends App implements IBootstrap {
|
||||
$event->addEntityCollection(self::COMMENT_ENTITY_TYPE, function ($name) {
|
||||
/** @var CardMapper */
|
||||
$cardMapper = $this->getContainer()->get(CardMapper::class);
|
||||
/** @var PermissionService $permissionService */
|
||||
$permissionService = $this->getContainer()->get(PermissionService::class);
|
||||
|
||||
try {
|
||||
@@ -224,7 +203,7 @@ class Application extends App implements IBootstrap {
|
||||
$resourceManager->registerResourceProvider(ResourceProviderCard::class);
|
||||
|
||||
$symfonyAdapter->addListener('\OCP\Collaboration\Resources::loadAdditionalScripts', static function () {
|
||||
if (strpos(Server::get(IRequest::class)->getPathInfo(), '/call/') === 0) {
|
||||
if (strpos(\OC::$server->getRequest()->getPathInfo(), '/call/') === 0) {
|
||||
// Talk integration has its own entrypoint which already includes collections handling
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -32,23 +32,20 @@ use OCP\AppFramework\QueryException;
|
||||
use OCP\Collaboration\Resources\IManager;
|
||||
use OCP\Collaboration\Resources\IProvider;
|
||||
use OCP\Collaboration\Resources\IResource;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\IUser;
|
||||
use OCP\Server;
|
||||
|
||||
class ResourceProvider implements IProvider {
|
||||
public const RESOURCE_TYPE = 'deck';
|
||||
|
||||
private BoardMapper $boardMapper;
|
||||
private PermissionService $permissionService;
|
||||
private IURLGenerator $urlGenerator;
|
||||
private $boardMapper;
|
||||
private $permissionService;
|
||||
|
||||
protected array $nodes = [];
|
||||
/** @var array */
|
||||
protected $nodes = [];
|
||||
|
||||
public function __construct(BoardMapper $boardMapper, PermissionService $permissionService, IURLGenerator $urlGenerator) {
|
||||
public function __construct(BoardMapper $boardMapper, PermissionService $permissionService) {
|
||||
$this->boardMapper = $boardMapper;
|
||||
$this->permissionService = $permissionService;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,14 +70,14 @@ class ResourceProvider implements IProvider {
|
||||
*/
|
||||
public function getResourceRichObject(IResource $resource): array {
|
||||
$board = $this->getBoard($resource);
|
||||
$link = $this->urlGenerator->linkToRoute('deck.page.index') . '#/board/' . $resource->getId();
|
||||
$link = \OC::$server->getURLGenerator()->linkToRoute('deck.page.index') . '#/board/' . $resource->getId();
|
||||
|
||||
return [
|
||||
'type' => self::RESOURCE_TYPE,
|
||||
'id' => $resource->getId(),
|
||||
'name' => $board->getTitle(),
|
||||
'link' => $link,
|
||||
'iconUrl' => $this->urlGenerator->imagePath('deck', 'deck-dark.svg')
|
||||
'iconUrl' => \OC::$server->getURLGenerator()->imagePath('deck', 'deck-dark.svg')
|
||||
];
|
||||
}
|
||||
|
||||
@@ -121,7 +118,7 @@ class ResourceProvider implements IProvider {
|
||||
public function invalidateAccessCache($boardId = null) {
|
||||
try {
|
||||
/** @var IManager $resourceManager */
|
||||
$resourceManager = Server::get(IManager::class);
|
||||
$resourceManager = \OC::$server->query(IManager::class);
|
||||
} catch (QueryException $e) {
|
||||
}
|
||||
if ($boardId !== null) {
|
||||
|
||||
@@ -37,16 +37,24 @@ use OCP\Collaboration\Resources\IResource;
|
||||
use OCP\Collaboration\Resources\ResourceException;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\IUser;
|
||||
use OCP\Server;
|
||||
|
||||
class ResourceProviderCard implements IProvider {
|
||||
public const RESOURCE_TYPE = 'deck-card';
|
||||
|
||||
private CardMapper $cardMapper;
|
||||
private BoardMapper $boardMapper;
|
||||
private PermissionService $permissionService;
|
||||
private IURLGenerator $urlGenerator;
|
||||
protected array $nodes = [];
|
||||
/** @var CardMapper */
|
||||
private $cardMapper;
|
||||
|
||||
/** @var BoardMapper */
|
||||
private $boardMapper;
|
||||
|
||||
/** @var PermissionService */
|
||||
private $permissionService;
|
||||
|
||||
/** @var IURLGenerator */
|
||||
private $urlGenerator;
|
||||
|
||||
/** @var array */
|
||||
protected $nodes = [];
|
||||
|
||||
public function __construct(CardMapper $cardMapper, BoardMapper $boardMapper, PermissionService $permissionService, IURLGenerator $urlGenerator) {
|
||||
$this->cardMapper = $cardMapper;
|
||||
@@ -139,7 +147,7 @@ class ResourceProviderCard implements IProvider {
|
||||
public function invalidateAccessCache($cardId = null) {
|
||||
try {
|
||||
/** @var IManager $resourceManager */
|
||||
$resourceManager = Server::get(IManager::class);
|
||||
$resourceManager = \OC::$server->query(IManager::class);
|
||||
} catch (QueryException $e) {
|
||||
}
|
||||
if ($cardId !== null) {
|
||||
|
||||
@@ -30,7 +30,8 @@ use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class BoardImport extends Command {
|
||||
private BoardImportCommandService $boardImportCommandService;
|
||||
/** @var BoardImportCommandService */
|
||||
private $boardImportCommandService;
|
||||
|
||||
public function __construct(
|
||||
BoardImportCommandService $boardImportCommandService
|
||||
|
||||
@@ -27,7 +27,6 @@ use OCA\Deck\Db\AssignmentMapper;
|
||||
use OCA\Deck\Db\BoardMapper;
|
||||
use OCA\Deck\Db\CardMapper;
|
||||
use OCA\Deck\Db\StackMapper;
|
||||
use OCA\Deck\Model\CardDetails;
|
||||
use OCA\Deck\Service\BoardService;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
||||
@@ -102,9 +101,7 @@ class UserExport extends Command {
|
||||
$fullCard = $this->cardMapper->find($card->getId());
|
||||
$assignedUsers = $this->assignedUsersMapper->findAll($card->getId());
|
||||
$fullCard->setAssignedUsers($assignedUsers);
|
||||
|
||||
$cardDetails = new CardDetails($fullCard, $fullBoard);
|
||||
$data[$board->getId()]['stacks'][$stack->getId()]['cards'][] = $cardDetails->jsonSerialize();
|
||||
$data[$board->getId()]['stacks'][$stack->getId()]['cards'][] = (array)$fullCard->jsonSerialize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,9 +29,7 @@ use OCA\Deck\Service\PermissionService;
|
||||
use OCA\Files\Event\LoadSidebar;
|
||||
use OCA\Viewer\Event\LoadViewer;
|
||||
use OCP\AppFramework\Http\ContentSecurityPolicy;
|
||||
use OCP\Collaboration\Resources\LoadAdditionalScriptsEvent as CollaborationResourcesEvent;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\IConfig;
|
||||
use OCP\IInitialStateService;
|
||||
use OCP\IRequest;
|
||||
use OCP\AppFramework\Http\TemplateResponse;
|
||||
@@ -43,17 +41,16 @@ use OCA\Deck\Db\Acl;
|
||||
use OCA\Deck\Service\CardService;
|
||||
|
||||
class PageController extends Controller {
|
||||
private PermissionService $permissionService;
|
||||
private IInitialStateService $initialState;
|
||||
private ConfigService $configService;
|
||||
private IEventDispatcher $eventDispatcher;
|
||||
private CardMapper $cardMapper;
|
||||
private IURLGenerator $urlGenerator;
|
||||
private CardService $cardService;
|
||||
private IConfig $config;
|
||||
private $permissionService;
|
||||
private $initialState;
|
||||
private $configService;
|
||||
private $eventDispatcher;
|
||||
private $cardMapper;
|
||||
private $urlGenerator;
|
||||
private $cardService;
|
||||
|
||||
public function __construct(
|
||||
string $AppName,
|
||||
$AppName,
|
||||
IRequest $request,
|
||||
PermissionService $permissionService,
|
||||
IInitialStateService $initialStateService,
|
||||
@@ -61,8 +58,7 @@ class PageController extends Controller {
|
||||
IEventDispatcher $eventDispatcher,
|
||||
CardMapper $cardMapper,
|
||||
IURLGenerator $urlGenerator,
|
||||
CardService $cardService,
|
||||
IConfig $config
|
||||
CardService $cardService
|
||||
) {
|
||||
parent::__construct($AppName, $request);
|
||||
|
||||
@@ -73,7 +69,6 @@ class PageController extends Controller {
|
||||
$this->cardMapper = $cardMapper;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->cardService = $cardService;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,17 +84,13 @@ class PageController extends Controller {
|
||||
$this->initialState->provideInitialState(Application::APP_ID, 'config', $this->configService->getAll());
|
||||
|
||||
$this->eventDispatcher->dispatchTyped(new LoadSidebar());
|
||||
$this->eventDispatcher->dispatchTyped(new CollaborationResourcesEvent());
|
||||
if (class_exists(LoadViewer::class)) {
|
||||
$this->eventDispatcher->dispatchTyped(new LoadViewer());
|
||||
}
|
||||
|
||||
$response = new TemplateResponse('deck', 'main', [
|
||||
'id-app-content' => '#app-content-vue',
|
||||
'id-app-navigation' => '#app-navigation-vue',
|
||||
]);
|
||||
$response = new TemplateResponse('deck', 'main');
|
||||
|
||||
if ($this->config->getSystemValueBool('debug', false)) {
|
||||
if (\OC::$server->getConfig()->getSystemValueBool('debug', false)) {
|
||||
$csp = new ContentSecurityPolicy();
|
||||
$csp->addAllowedConnectDomain('*');
|
||||
$csp->addAllowedScriptDomain('*');
|
||||
|
||||
@@ -27,7 +27,6 @@ declare(strict_types=1);
|
||||
namespace OCA\Deck\Controller;
|
||||
|
||||
use OCA\Deck\Db\Card;
|
||||
use OCA\Deck\Model\CardDetails;
|
||||
use OCA\Deck\Service\SearchService;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\AppFramework\OCSController;
|
||||
@@ -51,12 +50,9 @@ class SearchController extends OCSController {
|
||||
public function search(string $term, ?int $limit = null, ?int $cursor = null): DataResponse {
|
||||
$cards = $this->searchService->searchCards($term, $limit, $cursor);
|
||||
return new DataResponse(array_map(function (Card $card) {
|
||||
$board = $card->getRelatedBoard();
|
||||
$json = (new CardDetails($card, $board))->jsonSerialize();
|
||||
|
||||
$json['relatedBoard'] = $board;
|
||||
$json = $card->jsonSerialize();
|
||||
$json['relatedStack'] = $card->getRelatedStack();
|
||||
|
||||
$json['relatedBoard'] = $card->getRelatedBoard();
|
||||
return $json;
|
||||
}, $cards));
|
||||
}
|
||||
|
||||
@@ -59,21 +59,20 @@ class Calendar extends ExternalCalendar {
|
||||
}
|
||||
|
||||
public function getACL() {
|
||||
// the calendar should always have the read and the write-properties permissions
|
||||
// write-properties is needed to allow the user to toggle the visibility of shared deck calendars
|
||||
$acl = [
|
||||
[
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $this->getOwner(),
|
||||
'protected' => true,
|
||||
],
|
||||
[
|
||||
]
|
||||
];
|
||||
if ($this->backend->checkBoardPermission($this->board->getId(), Acl::PERMISSION_MANAGE)) {
|
||||
$acl[] = [
|
||||
'privilege' => '{DAV:}write-properties',
|
||||
'principal' => $this->getOwner(),
|
||||
'protected' => true,
|
||||
]
|
||||
];
|
||||
|
||||
];
|
||||
}
|
||||
return $acl;
|
||||
}
|
||||
|
||||
@@ -188,18 +187,12 @@ class Calendar extends ExternalCalendar {
|
||||
foreach ($properties as $key => $value) {
|
||||
switch ($key) {
|
||||
case '{DAV:}displayname':
|
||||
if (!$this->backend->checkBoardPermission($this->board->getId(), Acl::PERMISSION_MANAGE)) {
|
||||
throw new Forbidden('no permission to change the displayname');
|
||||
}
|
||||
if (mb_strpos($value, 'Deck: ') === 0) {
|
||||
$value = mb_substr($value, strlen('Deck: '));
|
||||
}
|
||||
$this->board->setTitle($value);
|
||||
break;
|
||||
case '{http://apple.com/ns/ical/}calendar-color':
|
||||
if (!$this->backend->checkBoardPermission($this->board->getId(), Acl::PERMISSION_MANAGE)) {
|
||||
throw new Forbidden('no permission to change the calendar color');
|
||||
}
|
||||
$color = substr($value, 1, 6);
|
||||
if (!preg_match('/[a-f0-9]{6}/i', $color)) {
|
||||
throw new InvalidDataException('No valid color provided');
|
||||
|
||||
@@ -26,34 +26,18 @@ declare(strict_types=1);
|
||||
|
||||
namespace OCA\Deck\Dashboard;
|
||||
|
||||
use DateTime;
|
||||
use OCA\Deck\AppInfo\Application;
|
||||
use OCA\Deck\Db\Label;
|
||||
use OCA\Deck\Service\OverviewService;
|
||||
use OCP\Dashboard\IAPIWidget;
|
||||
use OCP\Dashboard\IButtonWidget;
|
||||
use OCP\Dashboard\IIconWidget;
|
||||
use OCP\Dashboard\Model\WidgetButton;
|
||||
use OCP\Dashboard\Model\WidgetItem;
|
||||
use OCP\IDateTimeFormatter;
|
||||
use OCP\Dashboard\IWidget;
|
||||
use OCP\IL10N;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\Util;
|
||||
|
||||
class DeckWidget implements IAPIWidget, IButtonWidget, IIconWidget {
|
||||
private IL10N $l10n;
|
||||
private OverviewService $dashboardService;
|
||||
private IURLGenerator $urlGenerator;
|
||||
private IDateTimeFormatter $dateTimeFormatter;
|
||||
class DeckWidget implements IWidget {
|
||||
|
||||
public function __construct(IL10N $l10n,
|
||||
OverviewService $dashboardService,
|
||||
IDateTimeFormatter $dateTimeFormatter,
|
||||
IURLGenerator $urlGenerator) {
|
||||
/**
|
||||
* @var IL10N
|
||||
*/
|
||||
private $l10n;
|
||||
|
||||
public function __construct(IL10N $l10n) {
|
||||
$this->l10n = $l10n;
|
||||
$this->dashboardService = $dashboardService;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->dateTimeFormatter = $dateTimeFormatter;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,88 +68,17 @@ class DeckWidget implements IAPIWidget, IButtonWidget, IIconWidget {
|
||||
return 'icon-deck';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getIconUrl(): string {
|
||||
return $this->urlGenerator->getAbsoluteURL(
|
||||
$this->urlGenerator->imagePath(Application::APP_ID, 'deck-dark.svg')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getUrl(): ?string {
|
||||
return $this->urlGenerator->getAbsoluteURL(
|
||||
$this->urlGenerator->linkToRoute(Application::APP_ID . '.page.index')
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function load(): void {
|
||||
Util::addScript('deck', 'deck-dashboard');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getItems(string $userId, ?string $since = null, int $limit = 7): array {
|
||||
$upcomingCards = $this->dashboardService->findUpcomingCards($userId);
|
||||
$nowTimestamp = (new Datetime())->getTimestamp();
|
||||
$sinceTimestamp = $since !== null ? (new Datetime($since))->getTimestamp() : null;
|
||||
$upcomingCards = array_filter($upcomingCards, static function (array $card) use ($nowTimestamp, $sinceTimestamp) {
|
||||
if ($card['duedate']) {
|
||||
$ts = (new Datetime($card['duedate']))->getTimestamp();
|
||||
return $ts > $nowTimestamp && ($sinceTimestamp === null || $ts > $sinceTimestamp);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
usort($upcomingCards, static function ($a, $b) {
|
||||
$a = new Datetime($a['duedate']);
|
||||
$ta = $a->getTimestamp();
|
||||
$b = new Datetime($b['duedate']);
|
||||
$tb = $b->getTimestamp();
|
||||
return ($ta > $tb) ? 1 : -1;
|
||||
});
|
||||
$upcomingCards = array_slice($upcomingCards, 0, $limit);
|
||||
$urlGenerator = $this->urlGenerator;
|
||||
$dateTimeFormatter = $this->dateTimeFormatter;
|
||||
return array_map(static function (array $card) use ($urlGenerator, $dateTimeFormatter) {
|
||||
$formattedDueDate = $dateTimeFormatter->formatDateTime(new DateTime($card['duedate']));
|
||||
return new WidgetItem(
|
||||
$card['title'] . ' (' . $formattedDueDate . ')',
|
||||
implode(
|
||||
', ',
|
||||
array_map(static function (Label $label) {
|
||||
return $label->jsonSerialize()['title'];
|
||||
}, $card['labels'])
|
||||
),
|
||||
$urlGenerator->getAbsoluteURL(
|
||||
$urlGenerator->linkToRoute(Application::APP_ID . '.page.redirectToCard', ['cardId' => $card['id']])
|
||||
),
|
||||
$urlGenerator->getAbsoluteURL(
|
||||
$urlGenerator->imagePath(Application::APP_ID, 'deck-dark.svg')
|
||||
),
|
||||
$card['duedate']
|
||||
);
|
||||
}, $upcomingCards);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getWidgetButtons(string $userId): array {
|
||||
return [
|
||||
new WidgetButton(
|
||||
WidgetButton::TYPE_MORE,
|
||||
$this->urlGenerator->getAbsoluteURL(
|
||||
$this->urlGenerator->linkToRoute(Application::APP_ID . '.page.index')
|
||||
),
|
||||
$this->l10n->t('Load more')
|
||||
),
|
||||
];
|
||||
\OCP\Util::addScript('deck', 'deck-dashboard');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,46 +33,18 @@ class AclMapper extends DeckMapper implements IPermissionMapper {
|
||||
parent::__construct($db, 'deck_board_acl', Acl::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param numeric $boardId
|
||||
* @param int|null $limit
|
||||
* @param int|null $offset
|
||||
* @return Acl[]
|
||||
* @throws \OCP\DB\Exception
|
||||
*/
|
||||
public function findAll($boardId, $limit = null, $offset = null) {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('id', 'board_id', 'type', 'participant', 'permission_edit', 'permission_share', 'permission_manage')
|
||||
->from('deck_board_acl')
|
||||
->where($qb->expr()->eq('board_id', $qb->createNamedParameter($boardId, IQueryBuilder::PARAM_INT)))
|
||||
->setMaxResults($limit)
|
||||
->setFirstResult($offset);
|
||||
|
||||
return $this->findEntities($qb);
|
||||
$sql = 'SELECT id, board_id, type, participant, permission_edit, permission_share, permission_manage FROM `*PREFIX*deck_board_acl` WHERE `board_id` = ? ';
|
||||
return $this->findEntities($sql, [$boardId], $limit, $offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param numeric $userId
|
||||
* @param numeric $id
|
||||
* @return bool
|
||||
* @throws \OCP\DB\Exception
|
||||
*/
|
||||
public function isOwner($userId, $id): bool {
|
||||
$aclId = $id;
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('acl.id')
|
||||
->from($this->getTableName(), 'acl')
|
||||
->innerJoin('acl', 'deck_boards', 'b', 'acl.board_id = b.id')
|
||||
->where($qb->expr()->eq('owner', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)))
|
||||
->andWhere($qb->expr()->eq('acl.id', $qb->createNamedParameter($aclId, IQueryBuilder::PARAM_INT)));
|
||||
|
||||
return count($qb->executeQuery()->fetchAll()) > 0;
|
||||
public function isOwner($userId, $aclId): bool {
|
||||
$sql = 'SELECT owner FROM `*PREFIX*deck_boards` WHERE `id` IN (SELECT board_id FROM `*PREFIX*deck_board_acl` WHERE id = ?)';
|
||||
$stmt = $this->execute($sql, [$aclId]);
|
||||
$row = $stmt->fetch();
|
||||
return ($row['owner'] === $userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param numeric $id
|
||||
* @return int|null
|
||||
*/
|
||||
public function findBoardId($id): ?int {
|
||||
try {
|
||||
$entity = $this->find($id);
|
||||
@@ -82,21 +54,9 @@ class AclMapper extends DeckMapper implements IPermissionMapper {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $type
|
||||
* @param string $participant
|
||||
* @return Acl[]
|
||||
* @throws \OCP\DB\Exception
|
||||
*/
|
||||
public function findByParticipant($type, $participant): array {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
|
||||
$qb->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($qb->expr()->eq('type', $qb->createNamedParameter($type, IQueryBuilder::PARAM_INT)))
|
||||
->andWhere($qb->expr()->eq('participant', $qb->createNamedParameter($participant, IQueryBuilder::PARAM_STR)));
|
||||
|
||||
return $this->findEntities($qb);
|
||||
$sql = 'SELECT * from *PREFIX*deck_board_acl WHERE type = ? AND participant = ?';
|
||||
return $this->findEntities($sql, [$type, $participant]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -55,6 +55,9 @@ class AssignmentMapper extends QBMapper implements IPermissionMapper {
|
||||
$this->circleService = $circleService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Assignment[]
|
||||
*/
|
||||
public function findAll(int $cardId): array {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('*')
|
||||
@@ -77,8 +80,8 @@ class AssignmentMapper extends QBMapper implements IPermissionMapper {
|
||||
}
|
||||
|
||||
|
||||
public function isOwner($userId, $id): bool {
|
||||
return $this->cardMapper->isOwner($userId, $id);
|
||||
public function isOwner($userId, $cardId): bool {
|
||||
return $this->cardMapper->isOwner($userId, $cardId);
|
||||
}
|
||||
|
||||
public function findBoardId($id): ?int {
|
||||
|
||||
@@ -30,6 +30,7 @@ use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\IUserManager;
|
||||
use PDO;
|
||||
|
||||
class AttachmentMapper extends DeckMapper implements IPermissionMapper {
|
||||
private $cardMapper;
|
||||
@@ -51,53 +52,70 @@ class AttachmentMapper extends DeckMapper implements IPermissionMapper {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @return Attachment
|
||||
* @throws DoesNotExistException
|
||||
* @throws MultipleObjectsReturnedException
|
||||
* @throws \OCP\DB\Exception
|
||||
* @param $id
|
||||
* @return Entity|Attachment
|
||||
* @throws \OCP\AppFramework\Db\DoesNotExistException
|
||||
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
|
||||
*/
|
||||
public function find($id) {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('*')
|
||||
->from($this->getTableName())
|
||||
->from('deck_attachment')
|
||||
->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
|
||||
|
||||
return $this->findEntity($qb);
|
||||
$cursor = $qb->execute();
|
||||
$row = $cursor->fetch(PDO::FETCH_ASSOC);
|
||||
if ($row === false) {
|
||||
$cursor->closeCursor();
|
||||
throw new DoesNotExistException('Did expect one result but found none when executing query: ' . $qb->getSQL());
|
||||
}
|
||||
|
||||
$row2 = $cursor->fetch();
|
||||
$cursor->closeCursor();
|
||||
if ($row2 !== false) {
|
||||
throw new MultipleObjectsReturnedException('Did not expect more than one result when executing query: ' . $qb->getSQL());
|
||||
}
|
||||
|
||||
return $this->mapRowToEntity($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $cardId
|
||||
* @param string $data
|
||||
* @return Attachment
|
||||
* @throws DoesNotExistException
|
||||
* @throws MultipleObjectsReturnedException
|
||||
* @throws \OCP\DB\Exception
|
||||
*/
|
||||
public function findByData($cardId, $data) {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('*')
|
||||
->from($this->getTableName())
|
||||
->from('deck_attachment')
|
||||
->where($qb->expr()->eq('card_id', $qb->createNamedParameter($cardId, IQueryBuilder::PARAM_INT)))
|
||||
->andWhere($qb->expr()->eq('data', $qb->createNamedParameter($data, IQueryBuilder::PARAM_STR)));
|
||||
|
||||
return $this->findEntity($qb);
|
||||
$cursor = $qb->execute();
|
||||
$row = $cursor->fetch(PDO::FETCH_ASSOC);
|
||||
if ($row === false) {
|
||||
$cursor->closeCursor();
|
||||
throw new DoesNotExistException('Did expect one result but found none when executing query: ' . $qb->getSQL());
|
||||
}
|
||||
$cursor->closeCursor();
|
||||
return $this->mapRowToEntity($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all attachments for a card
|
||||
*
|
||||
* @param $cardId
|
||||
* @return Entity[]
|
||||
* @throws \OCP\DB\Exception
|
||||
* @return array
|
||||
*/
|
||||
public function findAll($cardId) {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('*')
|
||||
->from($this->getTableName())
|
||||
->from('deck_attachment')
|
||||
->where($qb->expr()->eq('card_id', $qb->createNamedParameter($cardId, IQueryBuilder::PARAM_INT)))
|
||||
->andWhere($qb->expr()->eq('deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
|
||||
|
||||
|
||||
return $this->findEntities($qb);
|
||||
$entities = [];
|
||||
$cursor = $qb->execute();
|
||||
while ($row = $cursor->fetch()) {
|
||||
$entities[] = $this->mapRowToEntity($row);
|
||||
}
|
||||
$cursor->closeCursor();
|
||||
return $entities;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,7 +128,7 @@ class AttachmentMapper extends DeckMapper implements IPermissionMapper {
|
||||
$timeLimit = time() - (60 * 5);
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('*')
|
||||
->from($this->getTableName())
|
||||
->from('deck_attachment')
|
||||
->where($qb->expr()->gt('deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
|
||||
if ($withOffset) {
|
||||
$qb
|
||||
@@ -121,7 +139,13 @@ class AttachmentMapper extends DeckMapper implements IPermissionMapper {
|
||||
->andWhere($qb->expr()->eq('card_id', $qb->createNamedParameter($cardId, IQueryBuilder::PARAM_INT)));
|
||||
}
|
||||
|
||||
return $this->findEntities($qb);
|
||||
$entities = [];
|
||||
$cursor = $qb->execute();
|
||||
while ($row = $cursor->fetch()) {
|
||||
$entities[] = $this->mapRowToEntity($row);
|
||||
}
|
||||
$cursor->closeCursor();
|
||||
return $entities;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -23,14 +23,6 @@
|
||||
|
||||
namespace OCA\Deck\Db;
|
||||
|
||||
/**
|
||||
* @method int getId()
|
||||
* @method string getTitle()
|
||||
* @method int getShared()
|
||||
* @method bool getArchived()
|
||||
* @method int getDeletedAt()
|
||||
* @method int getLastModified()
|
||||
*/
|
||||
class Board extends RelationalEntity {
|
||||
protected $title;
|
||||
protected $owner;
|
||||
|
||||
@@ -42,9 +42,9 @@ class BoardMapper extends QBMapper implements IPermissionMapper {
|
||||
private $circlesService;
|
||||
private $logger;
|
||||
|
||||
/** @var CappedMemoryCache<Board[]> */
|
||||
/** @var CappedMemoryCache */
|
||||
private $userBoardCache;
|
||||
/** @var CappedMemoryCache<Board> */
|
||||
/** @var CappedMemoryCache */
|
||||
private $boardCache;
|
||||
|
||||
public function __construct(
|
||||
@@ -107,47 +107,6 @@ class BoardMapper extends QBMapper implements IPermissionMapper {
|
||||
return $this->boardCache[$id];
|
||||
}
|
||||
|
||||
public function findBoardIds(string $userId): array {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->selectDistinct('b.id')
|
||||
->from($this->getTableName(), 'b')
|
||||
->leftJoin('b', 'deck_board_acl', 'acl', $qb->expr()->eq('b.id', 'acl.board_id'));
|
||||
|
||||
// Owned by the user
|
||||
$qb->where($qb->expr()->andX(
|
||||
$qb->expr()->eq('owner', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)),
|
||||
));
|
||||
|
||||
// Shared to the user
|
||||
$qb->orWhere($qb->expr()->andX(
|
||||
$qb->expr()->eq('acl.participant', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)),
|
||||
$qb->expr()->eq('acl.type', $qb->createNamedParameter(Acl::PERMISSION_TYPE_USER, IQueryBuilder::PARAM_INT)),
|
||||
));
|
||||
|
||||
// Shared to user groups of the user
|
||||
$groupIds = $this->groupManager->getUserGroupIds($this->userManager->get($userId));
|
||||
if (count($groupIds) !== 0) {
|
||||
$qb->orWhere($qb->expr()->andX(
|
||||
$qb->expr()->in('acl.participant', $qb->createNamedParameter($groupIds, IQueryBuilder::PARAM_STR_ARRAY)),
|
||||
$qb->expr()->eq('acl.type', $qb->createNamedParameter(Acl::PERMISSION_TYPE_GROUP, IQueryBuilder::PARAM_INT)),
|
||||
));
|
||||
}
|
||||
|
||||
// Shared to circles of the user
|
||||
$circles = $this->circlesService->getUserCircles($userId);
|
||||
if (count($circles) !== 0) {
|
||||
$qb->orWhere($qb->expr()->andX(
|
||||
$qb->expr()->in('acl.participant', $qb->createNamedParameter($circles, IQueryBuilder::PARAM_STR_ARRAY)),
|
||||
$qb->expr()->eq('acl.type', $qb->createNamedParameter(Acl::PERMISSION_TYPE_CIRCLE, IQueryBuilder::PARAM_INT)),
|
||||
));
|
||||
}
|
||||
|
||||
$result = $qb->executeQuery();
|
||||
return array_map(function (string $id) {
|
||||
return (int)$id;
|
||||
}, $result->fetchAll(\PDO::FETCH_COLUMN));
|
||||
}
|
||||
|
||||
public function findAllForUser(string $userId, ?int $since = null, bool $includeArchived = true, ?int $before = null,
|
||||
?string $term = null): array {
|
||||
$useCache = ($since === -1 && $includeArchived === true && $before === null && $term === null);
|
||||
@@ -172,9 +131,14 @@ class BoardMapper extends QBMapper implements IPermissionMapper {
|
||||
|
||||
/**
|
||||
* Find all boards for a given user
|
||||
*
|
||||
* @param $userId
|
||||
* @param null $limit
|
||||
* @param null $offset
|
||||
* @return array
|
||||
*/
|
||||
public function findAllByUser(string $userId, ?int $limit = null, ?int $offset = null, ?int $since = null,
|
||||
bool $includeArchived = true, ?int $before = null, ?string $term = null): array {
|
||||
bool $includeArchived = true, ?int $before = null, ?string $term = null) {
|
||||
// FIXME this used to be a UNION to get boards owned by $userId and the user shares in one single query
|
||||
// Is it possible with the query builder?
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
@@ -283,9 +247,15 @@ class BoardMapper extends QBMapper implements IPermissionMapper {
|
||||
|
||||
/**
|
||||
* Find all boards for a given user
|
||||
*
|
||||
* @param $userId
|
||||
* @param $groups
|
||||
* @param null $limit
|
||||
* @param null $offset
|
||||
* @return array
|
||||
*/
|
||||
public function findAllByGroups(string $userId, array $groups, ?int $limit = null, ?int $offset = null, ?int $since = null,
|
||||
bool $includeArchived = true, ?int $before = null, ?string $term = null): array {
|
||||
bool $includeArchived = true, ?int $before = null, ?string $term = null) {
|
||||
if (count($groups) <= 0) {
|
||||
return [];
|
||||
}
|
||||
@@ -444,8 +414,8 @@ class BoardMapper extends QBMapper implements IPermissionMapper {
|
||||
return parent::delete($entity);
|
||||
}
|
||||
|
||||
public function isOwner($userId, $id): bool {
|
||||
$board = $this->find($id);
|
||||
public function isOwner($userId, $boardId): bool {
|
||||
$board = $this->find($boardId);
|
||||
return ($board->getOwner() === $userId);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,44 +27,6 @@ use DateTime;
|
||||
use DateTimeZone;
|
||||
use Sabre\VObject\Component\VCalendar;
|
||||
|
||||
/**
|
||||
* @method string getTitle()
|
||||
* @method string getDescription()
|
||||
* @method string getDescriptionPrev()
|
||||
* @method int getStackId()
|
||||
* @method int getOrder()
|
||||
* @method int getLastModified()
|
||||
* @method int getCreatedAt()
|
||||
* @method bool getArchived()
|
||||
* @method bool getNotified()
|
||||
*
|
||||
* @method void setLabels(Label[] $labels)
|
||||
* @method null|Label[] getLabels()
|
||||
*
|
||||
* @method void setAssignedUsers(Assignment[] $users)
|
||||
* @method null|User[] getAssignedUsers()
|
||||
*
|
||||
* @method void setAttachments(Attachment[] $attachments)
|
||||
* @method null|Attachment[] getAttachments()
|
||||
*
|
||||
* @method void setAttachmentCount(int $count)
|
||||
* @method null|int getAttachmentCount()
|
||||
*
|
||||
* @method void setCommentsUnread(int $count)
|
||||
* @method null|int getCommentsUnread()
|
||||
*
|
||||
* @method void setCommentsCount(int $count)
|
||||
* @method null|int getCommentsCount()
|
||||
*
|
||||
* @method void setOwner(string $user)
|
||||
* @method null|string getOwner()
|
||||
*
|
||||
* @method void setRelatedStack(Stack $stack)
|
||||
* @method null|Stack getRelatedStack()
|
||||
*
|
||||
* @method void setRelatedBoard(Board $board)
|
||||
* @method null|Board getRelatedBoard()
|
||||
*/
|
||||
class Card extends RelationalEntity {
|
||||
public const TITLE_MAX_LENGTH = 255;
|
||||
|
||||
@@ -108,7 +70,6 @@ class Card extends RelationalEntity {
|
||||
$this->addType('archived', 'boolean');
|
||||
$this->addType('notified', 'boolean');
|
||||
$this->addType('deletedAt', 'integer');
|
||||
$this->addType('duedate', 'datetime');
|
||||
$this->addRelation('labels');
|
||||
$this->addRelation('assignedUsers');
|
||||
$this->addRelation('attachments');
|
||||
@@ -126,6 +87,50 @@ class Card extends RelationalEntity {
|
||||
$this->databaseType = $type;
|
||||
}
|
||||
|
||||
public function getDuedate($isoFormat = false) {
|
||||
if ($this->duedate === null) {
|
||||
return null;
|
||||
}
|
||||
$dt = new DateTime($this->duedate);
|
||||
if (!$isoFormat && $this->databaseType === 'mysql') {
|
||||
return $dt->format('Y-m-d H:i:s');
|
||||
}
|
||||
return $dt->format('c');
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array {
|
||||
$json = parent::jsonSerialize();
|
||||
$json['overdue'] = self::DUEDATE_FUTURE;
|
||||
$due = $this->duedate ? strtotime($this->duedate) : false;
|
||||
if ($due !== false) {
|
||||
$today = new DateTime();
|
||||
$today->setTime(0, 0);
|
||||
|
||||
$match_date = new DateTime($this->duedate);
|
||||
|
||||
$match_date->setTime(0, 0);
|
||||
|
||||
$diff = $today->diff($match_date);
|
||||
$diffDays = (integer) $diff->format('%R%a'); // Extract days count in interval
|
||||
|
||||
if ($diffDays === 1) {
|
||||
$json['overdue'] = self::DUEDATE_NEXT;
|
||||
}
|
||||
if ($diffDays === 0) {
|
||||
$json['overdue'] = self::DUEDATE_NOW;
|
||||
}
|
||||
if ($diffDays < 0) {
|
||||
$json['overdue'] = self::DUEDATE_OVERDUE;
|
||||
}
|
||||
}
|
||||
$json['duedate'] = $this->getDuedate(true);
|
||||
unset($json['notified']);
|
||||
unset($json['descriptionPrev']);
|
||||
unset($json['relatedStack']);
|
||||
unset($json['relatedBoard']);
|
||||
return $json;
|
||||
}
|
||||
|
||||
public function getCalendarObject(): VCalendar {
|
||||
$calendar = new VCalendar();
|
||||
$event = $calendar->createComponent('VTODO');
|
||||
@@ -134,7 +139,7 @@ class Card extends RelationalEntity {
|
||||
$creationDate = new DateTime();
|
||||
$creationDate->setTimestamp($this->createdAt);
|
||||
$event->DTSTAMP = $creationDate;
|
||||
$event->DUE = new DateTime($this->getDuedate()->format('c'), new DateTimeZone('UTC'));
|
||||
$event->DUE = new DateTime($this->getDuedate(true), new DateTimeZone('UTC'));
|
||||
}
|
||||
$event->add('RELATED-TO', 'deck-stack-' . $this->getStackId());
|
||||
|
||||
|
||||
@@ -238,21 +238,6 @@ class CardMapper extends QBMapper implements IPermissionMapper {
|
||||
return $this->findEntities($qb);
|
||||
}
|
||||
|
||||
public function findAllByBoardId(int $boardId, ?int $limit = null, ?int $offset = null): array {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('c.*')
|
||||
->from('deck_cards', 'c')
|
||||
->innerJoin('c', 'deck_stacks', 's', 's.id = c.stack_id')
|
||||
->innerJoin('s', 'deck_boards', 'b', 'b.id = s.board_id')
|
||||
->where($qb->expr()->eq('board_id', $qb->createNamedParameter($boardId, IQueryBuilder::PARAM_INT)))
|
||||
->andWhere($qb->expr()->eq('archived', $qb->createNamedParameter(false, IQueryBuilder::PARAM_BOOL)))
|
||||
->setMaxResults($limit)
|
||||
->setFirstResult($offset)
|
||||
->orderBy('c.lastmodified')
|
||||
->addOrderBy('c.id');
|
||||
return $this->findEntities($qb);
|
||||
}
|
||||
|
||||
public function findAllWithDue($boardId) {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('c.*')
|
||||
@@ -575,10 +560,10 @@ class CardMapper extends QBMapper implements IPermissionMapper {
|
||||
$qb->execute();
|
||||
}
|
||||
|
||||
public function isOwner($userId, $id): bool {
|
||||
public function isOwner($userId, $cardId): bool {
|
||||
$sql = 'SELECT owner FROM `*PREFIX*deck_boards` WHERE `id` IN (SELECT board_id FROM `*PREFIX*deck_stacks` WHERE id IN (SELECT stack_id FROM `*PREFIX*deck_cards` WHERE id = ?))';
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$stmt->bindParam(1, $id, \PDO::PARAM_INT, 0);
|
||||
$stmt->bindParam(1, $cardId, \PDO::PARAM_INT, 0);
|
||||
$stmt->execute();
|
||||
$row = $stmt->fetch();
|
||||
return ($row['owner'] === $userId);
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
namespace OCA\Deck\Db;
|
||||
|
||||
use OCP\ICacheFactory;
|
||||
use OCP\ICache;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\IRequest;
|
||||
|
||||
@@ -32,16 +31,13 @@ class ChangeHelper {
|
||||
public const TYPE_BOARD = 'boardChanged';
|
||||
public const TYPE_CARD = 'cardChanged';
|
||||
|
||||
private IDBConnection $db;
|
||||
private ICache $cache;
|
||||
private IRequest $request;
|
||||
private ?string $userId;
|
||||
private $db;
|
||||
|
||||
public function __construct(
|
||||
IDBConnection $db,
|
||||
ICacheFactory $cacheFactory,
|
||||
IRequest $request,
|
||||
?string $userId
|
||||
$userId
|
||||
) {
|
||||
$this->db = $db;
|
||||
$this->cache = $cacheFactory->createDistributed('deck_changes');
|
||||
|
||||
@@ -23,15 +23,17 @@
|
||||
|
||||
namespace OCA\Deck\Db;
|
||||
|
||||
use OCP\AppFramework\Db\QBMapper;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\AppFramework\Db\Mapper;
|
||||
|
||||
/**
|
||||
* Class DeckMapper
|
||||
*
|
||||
* @package OCA\Deck\Db
|
||||
* @deprecated use QBMapper
|
||||
*
|
||||
* TODO: Move to QBMapper once Nextcloud 14 is a minimum requirement
|
||||
*/
|
||||
class DeckMapper extends QBMapper {
|
||||
class DeckMapper extends Mapper {
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
@@ -40,11 +42,11 @@ class DeckMapper extends QBMapper {
|
||||
* @throws \OCP\AppFramework\Db\DoesNotExistException
|
||||
*/
|
||||
public function find($id) {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
|
||||
$sql = 'SELECT * FROM `' . $this->tableName . '` ' . 'WHERE `id` = ?';
|
||||
return $this->findEntity($sql, [$id]);
|
||||
}
|
||||
|
||||
return $this->findEntity($qb);
|
||||
protected function execute($sql, array $params = [], $limit = null, $offset = null) {
|
||||
return parent::execute($sql, $params, $limit, $offset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ namespace OCA\Deck\Db;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Db\Entity;
|
||||
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\IDBConnection;
|
||||
|
||||
class LabelMapper extends DeckMapper implements IPermissionMapper {
|
||||
@@ -34,105 +33,41 @@ class LabelMapper extends DeckMapper implements IPermissionMapper {
|
||||
parent::__construct($db, 'deck_labels', Label::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param numeric $boardId
|
||||
* @param int|null $limit
|
||||
* @param int|null $offset
|
||||
* @return Label[]
|
||||
* @throws \OCP\DB\Exception
|
||||
*/
|
||||
public function findAll($boardId, $limit = null, $offset = null): array {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($qb->expr()->eq('board_id', $qb->createNamedParameter($boardId, IQueryBuilder::PARAM_INT)))
|
||||
->setMaxResults($limit)
|
||||
->setFirstResult($offset);
|
||||
return $this->findEntities($qb);
|
||||
public function findAll($boardId, $limit = null, $offset = null) {
|
||||
$sql = 'SELECT * FROM `*PREFIX*deck_labels` WHERE `board_id` = ? ORDER BY `id`';
|
||||
return $this->findEntities($sql, [$boardId], $limit, $offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Entity $entity
|
||||
* @return Entity
|
||||
* @throws \OCP\DB\Exception
|
||||
*/
|
||||
public function delete(Entity $entity): Entity {
|
||||
public function delete(\OCP\AppFramework\Db\Entity $entity) {
|
||||
// delete assigned labels
|
||||
$this->deleteLabelAssignments($entity->getId());
|
||||
// delete label
|
||||
return parent::delete($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param numeric $cardId
|
||||
* @param int|null $limit
|
||||
* @param int|null $offset
|
||||
* @return Label[]
|
||||
* @throws \OCP\DB\Exception
|
||||
*/
|
||||
public function findAssignedLabelsForCard($cardId, $limit = null, $offset = null): array {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('l.*', 'card_id')
|
||||
->from($this->getTableName(), 'l')
|
||||
->innerJoin('l', 'deck_assigned_labels', 'al', 'l.id = al.label_id')
|
||||
->where($qb->expr()->eq('card_id', $qb->createNamedParameter($cardId, IQueryBuilder::PARAM_INT)))
|
||||
->orderBy('l.id')
|
||||
->setMaxResults($limit)
|
||||
->setFirstResult($offset);
|
||||
|
||||
return $this->findEntities($qb);
|
||||
public function findAssignedLabelsForCard($cardId, $limit = null, $offset = null) {
|
||||
$sql = 'SELECT l.*,card_id FROM `*PREFIX*deck_assigned_labels` as al INNER JOIN *PREFIX*deck_labels as l ON l.id = al.label_id WHERE `card_id` = ? ORDER BY l.id';
|
||||
return $this->findEntities($sql, [$cardId], $limit, $offset);
|
||||
}
|
||||
public function findAssignedLabelsForBoard($boardId, $limit = null, $offset = null) {
|
||||
$sql = 'SELECT c.id as card_id, l.id as id, l.title as title, l.color as color FROM `*PREFIX*deck_cards` as c ' .
|
||||
' INNER JOIN `*PREFIX*deck_assigned_labels` as al ON al.card_id = c.id INNER JOIN `*PREFIX*deck_labels` as l ON al.label_id = l.id WHERE board_id=? ORDER BY l.id';
|
||||
return $this->findEntities($sql, [$boardId], $limit, $offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param numeric $boardId
|
||||
* @param int|null $limit
|
||||
* @param int|null $offset
|
||||
* @return Label[]
|
||||
* @throws \OCP\DB\Exception
|
||||
*/
|
||||
public function findAssignedLabelsForBoard($boardId, $limit = null, $offset = null): array {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('l.id as id', 'l.title as title', 'l.color as color')
|
||||
->selectAlias('c.id', 'card_id')
|
||||
->from($this->getTableName(), 'l')
|
||||
->innerJoin('l', 'deck_assigned_labels', 'al', 'al.label_id = l.id')
|
||||
->innerJoin('l', 'deck_cards', 'c', 'al.card_id = c.id')
|
||||
->where($qb->expr()->eq('board_id', $qb->createNamedParameter($boardId, IQueryBuilder::PARAM_INT)))
|
||||
->orderBy('l.id')
|
||||
->setMaxResults($limit)
|
||||
->setFirstResult($offset);
|
||||
|
||||
return $this->findEntities($qb);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Entity $entity
|
||||
* @return Entity
|
||||
* @throws \OCP\DB\Exception
|
||||
*/
|
||||
public function insert(Entity $entity): Entity {
|
||||
public function insert(Entity $entity) {
|
||||
$entity->setLastModified(time());
|
||||
return parent::insert($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Entity $entity
|
||||
* @param bool $updateModified
|
||||
* @return Entity
|
||||
* @throws \OCP\DB\Exception
|
||||
*/
|
||||
public function update(Entity $entity, $updateModified = true): Entity {
|
||||
public function update(Entity $entity, $updateModified = true) {
|
||||
if ($updateModified) {
|
||||
$entity->setLastModified(time());
|
||||
}
|
||||
return parent::update($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param numeric $boardId
|
||||
* @return array
|
||||
* @throws \OCP\DB\Exception
|
||||
*/
|
||||
|
||||
public function getAssignedLabelsForBoard($boardId) {
|
||||
$labels = $this->findAssignedLabelsForBoard($boardId);
|
||||
$result = [];
|
||||
@@ -145,51 +80,27 @@ class LabelMapper extends DeckMapper implements IPermissionMapper {
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param numeric $labelId
|
||||
* @return void
|
||||
* @throws \OCP\DB\Exception
|
||||
*/
|
||||
public function deleteLabelAssignments($labelId) {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->delete('deck_assigned_labels')
|
||||
->where($qb->expr()->eq('label_id', $qb->createNamedParameter($labelId, IQueryBuilder::PARAM_INT)));
|
||||
$qb->executeStatement();
|
||||
$sql = 'DELETE FROM `*PREFIX*deck_assigned_labels` WHERE label_id = ?';
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$stmt->bindParam(1, $labelId, \PDO::PARAM_INT, 0);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param numeric $cardId
|
||||
* @return void
|
||||
* @throws \OCP\DB\Exception
|
||||
*/
|
||||
public function deleteLabelAssignmentsForCard($cardId) {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->delete('deck_assigned_labels')
|
||||
->where($qb->expr()->eq('card_id', $qb->createNamedParameter($cardId, IQueryBuilder::PARAM_INT)));
|
||||
$qb->executeStatement();
|
||||
$sql = 'DELETE FROM `*PREFIX*deck_assigned_labels` WHERE card_id = ?';
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$stmt->bindParam(1, $cardId, \PDO::PARAM_INT, 0);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $userId
|
||||
* @param numeric $labelId
|
||||
* @return bool
|
||||
* @throws \OCP\DB\Exception
|
||||
*/
|
||||
public function isOwner($userId, $labelId): bool {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('l.id')
|
||||
->from($this->getTableName(), 'l')
|
||||
->innerJoin('l', 'deck_boards', 'b', 'l.board_id = b.id')
|
||||
->where($qb->expr()->eq('l.id', $qb->createNamedParameter($labelId, IQueryBuilder::PARAM_INT)))
|
||||
->andWhere($qb->expr()->eq('b.owner', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
|
||||
|
||||
return count($qb->executeQuery()->fetchAll()) > 0;
|
||||
$sql = 'SELECT owner FROM `*PREFIX*deck_boards` WHERE `id` IN (SELECT board_id FROM `*PREFIX*deck_labels` WHERE id = ?)';
|
||||
$stmt = $this->execute($sql, [$labelId]);
|
||||
$row = $stmt->fetch();
|
||||
return ($row['owner'] === $userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param numeric $id
|
||||
* @return int|null
|
||||
*/
|
||||
public function findBoardId($id): ?int {
|
||||
try {
|
||||
$entity = $this->find($id);
|
||||
|
||||
@@ -72,9 +72,6 @@ class RelationalEntity extends Entity implements \JsonSerializable {
|
||||
$propertyReflection = $reflection->getProperty($property);
|
||||
if (!$propertyReflection->isPrivate() && !in_array($property, $this->_resolvedProperties, true)) {
|
||||
$json[$property] = $this->getter($property);
|
||||
if ($json[$property] instanceof \DateTimeInterface) {
|
||||
$json[$property] = $json[$property]->format('c');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ namespace OCA\Deck\Db;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Db\Entity;
|
||||
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\IDBConnection;
|
||||
|
||||
class StackMapper extends DeckMapper implements IPermissionMapper {
|
||||
@@ -39,112 +38,62 @@ class StackMapper extends DeckMapper implements IPermissionMapper {
|
||||
|
||||
|
||||
/**
|
||||
* @param numeric $id
|
||||
* @return Stack
|
||||
* @throws DoesNotExistException
|
||||
* @param $id
|
||||
* @throws MultipleObjectsReturnedException
|
||||
* @throws \OCP\DB\Exception
|
||||
* @throws DoesNotExistException
|
||||
*/
|
||||
public function find($id): Stack {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
|
||||
|
||||
return $this->findEntity($qb);
|
||||
$sql = 'SELECT * FROM `*PREFIX*deck_stacks` ' .
|
||||
'WHERE `id` = ?';
|
||||
return $this->findEntity($sql, [$id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $cardId
|
||||
* @return Stack|null
|
||||
* @throws \OCP\DB\Exception
|
||||
*/
|
||||
public function findStackFromCardId($cardId): ?Stack {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('s.*')
|
||||
->from($this->getTableName(), 's')
|
||||
->innerJoin('s', 'deck_cards', 'c', 's.id = c.stack_id')
|
||||
->where($qb->expr()->eq('c.id', $qb->createNamedParameter($cardId, IQueryBuilder::PARAM_INT)));
|
||||
|
||||
$sql = <<<SQL
|
||||
SELECT s.*
|
||||
FROM `*PREFIX*deck_stacks` as `s`
|
||||
INNER JOIN `*PREFIX*deck_cards` as `c` ON s.id = c.stack_id
|
||||
WHERE c.id = ?
|
||||
SQL;
|
||||
try {
|
||||
return $this->findEntity($qb);
|
||||
return $this->findEntity($sql, [$cardId]);
|
||||
} catch (MultipleObjectsReturnedException|DoesNotExistException $e) {
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param numeric $boardId
|
||||
* @param int|null $limit
|
||||
* @param int|null $offset
|
||||
* @return Stack[]
|
||||
* @throws \OCP\DB\Exception
|
||||
*/
|
||||
public function findAll($boardId, $limit = null, $offset = null): array {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($qb->expr()->eq('board_id', $qb->createNamedParameter($boardId, IQueryBuilder::PARAM_INT)))
|
||||
->andWhere($qb->expr()->eq('deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)))
|
||||
->setFirstResult($offset)
|
||||
->setMaxResults($limit);
|
||||
|
||||
return $this->findEntities($qb);
|
||||
public function findAll($boardId, $limit = null, $offset = null) {
|
||||
$sql = 'SELECT * FROM `*PREFIX*deck_stacks` WHERE `board_id` = ? AND deleted_at = 0 ORDER BY `order`, `id`';
|
||||
return $this->findEntities($sql, [$boardId], $limit, $offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param numeric $boardId
|
||||
* @param int|null $limit
|
||||
* @param int|null $offset
|
||||
* @return Stack[]
|
||||
* @throws \OCP\DB\Exception
|
||||
*/
|
||||
|
||||
public function findDeleted($boardId, $limit = null, $offset = null) {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($qb->expr()->eq('board_id', $qb->createNamedParameter($boardId, IQueryBuilder::PARAM_INT)))
|
||||
->andWhere($qb->expr()->neq('deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)))
|
||||
->setFirstResult($offset)
|
||||
->setMaxResults($limit);
|
||||
|
||||
return $this->findEntities($qb);
|
||||
$sql = 'SELECT * FROM `*PREFIX*deck_stacks` s
|
||||
WHERE `s`.`board_id` = ? AND NOT s.deleted_at = 0';
|
||||
return $this->findEntities($sql, [$boardId], $limit, $offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Entity $entity
|
||||
* @return Entity
|
||||
* @throws \OCP\DB\Exception
|
||||
*/
|
||||
public function delete(Entity $entity): Entity {
|
||||
|
||||
public function delete(Entity $entity) {
|
||||
// delete cards on stack
|
||||
$this->cardMapper->deleteByStack($entity->getId());
|
||||
return parent::delete($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param numeric $userId
|
||||
* @param numeric $stackId
|
||||
* @return bool
|
||||
* @throws \OCP\DB\Exception
|
||||
*/
|
||||
public function isOwner($userId, $id): bool {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('s.id')
|
||||
->from($this->getTableName(), 's')
|
||||
->innerJoin('s', 'deck_boards', 'b', 'b.id = s.board_id')
|
||||
->where($qb->expr()->eq('s.id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)))
|
||||
->andWhere($qb->expr()->eq('owner', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
|
||||
|
||||
return count($qb->executeQuery()->fetchAll()) > 0;
|
||||
public function isOwner($userId, $stackId): bool {
|
||||
$sql = 'SELECT owner FROM `*PREFIX*deck_boards` WHERE `id` IN (SELECT board_id FROM `*PREFIX*deck_stacks` WHERE id = ?)';
|
||||
$stmt = $this->execute($sql, [$stackId]);
|
||||
$row = $stmt->fetch();
|
||||
return ($row['owner'] === $userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param numeric $id
|
||||
* @return int|null
|
||||
* @throws \OCP\DB\Exception
|
||||
*/
|
||||
public function findBoardId($id): ?int {
|
||||
try {
|
||||
$entity = $this->find($id);
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2022 Raul Ferreira Fuentes <raul@nextcloud.com>
|
||||
*
|
||||
* @author Raul Ferreira Fuentes <raul@nextcloud.com>
|
||||
*
|
||||
* @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/>.
|
||||
*
|
||||
*/
|
||||
namespace OCA\Deck\Model;
|
||||
|
||||
use DateTime;
|
||||
use OCA\Deck\Db\Board;
|
||||
use OCA\Deck\Db\Card;
|
||||
|
||||
class CardDetails extends Card {
|
||||
private Card $card;
|
||||
private ?Board $board;
|
||||
|
||||
public function __construct(Card $card, ?Board $board = null) {
|
||||
parent::__construct();
|
||||
$this->card = $card;
|
||||
$this->board = $board;
|
||||
}
|
||||
|
||||
public function setBoard(?Board $board): void {
|
||||
$this->board = $board;
|
||||
}
|
||||
|
||||
public function jsonSerialize(array $extras = []): array {
|
||||
$array = $this->card->jsonSerialize();
|
||||
unset($array['notified'], $array['descriptionPrev'], $array['relatedStack'], $array['relatedBoard']);
|
||||
|
||||
$array['overdue'] = $this->getDueStatus();
|
||||
$this->appendBoardDetails($array);
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
private function getDueStatus(): int {
|
||||
$today = new DateTime();
|
||||
$today->setTime(0, 0);
|
||||
|
||||
$match_date = $this->card->getDuedate();
|
||||
if (!$match_date) {
|
||||
return Card::DUEDATE_FUTURE;
|
||||
}
|
||||
$match_date->setTime(0, 0);
|
||||
|
||||
$diff = $today->diff($match_date);
|
||||
$diffDays = (int) $diff->format('%R%a'); // Extract days count in interval
|
||||
|
||||
|
||||
if ($diffDays === 1) {
|
||||
return Card::DUEDATE_NEXT;
|
||||
}
|
||||
if ($diffDays === 0) {
|
||||
return Card::DUEDATE_NOW;
|
||||
}
|
||||
if ($diffDays < 0) {
|
||||
return Card::DUEDATE_OVERDUE;
|
||||
}
|
||||
|
||||
return Card::DUEDATE_FUTURE;
|
||||
}
|
||||
|
||||
private function appendBoardDetails(&$array): void {
|
||||
if (!$this->board) {
|
||||
return;
|
||||
}
|
||||
|
||||
$array['boardId'] = $this->board->id;
|
||||
$array['board'] = (new BoardSummary($this->board))->jsonSerialize();
|
||||
}
|
||||
|
||||
public function __call($name, $arguments) {
|
||||
return $this->card->__call($name, $arguments);
|
||||
}
|
||||
}
|
||||
@@ -129,7 +129,7 @@ class NotificationHelper {
|
||||
->setSubject('card-overdue', [
|
||||
$card->getTitle(), $board->getTitle()
|
||||
])
|
||||
->setDateTime($card->getDuedate());
|
||||
->setDateTime(new DateTime($card->getDuedate()));
|
||||
$this->notificationManager->notify($notification);
|
||||
}
|
||||
}
|
||||
@@ -242,7 +242,7 @@ class NotificationHelper {
|
||||
}
|
||||
return $this->boards[$boardId];
|
||||
}
|
||||
|
||||
|
||||
private function generateBoardShared(Board $board, string $userId): INotification {
|
||||
$notification = $this->notificationManager->createNotification();
|
||||
$notification
|
||||
|
||||
@@ -114,7 +114,7 @@ class Notifier implements INotifier {
|
||||
$dn = $params[2];
|
||||
}
|
||||
$notification->setParsedSubject(
|
||||
$l->t('The card "%s" on "%s" has been assigned to you by %s.', [$params[0], $params[1], $dn])
|
||||
(string) $l->t('The card "%s" on "%s" has been assigned to you by %s.', [$params[0], $params[1], $dn])
|
||||
);
|
||||
$notification->setRichSubject(
|
||||
$l->t('{user} has assigned the card {deck-card} on {deck-board} to you.'),
|
||||
@@ -151,7 +151,7 @@ class Notifier implements INotifier {
|
||||
}
|
||||
|
||||
$notification->setParsedSubject(
|
||||
$l->t('The card "%s" on "%s" has reached its due date.', $params)
|
||||
(string) $l->t('The card "%s" on "%s" has reached its due date.', $params)
|
||||
);
|
||||
$notification->setRichSubject(
|
||||
$l->t('The card {deck-card} on {deck-board} has reached its due date.'),
|
||||
@@ -189,7 +189,7 @@ class Notifier implements INotifier {
|
||||
$dn = $params[2];
|
||||
}
|
||||
$notification->setParsedSubject(
|
||||
$l->t('%s has mentioned you in a comment on "%s".', [$dn, $params[0]])
|
||||
(string) $l->t('%s has mentioned you in a comment on "%s".', [$dn, $params[0]])
|
||||
);
|
||||
$notification->setRichSubject(
|
||||
$l->t('{user} has mentioned you in a comment on {deck-card}.'),
|
||||
@@ -226,7 +226,7 @@ class Notifier implements INotifier {
|
||||
$dn = $params[1];
|
||||
}
|
||||
$notification->setParsedSubject(
|
||||
$l->t('The board "%s" has been shared with you by %s.', [$params[0], $dn])
|
||||
(string) $l->t('The board "%s" has been shared with you by %s.', [$params[0], $dn])
|
||||
);
|
||||
$notification->setRichSubject(
|
||||
$l->t('{user} has shared {deck-board} with you.'),
|
||||
|
||||
@@ -54,11 +54,22 @@ use OCP\IURLGenerator;
|
||||
class DeckProvider implements IFullTextSearchProvider {
|
||||
public const DECK_PROVIDER_ID = 'deck';
|
||||
|
||||
private IL10N $l10n;
|
||||
private IUrlGenerator $urlGenerator;
|
||||
private FullTextSearchService $fullTextSearchService;
|
||||
private ?IRunner $runner = null;
|
||||
private ?IIndexOptions $indexOptions = null;
|
||||
|
||||
/** @var IL10N */
|
||||
private $l10n;
|
||||
|
||||
/** @var IUrlGenerator */
|
||||
private $urlGenerator;
|
||||
|
||||
/** @var FullTextSearchService */
|
||||
private $fullTextSearchService;
|
||||
|
||||
|
||||
/** @var IRunner */
|
||||
private $runner;
|
||||
|
||||
/** @var IIndexOptions */
|
||||
private $indexOptions = [];
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,169 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2022 Julien Veyssier <eneiluj@posteo.net>
|
||||
*
|
||||
* @author Julien Veyssier <eneiluj@posteo.net>
|
||||
*
|
||||
* @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/>.
|
||||
*/
|
||||
|
||||
namespace OCA\Deck\Reference;
|
||||
|
||||
use OCA\Deck\AppInfo\Application;
|
||||
use OCA\Deck\Db\Assignment;
|
||||
use OCA\Deck\Db\Attachment;
|
||||
use OCA\Deck\Db\Label;
|
||||
use OCA\Deck\Model\CardDetails;
|
||||
use OCA\Deck\Service\BoardService;
|
||||
use OCA\Deck\Service\CardService;
|
||||
use OCA\Deck\Service\StackService;
|
||||
use OCP\Collaboration\Reference\IReference;
|
||||
use OCP\Collaboration\Reference\IReferenceProvider;
|
||||
use OCP\Collaboration\Reference\Reference;
|
||||
use OCP\IURLGenerator;
|
||||
|
||||
class CardReferenceProvider implements IReferenceProvider {
|
||||
private CardService $cardService;
|
||||
private IURLGenerator $urlGenerator;
|
||||
private BoardService $boardService;
|
||||
private StackService $stackService;
|
||||
private ?string $userId;
|
||||
|
||||
public function __construct(CardService $cardService,
|
||||
BoardService $boardService,
|
||||
StackService $stackService,
|
||||
IURLGenerator $urlGenerator,
|
||||
?string $userId) {
|
||||
$this->cardService = $cardService;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->boardService = $boardService;
|
||||
$this->stackService = $stackService;
|
||||
$this->userId = $userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function matchReference(string $referenceText): bool {
|
||||
$start = $this->urlGenerator->getAbsoluteURL('/apps/' . Application::APP_ID);
|
||||
$startIndex = $this->urlGenerator->getAbsoluteURL('/index.php/apps/' . Application::APP_ID);
|
||||
|
||||
// link example: https://nextcloud.local/index.php/apps/deck/#/board/2/card/11
|
||||
$noIndexMatch = preg_match('/^' . preg_quote($start, '/') . '\/#\/board\/[0-9]+\/card\/[0-9]+$/', $referenceText) === 1;
|
||||
$indexMatch = preg_match('/^' . preg_quote($startIndex, '/') . '\/#\/board\/[0-9]+\/card\/[0-9]+$/', $referenceText) === 1;
|
||||
|
||||
return $noIndexMatch || $indexMatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function resolveReference(string $referenceText): ?IReference {
|
||||
if ($this->matchReference($referenceText)) {
|
||||
$ids = $this->getBoardCardId($referenceText);
|
||||
if ($ids !== null) {
|
||||
[$boardId, $cardId] = $ids;
|
||||
$card = $this->cardService->find((int) $cardId)->jsonSerialize();
|
||||
$board = $this->boardService->find((int) $boardId)->jsonSerialize();
|
||||
$stack = $this->stackService->find((int) $card['stackId'])->jsonSerialize();
|
||||
|
||||
$card = $this->sanitizeSerializedCard($card);
|
||||
$board = $this->sanitizeSerializedBoard($board);
|
||||
$stack = $this->sanitizeSerializedStack($stack);
|
||||
|
||||
$reference = new Reference($referenceText);
|
||||
$reference->setRichObject(Application::APP_ID . '-card', [
|
||||
'id' => $boardId . '/' . $cardId,
|
||||
'card' => $card,
|
||||
'board' => $board,
|
||||
'stack' => $stack,
|
||||
]);
|
||||
return $reference;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function sanitizeSerializedStack(array $stack): array {
|
||||
$stack['cards'] = array_map(function (CardDetails $cardDetails) {
|
||||
$result = $cardDetails->jsonSerialize();
|
||||
unset($result['assignedUsers']);
|
||||
return $result;
|
||||
}, $stack['cards']);
|
||||
|
||||
return $stack;
|
||||
}
|
||||
|
||||
private function sanitizeSerializedBoard(array $board): array {
|
||||
unset($board['labels']);
|
||||
$board['owner'] = $board['owner']->jsonSerialize();
|
||||
unset($board['acl']);
|
||||
unset($board['users']);
|
||||
|
||||
return $board;
|
||||
}
|
||||
|
||||
private function sanitizeSerializedCard(array $card): array {
|
||||
$card['labels'] = array_map(function (Label $label) {
|
||||
return $label->jsonSerialize();
|
||||
}, $card['labels']);
|
||||
$card['assignedUsers'] = array_map(function (Assignment $assignment) {
|
||||
$result = $assignment->jsonSerialize();
|
||||
$result['participant'] = $result['participant']->jsonSerialize();
|
||||
return $result;
|
||||
}, $card['assignedUsers']);
|
||||
$card['owner'] = $card['owner']->jsonSerialize();
|
||||
unset($card['relatedStack']);
|
||||
unset($card['relatedBoard']);
|
||||
$card['attachments'] = array_map(function (Attachment $attachment) {
|
||||
return $attachment->jsonSerialize();
|
||||
}, $card['attachments']);
|
||||
|
||||
return $card;
|
||||
}
|
||||
|
||||
private function getBoardCardId(string $url): ?array {
|
||||
$start = $this->urlGenerator->getAbsoluteURL('/apps/' . Application::APP_ID);
|
||||
$startIndex = $this->urlGenerator->getAbsoluteURL('/index.php/apps/' . Application::APP_ID);
|
||||
|
||||
preg_match('/^' . preg_quote($start, '/') . '\/#\/board\/([0-9]+)\/card\/([0-9]+)$/', $url, $matches);
|
||||
if ($matches && count($matches) > 2) {
|
||||
return [$matches[1], $matches[2]];
|
||||
}
|
||||
|
||||
preg_match('/^' . preg_quote($startIndex, '/') . '\/#\/board\/([0-9]+)\/card\/([0-9]+)$/', $url, $matches2);
|
||||
if ($matches2 && count($matches2) > 2) {
|
||||
return [$matches2[1], $matches2[2]];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getCachePrefix(string $referenceId): string {
|
||||
$ids = $this->getBoardCardId($referenceId);
|
||||
if ($ids !== null) {
|
||||
[$boardId, $cardId] = $ids;
|
||||
return $boardId . '/' . $cardId;
|
||||
}
|
||||
|
||||
return $referenceId;
|
||||
}
|
||||
|
||||
public function getCacheKey(string $referenceId): ?string {
|
||||
return $this->userId ?? '';
|
||||
}
|
||||
}
|
||||
@@ -60,23 +60,22 @@ class AttachmentService {
|
||||
private $activityManager;
|
||||
/** @var ChangeHelper */
|
||||
private $changeHelper;
|
||||
private IUserManager $userManager;
|
||||
/** @var IUserManager */
|
||||
private $userManager;
|
||||
/** @var AttachmentServiceValidator */
|
||||
private AttachmentServiceValidator $attachmentServiceValidator;
|
||||
private $attachmentServiceValidator;
|
||||
|
||||
public function __construct(
|
||||
AttachmentMapper $attachmentMapper,
|
||||
CardMapper $cardMapper,
|
||||
IUserManager $userManager,
|
||||
ChangeHelper $changeHelper,
|
||||
PermissionService $permissionService,
|
||||
Application $application,
|
||||
AttachmentCacheHelper $attachmentCacheHelper,
|
||||
$userId,
|
||||
IL10N $l10n,
|
||||
ActivityManager $activityManager,
|
||||
AttachmentServiceValidator $attachmentServiceValidator
|
||||
) {
|
||||
public function __construct(AttachmentMapper $attachmentMapper,
|
||||
CardMapper $cardMapper,
|
||||
IUserManager $userManager,
|
||||
ChangeHelper $changeHelper,
|
||||
PermissionService $permissionService,
|
||||
Application $application,
|
||||
AttachmentCacheHelper $attachmentCacheHelper,
|
||||
$userId,
|
||||
IL10N $l10n,
|
||||
ActivityManager $activityManager,
|
||||
AttachmentServiceValidator $attachmentServiceValidator) {
|
||||
$this->attachmentMapper = $attachmentMapper;
|
||||
$this->cardMapper = $cardMapper;
|
||||
$this->permissionService = $permissionService;
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
namespace OCA\Deck\Service;
|
||||
|
||||
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
|
||||
use OCA\Deck\Activity\ActivityManager;
|
||||
use OCA\Deck\Activity\ChangeSet;
|
||||
use OCA\Deck\AppInfo\Application;
|
||||
@@ -44,10 +45,8 @@ use OCA\Deck\Notification\NotificationHelper;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\IConfig;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\IGroupManager;
|
||||
use OCP\IL10N;
|
||||
use OCP\DB\Exception as DbException;
|
||||
use OCA\Deck\Db\Board;
|
||||
use OCA\Deck\Db\BoardMapper;
|
||||
use OCA\Deck\Db\LabelMapper;
|
||||
@@ -55,29 +54,30 @@ use OCP\IUserManager;
|
||||
use OCA\Deck\BadRequestException;
|
||||
use OCA\Deck\Validators\BoardServiceValidator;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\Server;
|
||||
|
||||
class BoardService {
|
||||
private BoardMapper $boardMapper;
|
||||
private StackMapper $stackMapper;
|
||||
private LabelMapper $labelMapper;
|
||||
private AclMapper $aclMapper;
|
||||
private IConfig $config;
|
||||
private IL10N $l10n;
|
||||
private PermissionService $permissionService;
|
||||
private NotificationHelper $notificationHelper;
|
||||
private AssignmentMapper $assignedUsersMapper;
|
||||
private IUserManager $userManager;
|
||||
private IGroupManager $groupManager;
|
||||
private ?string $userId;
|
||||
private ActivityManager $activityManager;
|
||||
private IEventDispatcher $eventDispatcher;
|
||||
private ChangeHelper $changeHelper;
|
||||
private CardMapper $cardMapper;
|
||||
private ?array $boardsCache = null;
|
||||
private IURLGenerator $urlGenerator;
|
||||
private IDBConnection $connection;
|
||||
private BoardServiceValidator $boardServiceValidator;
|
||||
private $boardMapper;
|
||||
private $stackMapper;
|
||||
private $labelMapper;
|
||||
private $aclMapper;
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
private $l10n;
|
||||
private $permissionService;
|
||||
private $notificationHelper;
|
||||
private $assignedUsersMapper;
|
||||
private $userManager;
|
||||
private $groupManager;
|
||||
private $userId;
|
||||
private $activityManager;
|
||||
private $eventDispatcher;
|
||||
private $changeHelper;
|
||||
private $cardMapper;
|
||||
|
||||
private $boardsCache = null;
|
||||
private $urlGenerator;
|
||||
private $boardServiceValidator;
|
||||
|
||||
|
||||
public function __construct(
|
||||
BoardMapper $boardMapper,
|
||||
@@ -96,9 +96,8 @@ class BoardService {
|
||||
IEventDispatcher $eventDispatcher,
|
||||
ChangeHelper $changeHelper,
|
||||
IURLGenerator $urlGenerator,
|
||||
IDBConnection $connection,
|
||||
BoardServiceValidator $boardServiceValidator,
|
||||
?string $userId
|
||||
$userId
|
||||
) {
|
||||
$this->boardMapper = $boardMapper;
|
||||
$this->stackMapper = $stackMapper;
|
||||
@@ -117,7 +116,6 @@ class BoardService {
|
||||
$this->userId = $userId;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->cardMapper = $cardMapper;
|
||||
$this->connection = $connection;
|
||||
$this->boardServiceValidator = $boardServiceValidator;
|
||||
}
|
||||
|
||||
@@ -481,7 +479,7 @@ class BoardService {
|
||||
|
||||
// TODO: use the dispatched event for this
|
||||
try {
|
||||
$resourceProvider = Server::get(\OCA\Deck\Collaboration\Resources\ResourceProvider::class);
|
||||
$resourceProvider = \OC::$server->query(\OCA\Deck\Collaboration\Resources\ResourceProvider::class);
|
||||
$resourceProvider->invalidateAccessCache($boardId);
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
@@ -523,12 +521,18 @@ class BoardService {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
* @return \OCP\AppFramework\Db\Entity
|
||||
* @throws DoesNotExistException
|
||||
* @throws \OCA\Deck\NoPermissionException
|
||||
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
|
||||
* @throws BadRequestException
|
||||
*/
|
||||
public function deleteAcl(int $id): bool {
|
||||
public function deleteAcl($id) {
|
||||
if (is_numeric($id) === false) {
|
||||
throw new BadRequestException('id must be a number');
|
||||
}
|
||||
|
||||
$this->permissionService->checkPermission($this->aclMapper, $id, Acl::PERMISSION_SHARE);
|
||||
/** @var Acl $acl */
|
||||
$acl = $this->aclMapper->find($id);
|
||||
@@ -547,14 +551,16 @@ class BoardService {
|
||||
$version = \OCP\Util::getVersion()[0];
|
||||
if ($version >= 16) {
|
||||
try {
|
||||
$resourceProvider = Server::get(\OCA\Deck\Collaboration\Resources\ResourceProvider::class);
|
||||
$resourceProvider = \OC::$server->query(\OCA\Deck\Collaboration\Resources\ResourceProvider::class);
|
||||
$resourceProvider->invalidateAccessCache($acl->getBoardId());
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
}
|
||||
$delete = $this->aclMapper->delete($acl);
|
||||
|
||||
$this->eventDispatcher->dispatchTyped(new AclDeletedEvent($acl));
|
||||
return (bool) $this->aclMapper->delete($acl);
|
||||
|
||||
return $delete;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -606,7 +612,7 @@ class BoardService {
|
||||
}
|
||||
|
||||
public function transferBoardOwnership(int $boardId, string $newOwner, bool $changeContent = false): Board {
|
||||
$this->connection->beginTransaction();
|
||||
\OC::$server->getDatabaseConnection()->beginTransaction();
|
||||
try {
|
||||
$board = $this->boardMapper->find($boardId);
|
||||
$previousOwner = $board->getOwner();
|
||||
@@ -615,10 +621,7 @@ class BoardService {
|
||||
if (!$changeContent) {
|
||||
try {
|
||||
$this->addAcl($boardId, Acl::PERMISSION_TYPE_USER, $previousOwner, true, true, true);
|
||||
} catch (DbException $e) {
|
||||
if ($e->getReason() !== DbException::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
|
||||
throw $e;
|
||||
}
|
||||
} catch (UniqueConstraintViolationException $e) {
|
||||
}
|
||||
}
|
||||
$this->boardMapper->transferOwnership($previousOwner, $newOwner, $boardId);
|
||||
@@ -628,10 +631,10 @@ class BoardService {
|
||||
$this->assignedUsersMapper->remapAssignedUser($boardId, $previousOwner, $newOwner);
|
||||
$this->cardMapper->remapCardOwner($boardId, $previousOwner, $newOwner);
|
||||
}
|
||||
$this->connection->commit();
|
||||
\OC::$server->getDatabaseConnection()->commit();
|
||||
return $this->boardMapper->find($boardId);
|
||||
} catch (\Throwable $e) {
|
||||
$this->connection->rollBack();
|
||||
\OC::$server->getDatabaseConnection()->rollBack();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,31 +46,27 @@ use OCA\Deck\BadRequestException;
|
||||
use OCA\Deck\Validators\CardServiceValidator;
|
||||
use OCP\Comments\ICommentsManager;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\IRequest;
|
||||
use OCP\IUserManager;
|
||||
use OCP\IURLGenerator;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class CardService {
|
||||
private CardMapper $cardMapper;
|
||||
private StackMapper $stackMapper;
|
||||
private BoardMapper $boardMapper;
|
||||
private LabelMapper $labelMapper;
|
||||
private PermissionService $permissionService;
|
||||
private BoardService $boardService;
|
||||
private NotificationHelper $notificationHelper;
|
||||
private AssignmentMapper $assignedUsersMapper;
|
||||
private AttachmentService $attachmentService;
|
||||
private ?string $currentUser;
|
||||
private ActivityManager $activityManager;
|
||||
private ICommentsManager $commentsManager;
|
||||
private ChangeHelper $changeHelper;
|
||||
private IEventDispatcher $eventDispatcher;
|
||||
private IUserManager $userManager;
|
||||
private IURLGenerator $urlGenerator;
|
||||
private LoggerInterface $logger;
|
||||
private IRequest $request;
|
||||
private CardServiceValidator $cardServiceValidator;
|
||||
private $cardMapper;
|
||||
private $stackMapper;
|
||||
private $boardMapper;
|
||||
private $labelMapper;
|
||||
private $permissionService;
|
||||
private $boardService;
|
||||
private $notificationHelper;
|
||||
private $assignedUsersMapper;
|
||||
private $attachmentService;
|
||||
private $currentUser;
|
||||
private $activityManager;
|
||||
private $commentsManager;
|
||||
private $changeHelper;
|
||||
private $eventDispatcher;
|
||||
private $userManager;
|
||||
private $urlGenerator;
|
||||
private $cardServiceValidator;
|
||||
|
||||
public function __construct(
|
||||
CardMapper $cardMapper,
|
||||
@@ -88,10 +84,8 @@ class CardService {
|
||||
ChangeHelper $changeHelper,
|
||||
IEventDispatcher $eventDispatcher,
|
||||
IURLGenerator $urlGenerator,
|
||||
LoggerInterface $logger,
|
||||
IRequest $request,
|
||||
CardServiceValidator $cardServiceValidator,
|
||||
?string $userId
|
||||
$userId
|
||||
) {
|
||||
$this->cardMapper = $cardMapper;
|
||||
$this->stackMapper = $stackMapper;
|
||||
@@ -109,8 +103,6 @@ class CardService {
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
$this->currentUser = $userId;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->logger = $logger;
|
||||
$this->request = $request;
|
||||
$this->cardServiceValidator = $cardServiceValidator;
|
||||
}
|
||||
|
||||
@@ -144,18 +136,23 @@ class CardService {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $cardId
|
||||
* @return \OCA\Deck\Db\RelationalEntity
|
||||
* @throws \OCA\Deck\NoPermissionException
|
||||
* @throws \OCP\AppFramework\Db\DoesNotExistException
|
||||
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
|
||||
* @throws BadRequestException
|
||||
*/
|
||||
public function find(int $cardId) {
|
||||
public function find($cardId) {
|
||||
if (is_numeric($cardId) === false) {
|
||||
throw new BadRequestException('card id must be a number');
|
||||
}
|
||||
|
||||
$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_READ);
|
||||
$card = $this->cardMapper->find($cardId);
|
||||
$assignedUsers = $this->assignedUsersMapper->findAll($card->getId());
|
||||
$attachments = $this->attachmentService->findAll($cardId, true);
|
||||
if ($this->request->getParam('apiVersion') === '1.0') {
|
||||
if (\OC::$server->getRequest()->getParam('apiVersion') === '1.0') {
|
||||
$attachments = array_filter($attachments, function ($attachment) {
|
||||
return $attachment->getType() === 'deck_file';
|
||||
});
|
||||
@@ -170,7 +167,7 @@ class CardService {
|
||||
try {
|
||||
$this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_READ);
|
||||
} catch (NoPermissionException $e) {
|
||||
$this->logger->error('Unable to check permission for a previously obtained board ' . $boardId, ['exception' => $e]);
|
||||
\OC::$server->getLogger()->error('Unable to check permission for a previously obtained board ' . $boardId, ['exception' => $e]);
|
||||
return [];
|
||||
}
|
||||
$cards = $this->cardMapper->findCalendarEntries($boardId);
|
||||
@@ -298,11 +295,11 @@ class CardService {
|
||||
$card->setType($type);
|
||||
$card->setOrder($order);
|
||||
$card->setOwner($owner);
|
||||
$card->setDuedate($duedate ? new \DateTime($duedate) : null);
|
||||
$card->setDuedate($duedate);
|
||||
$resetDuedateNotification = false;
|
||||
if (
|
||||
$card->getDuedate() === null ||
|
||||
($card->getDuedate()) != ($changes->getBefore()->getDuedate())
|
||||
(new \DateTime($card->getDuedate())) != (new \DateTime($changes->getBefore()->getDuedate() ?? ''))
|
||||
) {
|
||||
$card->setNotified(false);
|
||||
$resetDuedateNotification = true;
|
||||
@@ -431,7 +428,7 @@ class CardService {
|
||||
* @throws StatusException
|
||||
* @throws \OCA\Deck\NoPermissionException
|
||||
* @throws \OCP\AppFramework\Db\DoesNotExistException
|
||||
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
|
||||
* @throws \OCP\AppFramework\Db\
|
||||
* @throws BadRequestException
|
||||
*/
|
||||
public function archive($id) {
|
||||
|
||||
@@ -31,7 +31,6 @@ use OCA\Circles\Model\Circle;
|
||||
use OCA\Circles\Model\Member;
|
||||
use OCA\Circles\Model\Probes\CircleProbe;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\Server;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
@@ -39,7 +38,7 @@ use Throwable;
|
||||
* having the app disabled is properly handled
|
||||
*/
|
||||
class CirclesService {
|
||||
private bool $circlesEnabled;
|
||||
private $circlesEnabled;
|
||||
|
||||
private $userCircleCache = [];
|
||||
|
||||
@@ -59,7 +58,8 @@ class CirclesService {
|
||||
try {
|
||||
|
||||
// Enforce current user condition since we always want the full list of members
|
||||
$circlesManager = Server::get(CirclesManager::class);
|
||||
/** @var CirclesManager $circlesManager */
|
||||
$circlesManager = \OC::$server->get(CirclesManager::class);
|
||||
$circlesManager->startSuperSession();
|
||||
return $circlesManager->getCircle($circleId);
|
||||
} catch (Throwable $e) {
|
||||
@@ -77,7 +77,8 @@ class CirclesService {
|
||||
}
|
||||
|
||||
try {
|
||||
$circlesManager = Server::get(CirclesManager::class);
|
||||
/** @var CirclesManager $circlesManager */
|
||||
$circlesManager = \OC::$server->get(CirclesManager::class);
|
||||
$federatedUser = $circlesManager->getFederatedUser($userId, Member::TYPE_USER);
|
||||
$circlesManager->startSession($federatedUser);
|
||||
$circle = $circlesManager->getCircle($circleId);
|
||||
@@ -105,7 +106,8 @@ class CirclesService {
|
||||
}
|
||||
|
||||
try {
|
||||
$circlesManager = Server::get(CirclesManager::class);
|
||||
/** @var CirclesManager $circlesManager */
|
||||
$circlesManager = \OC::$server->get(CirclesManager::class);
|
||||
$federatedUser = $circlesManager->getFederatedUser($userId, Member::TYPE_USER);
|
||||
$circlesManager->startSession($federatedUser);
|
||||
$probe = new CircleProbe();
|
||||
|
||||
@@ -40,14 +40,20 @@ use OutOfBoundsException;
|
||||
use function is_numeric;
|
||||
|
||||
class CommentService {
|
||||
private ICommentsManager $commentsManager;
|
||||
private IUserManager $userManager;
|
||||
private CardMapper $cardMapper;
|
||||
private PermissionService $permissionService;
|
||||
private ILogger $logger;
|
||||
private ?string $userId;
|
||||
|
||||
public function __construct(ICommentsManager $commentsManager, PermissionService $permissionService, CardMapper $cardMapper, IUserManager $userManager, ILogger $logger, ?string $userId) {
|
||||
/**
|
||||
* @var ICommentsManager
|
||||
*/
|
||||
private $commentsManager;
|
||||
/**
|
||||
* @var IUserManager
|
||||
*/
|
||||
private $userManager;
|
||||
/** @var ILogger */
|
||||
private $logger;
|
||||
private $userId;
|
||||
|
||||
public function __construct(ICommentsManager $commentsManager, PermissionService $permissionService, CardMapper $cardMapper, IUserManager $userManager, ILogger $logger, $userId) {
|
||||
$this->commentsManager = $commentsManager;
|
||||
$this->permissionService = $permissionService;
|
||||
$this->cardMapper = $cardMapper;
|
||||
|
||||
@@ -40,9 +40,9 @@ class ConfigService {
|
||||
public const SETTING_BOARD_NOTIFICATION_DUE_ALL = 'all';
|
||||
public const SETTING_BOARD_NOTIFICATION_DUE_DEFAULT = self::SETTING_BOARD_NOTIFICATION_DUE_ASSIGNED;
|
||||
|
||||
private IConfig $config;
|
||||
private ?string $userId = null;
|
||||
private IGroupManager $groupManager;
|
||||
private $config;
|
||||
private $userId;
|
||||
private $groupManager;
|
||||
|
||||
public function __construct(
|
||||
IConfig $config,
|
||||
@@ -52,14 +52,11 @@ class ConfigService {
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function getUserId(): ?string {
|
||||
public function getUserId() {
|
||||
if (!$this->userId) {
|
||||
// We cannot use DI for the userId or UserSession as the ConfigService
|
||||
// is initiated too early before the session is actually loaded
|
||||
$user = \OCP\Server::get(IUserSession::class)->getUser();
|
||||
$user = \OC::$server->get(IUserSession::class)->getUser();
|
||||
$this->userId = $user ? $user->getUID() : null;
|
||||
}
|
||||
|
||||
return $this->userId;
|
||||
}
|
||||
|
||||
@@ -78,11 +75,8 @@ class ConfigService {
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|array{id: string, displayname: string}[]
|
||||
* @throws NoPermissionException
|
||||
*/
|
||||
public function get(string $key) {
|
||||
public function get($key) {
|
||||
$result = null;
|
||||
[$scope] = explode(':', $key, 2);
|
||||
switch ($scope) {
|
||||
case 'groupLimit':
|
||||
@@ -96,12 +90,11 @@ class ConfigService {
|
||||
}
|
||||
return (bool)$this->config->getUserValue($this->getUserId(), Application::APP_ID, 'calendar', true);
|
||||
case 'cardDetailsInModal':
|
||||
if ($this->getUserId() === null) {
|
||||
return false;
|
||||
}
|
||||
if ($this->getUserId() === null) {
|
||||
return false;
|
||||
}
|
||||
return (bool)$this->config->getUserValue($this->getUserId(), Application::APP_ID, 'cardDetailsInModal', true);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isCalendarEnabled(int $boardId = null): bool {
|
||||
@@ -164,10 +157,7 @@ class ConfigService {
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function setGroupLimit(array $value): array {
|
||||
private function setGroupLimit($value) {
|
||||
$groups = [];
|
||||
foreach ($value as $group) {
|
||||
$groups[] = $group['id'];
|
||||
@@ -177,7 +167,7 @@ class ConfigService {
|
||||
return $groups;
|
||||
}
|
||||
|
||||
private function getGroupLimitList(): array {
|
||||
private function getGroupLimitList() {
|
||||
$value = $this->config->getAppValue(Application::APP_ID, 'groupLimit', '');
|
||||
$groups = explode(',', $value);
|
||||
if ($value === '') {
|
||||
@@ -186,10 +176,9 @@ class ConfigService {
|
||||
return $groups;
|
||||
}
|
||||
|
||||
/** @return array{id: string, displayname: string}[] */
|
||||
private function getGroupLimit() {
|
||||
$groups = $this->getGroupLimitList();
|
||||
$groups = array_map(function (string $groupId): ?array {
|
||||
$groups = array_map(function ($groupId) {
|
||||
/** @var IGroup $groups */
|
||||
$group = $this->groupManager->get($groupId);
|
||||
if ($group === null) {
|
||||
|
||||
@@ -88,6 +88,18 @@ class DefaultBoardService {
|
||||
* @throws BadRequestException
|
||||
*/
|
||||
public function createDefaultBoard(string $title, string $userId, string $color) {
|
||||
if ($title === false || $title === null) {
|
||||
throw new BadRequestException('title must be provided');
|
||||
}
|
||||
|
||||
if ($userId === false || $userId === null) {
|
||||
throw new BadRequestException('userId must be provided');
|
||||
}
|
||||
|
||||
if ($color === false || $color === null) {
|
||||
throw new BadRequestException('color must be provided');
|
||||
}
|
||||
|
||||
$defaultBoard = $this->boardService->create($title, $userId, $color);
|
||||
$defaultStacks = [];
|
||||
$defaultCards = [];
|
||||
|
||||
@@ -219,11 +219,8 @@ class FileService implements IAttachmentService {
|
||||
throw new \Exception('no instance id!');
|
||||
}
|
||||
$name = 'appdata_' . $instanceId;
|
||||
/** @var \OCP\Files\Folder $appDataFolder */
|
||||
$appDataFolder = $this->rootFolder->get($name);
|
||||
/** @var \OCP\Files\Folder $appDataFolder */
|
||||
$appDataFolder = $appDataFolder->get('deck');
|
||||
/** @var \OCP\Files\Folder $cardFolder */
|
||||
$cardFolder = $appDataFolder->get($folderName);
|
||||
return $cardFolder->get($attachment->getData());
|
||||
}
|
||||
|
||||
@@ -44,19 +44,18 @@ use OCP\Share\IShare;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class FilesAppService implements IAttachmentService, ICustomAttachmentService {
|
||||
private IRequest $request;
|
||||
private IRootFolder $rootFolder;
|
||||
private DeckShareProvider $shareProvider;
|
||||
private IManager $shareManager;
|
||||
private ?string $userId;
|
||||
private ConfigService $configService;
|
||||
private IL10N $l10n;
|
||||
private IPreview $preview;
|
||||
private IMimeTypeDetector $mimeTypeDetector;
|
||||
private PermissionService $permissionService;
|
||||
private CardMapper $cardMapper;
|
||||
private LoggerInterface $logger;
|
||||
private IDBConnection $connection;
|
||||
private $request;
|
||||
private $rootFolder;
|
||||
private $shareProvider;
|
||||
private $shareManager;
|
||||
private $userId;
|
||||
private $configService;
|
||||
private $l10n;
|
||||
private $preview;
|
||||
private $mimeTypeDetector;
|
||||
private $permissionService;
|
||||
private $cardMapper;
|
||||
private $logger;
|
||||
|
||||
public function __construct(
|
||||
IRequest $request,
|
||||
@@ -70,8 +69,7 @@ class FilesAppService implements IAttachmentService, ICustomAttachmentService {
|
||||
PermissionService $permissionService,
|
||||
CardMapper $cardMapper,
|
||||
LoggerInterface $logger,
|
||||
IDBConnection $connection,
|
||||
?string $userId
|
||||
string $userId = null
|
||||
) {
|
||||
$this->request = $request;
|
||||
$this->l10n = $l10n;
|
||||
@@ -85,7 +83,6 @@ class FilesAppService implements IAttachmentService, ICustomAttachmentService {
|
||||
$this->permissionService = $permissionService;
|
||||
$this->cardMapper = $cardMapper;
|
||||
$this->logger = $logger;
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
public function listAttachments(int $cardId): array {
|
||||
@@ -111,7 +108,9 @@ class FilesAppService implements IAttachmentService, ICustomAttachmentService {
|
||||
}
|
||||
|
||||
public function getAttachmentCount(int $cardId): int {
|
||||
$qb = $this->connection->getQueryBuilder();
|
||||
/** @var IDBConnection $qb */
|
||||
$db = \OC::$server->getDatabaseConnection();
|
||||
$qb = $db->getQueryBuilder();
|
||||
$qb->select('s.id', 'f.fileid', 'f.path')
|
||||
->selectAlias('st.id', 'storage_string_id')
|
||||
->from('share', 's')
|
||||
@@ -126,7 +125,7 @@ class FilesAppService implements IAttachmentService, ICustomAttachmentService {
|
||||
));
|
||||
|
||||
$count = 0;
|
||||
$cursor = $qb->executeQuery();
|
||||
$cursor = $qb->execute();
|
||||
while ($data = $cursor->fetch()) {
|
||||
if ($this->shareProvider->isAccessibleResult($data)) {
|
||||
$count++;
|
||||
|
||||
@@ -47,22 +47,34 @@ use OCP\Comments\ICommentsManager;
|
||||
use OCP\Comments\NotFoundException as CommentNotFoundException;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\IUserManager;
|
||||
use OCP\Server;
|
||||
|
||||
class BoardImportService {
|
||||
private IUserManager $userManager;
|
||||
private BoardMapper $boardMapper;
|
||||
private AclMapper $aclMapper;
|
||||
private LabelMapper $labelMapper;
|
||||
private StackMapper $stackMapper;
|
||||
private CardMapper $cardMapper;
|
||||
private AssignmentMapper $assignmentMapper;
|
||||
private AttachmentMapper $attachmentMapper;
|
||||
private ICommentsManager $commentsManager;
|
||||
private IEventDispatcher $eventDispatcher;
|
||||
private string $system = '';
|
||||
private ?ABoardImportService $systemInstance;
|
||||
private array $allowedSystems = [];
|
||||
/** @var IUserManager */
|
||||
private $userManager;
|
||||
/** @var BoardMapper */
|
||||
private $boardMapper;
|
||||
/** @var AclMapper */
|
||||
private $aclMapper;
|
||||
/** @var LabelMapper */
|
||||
private $labelMapper;
|
||||
/** @var StackMapper */
|
||||
private $stackMapper;
|
||||
/** @var CardMapper */
|
||||
private $cardMapper;
|
||||
/** @var AssignmentMapper */
|
||||
private $assignmentMapper;
|
||||
/** @var AttachmentMapper */
|
||||
private $attachmentMapper;
|
||||
/** @var ICommentsManager */
|
||||
private $commentsManager;
|
||||
/** @var IEventDispatcher */
|
||||
private $eventDispatcher;
|
||||
/** @var string */
|
||||
private $system = '';
|
||||
/** @var null|ABoardImportService */
|
||||
private $systemInstance;
|
||||
/** @var array */
|
||||
private $allowedSystems = [];
|
||||
/**
|
||||
* Data object created from config JSON
|
||||
*
|
||||
@@ -77,7 +89,10 @@ class BoardImportService {
|
||||
* @psalm-suppress PropertyNotSetInConstructor
|
||||
*/
|
||||
private $data;
|
||||
private Board $board;
|
||||
/**
|
||||
* @var Board
|
||||
*/
|
||||
private $board;
|
||||
|
||||
public function __construct(
|
||||
IUserManager $userManager,
|
||||
@@ -183,7 +198,7 @@ class BoardImportService {
|
||||
}
|
||||
if (!is_object($this->systemInstance)) {
|
||||
$systemClass = 'OCA\\Deck\\Service\\Importer\\Systems\\' . ucfirst($this->getSystem()) . 'Service';
|
||||
$this->systemInstance = Server::get($systemClass);
|
||||
$this->systemInstance = \OC::$server->get($systemClass);
|
||||
$this->systemInstance->setImportService($this);
|
||||
}
|
||||
return $this->systemInstance;
|
||||
@@ -328,7 +343,7 @@ class BoardImportService {
|
||||
}
|
||||
|
||||
public function insertAttachment(Attachment $attachment, string $content): Attachment {
|
||||
$service = Server::get(FileService::class);
|
||||
$service = \OC::$server->get(FileService::class);
|
||||
$folder = $service->getFolder($attachment);
|
||||
|
||||
if ($folder->fileExists($attachment->getData())) {
|
||||
|
||||
@@ -91,10 +91,12 @@ class LabelService {
|
||||
$this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_MANAGE);
|
||||
|
||||
$boardLabels = $this->labelMapper->findAll($boardId);
|
||||
foreach ($boardLabels as $boardLabel) {
|
||||
if ($boardLabel->getTitle() === $title) {
|
||||
throw new BadRequestException('title must be unique');
|
||||
break;
|
||||
if (\is_array($boardLabels)) {
|
||||
foreach ($boardLabels as $boardLabel) {
|
||||
if ($boardLabel->getTitle() === $title) {
|
||||
throw new BadRequestException('title must be unique');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,13 +151,15 @@ class LabelService {
|
||||
$label = $this->find($id);
|
||||
|
||||
$boardLabels = $this->labelMapper->findAll($label->getBoardId());
|
||||
foreach ($boardLabels as $boardLabel) {
|
||||
if ($boardLabel->getId() === $label->getId()) {
|
||||
continue;
|
||||
}
|
||||
if ($boardLabel->getTitle() === $title) {
|
||||
throw new BadRequestException('title must be unique');
|
||||
break;
|
||||
if (\is_array($boardLabels)) {
|
||||
foreach ($boardLabels as $boardLabel) {
|
||||
if ($boardLabel->getId() === $label->getId()) {
|
||||
continue;
|
||||
}
|
||||
if ($boardLabel->getTitle() === $title) {
|
||||
throw new BadRequestException('title must be unique');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,20 +30,31 @@ namespace OCA\Deck\Service;
|
||||
use OCA\Deck\Db\AssignmentMapper;
|
||||
use OCA\Deck\Db\Card;
|
||||
use OCA\Deck\Db\CardMapper;
|
||||
use OCA\Deck\Model\CardDetails;
|
||||
use OCP\Comments\ICommentsManager;
|
||||
use OCP\IGroupManager;
|
||||
use OCA\Deck\Db\Board;
|
||||
use OCA\Deck\Db\BoardMapper;
|
||||
use OCA\Deck\Db\LabelMapper;
|
||||
use OCP\IUserManager;
|
||||
|
||||
class OverviewService {
|
||||
private BoardMapper $boardMapper;
|
||||
private LabelMapper $labelMapper;
|
||||
private CardMapper $cardMapper;
|
||||
private AssignmentMapper $assignedUsersMapper;
|
||||
private IUserManager $userManager;
|
||||
private ICommentsManager $commentsManager;
|
||||
private AttachmentService $attachmentService;
|
||||
|
||||
/** @var BoardMapper */
|
||||
private $boardMapper;
|
||||
/** @var LabelMapper */
|
||||
private $labelMapper;
|
||||
/** @var CardMapper */
|
||||
private $cardMapper;
|
||||
/** @var AssignmentMapper */
|
||||
private $assignedUsersMapper;
|
||||
/** @var IUserManager */
|
||||
private $userManager;
|
||||
/** @var IGroupManager */
|
||||
private $groupManager;
|
||||
/** @var ICommentsManager */
|
||||
private $commentsManager;
|
||||
/** @var AttachmentService */
|
||||
private $attachmentService;
|
||||
|
||||
public function __construct(
|
||||
BoardMapper $boardMapper,
|
||||
@@ -51,6 +62,7 @@ class OverviewService {
|
||||
CardMapper $cardMapper,
|
||||
AssignmentMapper $assignedUsersMapper,
|
||||
IUserManager $userManager,
|
||||
IGroupManager $groupManager,
|
||||
ICommentsManager $commentsManager,
|
||||
AttachmentService $attachmentService
|
||||
) {
|
||||
@@ -59,6 +71,7 @@ class OverviewService {
|
||||
$this->cardMapper = $cardMapper;
|
||||
$this->assignedUsersMapper = $assignedUsersMapper;
|
||||
$this->userManager = $userManager;
|
||||
$this->groupManager = $groupManager;
|
||||
$this->commentsManager = $commentsManager;
|
||||
$this->attachmentService = $attachmentService;
|
||||
}
|
||||
@@ -80,37 +93,62 @@ class OverviewService {
|
||||
}
|
||||
|
||||
public function findAllWithDue(string $userId): array {
|
||||
$userBoards = $this->boardMapper->findAllForUser($userId);
|
||||
$userBoards = $this->findAllBoardsFromUser($userId);
|
||||
$allDueCards = [];
|
||||
foreach ($userBoards as $userBoard) {
|
||||
$allDueCards[] = array_map(function ($card) use ($userBoard, $userId) {
|
||||
$this->enrich($card, $userId);
|
||||
return (new CardDetails($card, $userBoard))->jsonSerialize();
|
||||
$service = $this;
|
||||
$allDueCards[] = array_map(static function ($card) use ($service, $userBoard, $userId) {
|
||||
$service->enrich($card, $userId);
|
||||
$cardData = $card->jsonSerialize();
|
||||
$cardData['boardId'] = $userBoard->getId();
|
||||
return $cardData;
|
||||
}, $this->cardMapper->findAllWithDue($userBoard->getId()));
|
||||
}
|
||||
return array_merge(...$allDueCards);
|
||||
return $allDueCards;
|
||||
}
|
||||
|
||||
public function findUpcomingCards(string $userId): array {
|
||||
$userBoards = $this->boardMapper->findAllForUser($userId);
|
||||
$foundCards = [];
|
||||
$userBoards = $this->findAllBoardsFromUser($userId);
|
||||
$findCards = [];
|
||||
foreach ($userBoards as $userBoard) {
|
||||
$service = $this;
|
||||
|
||||
if (count($userBoard->getAcl()) === 0) {
|
||||
// private board: get cards with due date
|
||||
$cards = $this->cardMapper->findAllWithDue($userBoard->getId());
|
||||
$findCards[] = array_map(static function ($card) use ($service, $userBoard, $userId) {
|
||||
$service->enrich($card, $userId);
|
||||
$cardData = $card->jsonSerialize();
|
||||
$cardData['boardId'] = $userBoard->getId();
|
||||
return $cardData;
|
||||
}, $this->cardMapper->findAllWithDue($userBoard->getId()));
|
||||
} else {
|
||||
// shared board: get all my assigned or unassigned cards
|
||||
$cards = $this->cardMapper->findToMeOrNotAssignedCards($userBoard->getId(), $userId);
|
||||
$findCards[] = array_map(static function ($card) use ($service, $userBoard, $userId) {
|
||||
$service->enrich($card, $userId);
|
||||
$cardData = $card->jsonSerialize();
|
||||
$cardData['boardId'] = $userBoard->getId();
|
||||
return $cardData;
|
||||
}, $this->cardMapper->findToMeOrNotAssignedCards($userBoard->getId(), $userId));
|
||||
}
|
||||
|
||||
$foundCards[] = array_map(
|
||||
function (Card $card) use ($userBoard, $userId) {
|
||||
$this->enrich($card, $userId);
|
||||
return (new CardDetails($card, $userBoard))->jsonSerialize();
|
||||
},
|
||||
$cards
|
||||
);
|
||||
}
|
||||
return array_merge(...$foundCards);
|
||||
return $findCards;
|
||||
}
|
||||
|
||||
// FIXME: This is duplicate code with the board service
|
||||
private function findAllBoardsFromUser(string $userId): array {
|
||||
$userInfo = $this->getBoardPrerequisites($userId);
|
||||
$userBoards = $this->boardMapper->findAllByUser($userInfo['user'], null, null);
|
||||
$groupBoards = $this->boardMapper->findAllByGroups($userInfo['user'], $userInfo['groups'], null, null);
|
||||
$circleBoards = $this->boardMapper->findAllByCircles($userInfo['user'], null, null);
|
||||
return array_unique(array_merge($userBoards, $groupBoards, $circleBoards));
|
||||
}
|
||||
|
||||
private function getBoardPrerequisites($userId): array {
|
||||
$user = $this->userManager->get($userId);
|
||||
$groups = $user !== null ? $this->groupManager->getUserGroupIds($user) : [];
|
||||
return [
|
||||
'user' => $userId,
|
||||
'groups' => $groups
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
namespace OCA\Deck\Service;
|
||||
|
||||
use OCP\Cache\CappedMemoryCache;
|
||||
use OC\Cache\CappedMemoryCache;
|
||||
use OCA\Circles\Model\Member;
|
||||
use OCA\Deck\Db\Acl;
|
||||
use OCA\Deck\Db\AclMapper;
|
||||
@@ -63,8 +63,8 @@ class PermissionService {
|
||||
/** @var array */
|
||||
private $users = [];
|
||||
|
||||
private CappedMemoryCache $boardCache;
|
||||
private CappedMemoryCache $permissionCache;
|
||||
private $boardCache;
|
||||
private $permissionCache;
|
||||
|
||||
public function __construct(
|
||||
ILogger $logger,
|
||||
|
||||
@@ -30,32 +30,28 @@ use OCA\Deck\BadRequestException;
|
||||
use OCA\Deck\Db\Acl;
|
||||
use OCA\Deck\Db\AssignmentMapper;
|
||||
use OCA\Deck\Db\BoardMapper;
|
||||
use OCA\Deck\Db\Card;
|
||||
use OCA\Deck\Db\CardMapper;
|
||||
use OCA\Deck\Db\ChangeHelper;
|
||||
use OCA\Deck\Db\LabelMapper;
|
||||
use OCA\Deck\Db\Stack;
|
||||
use OCA\Deck\Db\StackMapper;
|
||||
use OCA\Deck\Model\CardDetails;
|
||||
use OCA\Deck\NoPermissionException;
|
||||
use OCA\Deck\StatusException;
|
||||
use OCA\Deck\Validators\StackServiceValidator;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class StackService {
|
||||
private StackMapper $stackMapper;
|
||||
private CardMapper $cardMapper;
|
||||
private BoardMapper $boardMapper;
|
||||
private LabelMapper $labelMapper;
|
||||
private PermissionService $permissionService;
|
||||
private BoardService $boardService;
|
||||
private CardService $cardService;
|
||||
private AssignmentMapper $assignedUsersMapper;
|
||||
private AttachmentService $attachmentService;
|
||||
private ActivityManager $activityManager;
|
||||
private ChangeHelper $changeHelper;
|
||||
private LoggerInterface $logger;
|
||||
private StackServiceValidator $stackServiceValidator;
|
||||
private $stackMapper;
|
||||
private $cardMapper;
|
||||
private $boardMapper;
|
||||
private $labelMapper;
|
||||
private $permissionService;
|
||||
private $boardService;
|
||||
private $cardService;
|
||||
private $assignedUsersMapper;
|
||||
private $attachmentService;
|
||||
private $activityManager;
|
||||
private $changeHelper;
|
||||
private $stackServiceValidator;
|
||||
|
||||
public function __construct(
|
||||
StackMapper $stackMapper,
|
||||
@@ -68,9 +64,8 @@ class StackService {
|
||||
AssignmentMapper $assignedUsersMapper,
|
||||
AttachmentService $attachmentService,
|
||||
ActivityManager $activityManager,
|
||||
ChangeHelper $changeHelper,
|
||||
LoggerInterface $logger,
|
||||
StackServiceValidator $stackServiceValidator
|
||||
StackServiceValidator $stackServiceValidator,
|
||||
ChangeHelper $changeHelper
|
||||
) {
|
||||
$this->stackMapper = $stackMapper;
|
||||
$this->boardMapper = $boardMapper;
|
||||
@@ -83,7 +78,6 @@ class StackService {
|
||||
$this->attachmentService = $attachmentService;
|
||||
$this->activityManager = $activityManager;
|
||||
$this->changeHelper = $changeHelper;
|
||||
$this->logger = $logger;
|
||||
$this->stackServiceValidator = $stackServiceValidator;
|
||||
}
|
||||
|
||||
@@ -94,13 +88,9 @@ class StackService {
|
||||
return;
|
||||
}
|
||||
|
||||
$cards = array_map(
|
||||
function (Card $card): CardDetails {
|
||||
$this->cardService->enrich($card);
|
||||
return new CardDetails($card);
|
||||
},
|
||||
$cards
|
||||
);
|
||||
foreach ($cards as $card) {
|
||||
$this->cardService->enrich($card);
|
||||
}
|
||||
|
||||
$stack->setCards($cards);
|
||||
}
|
||||
@@ -126,18 +116,12 @@ class StackService {
|
||||
|
||||
$this->permissionService->checkPermission($this->stackMapper, $stackId, Acl::PERMISSION_READ);
|
||||
$stack = $this->stackMapper->find($stackId);
|
||||
|
||||
$cards = array_map(
|
||||
function (Card $card): CardDetails {
|
||||
$assignedUsers = $this->assignedUsersMapper->findAll($card->getId());
|
||||
$card->setAssignedUsers($assignedUsers);
|
||||
$card->setAttachmentCount($this->attachmentService->count($card->getId()));
|
||||
|
||||
return new CardDetails($card);
|
||||
},
|
||||
$this->cardMapper->findAll($stackId)
|
||||
);
|
||||
|
||||
$cards = $this->cardMapper->findAll($stackId);
|
||||
foreach ($cards as $cardIndex => $card) {
|
||||
$assignedUsers = $this->assignedUsersMapper->findAll($card->getId());
|
||||
$card->setAssignedUsers($assignedUsers);
|
||||
$card->setAttachmentCount($this->attachmentService->count($card->getId()));
|
||||
}
|
||||
$stack->setCards($cards);
|
||||
|
||||
return $stack;
|
||||
@@ -166,7 +150,7 @@ class StackService {
|
||||
try {
|
||||
$this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_READ);
|
||||
} catch (NoPermissionException $e) {
|
||||
$this->logger->error('Unable to check permission for a previously obtained board ' . $boardId, ['exception' => $e]);
|
||||
\OC::$server->getLogger()->error('Unable to check permission for a previously obtained board ' . $boardId, ['exception' => $e]);
|
||||
return [];
|
||||
}
|
||||
return $this->stackMapper->findAll($boardId);
|
||||
@@ -326,7 +310,6 @@ class StackService {
|
||||
$this->permissionService->checkPermission($this->stackMapper, $id, Acl::PERMISSION_MANAGE);
|
||||
$stackToSort = $this->stackMapper->find($id);
|
||||
$stacks = $this->stackMapper->findAll($stackToSort->getBoardId());
|
||||
usort($stacks, static fn (Stack $stackA, Stack $stackB) => $stackA->getOrder() - $stackB->getOrder());
|
||||
$result = [];
|
||||
$i = 0;
|
||||
foreach ($stacks as $stack) {
|
||||
|
||||
@@ -65,16 +65,22 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
|
||||
|
||||
public const SHARE_TYPE_DECK_USER = IShare::TYPE_DECK_USER;
|
||||
|
||||
private IDBConnection $dbConnection;
|
||||
private IManager $shareManager;
|
||||
private AttachmentCacheHelper $attachmentCacheHelper;
|
||||
private BoardMapper $boardMapper;
|
||||
private CardMapper $cardMapper;
|
||||
private PermissionService $permissionService;
|
||||
private ITimeFactory $timeFactory;
|
||||
private IL10N $l;
|
||||
private IMimeTypeLoader $mimeTypeLoader;
|
||||
private ?string $userId;
|
||||
/** @var IDBConnection */
|
||||
private $dbConnection;
|
||||
/** @var IManager */
|
||||
private $shareManager;
|
||||
/** @var AttachmentCacheHelper */
|
||||
private $attachmentCacheHelper;
|
||||
/** @var BoardMapper */
|
||||
private $boardMapper;
|
||||
/** @var CardMapper */
|
||||
private $cardMapper;
|
||||
/** @var PermissionService */
|
||||
private $permissionService;
|
||||
/** @var ITimeFactory */
|
||||
private $timeFactory;
|
||||
/** @var IL10N */
|
||||
private $l;
|
||||
|
||||
public function __construct(
|
||||
IDBConnection $connection,
|
||||
@@ -83,10 +89,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
|
||||
CardMapper $cardMapper,
|
||||
PermissionService $permissionService,
|
||||
AttachmentCacheHelper $attachmentCacheHelper,
|
||||
IL10N $l,
|
||||
ITimeFactory $timeFactory,
|
||||
IMimeTypeLoader $mimeTypeLoader,
|
||||
?string $userId
|
||||
IL10N $l
|
||||
) {
|
||||
$this->dbConnection = $connection;
|
||||
$this->shareManager = $shareManager;
|
||||
@@ -94,10 +97,9 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
|
||||
$this->cardMapper = $cardMapper;
|
||||
$this->attachmentCacheHelper = $attachmentCacheHelper;
|
||||
$this->permissionService = $permissionService;
|
||||
|
||||
$this->l = $l;
|
||||
$this->timeFactory = $timeFactory;
|
||||
$this->mimeTypeLoader = $mimeTypeLoader;
|
||||
$this->userId = $userId;
|
||||
$this->timeFactory = \OC::$server->get(ITimeFactory::class);
|
||||
}
|
||||
|
||||
public static function register(IEventDispatcher $dispatcher): void {
|
||||
@@ -205,13 +207,13 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
|
||||
->setValue('file_target', $qb->createNamedParameter($target))
|
||||
->setValue('permissions', $qb->createNamedParameter($permissions))
|
||||
->setValue('token', $qb->createNamedParameter($token))
|
||||
->setValue('stime', $qb->createNamedParameter($this->timeFactory->getTime()));
|
||||
->setValue('stime', $qb->createNamedParameter(\OC::$server->get(ITimeFactory::class)->getTime()));
|
||||
|
||||
if ($expirationDate !== null) {
|
||||
$qb->setValue('expiration', $qb->createNamedParameter($expirationDate, 'datetime'));
|
||||
}
|
||||
|
||||
$qb->executeStatement();
|
||||
$qb->execute();
|
||||
|
||||
return $qb->getLastInsertId();
|
||||
}
|
||||
@@ -279,7 +281,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
|
||||
$entryData = $data;
|
||||
$entryData['permissions'] = $entryData['f_permissions'];
|
||||
$entryData['parent'] = $entryData['f_parent'];
|
||||
$share->setNodeCacheEntry(Cache::cacheEntryFromData($entryData, $this->mimeTypeLoader));
|
||||
$share->setNodeCacheEntry(Cache::cacheEntryFromData($entryData, \OC::$server->get(IMimeTypeLoader::class)));
|
||||
}
|
||||
return $share;
|
||||
}
|
||||
@@ -472,14 +474,14 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
|
||||
'file_target' => $qb->createNamedParameter($share->getTarget()),
|
||||
'permissions' => $qb->createNamedParameter($share->getPermissions()),
|
||||
'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
|
||||
])->executeStatement();
|
||||
])->execute();
|
||||
} else {
|
||||
// Already a userroom share. Update it.
|
||||
$qb = $this->dbConnection->getQueryBuilder();
|
||||
$qb->update('share')
|
||||
->set('file_target', $qb->createNamedParameter($share->getTarget()))
|
||||
->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id'])))
|
||||
->executeStatement();
|
||||
->execute();
|
||||
}
|
||||
|
||||
return $share;
|
||||
@@ -489,7 +491,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
|
||||
* @inheritDoc
|
||||
* @returns
|
||||
*/
|
||||
public function getSharesInFolder($userId, Folder $node, $reshares, $shallow = true) {
|
||||
public function getSharesInFolder($userId, Folder $node, $reshares) {
|
||||
$qb = $this->dbConnection->getQueryBuilder();
|
||||
$qb->select('*')
|
||||
->from('share', 's')
|
||||
@@ -516,11 +518,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
|
||||
}
|
||||
|
||||
$qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
|
||||
if ($shallow) {
|
||||
$qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
|
||||
} else {
|
||||
$qb->andWhere($qb->expr()->like('f.path', $qb->createNamedParameter($this->dbConnection->escapeLikeParameter($node->getInternalPath()) . '/%')));
|
||||
}
|
||||
$qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
|
||||
|
||||
$qb->orderBy('s.id');
|
||||
|
||||
@@ -634,8 +632,8 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
|
||||
$start = 0;
|
||||
while (true) {
|
||||
/** @var IShare[] $shareSlice */
|
||||
$shareSlice = array_slice($shares, $start, 1000);
|
||||
$start += 1000;
|
||||
$shareSlice = array_slice($shares, $start, 100);
|
||||
$start += 100;
|
||||
|
||||
if ($shareSlice === []) {
|
||||
break;
|
||||
@@ -714,15 +712,15 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
|
||||
* @return IShare[]
|
||||
*/
|
||||
public function getSharedWith($userId, $shareType, $node, $limit, $offset): array {
|
||||
$allBoards = $this->boardMapper->findBoardIds($userId);
|
||||
$allBoards = $this->boardMapper->findAllForUser($userId);
|
||||
|
||||
/** @var IShare[] $shares */
|
||||
$shares = [];
|
||||
|
||||
$start = 0;
|
||||
while (true) {
|
||||
$boards = array_slice($allBoards, $start, 1000);
|
||||
$start += 1000;
|
||||
$boards = array_slice($allBoards, $start, 100);
|
||||
$start += 100;
|
||||
|
||||
if ($boards === []) {
|
||||
break;
|
||||
@@ -752,6 +750,10 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
|
||||
$qb->andWhere($qb->expr()->eq('s.file_source', $qb->createNamedParameter($node->getId())));
|
||||
}
|
||||
|
||||
$boards = array_map(function (Board $board) {
|
||||
return $board->getId();
|
||||
}, $boards);
|
||||
|
||||
$qb->andWhere($qb->expr()->eq('s.share_type', $qb->createNamedParameter(IShare::TYPE_DECK)))
|
||||
->andWhere($qb->expr()->in('db.id', $qb->createNamedParameter(
|
||||
$boards,
|
||||
@@ -819,7 +821,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
|
||||
$qb->expr()->eq('s.item_type', $qb->createNamedParameter('folder'))
|
||||
));
|
||||
|
||||
$cursor = $qb->executeQuery();
|
||||
$cursor = $qb->execute();
|
||||
while ($data = $cursor->fetch()) {
|
||||
if (!$this->isAccessibleResult($data)) {
|
||||
continue;
|
||||
@@ -834,7 +836,9 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
|
||||
}
|
||||
$cursor->closeCursor();
|
||||
|
||||
return $this->resolveSharesForRecipient($shares, $this->userId);
|
||||
$shares = $this->resolveSharesForRecipient($shares, \OC::$server->getUserSession()->getUser()->getUID());
|
||||
|
||||
return $shares;
|
||||
}
|
||||
|
||||
public function isAccessibleResult(array $data): bool {
|
||||
@@ -937,7 +941,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
|
||||
$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
|
||||
$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
|
||||
));
|
||||
$cursor = $qb->executeQuery();
|
||||
$cursor = $qb->execute();
|
||||
|
||||
$users = [];
|
||||
while ($row = $cursor->fetch()) {
|
||||
|
||||
@@ -29,13 +29,14 @@ namespace OCA\Deck\Sharing;
|
||||
use OC\Files\Filesystem;
|
||||
use OCA\Deck\Service\ConfigService;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\Server;
|
||||
use OCP\Share\Events\VerifyMountPointEvent;
|
||||
use OCP\Share\IShare;
|
||||
use Symfony\Component\EventDispatcher\GenericEvent;
|
||||
|
||||
class Listener {
|
||||
private ConfigService $configService;
|
||||
|
||||
/** @var ConfigService */
|
||||
private $configService;
|
||||
|
||||
public function __construct(ConfigService $configService) {
|
||||
$this->configService = $configService;
|
||||
@@ -51,13 +52,13 @@ class Listener {
|
||||
|
||||
public static function listenPreShare(GenericEvent $event): void {
|
||||
/** @var self $listener */
|
||||
$listener = Server::get(self::class);
|
||||
$listener = \OC::$server->query(self::class);
|
||||
$listener->overwriteShareTarget($event);
|
||||
}
|
||||
|
||||
public static function listenVerifyMountPointEvent(VerifyMountPointEvent $event): void {
|
||||
/** @var self $listener */
|
||||
$listener = Server::get(self::class);
|
||||
$listener = \OC::$server->query(self::class);
|
||||
$listener->overwriteMountPoint($event);
|
||||
}
|
||||
|
||||
|
||||
14164
package-lock.json
generated
62
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "deck",
|
||||
"description": "",
|
||||
"version": "1.8.5",
|
||||
"version": "1.7.4",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Julius Härtl",
|
||||
@@ -29,37 +29,35 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/polyfill": "^7.12.1",
|
||||
"@babel/runtime": "^7.19.0",
|
||||
"@babel/runtime": "^7.17.9",
|
||||
"@juliushaertl/vue-richtext": "^1.0.1",
|
||||
"@nextcloud/auth": "^2.0.0",
|
||||
"@nextcloud/axios": "^2.0.0",
|
||||
"@nextcloud/dialogs": "^3.2.0",
|
||||
"@nextcloud/event-bus": "^3.0.2",
|
||||
"@nextcloud/auth": "^1.3.0",
|
||||
"@nextcloud/axios": "^1.9.0",
|
||||
"@nextcloud/dialogs": "^3.1.2",
|
||||
"@nextcloud/event-bus": "^2.1.1",
|
||||
"@nextcloud/files": "^2.1.0",
|
||||
"@nextcloud/initial-state": "^2.0.0",
|
||||
"@nextcloud/l10n": "^1.6.0",
|
||||
"@nextcloud/moment": "^1.2.1",
|
||||
"@nextcloud/initial-state": "^1.2.1",
|
||||
"@nextcloud/l10n": "^1.4.1",
|
||||
"@nextcloud/moment": "^1.2.0",
|
||||
"@nextcloud/router": "^2.0.0",
|
||||
"@nextcloud/vue": "^7.3.0",
|
||||
"@nextcloud/vue": "^5.3.1",
|
||||
"@nextcloud/vue-dashboard": "^2.0.1",
|
||||
"@nextcloud/vue-richtext": "^2.0.1",
|
||||
"blueimp-md5": "^2.19.0",
|
||||
"dompurify": "^2.4.0",
|
||||
"dompurify": "^2.3.6",
|
||||
"lodash": "^4.17.21",
|
||||
"markdown-it": "^13.0.1",
|
||||
"markdown-it-link-attributes": "^4.0.1",
|
||||
"markdown-it": "^12.3.2",
|
||||
"markdown-it-link-attributes": "^4.0.0",
|
||||
"markdown-it-task-checkbox": "^1.0.6",
|
||||
"moment": "^2.29.4",
|
||||
"nextcloud-vue-collections": "^0.10.0",
|
||||
"p-queue": "^7.3.0",
|
||||
"moment": "^2.29.2",
|
||||
"nextcloud-vue-collections": "^0.9.0",
|
||||
"p-queue": "^6.6.2",
|
||||
"url-search-params-polyfill": "^8.1.1",
|
||||
"vue": "^2.7.9",
|
||||
"vue-at": "^2.5.0",
|
||||
"vue": "^2.6.14",
|
||||
"vue-at": "^2.5.0-beta.2",
|
||||
"vue-click-outside": "^1.1.0",
|
||||
"vue-easymde": "^2.0.0",
|
||||
"vue-infinite-loading": "^2.4.5",
|
||||
"vue-material-design-icons": "^5.1.2",
|
||||
"vue-router": "^3.6.5",
|
||||
"vue-router": "^3.5.3",
|
||||
"vue-smooth-dnd": "^0.8.1",
|
||||
"vuex": "^3.6.2",
|
||||
"vuex-router-sync": "^5.0.0"
|
||||
@@ -68,24 +66,20 @@
|
||||
"extends @nextcloud/browserslist-config"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^16.0.0",
|
||||
"npm": "^7.0.0 || ^8.0.0"
|
||||
"node": "^14.0.0",
|
||||
"npm": "^7.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nextcloud/babel-config": "^1.0.0",
|
||||
"@nextcloud/browserslist-config": "^2.3.0",
|
||||
"@nextcloud/eslint-config": "^8.0.0",
|
||||
"@nextcloud/stylelint-config": "^2.2.0",
|
||||
"@nextcloud/webpack-vue-config": "^5.3.0",
|
||||
"@relative-ci/agent": "^4.1.0",
|
||||
"@nextcloud/browserslist-config": "^2.2.0",
|
||||
"@nextcloud/eslint-config": "^6.1.2",
|
||||
"@nextcloud/stylelint-config": "^2.1.2",
|
||||
"@nextcloud/webpack-vue-config": "^5.0.0",
|
||||
"@relative-ci/agent": "^3.1.2",
|
||||
"@vue/test-utils": "^1.3.0",
|
||||
"cypress": "^10.8.0",
|
||||
"eslint-webpack-plugin": "^3.2.0",
|
||||
"jest": "^29.0.1",
|
||||
"jest": "^27.5.1",
|
||||
"jest-serializer-vue": "^2.0.2",
|
||||
"stylelint-webpack-plugin": "^3.3.0",
|
||||
"vue-jest": "^3.0.7",
|
||||
"vue-template-compiler": "^2.7.9"
|
||||
"vue-jest": "^3.0.7"
|
||||
},
|
||||
"jest": {
|
||||
"moduleFileExtensions": [
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
|
||||
errorBaseline="tests/psalm-baseline.xml"
|
||||
>
|
||||
<stubs>
|
||||
<file name="tests/stub.phpstub" preloadClasses="true"/>
|
||||
</stubs>
|
||||
<projectFiles>
|
||||
<directory name="lib" />
|
||||
<ignoreFiles>
|
||||
|
||||
59
src/App.vue
@@ -21,13 +21,14 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<NcContent app-name="deck" :class="{ 'nav-hidden': !navShown, 'sidebar-hidden': !sidebarRouterView }">
|
||||
<Content id="content" app-name="deck" :class="{ 'nav-hidden': !navShown, 'sidebar-hidden': !sidebarRouterView }">
|
||||
<AppNavigation />
|
||||
<NcAppContent>
|
||||
<AppContent>
|
||||
<router-view />
|
||||
</NcAppContent>
|
||||
</AppContent>
|
||||
|
||||
<NcModal v-if="cardDetailsInModal && $route.params.cardId"
|
||||
<Modal
|
||||
v-if="cardDetailsInModal && $route.params.cardId"
|
||||
:clear-view-delay="0"
|
||||
:title="t('deck', 'Card details')"
|
||||
size="large"
|
||||
@@ -35,17 +36,18 @@
|
||||
<div class="modal__content modal__card">
|
||||
<router-view name="sidebar" />
|
||||
</div>
|
||||
</NcModal>
|
||||
</Modal>
|
||||
|
||||
<router-view name="sidebar" :visible="!cardDetailsInModal || !$route.params.cardId" />
|
||||
</NcContent>
|
||||
</Content>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { mapState } from 'vuex'
|
||||
import AppNavigation from './components/navigation/AppNavigation.vue'
|
||||
import { NcModal, NcContent, NcAppContent } from '@nextcloud/vue'
|
||||
import { BoardApi } from './services/BoardApi.js'
|
||||
import AppNavigation from './components/navigation/AppNavigation'
|
||||
import { Modal, Content, AppContent } from '@nextcloud/vue'
|
||||
import { BoardApi } from './services/BoardApi'
|
||||
import { emit, subscribe } from '@nextcloud/event-bus'
|
||||
|
||||
const boardApi = new BoardApi()
|
||||
@@ -54,9 +56,9 @@ export default {
|
||||
name: 'App',
|
||||
components: {
|
||||
AppNavigation,
|
||||
NcModal,
|
||||
NcContent,
|
||||
NcAppContent,
|
||||
Modal,
|
||||
Content,
|
||||
AppContent,
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
@@ -128,7 +130,7 @@ export default {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
#content-vue {
|
||||
#content {
|
||||
#app-content {
|
||||
transition: margin-left 100ms ease;
|
||||
position: relative;
|
||||
@@ -156,37 +158,6 @@ export default {
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../css/print';
|
||||
|
||||
.icon-activity {
|
||||
background-image: url(../img/activity-dark.svg);
|
||||
|
||||
body[data-theme-dark] & {
|
||||
background-image: url(../img/activity.svg);
|
||||
}
|
||||
}
|
||||
|
||||
.avatardiv.circles {
|
||||
background: var(--color-primary);
|
||||
}
|
||||
|
||||
.icon-circles {
|
||||
background-image: url(../img/circles-dark.svg);
|
||||
opacity: 1;
|
||||
background-size: 20px;
|
||||
background-position: center center;
|
||||
}
|
||||
|
||||
.icon-circles-white, .icon-circles.icon-white {
|
||||
background-image: url(../img/circles.svg);
|
||||
opacity: 1;
|
||||
background-size: 20px;
|
||||
background-position: center center;
|
||||
}
|
||||
|
||||
.icon-colorpicker {
|
||||
background-image: url('../img/color_picker.svg');
|
||||
}
|
||||
|
||||
.multiselect {
|
||||
width: 100%;
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<NcModal @close="close">
|
||||
<Modal @close="close">
|
||||
<div id="modal-inner" :class="{ 'icon-loading': loading }">
|
||||
<h1>{{ t('deck', 'Select the board to link to a project') }}</h1>
|
||||
<input v-model="filter" type="text" :placeholder="t('deck', 'Search by board title')">
|
||||
@@ -38,17 +38,17 @@
|
||||
{{ t('deck', 'Select board') }}
|
||||
</button>
|
||||
</div>
|
||||
</NcModal>
|
||||
</Modal>
|
||||
</template>
|
||||
<script>
|
||||
import { NcModal } from '@nextcloud/vue'
|
||||
import Modal from '@nextcloud/vue/dist/Components/Modal'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
|
||||
export default {
|
||||
name: 'BoardSelector',
|
||||
components: {
|
||||
NcModal,
|
||||
Modal,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
@@ -21,11 +21,11 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<NcModal class="card-selector" @close="close">
|
||||
<Modal class="card-selector" @close="close">
|
||||
<div class="modal-scroller">
|
||||
<div v-if="!creating && !created" id="modal-inner" :class="{ 'icon-loading': loading }">
|
||||
<h3>{{ t('deck', 'Create a new card') }}</h3>
|
||||
<NcMultiselect v-model="selectedBoard"
|
||||
<Multiselect v-model="selectedBoard"
|
||||
:placeholder="t('deck', 'Select a board')"
|
||||
:options="boards"
|
||||
:disabled="loading"
|
||||
@@ -44,9 +44,9 @@
|
||||
<span>{{ props.option.title }}</span>
|
||||
</span>
|
||||
</template>
|
||||
</NcMultiselect>
|
||||
</Multiselect>
|
||||
|
||||
<NcMultiselect v-model="selectedStack"
|
||||
<Multiselect v-model="selectedStack"
|
||||
:placeholder="t('deck', 'Select a list')"
|
||||
:options="stacksFromBoard"
|
||||
:max-height="100"
|
||||
@@ -71,22 +71,12 @@
|
||||
</div>
|
||||
</div>
|
||||
<div v-else id="modal-inner">
|
||||
<NcEmptyContent v-if="creating">
|
||||
<template #icon>
|
||||
<NcLoadingIcon />
|
||||
</template>
|
||||
<template #title>
|
||||
{{ t('deck', 'Creating the new card …') }}
|
||||
</template>
|
||||
</NcEmptyContent>
|
||||
<NcEmptyContent v-else-if="created">
|
||||
<template #icon>
|
||||
<CardPlusOutline />
|
||||
</template>
|
||||
<template #title>
|
||||
{{ t('deck', 'Card "{card}" was added to "{board}"', { card: pendingTitle, board: selectedBoard.title }) }}
|
||||
</template>
|
||||
<template #action>
|
||||
<EmptyContent v-if="creating" icon="icon-loading">
|
||||
{{ t('deck', 'Creating the new card …') }}
|
||||
</EmptyContent>
|
||||
<EmptyContent v-else-if="created" icon="icon-checkmark">
|
||||
{{ t('deck', 'Card "{card}" was added to "{board}"', { card: pendingTitle, board: selectedBoard.title }) }}
|
||||
<template #desc>
|
||||
<button class="primary" @click="openNewCard">
|
||||
{{ t('deck', 'Open card') }}
|
||||
</button>
|
||||
@@ -94,29 +84,28 @@
|
||||
{{ t('deck', 'Close') }}
|
||||
</button>
|
||||
</template>
|
||||
</NcEmptyContent>
|
||||
</EmptyContent>
|
||||
</div>
|
||||
</div>
|
||||
</NcModal>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import { NcModal, NcMultiselect, NcEmptyContent, NcLoadingIcon } from '@nextcloud/vue'
|
||||
import CardPlusOutline from 'vue-material-design-icons/CardPlusOutline.vue'
|
||||
import Modal from '@nextcloud/vue/dist/Components/Modal'
|
||||
import Multiselect from '@nextcloud/vue/dist/Components/Multiselect'
|
||||
import EmptyContent from '@nextcloud/vue/dist/Components/EmptyContent'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { CardApi } from './services/CardApi.js'
|
||||
import { CardApi } from './services/CardApi'
|
||||
|
||||
const cardApi = new CardApi()
|
||||
|
||||
export default {
|
||||
name: 'CardCreateDialog',
|
||||
components: {
|
||||
NcEmptyContent,
|
||||
NcModal,
|
||||
NcMultiselect,
|
||||
NcLoadingIcon,
|
||||
CardPlusOutline,
|
||||
EmptyContent,
|
||||
Modal,
|
||||
Multiselect,
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
@@ -216,7 +205,6 @@ export default {
|
||||
max-width: 400px;
|
||||
padding: 10px;
|
||||
min-height: 200px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.multiselect-board, .multiselect-list, input, textarea {
|
||||
|
||||
@@ -21,10 +21,10 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<NcModal class="card-selector" @close="close">
|
||||
<Modal class="card-selector" @close="close">
|
||||
<div id="modal-inner" :class="{ 'icon-loading': loading }">
|
||||
<h3>{{ title }}</h3>
|
||||
<NcMultiselect v-model="selectedBoard"
|
||||
<Multiselect v-model="selectedBoard"
|
||||
:placeholder="t('deck', 'Select a board')"
|
||||
:options="boards"
|
||||
:disabled="loading"
|
||||
@@ -42,9 +42,9 @@
|
||||
<span>{{ props.option.title }}</span>
|
||||
</span>
|
||||
</template>
|
||||
</NcMultiselect>
|
||||
</Multiselect>
|
||||
|
||||
<NcMultiselect v-model="selectedCard"
|
||||
<Multiselect v-model="selectedCard"
|
||||
:placeholder="t('deck', 'Select a card')"
|
||||
:options="cardsFromBoard"
|
||||
:disabled="loading || selectedBoard === ''"
|
||||
@@ -57,19 +57,20 @@
|
||||
{{ t('deck', 'Cancel') }}
|
||||
</button>
|
||||
</div>
|
||||
</NcModal>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import { NcModal, NcMultiselect } from '@nextcloud/vue'
|
||||
import Modal from '@nextcloud/vue/dist/Components/Modal'
|
||||
import Multiselect from '@nextcloud/vue/dist/Components/Multiselect'
|
||||
import axios from '@nextcloud/axios'
|
||||
|
||||
export default {
|
||||
name: 'CardSelector',
|
||||
components: {
|
||||
NcModal,
|
||||
NcMultiselect,
|
||||
Modal,
|
||||
Multiselect,
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
|
||||
@@ -36,10 +36,10 @@
|
||||
|
||||
<script>
|
||||
import RichText from '@juliushaertl/vue-richtext'
|
||||
import { NcUserBubble } from '@nextcloud/vue'
|
||||
import { UserBubble } from '@nextcloud/vue'
|
||||
import moment from '@nextcloud/moment'
|
||||
import DOMPurify from 'dompurify'
|
||||
import relativeDate from '../mixins/relativeDate.js'
|
||||
import relativeDate from '../mixins/relativeDate'
|
||||
|
||||
const InternalLink = {
|
||||
name: 'InternalLink',
|
||||
@@ -93,7 +93,7 @@ export default {
|
||||
break
|
||||
case 'user':
|
||||
parameters[key] = {
|
||||
component: NcUserBubble,
|
||||
component: UserBubble,
|
||||
props: {
|
||||
user: parameters[key].id,
|
||||
displayName: parameters[key].name,
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
<script>
|
||||
import axios from '@nextcloud/axios'
|
||||
import { generateOcsUrl } from '@nextcloud/router'
|
||||
import ActivityEntry from './ActivityEntry.vue'
|
||||
import ActivityEntry from './ActivityEntry'
|
||||
import InfiniteLoading from 'vue-infinite-loading'
|
||||
|
||||
const ACTIVITY_FETCH_LIMIT = 50
|
||||
|
||||
@@ -27,21 +27,24 @@
|
||||
@drop.prevent="handleDropFiles">
|
||||
<slot />
|
||||
<transition name="fade" mode="out-in">
|
||||
<div v-show="isDraggingOver"
|
||||
<div
|
||||
v-show="isDraggingOver"
|
||||
class="dragover">
|
||||
<div class="drop-hint">
|
||||
<div class="drop-hint__icon"
|
||||
<div
|
||||
class="drop-hint__icon"
|
||||
:class="{
|
||||
'icon-upload' : !isReadOnly,
|
||||
'icon-error' : isReadOnly}" />
|
||||
<h2 class="drop-hint__text">
|
||||
<h2
|
||||
class="drop-hint__text">
|
||||
{{ dropHintText }}
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<NcModal v-if="modalShow" :title="t('deck', 'File already exists')" @close="modalShow=false">
|
||||
<Modal v-if="modalShow" :title="t('deck', 'File already exists')" @close="modalShow=false">
|
||||
<div class="modal__content">
|
||||
<h2>{{ t('deck', 'File already exists') }}</h2>
|
||||
<p>
|
||||
@@ -57,13 +60,13 @@
|
||||
{{ t('deck', 'Keep existing file') }}
|
||||
</button>
|
||||
</div>
|
||||
</NcModal>
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { NcModal } from '@nextcloud/vue'
|
||||
import attachmentUpload from '../mixins/attachmentUpload.js'
|
||||
import { Modal } from '@nextcloud/vue'
|
||||
import attachmentUpload from '../mixins/attachmentUpload'
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
|
||||
let maxUploadSizeState
|
||||
@@ -75,7 +78,7 @@ try {
|
||||
|
||||
export default {
|
||||
name: 'AttachmentDragAndDrop',
|
||||
components: { NcModal },
|
||||
components: { Modal },
|
||||
mixins: [attachmentUpload],
|
||||
props: {
|
||||
cardId: {
|
||||
|
||||