Compare commits
7 Commits
v0.6.0-bet
...
v0.2.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3bd32e6c0d | ||
|
|
1633be74ca | ||
|
|
1ede06fe45 | ||
|
|
4e4d412717 | ||
|
|
d590e9972b | ||
|
|
91fb1533e6 | ||
|
|
04f17aad68 |
290
.drone.yml
@@ -1,12 +1,15 @@
|
|||||||
kind: pipeline
|
clone:
|
||||||
name: checkers
|
git:
|
||||||
steps:
|
image: plugins/git
|
||||||
- name: compatibility
|
depth: 1
|
||||||
image: nextcloudci/php7.1:php7.1-16
|
|
||||||
|
pipeline:
|
||||||
|
check-app-compatbility:
|
||||||
|
image: nextcloudci/php5.6:php5.6-3
|
||||||
environment:
|
environment:
|
||||||
APP_NAME: deck
|
- APP_NAME=deck
|
||||||
CORE_BRANCH: master
|
- CORE_BRANCH=master
|
||||||
DB: sqlite
|
- DB=sqlite
|
||||||
commands:
|
commands:
|
||||||
# Pre-setup steps
|
# Pre-setup steps
|
||||||
- wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh
|
- wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh
|
||||||
@@ -16,59 +19,87 @@ steps:
|
|||||||
- ./occ app:check-code $APP_NAME -c strong-comparison
|
- ./occ app:check-code $APP_NAME -c strong-comparison
|
||||||
- ./occ app:check-code $APP_NAME -c deprecation
|
- ./occ app:check-code $APP_NAME -c deprecation
|
||||||
- cd apps/$APP_NAME/
|
- cd apps/$APP_NAME/
|
||||||
- name: syntax-php7.0
|
when:
|
||||||
image: nextcloudci/php7.0:php7.0-17
|
matrix:
|
||||||
|
TESTS: check-app-compatbility
|
||||||
|
signed-off-check:
|
||||||
|
image: nextcloudci/php7.0:php7.0-2
|
||||||
environment:
|
environment:
|
||||||
APP_NAME: deck
|
- APP_NAME=deck
|
||||||
CORE_BRANCH: stable15
|
- CORE_BRANCH=master
|
||||||
DB: sqlite
|
- DB=sqlite
|
||||||
commands:
|
commands:
|
||||||
- composer install
|
- wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh
|
||||||
- ./vendor/bin/parallel-lint --exclude ./vendor/ --exclude ./lib/Collaboration/ .
|
- bash ./before_install.sh $APP_NAME $CORE_BRANCH $DB
|
||||||
- name: syntax-php7.1
|
- cd ../server
|
||||||
image: nextcloudci/php7.1:php7.1-15
|
- php ./build/signed-off-checker.php
|
||||||
|
when:
|
||||||
|
matrix:
|
||||||
|
TESTS: signed-off-check
|
||||||
|
syntax-php5.6:
|
||||||
|
image: nextcloudci/php5.6:php5.6-3
|
||||||
environment:
|
environment:
|
||||||
APP_NAME: deck
|
- APP_NAME=deck
|
||||||
CORE_BRANCH: stable15
|
- CORE_BRANCH=master
|
||||||
DB: sqlite
|
- DB=sqlite
|
||||||
commands:
|
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
|
||||||
- composer install
|
- composer install
|
||||||
- ./vendor/bin/parallel-lint --exclude ./vendor/ .
|
- ./lib/composer/bin/parallel-lint --exclude build/.phan/ --exclude lib/composer/jakub-onderka/ --exclude 3rdparty/symfony/polyfill-php70/Resources/stubs/ --exclude 3rdparty/patchwork/utf8/src/Patchwork/Utf8/Bootup/ --exclude 3rdparty/paragonie/random_compat/lib/ --exclude lib/composer/composer/autoload_static.php --exclude 3rdparty/composer/autoload_static.php .
|
||||||
- name: syntax-php7.2
|
when:
|
||||||
image: nextcloudci/php7.2:php7.2-9
|
matrix:
|
||||||
|
TESTS: syntax-php5.6
|
||||||
|
syntax-php7.0:
|
||||||
|
image: nextcloudci/php7.0:php7.0-2
|
||||||
environment:
|
environment:
|
||||||
APP_NAME: deck
|
- APP_NAME=deck
|
||||||
CORE_BRANCH: stable15
|
- CORE_BRANCH=master
|
||||||
DB: sqlite
|
- DB=sqlite
|
||||||
commands:
|
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
|
||||||
- composer install
|
- composer install
|
||||||
- ./vendor/bin/parallel-lint --exclude ./vendor/ .
|
- ./lib/composer/bin/parallel-lint --exclude lib/composer/jakub-onderka/ --exclude 3rdparty/symfony/polyfill-php70/Resources/stubs/ --exclude 3rdparty/patchwork/utf8/src/Patchwork/Utf8/Bootup/ --exclude 3rdparty/paragonie/random_compat/lib/ --exclude lib/composer/composer/autoload_static.php --exclude 3rdparty/composer/autoload_static.php .
|
||||||
- name: syntax-php7.3
|
when:
|
||||||
image: nextcloudci/php7.3:php7.3-2
|
matrix:
|
||||||
|
TESTS: syntax-php7.0
|
||||||
|
php5.6:
|
||||||
|
image: nextcloudci/php5.6:php5.6-7
|
||||||
environment:
|
environment:
|
||||||
APP_NAME: deck
|
- APP_NAME=deck
|
||||||
CORE_BRANCH: stable15
|
- CORE_BRANCH=master
|
||||||
DB: sqlite
|
- DB=sqlite
|
||||||
commands:
|
commands:
|
||||||
- composer install
|
- apt update && apt-get -y install php5-xdebug
|
||||||
- ./vendor/bin/parallel-lint --exclude ./vendor/ .
|
|
||||||
trigger:
|
# Pre-setup steps
|
||||||
branch:
|
- wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh
|
||||||
- master
|
- bash ./before_install.sh $APP_NAME $CORE_BRANCH $DB
|
||||||
- stable*
|
- cd ../server/
|
||||||
event:
|
- ./occ app:enable $APP_NAME
|
||||||
- pull_request
|
- cd apps/$APP_NAME
|
||||||
- push
|
|
||||||
---
|
- phpunit -c tests/phpunit.xml --coverage-clover build/php-unit.coverage.xml
|
||||||
kind: pipeline
|
- phpunit -c tests/phpunit.integration.xml --coverage-clover build/php-integration.coverage.xml
|
||||||
name: unit-php7.0
|
|
||||||
steps:
|
# Create coverage report
|
||||||
- name: php7.0
|
- wget https://codecov.io/bash -O codecov.sh
|
||||||
image: nextcloudci/php7.0:php7.0-17
|
- sh -c "if [ '$DRONE_BUILD_EVENT' = 'pull_request' ]; then bash codecov.sh -B $DRONE_BRANCH -C $DRONE_COMMIT -P $DRONE_PULL_REQUEST -t f6375299-4832-487e-b831-091772ab0384; fi"
|
||||||
|
- sh -c "if [ '$DRONE_BUILD_EVENT' != 'pull_request' ]; then bash codecov.sh -B $DRONE_BRANCH -C $DRONE_COMMIT -t f6375299-4832-487e-b831-091772ab0384; fi"
|
||||||
|
when:
|
||||||
|
matrix:
|
||||||
|
TESTS: php5.6
|
||||||
|
php7.0:
|
||||||
|
image: nextcloudci/php7.0:php7.0-8
|
||||||
environment:
|
environment:
|
||||||
APP_NAME: deck
|
- APP_NAME=deck
|
||||||
CORE_BRANCH: stable15
|
- CORE_BRANCH=master
|
||||||
DB: sqlite
|
- DB=sqlite
|
||||||
commands:
|
commands:
|
||||||
# Pre-setup steps
|
# Pre-setup steps
|
||||||
- wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh
|
- wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh
|
||||||
@@ -77,107 +108,36 @@ steps:
|
|||||||
- php occ app:enable deck
|
- php occ app:enable deck
|
||||||
- cd apps/$APP_NAME
|
- cd apps/$APP_NAME
|
||||||
# Run phpunit tests
|
# Run phpunit tests
|
||||||
- composer install
|
|
||||||
- phpunit -c tests/phpunit.xml --coverage-clover build/php-unit.coverage.xml
|
- 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
|
- phpunit -c tests/phpunit.integration.xml --coverage-clover build/php-integration.coverage.xml
|
||||||
trigger:
|
when:
|
||||||
branch:
|
matrix:
|
||||||
- master
|
TESTS: php7.0
|
||||||
- stable*
|
php7.1:
|
||||||
event:
|
image: nextcloudci/php7.1:php7.1-11
|
||||||
- pull_request
|
|
||||||
- push
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: unit-php7.1
|
|
||||||
steps:
|
|
||||||
- name: php7.1
|
|
||||||
image: nextcloudci/php7.1:php7.1-15
|
|
||||||
environment:
|
environment:
|
||||||
APP_NAME: deck
|
- APP_NAME=deck
|
||||||
CORE_BRANCH: stable15
|
- CORE_BRANCH=master
|
||||||
DB: sqlite
|
- DB=sqlite
|
||||||
commands:
|
commands:
|
||||||
# Pre-setup steps
|
# Pre-setup steps
|
||||||
|
- yum -y install wget
|
||||||
- wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh
|
- wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh
|
||||||
- bash ./before_install.sh $APP_NAME $CORE_BRANCH $DB
|
- bash ./before_install.sh $APP_NAME $CORE_BRANCH $DB
|
||||||
- cd ../server/
|
- cd ../server/
|
||||||
- php occ app:enable deck
|
- php occ app:enable deck
|
||||||
- cd apps/$APP_NAME
|
- cd apps/$APP_NAME
|
||||||
- composer install
|
|
||||||
- phpunit -c tests/phpunit.xml --coverage-clover build/php-unit.coverage.xml
|
- 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
|
- phpunit -c tests/phpunit.integration.xml --coverage-clover build/php-integration.coverage.xml
|
||||||
trigger:
|
when:
|
||||||
branch:
|
matrix:
|
||||||
- master
|
TESTS: php7.1
|
||||||
- stable*
|
integration:
|
||||||
event:
|
image: nextcloudci/integration-php7.0:integration-php7.0-3
|
||||||
- pull_request
|
|
||||||
- push
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: unit-php7.2
|
|
||||||
steps:
|
|
||||||
- name: php7.2
|
|
||||||
image: nextcloudci/php7.2:php7.2-9
|
|
||||||
environment:
|
environment:
|
||||||
APP_NAME: deck
|
- APP_NAME=deck
|
||||||
CORE_BRANCH: stable15
|
- CORE_BRANCH=master
|
||||||
DB: sqlite
|
- 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
|
|
||||||
- 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: unit-php7.3
|
|
||||||
steps:
|
|
||||||
- name: php7.3
|
|
||||||
image: nextcloudci/php7.3:php7.3-2
|
|
||||||
environment:
|
|
||||||
APP_NAME: deck
|
|
||||||
CORE_BRANCH: stable15
|
|
||||||
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
|
|
||||||
- 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.1:php7.1-16
|
|
||||||
environment:
|
|
||||||
APP_NAME: deck
|
|
||||||
CORE_BRANCH: master
|
|
||||||
DB: sqlite
|
|
||||||
commands:
|
commands:
|
||||||
# Pre-setup steps
|
# Pre-setup steps
|
||||||
- wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh
|
- wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh
|
||||||
@@ -186,32 +146,32 @@ steps:
|
|||||||
- php occ app:enable deck
|
- php occ app:enable deck
|
||||||
- cd apps/$APP_NAME
|
- cd apps/$APP_NAME
|
||||||
- cd tests/integration
|
- cd tests/integration
|
||||||
- ./run.sh || true
|
- ./run.sh
|
||||||
trigger:
|
when:
|
||||||
branch:
|
matrix:
|
||||||
- master
|
TESTS: integration
|
||||||
- stable*
|
jsbuild:
|
||||||
event:
|
|
||||||
- pull_request
|
|
||||||
- push
|
|
||||||
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: frontend
|
|
||||||
steps:
|
|
||||||
- name: eslint
|
|
||||||
image: nextcloudci/eslint:eslint-1
|
|
||||||
commands:
|
|
||||||
- ./run-eslint.sh
|
|
||||||
- name: jsbuild
|
|
||||||
image: mhart/alpine-node:6.8.0
|
image: mhart/alpine-node:6.8.0
|
||||||
commands:
|
commands:
|
||||||
- apk add --no-cache make
|
- apk add --no-cache git
|
||||||
- make build-js
|
- cd js
|
||||||
trigger:
|
- npm install --deps
|
||||||
branch:
|
- ./node_modules/.bin/bower --allow-root install
|
||||||
- master
|
when:
|
||||||
- stable*
|
matrix:
|
||||||
event:
|
TESTS: jsbuild
|
||||||
- pull_request
|
matrix:
|
||||||
- push
|
include:
|
||||||
|
- TESTS: check-app-compatbility
|
||||||
|
- TESTS: signed-off-check
|
||||||
|
- TESTS: syntax-php5.6
|
||||||
|
- TESTS: syntax-php7.0
|
||||||
|
- TESTS: php5.6
|
||||||
|
- TESTS: php7.0
|
||||||
|
- TESTS: php7.1
|
||||||
|
- TESTS: jsbuild
|
||||||
|
- TESTS: integration
|
||||||
|
|
||||||
|
branches: [ master, stable* ]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
/js/tests/*
|
|
||||||
/js/vendor/*
|
|
||||||
/js/legacy/*
|
|
||||||
/js/node_modules/*
|
|
||||||
/js/public/*
|
|
||||||
/karma.conf.js
|
|
||||||
/tests/*
|
|
||||||
/l10n/*
|
|
||||||
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
root: true
|
|
||||||
|
|
||||||
extends:
|
|
||||||
- eslint:recommended
|
|
||||||
|
|
||||||
env:
|
|
||||||
browser: true
|
|
||||||
amd: true
|
|
||||||
es6: true
|
|
||||||
|
|
||||||
globals:
|
|
||||||
global: false
|
|
||||||
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
|
|
||||||
|
|
||||||
parserOptions:
|
|
||||||
ecmaVersion: 6
|
|
||||||
sourceType: "module"
|
|
||||||
|
|
||||||
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-useless-escape: warn
|
|
||||||
no-use-before-define: error
|
|
||||||
semi: ["error", "always"]
|
|
||||||
indent:
|
|
||||||
- error
|
|
||||||
- tab
|
|
||||||
- SwitchCase: 1
|
|
||||||
17
.github/ISSUE_TEMPLATE/Feature_request.md
vendored
@@ -1,17 +0,0 @@
|
|||||||
---
|
|
||||||
name: Feature request
|
|
||||||
about: Suggest an idea for this project
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**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.
|
|
||||||
17
.github/pull_request_template.md
vendored
@@ -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
@@ -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.
|
|
||||||
5
.gitignore
vendored
@@ -1,13 +1,10 @@
|
|||||||
js/node_modules/*
|
js/node_modules/*
|
||||||
js/vendor/
|
js/vendor/
|
||||||
js/public/
|
js/public/
|
||||||
js/build/
|
js/package-lock.json
|
||||||
build/
|
build/
|
||||||
css/style.css
|
css/style.css
|
||||||
css/vendor.css
|
|
||||||
tests/integration/vendor/
|
tests/integration/vendor/
|
||||||
tests/integration/composer.lock
|
tests/integration/composer.lock
|
||||||
vendor/
|
vendor/
|
||||||
*.lock
|
*.lock
|
||||||
|
|
||||||
\.idea/
|
|
||||||
|
|||||||
20
.travis.yml
@@ -1,36 +1,36 @@
|
|||||||
language: php
|
language: php
|
||||||
|
dist: trusty
|
||||||
|
sudo: required
|
||||||
services:
|
services:
|
||||||
- mysql
|
- mysql
|
||||||
php:
|
php:
|
||||||
- 7.0
|
- 5.6
|
||||||
- 7.1
|
- 7
|
||||||
- 7.2
|
|
||||||
- 7.3
|
|
||||||
env:
|
env:
|
||||||
- CORE_BRANCH=stable15 DB=mysql
|
matrix:
|
||||||
|
- CORE_BRANCH=master DB=mysql
|
||||||
|
- CORE_BRANCH=stable12 DB=mysql
|
||||||
|
- CORE_BRANCH=stable11 DB=mysql
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- wget https://phar.phpunit.de/phpunit-5.7.phar
|
- wget https://phar.phpunit.de/phpunit-5.7.phar
|
||||||
- chmod +x phpunit-5.7.phar
|
- chmod +x phpunit-5.7.phar
|
||||||
- mkdir bin
|
- mkdir bin
|
||||||
- mv phpunit-5.7.phar bin/phpunit
|
- sudo mv phpunit-5.7.phar bin/phpunit
|
||||||
- export PATH="$PWD/bin:$PATH"
|
- export PATH="$PWD/bin:$PATH"
|
||||||
- phpunit --version
|
- phpunit --version
|
||||||
- wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh
|
- wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh
|
||||||
- bash ./before_install.sh deck $CORE_BRANCH $DB
|
- bash ./before_install.sh deck $CORE_BRANCH $DB
|
||||||
- cd ../server
|
- cd ../server
|
||||||
- ./occ app:enable deck
|
- ./occ app:enable deck
|
||||||
|
- php -S localhost:8080 &
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- cd apps/deck
|
- cd apps/deck
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- composer install
|
|
||||||
- make test-unit
|
- make test-unit
|
||||||
|
|
||||||
after_success:
|
|
||||||
- bash <(curl -s https://codecov.io/bash)
|
|
||||||
|
|
||||||
after_failure:
|
after_failure:
|
||||||
- cat ../../data/nextcloud.log
|
- cat ../../data/nextcloud.log
|
||||||
|
|
||||||
|
|||||||
139
CHANGELOG.md
@@ -1,145 +1,6 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
## 0.6.0 - unreleased
|
|
||||||
|
|
||||||
### 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
|
|
||||||
|
|
||||||
|
|
||||||
## 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
|
|
||||||
- Allow to assign users to cards
|
|
||||||
- Emit notifications for overdue cards
|
|
||||||
- Emit notifications if boards gets shared to a user
|
|
||||||
- Add support for Nextcloud 13
|
|
||||||
- Simplify layout for cleaner user experience
|
|
||||||
- Add contacts menu to avatars
|
|
||||||
- Automatically save card description on inactivity
|
|
||||||
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Fix card dragging behaviour
|
|
||||||
- Fix scrolling and dragging on mobile
|
|
||||||
- Various fixes when data is not syncronized between different views
|
|
||||||
- Improved performance
|
|
||||||
- Update document title when renaming a board
|
|
||||||
- Automatically chose the least used color
|
|
||||||
- Improve accessibility
|
|
||||||
- Fix issue when assigning labels after creating them
|
|
||||||
- Allow to save tag changes with enter
|
|
||||||
- Fix bug when removing labels changed the color of the remaining ones
|
|
||||||
- Fix issues with auto saving of card descriptions
|
|
||||||
|
|
||||||
|
|
||||||
## 0.2.8 - 2017-11-26
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Drop support for NC 13, since that will only be supported by the next version of Deck
|
|
||||||
|
|
||||||
## 0.2.7 - 2017-11-10
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Fix bug that caused update to fail
|
|
||||||
|
|
||||||
## 0.2.6 - 2017-11-10
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Fix duedates not being updated with MySQL databases
|
|
||||||
|
|
||||||
## 0.2.5 - 2017-11-08
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Fix duedates not being saved with MySQL databases
|
|
||||||
|
|
||||||
## 0.2.4 - 2017-10-08
|
## 0.2.4 - 2017-10-08
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
26
Makefile
@@ -12,30 +12,29 @@ sign_dir=$(build_dir)/sign
|
|||||||
cert_dir=$(HOME)/.nextcloud/certificates
|
cert_dir=$(HOME)/.nextcloud/certificates
|
||||||
|
|
||||||
|
|
||||||
default: package
|
default: build
|
||||||
|
|
||||||
clean-build:
|
clean-build:
|
||||||
rm -rf $(build_dir)
|
rm -rf $(build_dir)
|
||||||
|
|
||||||
clean-dist:
|
clean-dist:
|
||||||
rm -rf js/node_modules
|
rm -rf js/node_modules
|
||||||
|
rm -rf js/vendor
|
||||||
|
|
||||||
install-deps: install-deps-js
|
install-deps:
|
||||||
composer install
|
cd js && npm install --deps
|
||||||
|
cd js && ./node_modules/.bin/bower install
|
||||||
|
|
||||||
install-deps-js:
|
build: build-js build-css
|
||||||
cd js && npm install
|
|
||||||
|
|
||||||
build: install-deps build-js
|
build-js: install-deps
|
||||||
|
cd js && ./node_modules/.bin/grunt build
|
||||||
|
|
||||||
build-js: install-deps-js
|
build-css: install-deps
|
||||||
cd js && npm run build
|
./js/node_modules/node-sass/bin/node-sass --output-style compressed css/legacy.scss css/style.css
|
||||||
|
|
||||||
build-js-dev: install-deps
|
|
||||||
cd js && npm run dev
|
|
||||||
|
|
||||||
watch:
|
watch:
|
||||||
cd js && npm run watch
|
cd js && ./node_modules/.bin/grunt watch
|
||||||
|
|
||||||
# appstore: clean install-deps
|
# appstore: clean install-deps
|
||||||
appstore: clean-build build
|
appstore: clean-build build
|
||||||
@@ -63,7 +62,6 @@ appstore: clean-build build
|
|||||||
--exclude="../$(app_name)/protractor\.*" \
|
--exclude="../$(app_name)/protractor\.*" \
|
||||||
--exclude="../$(app_name)/.*" \
|
--exclude="../$(app_name)/.*" \
|
||||||
--exclude="../$(app_name)/*.lock" \
|
--exclude="../$(app_name)/*.lock" \
|
||||||
--exclude="../$(app_name)/run-eslint.sh" \
|
|
||||||
--exclude="../$(app_name)/js/.*" \
|
--exclude="../$(app_name)/js/.*" \
|
||||||
--exclude="../$(app_name)/vendor" \
|
--exclude="../$(app_name)/vendor" \
|
||||||
--exclude-vcs \
|
--exclude-vcs \
|
||||||
@@ -98,5 +96,3 @@ test-integration:
|
|||||||
test-js: install-deps
|
test-js: install-deps
|
||||||
cd js && run test
|
cd js && run test
|
||||||
|
|
||||||
package:
|
|
||||||
krankerl package
|
|
||||||
|
|||||||
28
README.md
@@ -1,6 +1,6 @@
|
|||||||
# Deck
|
# Deck
|
||||||
|
|
||||||
[](https://travis-ci.org/nextcloud/deck) [](https://codecov.io/github/nextcloud/deck) [](https://www.codacy.com/app/juliushaertl/deck?utm_source=github.com&utm_medium=referral&utm_content=nextcloud/deck&utm_campaign=Badge_Grade) [](https://scrutinizer-ci.com/g/nextcloud/deck/?branch=master) [](https://webchat.freenode.net/?channels=nextcloud-deck)
|
[](https://travis-ci.org/nextcloud/deck) [](https://codecov.io/github/nextcloud/deck) [](https://scrutinizer-ci.com/g/nextcloud/deck/?branch=master) [](https://www.versioneye.com/user/projects/58ad558f4ca76f004ed475b3) [](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.
|
Deck is a kanban style organization tool aimed at personal planning and project organization for teams integrated with Nextcloud.
|
||||||
@@ -9,17 +9,22 @@ Deck is a kanban style organization tool aimed at personal planning and project
|
|||||||
- :page_facing_up: Write down additional notes in markdown
|
- :page_facing_up: Write down additional notes in markdown
|
||||||
- :bookmark: Assign labels for even better organization
|
- :bookmark: Assign labels for even better organization
|
||||||
- :busts_in_silhouette: Share with your team, friends or family
|
- :busts_in_silhouette: Share with your team, friends or family
|
||||||
- :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
|
- :rocket: Get your project organized
|
||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
### 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
|
## 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
|
### Install latest release
|
||||||
|
|
||||||
@@ -33,10 +38,10 @@ If you want to run the latest development version from git source, you need to c
|
|||||||
git clone https://github.com/nextcloud/deck.git
|
git clone https://github.com/nextcloud/deck.git
|
||||||
cd deck
|
cd deck
|
||||||
make install-deps
|
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
|
### Install the nightly builds
|
||||||
|
|
||||||
@@ -50,22 +55,21 @@ Nothing to prepare, just dig into the code.
|
|||||||
|
|
||||||
### JavaScript
|
### 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.
|
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
|
### Running tests
|
||||||
You can use the provided Makefile to run all tests by using:
|
You can use the provided Makefile to run all tests by using:
|
||||||
|
|
||||||
make test
|
make test
|
||||||
|
|
||||||
### Documentation
|
|
||||||
|
|
||||||
The documentation for our REST API can be found at https://deck.readthedocs.io/en/latest/API/
|
|
||||||
|
|
||||||
## Contribution Guidelines
|
## 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.
|
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
|
### Apply a license
|
||||||
|
|
||||||
|
|||||||
2
_config.yml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
theme: jekyll-theme-cayman
|
||||||
|
site: https://deck-app.com
|
||||||
@@ -21,15 +21,5 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if ((@include_once __DIR__ . '/../vendor/autoload.php')===false) {
|
|
||||||
throw new Exception('Cannot include autoload. Did you run install dependencies using composer?');
|
|
||||||
}
|
|
||||||
|
|
||||||
$app = new \OCA\Deck\AppInfo\Application();
|
$app = new \OCA\Deck\AppInfo\Application();
|
||||||
$app->registerNavigationEntry();
|
$app->registerNavigationEntry();
|
||||||
$app->registerNotifications();
|
|
||||||
$app->registerCommentsEntity();
|
|
||||||
$app->registerFullTextSearch();
|
|
||||||
|
|
||||||
/** Load activity style global so it is availabile in the activity app as well */
|
|
||||||
\OC_Util::addStyle('deck', 'activity');
|
|
||||||
|
|||||||
@@ -28,4 +28,4 @@ use OCP\AppFramework\App;
|
|||||||
/**
|
/**
|
||||||
* Additional autoloader registration, e.g. registering composer autoloaders
|
* Additional autoloader registration, e.g. registering composer autoloaders
|
||||||
*/
|
*/
|
||||||
require_once __DIR__ . '/../vendor/autoload.php';
|
// require_once __DIR__ . '/../vendor/autoload.php';
|
||||||
@@ -46,13 +46,6 @@
|
|||||||
<notnull>false</notnull>
|
<notnull>false</notnull>
|
||||||
<unsigned>true</unsigned>
|
<unsigned>true</unsigned>
|
||||||
</field>
|
</field>
|
||||||
<field>
|
|
||||||
<name>last_modified</name>
|
|
||||||
<type>integer</type>
|
|
||||||
<default></default>
|
|
||||||
<notnull>false</notnull>
|
|
||||||
<unsigned>true</unsigned>
|
|
||||||
</field>
|
|
||||||
</declaration>
|
</declaration>
|
||||||
</table>
|
</table>
|
||||||
<table>
|
<table>
|
||||||
@@ -84,21 +77,6 @@
|
|||||||
<length>8</length>
|
<length>8</length>
|
||||||
<notnull>false</notnull>
|
<notnull>false</notnull>
|
||||||
</field>
|
</field>
|
||||||
<field>
|
|
||||||
<name>deleted_at</name>
|
|
||||||
<type>integer</type>
|
|
||||||
<default>0</default>
|
|
||||||
<length>8</length>
|
|
||||||
<notnull>false</notnull>
|
|
||||||
<unsigned>true</unsigned>
|
|
||||||
</field>
|
|
||||||
<field>
|
|
||||||
<name>last_modified</name>
|
|
||||||
<type>integer</type>
|
|
||||||
<default></default>
|
|
||||||
<notnull>false</notnull>
|
|
||||||
<unsigned>true</unsigned>
|
|
||||||
</field>
|
|
||||||
<index>
|
<index>
|
||||||
<name>deck_stacks_board_id_index</name>
|
<name>deck_stacks_board_id_index</name>
|
||||||
<field>
|
<field>
|
||||||
@@ -135,11 +113,6 @@
|
|||||||
<type>clob</type>
|
<type>clob</type>
|
||||||
<notnull>false</notnull>
|
<notnull>false</notnull>
|
||||||
</field>
|
</field>
|
||||||
<field>
|
|
||||||
<name>description_prev</name>
|
|
||||||
<type>clob</type>
|
|
||||||
<notnull>false</notnull>
|
|
||||||
</field>
|
|
||||||
<field>
|
<field>
|
||||||
<name>stack_id</name>
|
<name>stack_id</name>
|
||||||
<type>integer</type>
|
<type>integer</type>
|
||||||
@@ -160,12 +133,6 @@
|
|||||||
<notnull>false</notnull>
|
<notnull>false</notnull>
|
||||||
<unsigned>true</unsigned>
|
<unsigned>true</unsigned>
|
||||||
</field>
|
</field>
|
||||||
<field>
|
|
||||||
<name>last_editor</name>
|
|
||||||
<type>text</type>
|
|
||||||
<notnull>false</notnull>
|
|
||||||
<length>64</length>
|
|
||||||
</field>
|
|
||||||
<field>
|
<field>
|
||||||
<name>created_at</name>
|
<name>created_at</name>
|
||||||
<type>integer</type>
|
<type>integer</type>
|
||||||
@@ -195,19 +162,6 @@
|
|||||||
<type>timestamp</type>
|
<type>timestamp</type>
|
||||||
<default>0</default>
|
<default>0</default>
|
||||||
</field>
|
</field>
|
||||||
<field>
|
|
||||||
<name>notified</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>
|
|
||||||
<index>
|
<index>
|
||||||
<name>deck_cards_stack_id_index</name>
|
<name>deck_cards_stack_id_index</name>
|
||||||
<field>
|
<field>
|
||||||
@@ -239,6 +193,12 @@
|
|||||||
<autoincrement>1</autoincrement>
|
<autoincrement>1</autoincrement>
|
||||||
<length>4</length>
|
<length>4</length>
|
||||||
</field>
|
</field>
|
||||||
|
<field>
|
||||||
|
<name>title</name>
|
||||||
|
<type>text</type>
|
||||||
|
<notnull>true</notnull>
|
||||||
|
<length>100</length>
|
||||||
|
</field>
|
||||||
<field>
|
<field>
|
||||||
<name>card_id</name>
|
<name>card_id</name>
|
||||||
<type>integer</type>
|
<type>integer</type>
|
||||||
@@ -253,12 +213,12 @@
|
|||||||
</field>
|
</field>
|
||||||
<field>
|
<field>
|
||||||
<name>data</name>
|
<name>data</name>
|
||||||
<type>text</type>
|
<type>clob</type>
|
||||||
</field>
|
</field>
|
||||||
<field>
|
<field>
|
||||||
<name>last_modified</name>
|
<name>last_modified</name>
|
||||||
<type>integer</type>
|
<type>integer</type>
|
||||||
<default/>
|
<default></default>
|
||||||
<length>8</length>
|
<length>8</length>
|
||||||
<notnull>false</notnull>
|
<notnull>false</notnull>
|
||||||
<unsigned>true</unsigned>
|
<unsigned>true</unsigned>
|
||||||
@@ -266,21 +226,7 @@
|
|||||||
<field>
|
<field>
|
||||||
<name>created_at</name>
|
<name>created_at</name>
|
||||||
<type>integer</type>
|
<type>integer</type>
|
||||||
<default/>
|
<default></default>
|
||||||
<length>8</length>
|
|
||||||
<notnull>false</notnull>
|
|
||||||
<unsigned>true</unsigned>
|
|
||||||
</field>
|
|
||||||
<field>
|
|
||||||
<name>created_by</name>
|
|
||||||
<type>text</type>
|
|
||||||
<notnull>true</notnull>
|
|
||||||
<length>64</length>
|
|
||||||
</field>
|
|
||||||
<field>
|
|
||||||
<name>deleted_at</name>
|
|
||||||
<type>integer</type>
|
|
||||||
<default>0</default>
|
|
||||||
<length>8</length>
|
<length>8</length>
|
||||||
<notnull>false</notnull>
|
<notnull>false</notnull>
|
||||||
<unsigned>true</unsigned>
|
<unsigned>true</unsigned>
|
||||||
@@ -317,13 +263,6 @@
|
|||||||
<notnull>true</notnull>
|
<notnull>true</notnull>
|
||||||
<length>8</length>
|
<length>8</length>
|
||||||
</field>
|
</field>
|
||||||
<field>
|
|
||||||
<name>last_modified</name>
|
|
||||||
<type>integer</type>
|
|
||||||
<default></default>
|
|
||||||
<notnull>false</notnull>
|
|
||||||
<unsigned>true</unsigned>
|
|
||||||
</field>
|
|
||||||
<index>
|
<index>
|
||||||
<name>deck_labels_board_id_index</name>
|
<name>deck_labels_board_id_index</name>
|
||||||
<field>
|
<field>
|
||||||
@@ -372,44 +311,6 @@
|
|||||||
</index>
|
</index>
|
||||||
</declaration>
|
</declaration>
|
||||||
</table>
|
</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>
|
<table>
|
||||||
<name>*dbprefix*deck_board_acl</name>
|
<name>*dbprefix*deck_board_acl</name>
|
||||||
<declaration>
|
<declaration>
|
||||||
|
|||||||
@@ -11,61 +11,30 @@
|
|||||||
- 📄 Write down additional notes in markdown
|
- 📄 Write down additional notes in markdown
|
||||||
- 🔖 Assign labels for even better organization
|
- 🔖 Assign labels for even better organization
|
||||||
- 👥 Share with your team, friends or family
|
- 👥 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
|
- 🚀 Get your project organized
|
||||||
|
|
||||||
|
💥 This is still alpha software: it may not be stable enough for production!
|
||||||
|
|
||||||
</description>
|
</description>
|
||||||
<version>0.6.0-beta1</version>
|
<version>0.2.4</version>
|
||||||
<licence>agpl</licence>
|
<licence>agpl</licence>
|
||||||
<author>Julius Härtl</author>
|
<author>Julius Härtl</author>
|
||||||
<namespace>Deck</namespace>
|
<namespace>Deck</namespace>
|
||||||
<types>
|
|
||||||
<dav />
|
|
||||||
</types>
|
|
||||||
<category>organization</category>
|
<category>organization</category>
|
||||||
<category>office</category>
|
<category>office</category>
|
||||||
<website>https://github.com/nextcloud/deck</website>
|
|
||||||
<bugs>https://github.com/nextcloud/deck/issues</bugs>
|
<bugs>https://github.com/nextcloud/deck/issues</bugs>
|
||||||
<repository type="git">https://github.com/nextcloud/deck.git</repository>
|
<repository type="git">https://github.com/nextcloud/deck.git</repository>
|
||||||
<screenshot>https://download.bitgrid.net/nextcloud/deck/screenshots/0.5/deck-notifications.png</screenshot>
|
<screenshot>https://download.bitgrid.net/nextcloud/deck/screenshots/Deck_Board.png</screenshot>
|
||||||
<screenshot>https://download.bitgrid.net/nextcloud/deck/screenshots/0.5/deck-comment2.png</screenshot>
|
<screenshot>https://download.bitgrid.net/nextcloud/deck/screenshots/Deck_Details.png</screenshot>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<php min-version="5.6"/>
|
<nextcloud min-version="11" max-version="13" />
|
||||||
<database min-version="9.4">pgsql</database>
|
|
||||||
<database>sqlite</database>
|
|
||||||
<database min-version="5.5">mysql</database>
|
|
||||||
<nextcloud min-version="15" max-version="16" />
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<background-jobs>
|
<background-jobs>
|
||||||
<job>OCA\Deck\Cron\DeleteCron</job>
|
<job>OCA\Deck\Cron\DeleteCron</job>
|
||||||
<job>OCA\Deck\Cron\ScheduledNotifications</job>
|
|
||||||
<job>OCA\Deck\Cron\CardDescriptionActivity</job>
|
|
||||||
</background-jobs>
|
</background-jobs>
|
||||||
<repair-steps>
|
<repair-steps>
|
||||||
<post-migration>
|
<post-migration>
|
||||||
<step>OCA\Deck\Migration\UnknownUsers</step>
|
<step>OCA\Deck\Migration\UnknownUsers</step>
|
||||||
</post-migration>
|
</post-migration>
|
||||||
</repair-steps>
|
</repair-steps>
|
||||||
<commands>
|
|
||||||
<command>OCA\Deck\Command\UserExport</command>
|
|
||||||
</commands>
|
|
||||||
<activity>
|
|
||||||
<settings>
|
|
||||||
<setting>OCA\Deck\Activity\Setting</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>
|
</info>
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
|
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
|
||||||
*
|
*
|
||||||
* @author 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
|
* @license GNU AGPL version 3 or any later version
|
||||||
*
|
*
|
||||||
@@ -26,9 +25,6 @@ return [
|
|||||||
'routes' => [
|
'routes' => [
|
||||||
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
|
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
|
||||||
|
|
||||||
['name' => 'Config#get', 'url' => '/config', 'verb' => 'GET'],
|
|
||||||
['name' => 'Config#setValue', 'url' => '/config/{key}', 'verb' => 'POST'],
|
|
||||||
|
|
||||||
// boards
|
// boards
|
||||||
['name' => 'board#index', 'url' => '/boards', 'verb' => 'GET'],
|
['name' => 'board#index', 'url' => '/boards', 'verb' => 'GET'],
|
||||||
['name' => 'board#create', 'url' => '/boards', 'verb' => 'POST'],
|
['name' => 'board#create', 'url' => '/boards', 'verb' => 'POST'],
|
||||||
@@ -38,7 +34,7 @@ return [
|
|||||||
['name' => 'board#deleteUndo', 'url' => '/boards/{boardId}/deleteUndo', 'verb' => 'POST'],
|
['name' => 'board#deleteUndo', 'url' => '/boards/{boardId}/deleteUndo', 'verb' => 'POST'],
|
||||||
['name' => 'board#getUserPermissions', 'url' => '/boards/{boardId}/permissions', 'verb' => 'GET'],
|
['name' => 'board#getUserPermissions', 'url' => '/boards/{boardId}/permissions', 'verb' => 'GET'],
|
||||||
['name' => 'board#addAcl', 'url' => '/boards/{boardId}/acl', 'verb' => 'POST'],
|
['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#deleteAcl', 'url' => '/boards/{boardId}/acl/{aclId}', 'verb' => 'DELETE'],
|
||||||
|
|
||||||
// stacks
|
// stacks
|
||||||
@@ -47,7 +43,6 @@ return [
|
|||||||
['name' => 'stack#update', 'url' => '/stacks/{stackId}', 'verb' => 'PUT'],
|
['name' => 'stack#update', 'url' => '/stacks/{stackId}', 'verb' => 'PUT'],
|
||||||
['name' => 'stack#reorder', 'url' => '/stacks/{stackId}/reorder', 'verb' => 'PUT'],
|
['name' => 'stack#reorder', 'url' => '/stacks/{stackId}/reorder', 'verb' => 'PUT'],
|
||||||
['name' => 'stack#delete', 'url' => '/stacks/{stackId}', 'verb' => 'DELETE'],
|
['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'],
|
['name' => 'stack#archived', 'url' => '/stacks/{boardId}/archived', 'verb' => 'GET'],
|
||||||
|
|
||||||
// cards
|
// cards
|
||||||
@@ -55,72 +50,17 @@ return [
|
|||||||
['name' => 'card#create', 'url' => '/cards', 'verb' => 'POST'],
|
['name' => 'card#create', 'url' => '/cards', 'verb' => 'POST'],
|
||||||
['name' => 'card#update', 'url' => '/cards/{cardId}', 'verb' => 'PUT'],
|
['name' => 'card#update', 'url' => '/cards/{cardId}', 'verb' => 'PUT'],
|
||||||
['name' => 'card#delete', 'url' => '/cards/{cardId}', 'verb' => 'DELETE'],
|
['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#rename', 'url' => '/cards/{cardId}/rename', 'verb' => 'PUT'],
|
||||||
['name' => 'card#reorder', 'url' => '/cards/{cardId}/reorder', 'verb' => 'PUT'],
|
['name' => 'card#reorder', 'url' => '/cards/{cardId}/reorder', 'verb' => 'PUT'],
|
||||||
['name' => 'card#archive', 'url' => '/cards/{cardId}/archive', 'verb' => 'PUT'],
|
['name' => 'card#archive', 'url' => '/cards/{cardId}/archive', 'verb' => 'PUT'],
|
||||||
['name' => 'card#unarchive', 'url' => '/cards/{cardId}/unarchive', 'verb' => 'PUT'],
|
['name' => 'card#unarchive', 'url' => '/cards/{cardId}/unarchive', 'verb' => 'PUT'],
|
||||||
['name' => 'card#assignLabel', 'url' => '/cards/{cardId}/label/{labelId}', 'verb' => 'POST'],
|
['name' => 'card#assignLabel', 'url' => '/cards/{cardId}/label/{labelId}', 'verb' => 'POST'],
|
||||||
['name' => 'card#removeLabel', 'url' => '/cards/{cardId}/label/{labelId}', 'verb' => 'DELETE'],
|
['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}/assign/{userId}', 'verb' => 'DELETE'],
|
|
||||||
|
|
||||||
['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'],
|
|
||||||
|
|
||||||
|
|
||||||
// labels
|
// labels
|
||||||
['name' => 'label#create', 'url' => '/labels', 'verb' => 'POST'],
|
['name' => 'label#create', 'url' => '/labels', 'verb' => 'POST'],
|
||||||
['name' => 'label#update', 'url' => '/labels/{labelId}', 'verb' => 'PUT'],
|
['name' => 'label#update', 'url' => '/labels/{labelId}', 'verb' => 'PUT'],
|
||||||
['name' => 'label#delete', 'url' => '/labels/{labelId}', 'verb' => 'DELETE'],
|
['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', 'verb' => 'DELETE'],
|
|
||||||
['name' => 'board_api#updateAcl', 'url' => '/api/v1.0/boards/{boardId}/acl', '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' => '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' => '.+']],
|
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "nextcloud/deck",
|
|
||||||
"type": "project",
|
|
||||||
"license": "AGPLv3",
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Julius Härtl",
|
|
||||||
"email": "jus@bitgrid.net"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"require": {
|
|
||||||
"cogpowered/finediff": "0.3.*"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"roave/security-advisories": "dev-master",
|
|
||||||
"christophwurst/nextcloud": "^14.0",
|
|
||||||
"jakub-onderka/php-parallel-lint": "^1.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
* @copyright Copyright (c) 2018 Michael Weimann <mail@michael-weimann.eu>
|
|
||||||
*
|
|
||||||
* @author 2018 Michael Weimann <mail@michael-weimann.eu>
|
|
||||||
*
|
|
||||||
* @license GNU AGPL version 3 or any later version
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
.compact-item.ng-enter,
|
|
||||||
.compact-item.ng-leave {
|
|
||||||
overflow: hidden;
|
|
||||||
transition: all 250ms linear;
|
|
||||||
}
|
|
||||||
|
|
||||||
.compact-item.ng-enter {
|
|
||||||
max-height: 0;
|
|
||||||
|
|
||||||
&.ng-enter-active {
|
|
||||||
max-height: 50px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.compact-item.ng-leave {
|
|
||||||
max-height: 50px;
|
|
||||||
|
|
||||||
&.ng-leave-active {
|
|
||||||
max-height: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
/**
|
|
||||||
* based upon apps/comments/js/vendor/At.js/dist/css/jquery.atwho.css,
|
|
||||||
* only changed colors and font-weight
|
|
||||||
*/
|
|
||||||
|
|
||||||
.atwho-view {
|
|
||||||
position:absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
display: none;
|
|
||||||
margin-top: 18px;
|
|
||||||
background: var(--color-main-background);
|
|
||||||
color: var(--color-main-text);
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
box-shadow: 0 0 5px var(--color-box-shadow);
|
|
||||||
min-width: 120px;
|
|
||||||
z-index: 11110 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.atwho-view .atwho-header {
|
|
||||||
padding: 5px;
|
|
||||||
margin: 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
border-bottom: solid 1px var(--color-border);
|
|
||||||
color: var(--color-main-text);
|
|
||||||
font-size: 11px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.atwho-view .atwho-header .small {
|
|
||||||
color: var(--color-main-text);
|
|
||||||
float: right;
|
|
||||||
padding-top: 2px;
|
|
||||||
margin-right: -5px;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.atwho-view .atwho-header:hover {
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
.atwho-view .cur {
|
|
||||||
background: var(--color-primary);
|
|
||||||
color: var(--color-primary-text);
|
|
||||||
}
|
|
||||||
.atwho-view .cur small {
|
|
||||||
color: var(--color-primary-text);
|
|
||||||
}
|
|
||||||
.atwho-view strong {
|
|
||||||
color: var(--color-main-text);
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
.atwho-view .cur strong {
|
|
||||||
color: var(--color-primary-text);
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
.atwho-view ul {
|
|
||||||
/* width: 100px; */
|
|
||||||
list-style:none;
|
|
||||||
padding:0;
|
|
||||||
margin:auto;
|
|
||||||
max-height: 200px;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
.atwho-view ul li {
|
|
||||||
display: block;
|
|
||||||
padding: 5px 10px;
|
|
||||||
border-bottom: 1px solid var(--color-border);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.atwho-view small {
|
|
||||||
font-size: smaller;
|
|
||||||
color: var(--color-main-text);
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
.icon-deck {
|
|
||||||
background-image: url('../img/deck-dark.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.resource-type-deck img {
|
|
||||||
opacity: 0.4 !important;
|
|
||||||
}
|
|
||||||
.resource-type-deck:hover img {
|
|
||||||
opacity: 0.7 !important;
|
|
||||||
}
|
|
||||||
@@ -1,261 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2016
|
|
||||||
*
|
|
||||||
* This file is licensed under the Affero General Public License version 3
|
|
||||||
* or later.
|
|
||||||
*
|
|
||||||
* See the COPYING-README file.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#commentsTabView .emptycontent {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#commentsTabView .newCommentForm {
|
|
||||||
margin-left: 36px;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
#commentsTabView .newCommentForm .message {
|
|
||||||
width: 100%;
|
|
||||||
padding: 10px;
|
|
||||||
min-height: 44px;
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
/* Prevent the text from overlapping with the submit button. */
|
|
||||||
padding-right: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#commentsTabView .newCommentForm {
|
|
||||||
.submit,
|
|
||||||
.submitLoading {
|
|
||||||
width: 44px;
|
|
||||||
height: 44px;
|
|
||||||
margin: 0;
|
|
||||||
padding: 13px;
|
|
||||||
background-color: transparent;
|
|
||||||
border: none;
|
|
||||||
opacity: .3;
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#commentsTabView .deleteLoading {
|
|
||||||
padding: 14px;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
#commentsTabView .newCommentForm .submit:not(:disabled):hover,
|
|
||||||
#commentsTabView .newCommentForm .submit:not(:disabled):focus {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#commentsTabView .newCommentForm div.message {
|
|
||||||
resize: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#commentsTabView .newCommentForm div.message:empty:before {
|
|
||||||
content: attr(data-placeholder);
|
|
||||||
color: grey;
|
|
||||||
}
|
|
||||||
|
|
||||||
#commentsTabView .comment {
|
|
||||||
position: relative;
|
|
||||||
/** padding bottom is little more so that the top and bottom gap look uniform **/
|
|
||||||
padding: 10px 0 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#commentsTabView .comments .comment {
|
|
||||||
border-top: 1px solid var(--color-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
#commentsTabView .comment .avatar,
|
|
||||||
.atwho-view-ul * .avatar{
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
line-height: 32px;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#commentsTabView .comment .message .avatar,
|
|
||||||
.atwho-view-ul * .avatar
|
|
||||||
{
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
#activityTabView li.comment.collapsed .activitymessage,
|
|
||||||
#commentsTabView .comment.collapsed .message {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
#activityTabView li.comment.collapsed .activitymessage,
|
|
||||||
#commentsTabView .comment.collapsed .message {
|
|
||||||
max-height: 70px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
#activityTabView li.comment .message-overlay,
|
|
||||||
#commentsTabView .comment .message-overlay {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#activityTabView li.comment.collapsed .message-overlay,
|
|
||||||
#commentsTabView .comment.collapsed .message-overlay {
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
z-index: 2;
|
|
||||||
height: 50px;
|
|
||||||
pointer-events: none;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background: -moz-linear-gradient(rgba(var(--color-main-background), 0), var(--color-main-background));
|
|
||||||
background: -webkit-linear-gradient(rgba(var(--color-main-background), 0), var(--color-main-background));
|
|
||||||
background: -o-linear-gradient(rgba(var(--color-main-background), 0), var(--color-main-background));
|
|
||||||
background: -ms-linear-gradient(rgba(var(--color-main-background), 0), var(--color-main-background));
|
|
||||||
background: linear-gradient(rgba(var(--color-main-background), 0), var(--color-main-background));
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
#commentsTabView .hidden {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** set min-height as 44px to ensure that it fits the button sizes. **/
|
|
||||||
#commentsTabView .comment .authorRow {
|
|
||||||
min-height: 44px;
|
|
||||||
}
|
|
||||||
#commentsTabView .comment .authorRow .tooltip {
|
|
||||||
/** because of the padding on the element, the tooltip appear too far up,
|
|
||||||
adding this brings them closer to the element**/
|
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.atwho-view-ul * .avatar-name-wrapper,
|
|
||||||
#commentsTabView .comment .authorRow {
|
|
||||||
position: relative;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#commentsTabView .comment:not(.newCommentRow) .message .avatar-name-wrapper:not(.currentUser),
|
|
||||||
#commentsTabView .comment:not(.newCommentRow) .message .avatar-name-wrapper:not(.currentUser) .avatar,
|
|
||||||
#commentsTabView .comment:not(.newCommentRow) .message .avatar-name-wrapper:not(.currentUser) .avatar img,
|
|
||||||
#commentsTabView .comment .authorRow .avatar:not(.currentUser),
|
|
||||||
#commentsTabView .comment .authorRow .author:not(.currentUser) {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.atwho-view-ul .avatar-name-wrapper,
|
|
||||||
.atwho-view-ul .avatar-name-wrapper .avatar,
|
|
||||||
.atwho-view-ul .avatar-name-wrapper .avatar img {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
#commentsTabView .comments li .message .atwho-inserted,
|
|
||||||
#commentsTabView .newCommentForm .atwho-inserted {
|
|
||||||
.avatar-name-wrapper {
|
|
||||||
/* Make the wrapper the positioning context of its child contacts
|
|
||||||
* menu. */
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
display: inline;
|
|
||||||
vertical-align: top;
|
|
||||||
background-color: var(--color-background-dark);
|
|
||||||
border-radius: 50vh;
|
|
||||||
padding: 1px 7px 1px 1px;
|
|
||||||
|
|
||||||
/* Ensure that the avatar and the user name will be kept together. */
|
|
||||||
white-space: nowrap;
|
|
||||||
|
|
||||||
.avatar {
|
|
||||||
img {
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
height: 16px;
|
|
||||||
width: 16px;
|
|
||||||
vertical-align: middle;
|
|
||||||
padding: 1px;
|
|
||||||
margin-top: -3px;
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 2px;
|
|
||||||
}
|
|
||||||
strong {
|
|
||||||
/* Ensure that the user name is shown in bold, as different browsers
|
|
||||||
* use different font weights for strong elements. */
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.avatar-name-wrapper.currentUser {
|
|
||||||
background-color: var(--color-primary);
|
|
||||||
color: var(--color-primary-text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.atwho-view-ul * .avatar-name-wrapper {
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
#commentsTabView .comment .author,
|
|
||||||
#commentsTabView .comment .date {
|
|
||||||
opacity: .5;
|
|
||||||
}
|
|
||||||
#commentsTabView .comment .author {
|
|
||||||
max-width: 210px;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
#commentsTabView .comment .date {
|
|
||||||
margin-left: auto;
|
|
||||||
/** this is to fix the tooltip being too close due to the margin-top applied
|
|
||||||
to bring the tooltip closer for the action icons **/
|
|
||||||
padding: 10px 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#commentsTabView .comments > li:not(.newCommentRow) .message {
|
|
||||||
padding-left: 40px;
|
|
||||||
word-wrap: break-word;
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
#commentsTabView .comment .action {
|
|
||||||
opacity: 0.3;
|
|
||||||
padding: 14px;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
#commentsTabView .comment .action:hover,
|
|
||||||
#commentsTabView .comment .action:focus {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#commentsTabView .newCommentRow .action-container {
|
|
||||||
margin-left: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#commentsTabView .comment.disabled .message {
|
|
||||||
opacity: 0.3;
|
|
||||||
}
|
|
||||||
|
|
||||||
#commentsTabView .comment.disabled .action {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#commentsTabView .message.error {
|
|
||||||
color: #e9322d;
|
|
||||||
border-color: #e9322d;
|
|
||||||
box-shadow: 0 0 6px #f8b9b7;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-files .action-comment {
|
|
||||||
padding: 16px 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#commentsTabView .comment .message .contactsmenu-popover {
|
|
||||||
left: -6px;
|
|
||||||
top: 24px;
|
|
||||||
}
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
/*
|
|
||||||
* @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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
* @copyright Copyright (c) 2018 Michael Weimann <mail@michael-weimann.eu>
|
|
||||||
*
|
|
||||||
* @author 2018 Michael Weimann <mail@michael-weimann.eu>
|
|
||||||
*
|
|
||||||
* @license GNU AGPL version 3 or any later version
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
.compact-mode {
|
|
||||||
.card {
|
|
||||||
margin: $compact-board-item-margin;
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
margin: $compact-board-last-item-margin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.stack {
|
|
||||||
.as-sortable-placeholder {
|
|
||||||
margin: $compact-board-item-margin;
|
|
||||||
min-height: 43px;
|
|
||||||
height: 43px;
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
margin: $compact-board-last-item-margin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
/**
|
|
||||||
* Custom icons
|
|
||||||
*/
|
|
||||||
.icon-deck {
|
|
||||||
background-image: url('../img/deck-dark.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-group {
|
|
||||||
background-image: url('../../../settings/img/users.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-help {
|
|
||||||
background-image: url('../../../settings/img/help.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-add-white {
|
|
||||||
background-image: url('../img/add-white.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: var(--icon-home-000, url('../../../core/img/places/home.svg'));
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-description {
|
|
||||||
background-image: var(--icon-text-000, url('../img/description.svg'));
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-badge {
|
|
||||||
background-image: url('../img/calendar-dark.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-toggle-compact-collapsed {
|
|
||||||
background-image: url('../img/toggle-view-expand.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-toggle-compact-expanded {
|
|
||||||
background-image: url('../img/toggle-view-collapse.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
@if mixin-exists('icon-black-white') {
|
|
||||||
@include icon-black-white('deck', 'deck', 1);
|
|
||||||
@include icon-black-white('archive', 'deck', 1);
|
|
||||||
@include icon-black-white('circles', 'deck', 1);
|
|
||||||
|
|
||||||
.icon-toggle-compact-collapsed {
|
|
||||||
@include icon-color('toggle-view-expand', 'deck', $color-black);
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-toggle-compact-expanded {
|
|
||||||
@include icon-color('toggle-view-collapse', 'deck', $color-black);
|
|
||||||
}
|
|
||||||
.icon-activity {
|
|
||||||
@include icon-color('activity-dark', 'activity', $color-black);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatardiv.circles {
|
|
||||||
background: var(--color-primary);
|
|
||||||
}
|
|
||||||
.icon-circles {
|
|
||||||
opacity: 1;
|
|
||||||
background-size: 20px;
|
|
||||||
background-position: center center;
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
<?php
|
/*
|
||||||
/**
|
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
|
||||||
* @copyright Copyright (c) 2018 Ryan Fletcher <ryan.fletcher@codepassion.ca>
|
|
||||||
*
|
*
|
||||||
* @author Ryan Fletcher <ryan.fletcher@codepassion.ca>
|
* @author Julius Härtl <jus@bitgrid.net>
|
||||||
*
|
*
|
||||||
* @license GNU AGPL version 3 or any later version
|
* @license GNU AGPL version 3 or any later version
|
||||||
*
|
*
|
||||||
@@ -21,16 +20,5 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace OCA\Deck;
|
@import '../../../core/css/variables.scss';
|
||||||
|
@import 'style.scss';
|
||||||
use OCP\AppFramework\Http;
|
|
||||||
class BadRequestException extends StatusException {
|
|
||||||
|
|
||||||
public function __construct($message) {
|
|
||||||
parent::__construct($message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getStatus() {
|
|
||||||
return HTTP::STATUS_BAD_REQUEST;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,90 +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;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.due { }
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1019
css/style.scss
@@ -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.
|
|
||||||
897
docs/API.md
@@ -1,897 +0,0 @@
|
|||||||
|
|
||||||
The REST API provides access for authenticated users to their data inside the Deck app. To get a better understand of Decks data models and their relations, please have a look at the [data structure](structure.md) documentation.
|
|
||||||
|
|
||||||
# Prequisited
|
|
||||||
|
|
||||||
- All requests require a `OCS-APIRequest` HTTP header to be set to `true` and a `Content-Type` of `application/json`.
|
|
||||||
- The API is located at https://nextcloud.local/index.php/apps/deck/api/v1.0
|
|
||||||
|
|
||||||
## Naming
|
|
||||||
|
|
||||||
- Board is the the project like grouping of tasks that can be shared to different users and groups
|
|
||||||
|
|
||||||
- Stack is the grouping of cards which is rendered in vertical columns in the UI
|
|
||||||
|
|
||||||
- Card is the representation of a single task
|
|
||||||
|
|
||||||
- Labels are defined on a board level and can be assigned to any number of cards
|
|
||||||
|
|
||||||
## Global responses
|
|
||||||
|
|
||||||
### 400 Bad request
|
|
||||||
|
|
||||||
In case the request is invalid, e.g. because a parameter is missing, a 400 error will be returned:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"status": 400,
|
|
||||||
"message": "title must be provided"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 403 Permission denied
|
|
||||||
|
|
||||||
In any case a user doesn't have access to a requested entity, a 403 error will be returned:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"status": 403,
|
|
||||||
"message": "Permission denied"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Headers
|
|
||||||
|
|
||||||
### If-Modified-Since
|
|
||||||
|
|
||||||
Some index endpoints support limiting the result set to entries that have been changed since the given time.
|
|
||||||
|
|
||||||
Example curl request:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -u admin:admin -X GET \
|
|
||||||
'http://localhost:8000/index.php/apps/deck/api/v1.0/boards/2/stacks' \
|
|
||||||
-H "OCS-APIRequest: true" \
|
|
||||||
-H "If-Modified-Since: Mon, 5 Nov 2018 09:28:00 GMT"
|
|
||||||
```
|
|
||||||
|
|
||||||
# Endpoints
|
|
||||||
|
|
||||||
## Boards
|
|
||||||
|
|
||||||
### GET /boards - Get a list of boards
|
|
||||||
|
|
||||||
#### Headers
|
|
||||||
|
|
||||||
The board list endpoint supports setting an `If-Modified-Since` header to limit the results to entities that are changed after the provided time.
|
|
||||||
|
|
||||||
#### Request parameters
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | ---------------------------- |
|
|
||||||
| options | Bool | **Optional** Enhance boards with details about labels, stacks and users |
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
Returns an array of board items
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"title": "Board title",
|
|
||||||
"owner": {
|
|
||||||
"primaryKey": "admin",
|
|
||||||
"uid": "admin",
|
|
||||||
"displayname": "Administrator"
|
|
||||||
},
|
|
||||||
"color": "ff0000",
|
|
||||||
"archived": false,
|
|
||||||
"labels": [],
|
|
||||||
"acl": [],
|
|
||||||
"permissions": {
|
|
||||||
"PERMISSION_READ": true,
|
|
||||||
"PERMISSION_EDIT": true,
|
|
||||||
"PERMISSION_MANAGE": true,
|
|
||||||
"PERMISSION_SHARE": true
|
|
||||||
},
|
|
||||||
"users": [],
|
|
||||||
"shared": 0,
|
|
||||||
"deletedAt": 0,
|
|
||||||
"id": 10
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
### POST /boards - Create a new board
|
|
||||||
|
|
||||||
#### Request body
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------ | ---------------------------------------------------- |
|
|
||||||
| title | String | The title of the new board |
|
|
||||||
| color | String | The hexadecimal color of the new board (e.g. FF0000) |
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"title": "Board title",
|
|
||||||
"color": "ff0000"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"title": "Board title",
|
|
||||||
"owner": {
|
|
||||||
"primaryKey": "admin",
|
|
||||||
"uid": "admin",
|
|
||||||
"displayname": "Administrator"
|
|
||||||
},
|
|
||||||
"color": "ff0000",
|
|
||||||
"archived": false,
|
|
||||||
"labels": [
|
|
||||||
{
|
|
||||||
"title": "Finished",
|
|
||||||
"color": "31CC7C",
|
|
||||||
"boardId": 10,
|
|
||||||
"cardId": null,
|
|
||||||
"id": 37
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "To review",
|
|
||||||
"color": "317CCC",
|
|
||||||
"boardId": 10,
|
|
||||||
"cardId": null,
|
|
||||||
"id": 38
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Action needed",
|
|
||||||
"color": "FF7A66",
|
|
||||||
"boardId": 10,
|
|
||||||
"cardId": null,
|
|
||||||
"id": 39
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Later",
|
|
||||||
"color": "F1DB50",
|
|
||||||
"boardId": 10,
|
|
||||||
"cardId": null,
|
|
||||||
"id": 40
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"acl": [],
|
|
||||||
"permissions": {
|
|
||||||
"PERMISSION_READ": true,
|
|
||||||
"PERMISSION_EDIT": true,
|
|
||||||
"PERMISSION_MANAGE": true,
|
|
||||||
"PERMISSION_SHARE": true
|
|
||||||
},
|
|
||||||
"users": [],
|
|
||||||
"deletedAt": 0,
|
|
||||||
"id": 10
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### GET /boards/{boardId} - Get board details
|
|
||||||
|
|
||||||
#### Request parameters
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | ---------------------------- |
|
|
||||||
| boardId | Integer | The id of the board to fetch |
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"title": "Board title",
|
|
||||||
"owner": {
|
|
||||||
"primaryKey": "admin",
|
|
||||||
"uid": "admin",
|
|
||||||
"displayname": "Administrator"
|
|
||||||
},
|
|
||||||
"color": "ff0000",
|
|
||||||
"archived": false,
|
|
||||||
"labels": [
|
|
||||||
{
|
|
||||||
"title": "Finished",
|
|
||||||
"color": "31CC7C",
|
|
||||||
"boardId": "10",
|
|
||||||
"cardId": null,
|
|
||||||
"id": 37
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "To review",
|
|
||||||
"color": "317CCC",
|
|
||||||
"boardId": "10",
|
|
||||||
"cardId": null,
|
|
||||||
"id": 38
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Action needed",
|
|
||||||
"color": "FF7A66",
|
|
||||||
"boardId": "10",
|
|
||||||
"cardId": null,
|
|
||||||
"id": 39
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Later",
|
|
||||||
"color": "F1DB50",
|
|
||||||
"boardId": "10",
|
|
||||||
"cardId": null,
|
|
||||||
"id": 40
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"acl": [],
|
|
||||||
"permissions": {
|
|
||||||
"PERMISSION_READ": true,
|
|
||||||
"PERMISSION_EDIT": true,
|
|
||||||
"PERMISSION_MANAGE": true,
|
|
||||||
"PERMISSION_SHARE": true
|
|
||||||
},
|
|
||||||
"users": [
|
|
||||||
{
|
|
||||||
"primaryKey": "admin",
|
|
||||||
"uid": "admin",
|
|
||||||
"displayname": "Administrator"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"deletedAt": 0,
|
|
||||||
"id": 10
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### PUT /boards/{boardId} - Update board details
|
|
||||||
|
|
||||||
#### Request body
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------ | ---------------------------------------------------- |
|
|
||||||
| title | String | The title of the new board |
|
|
||||||
| color | String | The hexadecimal color of the new board (e.g. FF0000) |
|
|
||||||
| archived | Bool | The hexadecimal color of the new board (e.g. FF0000) |
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"title": "Board title",
|
|
||||||
"color": "ff0000",
|
|
||||||
"archived": false
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
### DELETE /boards/{boardId} - Delete a board
|
|
||||||
|
|
||||||
#### Request parameters
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | ---------------------------- |
|
|
||||||
| boardId | Integer | The id of the board to fetch |
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
### POST /boards/{boardId}/undo_delete - Restore a deleted board
|
|
||||||
|
|
||||||
#### Request parameters
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | ---------------------------- |
|
|
||||||
| boardId | Integer | The id of the board to fetch |
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
### POST /boards/{boardId}/acl - Add new acl rule
|
|
||||||
|
|
||||||
#### Request body
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------ | ---------------------------------------------------- |
|
|
||||||
| type | Integer | Type of the participant |
|
|
||||||
| participant | String | The uid of the participant |
|
|
||||||
| permissionEdit | Bool | Setting if the participant has edit permissions |
|
|
||||||
| permissionShare | Bool | Setting if the participant has sharing permissions |
|
|
||||||
| permissionManage | Bool | Setting if the participant has management permissions |
|
|
||||||
|
|
||||||
##### Supported participant types:
|
|
||||||
- 0 User
|
|
||||||
- 1 Group
|
|
||||||
- 7 Circle
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
```json
|
|
||||||
[{
|
|
||||||
"participant": {
|
|
||||||
"primaryKey": "userid",
|
|
||||||
"uid": "userid",
|
|
||||||
"displayname": "User Name"
|
|
||||||
},
|
|
||||||
"type": 0,
|
|
||||||
"boardId": 1,
|
|
||||||
"permissionEdit": true,
|
|
||||||
"permissionShare": false,
|
|
||||||
"permissionManage": true,
|
|
||||||
"owner": false,
|
|
||||||
"id": 1
|
|
||||||
}]
|
|
||||||
```
|
|
||||||
|
|
||||||
### PUT /boards/{boardId}/acl/{aclId} - Update an acl rule
|
|
||||||
|
|
||||||
#### Request parameters
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------ | ---------------------------------------------------- |
|
|
||||||
| permissionEdit | Bool | Setting if the participant has edit permissions |
|
|
||||||
| permissionShare | Bool | Setting if the participant has sharing permissions |
|
|
||||||
| permissionManage | Bool | Setting if the participant has management permissions |
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
### DELETE /boards/{boardId}/acl/{aclId} - Delete an acl rule
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
## Stacks
|
|
||||||
|
|
||||||
### GET /board/{boardId}/stacks - Get stacks
|
|
||||||
|
|
||||||
#### Headers
|
|
||||||
|
|
||||||
The board list endpoint supports setting an `If-Modified-Since` header to limit the results to entities that are changed after the provided time.
|
|
||||||
|
|
||||||
|
|
||||||
#### Request parameters
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | ---------------------------- |
|
|
||||||
| boardId | Integer | The id of the board to fetch |
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"title": "ToDo",
|
|
||||||
"boardId": 2,
|
|
||||||
"deletedAt": 0,
|
|
||||||
"lastModified": 1541426139,
|
|
||||||
"cards": [...],
|
|
||||||
"order": 999,
|
|
||||||
"id": 4
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
### GET /board/{boardId}/stacks/archived - Get list of archived stacks
|
|
||||||
|
|
||||||
#### Request parameters
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | ---------------------------- |
|
|
||||||
| boardId | Integer | The id of the board to fetch |
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"title": "ToDo",
|
|
||||||
"boardId": 2,
|
|
||||||
"deletedAt": 0,
|
|
||||||
"lastModified": 1541426139,
|
|
||||||
"cards": [...],
|
|
||||||
"order": 999,
|
|
||||||
"id": 4
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
### GET /board/{boardId}/stacks/{stackId} - Get stack details
|
|
||||||
|
|
||||||
#### Request parameters
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | ---------------------------------------- |
|
|
||||||
| boardId | Integer | The id of the board the stack belongs to |
|
|
||||||
| stackId | Integer | The id of the stack |
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
### POST /board/{boardId}/stacks - Create a new stack
|
|
||||||
|
|
||||||
#### Request parameters
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | ---------------------------- |
|
|
||||||
| boardId | Integer | The id of the board to fetch |
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
### PUT /board/{boardId}/stacks/{stackId} - Update stack details
|
|
||||||
|
|
||||||
#### Request parameters
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | ---------------------------------------- |
|
|
||||||
| boardId | Integer | The id of the board the stack belongs to |
|
|
||||||
| stackId | Integer | The id of the stack |
|
|
||||||
|
|
||||||
#### Request body
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | ---------------------------------------------------- |
|
|
||||||
| title | String | The title of the new stack |
|
|
||||||
| order | Integer | Order for sorting the stacks |
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
### DELETE /board/{boardId}/stacks/{stackId} - Delete a stack
|
|
||||||
|
|
||||||
#### Request parameters
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | ---------------------------------------- |
|
|
||||||
| boardId | Integer | The id of the board the stack belongs to |
|
|
||||||
| stackId | Integer | The id of the stack |
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
## Cards
|
|
||||||
|
|
||||||
### GET /board/{boardId}/stacks/{stackId}/cards/{cardId} - Get card details
|
|
||||||
|
|
||||||
#### Request parameters
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | --------------------------------------- |
|
|
||||||
| boardId | Integer | The id of the board the card belongs to |
|
|
||||||
| stackId | Integer | The id of the stack the card belongs to |
|
|
||||||
| cardId | Integer | The id of the card |
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
### POST /board/{boardId}/stacks/{stackId}/cards - Create a new card
|
|
||||||
|
|
||||||
#### Request parameters
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | --------------------------------------- |
|
|
||||||
| boardId | Integer | The id of the board the card belongs to |
|
|
||||||
| stackId | Integer | The id of the stack the card belongs to |
|
|
||||||
|
|
||||||
#### Request body
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | ---------------------------------------------------- |
|
|
||||||
| title | String | The title of the new stack |
|
|
||||||
| type | String | Type of the card (for later use) use 'plain' for now |
|
|
||||||
| order | Integer | Order for sorting the stacks |
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"title":"Test",
|
|
||||||
"description":null,
|
|
||||||
"stackId":6,
|
|
||||||
"type":"plain",
|
|
||||||
"lastModified":1541528026,
|
|
||||||
"createdAt":1541528026,
|
|
||||||
"labels":null,
|
|
||||||
"assignedUsers":null,
|
|
||||||
"attachments":null,
|
|
||||||
"attachmentCount":null,
|
|
||||||
"owner":"admin",
|
|
||||||
"order":999,
|
|
||||||
"archived":false,
|
|
||||||
"duedate":null,
|
|
||||||
"deletedAt":0,
|
|
||||||
"commentsUnread":0,
|
|
||||||
"id":10,
|
|
||||||
"overdue":0
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
### PUT /board/{boardId}/stacks/{stackId}/cards/{cardId} - Update card details
|
|
||||||
|
|
||||||
#### Request parameters
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | --------------------------------------- |
|
|
||||||
| boardId | Integer | The id of the board the card belongs to |
|
|
||||||
| stackId | Integer | The id of the stack the card belongs to |
|
|
||||||
| cardId | Integer | The id of the card |
|
|
||||||
|
|
||||||
#### Request data
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
|-------------|-----------|------------------------------------------------------|
|
|
||||||
| title | String | The card title |
|
|
||||||
| description | String | The markdown description of the card |
|
|
||||||
| type | String | Type of the card (for later use) use 'plain' for now |
|
|
||||||
| order | Integer | Order for sorting the stacks |
|
|
||||||
| duedate | timestamp | The duedate of the card or null |
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"title": "Test card",
|
|
||||||
"description": "A card description",
|
|
||||||
"type": "plain",
|
|
||||||
"order": 999,
|
|
||||||
"duedate": null,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
### DELETE /board/{boardId}/stacks/{stackId}/cards/{cardId} - Delete a card
|
|
||||||
|
|
||||||
#### Request parameters
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | --------------------------------------- |
|
|
||||||
| boardId | Integer | The id of the board the card belongs to |
|
|
||||||
| stackId | Integer | The id of the stack the card belongs to |
|
|
||||||
| cardId | Integer | The id of the card |
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
### PUT /board/{boardId}/stacks/{stackId}/cards/{cardId}/assignLabel - Assign a label to a card
|
|
||||||
|
|
||||||
#### Request parameters
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | --------------------------------------- |
|
|
||||||
| boardId | Integer | The id of the board the card belongs to |
|
|
||||||
| stackId | Integer | The id of the stack the card belongs to |
|
|
||||||
| cardId | Integer | The id of the card |
|
|
||||||
|
|
||||||
#### Request data
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | --------------------------------------- |
|
|
||||||
| labelId | Integer | The label id to assign to the card |
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
### PUT /board/{boardId}/stacks/{stackId}/cards/{cardId}/removeLabel - Remove a label to a card
|
|
||||||
|
|
||||||
#### Request parameters
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | --------------------------------------- |
|
|
||||||
| boardId | Integer | The id of the board the card belongs to |
|
|
||||||
| stackId | Integer | The id of the stack the card belongs to |
|
|
||||||
| cardId | Integer | The id of the card |
|
|
||||||
|
|
||||||
#### Request data
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | --------------------------------------- |
|
|
||||||
| labelId | Integer | The label id to remove to the card |
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
### PUT /board/{boardId}/stacks/{stackId}/cards/{cardId}/assignUser - Assign a user to a card
|
|
||||||
|
|
||||||
#### Request parameters
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | --------------------------------------- |
|
|
||||||
| boardId | Integer | The id of the board the card belongs to |
|
|
||||||
| stackId | Integer | The id of the stack the card belongs to |
|
|
||||||
| cardId | Integer | The id of the card |
|
|
||||||
|
|
||||||
#### Request data
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | --------------------------------------- |
|
|
||||||
| userId | String | The user id to assign to the card |
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
### PUT /board/{boardId}/stacks/{stackId}/cards/{cardId}/unassignUser - Assign a user to a card
|
|
||||||
|
|
||||||
#### Request parameters
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | --------------------------------------- |
|
|
||||||
| boardId | Integer | The id of the board the card belongs to |
|
|
||||||
| stackId | Integer | The id of the stack the card belongs to |
|
|
||||||
| cardId | Integer | The id of the card |
|
|
||||||
|
|
||||||
#### Request data
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | --------------------------------------- |
|
|
||||||
| userId | String | The user id to assign to the card |
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
### PUT /board/{boardId}/stacks/{stackId}/cards/{cardId}/reorder - Change the sorting order of a card
|
|
||||||
|
|
||||||
#### Request parameters
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | --------------------------------------- |
|
|
||||||
| boardId | Integer | The id of the board the card belongs to |
|
|
||||||
| stackId | Integer | The id of the stack the card belongs to |
|
|
||||||
| cardId | Integer | The id of the card |
|
|
||||||
|
|
||||||
#### Request data
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | ----------------------------------------------------------- |
|
|
||||||
| order | Integer | The position in the stack where the card should be moved to |
|
|
||||||
| stackId | Integer | The id of the stack where the card should be moved to |
|
|
||||||
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
## Labels
|
|
||||||
|
|
||||||
### GET /board/{boardId}/labels/{labelId} - Get label details
|
|
||||||
|
|
||||||
#### Request parameters
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | ---------------------------------------- |
|
|
||||||
| boardId | Integer | The id of the board the label belongs to |
|
|
||||||
| labelId | Integer | The id of the label |
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"title": "Abgeschlossen",
|
|
||||||
"color": "31CC7C",
|
|
||||||
"boardId": "2",
|
|
||||||
"cardId": null,
|
|
||||||
"id": 5
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### POST /board/{boardId}/labels - Create a new label
|
|
||||||
|
|
||||||
#### Request parameters
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | ---------------------------------------- |
|
|
||||||
| boardId | Integer | The id of the board the label belongs to |
|
|
||||||
|
|
||||||
#### Request data
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"title": "Finished",
|
|
||||||
"color": "31CC7C"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
### PUT /board/{boardId}/labels/{labelId} - Update label details
|
|
||||||
|
|
||||||
#### Request parameters
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | ---------------------------------------- |
|
|
||||||
| boardId | Integer | The id of the board the label belongs to |
|
|
||||||
| labelId | Integer | The id of the label |
|
|
||||||
|
|
||||||
|
|
||||||
#### Request data
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"title": "Finished",
|
|
||||||
"color": "31CC7C"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
### DELETE /board/{boardId}/labels/{labelId} - Delete a label
|
|
||||||
|
|
||||||
#### Request parameters
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | ---------------------------------------- |
|
|
||||||
| boardId | Integer | The id of the board the label belongs to |
|
|
||||||
| labelId | Integer | The id of the label |
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
## Attachments
|
|
||||||
|
|
||||||
### GET /board/{boardId}/stacks/{stackId}/cards/{cardId}/attachments - Get a list of attachments
|
|
||||||
|
|
||||||
#### Request parameters
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | --------------------------------------- |
|
|
||||||
| boardId | Integer | The id of the board the card belongs to |
|
|
||||||
| stackId | Integer | The id of the stack the card belongs to |
|
|
||||||
| cardId | Integer | The id of the card |
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"cardId": 5,
|
|
||||||
"type": "deck_file",
|
|
||||||
"data": "6DADC2C69F4.eml",
|
|
||||||
"lastModified": 1541529048,
|
|
||||||
"createdAt": 1541529048,
|
|
||||||
"createdBy": "admin",
|
|
||||||
"deletedAt": 0,
|
|
||||||
"extendedData": {
|
|
||||||
"filesize": 922258,
|
|
||||||
"mimetype": "application/octet-stream",
|
|
||||||
"info": {
|
|
||||||
"dirname": ".",
|
|
||||||
"basename": "6DADC2C69F4.eml",
|
|
||||||
"extension": "eml",
|
|
||||||
"filename": "6DADC2C69F4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"id": 6
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
### GET /board/{boardId}/stacks/{stackId}/cards/{cardId}/attachments/{attachmentId} - Get the attachment file
|
|
||||||
|
|
||||||
#### Request parameters
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| ------------ | ------- | --------------------------------------------- |
|
|
||||||
| boardId | Integer | The id of the board the attachment belongs to |
|
|
||||||
| stackId | Integer | The id of the stack the attachment belongs to |
|
|
||||||
| cardId | Integer | The id of the card the attachment belongs to |
|
|
||||||
| attachmentId | Integer | The id of the attachment |
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
### POST /board/{boardId}/stacks/{stackId}/cards/{cardId}/attachments - Upload an attachment
|
|
||||||
|
|
||||||
#### Request parameters
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | --------------------------------------------- |
|
|
||||||
| boardId | Integer | The id of the board the attachment belongs to |
|
|
||||||
| stackId | Integer | The id of the stack the attachment belongs to |
|
|
||||||
| cardId | Integer | The id of the card the attachment belongs to |
|
|
||||||
|
|
||||||
#### Request data
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | --------------------------------------------- |
|
|
||||||
| type | String | The type of the attachement |
|
|
||||||
| file | Binary | File data to add as an attachment |
|
|
||||||
|
|
||||||
For now only `deck_file` is supported as an attachment type.
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
### PUT /board/{boardId}/stacks/{stackId}/cards/{cardId}/attachments/{attachmentId} - Update an attachment
|
|
||||||
|
|
||||||
#### Request parameters
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| ------------ | ------- | --------------------------------------------- |
|
|
||||||
| boardId | Integer | The id of the board the attachment belongs to |
|
|
||||||
| stackId | Integer | The id of the stack the attachment belongs to |
|
|
||||||
| cardId | Integer | The id of the card the attachment belongs to |
|
|
||||||
| attachmentId | Integer | The id of the attachment |
|
|
||||||
|
|
||||||
#### Request data
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| --------- | ------- | --------------------------------------------- |
|
|
||||||
| type | String | The type of the attachement |
|
|
||||||
| file | Binary | File data to add as an attachment |
|
|
||||||
|
|
||||||
For now only `deck_file` is supported as an attachment type.
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
### DELETE /board/{boardId}/stacks/{stackId}/cards/{cardId}/attachments/{attachmentId} - Delete an attachment
|
|
||||||
|
|
||||||
#### Request parameters
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| ------------ | ------- | --------------------------------------------- |
|
|
||||||
| boardId | Integer | The id of the board the attachment belongs to |
|
|
||||||
| stackId | Integer | The id of the stack the attachment belongs to |
|
|
||||||
| cardId | Integer | The id of the card the attachment belongs to |
|
|
||||||
| attachmentId | Integer | The id of the attachment |
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
### PUT /board/{boardId}/stacks/{stackId}/cards/{cardId}/attachments/{attachmentId}/restore - Resore a deleted attachment
|
|
||||||
|
|
||||||
#### Request parameters
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| ------------ | ------- | --------------------------------------------- |
|
|
||||||
| boardId | Integer | The id of the board the attachment belongs to |
|
|
||||||
| stackId | Integer | The id of the stack the attachment belongs to |
|
|
||||||
| cardId | Integer | The id of the card the attachment belongs to |
|
|
||||||
| attachmentId | Integer | The id of the attachment |
|
|
||||||
|
|
||||||
#### Response
|
|
||||||
|
|
||||||
##### 200 Success
|
|
||||||
|
|
||||||
@@ -1,27 +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/)
|
|
||||||
|
|
||||||
## 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.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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
|
|
||||||
```
|
|
||||||
@@ -1,69 +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.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
### 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 :**
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
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 :**
|
|
||||||
|
|
||||||

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

|
|
||||||
|
|
||||||
And even :
|
|
||||||
|
|
||||||
- Description in markdown language
|
|
||||||
- Attachment - *you can leave a document, a picture or some other bonus like that.*
|
|
||||||
|
|
||||||

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

|
|
||||||
|
|
||||||
### 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!
|
|
||||||
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
.subnav ul {
|
|
||||||
padding-left: 20px;
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
../README.md
|
|
||||||
|
Before Width: | Height: | Size: 107 KiB |
|
Before Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 218 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 158 KiB |
|
Before Width: | Height: | Size: 245 KiB |
|
Before Width: | Height: | Size: 556 KiB |
@@ -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:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
@@ -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 |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 58 58" width="512" height="512"><g fill="#000"><path d="M54.319 37.839C54.762 35.918 55 33.96 55 32c0-9.095-4.631-17.377-12.389-22.153a1 1 0 1 0-1.049 1.703C48.724 15.96 53 23.604 53 32c0 1.726-.2 3.451-.573 5.147A6.992 6.992 0 0 0 51 37c-3.86 0-7 3.141-7 7s3.14 7 7 7 7-3.141 7-7a7.006 7.006 0 0 0-3.681-6.161zM38.171 54.182A23.867 23.867 0 0 1 29 56a24.047 24.047 0 0 1-17.017-7.092A6.974 6.974 0 0 0 14 44c0-3.859-3.14-7-7-7s-7 3.141-7 7 3.14 7 7 7a6.952 6.952 0 0 0 3.381-.875C15.26 55.136 21.994 58 29 58c3.435 0 6.778-.663 9.936-1.971.51-.211.753-.796.542-1.307a1.001 1.001 0 0 0-1.307-.54zM4 31.213a1 1 0 0 0 1.068-.927c.712-10.089 7.586-18.52 17.22-21.314C23.142 11.874 25.825 14 29 14c3.86 0 7-3.141 7-7s-3.14-7-7-7c-3.851 0-6.985 3.127-6.999 6.975C11.42 9.922 3.851 19.12 3.073 30.146A.999.999 0 0 0 4 31.213z"/></g></svg>
|
|
||||||
|
Before Width: | Height: | Size: 885 B |
@@ -1 +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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -1,33 +1,15 @@
|
|||||||
---
|
### Steps to reproduce
|
||||||
name: Bug report
|
1.
|
||||||
about: Create a report to help us improve
|
2.
|
||||||
|
3.
|
||||||
|
|
||||||
---
|
### Expected behaviour
|
||||||
|
Tell us what should happen
|
||||||
|
|
||||||
**Describe the bug**
|
### Actual behaviour
|
||||||
A clear and concise description of what the bug is.
|
Tell us what happens instead
|
||||||
|
|
||||||
**To Reproduce**
|
### Server configuration
|
||||||
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
|
You can use the Issue Template application to prefill most of the required information: https://apps.nextcloud.com/apps/issuetemplate
|
||||||
-->
|
-->
|
||||||
@@ -75,10 +57,12 @@ Make sure to remove all sensitive content such as passwords. (e.g. database pass
|
|||||||
|
|
||||||
**Are you using an external user-backend, if yes which one:** LDAP/ActiveDirectory/Webdav/...
|
**Are you using an external user-backend, if yes which one:** LDAP/ActiveDirectory/Webdav/...
|
||||||
|
|
||||||
</details>
|
### Client configuration
|
||||||
|
**Browser:**
|
||||||
|
|
||||||
<details>
|
**Operating system:**
|
||||||
<summary>Logs</summary>
|
|
||||||
|
### Logs
|
||||||
|
|
||||||
#### Nextcloud log (data/nextcloud.log)
|
#### Nextcloud log (data/nextcloud.log)
|
||||||
```
|
```
|
||||||
@@ -93,5 +77,3 @@ a) The javascript console log
|
|||||||
b) The network log
|
b) The network log
|
||||||
c) ...
|
c) ...
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
presets: [
|
|
||||||
[
|
|
||||||
'@babel/preset-env',
|
|
||||||
{
|
|
||||||
targets: {
|
|
||||||
browsers: ['last 2 versions', 'ie >= 11']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
3
js/.bowerrc
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"directory": "vendor"
|
||||||
|
}
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
{
|
{
|
||||||
"esversion": 6,
|
|
||||||
|
|
||||||
"globals": {
|
"globals": {
|
||||||
"jasmine" : false,
|
"jasmine" : false,
|
||||||
"spyOn" : false,
|
"spyOn" : false,
|
||||||
@@ -23,6 +21,7 @@
|
|||||||
"devel" : true,
|
"devel" : true,
|
||||||
"eqeqeq" : true,
|
"eqeqeq" : true,
|
||||||
"eqnull" : false,
|
"eqnull" : false,
|
||||||
|
"es5" : true,
|
||||||
"evil" : false,
|
"evil" : false,
|
||||||
"forin" : true,
|
"forin" : true,
|
||||||
"immed" : true,
|
"immed" : true,
|
||||||
@@ -40,6 +39,7 @@
|
|||||||
"plusplus" : false,
|
"plusplus" : false,
|
||||||
"quotmark" : "single",
|
"quotmark" : "single",
|
||||||
"regexp" : false,
|
"regexp" : false,
|
||||||
|
"strict" : true,
|
||||||
"sub" : true,
|
"sub" : true,
|
||||||
"trailing" : true,
|
"trailing" : true,
|
||||||
"undef" : true,
|
"undef" : true,
|
||||||
|
|||||||
123
js/Gruntfile.js
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* @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/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
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']);
|
||||||
|
|
||||||
|
};
|
||||||
@@ -20,9 +20,6 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* global angular */
|
|
||||||
|
|
||||||
|
|
||||||
angular.module('markdown', [])
|
angular.module('markdown', [])
|
||||||
.provider('markdown', [function () {
|
.provider('markdown', [function () {
|
||||||
var opts = {};
|
var opts = {};
|
||||||
@@ -41,24 +38,13 @@ angular.module('markdown', [])
|
|||||||
};
|
};
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
import uirouter from '@uirouter/angularjs';
|
|
||||||
import ngsanitize from 'angular-sanitize';
|
|
||||||
import angularuiselect from 'ui-select';
|
|
||||||
import ngsortable from 'ng-sortable';
|
|
||||||
import md from 'angular-markdown-it';
|
|
||||||
import nganimate from 'angular-animate';
|
|
||||||
import 'angular-file-upload';
|
|
||||||
import ngInfiniteScroll from 'ng-infinite-scroll';
|
|
||||||
import '../legacy/jquery.atwho.min';
|
|
||||||
import '../legacy/jquery.caret.min';
|
|
||||||
|
|
||||||
var app = angular.module('Deck', [
|
var app = angular.module('Deck', [
|
||||||
ngsanitize,
|
'ngRoute',
|
||||||
uirouter,
|
'ngSanitize',
|
||||||
angularuiselect,
|
'ui.router',
|
||||||
ngsortable, md, nganimate,
|
'ui.select',
|
||||||
'angularFileUpload',
|
'as.sortable',
|
||||||
ngInfiniteScroll
|
'mdMarkdownIt',
|
||||||
|
'ngAnimate'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export default app;
|
|
||||||
|
|||||||
@@ -20,29 +20,18 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* global app oc_requesttoken markdownitLinkTarget */
|
app.config(function ($provide, $routeProvider, $interpolateProvider, $httpProvider, $urlRouterProvider, $stateProvider, $compileProvider, markdownItConverterProvider) {
|
||||||
|
|
||||||
import app from './App.js';
|
|
||||||
import md from 'angular-markdown-it';
|
|
||||||
import markdownitLinkTarget from 'markdown-it-link-target';
|
|
||||||
import markdownitCheckbox from 'legacy/markdown-it-checkbox.js';
|
|
||||||
|
|
||||||
app.config(function ($provide, $interpolateProvider, $httpProvider, $urlRouterProvider, $stateProvider, $compileProvider, markdownItConverterProvider) {
|
|
||||||
'use strict';
|
'use strict';
|
||||||
$httpProvider.defaults.headers.common.requesttoken = oc_requesttoken;
|
$httpProvider.defaults.headers.common.requesttoken = oc_requesttoken;
|
||||||
|
|
||||||
|
|
||||||
$compileProvider.debugInfoEnabled(true);
|
$compileProvider.debugInfoEnabled(true);
|
||||||
// This should fix adding "unsafe:" prefix to ui-select href links containing javascript
|
|
||||||
// inline JS is blocked by CSP anyway and filtered out by our markdown renderer as well
|
|
||||||
$compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|javascript):/);
|
|
||||||
|
|
||||||
markdownItConverterProvider.config({
|
markdownItConverterProvider.config({
|
||||||
breaks: true,
|
breaks: true,
|
||||||
linkify: true,
|
linkify: true,
|
||||||
xhtmlOut: true
|
xhtmlOut: true
|
||||||
});
|
});
|
||||||
markdownItConverterProvider.use(markdownitLinkTarget).use(markdownitCheckbox);
|
markdownItConverterProvider.use(markdownitLinkTarget);
|
||||||
|
|
||||||
$urlRouterProvider.otherwise('/');
|
$urlRouterProvider.otherwise('/');
|
||||||
|
|
||||||
@@ -67,51 +56,21 @@ app.config(function ($provide, $interpolateProvider, $httpProvider, $urlRouterPr
|
|||||||
.state('board.detail', {
|
.state('board.detail', {
|
||||||
url: '/detail/',
|
url: '/detail/',
|
||||||
reloadOnSearch : false,
|
reloadOnSearch : false,
|
||||||
params: {
|
|
||||||
tab: {value: 0, dynamic: true},
|
|
||||||
},
|
|
||||||
views: {
|
views: {
|
||||||
'sidebarView@': {
|
'sidebarView': {
|
||||||
templateUrl: '/board.sidebarView.html',
|
templateUrl: '/board.sidebarView.html'
|
||||||
controller: 'BoardController'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.state('board.card', {
|
.state('board.card', {
|
||||||
url: '/card/:cardId',
|
url: '/card/:cardId',
|
||||||
params: {
|
|
||||||
tab: {value: 0, dynamic: true},
|
|
||||||
},
|
|
||||||
views: {
|
views: {
|
||||||
'sidebarView@': {
|
'sidebarView': {
|
||||||
templateUrl: '/card.sidebarView.html',
|
templateUrl: '/card.sidebarView.html',
|
||||||
controller: 'CardController'
|
controller: 'CardController'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$provide.decorator('nvFileOverDirective', function ($delegate) {
|
|
||||||
var directive = $delegate[0],
|
|
||||||
link = directive.link;
|
|
||||||
|
|
||||||
directive.compile = function () {
|
|
||||||
return function (scope, element, attrs) {
|
|
||||||
var overClass = attrs.overClass || 'nv-file-over';
|
|
||||||
link.apply(this, arguments);
|
|
||||||
let counter = 0;
|
|
||||||
element.on('dragenter', function (event) {
|
|
||||||
counter++;
|
|
||||||
});
|
|
||||||
element.on('dragleave', function (event) {
|
|
||||||
counter--;
|
|
||||||
if (counter <= 0) {
|
|
||||||
$('.' + overClass).removeClass(overClass);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
return $delegate;
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -19,12 +19,9 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import app from './App.js';
|
|
||||||
|
|
||||||
/* global Snap */
|
|
||||||
app.run(function ($document, $rootScope, $transitions, BoardService) {
|
app.run(function ($document, $rootScope, $transitions, BoardService) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
$document.click(function (event) {
|
$document.click(function (event) {
|
||||||
$rootScope.$broadcast('documentClicked', event);
|
$rootScope.$broadcast('documentClicked', event);
|
||||||
});
|
});
|
||||||
@@ -56,9 +53,24 @@ app.run(function ($document, $rootScope, $transitions, BoardService) {
|
|||||||
OC.filePath('deck', 'img', 'app-512.png')
|
OC.filePath('deck', 'img', 'app-512.png')
|
||||||
);
|
);
|
||||||
|
|
||||||
// Select all elements with data-toggle="tooltips" in the document
|
$('#app-navigation-toggle').off('click');
|
||||||
$('body').tooltip({
|
// App sidebar on mobile
|
||||||
selector: '[data-toggle="tooltip"]'
|
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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
29
js/bower.json
Normal 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"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,349 +0,0 @@
|
|||||||
/*
|
|
||||||
* @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/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* global OC OCA OCP t escapeHTML Handlebars moment */
|
|
||||||
|
|
||||||
import CommentCollection from '../legacy/commentcollection';
|
|
||||||
import CommentModel from '../legacy/commentmodel';
|
|
||||||
|
|
||||||
class ActivityController {
|
|
||||||
constructor ($scope, CardService, ActivityService, BoardService) {
|
|
||||||
'ngInject';
|
|
||||||
this.cardservice = CardService;
|
|
||||||
this.boardservice = BoardService;
|
|
||||||
this.activityservice = ActivityService;
|
|
||||||
this.$scope = $scope;
|
|
||||||
this.type = '';
|
|
||||||
this.loading = false;
|
|
||||||
this.status = {
|
|
||||||
commentCreateLoading: false
|
|
||||||
};
|
|
||||||
this.$scope.newComment = '';
|
|
||||||
this.$scope.newCommentString = 'New comment…';
|
|
||||||
|
|
||||||
this.currentUser = OC.getCurrentUser();
|
|
||||||
|
|
||||||
const self = this;
|
|
||||||
this.$scope.$watch(function () {
|
|
||||||
return self.element.id;
|
|
||||||
}, function (params) {
|
|
||||||
if (self.getData(self.element.id).length === 0) {
|
|
||||||
if (self.type === 'deck_card') {
|
|
||||||
self.activityservice.loadComments(self.element.id);
|
|
||||||
}
|
|
||||||
self.loading = true;
|
|
||||||
self.fetchUntilResults();
|
|
||||||
}
|
|
||||||
self.activityservice.fetchNewerActivities(self.type, self.element.id).then(function () {});
|
|
||||||
if (self.type === 'deck_card') {
|
|
||||||
self.cardservice.getCurrent().commentsUnread = 0;
|
|
||||||
}
|
|
||||||
}, true);
|
|
||||||
|
|
||||||
let $target = $('.newCommentForm .message');
|
|
||||||
this.applyAtWho($target);
|
|
||||||
|
|
||||||
this.activityservice.subscribe(this.$scope, function() {
|
|
||||||
self.$scope.$apply();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (typeof OCA.Activity.Templates !== 'undefined') {
|
|
||||||
OCA.Activity.Templates.userLocal = Handlebars.template({"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) {
|
|
||||||
var helper;
|
|
||||||
// Compiled handlesbars template
|
|
||||||
// '<span class="avatar-name-wrapper"><avatar ng-attr-contactsmenu ng-attr-tooltip ng-attr-user="{{ id }}" ng-attr-displayname="{{name}}" ng-attr-size="16"></avatar> {{ name }}</span>';
|
|
||||||
return "<span class=\"avatar-name-wrapper\"><avatar ng-attr-contactsmenu ng-attr-tooltip ng-attr-user=\""
|
|
||||||
+ container.escapeExpression(((helper = (helper = helpers.id || (depth0 != null ? depth0.id : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : {},{"name":"id","hash":{},"data":data}) : helper)))
|
|
||||||
+ "\" ng-attr-displayname=\""
|
|
||||||
+ container.escapeExpression(((helper = (helper = helpers.name || (depth0 != null ? depth0.name : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : {},{"name":"name","hash":{},"data":data}) : helper)))
|
|
||||||
+ "\" ng-attr-size=\"16\"></avatar> "
|
|
||||||
+ container.escapeExpression(((helper = (helper = helpers.name || (depth0 != null ? depth0.name : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : {},{"name":"name","hash":{},"data":data}) : helper)))
|
|
||||||
+ "</span>";
|
|
||||||
},"useData":true});
|
|
||||||
} else {
|
|
||||||
OCA.Activity.RichObjectStringParser._userLocalTemplate = '<span class="avatar-name-wrapper"><avatar ng-attr-contactsmenu ng-attr-tooltip ng-attr-user="{{ id }}" ng-attr-displayname="{{name}}" ng-attr-size="16"></avatar> {{ name }}</span>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
applyAtWho($target) {
|
|
||||||
const self = this;
|
|
||||||
if (!$target) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$target.atwho({
|
|
||||||
at: '@',
|
|
||||||
callbacks: {
|
|
||||||
remoteFilter: function(query, callback) {
|
|
||||||
let uids = self.boardservice.getUsers();
|
|
||||||
uids = uids.filter((x) => x.uid.toLowerCase().includes(query.toLowerCase()) || x.displayname.toLowerCase().includes(query.toLowerCase()));
|
|
||||||
callback(uids);
|
|
||||||
},
|
|
||||||
highlighter: function (li) {
|
|
||||||
// misuse the highlighter callback to instead of
|
|
||||||
// highlighting loads the avatars.
|
|
||||||
var $li = $(li);
|
|
||||||
$li.find('.avatar').avatar(undefined, 32);
|
|
||||||
return $li;
|
|
||||||
},
|
|
||||||
sorter: function (q, items) { return items; }
|
|
||||||
},
|
|
||||||
displayTpl: function (item) {
|
|
||||||
return '<li>' +
|
|
||||||
'<span class="avatar-name-wrapper">' +
|
|
||||||
'<span class="avatar" ' +
|
|
||||||
'data-username="' + escapeHTML(item.uid) + '" ' + // for avatars
|
|
||||||
'data-user="' + escapeHTML(item.uid) + '" ' + // for contactsmenu
|
|
||||||
'data-user-display-name="' + escapeHTML(item.displayname) + '">' +
|
|
||||||
'</span>' +
|
|
||||||
'<strong>' + escapeHTML(item.displayname) + '</strong>' +
|
|
||||||
'</span></li>';
|
|
||||||
},
|
|
||||||
insertTpl: function (item) {
|
|
||||||
return '' +
|
|
||||||
'<span class="avatar-name-wrapper">' +
|
|
||||||
'<span class="avatar" ' +
|
|
||||||
'data-username="' + escapeHTML(item.uid) + '" ' + // for avatars
|
|
||||||
'data-user="' + escapeHTML(item.uid) + '" ' + // for contactsmenu
|
|
||||||
'data-user-display-name="' + escapeHTML(item.displayname) + '">' +
|
|
||||||
'</span>' +
|
|
||||||
'<strong>' + escapeHTML(item.displayname) + '</strong>' +
|
|
||||||
'</span>';
|
|
||||||
},
|
|
||||||
searchKey: 'displayname'
|
|
||||||
});
|
|
||||||
$target.on('inserted.atwho', function (je, $el) {
|
|
||||||
$(je.target).find(
|
|
||||||
'span[data-username="' + $el.find('[data-username]').data('username') + '"]'
|
|
||||||
).avatar(undefined, 16);
|
|
||||||
});
|
|
||||||
$target.on('shown.atwho', function (je) {
|
|
||||||
$target.find('.avatar').avatar(undefined, 16);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
commentBodyToPlain(content) {
|
|
||||||
let $comment = $('<div/>').html(content);
|
|
||||||
$comment.find('.avatar-name-wrapper').each(function () {
|
|
||||||
var $this = $(this);
|
|
||||||
var $inserted = $this.parent();
|
|
||||||
$inserted.html('@' + $this.find('.avatar').data('username'));
|
|
||||||
});
|
|
||||||
$comment.html(OCP.Comments.richToPlain($comment.html()));
|
|
||||||
$comment.html($comment.html().replace(/<br\s*[\/]?>/gi, '\n'));
|
|
||||||
return $comment.text();
|
|
||||||
}
|
|
||||||
|
|
||||||
static _composeHTMLMention(uid, displayName) {
|
|
||||||
var avatar = '' +
|
|
||||||
'<span class="avatar" data-username="' + escapeHTML(uid) + '" data-user="' + escapeHTML(uid) + '" ng-attr-size="16" ' +
|
|
||||||
'ng-attr-user="' + escapeHTML(uid) + '" ' +
|
|
||||||
'ng-attr-displayname="' + escapeHTML(displayName) + '" ng-attr-contactsmenu="true">' +
|
|
||||||
'</span>';
|
|
||||||
|
|
||||||
var isCurrentUser = (uid === OC.getCurrentUser().uid);
|
|
||||||
|
|
||||||
return '' +
|
|
||||||
'<span class="atwho-inserted" contenteditable="false">' +
|
|
||||||
'<span class="avatar-name-wrapper' + (isCurrentUser ? ' currentUser' : '') + '">' +
|
|
||||||
avatar +
|
|
||||||
'<strong>' + escapeHTML(displayName) + '</strong>' +
|
|
||||||
'</span>' +
|
|
||||||
'</span>';
|
|
||||||
}
|
|
||||||
|
|
||||||
formatMessage(activity) {
|
|
||||||
let message = activity.message;
|
|
||||||
let mentions = activity.commentModel.get('mentions');
|
|
||||||
const editMode = false;
|
|
||||||
message = escapeHTML(message).replace(/\n/g, '<br/>');
|
|
||||||
|
|
||||||
for(var i in mentions) {
|
|
||||||
if(!mentions.hasOwnProperty(i)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var mention = '@' + mentions[i].mentionId;
|
|
||||||
// escape possible regex characters in the name
|
|
||||||
mention = mention.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
||||||
|
|
||||||
const displayName = ActivityController._composeHTMLMention(mentions[i].mentionId, mentions[i].mentionDisplayName);
|
|
||||||
// replace every mention either at the start of the input or after a whitespace
|
|
||||||
// followed by a non-word character.
|
|
||||||
message = message.replace(new RegExp('(^|\\s)(' + mention + ')\\b', 'g'),
|
|
||||||
function(match, p1) {
|
|
||||||
// to get number of whitespaces (0 vs 1) right
|
|
||||||
return p1+displayName;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
if(editMode !== true) {
|
|
||||||
message = OCP.Comments.plainToRich(message);
|
|
||||||
}
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
postComment() {
|
|
||||||
const self = this;
|
|
||||||
this.status.commentCreateLoading = true;
|
|
||||||
|
|
||||||
let content = this.commentBodyToPlain(self.$scope.newComment);
|
|
||||||
if (content.length < 1) {
|
|
||||||
self.status.commentCreateLoading = false;
|
|
||||||
OC.Notification.showTemporary(t('deck', 'Please provide a content for your comment.'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var model = this.activityservice.commentCollection.create({
|
|
||||||
actorId: OC.getCurrentUser().uid,
|
|
||||||
actorDisplayName: OC.getCurrentUser().displayName,
|
|
||||||
actorType: 'users',
|
|
||||||
verb: 'comment',
|
|
||||||
message: content,
|
|
||||||
creationDateTime: (new Date()).toUTCString()
|
|
||||||
}, {
|
|
||||||
at: 0,
|
|
||||||
// wait for real creation before adding
|
|
||||||
wait: true,
|
|
||||||
success: function() {
|
|
||||||
self.$scope.newComment = '';
|
|
||||||
self.activityservice.fetchNewerActivities(self.type, self.element.id).then(function () {});
|
|
||||||
self.status.commentCreateLoading = false;
|
|
||||||
},
|
|
||||||
error: function() {
|
|
||||||
self.status.commentCreateLoading = false;
|
|
||||||
OC.Notification.showTemporary(t('deck', 'Posting the comment failed.'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateComment(item) {
|
|
||||||
item.commentEdit = this.formatMessage(item);
|
|
||||||
let $target = $('.newCommentForm .message');
|
|
||||||
this.applyAtWho($target);
|
|
||||||
/** Workaround to trigger avatar rendering after the view has been updated */
|
|
||||||
window.setTimeout(function () {
|
|
||||||
$target.find('.avatar').avatar(undefined, 16);
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
editComment(item) {
|
|
||||||
const self = this;
|
|
||||||
let content = this.commentBodyToPlain(item.commentEdit);
|
|
||||||
if (content.length < 1) {
|
|
||||||
OC.Notification.showTemporary(t('deck', 'Please provide a content for your comment.'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/** We need to save the model and afterwards run a fetch to update the mentions
|
|
||||||
* and call apply to propagate the changes to angular
|
|
||||||
*/
|
|
||||||
item.commentModel.on('sync', function() {
|
|
||||||
item.commentModel.off('sync');
|
|
||||||
item.commentModel.fetch({
|
|
||||||
success: function() {
|
|
||||||
self.$scope.$apply();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
item.commentModel.save({
|
|
||||||
message: content,
|
|
||||||
});
|
|
||||||
item.message = content;
|
|
||||||
item.commentEdit = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteComment(item) {
|
|
||||||
item.commentModel.destroy();
|
|
||||||
item.deleted = true;
|
|
||||||
item.commentModel = undefined;
|
|
||||||
item.message = t('deck', 'The comment has been deleted');
|
|
||||||
}
|
|
||||||
|
|
||||||
getData(id) {
|
|
||||||
return this.activityservice.getData(this.type, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
parseMessage(activity) {
|
|
||||||
let subject = activity.subject_rich[0];
|
|
||||||
let parameters = activity.subject_rich[1];
|
|
||||||
if (parameters.after && parameters.after.id && parameters.after.id.startsWith('dt:')) {
|
|
||||||
let dateTime = parameters.after.id.substr(3);
|
|
||||||
parameters.after.name = moment(dateTime).format('L LTS');
|
|
||||||
}
|
|
||||||
return OCA.Activity.RichObjectStringParser.parseMessage(subject, parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
fetchUntilResults () {
|
|
||||||
const self = this;
|
|
||||||
let dataLengthBefore = self.getData(self.element.id).length;
|
|
||||||
let _executeFetch = function() {
|
|
||||||
let promise = self.activityservice.fetchMoreActivities(self.type, self.element.id);
|
|
||||||
promise.then(function (data) {
|
|
||||||
let dataLengthAfter = self.getData(self.element.id).length;
|
|
||||||
if (data !== null && (dataLengthAfter <= dataLengthBefore || dataLengthAfter < self.activityservice.RESULT_PER_PAGE)) {
|
|
||||||
_executeFetch();
|
|
||||||
} else {
|
|
||||||
self.loading = false;
|
|
||||||
}
|
|
||||||
}, function () {
|
|
||||||
self.loading = false;
|
|
||||||
self.$scope.$apply();
|
|
||||||
});
|
|
||||||
|
|
||||||
};
|
|
||||||
_executeFetch();
|
|
||||||
}
|
|
||||||
|
|
||||||
getComments() {
|
|
||||||
return this.activityservice.comments;
|
|
||||||
}
|
|
||||||
|
|
||||||
getActivityStream() {
|
|
||||||
let activities = this.activityservice.getData(this.type, this.element.id);
|
|
||||||
return activities;
|
|
||||||
}
|
|
||||||
|
|
||||||
page() {
|
|
||||||
if (!this.activityservice.since[this.type][this.element.id].finished) {
|
|
||||||
this.loading = true;
|
|
||||||
this.fetchUntilResults();
|
|
||||||
} else {
|
|
||||||
this.loading = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadingNewer() {
|
|
||||||
return this.activityservice.runningNewer;
|
|
||||||
}
|
|
||||||
|
|
||||||
t(text) {
|
|
||||||
return t('deck', text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let activityComponent = {
|
|
||||||
templateUrl: OC.linkTo('deck', 'templates/part.card.activity.html'),
|
|
||||||
controller: ActivityController,
|
|
||||||
bindings: {
|
|
||||||
type: '@',
|
|
||||||
element: '='
|
|
||||||
}
|
|
||||||
};
|
|
||||||
export default activityComponent;
|
|
||||||
@@ -20,25 +20,10 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import app from '../app/App.js';
|
app.controller('AppController', function ($scope, $location, $http, $route, $log, $rootScope, $stateParams) {
|
||||||
|
|
||||||
/* globals oc_current_user: false */
|
|
||||||
app.controller('AppController', function ($scope, $location, $http, $log, $rootScope, $attrs) {
|
|
||||||
$rootScope.sidebar = {
|
$rootScope.sidebar = {
|
||||||
show: false
|
show: false
|
||||||
};
|
};
|
||||||
$scope.sidebar = $rootScope.sidebar;
|
$scope.sidebar = $rootScope.sidebar;
|
||||||
$scope.user = oc_current_user;
|
$scope.user = oc_current_user;
|
||||||
$rootScope.config = JSON.parse($attrs.config);
|
|
||||||
|
|
||||||
$rootScope.compactMode = localStorage.getItem('deck.compactMode') === 'true';
|
|
||||||
$scope.appNavigationHide = localStorage.getItem('deck.appNavigationHide') === 'true';
|
|
||||||
|
|
||||||
$scope.toggleSidebar = function() {
|
|
||||||
if ($(window).width() > 768) {
|
|
||||||
$log.debug($scope.appNavigationHide);
|
|
||||||
$scope.appNavigationHide = !$scope.appNavigationHide;
|
|
||||||
localStorage.setItem('deck.appNavigationHide', JSON.stringify($scope.appNavigationHide));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
/*
|
|
||||||
* @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/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* global OC */
|
|
||||||
|
|
||||||
class AttachmentListController {
|
|
||||||
constructor ($scope, CardService, FileService) {
|
|
||||||
'ngInject';
|
|
||||||
this.cardservice = CardService;
|
|
||||||
this.fileservice = FileService;
|
|
||||||
this.attachments = CardService.getCurrent().attachments;
|
|
||||||
}
|
|
||||||
|
|
||||||
mimetypeForAttachment(attachment) {
|
|
||||||
let url = OC.MimeType.getIconUrl(attachment.extendedData.mimetype);
|
|
||||||
let styles = {
|
|
||||||
'background-image': `url("${url}")`,
|
|
||||||
};
|
|
||||||
return styles;
|
|
||||||
}
|
|
||||||
|
|
||||||
attachmentUrl(attachment) {
|
|
||||||
let cardId = this.cardservice.getCurrent().id;
|
|
||||||
let attachmentId = attachment.id;
|
|
||||||
return OC.generateUrl(`/apps/deck/cards/${cardId}/attachment/${attachmentId}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
getAttachmentMarkdown(attachment) {
|
|
||||||
const inlineMimetypes = ['image/png', 'image/jpg', 'image/jpeg'];
|
|
||||||
let url = this.attachmentUrl(attachment);
|
|
||||||
let filename = attachment.data;
|
|
||||||
let insertText = `[📎 ${filename}](${url})`;
|
|
||||||
if (inlineMimetypes.indexOf(attachment.extendedData.mimetype) > -1) {
|
|
||||||
insertText = ``;
|
|
||||||
}
|
|
||||||
return insertText;
|
|
||||||
}
|
|
||||||
|
|
||||||
select(attachment) {
|
|
||||||
this.onSelect({attachment: this.getAttachmentMarkdown(attachment)});
|
|
||||||
}
|
|
||||||
|
|
||||||
abort() {
|
|
||||||
this.onAbort();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
let attachmentListComponent = {
|
|
||||||
templateUrl: '/card.attachments.html',
|
|
||||||
controller: AttachmentListController,
|
|
||||||
bindings: {
|
|
||||||
isFileSelector: '<',
|
|
||||||
attachments: '=',
|
|
||||||
onSelect: '&',
|
|
||||||
onAbort: '&'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
export default attachmentListComponent;
|
|
||||||
@@ -19,18 +19,8 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
/* global oc_defaults oc_config OC OCP OCA t n */
|
|
||||||
|
|
||||||
import app from '../app/App.js';
|
app.controller('BoardController', function ($rootScope, $scope, $stateParams, StatusService, BoardService, StackService, CardService, LabelService, $state, $transitions, $filter) {
|
||||||
import Vue from 'vue';
|
|
||||||
|
|
||||||
Vue.prototype.t = t;
|
|
||||||
Vue.prototype.n = n;
|
|
||||||
Vue.prototype.OC = OC;
|
|
||||||
|
|
||||||
import CollaborationView from '../views/CollaborationView';
|
|
||||||
|
|
||||||
app.controller('BoardController', function ($rootScope, $scope, $element, $stateParams, StatusService, BoardService, StackService, CardService, LabelService, $state, $transitions, $filter, FileService) {
|
|
||||||
|
|
||||||
$scope.sidebar = $rootScope.sidebar;
|
$scope.sidebar = $rootScope.sidebar;
|
||||||
|
|
||||||
@@ -39,160 +29,58 @@ app.controller('BoardController', function ($rootScope, $scope, $element, $state
|
|||||||
addCard: [],
|
addCard: [],
|
||||||
};
|
};
|
||||||
$scope.newLabel = {};
|
$scope.newLabel = {};
|
||||||
|
$scope.status.boardtab = $stateParams.detailTab;
|
||||||
|
|
||||||
$scope.OC = OC;
|
|
||||||
$scope.stackservice = StackService;
|
$scope.stackservice = StackService;
|
||||||
$scope.boardservice = BoardService;
|
$scope.boardservice = BoardService;
|
||||||
$scope.cardservice = CardService;
|
$scope.cardservice = CardService;
|
||||||
$scope.statusservice = StatusService.getInstance();
|
$scope.statusservice = StatusService.getInstance();
|
||||||
$scope.labelservice = LabelService;
|
$scope.labelservice = LabelService;
|
||||||
$scope.defaultColors = ['31CC7C', '317CCC', 'FF7A66', 'F1DB50', '7C31CC', 'CC317C', '3A3B3D', 'CACBCD'];
|
$scope.defaultColors = ['31CC7C', '317CCC', 'FF7A66', 'F1DB50', '7C31CC', 'CC317C', '3A3B3D', 'CACBCD'];
|
||||||
$scope.board = BoardService.getCurrent();
|
|
||||||
$scope.uploader = FileService.uploader;
|
|
||||||
$scope.searchText = '';
|
|
||||||
|
|
||||||
$scope.startTitleEdit = function(card) {
|
|
||||||
card.renameTitle = card.title;
|
|
||||||
card.status = card.status || {};
|
|
||||||
card.status.editCard = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.finishTitleEdit = function(card) {
|
|
||||||
var newTitle;
|
|
||||||
if (!card.renameTitle || !card.renameTitle.trim()) {
|
|
||||||
newTitle = '';
|
|
||||||
} else {
|
|
||||||
newTitle = card.renameTitle.trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newTitle === card.title) {
|
|
||||||
// title unchanged
|
|
||||||
card.status.editCard = false;
|
|
||||||
delete card.renameTitle;
|
|
||||||
} else if (newTitle !== '') {
|
|
||||||
// title changed
|
|
||||||
card.title = newTitle;
|
|
||||||
CardService.update(card).then(function (data) {
|
|
||||||
card.status.editCard = false;
|
|
||||||
delete card.renameTitle;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// empty title
|
|
||||||
card.status.editCard = false;
|
|
||||||
delete card.renameTitle;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.$watch(function() {
|
|
||||||
return $scope.params.tab;
|
|
||||||
}, function (newTab, oldTab) {
|
|
||||||
if (newTab === 2 && oldTab !== 2) {
|
|
||||||
CardService.fetchDeleted($scope.id);
|
|
||||||
StackService.fetchDeleted($scope.id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// workaround for $stateParams changes not being propagated
|
|
||||||
$scope.$watch(function() {
|
|
||||||
return $state.params;
|
|
||||||
}, function (params) {
|
|
||||||
$scope.params = params;
|
|
||||||
}, true);
|
|
||||||
$scope.params = $state.params;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check for markdown checkboxes in description to render the counter
|
|
||||||
*
|
|
||||||
* This should probably be moved to the backend at some point
|
|
||||||
*
|
|
||||||
* @param text
|
|
||||||
* @returns array of [finished, total] checkboxes
|
|
||||||
*/
|
|
||||||
$scope.getCheckboxes = function(text) {
|
|
||||||
const regTotal = /\[(X|\s|\_|\-)\]/igm;
|
|
||||||
const regFinished = /\[(X|\_|\-)\]/igm;
|
|
||||||
return [
|
|
||||||
((text || '').match(regFinished) || []).length,
|
|
||||||
((text || '').match(regTotal) || []).length
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.search = function (searchText) {
|
$scope.search = function (searchText) {
|
||||||
$scope.searchText = searchText;
|
$scope.searchText = searchText;
|
||||||
$scope.refreshData();
|
$scope.refreshData();
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.$watch(function () {
|
$scope.board = BoardService.getCurrent();
|
||||||
if (typeof BoardService.getCurrent() !== 'undefined') {
|
StackService.clear(); //FIXME: Is this still needed?
|
||||||
return BoardService.getCurrent().title;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}, function () {
|
|
||||||
$scope.setPageTitle();
|
|
||||||
});
|
|
||||||
$scope.setPageTitle = function() {
|
$scope.setPageTitle = function() {
|
||||||
if(BoardService.getCurrent()) {
|
if(BoardService.getCurrent()) {
|
||||||
document.title = BoardService.getCurrent().title + ' | Deck - ' + oc_defaults.name;
|
document.title = BoardService.getCurrent().title + " | Deck - " + oc_defaults.name;
|
||||||
} else {
|
} else {
|
||||||
document.title = 'Deck - ' + oc_defaults.name;
|
document.title = "Deck - " + oc_defaults.name;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.statusservice.retainWaiting();
|
$scope.statusservice.retainWaiting();
|
||||||
$scope.statusservice.retainWaiting();
|
$scope.statusservice.retainWaiting();
|
||||||
|
|
||||||
// handle filter parameter for switching between archived/unarchived cards
|
// FIXME: ugly solution for archive
|
||||||
|
$scope.$state = $stateParams;
|
||||||
|
$scope.filter = $stateParams.filter;
|
||||||
|
$scope.$watch('$state.filter', function (name) {
|
||||||
|
$scope.filter = name;
|
||||||
|
});
|
||||||
$scope.switchFilter = function (filter) {
|
$scope.switchFilter = function (filter) {
|
||||||
$state.go('.', {filter: filter});
|
$state.go('.', {filter: filter}, {notify: false});
|
||||||
|
$scope.filter = filter;
|
||||||
};
|
};
|
||||||
$scope.$watch(function() {
|
$scope.$watch('filter', function (name) {
|
||||||
return $scope.params.filter;
|
if (name === "archive") {
|
||||||
}, function (filter) {
|
|
||||||
if (filter === 'archive') {
|
|
||||||
$scope.loadArchived();
|
$scope.loadArchived();
|
||||||
} else {
|
} else {
|
||||||
$scope.loadDefault();
|
$scope.loadDefault();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (parseInt(oc_config.version.split('.')[0]) >= 16) {
|
|
||||||
const ComponentVM = new Vue({
|
|
||||||
render: h => h(CollaborationView),
|
|
||||||
data: {
|
|
||||||
model: BoardService.getCurrent()
|
|
||||||
},
|
|
||||||
});
|
|
||||||
$scope.mountCollections = function () {
|
|
||||||
const MountingPoint = document.getElementById('collaborationResources');
|
|
||||||
if (MountingPoint) {
|
|
||||||
ComponentVM.model = BoardService.getCurrent();
|
|
||||||
ComponentVM.$mount(MountingPoint);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
$scope.$$postDigest($scope.mountCollections);
|
|
||||||
$scope.$watch(function () {
|
|
||||||
return BoardService.getCurrent();
|
|
||||||
}, function () {
|
|
||||||
ComponentVM.model = BoardService.getCurrent();
|
|
||||||
if ($scope.sidebar.show) {
|
|
||||||
$scope.$$postDigest($scope.mountCollections);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.toggleCompactMode = function() {
|
|
||||||
$rootScope.compactMode = !$rootScope.compactMode;
|
|
||||||
localStorage.setItem('deck.compactMode', JSON.stringify($rootScope.compactMode));
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.stacksData = StackService;
|
$scope.stacksData = StackService;
|
||||||
$scope.stacks = [];
|
$scope.stacks = [];
|
||||||
$scope.$watch('stacksData', function () {
|
$scope.$watch('stacksData', function (value) {
|
||||||
$scope.refreshData();
|
$scope.refreshData();
|
||||||
}, true);
|
}, true);
|
||||||
$scope.refreshData = function () {
|
$scope.refreshData = function () {
|
||||||
if ($scope.params.filter === 'archive') {
|
if ($scope.filter === "archive") {
|
||||||
$scope.filterData('-lastModified', $scope.searchText);
|
$scope.filterData('-lastModified', $scope.searchText);
|
||||||
} else {
|
} else {
|
||||||
$scope.filterData('order', $scope.searchText);
|
$scope.filterData('order', $scope.searchText);
|
||||||
@@ -204,9 +92,8 @@ app.controller('BoardController', function ($rootScope, $scope, $element, $state
|
|||||||
|
|
||||||
// filter cards here, as ng-sortable will not work nicely with html-inline filters
|
// filter cards here, as ng-sortable will not work nicely with html-inline filters
|
||||||
$scope.filterData = function (order, text) {
|
$scope.filterData = function (order, text) {
|
||||||
if ($scope.stacks === undefined) {
|
if ($scope.stacks === undefined)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
angular.copy(StackService.getData(), $scope.stacks);
|
angular.copy(StackService.getData(), $scope.stacks);
|
||||||
$scope.stacks = $filter('orderBy')($scope.stacks, 'order');
|
$scope.stacks = $filter('orderBy')($scope.stacks, 'order');
|
||||||
angular.forEach($scope.stacks, function (value, key) {
|
angular.forEach($scope.stacks, function (value, key) {
|
||||||
@@ -250,12 +137,11 @@ app.controller('BoardController', function ($rootScope, $scope, $element, $state
|
|||||||
// Create a new Stack
|
// Create a new Stack
|
||||||
$scope.createStack = function () {
|
$scope.createStack = function () {
|
||||||
StackService.create($scope.newStack).then(function (data) {
|
StackService.create($scope.newStack).then(function (data) {
|
||||||
$scope.newStack.title = '';
|
$scope.newStack.title = "";
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.createCard = function (stack, title) {
|
$scope.createCard = function (stack, title) {
|
||||||
if (this['addCardForm' + stack].$valid) {
|
|
||||||
var newCard = {
|
var newCard = {
|
||||||
'title': title,
|
'title': title,
|
||||||
'stackId': stack,
|
'stackId': stack,
|
||||||
@@ -263,89 +149,18 @@ app.controller('BoardController', function ($rootScope, $scope, $element, $state
|
|||||||
};
|
};
|
||||||
CardService.create(newCard).then(function (data) {
|
CardService.create(newCard).then(function (data) {
|
||||||
$scope.stackservice.addCard(data);
|
$scope.stackservice.addCard(data);
|
||||||
$scope.newCard.title = '';
|
$scope.newCard.title = "";
|
||||||
});
|
});
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.stackDelete = function (stack) {
|
|
||||||
$scope.stackservice.delete(stack.id);
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.stackUndoDelete = function (deletedStack) {
|
|
||||||
return StackService.undoDelete(deletedStack);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.cardDelete = function (card) {
|
$scope.cardDelete = function (card) {
|
||||||
CardService.delete(card.id).then(function () {
|
CardService.delete(card.id);
|
||||||
StackService.removeCard(card);
|
StackService.removeCard(card);
|
||||||
$scope.sidebar.show = false;
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.cardOrCardAndStackUndoDelete = function (deletedCard) {
|
|
||||||
var associatedDeletedStack = $scope.stackservice.deleted[deletedCard.stackId];
|
|
||||||
if(associatedDeletedStack !== undefined) {
|
|
||||||
$scope.cardAndStackUndoDeleteAskForConfirmation(deletedCard, associatedDeletedStack);
|
|
||||||
} else {
|
|
||||||
$scope.cardUndoDelete(deletedCard);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.cardAndStackUndoDeleteAskForConfirmation = function(deletedCard, associatedDeletedStack) {
|
|
||||||
OC.dialogs.confirm(
|
|
||||||
t('deck', 'The associated stack is deleted as well, it will be restored as well.'),
|
|
||||||
t('deck', 'Restore associated stack'),
|
|
||||||
function(state) {
|
|
||||||
if (state) {
|
|
||||||
$scope.cardAndStackUndoDelete(deletedCard, associatedDeletedStack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.cardAndStackUndoDelete = function(deletedCard, associatedDeletedStack) {
|
|
||||||
$scope.stackUndoDelete(associatedDeletedStack).then(function() {
|
|
||||||
$scope.cardUndoDelete(deletedCard);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.cardUndoDelete = function(deletedCard) {
|
|
||||||
CardService.undoDelete(deletedCard).then(function() {
|
|
||||||
StackService.addCard(deletedCard);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.cardArchive = function (card) {
|
$scope.cardArchive = function (card) {
|
||||||
CardService.archive(card);
|
CardService.archive(card);
|
||||||
StackService.removeCard(card);
|
StackService.removeCard(card);
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.isCurrentUserAssigned = function (card) {
|
|
||||||
if (! CardService.get(card.id).assignedUsers) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
var userList = CardService.get(card.id).assignedUsers.filter(function (obj) {
|
|
||||||
return obj.participant.uid === OC.getCurrentUser().uid;
|
|
||||||
});
|
|
||||||
return userList.length === 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.cardAssignToMe = function (card) {
|
|
||||||
CardService.assignUser(card, OC.getCurrentUser().uid)
|
|
||||||
.then(
|
|
||||||
function() {StackService.updateCard(card);}
|
|
||||||
);
|
|
||||||
// TODO: remove this jquery call. Fix and use appPopoverMenuUtils instead
|
|
||||||
$('.popovermenu').addClass('hidden');
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.cardUnassignFromMe = function (card) {
|
|
||||||
CardService.unassignUser(card, OC.getCurrentUser().uid);
|
|
||||||
StackService.updateCard(card);
|
|
||||||
// TODO: remove this jquery call.Fix and use appPopoverMenuUtils instead
|
|
||||||
$('.popovermenu').addClass('hidden');
|
|
||||||
};
|
|
||||||
$scope.cardUnarchive = function (card) {
|
$scope.cardUnarchive = function (card) {
|
||||||
CardService.unarchive(card);
|
CardService.unarchive(card);
|
||||||
StackService.removeCard(card);
|
StackService.removeCard(card);
|
||||||
@@ -356,58 +171,18 @@ app.controller('BoardController', function ($rootScope, $scope, $element, $state
|
|||||||
// remove from board data
|
// remove from board data
|
||||||
var i = BoardService.getCurrent().labels.indexOf(label);
|
var i = BoardService.getCurrent().labels.indexOf(label);
|
||||||
BoardService.getCurrent().labels.splice(i, 1);
|
BoardService.getCurrent().labels.splice(i, 1);
|
||||||
|
// TODO: remove from cards
|
||||||
// remove from cards
|
|
||||||
var cards = CardService.data;
|
|
||||||
for (var card in cards) {
|
|
||||||
if (Object.prototype.hasOwnProperty.call(cards, card)) {
|
|
||||||
var labelsFromCard = cards[card].labels;
|
|
||||||
|
|
||||||
labelsFromCard.forEach(function (labelFromCard, index) {
|
|
||||||
if (labelFromCard.id === label.id) {
|
|
||||||
cards[card].labels.splice(index, 1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.labelCreate = function (label) {
|
$scope.labelCreate = function (label) {
|
||||||
label.boardId = $scope.id;
|
label.boardId = $scope.id;
|
||||||
LabelService.create(label).then(function (data) {
|
LabelService.create(label);
|
||||||
$scope.newStack.title = '';
|
BoardService.getCurrent().labels.push(label);
|
||||||
BoardService.getCurrent().labels.push(data);
|
|
||||||
$scope.status.createLabel = false;
|
$scope.status.createLabel = false;
|
||||||
$scope.newLabel = {};
|
$scope.newLabel = {};
|
||||||
}).catch((err) => {
|
|
||||||
OC.Notification.showTemporary(err);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.labelUpdateBefore = function (label) {
|
|
||||||
label.renameTitle = label.title;
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.labelUpdate = function (label) {
|
$scope.labelUpdate = function (label) {
|
||||||
label.edit = false;
|
label.edit = false;
|
||||||
LabelService.update(label).catch((err) => {
|
LabelService.update(label);
|
||||||
label.title = label.renameTitle;
|
|
||||||
OC.Notification.showTemporary(err);
|
|
||||||
});
|
|
||||||
|
|
||||||
// update labels in UI
|
|
||||||
var cards = CardService.data;
|
|
||||||
for (var card in cards) {
|
|
||||||
if (Object.prototype.hasOwnProperty.call(cards, card)) {
|
|
||||||
var labelsFromCard = cards[card].labels;
|
|
||||||
|
|
||||||
labelsFromCard.forEach(function (labelFromCard, index) {
|
|
||||||
if (labelFromCard.id === label.id) {
|
|
||||||
cards[card].labels[index] = label;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.aclAdd = function (sharee) {
|
$scope.aclAdd = function (sharee) {
|
||||||
@@ -415,33 +190,13 @@ app.controller('BoardController', function ($rootScope, $scope, $element, $state
|
|||||||
BoardService.addAcl(sharee);
|
BoardService.addAcl(sharee);
|
||||||
$scope.status.addSharee = null;
|
$scope.status.addSharee = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.aclDelete = function (acl) {
|
$scope.aclDelete = function (acl) {
|
||||||
BoardService.deleteAcl(acl).then(function(data) {
|
BoardService.deleteAcl(acl);
|
||||||
$scope.loadDefault();
|
|
||||||
$scope.refreshData();
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.aclUpdate = function (acl) {
|
$scope.aclUpdate = function (acl) {
|
||||||
BoardService.updateAcl(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';
|
|
||||||
case OC.Share.SHARE_TYPE_CIRCLE:
|
|
||||||
return 'circles';
|
|
||||||
default:
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// settings for card sorting
|
// settings for card sorting
|
||||||
$scope.sortOptions = {
|
$scope.sortOptions = {
|
||||||
@@ -449,7 +204,7 @@ app.controller('BoardController', function ($rootScope, $scope, $element, $state
|
|||||||
itemMoved: function (event) {
|
itemMoved: function (event) {
|
||||||
event.source.itemScope.modelValue.status = event.dest.sortableScope.$parent.column;
|
event.source.itemScope.modelValue.status = event.dest.sortableScope.$parent.column;
|
||||||
var order = event.dest.index;
|
var order = event.dest.index;
|
||||||
var card = $scope.cardservice.get(event.source.itemScope.c.id);
|
var card = event.source.itemScope.c;
|
||||||
var newStack = event.dest.sortableScope.$parent.s.id;
|
var newStack = event.dest.sortableScope.$parent.s.id;
|
||||||
var oldStack = card.stackId;
|
var oldStack = card.stackId;
|
||||||
card.stackId = newStack;
|
card.stackId = newStack;
|
||||||
@@ -465,33 +220,33 @@ app.controller('BoardController', function ($rootScope, $scope, $element, $state
|
|||||||
},
|
},
|
||||||
orderChanged: function (event) {
|
orderChanged: function (event) {
|
||||||
var order = event.dest.index;
|
var order = event.dest.index;
|
||||||
var card = $scope.cardservice.get(event.source.itemScope.c.id);
|
var card = event.source.itemScope.c;
|
||||||
var stack = event.dest.sortableScope.$parent.s.id;
|
var stack = event.dest.sortableScope.$parent.s.id;
|
||||||
CardService.reorder(card, order).then(function (data) {
|
CardService.reorder(card, order).then(function (data) {
|
||||||
StackService.reorderCard(card, order);
|
StackService.reorderCard(card, order);
|
||||||
$scope.refreshData();
|
$scope.refreshData();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
scrollableContainer: '#innerBoard',
|
scrollableContainer: '#board',
|
||||||
containerPositioning: 'relative',
|
containerPositioning: 'relative',
|
||||||
containment: '#innerBoard',
|
containment: '#board',
|
||||||
longTouch: true,
|
longTouch: true,
|
||||||
// auto scroll on drag
|
// auto scroll on drag
|
||||||
dragMove: function (itemPosition, containment, eventObj) {
|
dragMove: function (itemPosition, containment, eventObj) {
|
||||||
if (eventObj) {
|
if (eventObj) {
|
||||||
var container = $('#board');
|
var container = $("#board");
|
||||||
var offset = container.offset();
|
var offset = container.offset();
|
||||||
var targetX = eventObj.pageX - (offset.left || container.scrollLeft());
|
var targetX = eventObj.pageX - (offset.left || container.scrollLeft());
|
||||||
var targetY = eventObj.pageY - (offset.top || container.scrollTop());
|
var targetY = eventObj.pageY - (offset.top || container.scrollTop());
|
||||||
if (targetX < offset.left) {
|
if (targetX < offset.left) {
|
||||||
container.scrollLeft(container.scrollLeft() - 25);
|
container.scrollLeft(container.scrollLeft() - 50);
|
||||||
} else if (targetX > container.width()) {
|
} else if (targetX > container.width()) {
|
||||||
container.scrollLeft(container.scrollLeft() + 25);
|
container.scrollLeft(container.scrollLeft() + 50);
|
||||||
}
|
}
|
||||||
if (targetY < offset.top) {
|
if (targetY < offset.top) {
|
||||||
container.scrollTop(container.scrollTop() - 25);
|
container.scrollTop(container.scrollTop() - 50);
|
||||||
} else if (targetY > container.height()) {
|
} else if (targetY > container.height()) {
|
||||||
container.scrollTop(container.scrollTop() + 25);
|
container.scrollTop(container.scrollTop() + 50);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -511,10 +266,11 @@ app.controller('BoardController', function ($rootScope, $scope, $element, $state
|
|||||||
},
|
},
|
||||||
scrollableContainer: '#board',
|
scrollableContainer: '#board',
|
||||||
containerPositioning: 'relative',
|
containerPositioning: 'relative',
|
||||||
containment: '#innerBoard',
|
containment: '#board',
|
||||||
|
longTouch: true,
|
||||||
dragMove: function (itemPosition, containment, eventObj) {
|
dragMove: function (itemPosition, containment, eventObj) {
|
||||||
if (eventObj) {
|
if (eventObj) {
|
||||||
var container = $('#board');
|
var container = $("#board");
|
||||||
var offset = container.offset();
|
var offset = container.offset();
|
||||||
var targetX = eventObj.pageX - (offset.left || container.scrollLeft());
|
var targetX = eventObj.pageX - (offset.left || container.scrollLeft());
|
||||||
var targetY = eventObj.pageY - (offset.top || container.scrollTop());
|
var targetY = eventObj.pageY - (offset.top || container.scrollTop());
|
||||||
@@ -535,34 +291,4 @@ app.controller('BoardController', function ($rootScope, $scope, $element, $state
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.labelStyle = function (color) {
|
|
||||||
return {
|
|
||||||
'background-color': '#' + color,
|
|
||||||
'color': $filter('textColorFilter')(color)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.colorValue = function(color) {
|
|
||||||
const re = /^#([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/;
|
|
||||||
if (re.test(color)) {
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.attachmentCount = function(card) {
|
|
||||||
if (Array.isArray(card.attachments)) {
|
|
||||||
return card.attachments.filter((obj) => obj.deletedAt === 0).length;
|
|
||||||
}
|
|
||||||
return card.attachmentCount;
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.unreadCommentCount = function(card) {
|
|
||||||
return card.commentsUnread;
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.isTimelineEnabled = function() {
|
|
||||||
return OCP.Comments && OCA.Activity;
|
|
||||||
};
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -20,196 +20,69 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* global app moment angular OC OCP OCA */
|
/* global app */
|
||||||
import app from '../app/App.js';
|
/* global moment */
|
||||||
|
|
||||||
app.controller('CardController', function ($scope, $rootScope, $sce, $location, $stateParams, $state, $interval, $timeout, $filter, BoardService, CardService, StackService, StatusService, markdownItConverter, FileService) {
|
app.controller('CardController', function ($scope, $rootScope, $routeParams, $location, $stateParams, BoardService, CardService, StackService, StatusService) {
|
||||||
$scope.sidebar = $rootScope.sidebar;
|
$scope.sidebar = $rootScope.sidebar;
|
||||||
$scope.status = {
|
$scope.status = {};
|
||||||
renameTitle: '',
|
|
||||||
lastEdit: 0,
|
|
||||||
lastSave: Date.now()
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.cardservice = CardService;
|
$scope.cardservice = CardService;
|
||||||
$scope.fileservice = FileService;
|
|
||||||
$scope.cardId = $stateParams.cardId;
|
$scope.cardId = $stateParams.cardId;
|
||||||
|
|
||||||
$scope.statusservice = StatusService.getInstance();
|
$scope.statusservice = StatusService.getInstance();
|
||||||
$scope.boardservice = BoardService;
|
$scope.boardservice = BoardService;
|
||||||
|
|
||||||
$scope.isArray = angular.isArray;
|
|
||||||
// workaround for $stateParams changes not being propagated
|
|
||||||
$scope.$watch(function() {
|
|
||||||
return $state.params;
|
|
||||||
}, function (params) {
|
|
||||||
$scope.params = params;
|
|
||||||
$scope.fileservice.reset();
|
|
||||||
}, true);
|
|
||||||
$scope.params = $state.params;
|
|
||||||
|
|
||||||
$scope.addAttachmentToDescription = function(insertText) {
|
|
||||||
let el = document.querySelectorAll('textarea')[0];
|
|
||||||
let start = el.selectionStart;
|
|
||||||
let end = el.selectionEnd;
|
|
||||||
let text = $scope.status.edit.description || '';
|
|
||||||
let before = text.substring(0, start);
|
|
||||||
let after = text.substring(end, text.length);
|
|
||||||
let newText = before + '\n' + insertText + '\n' + after;
|
|
||||||
$scope.status.edit.description = newText;
|
|
||||||
el.selectionStart = el.selectionEnd = start + newText.length;
|
|
||||||
el.focus();
|
|
||||||
$scope.status.continueEdit = false;
|
|
||||||
$scope.cardEditDescriptionChanged();
|
|
||||||
$scope.status.selectAttachment = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.abortAttachmentSelection = function() {
|
|
||||||
$scope.status.continueEdit = false;
|
|
||||||
$scope.status.selectAttachment = false;
|
|
||||||
let el = document.querySelectorAll('textarea')[0];
|
|
||||||
el.focus();
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.statusservice.retainWaiting();
|
$scope.statusservice.retainWaiting();
|
||||||
|
|
||||||
$scope.description = function() {
|
|
||||||
return $scope.rendered;
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.updateMarkdown = function(content) {
|
|
||||||
// only trust the html from markdown-it-checkbox
|
|
||||||
$scope.rendered = $sce.trustAsHtml(markdownItConverter.render(content || ''));
|
|
||||||
};
|
|
||||||
|
|
||||||
CardService.fetchOne($scope.cardId).then(function (data) {
|
CardService.fetchOne($scope.cardId).then(function (data) {
|
||||||
$scope.statusservice.releaseWaiting();
|
$scope.statusservice.releaseWaiting();
|
||||||
$scope.archived = CardService.getCurrent().archived;
|
$scope.archived = CardService.getCurrent().archived;
|
||||||
$scope.updateMarkdown(CardService.getCurrent().description);
|
|
||||||
}, function (error) {
|
}, function (error) {
|
||||||
});
|
});
|
||||||
|
|
||||||
$scope.cardRenameShow = function () {
|
$scope.cardRenameShow = function () {
|
||||||
if ($scope.archived || !BoardService.canEdit()) {
|
if ($scope.archived || !BoardService.canEdit())
|
||||||
return false;
|
return false;
|
||||||
} else {
|
else {
|
||||||
$scope.status.renameTitle = CardService.getCurrent().title;
|
|
||||||
$scope.status.cardRename = true;
|
$scope.status.cardRename = true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
$scope.cardEditDescriptionShow = function ($event) {
|
||||||
$scope.toggleCheckbox = function (id) {
|
|
||||||
$('#markdown input[type=checkbox]').attr('disabled', true);
|
|
||||||
$scope.status.edit = angular.copy(CardService.getCurrent());
|
|
||||||
var reg = /\[(X|\s|\_|\-)\]/ig;
|
|
||||||
var nth = 0;
|
|
||||||
$scope.status.edit.description = $scope.status.edit.description.replace(reg, function (match, i, original) {
|
|
||||||
var result = match;
|
|
||||||
if ('' + nth++ === '' + id) {
|
|
||||||
if (match.match(/^\[\s\]/i)) {
|
|
||||||
result = match.replace(/\[\s\]/i, '[x]');
|
|
||||||
}
|
|
||||||
if (match.match(/^\[x\]/i)) {
|
|
||||||
result = match.replace(/\[x\]/i, '[ ]');
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
return match;
|
|
||||||
});
|
|
||||||
CardService.update($scope.status.edit).then(function (data) {
|
|
||||||
var header = $('.tabDetails');
|
|
||||||
header.find('.save-indicator.unsaved').hide();
|
|
||||||
header.find('.save-indicator.saved').fadeIn(250).fadeOut(1000);
|
|
||||||
});
|
|
||||||
$('#markdown input[type=checkbox]').removeAttr('disabled');
|
|
||||||
|
|
||||||
};
|
|
||||||
$scope.clickCardDescription = function ($event) {
|
|
||||||
var checkboxId = $($event.target).data('id');
|
|
||||||
if ($event.target.tagName === 'LABEL') {
|
|
||||||
$scope.toggleCheckbox(checkboxId);
|
|
||||||
$event.stopPropagation();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ($event.target.tagName === 'INPUT') {
|
|
||||||
$event.stopPropagation();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (BoardService.isArchived() || CardService.getCurrent().archived) {
|
if (BoardService.isArchived() || CardService.getCurrent().archived) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
var node = $event.target.nodeName;
|
||||||
if ($scope.card.archived || !$scope.boardservice.canEdit()) {
|
if ($scope.card.archived || !$scope.boardservice.canEdit()) {
|
||||||
return false;
|
console.log(node);
|
||||||
}
|
} else {
|
||||||
|
console.log("edit");
|
||||||
$scope.status.cardEditDescription = true;
|
$scope.status.cardEditDescription = true;
|
||||||
$scope.status.edit = angular.copy(CardService.getCurrent());
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
$scope.cardEditDescriptionChanged = function ($event) {
|
|
||||||
$scope.status.lastEdit = Date.now();
|
|
||||||
var header = $('.tabDetails');
|
|
||||||
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.saving) {
|
|
||||||
$scope.status.lastSave = currentTime;
|
|
||||||
$scope.status.saving = true;
|
|
||||||
var header = $('.tabDetails');
|
|
||||||
header.find('.save-indicator.unsaved').fadeIn(500);
|
|
||||||
CardService.update($scope.status.edit).then(function (data) {
|
|
||||||
var header = $('.tabDetails');
|
|
||||||
header.find('.save-indicator.unsaved').hide();
|
|
||||||
header.find('.save-indicator.saved').fadeIn(250).fadeOut(1000);
|
|
||||||
$scope.status.saving = false;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}, 500, 0, false);
|
console.log($scope.status.canEditDescription);
|
||||||
|
};
|
||||||
// handle rename to update information on the board as well
|
// handle rename to update information on the board as well
|
||||||
$scope.cardRename = function (card) {
|
$scope.cardRename = function (card) {
|
||||||
var newTitle;
|
|
||||||
if (!$scope.status.renameTitle || !$scope.status.renameTitle.trim()) {
|
|
||||||
newTitle = '';
|
|
||||||
} else {
|
|
||||||
newTitle = $scope.status.renameTitle.trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newTitle === card.title) {
|
|
||||||
// title unchanged
|
|
||||||
$scope.status.renameCard = false;
|
|
||||||
} else if (newTitle !== '') {
|
|
||||||
// title changed
|
|
||||||
card.title = newTitle;
|
|
||||||
CardService.rename(card).then(function (data) {
|
CardService.rename(card).then(function (data) {
|
||||||
|
StackService.updateCard(card);
|
||||||
$scope.status.renameCard = false;
|
$scope.status.renameCard = false;
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
// empty title
|
|
||||||
$scope.status.renameTitle = card.title;
|
|
||||||
$scope.status.renameCard = false;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
$scope.cardUpdate = function (card) {
|
$scope.cardUpdate = function (card) {
|
||||||
CardService.update(card).then(function (data) {
|
CardService.update(CardService.getCurrent()).then(function (data) {
|
||||||
$scope.status.cardEditDescription = false;
|
$scope.status.cardEditDescription = false;
|
||||||
$scope.updateMarkdown($scope.status.edit.description);
|
$('#card-description').find('.save-indicator').fadeIn(500).fadeOut(1000);
|
||||||
var header = $('.tabDetails');
|
|
||||||
header.find('.save-indicator.unsaved').hide();
|
|
||||||
header.find('.save-indicator.saved').fadeIn(500).fadeOut(1000);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.labelAssign = function (element, model) {
|
$scope.labelAssign = function (element, model) {
|
||||||
CardService.assignLabel($scope.cardId, element.id).then(function (data) {
|
CardService.assignLabel($scope.cardId, element.id);
|
||||||
});
|
var card = CardService.getCurrent();
|
||||||
|
StackService.updateCard(card);
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.labelRemove = function (element, model) {
|
$scope.labelRemove = function (element, model) {
|
||||||
CardService.removeLabel($scope.cardId, element.id).then(function (data) {
|
CardService.removeLabel($scope.cardId, element.id)
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.setDuedate = function (duedate) {
|
$scope.setDuedate = function (duedate) {
|
||||||
@@ -223,6 +96,7 @@ app.controller('CardController', function ($scope, $rootScope, $sce, $location,
|
|||||||
newDate.year(duedate.year());
|
newDate.year(duedate.year());
|
||||||
element.duedate = newDate.toISOString();
|
element.duedate = newDate.toISOString();
|
||||||
CardService.update(element);
|
CardService.update(element);
|
||||||
|
StackService.updateCard(element);
|
||||||
};
|
};
|
||||||
$scope.setDuedateTime = function (time) {
|
$scope.setDuedateTime = function (time) {
|
||||||
var element = CardService.getCurrent();
|
var element = CardService.getCurrent();
|
||||||
@@ -234,53 +108,13 @@ app.controller('CardController', function ($scope, $rootScope, $sce, $location,
|
|||||||
newDate.minute(time.minute());
|
newDate.minute(time.minute());
|
||||||
element.duedate = newDate.toISOString();
|
element.duedate = newDate.toISOString();
|
||||||
CardService.update(element);
|
CardService.update(element);
|
||||||
|
StackService.updateCard(element);
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.resetDuedate = function () {
|
$scope.resetDuedate = function () {
|
||||||
var element = CardService.getCurrent();
|
var element = CardService.getCurrent();
|
||||||
element.duedate = null;
|
element.duedate = null;
|
||||||
CardService.update(element);
|
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) {
|
|
||||||
});
|
|
||||||
$scope.status.showAssignUser = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.removeAssignedUser = function(uid) {
|
|
||||||
CardService.unassignUser(CardService.getCurrent(), uid).then(function (data) {
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.labelStyle = function (color) {
|
|
||||||
return {
|
|
||||||
'background-color': '#' + color,
|
|
||||||
'color': $filter('textColorFilter')(color)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.isTimelineEnabled = function() {
|
|
||||||
return OCP.Comments && OCA.Activity;
|
|
||||||
};
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
/*
|
|
||||||
* @copyright Copyright (c) 2018 Oskar Kurz <oskar.kurz@gmail.com>
|
|
||||||
*
|
|
||||||
* @author Oskar Kurz <oskar.kurz@gmail.com>
|
|
||||||
*
|
|
||||||
* @license GNU AGPL version 3 or any later version
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
import app from '../app/App.js';
|
|
||||||
|
|
||||||
/* global oc_defaults OC */
|
|
||||||
app.controller('ColorPickerController', ['$scope', function ($scope) {
|
|
||||||
$scope.hashedColor = '';
|
|
||||||
|
|
||||||
$scope.setColor = function (object, color) {
|
|
||||||
object.color = color;
|
|
||||||
object.hashedColor = '#' + color;
|
|
||||||
|
|
||||||
return object;
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.setHashedColor = function (object) {
|
|
||||||
object.color = object.hashedColor.substr(1);
|
|
||||||
return object;
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.getCustomBackground = function (color) {
|
|
||||||
return {'background-color': color};
|
|
||||||
};
|
|
||||||
}]);
|
|
||||||
@@ -20,31 +20,9 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* global app angular oc_isadmin */
|
/* global app angular */
|
||||||
|
|
||||||
var ListController = function ($scope, $location, $filter, BoardService, $element, $timeout, $stateParams, $state, StatusService, $http, $q, $rootScope) {
|
|
||||||
|
|
||||||
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))];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
app.controller('ListController', function ($scope, $location, $filter, BoardService, $element, $timeout, $stateParams, $state) {
|
||||||
$scope.boards = [];
|
$scope.boards = [];
|
||||||
$scope.newBoard = {};
|
$scope.newBoard = {};
|
||||||
$scope.status = {
|
$scope.status = {
|
||||||
@@ -54,72 +32,24 @@ var ListController = function ($scope, $location, $filter, BoardService, $elemen
|
|||||||
};
|
};
|
||||||
$scope.colors = ['0082c9', '00c9c6','00c906', 'c92b00', 'F1DB50', '7C31CC', '3A3B3D', 'CACBCD'];
|
$scope.colors = ['0082c9', '00c9c6','00c906', 'c92b00', 'F1DB50', '7C31CC', '3A3B3D', 'CACBCD'];
|
||||||
$scope.boardservice = BoardService;
|
$scope.boardservice = BoardService;
|
||||||
|
$scope.newBoard.color = $scope.colors[0];
|
||||||
$scope.updatingBoard = null;
|
$scope.updatingBoard = null;
|
||||||
$scope.isAdmin = oc_isadmin;
|
|
||||||
$scope.canCreate = $rootScope.config.canCreate;
|
|
||||||
|
|
||||||
if ($scope.isAdmin) {
|
// FIXME: not nice, but we want to load this only once
|
||||||
OC.Apps.enableDynamicSlideToggle();
|
if($element.attr('id') === 'app-navigation') {
|
||||||
$scope.groups = [];
|
BoardService.fetchAll().then(function(data) {
|
||||||
$scope.groupLimit = [];
|
$scope.filterData();
|
||||||
$scope.groupLimitDisabled = true;
|
|
||||||
let fetchGroups = function () {
|
|
||||||
var deferred = $q.defer();
|
|
||||||
// TODO: move to groups/details once 15 is min version
|
|
||||||
$http.get(OC.linkToOCS('cloud', 2) + 'groups').then(function (response) {
|
|
||||||
$scope.groups = response.data.ocs.data.groups.reduce((obj, item) => {
|
|
||||||
obj.push({
|
|
||||||
id: item,
|
|
||||||
displayname: item,
|
|
||||||
});
|
|
||||||
return obj;
|
|
||||||
}, []);
|
|
||||||
deferred.resolve($scope.groups);
|
|
||||||
}, function (error) {
|
}, function (error) {
|
||||||
deferred.reject('Error while loading groups');
|
// TODO: show error when loading fails
|
||||||
});
|
});
|
||||||
$http.get(OC.generateUrl('apps/deck/config')).then(function (response) {
|
|
||||||
$scope.groupLimit = response.data.groupLimit;
|
|
||||||
$scope.groupLimitDisabled = false;
|
|
||||||
deferred.resolve(response.data);
|
|
||||||
}, function (error) {
|
|
||||||
deferred.reject('Error while loading groupLimit');
|
|
||||||
});
|
|
||||||
return deferred.promise;
|
|
||||||
};
|
|
||||||
|
|
||||||
let updateConfig = function() {
|
|
||||||
$scope.groupLimitDisabled = true;
|
|
||||||
var deferred = $q.defer();
|
|
||||||
$http.post(OC.generateUrl('apps/deck/config/groupLimit'), {value: $scope.groupLimit}).then(function (response) {
|
|
||||||
$scope.groupLimitDisabled = false;
|
|
||||||
deferred.resolve(response.data);
|
|
||||||
}, function (error) {
|
|
||||||
deferred.reject('Error while saving groupLimit');
|
|
||||||
});
|
|
||||||
return deferred.promise;
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.groupLimitAdd = function (element, model) {
|
|
||||||
$scope.groupLimit.push(element);
|
|
||||||
updateConfig();
|
|
||||||
};
|
|
||||||
$scope.groupLimitRemove = function (element, model) {
|
|
||||||
$scope.groupLimit = $scope.groupLimit.filter((el) => {
|
|
||||||
return el.id !== element.id;
|
|
||||||
});
|
|
||||||
updateConfig();
|
|
||||||
};
|
|
||||||
fetchGroups();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var filterData = function () {
|
$scope.filterData = function () {
|
||||||
if($element.attr('id') === 'app-navigation') {
|
angular.copy($scope.boardservice.getData(), $scope.boardservice.sorted);
|
||||||
$scope.boardservice.sidebar = $scope.boardservice.getData();
|
angular.copy($scope.boardservice.sorted, $scope.boardservice.sidebar);
|
||||||
$scope.boardservice.sidebar = $filter('orderBy')($scope.boardservice.sidebar, 'title');
|
$scope.boardservice.sidebar = $filter('orderBy')($scope.boardservice.sidebar, 'title');
|
||||||
$scope.boardservice.sidebar = $filter('cardFilter')($scope.boardservice.sidebar, {archived: false});
|
$scope.boardservice.sidebar = $filter('cardFilter')($scope.boardservice.sidebar, {archived: false});
|
||||||
} else {
|
|
||||||
$scope.boardservice.sorted = $scope.boardservice.getData();
|
|
||||||
if ($scope.status.filter === 'archived') {
|
if ($scope.status.filter === 'archived') {
|
||||||
var filter = {};
|
var filter = {};
|
||||||
filter[$scope.status.filter] = true;
|
filter[$scope.status.filter] = true;
|
||||||
@@ -131,53 +61,15 @@ var ListController = function ($scope, $location, $filter, BoardService, $elemen
|
|||||||
$scope.boardservice.sorted = $filter('cardFilter')($scope.boardservice.sorted, {archived: false});
|
$scope.boardservice.sorted = $filter('cardFilter')($scope.boardservice.sorted, {archived: false});
|
||||||
}
|
}
|
||||||
$scope.boardservice.sorted = $filter('orderBy')($scope.boardservice.sorted, ['deletedAt', 'title']);
|
$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(){
|
$scope.$watchCollection(function(){
|
||||||
return $state.params;
|
return $state.params;
|
||||||
}, function(){
|
}, function(){
|
||||||
$scope.status.filter = $state.params.filter;
|
$scope.status.filter = $state.params.filter;
|
||||||
filterData();
|
$scope.filterData();
|
||||||
});
|
});
|
||||||
};
|
|
||||||
initialize();
|
|
||||||
|
|
||||||
$scope.selectColor = function(color) {
|
$scope.selectColor = function(color) {
|
||||||
$scope.newBoard.color = color;
|
$scope.newBoard.color = color;
|
||||||
@@ -198,9 +90,9 @@ var ListController = function ($scope, $location, $filter, BoardService, $elemen
|
|||||||
BoardService.create($scope.newBoard)
|
BoardService.create($scope.newBoard)
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
$scope.newBoard = {};
|
$scope.newBoard = {};
|
||||||
$scope.newBoard.color = calculateNewColor();
|
$scope.newBoard.color = $scope.colors[0];
|
||||||
$scope.status.addBoard=false;
|
$scope.status.addBoard=false;
|
||||||
filterData();
|
$scope.filterData();
|
||||||
}, function(error) {
|
}, function(error) {
|
||||||
$scope.status.createBoard = 'Unable to insert board: ' + error.message;
|
$scope.status.createBoard = 'Unable to insert board: ' + error.message;
|
||||||
});
|
});
|
||||||
@@ -208,48 +100,47 @@ var ListController = function ($scope, $location, $filter, BoardService, $elemen
|
|||||||
|
|
||||||
$scope.boardUpdate = function(board) {
|
$scope.boardUpdate = function(board) {
|
||||||
BoardService.update(board).then(function(data) {
|
BoardService.update(board).then(function(data) {
|
||||||
|
$scope.filterData();
|
||||||
board.status.edit = false;
|
board.status.edit = false;
|
||||||
filterData();
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.boardUpdateBegin = function(board) {
|
$scope.boardUpdateBegin = function(board) {
|
||||||
$scope.updatingBoard = angular.copy(board);
|
$scope.updatingBoard = board;
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.boardUpdateReset = function(board) {
|
$scope.boardUpdateReset = function(board) {
|
||||||
board.title = $scope.updatingBoard.title;
|
board.title = $scope.updatingBoard.title;
|
||||||
board.color = $scope.updatingBoard.color;
|
board.color = $scope.updatingBoard.color;
|
||||||
filterData();
|
$scope.filterData();
|
||||||
board.status.edit = false;
|
board.status.edit = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.boardArchive = function (board) {
|
$scope.boardArchive = function (board) {
|
||||||
board.archived = true;
|
board.archived = true;
|
||||||
BoardService.update(board).then(function(data) {
|
BoardService.update(board).then(function(data) {
|
||||||
filterData();
|
$scope.filterData();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.boardUnarchive = function (board) {
|
$scope.boardUnarchive = function (board) {
|
||||||
board.archived = false;
|
board.archived = false;
|
||||||
BoardService.update(board).then(function(data) {
|
BoardService.update(board).then(function(data) {
|
||||||
filterData();
|
$scope.filterData();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.boardDelete = function(board) {
|
$scope.boardDelete = function(board) {
|
||||||
BoardService.delete(board.id).then(function (data) {
|
BoardService.delete(board.id).then(function (data) {
|
||||||
filterData();
|
$scope.filterData();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.boardDeleteUndo = function (board) {
|
$scope.boardDeleteUndo = function (board) {
|
||||||
BoardService.deleteUndo(board.id).then(function (data) {
|
BoardService.deleteUndo(board.id).then(function (data) {
|
||||||
filterData();
|
$scope.filterData();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
});
|
||||||
|
|
||||||
export default ListController;
|
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import app from '../app/App.js';
|
|
||||||
|
|
||||||
app.directive('appPopoverMenuUtils', function () {
|
app.directive('appPopoverMenuUtils', function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
@@ -29,11 +28,12 @@ app.directive('appPopoverMenuUtils', function () {
|
|||||||
var menu = elm.find('.popovermenu');
|
var menu = elm.find('.popovermenu');
|
||||||
var button = elm.find('button');
|
var button = elm.find('button');
|
||||||
button.click(function (e) {
|
button.click(function (e) {
|
||||||
var popovermenus = $('.popovermenu');
|
$('.popovermenu').addClass('hidden');
|
||||||
var shouldShow = menu.hasClass('hidden');
|
|
||||||
popovermenus.addClass('hidden');
|
|
||||||
if (shouldShow) {
|
|
||||||
menu.toggleClass('hidden');
|
menu.toggleClass('hidden');
|
||||||
|
if(!menu.hasClass('hidden')) {
|
||||||
|
button.css('display','block');
|
||||||
|
} else {
|
||||||
|
button.css('display','');
|
||||||
}
|
}
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
});
|
});
|
||||||
@@ -42,6 +42,7 @@ app.directive('appPopoverMenuUtils', function () {
|
|||||||
if (event.target !== button && !$(event.target).hasClass('no-close')) {
|
if (event.target !== button && !$(event.target).hasClass('no-close')) {
|
||||||
menu.addClass('hidden');
|
menu.addClass('hidden');
|
||||||
}
|
}
|
||||||
|
button.css('display','');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import app from '../app/App.js';
|
|
||||||
// OwnCloud Click Handling
|
// OwnCloud Click Handling
|
||||||
// https://doc.owncloud.org/server/8.0/developer_manual/app/css.html
|
// https://doc.owncloud.org/server/8.0/developer_manual/app/css.html
|
||||||
app.directive('appNavigationEntryUtils', function () {
|
app.directive('appNavigationEntryUtils', function () {
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import app from '../app/App.js';
|
|
||||||
|
|
||||||
app.directive('autofocusOnInsert', function () {
|
app.directive('autofocusOnInsert', function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|||||||
@@ -19,37 +19,18 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import app from '../app/App.js';
|
|
||||||
|
|
||||||
app.directive('avatar', function() {
|
app.directive('avatar', function() {
|
||||||
'use strict';
|
'use strict';
|
||||||
return {
|
return {
|
||||||
restrict: 'AEC',
|
restrict: 'A',
|
||||||
transclude: true,
|
scope: 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){
|
link: function(scope, element, attr){
|
||||||
scope.uid = attr.displayname;
|
attr.$observe('displayname', function(value){
|
||||||
scope.displayname = attr.displayname;
|
if(value!==undefined) {
|
||||||
scope.size = attr.size;
|
$(element).avatar(value, 32);
|
||||||
if (typeof scope.size === 'undefined') {
|
|
||||||
scope.size = 32;
|
|
||||||
}
|
}
|
||||||
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, scope.size, false, false, false, attr.displayname);
|
|
||||||
},
|
|
||||||
controller: function () {}
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
/*
|
|
||||||
* @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/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
import app from '../app/App.js';
|
|
||||||
|
|
||||||
app.directive('bindHtmlCompile', function ($compile) {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
return {
|
|
||||||
restrict: 'A',
|
|
||||||
link: function (scope, element, attrs) {
|
|
||||||
scope.$watch(function () {
|
|
||||||
return scope.$eval(attrs.bindHtmlCompile);
|
|
||||||
}, function (value) {
|
|
||||||
element.html(value);
|
|
||||||
$compile(element.contents())(scope);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
/*
|
|
||||||
* @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/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
import app from '../app/App.js';
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
/*
|
|
||||||
* @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/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
import app from '../app/App';
|
|
||||||
|
|
||||||
app.directive('ngContenteditable', function($compile) {
|
|
||||||
return {
|
|
||||||
require: 'ngModel',
|
|
||||||
restrict: 'A',
|
|
||||||
scope: {
|
|
||||||
submit: '&ngSubmit'
|
|
||||||
},
|
|
||||||
link: function(scope, element, attrs, ngModel) {
|
|
||||||
|
|
||||||
//read the text typed in the div (syncing model with the view)
|
|
||||||
function read() {
|
|
||||||
ngModel.$setViewValue(element.html());
|
|
||||||
}
|
|
||||||
|
|
||||||
//render the data now in your model into your view
|
|
||||||
//$render is invoked when the modelvalue differs from the viewvalue
|
|
||||||
//see documentation: https://docs.angularjs.org/api/ng/type/ngModel.NgModelController#
|
|
||||||
ngModel.$render = function() {
|
|
||||||
element.html(ngModel.$viewValue || '');
|
|
||||||
};
|
|
||||||
|
|
||||||
//do this whenever someone starts typing
|
|
||||||
element.bind('blur keyup change', function(event) {
|
|
||||||
scope.$apply(read);
|
|
||||||
});
|
|
||||||
|
|
||||||
element.bind('keydown', function(event) {
|
|
||||||
if(event.which === 13 && event.shiftKey) {
|
|
||||||
scope.submit();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
@@ -19,7 +19,6 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import app from '../app/App.js';
|
|
||||||
|
|
||||||
/* global app */
|
/* global app */
|
||||||
/* gloabl t */
|
/* gloabl t */
|
||||||
@@ -30,10 +29,9 @@ app.directive('datepicker', function () {
|
|||||||
return {
|
return {
|
||||||
link: function (scope, elm, attr) {
|
link: function (scope, elm, attr) {
|
||||||
return elm.datepicker({
|
return elm.datepicker({
|
||||||
dateFormat: moment.localeData().longDateFormat('L').replace('YYYY', 'YY').toLowerCase(),
|
dateFormat: 'yy-mm-dd',
|
||||||
onSelect: function(date, inst) {
|
onSelect: function(date, inst) {
|
||||||
var selectedDate = $(this).datepicker('getDate');
|
scope.setDuedate(moment(date));
|
||||||
scope.setDuedate(moment(selectedDate));
|
|
||||||
scope.$apply();
|
scope.$apply();
|
||||||
},
|
},
|
||||||
beforeShow: function(input, inst) {
|
beforeShow: function(input, inst) {
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import app from '../app/App.js';
|
|
||||||
|
|
||||||
// original idea from blockloop: http://stackoverflow.com/a/24090733
|
// original idea from blockloop: http://stackoverflow.com/a/24090733
|
||||||
app.directive('elastic', [
|
app.directive('elastic', [
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import app from '../app/App.js';
|
|
||||||
|
|
||||||
app.directive('search', function ($document, $location) {
|
app.directive('search', function ($document, $location) {
|
||||||
'use strict';
|
'use strict';
|
||||||
@@ -30,19 +29,7 @@ app.directive('search', function ($document, $location) {
|
|||||||
'onSearch': '='
|
'onSearch': '='
|
||||||
},
|
},
|
||||||
link: function (scope) {
|
link: function (scope) {
|
||||||
if (OCA.Search && OCA.Search.Core) {
|
var box = $('#searchbox');
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
const search = new OCA.Search((term) => {
|
|
||||||
scope.$apply(function () {
|
|
||||||
scope.onSearch(term);
|
|
||||||
});
|
|
||||||
}, () => {
|
|
||||||
scope.$apply(function () {
|
|
||||||
scope.onSearch('');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const box = $('#searchbox');
|
|
||||||
box.val($location.search().search);
|
box.val($location.search().search);
|
||||||
|
|
||||||
var doSearch = function() {
|
var doSearch = function() {
|
||||||
@@ -53,9 +40,11 @@ app.directive('search', function ($document, $location) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
box.on('search keyup', function (event) {
|
box.on('search keyup', function (event) {
|
||||||
|
if (event.type === 'search' || event.keyCode === 13 ) {
|
||||||
doSearch();
|
doSearch();
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -19,9 +19,6 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import app from '../app/App.js';
|
|
||||||
import '../legacy/jquery.ui.timepicker.js';
|
|
||||||
import 'legacy/jquery.ui.timepicker.css';
|
|
||||||
|
|
||||||
/* global app */
|
/* global app */
|
||||||
/* global t */
|
/* global t */
|
||||||
@@ -32,7 +29,7 @@ app.directive('timepicker', function() {
|
|||||||
return {
|
return {
|
||||||
restrict: 'A',
|
restrict: 'A',
|
||||||
link: function(scope, elm, attr) {
|
link: function(scope, elm, attr) {
|
||||||
return $(elm).timepicker({
|
return elm.timepicker({
|
||||||
onSelect: function(date, inst) {
|
onSelect: function(date, inst) {
|
||||||
scope.setDuedateTime(moment('2000-01-01 ' + date));
|
scope.setDuedateTime(moment('2000-01-01 ' + date));
|
||||||
scope.$apply();
|
scope.$apply();
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import app from '../app/App.js';
|
|
||||||
|
|
||||||
app.filter('boardFilterAcl', function() {
|
app.filter('boardFilterAcl', function() {
|
||||||
return function(boards) {
|
return function(boards) {
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
/*
|
|
||||||
* @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/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
import app from '../app/App.js';
|
|
||||||
|
|
||||||
app.filter('bytes', function () {
|
|
||||||
return function (bytes, precision) {
|
|
||||||
if (isNaN(parseFloat(bytes, 10)) || !isFinite(bytes)) {
|
|
||||||
return '-';
|
|
||||||
}
|
|
||||||
if (typeof precision === 'undefined') {
|
|
||||||
precision = 2;
|
|
||||||
}
|
|
||||||
var units = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB'],
|
|
||||||
number = Math.floor(Math.log(bytes) / Math.log(1024));
|
|
||||||
return (bytes / Math.pow(1024, Math.floor(number))).toFixed(precision) + ' ' + units[number];
|
|
||||||
};
|
|
||||||
});
|
|
||||||
@@ -19,7 +19,6 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import app from '../app/App.js';
|
|
||||||
|
|
||||||
// usage | cardFilter({ member: 'admin'})
|
// usage | cardFilter({ member: 'admin'})
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import app from '../app/App.js';
|
|
||||||
|
|
||||||
app.filter('cardSearchFilter', function() {
|
app.filter('cardSearchFilter', function() {
|
||||||
return function(cards, searchString) {
|
return function(cards, searchString) {
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import app from '../app/App.js';
|
|
||||||
|
|
||||||
/* global app */
|
/* global app */
|
||||||
/* global OC */
|
/* global OC */
|
||||||
@@ -28,29 +27,28 @@ import app from '../app/App.js';
|
|||||||
app.filter('relativeDateFilter', function() {
|
app.filter('relativeDateFilter', function() {
|
||||||
return function (timestamp) {
|
return function (timestamp) {
|
||||||
return OC.Util.relativeModifiedDate(timestamp*1000);
|
return OC.Util.relativeModifiedDate(timestamp*1000);
|
||||||
};
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.filter('relativeDateFilterString', function() {
|
app.filter('relativeDateFilterString', function() {
|
||||||
return function (date) {
|
return function (date) {
|
||||||
return OC.Util.relativeModifiedDate(Date.parse(date));
|
return OC.Util.relativeModifiedDate(Date.parse(date));
|
||||||
};
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.filter('dateToTimestamp', function() {
|
app.filter('dateToTimestamp', function() {
|
||||||
return function (date) {
|
return function (date) {
|
||||||
return Date.parse(date);
|
return Date.parse(date);
|
||||||
};
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.filter('parseDate', function() {
|
app.filter('parseDate', function() {
|
||||||
return function (date) {
|
return function (date) {
|
||||||
if(moment(date).isValid()) {
|
if(moment(date).isValid()) {
|
||||||
var dateFormat = moment.localeData().longDateFormat('L');
|
return moment(date).format('YYYY-MM-DD');
|
||||||
return moment(date).format(dateFormat);
|
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
};
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.filter('parseTime', function() {
|
app.filter('parseTime', function() {
|
||||||
@@ -59,5 +57,5 @@ app.filter('parseTime', function() {
|
|||||||
return moment(date).format('HH:mm');
|
return moment(date).format('HH:mm');
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
};
|
}
|
||||||
});
|
});
|
||||||
@@ -19,7 +19,6 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import app from '../app/App.js';
|
|
||||||
|
|
||||||
app.filter('iconWhiteFilter', function() {
|
app.filter('iconWhiteFilter', function() {
|
||||||
return function (hex) {
|
return function (hex) {
|
||||||
@@ -31,37 +30,36 @@ app.filter('iconWhiteFilter', function () {
|
|||||||
g: parseInt(result[2], 16),
|
g: parseInt(result[2], 16),
|
||||||
b: parseInt(result[3], 16)
|
b: parseInt(result[3], 16)
|
||||||
} : null;
|
} : null;
|
||||||
if (result === null) {
|
if(result !== null) {
|
||||||
return '';
|
|
||||||
}
|
|
||||||
var r = color.r/255;
|
var r = color.r/255;
|
||||||
var g = color.g/255;
|
var g = color.g/255;
|
||||||
var b = color.b/255;
|
var b = color.b/255;
|
||||||
var max = Math.max(r, g, b), min = Math.min(r, g, b);
|
var max = Math.max(r, g, b), min = Math.min(r, g, b);
|
||||||
var h, s, l = (max + min) / 2;
|
var h, s, l = (max + min) / 2;
|
||||||
|
|
||||||
if (max === min) {
|
if(max == min){
|
||||||
h = s = 0; // achromatic
|
h = s = 0; // achromatic
|
||||||
}else{
|
}else{
|
||||||
var d = max - min;
|
var d = max - min;
|
||||||
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
||||||
switch(max){
|
switch(max){
|
||||||
case r:
|
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
||||||
h = (g - b) / d + (g < b ? 6 : 0);
|
case g: h = (b - r) / d + 2; break;
|
||||||
break;
|
case b: h = (r - g) / d + 4; break;
|
||||||
case g:
|
|
||||||
h = (b - r) / d + 2;
|
|
||||||
break;
|
|
||||||
case b:
|
|
||||||
h = (r - g) / d + 4;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
h /= 6;
|
h /= 6;
|
||||||
}
|
}
|
||||||
|
// TODO: Maybe just darken/lighten the color
|
||||||
if(l<0.5) {
|
if(l<0.5) {
|
||||||
return '-white';
|
return "-white";
|
||||||
} else {
|
} else {
|
||||||
return '';
|
return "";
|
||||||
|
}
|
||||||
|
//var rgba = "rgba(" + color.r + "," + color.g + "," + color.b + ",0.7)";
|
||||||
|
//return rgba;
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import app from '../app/App.js';
|
|
||||||
|
|
||||||
app.filter('lightenColorFilter', function() {
|
app.filter('lightenColorFilter', function() {
|
||||||
return function (hex) {
|
return function (hex) {
|
||||||
@@ -30,9 +29,9 @@ app.filter('lightenColorFilter', function() {
|
|||||||
b: parseInt(result[3], 16)
|
b: parseInt(result[3], 16)
|
||||||
} : null;
|
} : null;
|
||||||
if (result !== null) {
|
if (result !== null) {
|
||||||
return 'rgba(' + color.r + ',' + color.g + ',' + color.b + ',0.7)';
|
return "rgba(" + color.r + "," + color.g + "," + color.b + ",0.7)";
|
||||||
} else {
|
} else {
|
||||||
return '#' + hex;
|
return "#" + hex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
});
|
});
|
||||||
@@ -19,19 +19,14 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import app from '../app/App.js';
|
|
||||||
|
|
||||||
app.filter('orderObjectBy', function(){
|
app.filter('orderObjectBy', function(){
|
||||||
return function(input, attribute) {
|
return function(input, attribute) {
|
||||||
if (!angular.isObject(input)) {
|
if (!angular.isObject(input)) return input;
|
||||||
return input;
|
|
||||||
}
|
|
||||||
var array = [];
|
var array = [];
|
||||||
for(var objectKey in input) {
|
for(var objectKey in input) {
|
||||||
if ({}.hasOwnProperty.call(input, objectKey)) {
|
|
||||||
array.push(input[objectKey]);
|
array.push(input[objectKey]);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
array.sort(function(a, b){
|
array.sort(function(a, b){
|
||||||
a = parseInt(a[attribute]);
|
a = parseInt(a[attribute]);
|
||||||
@@ -39,5 +34,5 @@ app.filter('orderObjectBy', function(){
|
|||||||
return a < b;
|
return a < b;
|
||||||
});
|
});
|
||||||
return array;
|
return array;
|
||||||
};
|
}
|
||||||
});
|
});
|
||||||
@@ -19,7 +19,6 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import app from '../app/App.js';
|
|
||||||
|
|
||||||
app.filter('textColorFilter', function() {
|
app.filter('textColorFilter', function() {
|
||||||
return function (hex) {
|
return function (hex) {
|
||||||
@@ -38,32 +37,26 @@ app.filter('textColorFilter', function () {
|
|||||||
var max = Math.max(r, g, b), min = Math.min(r, g, b);
|
var max = Math.max(r, g, b), min = Math.min(r, g, b);
|
||||||
var h, s, l = (max + min) / 2;
|
var h, s, l = (max + min) / 2;
|
||||||
|
|
||||||
if (max === min) {
|
if(max == min){
|
||||||
h = s = 0; // achromatic
|
h = s = 0; // achromatic
|
||||||
}else{
|
}else{
|
||||||
var d = max - min;
|
var d = max - min;
|
||||||
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
||||||
switch(max){
|
switch(max){
|
||||||
case r:
|
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
||||||
h = (g - b) / d + (g < b ? 6 : 0);
|
case g: h = (b - r) / d + 2; break;
|
||||||
break;
|
case b: h = (r - g) / d + 4; break;
|
||||||
case g:
|
|
||||||
h = (b - r) / d + 2;
|
|
||||||
break;
|
|
||||||
case b:
|
|
||||||
h = (r - g) / d + 4;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
h /= 6;
|
h /= 6;
|
||||||
}
|
}
|
||||||
if(l<0.5) {
|
if(l<0.5) {
|
||||||
return '#ffffff';
|
return "#ffffff";
|
||||||
} else {
|
} else {
|
||||||
return '#000000';
|
return "#000000";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return '#000000';
|
return "#000000";
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
/*
|
|
||||||
* @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/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
import app from '../app/App.js';
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
};
|
|
||||||
});
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
/*
|
|
||||||
* @copyright Copyright (c) 2019 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/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/* global __webpack_nonce__ __webpack_public_path__ OC t n */
|
|
||||||
// eslint-disable-next-line
|
|
||||||
__webpack_nonce__ = btoa(OC.requestToken);
|
|
||||||
// eslint-disable-next-line
|
|
||||||
__webpack_public_path__ = OC.linkTo('deck', 'js/build/');
|
|
||||||
|
|
||||||
import Vue from 'vue';
|
|
||||||
|
|
||||||
Vue.prototype.t = t;
|
|
||||||
Vue.prototype.n = n;
|
|
||||||
Vue.prototype.OC = OC;
|
|
||||||
|
|
||||||
import BoardSelector from './views/BoardSelector';
|
|
||||||
|
|
||||||
import './../css/collections.css';
|
|
||||||
|
|
||||||
((function(OCP) {
|
|
||||||
|
|
||||||
OCP.Collaboration.registerType('deck', {
|
|
||||||
action: () => {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const container = document.createElement('div');
|
|
||||||
container.id = 'deck-board-select';
|
|
||||||
const body = document.getElementById('body-user');
|
|
||||||
body.append(container);
|
|
||||||
const ComponentVM = new Vue({
|
|
||||||
render: h => h(BoardSelector),
|
|
||||||
});
|
|
||||||
ComponentVM.$mount(container);
|
|
||||||
ComponentVM.$root.$on('close', () => {
|
|
||||||
ComponentVM.$el.remove();
|
|
||||||
ComponentVM.$destroy();
|
|
||||||
reject();
|
|
||||||
});
|
|
||||||
ComponentVM.$root.$on('select', (id) => {
|
|
||||||
resolve(id);
|
|
||||||
ComponentVM.$el.remove();
|
|
||||||
ComponentVM.$destroy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
typeString: t('deck', 'Link to a board'),
|
|
||||||
typeIconClass: 'icon-deck'
|
|
||||||
});
|
|
||||||
})(window.OCP));
|
|
||||||
36
js/init.js
@@ -1,36 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
/* global __webpack_nonce__ __webpack_public_path__ OC t n */
|
|
||||||
// eslint-disable-next-line
|
|
||||||
__webpack_nonce__ = btoa(OC.requestToken);
|
|
||||||
// eslint-disable-next-line
|
|
||||||
__webpack_public_path__ = OC.linkTo('deck', 'js/build/');
|
|
||||||
|
|
||||||
// used for building a vendor stylesheet
|
|
||||||
import 'ng-sortable/dist/ng-sortable.css';
|
|
||||||
|
|
||||||
import angular from 'angular';
|
|
||||||
import markdownit from 'markdown-it';
|
|
||||||
global.markdownit = markdownit;
|
|
||||||
|
|
||||||
import app from './app/App.js';
|
|
||||||
import './app/Config.js';
|
|
||||||
import './app/Run.js';
|
|
||||||
|
|
||||||
|
|
||||||
import ListController from 'controller/ListController.js';
|
|
||||||
import attachmentListComponent from './controller/AttachmentController.js';
|
|
||||||
import activityComponent from './controller/ActivityController.js';
|
|
||||||
|
|
||||||
app.controller('ListController', ListController);
|
|
||||||
app.component('attachmentListComponent', attachmentListComponent);
|
|
||||||
app.component('activityComponent', activityComponent);
|
|
||||||
|
|
||||||
|
|
||||||
// require all the js files from subdirectories
|
|
||||||
var context = require.context('.', true, /(controller|service|filters|directive)\/(.*)\.js$/);
|
|
||||||
|
|
||||||
context.keys().forEach(function (key) {
|
|
||||||
context(key);
|
|
||||||
});
|
|
||||||
|
|
||||||
@@ -1,161 +0,0 @@
|
|||||||
/**
|
|
||||||
* @licence
|
|
||||||
*/
|
|
||||||
|
|
||||||
import CommentModel from './commentmodel.js';
|
|
||||||
import CommentSummaryModel from './commentsummarymodel.js';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class CommentCollection
|
|
||||||
* @classdesc
|
|
||||||
*
|
|
||||||
* Collection of comments assigned to a file
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
var CommentCollection = OC.Backbone.Collection.extend(
|
|
||||||
/** @lends OCA.AnnouncementCenter.Comments.CommentCollection.prototype */ {
|
|
||||||
|
|
||||||
sync: OC.Backbone.davSync,
|
|
||||||
|
|
||||||
model: CommentModel,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Object type
|
|
||||||
*
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
_objectType: 'deckCard',
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Object id
|
|
||||||
*
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
_objectId: null,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* True if there are no more page results left to fetch
|
|
||||||
*
|
|
||||||
* @type bool
|
|
||||||
*/
|
|
||||||
_endReached: false,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Number of comments to fetch per page
|
|
||||||
*
|
|
||||||
* @type int
|
|
||||||
*/
|
|
||||||
_limit : 5,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the collection
|
|
||||||
*
|
|
||||||
* @param {string} [options.objectType] object type
|
|
||||||
* @param {string} [options.objectId] object id
|
|
||||||
*/
|
|
||||||
initialize: function(models, options) {
|
|
||||||
options = options || {};
|
|
||||||
if (options.objectType) {
|
|
||||||
this._objectType = options.objectType;
|
|
||||||
}
|
|
||||||
if (options.objectId) {
|
|
||||||
this._objectId = options.objectId;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
url: function() {
|
|
||||||
return OC.linkToRemote('dav') + '/comments/' +
|
|
||||||
encodeURIComponent(this._objectType) + '/' +
|
|
||||||
encodeURIComponent(this._objectId) + '/';
|
|
||||||
},
|
|
||||||
|
|
||||||
setObjectId: function(objectId) {
|
|
||||||
this._objectId = objectId;
|
|
||||||
},
|
|
||||||
|
|
||||||
hasMoreResults: function() {
|
|
||||||
return !this._endReached;
|
|
||||||
},
|
|
||||||
|
|
||||||
reset: function() {
|
|
||||||
this._endReached = false;
|
|
||||||
this._summaryModel = null;
|
|
||||||
return OC.Backbone.Collection.prototype.reset.apply(this, arguments);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch the next set of results
|
|
||||||
*/
|
|
||||||
fetchNext: function(options) {
|
|
||||||
var self = this;
|
|
||||||
if (!this.hasMoreResults()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var body = '<?xml version="1.0" encoding="utf-8" ?>\n' +
|
|
||||||
'<oc:filter-comments xmlns:D="DAV:" xmlns:oc="http://owncloud.org/ns">\n' +
|
|
||||||
// load one more so we know there is more
|
|
||||||
' <oc:limit>' + (this._limit + 1) + '</oc:limit>\n' +
|
|
||||||
' <oc:offset>' + this.length + '</oc:offset>\n' +
|
|
||||||
'</oc:filter-comments>\n';
|
|
||||||
|
|
||||||
options = options || {};
|
|
||||||
var success = options.success;
|
|
||||||
options = _.extend({
|
|
||||||
remove: false,
|
|
||||||
parse: true,
|
|
||||||
data: body,
|
|
||||||
davProperties: CommentCollection.prototype.model.prototype.davProperties,
|
|
||||||
success: function(resp) {
|
|
||||||
if (resp.length <= self._limit) {
|
|
||||||
// no new entries, end reached
|
|
||||||
self._endReached = true;
|
|
||||||
} else {
|
|
||||||
// remove last entry, for next page load
|
|
||||||
resp = _.initial(resp);
|
|
||||||
}
|
|
||||||
if (!self.set(resp, options)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (success) {
|
|
||||||
success.apply(null, arguments);
|
|
||||||
}
|
|
||||||
self.trigger('sync', 'REPORT', self, options);
|
|
||||||
}
|
|
||||||
}, options);
|
|
||||||
|
|
||||||
return this.sync('REPORT', this, options);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the matching summary model
|
|
||||||
*
|
|
||||||
* @return {OCA.AnnouncementCenter.Comments.CommentSummaryModel} summary model
|
|
||||||
*/
|
|
||||||
getSummaryModel: function() {
|
|
||||||
if (!this._summaryModel) {
|
|
||||||
this._summaryModel = new CommentSummaryModel({
|
|
||||||
id: this._objectId,
|
|
||||||
objectType: this._objectType
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return this._summaryModel;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the read marker for this comment thread
|
|
||||||
*
|
|
||||||
* @param {Date} [date] optional date, defaults to now
|
|
||||||
* @param {Object} [options] backbone options
|
|
||||||
*/
|
|
||||||
updateReadMarker: function(date, options) {
|
|
||||||
options = options || {};
|
|
||||||
|
|
||||||
return this.getSummaryModel().save({
|
|
||||||
readMarker: (date || new Date()).toUTCString()
|
|
||||||
}, options);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default CommentCollection;
|
|
||||||
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2016
|
|
||||||
*
|
|
||||||
* This file is licensed under the Affero General Public License version 3
|
|
||||||
* or later.
|
|
||||||
*
|
|
||||||
* See the COPYING-README file.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
var NS_OWNCLOUD = 'http://owncloud.org/ns';
|
|
||||||
/**
|
|
||||||
* @class CommentModel
|
|
||||||
* @classdesc
|
|
||||||
*
|
|
||||||
* Comment
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
var CommentModel = OC.Backbone.Model.extend(
|
|
||||||
/** @lends OCA.Comments.CommentModel.prototype */ {
|
|
||||||
sync: OC.Backbone.davSync,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Object type
|
|
||||||
*
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
_objectType: 'deckCard',
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Object id
|
|
||||||
*
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
_objectId: null,
|
|
||||||
|
|
||||||
initialize: function(model, options) {
|
|
||||||
options = options || {};
|
|
||||||
if (options.objectType) {
|
|
||||||
this._objectType = options.objectType;
|
|
||||||
}
|
|
||||||
if (options.objectId) {
|
|
||||||
this._objectId = options.objectId;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
defaults: {
|
|
||||||
actorType: 'users',
|
|
||||||
objectType: 'deckCard'
|
|
||||||
},
|
|
||||||
|
|
||||||
davProperties: {
|
|
||||||
'id': '{' + NS_OWNCLOUD + '}id',
|
|
||||||
'message': '{' + NS_OWNCLOUD + '}message',
|
|
||||||
'actorType': '{' + NS_OWNCLOUD + '}actorType',
|
|
||||||
'actorId': '{' + NS_OWNCLOUD + '}actorId',
|
|
||||||
'actorDisplayName': '{' + NS_OWNCLOUD + '}actorDisplayName',
|
|
||||||
'creationDateTime': '{' + NS_OWNCLOUD + '}creationDateTime',
|
|
||||||
'objectType': '{' + NS_OWNCLOUD + '}objectType',
|
|
||||||
'objectId': '{' + NS_OWNCLOUD + '}objectId',
|
|
||||||
'isUnread': '{' + NS_OWNCLOUD + '}isUnread',
|
|
||||||
'mentions': '{' + NS_OWNCLOUD + '}mentions'
|
|
||||||
},
|
|
||||||
|
|
||||||
parse: function(data) {
|
|
||||||
return {
|
|
||||||
id: data.id,
|
|
||||||
message: data.message,
|
|
||||||
actorType: data.actorType,
|
|
||||||
actorId: data.actorId,
|
|
||||||
actorDisplayName: data.actorDisplayName,
|
|
||||||
creationDateTime: data.creationDateTime,
|
|
||||||
objectType: data.objectType,
|
|
||||||
objectId: data.objectId,
|
|
||||||
isUnread: (data.isUnread === 'true'),
|
|
||||||
mentions: this._parseMentions(data.mentions)
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
_parseMentions: function(mentions) {
|
|
||||||
if(_.isUndefined(mentions)) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
var result = {};
|
|
||||||
for(var i in mentions) {
|
|
||||||
var mention = mentions[i];
|
|
||||||
if(_.isUndefined(mention.localName) || mention.localName !== 'mention') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
result[i] = {};
|
|
||||||
for (var child = mention.firstChild; child; child = child.nextSibling) {
|
|
||||||
if(_.isUndefined(child.localName) || !child.localName.startsWith('mention')) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
result[i][child.localName] = child.textContent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
|
|
||||||
url: function() {
|
|
||||||
let baseUrl;
|
|
||||||
if (typeof this.collection === 'undefined') {
|
|
||||||
baseUrl = OC.linkToRemote('dav') + '/comments/' +
|
|
||||||
encodeURIComponent(this.get('objectType')) + '/' +
|
|
||||||
encodeURIComponent(this.get('objectId')) + '/';
|
|
||||||
} else {
|
|
||||||
baseUrl = this.collection.url();
|
|
||||||
}
|
|
||||||
if (typeof this.get('id') !== 'undefined') {
|
|
||||||
return baseUrl + this.get('id');
|
|
||||||
} else {
|
|
||||||
return baseUrl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default CommentModel;
|
|
||||||
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
|
|
||||||
var NS_OWNCLOUD = 'http://owncloud.org/ns';
|
|
||||||
/**
|
|
||||||
* @class OCA.AnnouncementCenter.Comments.CommentSummaryModel
|
|
||||||
* @classdesc
|
|
||||||
*
|
|
||||||
* Model containing summary information related to comments
|
|
||||||
* like the read marker.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
var CommentSummaryModel = OC.Backbone.Model.extend(
|
|
||||||
/** @lends OCA.AnnouncementCenter.Comments.CommentSummaryModel.prototype */ {
|
|
||||||
sync: OC.Backbone.davSync,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Object type
|
|
||||||
*
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
_objectType: 'deckCard',
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Object id
|
|
||||||
*
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
_objectId: null,
|
|
||||||
|
|
||||||
davProperties: {
|
|
||||||
'readMarker': '{' + NS_OWNCLOUD + '}readMarker'
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the summary model
|
|
||||||
*
|
|
||||||
* @param {string} [options.objectType] object type
|
|
||||||
* @param {string} [options.objectId] object id
|
|
||||||
*/
|
|
||||||
initialize: function(attrs, options) {
|
|
||||||
options = options || {};
|
|
||||||
if (options.objectType) {
|
|
||||||
this._objectType = options.objectType;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
url: function() {
|
|
||||||
return OC.linkToRemote('dav') + '/comments/' +
|
|
||||||
encodeURIComponent(this._objectType) + '/' +
|
|
||||||
encodeURIComponent(this.id) + '/';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default CommentSummaryModel;
|
|
||||||
|
|
||||||
1
js/legacy/jquery.atwho.min.js
vendored
561
js/legacy/jquery.caret.min.js
vendored
@@ -1,561 +0,0 @@
|
|||||||
/*
|
|
||||||
* @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/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
(function($, undefined) {
|
|
||||||
|
|
||||||
var _input = document.createElement('input');
|
|
||||||
|
|
||||||
var _support = {
|
|
||||||
setSelectionRange: ('setSelectionRange' in _input) || ('selectionStart' in _input),
|
|
||||||
createTextRange: ('createTextRange' in _input) || ('selection' in document)
|
|
||||||
};
|
|
||||||
|
|
||||||
var _rNewlineIE = /\r\n/g,
|
|
||||||
_rCarriageReturn = /\r/g;
|
|
||||||
|
|
||||||
var _getValue = function(input) {
|
|
||||||
if (typeof(input.value) !== 'undefined') {
|
|
||||||
return input.value;
|
|
||||||
}
|
|
||||||
return $(input).text();
|
|
||||||
};
|
|
||||||
|
|
||||||
var _setValue = function(input, value) {
|
|
||||||
if (typeof(input.value) !== 'undefined') {
|
|
||||||
input.value = value;
|
|
||||||
} else {
|
|
||||||
$(input).text(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var _getIndex = function(input, pos) {
|
|
||||||
var norm = _getValue(input).replace(_rCarriageReturn, '');
|
|
||||||
var len = norm.length;
|
|
||||||
|
|
||||||
if (typeof(pos) === 'undefined') {
|
|
||||||
pos = len;
|
|
||||||
}
|
|
||||||
|
|
||||||
pos = Math.floor(pos);
|
|
||||||
|
|
||||||
// Negative index counts backward from the end of the input/textarea's value
|
|
||||||
if (pos < 0) {
|
|
||||||
pos = len + pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enforce boundaries
|
|
||||||
if (pos < 0) { pos = 0; }
|
|
||||||
if (pos > len) { pos = len; }
|
|
||||||
|
|
||||||
return pos;
|
|
||||||
};
|
|
||||||
|
|
||||||
var _hasAttr = function(input, attrName) {
|
|
||||||
return input.hasAttribute ? input.hasAttribute(attrName) : (typeof(input[attrName]) !== 'undefined');
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
var Range = function(start, end, length, text) {
|
|
||||||
this.start = start || 0;
|
|
||||||
this.end = end || 0;
|
|
||||||
this.length = length || 0;
|
|
||||||
this.text = text || '';
|
|
||||||
};
|
|
||||||
|
|
||||||
Range.prototype.toString = function() {
|
|
||||||
return JSON.stringify(this, null, ' ');
|
|
||||||
};
|
|
||||||
|
|
||||||
var _getCaretW3 = function(input) {
|
|
||||||
return input.selectionStart;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see http://stackoverflow.com/q/6943000/467582
|
|
||||||
*/
|
|
||||||
var _getCaretIE = function(input) {
|
|
||||||
var caret, range, textInputRange, rawValue, len, endRange;
|
|
||||||
|
|
||||||
// Yeah, you have to focus twice for IE 7 and 8. *cries*
|
|
||||||
input.focus();
|
|
||||||
input.focus();
|
|
||||||
|
|
||||||
range = document.selection.createRange();
|
|
||||||
|
|
||||||
if (range && range.parentElement() === input) {
|
|
||||||
rawValue = _getValue(input);
|
|
||||||
|
|
||||||
len = rawValue.length;
|
|
||||||
|
|
||||||
// Create a working TextRange that lives only in the input
|
|
||||||
textInputRange = input.createTextRange();
|
|
||||||
textInputRange.moveToBookmark(range.getBookmark());
|
|
||||||
|
|
||||||
// Check if the start and end of the selection are at the very end
|
|
||||||
// of the input, since moveStart/moveEnd doesn't return what we want
|
|
||||||
// in those cases
|
|
||||||
endRange = input.createTextRange();
|
|
||||||
endRange.collapse(false);
|
|
||||||
|
|
||||||
if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
|
|
||||||
caret = rawValue.replace(_rNewlineIE, '\n').length;
|
|
||||||
} else {
|
|
||||||
caret = -textInputRange.moveStart("character", -len);
|
|
||||||
}
|
|
||||||
|
|
||||||
return caret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: This occurs when you highlight part of a textarea and then click in the middle of the highlighted portion in IE 6-10.
|
|
||||||
// There doesn't appear to be anything we can do about it.
|
|
||||||
// alert("Your browser is incredibly stupid. I don't know what else to say.");
|
|
||||||
// alert(range + '\n\n' + range.parentElement().tagName + '#' + range.parentElement().id);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the position of the caret in the given input.
|
|
||||||
* @param {HTMLInputElement|HTMLTextAreaElement} input input or textarea element
|
|
||||||
* @returns {Number}
|
|
||||||
* @see http://stackoverflow.com/questions/263743/how-to-get-cursor-position-in-textarea/263796#263796
|
|
||||||
*/
|
|
||||||
var _getCaret = function(input) {
|
|
||||||
if (!input) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mozilla, et al.
|
|
||||||
if (_support.setSelectionRange) {
|
|
||||||
return _getCaretW3(input);
|
|
||||||
}
|
|
||||||
// IE
|
|
||||||
else if (_support.createTextRange) {
|
|
||||||
return _getCaretIE(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
var _setCaretW3 = function(input, pos) {
|
|
||||||
input.setSelectionRange(pos, pos);
|
|
||||||
};
|
|
||||||
|
|
||||||
var _setCaretIE = function(input, pos) {
|
|
||||||
var range = input.createTextRange();
|
|
||||||
range.move('character', pos);
|
|
||||||
range.select();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the position of the caret in the given input.
|
|
||||||
* @param {HTMLInputElement|HTMLTextAreaElement} input input or textarea element
|
|
||||||
* @param {Number} pos
|
|
||||||
* @see http://parentnode.org/javascript/working-with-the-cursor-position/
|
|
||||||
*/
|
|
||||||
var _setCaret = function(input, pos) {
|
|
||||||
input.focus();
|
|
||||||
|
|
||||||
pos = _getIndex(input, pos);
|
|
||||||
|
|
||||||
// Mozilla, et al.
|
|
||||||
if (_support.setSelectionRange) {
|
|
||||||
_setCaretW3(input, pos);
|
|
||||||
}
|
|
||||||
// IE
|
|
||||||
else if (_support.createTextRange) {
|
|
||||||
_setCaretIE(input, pos);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inserts the specified text at the current caret position in the given input.
|
|
||||||
* @param {HTMLInputElement|HTMLTextAreaElement} input input or textarea element
|
|
||||||
* @param {String} text
|
|
||||||
* @see http://parentnode.org/javascript/working-with-the-cursor-position/
|
|
||||||
*/
|
|
||||||
var _insertAtCaret = function(input, text) {
|
|
||||||
var curPos = _getCaret(input);
|
|
||||||
|
|
||||||
var oldValueNorm = _getValue(input).replace(_rCarriageReturn, '');
|
|
||||||
|
|
||||||
var newLength = +(curPos + text.length + (oldValueNorm.length - curPos));
|
|
||||||
var maxLength = +input.getAttribute('maxlength');
|
|
||||||
|
|
||||||
if(_hasAttr(input, 'maxlength') && newLength > maxLength) {
|
|
||||||
var delta = text.length - (newLength - maxLength);
|
|
||||||
text = text.substr(0, delta);
|
|
||||||
}
|
|
||||||
|
|
||||||
_setValue(input, oldValueNorm.substr(0, curPos) + text + oldValueNorm.substr(curPos));
|
|
||||||
|
|
||||||
_setCaret(input, curPos + text.length);
|
|
||||||
};
|
|
||||||
|
|
||||||
var _getInputRangeW3 = function(input) {
|
|
||||||
var range = new Range();
|
|
||||||
|
|
||||||
range.start = input.selectionStart;
|
|
||||||
range.end = input.selectionEnd;
|
|
||||||
|
|
||||||
var min = Math.min(range.start, range.end);
|
|
||||||
var max = Math.max(range.start, range.end);
|
|
||||||
|
|
||||||
range.length = max - min;
|
|
||||||
range.text = _getValue(input).substring(min, max);
|
|
||||||
|
|
||||||
return range;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** @see http://stackoverflow.com/a/3648244/467582 */
|
|
||||||
var _getInputRangeIE = function(input) {
|
|
||||||
var range = new Range();
|
|
||||||
|
|
||||||
input.focus();
|
|
||||||
|
|
||||||
var selection = document.selection.createRange();
|
|
||||||
|
|
||||||
if (selection && selection.parentElement() === input) {
|
|
||||||
var len, normalizedValue, textInputRange, endRange, start = 0, end = 0;
|
|
||||||
var rawValue = _getValue(input);
|
|
||||||
|
|
||||||
len = rawValue.length;
|
|
||||||
normalizedValue = rawValue.replace(/\r\n/g, "\n");
|
|
||||||
|
|
||||||
// Create a working TextRange that lives only in the input
|
|
||||||
textInputRange = input.createTextRange();
|
|
||||||
textInputRange.moveToBookmark(selection.getBookmark());
|
|
||||||
|
|
||||||
// Check if the start and end of the selection are at the very end
|
|
||||||
// of the input, since moveStart/moveEnd doesn't return what we want
|
|
||||||
// in those cases
|
|
||||||
endRange = input.createTextRange();
|
|
||||||
endRange.collapse(false);
|
|
||||||
|
|
||||||
if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
|
|
||||||
start = end = len;
|
|
||||||
} else {
|
|
||||||
start = -textInputRange.moveStart("character", -len);
|
|
||||||
start += normalizedValue.slice(0, start).split("\n").length - 1;
|
|
||||||
|
|
||||||
if (textInputRange.compareEndPoints("EndToEnd", endRange) > -1) {
|
|
||||||
end = len;
|
|
||||||
} else {
|
|
||||||
end = -textInputRange.moveEnd("character", -len);
|
|
||||||
end += normalizedValue.slice(0, end).split("\n").length - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// normalize newlines
|
|
||||||
start -= (rawValue.substring(0, start).split('\r\n').length - 1);
|
|
||||||
end -= (rawValue.substring(0, end).split('\r\n').length - 1);
|
|
||||||
/// normalize newlines
|
|
||||||
|
|
||||||
range.start = start;
|
|
||||||
range.end = end;
|
|
||||||
range.length = range.end - range.start;
|
|
||||||
range.text = normalizedValue.substr(range.start, range.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
return range;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the selected text range of the given input.
|
|
||||||
* @param {HTMLInputElement|HTMLTextAreaElement} input input or textarea element
|
|
||||||
* @returns {Range}
|
|
||||||
* @see http://stackoverflow.com/a/263796/467582
|
|
||||||
* @see http://stackoverflow.com/a/2966703/467582
|
|
||||||
*/
|
|
||||||
var _getInputRange = function(input) {
|
|
||||||
if (!input) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mozilla, et al.
|
|
||||||
if (_support.setSelectionRange) {
|
|
||||||
return _getInputRangeW3(input);
|
|
||||||
}
|
|
||||||
// IE
|
|
||||||
else if (_support.createTextRange) {
|
|
||||||
return _getInputRangeIE(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
var _setInputRangeW3 = function(input, startPos, endPos) {
|
|
||||||
input.setSelectionRange(startPos, endPos);
|
|
||||||
};
|
|
||||||
|
|
||||||
var _setInputRangeIE = function(input, startPos, endPos) {
|
|
||||||
var tr = input.createTextRange();
|
|
||||||
tr.moveEnd('textedit', -1);
|
|
||||||
tr.moveStart('character', startPos);
|
|
||||||
tr.moveEnd('character', endPos - startPos);
|
|
||||||
tr.select();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the selected text range of (i.e., highlights text in) the given input.
|
|
||||||
* @param {HTMLInputElement|HTMLTextAreaElement} input input or textarea element
|
|
||||||
* @param {Number} startPos Zero-based index
|
|
||||||
* @param {Number} endPos Zero-based index
|
|
||||||
* @see http://parentnode.org/javascript/working-with-the-cursor-position/
|
|
||||||
* @see http://stackoverflow.com/a/2966703/467582
|
|
||||||
*/
|
|
||||||
var _setInputRange = function(input, startPos, endPos) {
|
|
||||||
startPos = _getIndex(input, startPos);
|
|
||||||
endPos = _getIndex(input, endPos);
|
|
||||||
|
|
||||||
// Mozilla, et al.
|
|
||||||
if (_support.setSelectionRange) {
|
|
||||||
_setInputRangeW3(input, startPos, endPos);
|
|
||||||
}
|
|
||||||
// IE
|
|
||||||
else if (_support.createTextRange) {
|
|
||||||
_setInputRangeIE(input, startPos, endPos);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replaces the currently selected text with the given string.
|
|
||||||
* @param {HTMLInputElement|HTMLTextAreaElement} input input or textarea element
|
|
||||||
* @param {String} text New text that will replace the currently selected text.
|
|
||||||
* @see http://parentnode.org/javascript/working-with-the-cursor-position/
|
|
||||||
*/
|
|
||||||
var _replaceInputRange = function(input, text) {
|
|
||||||
var $input = $(input);
|
|
||||||
|
|
||||||
var oldValue = $input.val();
|
|
||||||
var selection = _getInputRange(input);
|
|
||||||
|
|
||||||
var newLength = +(selection.start + text.length + (oldValue.length - selection.end));
|
|
||||||
var maxLength = +$input.attr('maxlength');
|
|
||||||
|
|
||||||
if($input.is('[maxlength]') && newLength > maxLength) {
|
|
||||||
var delta = text.length - (newLength - maxLength);
|
|
||||||
text = text.substr(0, delta);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now that we know what the user selected, we can replace it
|
|
||||||
var startText = oldValue.substr(0, selection.start);
|
|
||||||
var endText = oldValue.substr(selection.end);
|
|
||||||
|
|
||||||
$input.val(startText + text + endText);
|
|
||||||
|
|
||||||
// Reset the selection
|
|
||||||
var startPos = selection.start;
|
|
||||||
var endPos = startPos + text.length;
|
|
||||||
|
|
||||||
_setInputRange(input, selection.length ? startPos : endPos, endPos);
|
|
||||||
};
|
|
||||||
|
|
||||||
var _selectAllW3 = function(elem) {
|
|
||||||
var selection = window.getSelection();
|
|
||||||
var range = document.createRange();
|
|
||||||
range.selectNodeContents(elem);
|
|
||||||
selection.removeAllRanges();
|
|
||||||
selection.addRange(range);
|
|
||||||
};
|
|
||||||
|
|
||||||
var _selectAllIE = function(elem) {
|
|
||||||
var range = document.body.createTextRange();
|
|
||||||
range.moveToElementText(elem);
|
|
||||||
range.select();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Select all text in the given element.
|
|
||||||
* @param {HTMLElement} elem Any block or inline element other than a form element.
|
|
||||||
*/
|
|
||||||
var _selectAll = function(elem) {
|
|
||||||
var $elem = $(elem);
|
|
||||||
if ($elem.is('input, textarea') || elem.select) {
|
|
||||||
$elem.select();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mozilla, et al.
|
|
||||||
if (_support.setSelectionRange) {
|
|
||||||
_selectAllW3(elem);
|
|
||||||
}
|
|
||||||
// IE
|
|
||||||
else if (_support.createTextRange) {
|
|
||||||
_selectAllIE(elem);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var _deselectAll = function() {
|
|
||||||
if (document.selection) {
|
|
||||||
document.selection.empty();
|
|
||||||
}
|
|
||||||
else if (window.getSelection) {
|
|
||||||
window.getSelection().removeAllRanges();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$.extend($.fn, {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets or sets the position of the caret or inserts text at the current caret position in an input or textarea element.
|
|
||||||
* @returns {Number|jQuery} The current caret position if invoked as a getter (with no arguments)
|
|
||||||
* or this jQuery object if invoked as a setter or inserter.
|
|
||||||
* @see http://web.archive.org/web/20080704185920/http://parentnode.org/javascript/working-with-the-cursor-position/
|
|
||||||
* @since 1.0.0
|
|
||||||
* @example
|
|
||||||
* <pre>
|
|
||||||
* // Get position
|
|
||||||
* var pos = $('input:first').caret();
|
|
||||||
* </pre>
|
|
||||||
* @example
|
|
||||||
* <pre>
|
|
||||||
* // Set position
|
|
||||||
* $('input:first').caret(15);
|
|
||||||
* $('input:first').caret(-3);
|
|
||||||
* </pre>
|
|
||||||
* @example
|
|
||||||
* <pre>
|
|
||||||
* // Insert text at current position
|
|
||||||
* $('input:first').caret('Some text');
|
|
||||||
* </pre>
|
|
||||||
*/
|
|
||||||
caret: function() {
|
|
||||||
var $inputs = this.filter('input, textarea');
|
|
||||||
|
|
||||||
// getCaret()
|
|
||||||
if (arguments.length === 0) {
|
|
||||||
var input = $inputs.get(0);
|
|
||||||
return _getCaret(input);
|
|
||||||
}
|
|
||||||
// setCaret(position)
|
|
||||||
else if (typeof arguments[0] === 'number') {
|
|
||||||
var pos = arguments[0];
|
|
||||||
$inputs.each(function(_i, input) {
|
|
||||||
_setCaret(input, pos);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// insertAtCaret(text)
|
|
||||||
else {
|
|
||||||
var text = arguments[0];
|
|
||||||
$inputs.each(function(_i, input) {
|
|
||||||
_insertAtCaret(input, text);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets or sets the selection range or replaces the currently selected text in an input or textarea element.
|
|
||||||
* @returns {Range|jQuery} The current selection range if invoked as a getter (with no arguments)
|
|
||||||
* or this jQuery object if invoked as a setter or replacer.
|
|
||||||
* @see http://stackoverflow.com/a/2966703/467582
|
|
||||||
* @since 1.0.0
|
|
||||||
* @example
|
|
||||||
* <pre>
|
|
||||||
* // Get selection range
|
|
||||||
* var range = $('input:first').range();
|
|
||||||
* </pre>
|
|
||||||
* @example
|
|
||||||
* <pre>
|
|
||||||
* // Set selection range
|
|
||||||
* $('input:first').range(15);
|
|
||||||
* $('input:first').range(15, 20);
|
|
||||||
* $('input:first').range(-3);
|
|
||||||
* $('input:first').range(-8, -3);
|
|
||||||
* </pre>
|
|
||||||
* @example
|
|
||||||
* <pre>
|
|
||||||
* // Replace the currently selected text
|
|
||||||
* $('input:first').range('Replacement text');
|
|
||||||
* </pre>
|
|
||||||
*/
|
|
||||||
range: function() {
|
|
||||||
var $inputs = this.filter('input, textarea');
|
|
||||||
|
|
||||||
// getRange() = { start: pos, end: pos }
|
|
||||||
if (arguments.length === 0) {
|
|
||||||
var input = $inputs.get(0);
|
|
||||||
return _getInputRange(input);
|
|
||||||
}
|
|
||||||
// setRange(startPos, endPos)
|
|
||||||
else if (typeof arguments[0] === 'number') {
|
|
||||||
var startPos = arguments[0];
|
|
||||||
var endPos = arguments[1];
|
|
||||||
$inputs.each(function(_i, input) {
|
|
||||||
_setInputRange(input, startPos, endPos);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// replaceRange(text)
|
|
||||||
else {
|
|
||||||
var text = arguments[0];
|
|
||||||
$inputs.each(function(_i, input) {
|
|
||||||
_replaceInputRange(input, text);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Selects all text in each element of this jQuery object.
|
|
||||||
* @returns {jQuery} This jQuery object
|
|
||||||
* @see http://stackoverflow.com/a/11128179/467582
|
|
||||||
* @since 1.5.0
|
|
||||||
* @example
|
|
||||||
* <pre>
|
|
||||||
* // Select the contents of span elements when clicked
|
|
||||||
* $('span').on('click', function() { $(this).highlight(); });
|
|
||||||
* </pre>
|
|
||||||
*/
|
|
||||||
selectAll: function() {
|
|
||||||
return this.each(function(_i, elem) {
|
|
||||||
_selectAll(elem);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
$.extend($, {
|
|
||||||
/**
|
|
||||||
* Deselects all text on the page.
|
|
||||||
* @returns {jQuery} The jQuery function
|
|
||||||
* @since 1.5.0
|
|
||||||
* @example
|
|
||||||
* <pre>
|
|
||||||
* // Select some text
|
|
||||||
* $('span').selectAll();
|
|
||||||
*
|
|
||||||
* // Deselect the text
|
|
||||||
* $.deselectAll();
|
|
||||||
* </pre>
|
|
||||||
*/
|
|
||||||
deselectAll: function() {
|
|
||||||
_deselectAll();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}(window.jQuery || window.Zepto || window.$));
|
|
||||||
57
js/legacy/jquery.ui.timepicker.css
vendored
@@ -1,57 +0,0 @@
|
|||||||
/*
|
|
||||||
* Timepicker stylesheet
|
|
||||||
* Highly inspired from datepicker
|
|
||||||
* FG - Nov 2010 - Web3R
|
|
||||||
*
|
|
||||||
* version 0.0.3 : Fixed some settings, more dynamic
|
|
||||||
* version 0.0.4 : Removed width:100% on tables
|
|
||||||
* version 0.1.1 : set width 0 on tables to fix an ie6 bug
|
|
||||||
*/
|
|
||||||
|
|
||||||
.ui-timepicker-inline { display: inline; }
|
|
||||||
|
|
||||||
#ui-timepicker-div { padding: 0.2em; }
|
|
||||||
.ui-timepicker-table { display: inline-table; width: 0; }
|
|
||||||
.ui-timepicker-table table { margin:0.15em 0 0 0; border-collapse: collapse; }
|
|
||||||
|
|
||||||
.ui-timepicker-hours, .ui-timepicker-minutes { padding: 0.2em; }
|
|
||||||
|
|
||||||
.ui-timepicker-table .ui-timepicker-title { line-height: 1.8em; text-align: center; }
|
|
||||||
.ui-timepicker-table td { padding: 0.1em; width: 2.2em; }
|
|
||||||
.ui-timepicker-table th.periods { padding: 0.1em; width: 2.2em; }
|
|
||||||
|
|
||||||
/* span for disabled cells */
|
|
||||||
.ui-timepicker-table td span {
|
|
||||||
display:block;
|
|
||||||
padding:0.2em 0.3em 0.2em 0.5em;
|
|
||||||
width: 1.2em;
|
|
||||||
|
|
||||||
text-align:right;
|
|
||||||
text-decoration:none;
|
|
||||||
}
|
|
||||||
/* anchors for clickable cells */
|
|
||||||
.ui-timepicker-table td a {
|
|
||||||
display:block;
|
|
||||||
padding:0.2em 0.3em 0.2em 0.5em;
|
|
||||||
width: 1.2em;
|
|
||||||
cursor: pointer;
|
|
||||||
text-align:right;
|
|
||||||
text-decoration:none;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* buttons and button pane styling */
|
|
||||||
.ui-timepicker .ui-timepicker-buttonpane {
|
|
||||||
background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0;
|
|
||||||
}
|
|
||||||
.ui-timepicker .ui-timepicker-buttonpane button { margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
|
|
||||||
/* The close button */
|
|
||||||
.ui-timepicker .ui-timepicker-close { float: right }
|
|
||||||
|
|
||||||
/* the now button */
|
|
||||||
.ui-timepicker .ui-timepicker-now { float: left; }
|
|
||||||
|
|
||||||
/* the deselect button */
|
|
||||||
.ui-timepicker .ui-timepicker-deselect { float: left; }
|
|
||||||
|
|
||||||
|
|
||||||
1496
js/legacy/jquery.ui.timepicker.js
vendored
@@ -1,120 +0,0 @@
|
|||||||
/**
|
|
||||||
* Original source code from https://github.com/mcecot/markdown-it-checkbox
|
|
||||||
* © 2015 Markus Cecot
|
|
||||||
* licenced under MIT
|
|
||||||
* https://github.com/mcecot/markdown-it-checkbox/blob/master/LICENSE
|
|
||||||
*/
|
|
||||||
var checkboxReplace;
|
|
||||||
|
|
||||||
checkboxReplace = function(md, options, Token) {
|
|
||||||
"use strict";
|
|
||||||
var arrayReplaceAt, createTokens, defaults, lastId, pattern, splitTextToken;
|
|
||||||
arrayReplaceAt = md.utils.arrayReplaceAt;
|
|
||||||
lastId = 0;
|
|
||||||
defaults = {
|
|
||||||
divWrap: false,
|
|
||||||
divClass: 'checkbox',
|
|
||||||
idPrefix: 'checkbox'
|
|
||||||
};
|
|
||||||
options = Object.assign(defaults, options);
|
|
||||||
pattern = /(.*?)(\[(X|\s|\_|\-)\])(.*)/igm;
|
|
||||||
createTokens = function(checked, label, Token, before) {
|
|
||||||
var id, idNumeric, nodes, token;
|
|
||||||
nodes = [];
|
|
||||||
|
|
||||||
token = new Token("text", "", 0);
|
|
||||||
token.content = before;
|
|
||||||
nodes.push(token);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <div class="checkbox">
|
|
||||||
*/
|
|
||||||
if (options.divWrap) {
|
|
||||||
token = new Token("checkbox_open", "div", 1);
|
|
||||||
token.attrs = [["class", options.divClass]];
|
|
||||||
nodes.push(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <input type="checkbox" id="checkbox{n}" checked="true">
|
|
||||||
*/
|
|
||||||
id = options.idPrefix + lastId;
|
|
||||||
idNumeric = lastId;
|
|
||||||
lastId += 1;
|
|
||||||
token = new Token("checkbox_input", "input", 0);
|
|
||||||
token.attrs = [["type", "checkbox"], ["id", id], ["data-id", idNumeric]];
|
|
||||||
if (checked === true) {
|
|
||||||
token.attrs.push(["checked", "true"]);
|
|
||||||
}
|
|
||||||
token.attrs.push(["class", "checkbox"]);
|
|
||||||
nodes.push(token);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <label for="checkbox{n}">
|
|
||||||
*/
|
|
||||||
token = new Token("label_open", "label", 1);
|
|
||||||
token.attrs = [["for", id], ["data-id", idNumeric]];
|
|
||||||
nodes.push(token);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* content of label tag
|
|
||||||
*/
|
|
||||||
token = new Token("text", "", 0);
|
|
||||||
token.content = label;
|
|
||||||
nodes.push(token);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* closing tags
|
|
||||||
*/
|
|
||||||
nodes.push(new Token("label_close", "label", -1));
|
|
||||||
if (options.divWrap) {
|
|
||||||
nodes.push(new Token("checkbox_close", "div", -1));
|
|
||||||
}
|
|
||||||
return nodes;
|
|
||||||
};
|
|
||||||
splitTextToken = function(original, Token) {
|
|
||||||
var checked, label, matches, text, value, before;
|
|
||||||
text = original.content;
|
|
||||||
matches = pattern.exec(text);
|
|
||||||
if (matches === null) {
|
|
||||||
return original;
|
|
||||||
}
|
|
||||||
checked = false;
|
|
||||||
before = matches[1];
|
|
||||||
value = matches[3];
|
|
||||||
label = matches[4];
|
|
||||||
if (value === "X" || value === "x") {
|
|
||||||
checked = true;
|
|
||||||
}
|
|
||||||
return createTokens(checked, label, Token, before);
|
|
||||||
};
|
|
||||||
return function(state) {
|
|
||||||
lastId = 0;
|
|
||||||
var blockTokens, i, j, l, token, tokens;
|
|
||||||
blockTokens = state.tokens;
|
|
||||||
j = 0;
|
|
||||||
l = blockTokens.length;
|
|
||||||
while (j < l) {
|
|
||||||
if (blockTokens[j].type !== "inline") {
|
|
||||||
j++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
tokens = blockTokens[j].children;
|
|
||||||
i = 0;
|
|
||||||
while (i < tokens.length) {
|
|
||||||
token = tokens[i];
|
|
||||||
blockTokens[j].children = tokens = arrayReplaceAt(tokens, i, splitTextToken(token, state.Token));
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*global module */
|
|
||||||
|
|
||||||
module.exports = function(md, options) {
|
|
||||||
"use strict";
|
|
||||||
md.core.ruler.push("checkbox", checkboxReplace(md, options));
|
|
||||||
};
|
|
||||||