Compare commits

..

50 Commits

Author SHA1 Message Date
Julius Härtl
105ffd597b Bump version to 0.3.1
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2018-03-07 17:46:16 +01:00
Julius Härtl
ae9d02121b Test stable-0.3 against NC13
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2018-03-07 17:36:54 +01:00
Nextcloud bot
61ff25106b [tx-robot] updated from transifex 2018-03-07 17:34:59 +01:00
Nextcloud bot
c449967382 [tx-robot] updated from transifex 2018-03-07 17:34:51 +01:00
Nextcloud bot
b6ad790006 [tx-robot] updated from transifex 2018-03-07 17:34:42 +01:00
Julius Härtl
f8ec7202c8 Fix translations of the delete confirmation
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2018-03-07 17:34:32 +01:00
Julius Härtl
a0ffc3a47f Make 0.3.1-rc1
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2018-03-02 19:33:02 +01:00
Julius Härtl
bb3b31f38f No typehinting in deck yet
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2018-03-02 19:31:43 +01:00
Julius Härtl
cae999ec80 Fix tests
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2018-03-02 19:31:43 +01:00
Julius Härtl
9273d15865 Update view when removing sharees and track by correct property
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2018-03-02 19:31:43 +01:00
Julius Härtl
5d40ecad25 Remove deleted users from assignments
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2018-03-02 19:31:42 +01:00
Nextcloud bot
5e4b3b9218 [tx-robot] updated from transifex 2018-03-02 18:01:21 +01:00
Nextcloud bot
9f154e6ab1 [tx-robot] updated from transifex 2018-03-02 18:01:14 +01:00
Julius Härtl
cdb3193d9c Disable integration tests for now
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2018-03-02 17:59:24 +01:00
Julius Härtl
0200179a76 Do not overwrite emtpy values
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2018-03-02 17:59:24 +01:00
Julius Härtl
b1863980d6 Track by correct property
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2018-03-02 17:59:23 +01:00
Julius Härtl
087d60776d Only return updated properties not resolvable ones
fixes #406

Signed-off-by: Julius Härtl <jus@bitgrid.net>
2018-03-02 17:59:23 +01:00
Julius Härtl
e859c6aa56 Fix popover position of assigned users
fixes #385

Signed-off-by: Julius Härtl <jus@bitgrid.net>
2018-03-02 17:59:23 +01:00
Julius Härtl
6a098dd981 Add confirmation dialog to delete options
this is only a temporary solution but it will probably safe some users from deleting their data by accident

Signed-off-by: Julius Härtl <jus@bitgrid.net>
2018-03-02 17:58:09 +01:00
Julius Härtl
7b47414a54 Add changes to CHANGELOG.md
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2018-02-25 11:07:17 +01:00
Julius Härtl
07ec3fd7d0 Add 0.3.1 version/dependencies
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2018-02-25 11:02:25 +01:00
Nextcloud bot
0af3c87d91 [tx-robot] updated from transifex 2018-02-25 11:01:10 +01:00
Nextcloud bot
c789a6356a [tx-robot] updated from transifex 2018-02-25 11:01:10 +01:00
Nextcloud bot
09c3f4e46d [tx-robot] updated from transifex 2018-02-25 11:01:09 +01:00
Nextcloud bot
a72a11584d [tx-robot] updated from transifex 2018-02-25 11:01:09 +01:00
Nextcloud bot
c850e8b1c2 [tx-robot] updated from transifex 2018-02-25 11:01:08 +01:00
Nextcloud bot
6b06f4e5e8 [tx-robot] updated from transifex 2018-02-25 11:01:08 +01:00
Nextcloud bot
52db1dc37c [tx-robot] updated from transifex 2018-02-25 11:01:07 +01:00
Nextcloud bot
8837d89503 [tx-robot] updated from transifex 2018-02-25 11:01:07 +01:00
Nextcloud bot
640db050bd [tx-robot] updated from transifex 2018-02-25 11:01:06 +01:00
Nextcloud bot
0c4ae1b913 [tx-robot] updated from transifex 2018-02-25 11:01:06 +01:00
Nextcloud bot
4b5ac63d2b [tx-robot] updated from transifex 2018-02-25 11:01:05 +01:00
Nextcloud bot
1a9c5225e1 [tx-robot] updated from transifex 2018-02-25 11:01:05 +01:00
Nextcloud bot
b74916d1f2 [tx-robot] updated from transifex 2018-02-25 11:01:04 +01:00
Nextcloud bot
86bcedd3ca [tx-robot] updated from transifex 2018-02-25 11:01:04 +01:00
Nextcloud bot
e998c3d927 [tx-robot] updated from transifex 2018-02-25 11:01:04 +01:00
Nextcloud bot
43241921e5 [tx-robot] updated from transifex 2018-02-25 11:01:03 +01:00
Nextcloud bot
13cf7fe7f2 [tx-robot] updated from transifex 2018-02-25 11:01:02 +01:00
Julius Härtl
475ecc3c2a Force limit of users that do not match exactly
fixes #422

Signed-off-by: Julius Härtl <jus@bitgrid.net>
2018-02-25 10:57:50 +01:00
Julius Härtl
c1425f7a4b Update user list when acl is changed
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2018-02-25 10:57:20 +01:00
Julius Härtl
111ff4ae7f Use actual acl list for assigning users
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2018-02-25 10:57:20 +01:00
Julius Härtl
b7ac7067ff Fix bug when user selection was staying hidden
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2018-02-25 10:57:20 +01:00
Julius Härtl
3d634ffdcb Only digest on card update not for every interval
and add various track by statements

Signed-off-by: Julius Härtl <jus@bitgrid.net>
2018-02-25 10:54:56 +01:00
Julius Härtl
30eeaf8ffd Add Repo as homepage (fixes #389)
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2018-02-25 10:53:45 +01:00
Julius Härtl
6594a3f5da Merge branch 'fix-app-compatibility' into stable-0.3 2018-02-25 10:53:16 +01:00
Julius Härtl
58df42ea9c Bump drone images (#391)
* Bump drone images

Signed-off-by: Julius Härtl <jus@bitgrid.net>

* Use travis for codecov

Signed-off-by: Julius Härtl <jus@bitgrid.net>

* Just test against master in travis

Signed-off-by: Julius Härtl <jus@bitgrid.net>
2018-01-13 12:29:29 +01:00
cloud2018
169175ab55 translate attributes in templates/part.card.php
translate attributes title and placeholder in class "section-content card-details-assign-users" in templates/part.card.php
2018-01-13 12:28:47 +01:00
Julius Härtl
f2b94f0933 Just test against master in travis
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2018-01-13 11:48:21 +01:00
Julius Härtl
2fece55b0e Use travis for codecov
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2018-01-13 11:37:48 +01:00
Julius Härtl
f34fae640d Bump drone images
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2018-01-13 10:39:40 +01:00
495 changed files with 12448 additions and 62765 deletions

View File

@@ -1,65 +1,113 @@
kind: pipeline
name: checkers
steps:
- name: compatibility
image: nextcloudci/php7.2:php7.2-13
environment:
APP_NAME: deck
CORE_BRANCH: master
DB: sqlite
commands:
# Pre-setup steps
- wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh
- bash ./before_install.sh $APP_NAME $CORE_BRANCH $DB
- cd ../server
# Code checker
- ./occ app:check-code $APP_NAME -c strong-comparison
- ./occ app:check-code $APP_NAME -c deprecation
- cd apps/$APP_NAME/
- name: syntax-php7.2
image: nextcloudci/php7.2:php7.2-13
environment:
APP_NAME: deck
CORE_BRANCH: master
DB: sqlite
commands:
- composer install
- ./vendor/bin/parallel-lint --exclude ./vendor/ .
- name: syntax-php7.3
image: nextcloudci/php7.3:php7.3-2
environment:
APP_NAME: deck
CORE_BRANCH: master
DB: sqlite
commands:
- composer install
- ./vendor/bin/parallel-lint --exclude ./vendor/ .
- name: syntax-php7.4
image: nextcloudci/php7.4:2
environment:
APP_NAME: deck
CORE_BRANCH: master
DB: sqlite
commands:
clone:
git:
image: plugins/git
depth: 1
pipeline:
check-app-compatbility:
image: nextcloudci/php7.0:php7.0-17
environment:
- APP_NAME=deck
- CORE_BRANCH=stable13
- DB=sqlite
commands:
# Pre-setup steps
- wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh
- bash ./before_install.sh $APP_NAME $CORE_BRANCH $DB
- cd ../server
# Code checker
- ./occ app:check-code $APP_NAME -c strong-comparison
- ./occ app:check-code $APP_NAME -c deprecation
- cd apps/$APP_NAME/
when:
matrix:
TESTS: check-app-compatbility
signed-off-check:
image: nextcloudci/php7.0:php7.0-17
environment:
- APP_NAME=deck
- CORE_BRANCH=stable13
- DB=sqlite
commands:
- wget https://raw.githubusercontent.com/nextcloud/server/master/build/signed-off-checker.php
- php ./signed-off-checker.php
when:
matrix:
TESTS: signed-off-check
syntax-php5.6:
image: nextcloudci/php5.6:php5.6-8
environment:
- APP_NAME=deck
- CORE_BRANCH=stable13
- DB=sqlite
commands:
- composer install
- ./vendor/bin/parallel-lint --exclude ./vendor/ .
trigger:
branch:
- master
- stable*
event:
- pull_request
- push
---
kind: pipeline
name: unit-php7.2
steps:
- name: php7.2
image: nextcloudci/php7.2:php7.2-13
when:
matrix:
TESTS: syntax-php5.6
syntax-php7.0:
image: nextcloudci/php7.0:php7.0-17
environment:
APP_NAME: deck
CORE_BRANCH: master
DB: sqlite
- APP_NAME=deck
- CORE_BRANCH=stable13
- DB=sqlite
commands:
- composer install
- ./vendor/bin/parallel-lint --exclude ./vendor/ .
when:
matrix:
TESTS: syntax-php7.0
syntax-php7.1:
image: nextcloudci/php7.1:php7.1-15
environment:
- APP_NAME=deck
- CORE_BRANCH=stable13
- DB=sqlite
commands:
- composer install
- ./vendor/bin/parallel-lint --exclude ./vendor/ .
when:
matrix:
TESTS: syntax-php7.1
syntax-php7.2:
image: nextcloudci/php7.2:php7.2-9
environment:
- APP_NAME=deck
- CORE_BRANCH=stable13
- DB=sqlite
commands:
- composer install
- ./vendor/bin/parallel-lint --exclude ./vendor/ .
when:
matrix:
TESTS: syntax-php7.2
php5.6:
image: nextcloudci/php5.6:php5.6-8
environment:
- APP_NAME=deck
- CORE_BRANCH=stable13
- DB=sqlite
commands:
- apt update && apt-get -y install php5-xdebug
# Pre-setup steps
- wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh
- bash ./before_install.sh $APP_NAME $CORE_BRANCH $DB
- cd ../server/
- ./occ app:enable $APP_NAME
- cd apps/$APP_NAME
- phpunit -c tests/phpunit.xml --coverage-clover build/php-unit.coverage.xml
- phpunit -c tests/phpunit.integration.xml --coverage-clover build/php-integration.coverage.xml
when:
matrix:
TESTS: php5.6
php7.0:
image: nextcloudci/php7.0:php7.0-17
environment:
- APP_NAME=deck
- CORE_BRANCH=stable13
- DB=sqlite
commands:
# Pre-setup steps
- wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh
@@ -67,26 +115,18 @@ steps:
- cd ../server/
- php occ app:enable deck
- cd apps/$APP_NAME
- composer install
# Run phpunit tests
- phpunit -c tests/phpunit.xml --coverage-clover build/php-unit.coverage.xml
- phpunit -c tests/phpunit.integration.xml --coverage-clover build/php-integration.coverage.xml
trigger:
branch:
- master
- stable*
event:
- pull_request
- push
---
kind: pipeline
name: unit-php7.3
steps:
- name: php7.3
image: nextcloudci/php7.3:php7.3-5
when:
matrix:
TESTS: php7.0
php7.1:
image: nextcloudci/php7.1:php7.1-15
environment:
APP_NAME: deck
CORE_BRANCH: master
DB: sqlite
- APP_NAME=deck
- CORE_BRANCH=stable13
- DB=sqlite
commands:
# Pre-setup steps
- wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh
@@ -94,26 +134,35 @@ steps:
- cd ../server/
- php occ app:enable deck
- cd apps/$APP_NAME
- composer install
- phpunit -c tests/phpunit.xml --coverage-clover build/php-unit.coverage.xml
- phpunit -c tests/phpunit.integration.xml --coverage-clover build/php-integration.coverage.xml
trigger:
branch:
- master
- stable*
event:
- pull_request
- push
---
kind: pipeline
name: integration
steps:
- name: integration
image: nextcloudci/php7.2:php7.2-13
when:
matrix:
TESTS: php7.1
php7.2:
image: nextcloudci/php7.2:php7.2-9
environment:
APP_NAME: deck
CORE_BRANCH: master
DB: sqlite
- APP_NAME=deck
- CORE_BRANCH=stable13
- DB=sqlite
commands:
# Pre-setup steps
- wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh
- bash ./before_install.sh $APP_NAME $CORE_BRANCH $DB
- cd ../server/
- php occ app:enable deck
- cd apps/$APP_NAME
- phpunit -c tests/phpunit.xml --coverage-clover build/php-unit.coverage.xml
- phpunit -c tests/phpunit.integration.xml --coverage-clover build/php-integration.coverage.xml
when:
matrix:
TESTS: php7.2
integration:
image: nextcloudci/integration-php7.0:integration-php7.0-6
environment:
- APP_NAME=deck
- CORE_BRANCH=stable13
- DB=sqlite
commands:
# Pre-setup steps
- wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh
@@ -122,11 +171,43 @@ steps:
- php occ app:enable deck
- cd apps/$APP_NAME
- cd tests/integration
- ./run.sh || true
trigger:
branch:
- master
- stable*
event:
- pull_request
- push
- ./run.sh
when:
matrix:
TESTS: integration
eslint:
image: nextcloudci/eslint:eslint-1
commands:
- ./run-eslint.sh
when:
matrix:
TESTS: eslint
jsbuild:
image: mhart/alpine-node:6.8.0
commands:
- apk add --no-cache git
- cd js
- npm install --deps
- ./node_modules/.bin/bower --allow-root install
when:
matrix:
TESTS: jsbuild
matrix:
include:
- TESTS: check-app-compatbility
- TESTS: signed-off-check
- TESTS: syntax-php5.6
- TESTS: syntax-php7.0
- TESTS: syntax-php7.1
- TESTS: syntax-php7.2
- TESTS: php5.6
- TESTS: php7.0
- TESTS: php7.1
- TESTS: php7.2
- TESTS: eslint
- TESTS: jsbuild
#- TESTS: integration
branches: [ master, stable* ]

View File

@@ -1,9 +0,0 @@
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
[*.{js,vue}]
indent_style = tab

View File

@@ -1,6 +1,5 @@
/js/tests/*
/js/vendor/*
/js/legacy/*
/js/node_modules/*
/js/public/*
/karma.conf.js

View File

@@ -1,8 +0,0 @@
module.exports = {
extends: [
'@nextcloud'
],
rules: {
'valid-jsdoc': ['off'],
}
}

37
.eslintrc.yml Normal file
View File

@@ -0,0 +1,37 @@
root: true
extends:
- eslint:recommended
env:
browser: true
amd: true
globals:
app: false
angular: false
$: false
escapeHTML: false
OC: false
OCA: false
t: false
oc_current_user: false
oc_requesttoken: false
Clipboard: false
oc_defaults: false
rules:
curly: error
eqeqeq: ["error", "smart"]
guard-for-in: error
no-console: off
no-fallthrough: error
no-mixed-spaces-and-tabs: error
no-unused-vars: warn
no-use-before-define: error
semi: ["error", "always"]
indent:
- error
- tab
- SwitchCase: 1

View File

@@ -1,120 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
---
<!--
Thanks for reporting issues back!
Guidelines for submitting issues:
* Please search the existing issues first, it's likely that your issue was already reported or even fixed.
* SECURITY: Report any potential security bug to us via our HackerOne page (https://hackerone.com/nextcloud) following our security policy (https://nextcloud.com/security/) instead of filing an issue in our bug tracker.
* The issues in other components should be reported in their respective repositories: You will find them in our GitHub Organization (https://github.com/nextcloud/)
* You can also use the Issue Template app to prefill most of the required information: https://apps.nextcloud.com/apps/issuetemplate
-->
<!--- Please keep this note for other contributors -->
### How to use GitHub
* Please use the 👍 [reaction](https://blog.github.com/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/) to show that you are affected by the same issue.
* Please don't comment if you have no relevant information to add. It's just extra noise for everyone subscribed to this issue.
* Subscribe to receive notifications on status change and new comments.
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Client details:**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
- Device: [e.g. iPhone6, desktop]
<details>
<summary>Server details</summary>
<!--
You can use the Issue Template application to prefill most of the required information: https://apps.nextcloud.com/apps/issuetemplate
-->
**Operating system**:
**Web server:**
**Database:**
**PHP version:**
**Nextcloud version:** (see Nextcloud admin page)
**Where did you install Nextcloud from:**
**Signing status:**
```
Login as admin user into your Nextcloud and access
http://example.com/index.php/settings/integrity/failed
paste the results here.
```
**List of activated apps:**
```
If you have access to your command line run e.g.:
sudo -u www-data php occ app:list
from within your Nextcloud installation folder
```
**Nextcloud configuration:**
```
If you have access to your command line run e.g.:
sudo -u www-data php occ config:list system
from within your Nextcloud installation folder
or
Insert your config.php content here
Make sure to remove all sensitive content such as passwords. (e.g. database password, passwordsalt, secret, smtp password, …)
```
**Are you using an external user-backend, if yes which one:** LDAP/ActiveDirectory/Webdav/...
</details>
<details>
<summary>Logs</summary>
#### Nextcloud log (data/nextcloud.log)
```
Insert your Nextcloud log here
```
#### Browser log
```
Insert your browser log here, this could for example include:
a) The javascript console log
b) The network log
c) ...
```
</details>

View File

@@ -1,39 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
---
<!--
Thanks for reporting issues back!
Guidelines for submitting issues:
* Please search the existing issues first, it's likely that your issue was already reported or even fixed.
* SECURITY: Report any potential security bug to us via our HackerOne page (https://hackerone.com/nextcloud) following our security policy (https://nextcloud.com/security/) instead of filing an issue in our bug tracker.
* The issues in other components should be reported in their respective repositories: You will find them in our GitHub Organization (https://github.com/nextcloud/)
* You can also use the Issue Template app to prefill most of the required information: https://apps.nextcloud.com/apps/issuetemplate
-->
<!--- Please keep this note for other contributors -->
### How to use GitHub
* Please use the 👍 [reaction](https://blog.github.com/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/) to show that you are affected by the same issue.
* Please don't comment if you have no relevant information to add. It's just extra noise for everyone subscribed to this issue.
* Subscribe to receive notifications on status change and new comments.
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@@ -1,28 +0,0 @@
version: 2
updates:
- package-ecosystem: npm
directory: "/"
schedule:
interval: weekly
day: saturday
time: "03:00"
timezone: Europe/Paris
open-pull-requests-limit: 10
reviewers:
- juliushaertl
- jakobroehrl
- package-ecosystem: composer
directory: "/"
schedule:
interval: weekly
day: saturday
time: "03:00"
timezone: Europe/Paris
open-pull-requests-limit: 10
reviewers:
- juliushaertl
ignore:
- dependency-name: christophwurst/nextcloud
versions:
- "< 16"
- ">= 15.a"

View File

@@ -1,17 +0,0 @@
* Resolves: # <!-- related github issue -->
* Target version: master
### Summary
### TODO
- [ ] ...
### Checklist
- [ ] Code is properly formatted
- [ ] Sign-off message is added to all commits
- [ ] Tests (unit, integration, api and/or acceptance) are included
- [ ] Documentation (manuals or wiki) has been updated or is not required

25
.github/stale.yml vendored
View File

@@ -1,25 +0,0 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- "1. to develop"
- "2. developing"
- "3. to review"
- "discussion"
- "bounty"
- "bug"
- "enhancement"
# Limit the number of actions per hour, from 1-30. Default is 30
limitPerRun: 30
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.

View File

@@ -1,38 +0,0 @@
name: Build app package
on:
pull_request:
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x]
steps:
- uses: actions/checkout@v1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Setup PHP
uses: shivammathur/setup-php@v1
with:
php-version: '7.4'
tools: composer
- name: install dependencies
run: |
wget https://github.com/ChristophWurst/krankerl/releases/download/v0.12.2/krankerl_0.12.2_amd64.deb
sudo dpkg -i krankerl_0.12.2_amd64.deb
- name: package
run: |
uname -a
RUST_BACKTRACE=1 krankerl --version
RUST_BACKTRACE=1 krankerl package
- uses: actions/upload-artifact@v2
with:
name: Deck app tarball
path: build/artifacts/deck.tar.gz

View File

@@ -1,83 +0,0 @@
name: Lint
on:
pull_request:
push:
branches:
- master
- stable*
jobs:
php:
runs-on: ubuntu-latest
strategy:
matrix:
php-versions: ['7.2', '7.3', '7.4']
name: php${{ matrix.php-versions }} lint
steps:
- uses: actions/checkout@v2
- name: Set up php${{ matrix.php-versions }}
uses: shivammathur/setup-php@v1
with:
php-version: ${{ matrix.php-versions }}
coverage: none
- name: Lint
run: composer run lint
php-cs-fixer:
name: php-cs check
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@master
- name: Set up php
uses: shivammathur/setup-php@master
with:
php-version: 7.4
coverage: none
- name: Install dependencies
run: composer i
- name: Run coding standards check
run: composer run cs:check
node:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x]
steps:
- uses: actions/checkout@v2
- name: Use node ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: npm ci
- name: ESLint
run: npm run lint
stylelint:
runs-on: ubuntu-latest
strategy:
matrix:
node-versions: [12.x]
name: stylelint node${{ matrix.node-versions }}
steps:
- uses: actions/checkout@v2
- name: Set up node ${{ matrix.node-versions }}
uses: actions/setup-node@v1
with:
node-versions: ${{ matrix.node-versions }}
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run stylelint

View File

@@ -1,62 +0,0 @@
name: Nightly build
on:
push:
branches:
- nightly
schedule:
- cron: '0 1 * * *' # run at 2 AM UTC
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x]
steps:
- uses: actions/checkout@v1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Setup PHP
uses: shivammathur/setup-php@v1
with:
php-version: '7.4'
tools: composer
- name: install dependencies
run: |
wget https://github.com/ChristophWurst/krankerl/releases/download/v0.12.2/krankerl_0.12.2_amd64.deb
sudo dpkg -i krankerl_0.12.2_amd64.deb
- name: package
run: |
uname -a
RUST_BACKTRACE=1 krankerl --version
RUST_BACKTRACE=1 krankerl package
- name: Set git config
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git tag -f nightly
- name: Push tag
uses: juliushaertl/github-push-action@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
tags: true
force: true
- name: Create Release
id: create_release
uses: juliushaertl/action-release@master
with:
token: ${{ secrets.GITHUB_TOKEN }}
tag: nightly
files: ./build/artifacts/deck.tar.gz
name: Nightly build
body: |
Nightly release of deck
draft: false
prerelease: true
overwrite: true

View File

@@ -1,26 +0,0 @@
name: Node CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x]
steps:
- uses: actions/checkout@v1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: install dependencies
run: |
npm ci
- name: build
run: |
npm run build --if-present

10
.gitignore vendored
View File

@@ -1,12 +1,10 @@
node_modules/*
js/
js/node_modules/*
js/vendor/
js/public/
js/package-lock.json
build/
css/style.css
css/vendor.css
tests/integration/vendor/
tests/integration/composer.lock
tests/.phpunit.result.cache
vendor/
*.lock
.php_cs.cache
\.idea/

View File

@@ -1,28 +0,0 @@
/build/
/.git
/.github
/docs/
/tests
/babel.config.js
/.editorconfig
/.eslintrc.js
/.nextcloudignore
/webpack.*.js
/.codecov.yml
/composer.json
/composer.lock
/_config.yml
/.drone.yml
/.travis.yml
/.eslintignore
/.eslintrc.yml
/.gitignore
/issue_template.md
/krankerl.toml
/Makefile
/mkdocs.yml
/run-eslint.sh
/package.json
/package-lock.json
/node_modules/
/src/

View File

@@ -1,18 +0,0 @@
<?php
declare(strict_types=1);
require_once './vendor/autoload.php';
use Nextcloud\CodingStandard\Config;
$config = new Config();
$config
->getFinder()
// ->ignoreVCSIgnored(true)
->notPath('build')
->notPath('l10n')
->notPath('src')
->notPath('vendor')
->in(__DIR__);
return $config;

View File

@@ -2,13 +2,18 @@ language: php
services:
- mysql
php:
- 7.0
- 7.1
- 7.2
- 7.3
env:
- CORE_BRANCH=master DB=mysql
- CORE_BRANCH=stable13 DB=mysql
before_install:
- export PATH="$PWD/vendor/bin:$PATH"
- wget https://phar.phpunit.de/phpunit-5.7.phar
- chmod +x phpunit-5.7.phar
- mkdir bin
- mv phpunit-5.7.phar bin/phpunit
- export PATH="$PWD/bin:$PATH"
- phpunit --version
- wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh
- bash ./before_install.sh deck $CORE_BRANCH $DB
@@ -19,7 +24,6 @@ before_script:
- cd apps/deck
script:
- composer install
- make test-unit
after_success:

View File

@@ -1,260 +1,18 @@
# Changelog
All notable changes to this project will be documented in this file.
## 1.0.5 - 2020-07-15
## 0.3.1 - 2018-03-07
### Fixed
- Fix performance issue with large LDAP instances
- Fix assigning users to newly created boards
- Various performance improvements
- Fix some untranslated strings (@cloud2018)
- Add confirmation dialog to delete options
- Fix issues with assigned users list when saving a card
- Delete assignments if users get removed
* [#2116](https://github.com/nextcloud/deck/pull/2116) Fix navigation layout issues @juliushaertl
* [#2118](https://github.com/nextcloud/deck/pull/2118) Use proper parameter when handling attachments @juliushaertl
## 1.0.4 - 2020-06-26
### Fixed
* [#2062](https://github.com/nextcloud/deck/pull/2062) Fix saving card description after toggling checkboxes @juliushaertl
* [#2065](https://github.com/nextcloud/deck/pull/2065) Adding CSS rule for Markdown Blockquotes @reox
* [#2059](https://github.com/nextcloud/deck/pull/2059) Fix fetching attachments on card change @juliushaertl
* [#2060](https://github.com/nextcloud/deck/pull/2060) Use mixing for relative date in card sidebar @juliushaertl
## 1.0.3 - 2020-06-19
### Fixed
* [#2019](https://github.com/nextcloud/deck/pull/2019) Remove old global css rule @juliushaertl
* [#2020](https://github.com/nextcloud/deck/pull/2020) Fix navigation issue with leftover nodes @juliushaertl
* [#2021](https://github.com/nextcloud/deck/pull/2021) Fix description issues @juliushaertl
* [#2022](https://github.com/nextcloud/deck/pull/2022) Fix replyto issues with the comments API @juliushaertl
* [#2027](https://github.com/nextcloud/deck/pull/2027) Allow to unassign current user from card @juliushaertl
* [#2029](https://github.com/nextcloud/deck/pull/2029) Fix wording : stack -> list @cloud2018
* [#2032](https://github.com/nextcloud/deck/pull/2032) Force order by id as second sorting key @juliushaertl
* [#2045](https://github.com/nextcloud/deck/pull/2045) Improve label styling @juliushaertl
* [#2010](https://github.com/nextcloud/deck/pull/2010) User documentation fixes @Nyco
* [#1998](https://github.com/nextcloud/deck/pull/1998) Add Checklist explaination to the doc @4rnoP
## 1.0.2 - 2020-06-03
### Fixed
* [#1774](https://github.com/nextcloud/deck/pull/1774) Remove deprecated global API calls
* [#1918](https://github.com/nextcloud/deck/pull/1918) Save compact mode on localstorage @jakobroehrl
* [#1919](https://github.com/nextcloud/deck/pull/1919) Show sidebar after card creation @jakobroehrl
* [#1924](https://github.com/nextcloud/deck/pull/1924) Boards ordered in main page @jakobroehrl
* [#1925](https://github.com/nextcloud/deck/pull/1925) Fix generated fronted urls
* [#1944](https://github.com/nextcloud/deck/pull/1944) Move navigation to @nextcloud/vue components
* [#1945](https://github.com/nextcloud/deck/pull/1945) Fix datetime picker
* [#1946](https://github.com/nextcloud/deck/pull/1946) Fix translations
* [#1976](https://github.com/nextcloud/deck/pull/1976) Delete boards that users own once they are deleted
* [#1977](https://github.com/nextcloud/deck/pull/1977) Redirect from previously used routes to the current ones
## 1.0.1 - 2020-05-15
### Fixed
* Removes debug filter output
* Labels are now sorted
* Stack title doesn't break up
* Fix move card modal
* Sort boards in navigation
* Fixes the attachment modal
* Handle deleted boards better
* User can only clone a board on canManage permissions
* Fix modal imports
* Show menu in compact mode
* Added a filter reset button
* Add hover effect to board list
* New filter icon
* Improve hovering response in board
* Enable linkify in description renderer @icewind1991
* Enhance board selector
* Fix issue if card description might be null
* Revert markdown styles from old frontend
* Do not scroll cards into view
* Fix reodering performance
## 1.0.0 - 2020-05-06
### Added
- Completly rewritten frontend
- Better maintainability
- Various small fixes
- Unified user interface with Nextcloud
- Separate comment and activity timelines
- Add ability to reply to comments #1537
- Filter cards on board #1507 @jakobroehrl
- Add cards to projects #1294 @jakobroehrl
- Move cards to other boards #1242 @jakobroehrl
- Clone boards with existing stacks and labels #1221 @jakobroehrl
- Upload multiple files at once and in parallel
A huge thangs goes to our awesome community that put enourmous effort into the frontend migration:
Special thanks for contributing huge parts of the Vue.js migration:
@jakobroehrl @weeman1337 @nicolad
Testers/reporters:
@cloud2018 @putt1ck @bpcurse
Android app team for helping to improve our REST API:
@desperateCoder @stefan-niedermann
## 0.8.0 - 2020-01-16
### Added
- Case insensitive search (@matchish)
### Fixed
- Fix reversed permissions for reordering stacks (@JLueke)
- Fix reversed visibility of 'add stack' field (@JLueke)
- Fix occ export command
- Fix error causing cron execution to fail
- Fix activity entry on moving cards
- Proper wording in activity timeline (@a11exandru)
## 0.7.0 - 2019-08-20
### Added
- Make deck compatible to Nextcloud 17
- Allow to set the description when creating cards though the REST API
## 0.6.6 - 2019-08-01
### Fixed
- Bump security related dependencies
## 0.6.5 - 2019-07-28
### Fixed
- Fix attachment upload/delete failures
- Bump dependencies
## 0.6.4 - 2019-06-30
### Fixed
- Restore stable15 compatibility
## 0.6.3 - 2019-06-30
### Fixed
- Fix issues with comments and activity stream
- Fix setting archived state through API
- Fix type of acl in API responses
- Fix type mismatch with fulltext search
## 0.6.2 - 2019-05-15
### Fixed
- Fix group limit for nonexisting groups
- Only map circle ACLs if the app is enabled
- Fix updating sharing permissions
- Add app version to capabilities
## 0.6.1 - 2019-04-27
### Fixed
- Fix issue with boards not being shown after update
- Fix board selection in projects view outside of deck
- Remove collections text from sidebar
- Remove leftover use statement
## 0.6.0 - 2019-04-23
### Added
- Share boards with circles
- Integration with collections in Nextcloud 16
- Support for full text search
- Nextcloud 16 compatibility
### Fixed
- Fix duplicate call to delete
- Prevent duplicate tag names @jakobroehrl
- Prevent loading details when editing the card title @jakobroehrl
- Hide sidebar after card deletion @jakobroehrl
- Update labels after change in the UI @jakobroehrl
- Allow limiting the app to groups again
- Various REST API enhancements and fixes
- Fix some issues with comments/activites
## 0.5.2 - 2018-12-20
### Fixed
- Mark notification as read if a card with duedate gets archived
- Use proper timezone and locale format for due date activities
- Various translation fixes and updates
- Check group limit properly
- 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
## 0.5.1 - 2018-12-05
### Added
- Separate settings for description changes in activity
- Less verbose description change activities
- Use server settings to restrict sharing to groups
- Add setting to exclude groups from creating their own boards
### Fixed
- Fix issue when using a separate table prefix @bpcurse
- Fix invalid activity parameters being published
- Wording fixes @cloud2018
- Improve loading performance by removing unused activity preloading
- Fix timestamp issues in deleted items tab
- Remember show state of the board navigation @weeman1337
- Add optional classes for custom styling @tinko92
- Fix missing details on activity emails
- Fix unrelated comments in board activity list
- Fix search not working properly
- Trigger comment notification on update only
## 0.5.0 - 2018-11-15
### Added
- Activity stream for board and cards
- Comments on cards
- Use users locale format on date picker
- Compact display mode
- Card title inline editing
- REST API
- Empty content view for board lists
- Undo for card and stack deletion
- Show tag name on board
- Notify users about card assignments
- Add shortcut to assign a card to yourself
- Improved view for printing
- Support for Nextcloud 15
### Fixed
- Accesibility improvements
- Don't allow empty card titles
- Improved checkbox handling in markdown
## 0.4.0 - 2018-07-11
### Added
- Attach files to cards
- Embed attachments into the card description
- Color picker to use any color value for board and labels
- Support for checkboxes inside the description
- occ command to export user data as JSON
### Fixed
- Improve frontend data management
- Fix bug the user list being empty on some occasions
## 0.3.0 - 2018-01-12
### Added

View File

@@ -14,30 +14,64 @@ cert_dir=$(HOME)/.nextcloud/certificates
default: build
clean-build:
rm -rf $(build_dir)
clean-dist:
rm -rf node_modules/
rm -rf js/node_modules
rm -rf js/vendor
install-deps: install-deps-js
composer install
install-deps:
cd js && npm install --deps
cd js && ./node_modules/.bin/bower install
install-deps-nodev: install-deps-js
composer install --no-dev
build: build-js
install-deps-js:
npm ci
build: clean-dist install-deps build-js
release: clean-dist install-deps-nodev build-js
build-js: install-deps-js
npm run build
build-js-dev: install-deps
npm run dev
build-js: install-deps
cd js && ./node_modules/.bin/grunt build
watch:
npm run watch
cd js && ./node_modules/.bin/grunt watch
# appstore: clean install-deps
appstore: clean-build build
rm -rf $(appstore_build_directory)
mkdir -p $(appstore_build_directory)
tar cvzf $(appstore_package_name).tar.gz \
--exclude="../$(app_name)/build" \
--exclude="../$(app_name)/tests" \
--exclude="../$(app_name)/Makefile" \
--exclude="../$(app_name)/*.log" \
--exclude="../$(app_name)/phpunit*xml" \
--exclude="../$(app_name)/composer.*" \
--exclude="../$(app_name)/js/node_modules" \
--exclude="../$(app_name)/js/tests" \
--exclude="../$(app_name)/js/test" \
--exclude="../$(app_name)/js/*.log" \
--exclude="../$(app_name)/js/package-lock.json" \
--exclude="../$(app_name)/js/package.json" \
--exclude="../$(app_name)/js/bower.json" \
--exclude="../$(app_name)/js/karma.*" \
--exclude="../$(app_name)/js/protractor.*" \
--exclude="../$(app_name)/package.json" \
--exclude="../$(app_name)/bower.json" \
--exclude="../$(app_name)/karma.*" \
--exclude="../$(app_name)/protractor\.*" \
--exclude="../$(app_name)/.*" \
--exclude="../$(app_name)/*.lock" \
--exclude="../$(app_name)/run-eslint.sh" \
--exclude="../$(app_name)/js/.*" \
--exclude="../$(app_name)/vendor" \
--exclude-vcs \
../$(app_name)
@if [ -f $(cert_dir)/$(app_name).key ]; then \
echo "Signing package…"; \
openssl dgst -sha512 -sign $(cert_dir)/$(app_name).key $(build_dir)/$(app_name).tar.gz | openssl base64; \
fi
echo $(appstore_package_name).tar.gz
test: test-unit test-integration
@@ -46,7 +80,7 @@ test-unit:
ifeq (, $(shell which phpunit 2> /dev/null))
@echo "No phpunit command available, downloading a copy from the web"
mkdir -p $(build_tools_directory)
curl -sSL https://phar.phpunit.de/phpunit-8.2.phar -o $(build_tools_directory)/phpunit.phar
curl -sSL https://phar.phpunit.de/phpunit-5.7.phar -o $(build_tools_directory)/phpunit.phar
php $(build_tools_directory)/phpunit.phar -c tests/phpunit.xml --coverage-clover build/php-unit.coverage.xml
php $(build_tools_directory)/phpunit.phar -c tests/phpunit.integration.xml --coverage-clover build/php-integration.coverage.xml
else
@@ -58,4 +92,5 @@ test-integration:
cd tests/integration && ./run.sh
test-js: install-deps
npm run test
cd js && run test

View File

@@ -1,6 +1,6 @@
# Deck
[![Build Status](https://travis-ci.org/nextcloud/deck.svg?branch=master)](https://travis-ci.org/nextcloud/deck) [![CodeCov](https://codecov.io/github/nextcloud/deck/coverage.svg?branch=master)](https://codecov.io/github/nextcloud/deck) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/e403f723f42a4abd93b2cfe36cbd7eee)](https://www.codacy.com/app/juliushaertl/deck?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=nextcloud/deck&amp;utm_campaign=Badge_Grade) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/nextcloud/deck/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/nextcloud/deck/?branch=master) [![#nextcloud-deck](https://img.shields.io/badge/IRC-%23nextcloud--deck%20on%20freenode-blue.svg)](https://webchat.freenode.net/?channels=nextcloud-deck)
[![Build Status](https://travis-ci.org/nextcloud/deck.svg?branch=master)](https://travis-ci.org/nextcloud/deck) [![CodeCov](https://codecov.io/github/nextcloud/deck/coverage.svg?branch=master)](https://codecov.io/github/nextcloud/deck) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/nextcloud/deck/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/nextcloud/deck/?branch=master) [![Dependency Status](https://www.versioneye.com/user/projects/58ad558f4ca76f004ed475b3/badge.svg?style=flat)](https://www.versioneye.com/user/projects/58ad558f4ca76f004ed475b3) [![#nextcloud-deck](https://img.shields.io/badge/IRC-%23nextcloud--deck%20on%20freenode-blue.svg)](https://webchat.freenode.net/?channels=nextcloud-deck)
Deck is a kanban style organization tool aimed at personal planning and project organization for teams integrated with Nextcloud.
@@ -9,21 +9,22 @@ Deck is a kanban style organization tool aimed at personal planning and project
- :page_facing_up: Write down additional notes in markdown
- :bookmark: Assign labels for even better organization
- :busts_in_silhouette: Share with your team, friends or family
- :family: Integrates with the [Circles](https://github.com/nextcloud/circles) app!
- :paperclip: Attach files and embed them in your markdown description
- :speech_balloon: Discuss with your team using comments
- :zap: Keep track of changes in the activity stream
- :rocket: Get your project organized
### Mobile apps
- The [Nextcloud Deck app for Android](https://github.com/stefan-niedermann/nextcloud-deck) is available in the [Google Play Store](https://play.google.com/store/apps/details?id=it.niedermann.nextcloud.deck.play)
![Deck - Manage cards on your board](https://download.bitgrid.net/nextcloud/deck/screenshots/Deck_Board.png)
![Deck - Manage cards on your board](http://download.bitgrid.net/nextcloud/deck/screenshots/1.0/Deck-2.png)
### Planned features
- :file_folder: Attach files directly from your Nextcloud
- :earth_africa: Share boards with the public
- :calendar: Integration with Nextcloud calendar and other apps
- :speech_balloon: Comments integration
- :exclamation: Checkout the project milestones for more ...
## Installation/Update
This app is supposed to work on the two latest Nextcloud versions.
This app is supposed to work on Nextcloud version 11 or later.
### Install latest release
@@ -37,14 +38,14 @@ If you want to run the latest development version from git source, you need to c
git clone https://github.com/nextcloud/deck.git
cd deck
make install-deps
make build
make
```
Please make sure you have installed the following dependencies: `make, which, tar, npm, curl, composer`
Please make sure you have installed the following dependencies: `make, which, tar, npm, curl`
### Install the nightly builds
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.
Instead of setting everything up manually, you can just [download the nightly builds](https://download.bitgrid.net/nextcloud/deck/nightly/) instead. These builds are updated every 24 hours, and are pre-configured with all the needed dependencies.
## Developing
@@ -54,43 +55,21 @@ Nothing to prepare, just dig into the code.
### JavaScript
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
Enable debug mode in your config.php `'debug' => true,`
Without SSL:
```
npx webpack-dev-server --config webpack.hot.js \
--public localhost:3000 \
--output-public-path 'http://localhost:3000/js/'
```
With SSL:
```
npx webpack-dev-server --config webpack.dev.js --https \
--cert ~/repos/nextcloud/nc-dev/data/ssl/nextcloud.local.crt \
--key ~/repos/nextcloud/nc-dev/data/ssl/nextcloud.local.key \
--public nextcloud.local:3000 \
--output-public-path 'https://nextcloud.local:3000/js/'
```
When `'debug'=>true` is set in your config.php files will get loaded automatically. Otherwise you need to ensure that `public/app.js` is generated by running `make` or `make watch` to regenerate it on every change.
Make sure you have installed the dependencies with ```make install-deps```.
### Running tests
You can use the provided Makefile to run all tests by using:
make test
### Documentation
The documentation for our REST API can be found at https://deck.readthedocs.io/en/latest/API/
## Contribution Guidelines
Please read the [Code of Conduct](https://nextcloud.com/community/code-of-conduct/). This document offers some guidance to ensure Nextcloud participants can cooperate effectively in a positive and inspiring atmosphere, and to explain how together we can strengthen and support each other.
For more information please review the [guidelines for contributing](https://github.com/nextcloud/server/blob/master/.github/CONTRIBUTING.md) to this repository.
For more information please review the [guidelines for contributing](https://github.com/nextcloud/server/blob/master/CONTRIBUTING.md) to this repository.
### Apply a license

View File

@@ -1,29 +0,0 @@
# Security Policy
## Supported Versions
| Version | Nextcloud version | Supported |
| ------- | ----------------- | ------------------ |
| 1.0.x | 18, 19 | :white_check_mark: |
| 0.8.x | 17 | :white_check_mark: |
| <0.7.x | - | :x: |
## Reporting a Vulnerability
Security is very important to us. If you have discovered a security issue with Nextcloud,
please read our responsible disclosure guidelines and contact us at [hackerone.com/nextcloud](https://hackerone.com/nextcloud).
Your report should include:
- Product version
- A vulnerability description
- Reproduction steps
A member of the security team will confirm the vulnerability, determine its impact, and develop a fix.
The fix will be applied to the master branch, tested, and packaged in the next security release.
The vulnerability will be publicly announced after the release. Finally, your name will be added
to the [hall of fame](https://hackerone.com/nextcloud/thanks) as a thank you from the entire Nextcloud community. Note our
[threat model](https://nextcloud.com/security/threat-model) to know what is expected behavior.
Please visit https://nextcloud.com/security/ for further information about security.

2
_config.yml Normal file
View File

@@ -0,0 +1,2 @@
theme: jekyll-theme-cayman
site: https://deck-app.com

View File

@@ -21,19 +21,6 @@
*
*/
use OCA\Deck\AppInfo\Application;
use OCP\AppFramework\QueryException;
if ((@include_once __DIR__ . '/../vendor/autoload.php')=== false) {
throw new Exception('Cannot include autoload. Did you run install dependencies using composer?');
}
try {
/** @var Application $app */
$app = \OC::$server->query(Application::class);
$app->register();
} catch (QueryException $e) {
}
/** Load activity style global so it is availabile in the activity app as well */
\OC_Util::addStyle('deck', 'activity');
$app = new \OCA\Deck\AppInfo\Application();
$app->registerNavigationEntry();
$app->registerNotifications();

View File

@@ -5,25 +5,27 @@
* @author Julius Härtl <jus@bitgrid.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\AppInfo;
use OCP\AppFramework\App;
/**
* Additional autoloader registration, e.g. registering composer autoloaders
*/
require_once __DIR__ . '/../vendor/autoload.php';
// require_once __DIR__ . '/../vendor/autoload.php';

426
appinfo/database.xml Normal file
View File

@@ -0,0 +1,426 @@
<?xml version="1.0" encoding="UTF-8" ?>
<database>
<name>*dbname*</name>
<create>true</create>
<overwrite>false</overwrite>
<charset>utf8</charset>
<table>
<name>*dbprefix*deck_boards</name>
<declaration>
<field>
<name>id</name>
<type>integer</type>
<default>0</default>
<notnull>true</notnull>
<autoincrement>1</autoincrement>
<length>4</length>
</field>
<field>
<name>title</name>
<type>text</type>
<notnull>true</notnull>
<length>100</length>
</field>
<field>
<name>owner</name>
<type>text</type>
<notnull>true</notnull>
<length>64</length>
</field>
<field>
<name>color</name>
<type>text</type>
<length>6</length>
<notnull>false</notnull>
</field>
<field>
<name>archived</name>
<type>boolean</type>
<default>false</default>
</field>
<field>
<name>deleted_at</name>
<type>integer</type>
<default>0</default>
<length>8</length>
<notnull>false</notnull>
<unsigned>true</unsigned>
</field>
</declaration>
</table>
<table>
<name>*dbprefix*deck_stacks</name>
<declaration>
<field>
<name>id</name>
<type>integer</type>
<default>0</default>
<notnull>true</notnull>
<autoincrement>1</autoincrement>
<length>4</length>
</field>
<field>
<name>title</name>
<type>text</type>
<notnull>true</notnull>
<length>100</length>
</field>
<field>
<name>board_id</name>
<type>integer</type>
<length>8</length>
<notnull>true</notnull>
</field>
<field>
<name>order</name>
<type>integer</type>
<length>8</length>
<notnull>false</notnull>
</field>
<index>
<name>deck_stacks_board_id_index</name>
<field>
<name>board_id</name>
</field>
</index>
<index>
<name>deck_stacks_order_index</name>
<field>
<name>order</name>
</field>
</index>
</declaration>
</table>
<table>
<name>*dbprefix*deck_cards</name>
<declaration>
<field>
<name>id</name>
<type>integer</type>
<default>0</default>
<notnull>true</notnull>
<autoincrement>1</autoincrement>
<length>4</length>
</field>
<field>
<name>title</name>
<type>text</type>
<length>100</length>
<notnull>true</notnull>
</field>
<field>
<name>description</name>
<type>clob</type>
<notnull>false</notnull>
</field>
<field>
<name>stack_id</name>
<type>integer</type>
<length>8</length>
<notnull>true</notnull>
</field>
<field>
<name>type</name>
<type>text</type>
<notnull>true</notnull>
<length>64</length>
<default>plain</default>
</field>
<field>
<name>last_modified</name>
<type>integer</type>
<default></default>
<notnull>false</notnull>
<unsigned>true</unsigned>
</field>
<field>
<name>created_at</name>
<type>integer</type>
<default></default>
<notnull>false</notnull>
<unsigned>true</unsigned>
</field>
<field>
<name>owner</name>
<type>text</type>
<notnull>true</notnull>
<length>64</length>
</field>
<field>
<name>order</name>
<type>integer</type>
<length>8</length>
<notnull>false</notnull>
</field>
<field>
<name>archived</name>
<type>boolean</type>
<default>false</default>
</field>
<field>
<name>duedate</name>
<type>timestamp</type>
<default>0</default>
</field>
<field>
<name>notified</name>
<type>boolean</type>
<default>false</default>
</field>
<index>
<name>deck_cards_stack_id_index</name>
<field>
<name>stack_id</name>
</field>
</index>
<index>
<name>deck_cards_order_index</name>
<field>
<name>order</name>
</field>
</index>
<index>
<name>deck_cards_archived_index</name>
<field>
<name>archived</name>
</field>
</index>
</declaration>
</table>
<table>
<name>*dbprefix*deck_attachment</name>
<declaration>
<field>
<name>id</name>
<type>integer</type>
<default>0</default>
<notnull>true</notnull>
<autoincrement>1</autoincrement>
<length>4</length>
</field>
<field>
<name>title</name>
<type>text</type>
<notnull>true</notnull>
<length>100</length>
</field>
<field>
<name>card_id</name>
<type>integer</type>
<length>8</length>
<notnull>true</notnull>
</field>
<field>
<name>type</name>
<type>text</type>
<notnull>true</notnull>
<length>64</length>
</field>
<field>
<name>data</name>
<type>clob</type>
</field>
<field>
<name>last_modified</name>
<type>integer</type>
<default></default>
<length>8</length>
<notnull>false</notnull>
<unsigned>true</unsigned>
</field>
<field>
<name>created_at</name>
<type>integer</type>
<default></default>
<length>8</length>
<notnull>false</notnull>
<unsigned>true</unsigned>
</field>
</declaration>
</table>
<table>
<name>*dbprefix*deck_labels</name>
<declaration>
<field>
<name>id</name>
<type>integer</type>
<default>0</default>
<notnull>true</notnull>
<autoincrement>1</autoincrement>
<length>4</length>
</field>
<field>
<name>title</name>
<type>text</type>
<notnull>false</notnull>
<length>100</length>
</field>
<field>
<name>color</name>
<type>text</type>
<length>6</length>
<notnull>false</notnull>
</field>
<field>
<name>board_id</name>
<type>integer</type>
<notnull>true</notnull>
<length>8</length>
</field>
<index>
<name>deck_labels_board_id_index</name>
<field>
<name>board_id</name>
</field>
</index>
</declaration>
</table>
<table>
<name>*dbprefix*deck_assigned_labels</name>
<declaration>
<field>
<name>id</name>
<type>integer</type>
<default>0</default>
<notnull>true</notnull>
<autoincrement>1</autoincrement>
<length>4</length>
</field>
<field>
<name>label_id</name>
<type>integer</type>
<default>0</default>
<notnull>true</notnull>
<length>4</length>
</field>
<field>
<name>card_id</name>
<type>integer</type>
<default>0</default>
<notnull>true</notnull>
<length>4</length>
</field>
<index>
<name>deck_assigned_labels_idx_i</name>
<field>
<name>label_id</name>
</field>
</index>
<index>
<name>deck_assigned_labels_idx_c</name>
<field>
<name>card_id</name>
</field>
</index>
</declaration>
</table>
<table>
<name>*dbprefix*deck_assigned_users</name>
<declaration>
<field>
<name>id</name>
<type>integer</type>
<default>0</default>
<notnull>true</notnull>
<autoincrement>1</autoincrement>
<length>4</length>
</field>
<field>
<name>participant</name>
<type>text</type>
<notnull>true</notnull>
<length>64</length>
</field>
<field>
<name>card_id</name>
<type>integer</type>
<default>0</default>
<notnull>true</notnull>
<length>4</length>
</field>
<index>
<name>deck_assigned_users_idx_p</name>
<field>
<name>participant</name>
</field>
</index>
<index>
<name>deck_assigned_users_idx_c</name>
<field>
<name>card_id</name>
</field>
</index>
</declaration>
</table>
<table>
<name>*dbprefix*deck_board_acl</name>
<declaration>
<field>
<name>id</name>
<type>integer</type>
<default>0</default>
<notnull>true</notnull>
<autoincrement>1</autoincrement>
<length>4</length>
</field>
<field>
<name>board_id</name>
<type>integer</type>
<notnull>true</notnull>
<length>8</length>
</field>
<field>
<name>type</name>
<type>integer</type>
<notnull>true</notnull>
<length>4</length>
</field>
<field>
<name>participant</name>
<type>text</type>
<notnull>true</notnull>
<length>64</length>
</field>
<field>
<name>permission_edit</name>
<type>boolean</type>
<default>false</default>
</field>
<field>
<name>permission_share</name>
<type>boolean</type>
<default>false</default>
</field>
<field>
<name>permission_manage</name>
<type>boolean</type>
<default>false</default>
</field>
<index>
<name>deck_board_acl_uq_i</name>
<unique>true</unique>
<field>
<name>board_id</name>
<sorting>ascending</sorting>
</field>
<field>
<name>type</name>
<sorting>ascending</sorting>
</field>
<field>
<name>participant</name>
<sorting>ascending</sorting>
</field>
</index>
<index>
<name>deck_board_acl_idx_i</name>
<field>
<name>board_id</name>
</field>
</index>
</declaration>
</table>
</database>

View File

@@ -3,7 +3,7 @@
xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
<id>deck</id>
<name>Deck</name>
<summary>Personal planning and team project organization</summary>
<summary>A kanban style project and personal management tool for Nextcloud</summary>
<description>Deck is a kanban style organization tool aimed at personal planning and project organization for teams integrated with Nextcloud.
@@ -11,62 +11,30 @@
- 📄 Write down additional notes in markdown
- 🔖 Assign labels for even better organization
- 👥 Share with your team, friends or family
- 📎 Attach files and embed them in your markdown description
- 💬 Discuss with your team using comments
- ⚡ Keep track of changes in the activity stream
- 🚀 Get your project organized
</description>
<version>1.0.5</version>
<version>0.3.1</version>
<licence>agpl</licence>
<author>Julius Härtl</author>
<namespace>Deck</namespace>
<types>
<dav />
</types>
<category>organization</category>
<category>office</category>
<website>https://github.com/nextcloud/deck</website>
<bugs>https://github.com/nextcloud/deck/issues</bugs>
<repository type="git">https://github.com/nextcloud/deck.git</repository>
<screenshot>https://download.bitgrid.net/nextcloud/deck/screenshots/1.0/Deck-1.png</screenshot>
<screenshot>https://download.bitgrid.net/nextcloud/deck/screenshots/1.0/Deck-2.png</screenshot>
<screenshot>https://download.bitgrid.net/nextcloud/deck/screenshots/Deck_Board.png</screenshot>
<screenshot>https://download.bitgrid.net/nextcloud/deck/screenshots/Deck_Details.png</screenshot>
<dependencies>
<php min-version="5.6"/>
<database min-version="9.4">pgsql</database>
<database>sqlite</database>
<database min-version="5.5">mysql</database>
<nextcloud min-version="18" max-version="20" />
<nextcloud min-version="12" max-version="13" />
</dependencies>
<background-jobs>
<job>OCA\Deck\Cron\DeleteCron</job>
<job>OCA\Deck\Cron\ScheduledNotifications</job>
<job>OCA\Deck\Cron\CardDescriptionActivity</job>
</background-jobs>
<repair-steps>
<post-migration>
<step>OCA\Deck\Migration\UnknownUsers</step>
</post-migration>
</repair-steps>
<commands>
<command>OCA\Deck\Command\UserExport</command>
</commands>
<activity>
<settings>
<setting>OCA\Deck\Activity\Setting</setting>
<setting>OCA\Deck\Activity\SettingComment</setting>
<setting>OCA\Deck\Activity\DescriptionSetting</setting>
</settings>
<filters>
<filter>OCA\Deck\Activity\Filter</filter>
</filters>
<providers>
<provider>OCA\Deck\Activity\DeckProvider</provider>
</providers>
</activity>
<fulltextsearch>
<provider min-version="16">OCA\Deck\Provider\DeckProvider</provider>
</fulltextsearch>
</info>

View File

@@ -3,32 +3,28 @@
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.net>
* @author Ryan Fletcher <ryan.fletcher@codepassion.ca>
*
* @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/>.
*
*
*/
return [
'routes' => [
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
['name' => 'Config#get', 'url' => '/config', 'verb' => 'GET'],
['name' => 'Config#setValue', 'url' => '/config/{key}', 'verb' => 'POST'],
// boards
['name' => 'board#index', 'url' => '/boards', 'verb' => 'GET'],
['name' => 'board#create', 'url' => '/boards', 'verb' => 'POST'],
@@ -38,9 +34,8 @@ return [
['name' => 'board#deleteUndo', 'url' => '/boards/{boardId}/deleteUndo', 'verb' => 'POST'],
['name' => 'board#getUserPermissions', 'url' => '/boards/{boardId}/permissions', 'verb' => 'GET'],
['name' => 'board#addAcl', 'url' => '/boards/{boardId}/acl', 'verb' => 'POST'],
['name' => 'board#updateAcl', 'url' => '/boards/{boardId}/acl/{aclId}', 'verb' => 'PUT'],
['name' => 'board#updateAcl', 'url' => '/boards/{boardId}/acl', 'verb' => 'PUT'],
['name' => 'board#deleteAcl', 'url' => '/boards/{boardId}/acl/{aclId}', 'verb' => 'DELETE'],
['name' => 'board#clone', 'url' => '/boards/{boardId}/clone', 'verb' => 'POST'],
// stacks
['name' => 'stack#index', 'url' => '/stacks/{boardId}', 'verb' => 'GET'],
@@ -48,7 +43,6 @@ return [
['name' => 'stack#update', 'url' => '/stacks/{stackId}', 'verb' => 'PUT'],
['name' => 'stack#reorder', 'url' => '/stacks/{stackId}/reorder', 'verb' => 'PUT'],
['name' => 'stack#delete', 'url' => '/stacks/{stackId}', 'verb' => 'DELETE'],
['name' => 'stack#deleted', 'url' => '/{boardId}/stacks/deleted', 'verb' => 'GET'],
['name' => 'stack#archived', 'url' => '/stacks/{boardId}/archived', 'verb' => 'GET'],
// cards
@@ -56,7 +50,6 @@ return [
['name' => 'card#create', 'url' => '/cards', 'verb' => 'POST'],
['name' => 'card#update', 'url' => '/cards/{cardId}', 'verb' => 'PUT'],
['name' => 'card#delete', 'url' => '/cards/{cardId}', 'verb' => 'DELETE'],
['name' => 'card#deleted', 'url' => '/{boardId}/cards/deleted', 'verb' => 'GET'],
['name' => 'card#rename', 'url' => '/cards/{cardId}/rename', 'verb' => 'PUT'],
['name' => 'card#reorder', 'url' => '/cards/{cardId}/reorder', 'verb' => 'PUT'],
['name' => 'card#archive', 'url' => '/cards/{cardId}/archive', 'verb' => 'PUT'],
@@ -64,78 +57,12 @@ return [
['name' => 'card#assignLabel', 'url' => '/cards/{cardId}/label/{labelId}', 'verb' => 'POST'],
['name' => 'card#removeLabel', 'url' => '/cards/{cardId}/label/{labelId}', 'verb' => 'DELETE'],
['name' => 'card#assignUser', 'url' => '/cards/{cardId}/assign', 'verb' => 'POST'],
['name' => 'card#unassignUser', 'url' => '/cards/{cardId}/unassign', 'verb' => 'PUT'],
// attachments
['name' => 'attachment#getAll', 'url' => '/cards/{cardId}/attachments', 'verb' => 'GET'],
['name' => 'attachment#create', 'url' => '/cards/{cardId}/attachment', 'verb' => 'POST'],
['name' => 'attachment#display', 'url' => '/cards/{cardId}/attachment/{attachmentId}', 'verb' => 'GET'],
['name' => 'attachment#update', 'url' => '/cards/{cardId}/attachment/{attachmentId}', 'verb' => 'PUT'],
// also allow to use POST for updates so we can properly access files when using application/x-www-form-urlencoded
['name' => 'attachment#update', 'url' => '/cards/{cardId}/attachment/{attachmentId}', 'verb' => 'POST'],
['name' => 'attachment#delete', 'url' => '/cards/{cardId}/attachment/{attachmentId}', 'verb' => 'DELETE'],
['name' => 'attachment#restore', 'url' => '/cards/{cardId}/attachment/{attachmentId}/restore', 'verb' => 'GET'],
['name' => 'card#unassignUser', 'url' => '/cards/{cardId}/assign/{userId}', 'verb' => 'DELETE'],
// labels
['name' => 'label#create', 'url' => '/labels', 'verb' => 'POST'],
['name' => 'label#update', 'url' => '/labels/{labelId}', 'verb' => 'PUT'],
['name' => 'label#delete', 'url' => '/labels/{labelId}', 'verb' => 'DELETE'],
// api
['name' => 'board_api#index', 'url' => '/api/v1.0/boards', 'verb' => 'GET'],
['name' => 'board_api#get', 'url' => '/api/v1.0/boards/{boardId}', 'verb' => 'GET'],
['name' => 'board_api#create', 'url' => '/api/v1.0/boards', 'verb' => 'POST'],
['name' => 'board_api#delete', 'url' => '/api/v1.0/boards/{boardId}', 'verb' => 'DELETE'],
['name' => 'board_api#update', 'url' => '/api/v1.0/boards/{boardId}', 'verb' => 'PUT'],
['name' => 'board_api#undo_delete', 'url' => '/api/v1.0/boards/{boardId}/undo_delete', 'verb' => 'POST'],
['name' => 'board_api#addAcl', 'url' => '/api/v1.0/boards/{boardId}/acl', 'verb' => 'POST'],
['name' => 'board_api#deleteAcl', 'url' => '/api/v1.0/boards/{boardId}/acl/{aclId}', 'verb' => 'DELETE'],
['name' => 'board_api#updateAcl', 'url' => '/api/v1.0/boards/{boardId}/acl/{aclId}', 'verb' => 'PUT'],
['name' => 'stack_api#index', 'url' => '/api/v1.0/boards/{boardId}/stacks', 'verb' => 'GET'],
['name' => 'stack_api#getArchived', 'url' => '/api/v1.0/boards/{boardId}/stacks/archived', 'verb' => 'GET'],
['name' => 'stack_api#get', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}', 'verb' => 'GET'],
['name' => 'stack_api#create', 'url' => '/api/v1.0/boards/{boardId}/stacks', 'verb' => 'POST'],
['name' => 'stack_api#update', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}', 'verb' => 'PUT'],
['name' => 'stack_api#delete', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}', 'verb' => 'DELETE'],
['name' => 'card_api#get', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}', 'verb' => 'GET'],
['name' => 'card_api#create', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}/cards', 'verb' => 'POST'],
['name' => 'card_api#update', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}', 'verb' => 'PUT'],
['name' => 'card_api#assignLabel', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/assignLabel', 'verb' => 'PUT'],
['name' => 'card_api#removeLabel', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/removeLabel', 'verb' => 'PUT'],
['name' => 'card_api#assignUser', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/assignUser', 'verb' => 'PUT'],
['name' => 'card_api#unassignUser', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/unassignUser', 'verb' => 'PUT'],
['name' => 'card_api#reorder', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/reorder', 'verb' => 'PUT'],
['name' => 'card_api#delete', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}', 'verb' => 'DELETE'],
['name' => 'card_api#findAllWithDue', 'url' => '/api/v1.0/dashboard/due', 'verb' => 'GET'],
['name' => 'label_api#get', 'url' => '/api/v1.0/boards/{boardId}/labels/{labelId}', 'verb' => 'GET'],
['name' => 'label_api#create', 'url' => '/api/v1.0/boards/{boardId}/labels', 'verb' => 'POST'],
['name' => 'label_api#update', 'url' => '/api/v1.0/boards/{boardId}/labels/{labelId}', 'verb' => 'PUT'],
['name' => 'label_api#delete', 'url' => '/api/v1.0/boards/{boardId}/labels/{labelId}', 'verb' => 'DELETE'],
['name' => 'attachment_api#getAll', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments', 'verb' => 'GET'],
['name' => 'attachment_api#display', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments/{attachmentId}', 'verb' => 'GET'],
['name' => 'attachment_api#create', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments', 'verb' => 'POST'],
['name' => 'attachment_api#update', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments/{attachmentId}', 'verb' => 'PUT'],
['name' => 'attachment_api#delete', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments/{attachmentId}', 'verb' => 'DELETE'],
['name' => 'attachment_api#restore', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments/{attachmentId}/restore', 'verb' => 'PUT'],
['name' => 'board_api#preflighted_cors', 'url' => '/api/v1.0/{path}','verb' => 'OPTIONS', 'requirements' => ['path' => '.+']],
],
'ocs' => [
['name' => 'comments_api#list', 'url' => '/api/v1.0/cards/{cardId}/comments', 'verb' => 'GET'],
['name' => 'comments_api#create', 'url' => '/api/v1.0/cards/{cardId}/comments', 'verb' => 'POST'],
['name' => 'comments_api#update', 'url' => '/api/v1.0/cards/{cardId}/comments/{commentId}', 'verb' => 'PUT'],
['name' => 'comments_api#delete', 'url' => '/api/v1.0/cards/{cardId}/comments/{commentId}', 'verb' => 'DELETE'],
// dashboard
['name' => 'overview_api#upcomingCards', 'url' => '/api/v1.0/overview/upcoming', 'verb' => 'GET'],
]
];

View File

@@ -1,11 +0,0 @@
module.exports = {
plugins: ['@babel/plugin-syntax-dynamic-import'],
presets: [
[
'@babel/preset-env',
{
modules: false
}
]
]
}

View File

@@ -8,24 +8,9 @@
"email": "jus@bitgrid.net"
}
],
"require": {
"cogpowered/finediff": "0.3.*"
},
"require": {},
"require-dev": {
"roave/security-advisories": "dev-master",
"christophwurst/nextcloud": "^17",
"jakub-onderka/php-parallel-lint": "^1.0.0",
"phpunit/phpunit": "^8",
"nextcloud/coding-standard": "^0.3.0",
"symfony/event-dispatcher": "^4.0"
},
"config": {
"optimize-autoloader": true,
"classmap-authoritative": true
},
"scripts": {
"lint": "find . -name \\*.php -not -path './vendor/*' -print0 | xargs -0 -n1 php -l",
"cs:check": "php-cs-fixer fix --dry-run --diff",
"cs:fix": "php-cs-fixer fix"
"christophwurst/nextcloud": "^12.0",
"jakub-onderka/php-parallel-lint": "^0.9.2"
}
}

View File

@@ -1,28 +0,0 @@
.activitymessage .visualdiff ins {
background-color: rgba(70, 186, 97, 0.2);
text-decoration: none;
}
.activitymessage .visualdiff del {
background-color: rgba(233, 50, 45, 0.2);
text-decoration: none;
}
.activitymessage .visualdiff {
overflow: scroll;
max-height: 200px;
}
.activityTabView .avatardiv-container {
display: inline-block;
bottom: -3px;
margin-left: 3px;
}
.activityTabView .avatar-name-wrapper {
font-weight: bold;
}
.activityTabView .activitysubject a {
font-weight: bold;
}

View File

@@ -1,7 +0,0 @@
.resource-type-deck img {
opacity: 0.4 !important;
}
.resource-type-deck:hover img {
opacity: 0.7 !important;
}

113
css/comp-appnav.scss Normal file
View File

@@ -0,0 +1,113 @@
/*
* @copyright Copyright (c) 2017 Julius Härtl <jus@bitgrid.net>
* @copyright Copyright (c) 2016, John Molakvoæ <skjnldsv@protonmail.com>
*
* @author Julius Härtl <jus@bitgrid.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/>.
*
*/
/**
* Hotfix for support <NC13 with new app sidebar
*/
#app-navigation {
.app-navigation-entry-menu.open {
ul li a {
background-position: 10px center;
padding: 0 10px 0 36px !important;
}
}
.app-navigation-entry-edit {
display: none;
}
.editing {
.app-navigation-entry-edit {
display: block;
position: absolute;
background: $color-main-background;
height: auto;
z-index: 250;
}
}
}
/**
* copied styles from core/css/styles.scss
* to have the same breadcrumb styling in NC12
*/
.breadcrumb {
display: inline-flex;
}
div.crumb {
display: inline-flex;
background-repeat: no-repeat;
background-position: right center;
height: 44px;
background-size: auto 24px;
flex: 0 0 auto;
order: 1;
padding-right: 7px;
&.crumbmenu {
order: 2;
position: relative;
a {
opacity: 0.5
}
}
&.hidden {
display: none;
~ .crumb {
order: 3;
}
}
> a,
> span {
position: relative;
padding: 12px;
opacity: 0.5;
top: 0 !important;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
flex: 0 0 auto;
&.icon-home {
// Hide home text
text-indent: -9999px;
}
}
> a[class^='icon-'] {
padding: 0;
width: 44px;
}
&:not(:first-child) a {
}
&:last-child {
font-weight: 600;
margin-right: 10px;
// Allow multiple span next to the main 'a'
a ~ span {
padding-left: 0;
}
}
&:hover, &:focus, a:focus, &:active {
> a,
> span {
opacity: .7;
}
}
}

View File

@@ -1,37 +1,42 @@
/**
* 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);
.icon-toggle-compact-collapsed {
@include icon-color('toggle-view-expand', 'deck', $color-black);
.icon-deck {
background-image: url('../img/deck-dark.svg');
}
.icon-toggle-compact-expanded {
@include icon-color('toggle-view-collapse', 'deck', $color-black);
}
.icon-activity {
@include icon-color('activity-dark', 'activity', $color-black);
.icon-group {
background-image: url('../../../settings/img/users.svg');
}
.avatardiv.circles {
background: var(--color-primary);
.icon-help {
background-image: url('../../../settings/img/help.svg');
}
.icon-circles {
opacity: 1;
background-size: 20px;
background-position: center center;
.icon-add-white {
background-image: url('../img/add-white.svg');
}
.icon-colorpicker {
background-image: url('../img/color_picker.svg');
.icon-archive {
background-image: url('../img/archive.svg');
}
.icon-archive-white {
background-image: url('../img/archive-white.svg');
}
.icon-details {
background-image: url('../img/details.svg');
}
.icon-details-white {
background-image: url('../img/details-white.svg');
}
.icon-home {
background-image: url('../../../core/img/places/home.svg');
}
.icon-badge {
background-image: url('../../../core/img/places/calendar-dark.svg');
}

View File

@@ -1,88 +0,0 @@
@media print {
/* hide stuff */
#body-user {
#header,
div#app-navigation,
div.board-header-controls,
#app-navigation-toggle,
#app-navigation-toggle-custom,
div#controls.ng-scope div.crumb:not(.title),
div#controls.ng-scope div.crumb a.bullet,
a.ng-binding + a,
div.card.create,
button.card-options {
display: none !important;
}
#content {
margin: 0;
padding: 0;
}
#app-content {
margin: 0 !important;
}
}
div#app-navigation-toggle.icon-menu {
display:block;
width:0px;
height:0px;
background:none;
}
/* title */
div#controls.ng-scope {padding-left:20px;}
div#controls.ng-scope div.crumb.title {
display:inline;
font-size: 2em;
line-height:2.5em;
background:none;
}
div#controls.ng-scope div.crumb.title a.ng-binding {
color:#000;
opacity:1;
}
/*Due, assigned-users and description*/
div.card-controls {
flex-direction:row;
flex-wrap:wrap;
}
div.card-controls i.icon.icon-filetype-text {background:none;}
div.card-controls i.icon.icon-filetype-text:after {
content: attr(title);
display:block;
width:289px;
height:1.5em;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
div.card-assigned-users {
margin-right:10px;
}
ul.labels li.ng-scope span.ng-binding {
color:#000;
display:inline;
padding-left:5px;
}
/* Layout */
@page {
size: A4 landscape;
margin: 2cm;
}
div#innerBoard {
display:flex;
flex-wrap: wrap;
}
div.stack.ng-scope.as-sortable-item {border-right: 1px solid #000;}
div#innerBoard.ng-pristine.ng-untouched.ng-valid.ng-scope.ng-not-empty div.stack.ng-scope.as-sortable-item:nth-child(6n) {
page-break-after: always;
}
}

1245
css/style.scss Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,42 +0,0 @@
# Nextcloud APIs
## Available sharees
When sharing a board to a user, group or circle, the possible sharees can be obtained though the files_sharing API.
API endpoint: https://nextcloud.local/index.php/apps/files_sharing/api/v1/sharees
### Parameters
- format: **The response format**
- perPage: **Limit response number**
- itemType: **List of types. Currently supported are**
- 0 user
- 1 group
- 7 circle
## Comments
Comments are stored using the Nextcloud Comments API. You can use the WebDAV endpoint of Nextcloud to fetch, update and delete comments.
### List comments
PROPFIND`remote.php/dav/comments/deckCard/{cardId}`
### Create comment
POST `remote.php/dav/comments/deckCard/{cardId}`
### Update comment
PROPPATCH `remote.php/dav/comments/deckCard/{cardId}/{commentId}`
### Delete comment
DELETE `remote.php/dav/comments/deckCard/{cardId}/{commentId}`
## Activity
The Nextcloud activity app provides an API to fetch activities filtered for deck: [Activity app API documentation](https://github.com/nextcloud/activity/blob/master/docs/endpoint-v2.md)
The deck app offers a filter `deck` to only request activity events that are relevant.

File diff suppressed because it is too large Load Diff

View File

@@ -1,31 +0,0 @@
## What is Markdown
The [wikipedia markdown entry](https://en.wikipedia.org/wiki/Markdown) introduced markdown as :
> Markdown is a lightweight markup language with plain text formatting syntax. It is designed so that it can be converted to HTML and many other formats using a tool by the same name. Markdown is often used to format readme files, for writing messages in online discussion forums, and to create rich text using a plain text editor. As the initial description of Markdown contained ambiguities and unanswered questions, many implementations and extensions of Markdown appeared over the years to answer these issues.
## Markdown in Deck
The Deck application plugin uses the [markdown-it](https://github.com/markdown-it/markdown-it) script to offer support for markdown in the cards description field.
## Supported Markdown
Markdown comes in may flavors. The best way to learn markdown and understand how to use it, is simply to [try it](https://markdown-it.github.io) on the original script official playground.
That same link offers also a comprehensive list of what is supported, and what is not - rendering it unnecessary to duplicate that content in here.
[CommonMark Markdown Reference](http://commonmark.org/help/)
## Note about checklists
It is possible to create checklists in Deck by writing it in Markdown, using the following syntax:
```md
- [ ] This is a not checked item
- [x] This is a checked item
```
Then, the items can be checked and unchecked by clicking on the rendered checkbox.
Also, a summary of the completed items will be visible at the bottom of the card element.
## Known Issues
As per [issue #127](https://github.com/nextcloud/deck/issues/127) Due to a known limitation of the current script to support markdown, Links that contain the `")"` character will not display well, or will break.
The recommended solution is to use `"<"` and `">"` to wrap those links. It should assure their integrity.
If you come by another case of broken link, or broken display of links, please report it by opening a new issue.

View File

@@ -1,55 +0,0 @@
Releasing a new version works quite easy with [krankerl](https://github.com/ChristophWurst/krankerl) and [github-release](https://github.com/aktau/github-release) installed:
1. Run krankerl to build the package
```
krankerl package
```
2. Tag the release on GitHub
```
# For a prerelease
github-release release -u nextcloud -r deck -t v0.3.1 -p
# For a regular release
github-release release -u nextcloud -r deck -t v0.3.1
```
3. Upload the release package to GitHub
```
github-release upload -u nextcloud -r deck -t v0.3.1 -n deck.tar.gz -f build/artifacts/deck.tar.gz
```
4. Run krankerl to release the package to the app store (add `--nightly` for prerelease packages)
```
krankerl publish https://github.com/nextcloud/deck/releases/download/v0.3.1/deck.tar.gz
```
## Release PR template
```
## Backports
- [ ] ...
## Translations
- [ ] ...
## Release
- [ ] Set proper Nextcloud versions in info.xml
- [ ] Update changelog
- [ ] Build test release
- [ ] Tested on
- [ ] Nextcloud 13
- [ ] Nextcloud 14
- [ ] Nextcloud 15
- [ ] Merge
- [ ] Build final release
- [ ] Publish release
- [ ] Upload to the app store
```

View File

@@ -1,71 +0,0 @@
## Introduction
### What about Deck ?
You may know Kanban website like Trello? Deck is about the same thing but secured and respectful of your privacy!
Integrated in Nextcloud, you can easily manage your projects while having your data secured.
### Use cases
Project management, time management or ideation, Deck makes it easier for you to manage your work.
## Using Deck
Overall, Deck is easy to use. You can create boards, add users, share the Deck, work collaboratively and in real time.
1. [Create my first board](#1-create-my-first-board)
2. [Create stacks and cards](#2-create-stacks-and-cards)
3. [Handle cards options](#3-handle-cards-options)
4. [Archive old tasks](#4-archive-old-tasks)
5. [Manage your board](#5-manage-your-board)
### 1. Create my first board
In this example, we're going to create a board and share it with an other nextcloud user.
![Gif for creating boards](resources/gifs/EN_create_board.gif)
### 2. Create stacks and cards
Stacks are simply columns with list of cards. It can represent a category of tasks or an y step in your projects for example.
**Check this out :**
![Gif for creating columns](resources/gifs/EN_create_columns.gif)
What about the cards? Cards are tasks, objects or ideas that fit into a stack. You can put a lot of cards in a stack! An infinity? Who knows! Who knows!
And all the magic of this software consists on moving your cards from a stack to an other.
**Check this out :**
![Gif for creating tasks](resources/gifs/EN_create_task.gif)
### 3. Handle cards options
Once you have created your cards, you can modify them or add options by clicking on them. So, what are the options? Well, there are several of them:
- Tag Management
- Assign a card to a user (s·he will receive a notification)
- Render date, or deadline
![Gif for puting infos on tasks](resources/gifs/EN_put_infos.gif)
And even :
- Description in markdown language
- Attachment - *you can leave a document, a picture or some other bonus like that.*
![Gif for puting infos on tasks 2](resources/gifs/EN_put_infos_2.gif)
### 4. Archive old tasks
Once finished or obsolete, a task could be archived. The tasks is not deleted, it's just archived, and you can retrieve it later
![Gif for puting infos on tasks 2](resources/gifs/EN_archive.gif)
### 5. Manage your board
You can manage the settings of your Deck once you are inside it, by clicking on the small wheel at the top right.
Once in this menu, you have access to several things:
- Sharing
- Tags
- Deleted objects
- Timeline
The **sharing tab** allows you to add users or even groups to your boards.
**Tags** allows you to modify the tags available for the cards.
**Deleted objects** allows you to return previously deleted stacks or cards.
The **Timeline** allows you to see everything that happened in your boards. Everything!

View File

@@ -1,3 +0,0 @@
.subnav ul {
padding-left: 20px;
}

View File

@@ -1 +0,0 @@
../README.md

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 556 KiB

View File

@@ -1,6 +0,0 @@
## Database structure
Deck stores most of its data inside of the database. The structure and relationships between entities is documented in the following ER diagram:
![Screenshot](resources/er-diagram.jpg)

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M16.5 6v11.5c0 2.21-1.79 4-4 4s-4-1.79-4-4V5c0-1.38 1.12-2.5 2.5-2.5s2.5 1.12 2.5 2.5v10.5c0 .55-.45 1-1 1s-1-.45-1-1V6H10v9.5c0 1.38 1.12 2.5 2.5 2.5s2.5-1.12 2.5-2.5V5c0-2.21-1.79-4-4-4S7 2.79 7 5v12.5c0 3.04 2.46 5.5 5.5 5.5s5.5-2.46 5.5-5.5V6h-1.5z"/><path d="M0 0h24v24H0z" fill="none"/></svg>

Before

Width:  |  Height:  |  Size: 390 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" version="1.1" height="32" viewbox="0 0 32 32"><path fill="#000" d="m8 2c-1.108 0-2 0.892-2 2v4c0 1.108 0.892 2 2 2s2-0.892 2-2v-4c0-1.108-0.892-2-2-2zm16 0c-1.108 0-2 0.892-2 2v4c0 1.108 0.892 2 2 2s2-0.892 2-2v-4c0-1.108-0.892-2-2-2zm-13 4v2c0 1.662-1.338 3-3 3s-3-1.338-3-3v-1.875a3.993 3.993 0 0 0 -3 3.875v16c0 2.216 1.784 4 4 4h20c2.216 0 4-1.784 4-4v-16a3.993 3.993 0 0 0 -3 -3.875v1.875c0 1.662-1.338 3-3 3s-3-1.338-3-3v-2zm-4.906 10h19.812a0.09 0.09 0 0 1 0.094 0.094v9.812a0.09 0.09 0 0 1 -0.094 0.094h-19.812a0.09 0.09 0 0 1 -0.094 -0.094v-9.812a0.09 0.09 0 0 1 0.094 -0.094z"/></svg>

Before

Width:  |  Height:  |  Size: 646 B

View File

@@ -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

View File

@@ -1 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 327 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 100 100"><path d="M91.645 8.355c-4.474-4.474-11.727-4.474-16.2 0l-13.5 13.501-3.727-3.727a5.015 5.015 0 1 0-7.093 7.093l3.727 3.727-41.51 41.508a11.411 11.411 0 0 0-3.329 7.324c-.073 1.087-.347 3.105-.675 5.292a1.748 1.748 0 0 1-.487.983l-3.105 3.106a2.546 2.546 0 0 0 0 3.6l3.493 3.493a2.546 2.546 0 0 0 3.6 0l3.106-3.105c.277-.275.622-.433.981-.486 2.187-.329 4.205-.602 5.293-.675a11.412 11.412 0 0 0 7.325-3.33l41.508-41.508 3.727 3.727a5.015 5.015 0 1 0 7.093-7.093L69.507 29.419l9.697 7.577 12.44-12.441c4.475-4.473 4.474-11.726.001-16.2zM65.051 42.749l-20.53 20.53a2.546 2.546 0 0 1-3.6 0l-3.27-3.27a2.545 2.545 0 0 0-3.599.001l-.616.616-.002-.002-14.728 14.727c-.337.337-.819.401-1.076.143s-.194-.74.143-1.076l23.841-23.841.004.004 15.633-15.633a2.546 2.546 0 0 1 3.6 0l4.2 4.201a2.546 2.546 0 0 1 0 3.6z"/></svg>

Before

Width:  |  Height:  |  Size: 897 B

View File

@@ -1 +0,0 @@
<svg width="15" height="15" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path d="M91.645 8.355c-4.474-4.474-11.727-4.474-16.2 0l-13.5 13.501-3.727-3.727a5.015 5.015 0 1 0-7.093 7.093l3.727 3.727-41.51 41.508a11.411 11.411 0 0 0-3.329 7.324c-.073 1.087-.347 3.105-.675 5.292a1.748 1.748 0 0 1-.487.983l-3.105 3.106a2.546 2.546 0 0 0 0 3.6l3.493 3.493a2.546 2.546 0 0 0 3.6 0l3.106-3.105c.277-.275.622-.433.981-.486 2.187-.329 4.205-.602 5.293-.675a11.412 11.412 0 0 0 7.325-3.33l41.508-41.508 3.727 3.727a5.015 5.015 0 1 0 7.093-7.093L69.507 29.419l9.697 7.577 12.44-12.441c4.475-4.473 4.474-11.726.001-16.2zM65.051 42.749l-20.53 20.53a2.546 2.546 0 0 1-3.6 0l-3.27-3.27a2.545 2.545 0 0 0-3.599.001l-.616.616-.002-.002-14.728 14.727c-.337.337-.819.401-1.076.143s-.194-.74.143-1.076l23.841-23.841.004.004 15.633-15.633a2.546 2.546 0 0 1 3.6 0l4.2 4.201a2.546 2.546 0 0 1 0 3.6z" fill="#fff"/></svg>

Before

Width:  |  Height:  |  Size: 910 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" version="1.1" height="16"><path fill="#000" d="m2.5 1c-0.28 0-0.5 0.22-0.5 0.5v13c0 0.28 0.22 0.5 0.5 0.5h11c0.28 0 0.5-0.22 0.5-0.5v-10.5l-3-3h-8.5zm1.5 2h6v1h-6v-1zm0 3h5v1h-5v-1zm0 3h8v1h-8v-1zm0 3h4v1h-4v-1z"/></svg>

Before

Width:  |  Height:  |  Size: 292 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 4.233 4.233"><g paint-order="stroke fill markers"><path d="M.52.465h3.283L2.631 1.918h-.99zM1.642 1.918h.992v1.866l-.996-.455z"/></g></svg>

Before

Width:  |  Height:  |  Size: 216 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 4.233 4.233" height="16" width="16"><path d="M.52.465h3.283L2.631 1.918h-.99zm1.122 1.453h.992v1.866l-.996-.455z" paint-order="stroke fill markers"/><ellipse ry=".691" rx=".674" cy="3.461" cx="3.45" fill="#000"/></svg>

Before

Width:  |  Height:  |  Size: 272 B

View File

@@ -1 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 128 B

View File

@@ -1 +0,0 @@
<svg width="16" height="16" version="1.1" viewBox="0 0 4.2333 4.2333" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -292.77)" display="none" stroke-width=".23666"><rect x=".28112" y="293.43" width="3.7042" height="1.1906" ry=".20225"/><rect x=".26458" y="295.15" width="3.7042" height="1.1906" ry=".20225"/></g><g transform="translate(0 -292.77)"><g transform="matrix(.040404 0 0 .040404 -3.0978 290.01)"><rect x="83.629" y="114.13" width="91.678" height="13.097" stroke-width="3.9049"/><path d="m155.25 81.388-26.194 26.194-26.194-26.154z" stroke-width="6.5484"/><path d="m155.25 159.97-26.194-26.194-26.194 26.154z" stroke-width="6.5484"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 671 B

View File

@@ -1 +0,0 @@
<svg width="16" height="16" version="1.1" viewBox="0 0 4.2333 4.2333" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -292.77)" display="none" stroke-width=".23666"><rect x=".28112" y="293.43" width="3.7042" height="1.1906" ry=".20225"/><rect x=".26458" y="295.15" width="3.7042" height="1.1906" ry=".20225"/></g><g transform="translate(0 -292.77)"><g transform="matrix(.040404 0 0 .040404 -3.0978 290.01)"><rect x="83.629" y="114.13" width="91.678" height="13.097" stroke-width="3.9049"/><path d="m155.25 107.58-26.194-26.194-26.194 26.154z" stroke-width="6.5484"/><path d="m155.25 133.78-26.194 26.194-26.194-26.154z" stroke-width="6.5484"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 671 B

79
issue_template.md Normal file
View File

@@ -0,0 +1,79 @@
### Steps to reproduce
1.
2.
3.
### Expected behaviour
Tell us what should happen
### Actual behaviour
Tell us what happens instead
### Server configuration
<!--
You can use the Issue Template application to prefill most of the required information: https://apps.nextcloud.com/apps/issuetemplate
-->
**Operating system**:
**Web server:**
**Database:**
**PHP version:**
**Nextcloud version:** (see Nextcloud admin page)
**Where did you install Nextcloud from:**
**Signing status:**
```
Login as admin user into your Nextcloud and access
http://example.com/index.php/settings/integrity/failed
paste the results here.
```
**List of activated apps:**
```
If you have access to your command line run e.g.:
sudo -u www-data php occ app:list
from within your Nextcloud installation folder
```
**Nextcloud configuration:**
```
If you have access to your command line run e.g.:
sudo -u www-data php occ config:list system
from within your Nextcloud installation folder
or
Insert your config.php content here
Make sure to remove all sensitive content such as passwords. (e.g. database password, passwordsalt, secret, smtp password, …)
```
**Are you using an external user-backend, if yes which one:** LDAP/ActiveDirectory/Webdav/...
### Client configuration
**Browser:**
**Operating system:**
### Logs
#### Nextcloud log (data/nextcloud.log)
```
Insert your Nextcloud log here
```
#### Browser log
```
Insert your browser log here, this could for example include:
a) The javascript console log
b) The network log
c) ...
```

3
js/.bowerrc Normal file
View File

@@ -0,0 +1,3 @@
{
"directory": "vendor"
}

49
js/.jshintrc Normal file
View File

@@ -0,0 +1,49 @@
{
"globals": {
"jasmine" : false,
"spyOn" : false,
"it" : false,
"describe" : false,
"expect" : false,
"beforeEach" : false,
"waits" : false,
"waitsFor" : false,
"runs" : false,
"require" : false,
"module": true
},
"asi" : true,
"boss" : true,
"browser" : true,
"curly" : true,
"debug" : true,
"devel" : true,
"eqeqeq" : true,
"eqnull" : false,
"es5" : true,
"evil" : false,
"forin" : true,
"immed" : true,
"indent" : 4,
"jquery" : true,
"latedef" : true,
"laxbreak" : false,
"newcap" : true,
"noarg" : true,
"node" : false,
"noempty" : false,
"nomen" : false,
"nonew" : true,
"onevar" : true,
"plusplus" : false,
"quotmark" : "single",
"regexp" : false,
"strict" : true,
"sub" : true,
"trailing" : true,
"undef" : true,
"unused" : true,
"white" : false,
"scripturl" : true
}

124
js/Gruntfile.js Normal file
View File

@@ -0,0 +1,124 @@
/*
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.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/>.
*
*/
/* global module */
module.exports = function(grunt) {
'use strict';
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-wrap');
grunt.loadNpmTasks('grunt-karma');
grunt.loadNpmTasks('grunt-phpunit');
grunt.initConfig({
meta: {
pkg: grunt.file.readJSON('package.json'),
version: '<%= meta.pkg.version %>',
configJS: 'config/',
buildJS: [
'app/**/*.js',
'controller/**/*.js',
'filters/**/*.js',
'directive/**/*.js',
'service/**/*.js'
],
productionJS: 'public/',
testsJS: '../tests/js/'
},
concat: {
options: {
stripBanners: true
},
dist: {
src: ['<%= meta.buildJS %>'],
dest: '<%= meta.productionJS %>app.js'
}
},
wrap: {
app: {
src: ['<%= meta.productionJS %>app.js'],
dest: '<%= meta.productionJS %>app.js',
option: {
wrapper: [
'(function(angular, $, oc_requesttoken, undefined){\n\n\'use strict\';\n\n',
'\n})(angular, jQuery, oc_requesttoken);'
]
}
}
},
jshint: {
files: [
'Gruntfile.js',
'<%= meta.buildJS %>**/*.js',
'<%= meta.testsJS %>**/*.js'
],
options: {
jshintrc: '.jshintrc',
reporter: require('jshint-stylish')
}
},
watch: {
concat: {
files: ['<%=meta.buildJS%>'],
options: {
livereload: true
},
tasks: ['build']
}
},
phpunit: {
classes: {
dir: '../tests/unit'
},
options: {
bootstrap: '../tests/bootstrap.php',
colors: true
}
},
karma: {
unit: {
configFile: '<%= meta.testsJS %>config/karma.js'
},
continuous: {
configFile: '<%= meta.testsJS %>config/karma.js',
browsers: ['Firefox'],
singleRun: true,
reporters: ['progress']
}
},
});
// make tasks available under simpler commands
grunt.registerTask('build', ['jshint', 'concat', 'wrap']);
grunt.registerTask('js-unit', ['karma:continuous']);
};

53
js/app/App.js Normal file
View File

@@ -0,0 +1,53 @@
/*
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.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/>.
*
*/
/* global angular */
angular.module('markdown', [])
.provider('markdown', [function () {
var opts = {};
return {
config: function (newOpts) {
opts = newOpts;
},
$get: function () {
return new window.showdown.Converter(opts);
}
};
}])
.filter('markdown', ['markdown', function (markdown) {
return function (text) {
return markdown.makeHtml(text || '');
};
}]);
var app = angular.module('Deck', [
'ngRoute',
'ngSanitize',
'ui.router',
'ui.select',
'as.sortable',
'mdMarkdownIt',
'ngAnimate'
]);

80
js/app/Config.js Normal file
View File

@@ -0,0 +1,80 @@
/*
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.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/>.
*
*/
/* global app oc_requesttoken markdownitLinkTarget */
app.config(function ($provide, $routeProvider, $interpolateProvider, $httpProvider, $urlRouterProvider, $stateProvider, $compileProvider, markdownItConverterProvider) {
'use strict';
$httpProvider.defaults.headers.common.requesttoken = oc_requesttoken;
$compileProvider.debugInfoEnabled(true);
markdownItConverterProvider.config({
breaks: true,
linkify: true,
xhtmlOut: true
});
markdownItConverterProvider.use(markdownitLinkTarget);
$urlRouterProvider.otherwise('/');
$stateProvider
.state('list', {
url: '/:filter',
templateUrl: '/boardlist.mainView.html',
controller: 'ListController',
reloadOnSearch: false,
params: {
filter: {value: '', dynamic: true}
}
})
.state('board', {
url: '/board/:boardId/:filter',
templateUrl: '/board.html',
controller: 'BoardController',
params: {
filter: {value: '', dynamic: true}
}
})
.state('board.detail', {
url: '/detail/',
reloadOnSearch: false,
params: {
tab: {value: 0, dynamic: true},
},
views: {
'sidebarView': {
templateUrl: '/board.sidebarView.html'
}
}
})
.state('board.card', {
url: '/card/:cardId',
views: {
'sidebarView': {
templateUrl: '/card.sidebarView.html',
controller: 'CardController'
}
}
});
});

82
js/app/Run.js Normal file
View File

@@ -0,0 +1,82 @@
/*
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.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/>.
*
*/
/* global Snap */
app.run(function ($document, $rootScope, $transitions, BoardService) {
'use strict';
$document.click(function (event) {
$rootScope.$broadcast('documentClicked', event);
});
$transitions.onEnter({from: 'list'}, function ($state, $transition$) {
BoardService.unsetCurrrent();
});
$transitions.onEnter({to: 'list'}, function ($state, $transition$) {
BoardService.unsetCurrrent();
document.title = "Deck - " + oc_defaults.name;
});
$transitions.onEnter({to: 'board.card'}, function ($state, $transition$) {
$rootScope.sidebar.show = true;
});
$transitions.onEnter({to: 'board.detail'}, function ($state, $transition$) {
$rootScope.sidebar.show = true;
});
$transitions.onEnter({to: 'board'}, function ($state) {
$rootScope.sidebar.show = false;
});
$transitions.onExit({from: 'board.card'}, function ($state) {
$rootScope.sidebar.show = false;
});
$transitions.onExit({from: 'board.detail'}, function ($state) {
$rootScope.sidebar.show = false;
});
$('link[rel="shortcut icon"]').attr(
'href',
OC.filePath('deck', 'img', 'app-512.png')
);
$('#app-navigation-toggle').off('click');
// App sidebar on mobile
var snapper = new Snap({
element: document.getElementById('app-content'),
disable: 'right',
maxPosition: 250,
touchToDrag: false
});
$('#app-navigation-toggle').click(function () {
if ($(window).width() > 768) {
$('#app-navigation').toggle('hidden');
} else {
if (snapper.state().state === 'left') {
snapper.close();
} else {
snapper.open('left');
}
}
});
// Select all elements with data-toggle="tooltips" in the document
$('body').tooltip({
selector: '[data-toggle="tooltip"]'
});
});

29
js/bower.json Normal file
View File

@@ -0,0 +1,29 @@
{
"name": "deck",
"version": "0.0.1",
"dependencies": {
"angular": "~1.6.1",
"angular-route": "~1.6.1",
"angular-mocks": "~1.6.1",
"angular-sanitize": "~1.6.1",
"angular-animate": "~1.6.1",
"ng-sortable": "1.3.8",
"jquery": "3.2.x",
"es6-shim": "~0.*",
"js-url": "~2.*",
"angular-ui-select": "~0.19.6",
"angular-markdown-it": "~0.6.1",
"angular-ui-router": "~1.0.0",
"markdown-it-link-target": "~1.0.1",
"jquery-timepicker": "883bb2cd94"
},
"license": "AGPL-3.0",
"private": true,
"ignore": [
"'**/.*",
"node_modules",
"bower_components",
"test",
"tests"
]
}

View File

@@ -1,36 +1,30 @@
<?php
/**
* @copyright Copyright (c) 2018 Ryan Fletcher <ryan.fletcher@codepassion.ca>
/*
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
*
* @author Ryan Fletcher <ryan.fletcher@codepassion.ca>
* @author Julius Härtl <jus@bitgrid.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;
use OCP\AppFramework\Http;
class BadRequestException extends StatusException {
public function __construct($message) {
parent::__construct($message);
}
public function getStatus() {
return HTTP::STATUS_BAD_REQUEST;
}
}
/** global: OC */
app.controller('AppController', function ($scope, $location, $http, $route, $log, $rootScope) {
$rootScope.sidebar = {
show: false
};
$scope.sidebar = $rootScope.sidebar;
$scope.user = oc_current_user;
});

View File

@@ -0,0 +1,339 @@
/*
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.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/>.
*
*/
/* global oc_defaults OC */
app.controller('BoardController', function ($rootScope, $scope, $stateParams, StatusService, BoardService, StackService, CardService, LabelService, $state, $transitions, $filter) {
$scope.sidebar = $rootScope.sidebar;
$scope.id = $stateParams.boardId;
$scope.status = {
addCard: [],
};
$scope.newLabel = {};
$scope.OC = OC;
$scope.stackservice = StackService;
$scope.boardservice = BoardService;
$scope.cardservice = CardService;
$scope.statusservice = StatusService.getInstance();
$scope.labelservice = LabelService;
$scope.defaultColors = ['31CC7C', '317CCC', 'FF7A66', 'F1DB50', '7C31CC', 'CC317C', '3A3B3D', 'CACBCD'];
$scope.board = BoardService.getCurrent();
// workaround for $stateParams changes not being propagated
$scope.$watch(function() {
return $state.params;
}, function (params) {
$scope.params = params;
}, true);
$scope.params = $state;
$scope.search = function (searchText) {
$scope.searchText = searchText;
$scope.refreshData();
};
$scope.$watch(function () {
if (typeof BoardService.getCurrent() !== 'undefined') {
return BoardService.getCurrent().title;
} else {
return null;
}
}, function () {
$scope.setPageTitle();
});
$scope.setPageTitle = function () {
if (BoardService.getCurrent()) {
document.title = BoardService.getCurrent().title + ' | Deck - ' + oc_defaults.name;
} else {
document.title = 'Deck - ' + oc_defaults.name;
}
};
$scope.statusservice.retainWaiting();
$scope.statusservice.retainWaiting();
// handle filter parameter for switching between archived/unarchived cards
$scope.switchFilter = function (filter) {
$state.go('.', {filter: filter});
};
$scope.$watch(function() {
return $scope.params.filter;
}, function (filter) {
if (filter === 'archive') {
$scope.loadArchived();
} else {
$scope.loadDefault();
}
});
$scope.stacksData = StackService;
$scope.stacks = [];
$scope.$watch('stacksData', function () {
$scope.refreshData();
}, true);
$scope.refreshData = function () {
if ($scope.params.filter === 'archive') {
$scope.filterData('-lastModified', $scope.searchText);
} else {
$scope.filterData('order', $scope.searchText);
}
};
$scope.checkCanEdit = function () {
return !BoardService.getCurrent().archived;
};
// filter cards here, as ng-sortable will not work nicely with html-inline filters
$scope.filterData = function (order, text) {
if ($scope.stacks === undefined) {
return;
}
angular.copy(StackService.getData(), $scope.stacks);
$scope.stacks = $filter('orderBy')($scope.stacks, 'order');
angular.forEach($scope.stacks, function (value, key) {
var cards = $filter('cardSearchFilter')(value.cards, text);
cards = $filter('orderBy')(cards, order);
$scope.stacks[key].cards = cards;
});
};
$scope.loadDefault = function () {
StackService.fetchAll($scope.id).then(function (data) {
$scope.statusservice.releaseWaiting();
}, function (error) {
$scope.statusservice.setError('Error occured', error);
});
};
$scope.loadArchived = function () {
StackService.fetchArchived($scope.id).then(function (data) {
$scope.statusservice.releaseWaiting();
}, function (error) {
$scope.statusservice.setError('Error occured', error);
});
};
// Handle initial Loading
BoardService.fetchOne($scope.id).then(function (data) {
$scope.statusservice.releaseWaiting();
$scope.setPageTitle();
}, function (error) {
$scope.statusservice.setError('Error occured', error);
});
$scope.searchForUser = function (search) {
BoardService.searchUsers(search);
};
$scope.newStack = {'boardId': $scope.id};
$scope.newCard = {};
// Create a new Stack
$scope.createStack = function () {
StackService.create($scope.newStack).then(function (data) {
$scope.newStack.title = "";
});
};
$scope.createCard = function (stack, title) {
var newCard = {
'title': title,
'stackId': stack,
'type': 'plain'
};
CardService.create(newCard).then(function (data) {
$scope.stackservice.addCard(data);
$scope.newCard.title = "";
});
};
$scope.cardDelete = function (card) {
OC.dialogs.confirm(t('deck', 'Are you sure you want to delete this card with all of its data?'), t('deck', 'Delete'), function(state) {
if (!state) {
return;
}
CardService.delete(card.id).then(function () {
StackService.removeCard(card);
});
});
};
$scope.cardArchive = function (card) {
CardService.archive(card);
StackService.removeCard(card);
};
$scope.cardUnarchive = function (card) {
CardService.unarchive(card);
StackService.removeCard(card);
};
$scope.labelDelete = function (label) {
LabelService.delete(label.id);
// remove from board data
var i = BoardService.getCurrent().labels.indexOf(label);
BoardService.getCurrent().labels.splice(i, 1);
// TODO: remove from cards
};
$scope.labelCreate = function (label) {
label.boardId = $scope.id;
LabelService.create(label).then(function (data) {
$scope.newStack.title = "";
BoardService.getCurrent().labels.push(data);
$scope.status.createLabel = false;
$scope.newLabel = {};
});
};
$scope.labelUpdate = function (label) {
label.edit = false;
LabelService.update(label);
};
$scope.aclAdd = function (sharee) {
sharee.boardId = $scope.id;
BoardService.addAcl(sharee);
$scope.status.addSharee = null;
};
$scope.aclDelete = function (acl) {
BoardService.deleteAcl(acl).then(function(data) {
$scope.loadDefault();
$scope.refreshData();
});
};
$scope.aclUpdate = function (acl) {
BoardService.updateAcl(acl);
};
$scope.aclTypeString = function (acl) {
if (typeof acl === 'undefined') {
return '';
}
switch (acl.type) {
case OC.Share.SHARE_TYPE_USER:
return 'user';
case OC.Share.SHARE_TYPE_GROUP:
return 'group';
default:
return '';
}
};
// settings for card sorting
$scope.sortOptions = {
id: 'card',
itemMoved: function (event) {
event.source.itemScope.modelValue.status = event.dest.sortableScope.$parent.column;
var order = event.dest.index;
var card = event.source.itemScope.c;
var newStack = event.dest.sortableScope.$parent.s.id;
var oldStack = card.stackId;
card.stackId = newStack;
CardService.update(card);
CardService.reorder(card, order).then(function (data) {
StackService.addCard(card);
StackService.reorderCard(card, order);
StackService.removeCard({
id: card.id,
stackId: oldStack
});
});
},
orderChanged: function (event) {
var order = event.dest.index;
var card = event.source.itemScope.c;
var stack = event.dest.sortableScope.$parent.s.id;
CardService.reorder(card, order).then(function (data) {
StackService.reorderCard(card, order);
$scope.refreshData();
});
},
scrollableContainer: '#innerBoard',
containerPositioning: 'relative',
containment: '#innerBoard',
longTouch: true,
// auto scroll on drag
dragMove: function (itemPosition, containment, eventObj) {
if (eventObj) {
var container = $("#board");
var offset = container.offset();
var targetX = eventObj.pageX - (offset.left || container.scrollLeft());
var targetY = eventObj.pageY - (offset.top || container.scrollTop());
if (targetX < offset.left) {
container.scrollLeft(container.scrollLeft() - 25);
} else if (targetX > container.width()) {
container.scrollLeft(container.scrollLeft() + 25);
}
if (targetY < offset.top) {
container.scrollTop(container.scrollTop() - 25);
} else if (targetY > container.height()) {
container.scrollTop(container.scrollTop() + 25);
}
}
},
accept: function (sourceItemHandleScope, destSortableScope, destItemScope) {
return sourceItemHandleScope.sortableScope.options.id === 'card';
}
};
$scope.sortOptionsStack = {
id: 'stack',
orderChanged: function (event) {
var order = event.dest.index;
var stack = event.source.itemScope.s;
StackService.reorder(stack, order).then(function (data) {
$scope.refreshData();
});
},
scrollableContainer: '#board',
containerPositioning: 'relative',
containment: '#innerBoard',
dragMove: function (itemPosition, containment, eventObj) {
if (eventObj) {
var container = $("#board");
var offset = container.offset();
var targetX = eventObj.pageX - (offset.left || container.scrollLeft());
var targetY = eventObj.pageY - (offset.top || container.scrollTop());
if (targetX < offset.left) {
container.scrollLeft(container.scrollLeft() - 50);
} else if (targetX > container.width()) {
container.scrollLeft(container.scrollLeft() + 50);
}
if (targetY < offset.top) {
container.scrollTop(container.scrollTop() - 50);
} else if (targetY > container.height()) {
container.scrollTop(container.scrollTop() + 50);
}
}
},
accept: function (sourceItemHandleScope, destSortableScope, destItemScope) {
return sourceItemHandleScope.sortableScope.options.id === 'stack';
}
};
$scope.labelStyle = function (color) {
return {
'background-color': '#' + color,
'color': $filter('textColorFilter')(color)
};
};
});

View File

@@ -0,0 +1,187 @@
/*
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.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/>.
*
*/
/* global app moment */
app.controller('CardController', function ($scope, $rootScope, $routeParams, $location, $stateParams, $interval, $timeout, $filter, BoardService, CardService, StackService, StatusService) {
$scope.sidebar = $rootScope.sidebar;
$scope.status = {
lastEdit: 0,
lastSave: Date.now()
};
$scope.cardservice = CardService;
$scope.cardId = $stateParams.cardId;
$scope.statusservice = StatusService.getInstance();
$scope.boardservice = BoardService;
$scope.statusservice.retainWaiting();
CardService.fetchOne($scope.cardId).then(function (data) {
$scope.statusservice.releaseWaiting();
$scope.archived = CardService.getCurrent().archived;
}, function (error) {
});
$scope.cardRenameShow = function () {
if ($scope.archived || !BoardService.canEdit())
{return false;}
else {
$scope.status.cardRename = true;
}
};
$scope.cardEditDescriptionShow = function ($event) {
if (BoardService.isArchived() || CardService.getCurrent().archived) {
return false;
}
if ($scope.card.archived || !$scope.boardservice.canEdit()) {
return false;
}
$scope.status.cardEditDescription = true;
$scope.status.edit = angular.copy(CardService.getCurrent());
return true;
};
$scope.cardEditDescriptionChanged = function ($event) {
$scope.status.lastEdit = Date.now();
var header = $('.section-header.card-description');
header.find('.save-indicator.unsaved').show();
header.find('.save-indicator.saved').hide();
};
$interval(function() {
var currentTime = Date.now();
var timeSinceEdit = currentTime-$scope.status.lastEdit;
if (timeSinceEdit > 1000 && $scope.status.lastEdit > $scope.status.lastSave) {
$scope.status.lastSave = currentTime;
var header = $('.section-header.card-description');
header.find('.save-indicator.unsaved').fadeIn(500);
CardService.update($scope.status.edit).then(function (data) {
var header = $('.section-header.card-description');
header.find('.save-indicator.unsaved').hide();
header.find('.save-indicator.saved').fadeIn(250).fadeOut(1000);
StackService.updateCard($scope.status.edit);
});
}
}, 500, 0, false);
// handle rename to update information on the board as well
$scope.cardRename = function (card) {
CardService.rename(card).then(function (data) {
StackService.updateCard(card);
$scope.status.renameCard = false;
});
};
$scope.cardUpdate = function (card) {
CardService.update(card).then(function (data) {
$scope.status.cardEditDescription = false;
var header = $('.section-content.card-description');
header.find('.save-indicator.unsaved').hide();
header.find('.save-indicator.saved').fadeIn(500).fadeOut(1000);
StackService.updateCard(card);
});
};
$scope.labelAssign = function (element, model) {
CardService.assignLabel($scope.cardId, element.id).then(function (data) {
StackService.updateCard(CardService.getCurrent());
});
};
$scope.labelRemove = function (element, model) {
CardService.removeLabel($scope.cardId, element.id).then(function (data) {
StackService.updateCard(CardService.getCurrent());
});
};
$scope.setDuedate = function (duedate) {
var element = CardService.getCurrent();
var newDate = moment(element.duedate);
if(!newDate.isValid()) {
newDate = moment();
}
newDate.date(duedate.date());
newDate.month(duedate.month());
newDate.year(duedate.year());
element.duedate = newDate.toISOString();
CardService.update(element);
StackService.updateCard(element);
};
$scope.setDuedateTime = function (time) {
var element = CardService.getCurrent();
var newDate = moment(element.duedate);
if(!newDate.isValid()) {
newDate = moment();
}
newDate.hour(time.hour());
newDate.minute(time.minute());
element.duedate = newDate.toISOString();
CardService.update(element);
StackService.updateCard(element);
};
$scope.resetDuedate = function () {
var element = CardService.getCurrent();
element.duedate = null;
CardService.update(element);
StackService.updateCard(element);
};
/**
* Show ui-select field when clicking the add button
*/
$scope.toggleAssignUser = function() {
$scope.status.showAssignUser = !$scope.status.showAssignUser;
if ($scope.status.showAssignUser === true) {
$timeout(function () {
$('#assignUserSelect').find('a').click();
});
}
};
/**
* Hide ui-select when select list is closed
*/
$scope.assingUserOpenClose = function(isOpen) {
$scope.status.showAssignUser = isOpen;
};
$scope.addAssignedUser = function(item) {
CardService.assignUser(CardService.getCurrent(), item.uid).then(function (data) {
StackService.updateCard(CardService.getCurrent());
});
$scope.status.showAssignUser = false;
};
$scope.removeAssignedUser = function(uid) {
CardService.unassignUser(CardService.getCurrent(), uid).then(function (data) {
StackService.updateCard(CardService.getCurrent());
});
};
$scope.labelStyle = function (color) {
return {
'background-color': '#' + color,
'color': $filter('textColorFilter')(color)
};
};
});

View File

@@ -0,0 +1,197 @@
/*
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.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/>.
*
*/
/* global app angular */
app.controller('ListController', function ($scope, $location, $filter, BoardService, $element, $timeout, $stateParams, $state, StatusService) {
function calculateNewColor() {
var boards = BoardService.getAll();
var boardKeys = Object.keys(boards);
var colorOccurrences = [];
for (var i = 0; i < $scope.colors.length; i++) {
colorOccurrences.push(0);
}
for (var j = 0; j < boardKeys.length; j++) {
var key = boardKeys[j];
var board = boards[key];
if (board && $scope.colors.indexOf(board.color) !== -1) {
colorOccurrences[$scope.colors.indexOf(board.color)]++;
}
}
return $scope.colors[colorOccurrences.indexOf(Math.min.apply(Math, colorOccurrences))];
}
$scope.boards = [];
$scope.newBoard = {};
$scope.status = {
deleteUndo: [],
filter: $stateParams.filter ? $stateParams.filter : '',
sidebar: false
};
$scope.colors = ['0082c9', '00c9c6','00c906', 'c92b00', 'F1DB50', '7C31CC', '3A3B3D', 'CACBCD'];
$scope.boardservice = BoardService;
$scope.updatingBoard = null;
var filterData = function () {
if($element.attr('id') === 'app-navigation') {
$scope.boardservice.sidebar = $scope.boardservice.getData();
$scope.boardservice.sidebar = $filter('orderBy')($scope.boardservice.sidebar, 'title');
$scope.boardservice.sidebar = $filter('cardFilter')($scope.boardservice.sidebar, {archived: false});
} else {
$scope.boardservice.sorted = $scope.boardservice.getData();
if ($scope.status.filter === 'archived') {
var filter = {};
filter[$scope.status.filter] = true;
$scope.boardservice.sorted = $filter('cardFilter')($scope.boardservice.sorted, filter);
} else if ($scope.status.filter === 'shared') {
$scope.boardservice.sorted = $filter('cardFilter')($scope.boardservice.sorted, {archived: false});
$scope.boardservice.sorted = $filter('boardFilterAcl')($scope.boardservice.sorted);
} else {
$scope.boardservice.sorted = $filter('cardFilter')($scope.boardservice.sorted, {archived: false});
}
$scope.boardservice.sorted = $filter('orderBy')($scope.boardservice.sorted, ['deletedAt', 'title']);
}
};
var finishedLoading = function() {
filterData();
$scope.newBoard.color = calculateNewColor();
};
var initialize = function () {
$scope.statusservice = StatusService.listStatus;
if($element.attr('id') === 'app-navigation') {
$scope.statusservice.retainWaiting();
BoardService.fetchAll().then(function(data) {
finishedLoading();
$scope.statusservice.releaseWaiting();
BoardService.loaded = true;
}, function (error) {
$scope.statusservice.setError('Error occured', error);
});
} else {
/* initialize main list controller when board list is loaded */
var boardDataWatch = $scope.$watch(function () {
return $scope.boardservice.loaded;
}, function () {
if (BoardService.loaded === true) {
boardDataWatch();
finishedLoading();
}
});
}
$scope.$watch(function () {
return $scope.boardservice.data;
}, function () {
filterData();
}, true);
/* Watch for board filter change */
$scope.$watchCollection(function(){
return $state.params;
}, function(){
$scope.status.filter = $state.params.filter;
filterData();
});
};
initialize();
$scope.selectColor = function(color) {
$scope.newBoard.color = color;
};
$scope.gotoBoard = function(board) {
if(board.deletedAt > 0) {
return false;
}
return $state.go('board', {boardId: board.id});
};
$scope.boardCreate = function() {
if(!$scope.newBoard.title || !$scope.newBoard.color) {
$scope.status.addBoard=false;
return;
}
BoardService.create($scope.newBoard)
.then(function (response) {
$scope.newBoard = {};
$scope.newBoard.color = calculateNewColor();
$scope.status.addBoard=false;
filterData();
}, function(error) {
$scope.status.createBoard = 'Unable to insert board: ' + error.message;
});
};
$scope.boardUpdate = function(board) {
BoardService.update(board).then(function(data) {
board.status.edit = false;
filterData();
});
};
$scope.boardUpdateBegin = function(board) {
$scope.updatingBoard = angular.copy(board);
};
$scope.boardUpdateReset = function(board) {
board.title = $scope.updatingBoard.title;
board.color = $scope.updatingBoard.color;
filterData();
board.status.edit = false;
};
$scope.boardArchive = function (board) {
board.archived = true;
BoardService.update(board).then(function(data) {
filterData();
});
};
$scope.boardUnarchive = function (board) {
board.archived = false;
BoardService.update(board).then(function(data) {
filterData();
});
};
$scope.boardDelete = function(board) {
BoardService.delete(board.id).then(function (data) {
filterData();
});
};
$scope.boardDeleteUndo = function (board) {
BoardService.deleteUndo(board.id).then(function (data) {
filterData();
});
};
});

View File

@@ -0,0 +1,47 @@
/*
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.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/>.
*
*/
app.directive('appPopoverMenuUtils', function () {
'use strict';
return {
restrict: 'C',
link: function (scope, elm) {
var menu = elm.find('.popovermenu');
var button = elm.find('button');
button.click(function (e) {
var popovermenus = $('.popovermenu');
var shouldShow = menu.hasClass('hidden');
popovermenus.addClass('hidden');
if (shouldShow) {
menu.toggleClass('hidden');
}
e.stopPropagation();
});
scope.$on('documentClicked', function (scope, event) {
/* prevent closing popover if target has no-close class */
if (event.target !== button && !$(event.target).hasClass('no-close')) {
menu.addClass('hidden');
}
});
}
};
});

View File

@@ -0,0 +1,46 @@
/*
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.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/>.
*
*/
// OwnCloud Click Handling
// https://doc.owncloud.org/server/8.0/developer_manual/app/css.html
app.directive('appNavigationEntryUtils', function () {
'use strict';
return {
restrict: 'C',
link: function (scope, elm) {
var menu = elm.siblings('.app-navigation-entry-menu');
var button = $(elm)
.find('.app-navigation-entry-utils-menu-button button');
button.click(function () {
menu.toggleClass('open');
});
scope.$on('documentClicked', function (scope, event) {
if (event.target !== button[0]) {
menu.removeClass('open');
}
});
}
};
});

View File

@@ -2,27 +2,27 @@
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
*
* @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
*
*
* 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/>.
*
*
*/
@import 'icons';
@import 'print';
app.directive('autofocusOnInsert', function () {
'use strict';
return function (scope, elm) {
elm.focus();
};
});

50
js/directive/avatar.js Normal file
View File

@@ -0,0 +1,50 @@
/*
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.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/>.
*
*/
app.directive('avatar', function() {
'use strict';
return {
restrict: 'AEC',
transclude: true,
replace: true,
template: '<div class="avatardiv-container"><div class="avatardiv" data-toggle="tooltip" ng-transclude></div></div>',
scope: { attr: '=' },
link: function(scope, element, attr){
scope.uid = attr.displayname;
scope.displayname = attr.displayname;
var value = attr.user;
var avatardiv = $(element).find('.avatardiv');
if(typeof attr.contactsmenu !== 'undefined' && attr.contactsmenu !== 'false') {
avatardiv.contactsMenu(value, 0, $(element));
avatardiv.addClass('has-contactsmenu');
}
if(typeof attr.tooltip !== 'undefined' && attr.tooltip !== 'false') {
$(element).tooltip({
title: scope.displayname,
placement: 'top'
});
}
avatardiv.avatar(value, 32, false, false, false, attr.displayname);
},
controller: function () {}
};
});

View File

@@ -0,0 +1,41 @@
/*
* @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.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/>.
*
*/
app.directive('contactsmenudelete', function() {
'use strict';
return {
restrict: 'A',
priority: 1,
link: function(scope, element, attr){
var user = attr.user;
var menu = $(element).parent().find('.contactsmenu-popover');
if (oc_current_user === user) {
menu.children(':first').remove();
}
var menuEntry = $('<li><a><span class="icon icon-delete"></span><span>' + t('deck', 'Remove user from card') + '</span></a></li>');
menuEntry.on('click', function () {
scope.removeAssignedUser(user);
});
$(menu).append(menuEntry);
}
};
});

View File

@@ -0,0 +1,53 @@
/*
* @copyright Copyright (c) 2017 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.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/>.
*
*/
/* global app */
/* gloabl t */
/* global moment */
app.directive('datepicker', function () {
'use strict';
return {
link: function (scope, elm, attr) {
return elm.datepicker({
dateFormat: 'yy-mm-dd',
onSelect: function(date, inst) {
scope.setDuedate(moment(date));
scope.$apply();
},
beforeShow: function(input, inst) {
var dp, marginLeft;
dp = $(inst).datepicker('widget');
marginLeft = -Math.abs($(input).outerWidth() - dp.outerWidth()) / 2 + 'px';
dp.css({
'margin-left': marginLeft
});
$('div.ui-datepicker:before').css({
'left': 100 + 'px'
});
return $('.hasDatepicker').datepicker();
},
minDate: null
});
}
};
});

40
js/directive/elastic.js Normal file
View File

@@ -0,0 +1,40 @@
/*
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.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/>.
*
*/
// original idea from blockloop: http://stackoverflow.com/a/24090733
app.directive('elastic', [
'$timeout',
function($timeout) {
return {
restrict: 'A',
link: function($scope, element) {
$scope.initialHeight = $scope.initialHeight || element[0].style.height;
var resize = function() {
element[0].style.height = $scope.initialHeight;
element[0].style.height = "" + element[0].scrollHeight + "px";
};
element.on("input change", resize);
$timeout(resize, 0);
}
};
}
]);

50
js/directive/search.js Normal file
View File

@@ -0,0 +1,50 @@
/*
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.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/>.
*
*/
app.directive('search', function ($document, $location) {
'use strict';
return {
restrict: 'E',
scope: {
'onSearch': '='
},
link: function (scope) {
var box = $('#searchbox');
box.val($location.search().search);
var doSearch = function() {
var value = box.val();
scope.$apply(function () {
scope.onSearch(value);
});
};
box.on('search keyup', function (event) {
if (event.type === 'search' || event.keyCode === 13 ) {
doSearch();
}
});
}
};
});

View File

@@ -1,5 +1,4 @@
<?php
/**
/*
* @copyright Copyright (c) 2017 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.net>
@@ -21,25 +20,26 @@
*
*/
namespace OCA\Deck\Db;
/* global app */
/* global t */
/* global moment */
class Circle extends RelationalObject {
/** @var \OCA\Circles\Model\Circle */
protected $object;
public function __construct(\OCA\Circles\Model\Circle $circle) {
$primaryKey = $circle->getUniqueId();
parent::__construct($primaryKey, $circle);
}
public function getObjectSerialization() {
return [
'uid' => $this->object->getUniqueId(),
'displayname' => $this->object->getName(),
'typeString' => $this->object->getTypeString(),
'circleOwner' => $this->object->getOwner(),
'type' => 7
];
}
}
app.directive('timepicker', function() {
'use strict';
return {
restrict: 'A',
link: function(scope, elm, attr) {
return elm.timepicker({
onSelect: function(date, inst) {
scope.setDuedateTime(moment('2000-01-01 ' + date));
scope.$apply();
},
myPosition: 'center top',
atPosition: 'center bottom',
hourText: t('deck', 'Hours'),
minuteText: t('deck', 'Minutes'),
showPeriodLabels: false
});
}
};
});

View File

@@ -0,0 +1,33 @@
/*
* @copyright Copyright (c) 2017 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.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/>.
*
*/
app.filter('boardFilterAcl', function() {
return function(boards) {
var _result = [];
angular.forEach(boards, function(board){
if(board.acl !== null && Object.keys(board.acl).length > 0) {
_result.push(board);
}
});
return _result;
};
});

39
js/filters/cardFilter.js Normal file
View File

@@ -0,0 +1,39 @@
/*
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.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/>.
*
*/
// usage | cardFilter({ member: 'admin'})
app.filter('cardFilter', function() {
return function(cards, rules) {
var _result = [];
angular.forEach(cards, function(card){
var _card = card;
var keys = Object.keys(rules);
keys.some(function(key, condition) {
if(_card[key]===rules[key]) {
_result.push(_card);
}
});
});
return _result;
};
});

View File

@@ -0,0 +1,43 @@
/*
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.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/>.
*
*/
app.filter('cardSearchFilter', function() {
return function(cards, searchString) {
var _result = {};
var rules = {
title: searchString,
//owner: searchString,
};
angular.forEach(cards, function(card){
var _card = card;
Object.keys(rules).some(function(rule) {
if(_card[rule].search(rules[rule])>=0) {
_result[_card.id] = _card;
}
});
});
return $.map(_result, function(value, index) {
return [value];
});
};
});

61
js/filters/dateFilters.js Normal file
View File

@@ -0,0 +1,61 @@
/*
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.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/>.
*
*/
/* global app */
/* global OC */
/* global moment */
app.filter('relativeDateFilter', function() {
return function (timestamp) {
return OC.Util.relativeModifiedDate(timestamp*1000);
};
});
app.filter('relativeDateFilterString', function() {
return function (date) {
return OC.Util.relativeModifiedDate(Date.parse(date));
};
});
app.filter('dateToTimestamp', function() {
return function (date) {
return Date.parse(date);
};
});
app.filter('parseDate', function() {
return function (date) {
if(moment(date).isValid()) {
return moment(date).format('YYYY-MM-DD');
}
return '';
};
});
app.filter('parseTime', function() {
return function (date) {
if(moment(date).isValid()) {
return moment(date).format('HH:mm');
}
return '';
};
});

View File

@@ -0,0 +1,66 @@
/*
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.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/>.
*
*/
app.filter('iconWhiteFilter', function () {
return function (hex) {
// RGB2HLS by Garry Tan
// http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
var result = /^([A-Fa-f\d]{2})([A-Fa-f\d]{2})([A-Fa-f\d]{2})$/i.exec(hex);
var color = result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
if (result === null) {
return "";
}
var r = color.r / 255;
var g = color.g / 255;
var b = color.b / 255;
var max = Math.max(r, g, b), min = Math.min(r, g, b);
var h, s, l = (max + min) / 2;
if (max === min) {
h = s = 0; // achromatic
} else {
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}
if (l < 0.5) {
return "-white";
} else {
return "";
}
};
});

View File

@@ -0,0 +1,37 @@
/*
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.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/>.
*
*/
app.filter('lightenColorFilter', function() {
return function (hex) {
var result = /^([A-Fa-f\d]{2})([A-Fa-f\d]{2})([A-Fa-f\d]{2})$/i.exec(hex);
var color = result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
if (result !== null) {
return "rgba(" + color.r + "," + color.g + "," + color.b + ",0.7)";
} else {
return "#" + hex;
}
};
});

View File

@@ -0,0 +1,42 @@
/*
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.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/>.
*
*/
app.filter('orderObjectBy', function(){
return function(input, attribute) {
if (!angular.isObject(input)) {
return input;
}
var array = [];
for(var objectKey in input) {
if ({}.hasOwnProperty.call(input, objectKey)) {
array.push(input[objectKey]);
}
}
array.sort(function(a, b){
a = parseInt(a[attribute]);
b = parseInt(b[attribute]);
return a < b;
});
return array;
};
});

View File

@@ -0,0 +1,68 @@
/*
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.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/>.
*
*/
app.filter('textColorFilter', function () {
return function (hex) {
// RGB2HLS by Garry Tan
// http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
var result = /^#?([A-Fa-f\d]{2})([A-Fa-f\d]{2})([A-Fa-f\d]{2})$/i.exec(hex);
var color = result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
if (result !== null) {
var r = color.r / 255;
var g = color.g / 255;
var b = color.b / 255;
var max = Math.max(r, g, b), min = Math.min(r, g, b);
var h, s, l = (max + min) / 2;
if (max === min) {
h = s = 0; // achromatic
} else {
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}
if (l < 0.5) {
return "#ffffff";
} else {
return "#000000";
}
} else {
return "#000000";
}
};
});

View File

@@ -0,0 +1,45 @@
/*
* @copyright Copyright (c) 2017 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.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/>.
*
*/
/* global app */
/* global angular */
/*
* Remove all assignedUsers from users list
*/
app.filter('withoutAssignedUsers', function () {
return function (users, assignedUsers) {
var _result = [];
angular.forEach(users, function (user) {
var _found = false;
angular.forEach(assignedUsers, function (assignedUser) {
if (assignedUser.participant.uid === user.uid) {
_found = true;
}
});
if (_found === false) {
_result.push(user);
}
});
return _result;
};
});

29
js/package.json Normal file
View File

@@ -0,0 +1,29 @@
{
"name": "deck",
"version": "1.0.0",
"main": "Gruntfile.js",
"directories": {
"test": "tests"
},
"dependencies": {},
"devDependencies": {
"bower": "^1.8.0",
"grunt": "^1.0.1",
"grunt-contrib-concat": "^1.0.1",
"grunt-contrib-jshint": "^1.1.0",
"grunt-contrib-watch": "^1.0.0",
"grunt-karma": "^2.0.0",
"grunt-phpunit": "^0.3.6",
"grunt-wrap": "^0.3.0",
"jshint-stylish": "^2.2.1",
"karma": "^1.4.1",
"node-sass": "^4.5.3"
},
"scripts": {
"test": "echo \"Warning: no test specified\" && exit 0"
},
"author": "",
"license": "AGPL-3.0",
"keywords": [],
"description": ""
}

173
js/service/ApiService.js Normal file
View File

@@ -0,0 +1,173 @@
/*
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.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/>.
*
*/
/** global: oc_defaults */
app.factory('ApiService', function ($http, $q) {
var ApiService = function (http, endpoint) {
this.endpoint = endpoint;
this.baseUrl = OC.generateUrl('/apps/deck/' + endpoint);
this.http = http;
this.q = $q;
this.data = {};
this.id = null;
this.sorted = [];
};
ApiService.prototype.fetchAll = function () {
var deferred = $q.defer();
var self = this;
$http.get(this.baseUrl).then(function (response) {
var objects = response.data;
objects.forEach(function (obj) {
self.data[obj.id] = obj;
});
deferred.resolve(self.data);
}, function (error) {
deferred.reject('Fetching ' + self.endpoint + ' failed');
});
return deferred.promise;
};
ApiService.prototype.fetchOne = function (id) {
this.id = id;
var deferred = $q.defer();
if (id === undefined) {
return deferred.promise;
}
var self = this;
$http.get(this.baseUrl + '/' + id).then(function (response) {
var data = response.data;
if (self.data[data.id] === undefined) {
self.data[data.id] = response.data;
}
$.each(response.data, function (key, value) {
self.data[data.id][key] = value;
});
deferred.resolve(response.data);
}, function (error) {
deferred.reject('Fetching ' + self.endpoint + ' failed');
});
return deferred.promise;
};
ApiService.prototype.create = function (entity) {
var deferred = $q.defer();
var self = this;
$http.post(this.baseUrl, entity).then(function (response) {
self.add(response.data);
deferred.resolve(response.data);
}, function (error) {
deferred.reject('Fetching' + self.endpoint + ' failed');
});
return deferred.promise;
};
ApiService.prototype.update = function (entity) {
var deferred = $q.defer();
var self = this;
$http.put(this.baseUrl + '/' + entity.id, entity).then(function (response) {
self.add(response.data);
deferred.resolve(response.data);
}, function (error) {
deferred.reject('Updating ' + self.endpoint + ' failed');
});
return deferred.promise;
};
ApiService.prototype.delete = function (id) {
var deferred = $q.defer();
var self = this;
$http.delete(this.baseUrl + '/' + id).then(function (response) {
self.remove(id);
deferred.resolve(response.data);
}, function (error) {
deferred.reject('Deleting ' + self.endpoint + ' failed');
});
return deferred.promise;
};
// methods for managing data
ApiService.prototype.clear = function () {
this.data = {};
};
ApiService.prototype.add = function (entity) {
var element = this.data[entity.id];
if (element === undefined) {
this.data[entity.id] = entity;
} else {
Object.keys(entity).forEach(function (key) {
if (entity[key] !== null) {
element[key] = entity[key];
}
});
element.status = {};
}
};
ApiService.prototype.remove = function (id) {
if (this.data[id] !== undefined) {
delete this.data[id];
}
};
ApiService.prototype.addAll = function (entities) {
var self = this;
angular.forEach(entities, function (entity) {
self.add(entity);
});
};
ApiService.prototype.getCurrent = function () {
return this.data[this.id];
};
ApiService.prototype.unsetCurrrent = function () {
this.id = null;
};
ApiService.prototype.getData = function () {
return $.map(this.data, function (value, index) {
return [value];
});
};
ApiService.prototype.getAll = function () {
return this.data;
};
ApiService.prototype.getName = function () {
var funcNameRegex = /function (.{1,})\(/;
var results = (funcNameRegex).exec((this).constructor.toString());
return (results && results.length > 1) ? results[1] : "";
};
return ApiService;
});

257
js/service/BoardService.js Normal file
View File

@@ -0,0 +1,257 @@
/*
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.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/>.
*
*/
/* global app OC */
app.factory('BoardService', function (ApiService, $http, $q) {
var BoardService = function ($http, ep, $q) {
ApiService.call(this, $http, ep, $q);
};
BoardService.prototype = angular.copy(ApiService.prototype);
BoardService.prototype.delete = function (id) {
var deferred = $q.defer();
var self = this;
$http.delete(this.baseUrl + '/' + id).then(function (response) {
self.data[id].deletedAt = response.data.deletedAt;
deferred.resolve(response.data);
}, function (error) {
deferred.reject('Deleting ' + self.endpoint + ' failed');
});
return deferred.promise;
};
BoardService.prototype.deleteUndo = function (id) {
var deferred = $q.defer();
var self = this;
var _id = id;
$http.post(this.baseUrl + '/' + id + '/deleteUndo').then(function (response) {
self.data[_id].deletedAt = 0;
deferred.resolve(response.data);
}, function (error) {
deferred.reject('Deleting ' + self.endpoint + ' failed');
});
return deferred.promise;
};
BoardService.prototype.searchUsers = function (search) {
var deferred = $q.defer();
var self = this;
var searchData = {
format: 'json',
perPage: 4,
itemType: [0, 1]
};
if (search !== "") {
searchData.search = search;
}
$http({
method: 'GET',
url: OC.linkToOCS('apps/files_sharing/api/v1') + 'sharees',
params: searchData
})
.then(function (result) {
var response = result.data;
if (response.ocs.meta.statuscode !== 100) {
deferred.reject('Error while searching for sharees');
return;
}
self.sharees = [];
var users = response.ocs.data.exact.users.concat(response.ocs.data.users.slice(0, 4));
var groups = response.ocs.data.exact.groups.concat(response.ocs.data.groups.slice(0, 4));
// filter out everyone who is already in the share list
angular.forEach(users, function (item) {
var acl = self.generateAcl(OC.Share.SHARE_TYPE_USER, item);
var exists = false;
angular.forEach(self.getCurrent().acl, function (acl) {
if (acl.participant.primaryKey === item.value.shareWith) {
exists = true;
}
});
if (!exists && OC.getCurrentUser().uid !== item.value.shareWith) {
self.sharees.push(acl);
}
});
angular.forEach(groups, function (item) {
var acl = self.generateAcl(OC.Share.SHARE_TYPE_GROUP, item);
var exists = false;
angular.forEach(self.getCurrent().acl, function (acl) {
if (acl.participant.primaryKey === item.value.shareWith) {
exists = true;
}
});
if (!exists) {
self.sharees.push(acl);
}
});
deferred.resolve(self.sharees);
}, function () {
deferred.reject('Error while searching for sharees');
});
return deferred.promise;
};
BoardService.prototype.generateAcl = function (type, ocsItem) {
return {
boardId: null,
id: null,
owner: false,
participant: {
primaryKey: ocsItem.value.shareWith,
uid: ocsItem.value.shareWith,
displayname: ocsItem.label
},
permissionEdit: true,
permissionManage: true,
permissionShare: true,
type: type
};
};
BoardService.prototype.addAcl = function (acl) {
var board = this.getCurrent();
var deferred = $q.defer();
var self = this;
var _acl = acl;
$http.post(this.baseUrl + '/' + acl.boardId + '/acl', _acl).then(function (response) {
if (!board.acl || board.acl.length === 0) {
board.acl = {};
}
board.acl[response.data.id] = response.data;
if (response.data.type === OC.Share.SHARE_TYPE_USER) {
self._updateUsers();
} else {
self.fetchOne(response.data.boardId);
}
deferred.resolve(response.data);
}, function (error) {
deferred.reject('Error creating ACL ' + _acl);
});
acl = null;
return deferred.promise;
};
BoardService.prototype.deleteAcl = function (acl) {
var board = this.getCurrent();
var deferred = $q.defer();
var self = this;
$http.delete(this.baseUrl + '/' + acl.boardId + '/acl/' + acl.id).then(function (response) {
delete board.acl[response.data.id];
if (response.data.type === OC.Share.SHARE_TYPE_USER) {
self._updateUsers();
} else {
self.fetchOne(response.data.boardId);
}
deferred.resolve(response.data);
}, function (error) {
deferred.reject('Error deleting ACL ' + acl.id);
});
acl = null;
return deferred.promise;
};
BoardService.prototype.updateAcl = function (acl) {
var board = this.getCurrent();
var deferred = $q.defer();
var self = this;
var _acl = acl;
$http.put(this.baseUrl + '/' + acl.boardId + '/acl', _acl).then(function (response) {
board.acl[_acl.id] = response.data;
if (response.data.type === OC.Share.SHARE_TYPE_USER) {
self._updateUsers();
} else {
self.fetchOne(response.data.boardId);
}
deferred.resolve(response.data);
}, function (error) {
deferred.reject('Error updating ACL ' + _acl);
});
acl = null;
return deferred.promise;
};
BoardService.prototype._updateUsers = function () {
if (!this.getCurrent() || !this.getCurrent().acl) {
return [];
}
var result = [this.getCurrent().owner];
angular.forEach(this.getCurrent().acl, function(value, key) {
if (value.type === OC.Share.SHARE_TYPE_USER) {
result.push(value.participant);
}
});
this.getCurrent().users = result;
};
BoardService.prototype.getUsers = function () {
if (this.getCurrent() && !this.getCurrent().users) {
this._updateUsers();
}
return this.getCurrent().users;
};
BoardService.prototype.canRead = function () {
if (!this.getCurrent() || !this.getCurrent().permissions) {
return false;
}
return this.getCurrent().permissions['PERMISSION_READ'];
};
BoardService.prototype.canEdit = function () {
if (!this.getCurrent() || !this.getCurrent().permissions) {
return false;
}
return this.getCurrent().permissions['PERMISSION_EDIT'];
};
BoardService.prototype.canManage = function (board) {
if (board !== null && board !== undefined) {
return board.permissions['PERMISSION_MANAGE'];
}
if (!this.getCurrent() || !this.getCurrent().permissions) {
return false;
}
return this.getCurrent().permissions['PERMISSION_MANAGE'];
};
BoardService.prototype.canShare = function () {
if (!this.getCurrent() || !this.getCurrent().permissions) {
return false;
}
return this.getCurrent().permissions['PERMISSION_SHARE'];
};
BoardService.prototype.isArchived = function () {
if (!this.getCurrent() || this.getCurrent().archived) {
return true;
}
return false;
};
return new BoardService($http, 'boards', $q);
});

137
js/service/CardService.js Normal file
View File

@@ -0,0 +1,137 @@
/*
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.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/>.
*
*/
app.factory('CardService', function (ApiService, $http, $q) {
var CardService = function ($http, ep, $q) {
ApiService.call(this, $http, ep, $q);
};
CardService.prototype = angular.copy(ApiService.prototype);
CardService.prototype.reorder = function (card, order) {
var deferred = $q.defer();
var self = this;
$http.put(this.baseUrl + '/' + card.id + '/reorder', {
cardId: card.id,
order: order,
stackId: card.stackId
}).then(function (response) {
deferred.resolve(response.data);
}, function (error) {
deferred.reject('Error while update ' + self.endpoint);
});
return deferred.promise;
};
CardService.prototype.rename = function (card) {
var deferred = $q.defer();
var self = this;
$http.put(this.baseUrl + '/' + card.id + '/rename', {
cardId: card.id,
title: card.title
}).then(function (response) {
self.data[card.id].title = card.title;
deferred.resolve(response.data);
}, function (error) {
deferred.reject('Error while renaming ' + self.endpoint);
});
return deferred.promise;
};
CardService.prototype.assignLabel = function (card, label) {
var url = this.baseUrl + '/' + card + '/label/' + label;
var deferred = $q.defer();
var self = this;
$http.post(url).then(function (response) {
deferred.resolve(response.data);
}, function (error) {
deferred.reject('Error while update ' + self.endpoint);
});
return deferred.promise;
};
CardService.prototype.removeLabel = function (card, label) {
var url = this.baseUrl + '/' + card + '/label/' + label;
var deferred = $q.defer();
var self = this;
$http.delete(url).then(function (response) {
deferred.resolve(response.data);
}, function (error) {
deferred.reject('Error while update ' + self.endpoint);
});
return deferred.promise;
};
CardService.prototype.archive = function (card) {
var deferred = $q.defer();
var self = this;
$http.put(this.baseUrl + '/' + card.id + '/archive', {}).then(function (response) {
deferred.resolve(response.data);
}, function (error) {
deferred.reject('Error while update ' + self.endpoint);
});
return deferred.promise;
};
CardService.prototype.unarchive = function (card) {
var deferred = $q.defer();
var self = this;
$http.put(this.baseUrl + '/' + card.id + '/unarchive', {}).then(function (response) {
deferred.resolve(response.data);
}, function (error) {
deferred.reject('Error while update ' + self.endpoint);
});
return deferred.promise;
};
CardService.prototype.assignUser = function (card, user) {
var deferred = $q.defer();
var self = this;
if (self.getCurrent().assignedUsers === null) {
self.getCurrent().assignedUsers = [];
}
$http.post(this.baseUrl + '/' + card.id + '/assign', {'userId': user}).then(function (response) {
self.getCurrent().assignedUsers.push(response.data);
deferred.resolve(response.data);
}, function (error) {
deferred.reject('Error while update ' + self.endpoint);
});
return deferred.promise;
};
CardService.prototype.unassignUser = function (card, user) {
var deferred = $q.defer();
var self = this;
$http.delete(this.baseUrl + '/' + card.id + '/assign/' + user, {}).then(function (response) {
self.getCurrent().assignedUsers = self.getCurrent().assignedUsers.filter(function (obj) {
return obj.participant.uid !== user;
});
deferred.resolve(response.data);
}, function (error) {
deferred.reject('Error while update ' + self.endpoint);
});
return deferred.promise;
};
var service = new CardService($http, 'cards', $q);
return service;
});

View File

@@ -1,43 +1,29 @@
<?php
/**
* @copyright Copyright (c) 2020 Jakob Röhrl <jakob.roehrl@web.de>
/*
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
*
* @author Jakob Röhrl <jakob.roehrl@web.de>
* @author Julius Härtl <jus@bitgrid.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\Exceptions;
use OCA\Deck\StatusException;
class ConflictException extends StatusException {
private $data;
public function __construct($message, $data = null) {
parent::__construct($message);
$this->data = $data;
}
public function getStatus() {
return 409;
}
public function getData() {
return $this->data;
}
}
app.factory('LabelService', function (ApiService, $http, $q) {
var LabelService = function ($http, ep, $q) {
ApiService.call(this, $http, ep, $q);
};
LabelService.prototype = angular.copy(ApiService.prototype);
return new LabelService($http, 'labels', $q);
});

144
js/service/StackService.js Normal file
View File

@@ -0,0 +1,144 @@
/*
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.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/>.
*
*/
app.factory('StackService', function (ApiService, $http, $q) {
var StackService = function ($http, ep, $q) {
ApiService.call(this, $http, ep, $q);
};
StackService.prototype = angular.copy(ApiService.prototype);
StackService.prototype.fetchAll = function (boardId) {
var deferred = $q.defer();
var self = this;
$http.get(this.baseUrl + '/' + boardId).then(function (response) {
self.clear();
self.addAll(response.data);
deferred.resolve(self.data);
}, function (error) {
deferred.reject('Error while loading stacks');
});
return deferred.promise;
};
StackService.prototype.fetchArchived = function (boardId) {
var deferred = $q.defer();
var self = this;
$http.get(this.baseUrl + '/' + boardId + '/archived').then(function (response) {
self.clear();
self.addAll(response.data);
deferred.resolve(self.data);
}, function (error) {
deferred.reject('Error while loading stacks');
});
return deferred.promise;
};
StackService.prototype.addCard = function (entity) {
if (!this.data[entity.stackId].cards) {
this.data[entity.stackId].cards = [];
}
this.data[entity.stackId].cards.push(entity);
};
StackService.prototype.reorder = function (stack, order) {
var deferred = $q.defer();
var self = this;
$http.put(this.baseUrl + '/' + stack.id + '/reorder', {
stackId: stack.id,
order: order
}).then(function (response) {
angular.forEach(response.data, function (value, key) {
var id = value.id;
self.data[id].order = value.order;
});
deferred.resolve(response.data);
}, function (error) {
deferred.reject('Error while update ' + self.endpoint);
});
return deferred.promise;
};
StackService.prototype.reorderCard = function (entity, order) {
// assign new order
for (var i = 0, j = 0; i < this.data[entity.stackId].cards.length; i++) {
if (this.data[entity.stackId].cards[i].id === entity.id) {
this.data[entity.stackId].cards[i].order = order;
}
if (j === order) {
j++;
}
if (this.data[entity.stackId].cards[i].id !== entity.id) {
this.data[entity.stackId].cards[i].order = j++;
}
}
// sort array by order
this.data[entity.stackId].cards.sort(function (a, b) {
if (a.order < b.order)
{return -1;}
if (a.order > b.order)
{return 1;}
return 0;
});
};
StackService.prototype.updateCard = function (entity) {
var self = this;
var cards = this.data[entity.stackId].cards;
for (var i = 0; i < cards.length; i++) {
if (cards[i].id === entity.id) {
cards[i] = entity;
}
}
};
StackService.prototype.removeCard = function (entity) {
var self = this;
var cards = this.data[entity.stackId].cards;
for (var i = 0; i < cards.length; i++) {
if (cards[i].id === entity.id) {
cards.splice(i, 1);
}
}
};
// FIXME: Should not sure popup but proper undo mechanism
StackService.prototype.delete = function (id) {
var deferred = $q.defer();
var self = this;
OC.dialogs.confirm(t('deck', 'Are you sure you want to delete the stack with all of its data?'), t('deck', 'Delete'), function(state) {
if (!state) {
return;
}
$http.delete(self.baseUrl + '/' + id).then(function (response) {
self.remove(id);
deferred.resolve(response.data);
}, function (error) {
deferred.reject('Deleting ' + self.endpoint + ' failed');
});
});
return deferred.promise;
};
var service = new StackService($http, 'stacks', $q);
return service;
});

Some files were not shown because too many files have changed in this diff Show More