Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b4e7ec2bf | ||
|
|
111a98ce88 | ||
|
|
7fb0d2374d | ||
|
|
491a73456b | ||
|
|
62d671bdd5 | ||
|
|
955c5eb864 | ||
|
|
49cd271cbc | ||
|
|
37bdbecdc2 | ||
|
|
62af080e78 | ||
|
|
84d4b87bb8 | ||
|
|
5ad515f42a | ||
|
|
98b57fe0a4 | ||
|
|
b961206da3 | ||
|
|
d8d8137300 | ||
|
|
56fcb081a4 | ||
|
|
2b9b22f2f2 | ||
|
|
084d892ce6 | ||
|
|
11610f306a | ||
|
|
60e9cf7088 | ||
|
|
7e345256cb | ||
|
|
52a7b8d560 | ||
|
|
d98db33a02 | ||
|
|
04f5e2c2e1 | ||
|
|
74c8a6e848 | ||
|
|
02eecb3a3f | ||
|
|
1e9c86e158 | ||
|
|
2b05227f4b | ||
|
|
3e304a9ff2 | ||
|
|
000e447801 | ||
|
|
1b6531e748 | ||
|
|
e2012dd2c5 | ||
|
|
5346cc4042 | ||
|
|
22438f8f3e | ||
|
|
6033baca23 | ||
|
|
c1ff005710 | ||
|
|
05adda1e03 | ||
|
|
6ae6bdf74a | ||
|
|
35ea7255cb | ||
|
|
c80deaaaee | ||
|
|
ab60f4e3ab | ||
|
|
965708e784 | ||
|
|
1b364b4f85 | ||
|
|
aad1cb4fb5 | ||
|
|
99725765fc | ||
|
|
f313a9ba6b | ||
|
|
6bbe0a3161 | ||
|
|
4414fdebd4 |
24
.drone.yml
24
.drone.yml
@@ -125,10 +125,29 @@ pipeline:
|
||||
- cd ../server/
|
||||
- php occ app:enable deck
|
||||
- cd apps/$APP_NAME
|
||||
- make test
|
||||
- phpunit -c tests/phpunit.xml --coverage-clover build/php-unit.coverage.xml
|
||||
- phpunit -c tests/phpunit.integration.xml --coverage-clover build/php-integration.coverage.xml
|
||||
when:
|
||||
matrix:
|
||||
TESTS: php7.1
|
||||
integration:
|
||||
image: nextcloudci/integration-php7.0:integration-php7.0-3
|
||||
environment:
|
||||
- APP_NAME=deck
|
||||
- CORE_BRANCH=master
|
||||
- DB=sqlite
|
||||
commands:
|
||||
# Pre-setup steps
|
||||
- wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh
|
||||
- bash ./before_install.sh $APP_NAME $CORE_BRANCH $DB
|
||||
- cd ../server/
|
||||
- php occ app:enable deck
|
||||
- cd apps/$APP_NAME
|
||||
- cd tests/integration
|
||||
- ./run.sh
|
||||
when:
|
||||
matrix:
|
||||
TESTS: integration
|
||||
jsbuild:
|
||||
image: mhart/alpine-node:6.8.0
|
||||
commands:
|
||||
@@ -149,3 +168,6 @@ matrix:
|
||||
- TESTS: php7.0
|
||||
- TESTS: php7.1
|
||||
- TESTS: jsbuild
|
||||
- TESTS: integration
|
||||
|
||||
branches: [ master, stable* ]
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -2,3 +2,5 @@ js/node_modules/*
|
||||
js/vendor/
|
||||
build/
|
||||
js/public/
|
||||
tests/integration/vendor/
|
||||
tests/integration/composer.lock
|
||||
|
||||
@@ -26,7 +26,7 @@ before_script:
|
||||
- cd apps/deck
|
||||
|
||||
script:
|
||||
- make test
|
||||
- make test-unit
|
||||
|
||||
after_failure:
|
||||
- cat ../../data/nextcloud.log
|
||||
|
||||
16
CHANGELOG.md
16
CHANGELOG.md
@@ -1,3 +1,19 @@
|
||||
## 0.1.3 - 2017-05-01
|
||||
|
||||
### Added
|
||||
|
||||
- Icon to show if a card has a description
|
||||
|
||||
### Changed
|
||||
|
||||
- Use OCS API to get users/groups for sharing
|
||||
- Various UI improvements
|
||||
- Show display name instead of uid
|
||||
- Fix bugs with limited field length
|
||||
- Automatically hide sidebar when clicking the board view
|
||||
- Start editing from everywhere in the description section
|
||||
|
||||
|
||||
## 0.1.2
|
||||
### Added
|
||||
- Add translations
|
||||
|
||||
12
Makefile
12
Makefile
@@ -23,7 +23,7 @@ clean-dist:
|
||||
|
||||
install-deps:
|
||||
cd js && npm install --deps
|
||||
cd js && bower install
|
||||
cd js && ./node_modules/.bin/bower install
|
||||
|
||||
build: build-js
|
||||
|
||||
@@ -69,8 +69,9 @@ appstore: clean-build build
|
||||
|
||||
echo $(appstore_package_name).tar.gz
|
||||
|
||||
test: test-unit test-integration
|
||||
|
||||
test:
|
||||
test-unit:
|
||||
mkdir -p build/
|
||||
ifeq (, $(shell which phpunit 2> /dev/null))
|
||||
@echo "No phpunit command available, downloading a copy from the web"
|
||||
@@ -83,6 +84,9 @@ else
|
||||
phpunit -c tests/phpunit.integration.xml --coverage-clover build/php-integration.coverage.xml
|
||||
endif
|
||||
|
||||
test-js:
|
||||
cd js && run test
|
||||
test-integration:
|
||||
cd tests/integration && ./run.sh
|
||||
|
||||
test-js: install-deps
|
||||
cd js && run test
|
||||
|
||||
|
||||
16
README.md
16
README.md
@@ -13,7 +13,7 @@ Deck is a kanban style organization tool aimed at personal planning and project
|
||||
|
||||

|
||||
|
||||
:boom: This is still alpha software: it may not be stable enough for production
|
||||
:boom: This is still alpha software: it may not be stable enough for production
|
||||
|
||||
### Planned features
|
||||
|
||||
@@ -31,7 +31,7 @@ This app is supposed to work on Nextcloud version 11 or later.
|
||||
|
||||
You can download and install the latest release from the [Nextcloud app store](https://apps.nextcloud.com/apps/deck)
|
||||
|
||||
### Install from git
|
||||
### Install from git
|
||||
|
||||
If you want to run the latest development version from git source, you need to clone the repo to your apps folder:
|
||||
|
||||
@@ -44,6 +44,10 @@ make
|
||||
|
||||
Please make sure you have installed the following dependencies: `make, which, tar, npm, curl`
|
||||
|
||||
### Install the nightly builds
|
||||
|
||||
Instead of setting everything up manually, you can just [download the nightly builds](https://download.bitgrid.net/nextcloud/deck/nightly/) instead. These builds are updated every 24 hours, and are pre-configured with all the needed dependencies.
|
||||
|
||||
## Developing
|
||||
|
||||
### PHP
|
||||
@@ -59,9 +63,9 @@ Make sure you have installed the dependencies with ```make install-deps```.
|
||||
You can use the provided Makefile to run all tests by using:
|
||||
|
||||
make test
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 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.
|
||||
@@ -73,7 +77,7 @@ For more information please review the [guidelines for contributing](https://git
|
||||
All contributions to this repository are considered to be licensed under
|
||||
the GNU AGPLv3 or any later version.
|
||||
|
||||
Contributors to the Spreed app retain their copyright. Therefore we recommend
|
||||
Contributors to the Deck app retain their copyright. Therefore we recommend
|
||||
to add following line to the header of a file, if you changed it substantially:
|
||||
|
||||
```
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<name>title</name>
|
||||
<type>text</type>
|
||||
<notnull>true</notnull>
|
||||
<length>64</length>
|
||||
<length>100</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>owner</name>
|
||||
@@ -55,7 +55,7 @@
|
||||
<name>title</name>
|
||||
<type>text</type>
|
||||
<notnull>true</notnull>
|
||||
<length>64</length>
|
||||
<length>100</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>board_id</name>
|
||||
@@ -97,14 +97,13 @@
|
||||
<field>
|
||||
<name>title</name>
|
||||
<type>text</type>
|
||||
<length>100</length>
|
||||
<notnull>true</notnull>
|
||||
<length>64</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>description</name>
|
||||
<type>text</type>
|
||||
<type>clob</type>
|
||||
<notnull>false</notnull>
|
||||
<length>4096</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>stack_id</name>
|
||||
@@ -187,7 +186,7 @@
|
||||
<name>title</name>
|
||||
<type>text</type>
|
||||
<notnull>true</notnull>
|
||||
<length>64</length>
|
||||
<length>100</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>card_id</name>
|
||||
@@ -239,7 +238,7 @@
|
||||
<name>title</name>
|
||||
<type>text</type>
|
||||
<notnull>false</notnull>
|
||||
<length>64</length>
|
||||
<length>100</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>color</name>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
💥 This is still alpha software: it may not be stable enough for production!
|
||||
|
||||
</description>
|
||||
<version>0.1.2</version>
|
||||
<version>0.1.3</version>
|
||||
<licence>agpl</licence>
|
||||
<author>Julius Härtl</author>
|
||||
<namespace>Deck</namespace>
|
||||
|
||||
@@ -25,9 +25,6 @@ return [
|
||||
'routes' => [
|
||||
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
|
||||
|
||||
// share
|
||||
['name' => 'share#searchUser', 'url' => '/share/search/{search}', 'verb' => 'GET'],
|
||||
|
||||
// boards
|
||||
['name' => 'board#index', 'url' => '/boards', 'verb' => 'GET'],
|
||||
['name' => 'board#create', 'url' => '/boards', 'verb' => 'POST'],
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
* @author Artem Anufrij <artem.anufrij@live.de>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
@@ -134,7 +135,6 @@ button:hover {
|
||||
position: relative;
|
||||
color: #888;
|
||||
margin: 8px;
|
||||
|
||||
}
|
||||
|
||||
#board-actions .filter .filter-button {
|
||||
@@ -176,7 +176,6 @@ button:hover {
|
||||
.board-actions button {
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.board-action-button {
|
||||
@@ -186,6 +185,28 @@ button:hover {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
#stack-add {
|
||||
float: right;
|
||||
margin: 8px;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #bbb;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
#stack-add input {
|
||||
padding: 4px 5px;
|
||||
margin: 0px;
|
||||
border: 0px none transparent;
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
min-height: initial;
|
||||
}
|
||||
|
||||
#stack-add button {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.stack {
|
||||
width: 320px;
|
||||
margin-right: 10px;
|
||||
@@ -397,7 +418,6 @@ button:hover {
|
||||
*/
|
||||
|
||||
#card-header {
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
#card-header h2 {
|
||||
@@ -415,7 +435,9 @@ button:hover {
|
||||
}
|
||||
|
||||
#card-meta {
|
||||
padding-top: 0;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#card-dates {
|
||||
@@ -431,6 +453,12 @@ button:hover {
|
||||
#card-dates span {
|
||||
}
|
||||
|
||||
#card-description {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#card-description > h3 {
|
||||
border-bottom: 1px solid #333333;
|
||||
font-weight: 600;
|
||||
@@ -438,14 +466,8 @@ button:hover {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.save-indicator {
|
||||
background-color: #009900;
|
||||
color: #ffffff;
|
||||
border-radius: 3px;
|
||||
float: right;
|
||||
padding: 1px 10px;
|
||||
font-size: 8pt !important;
|
||||
display: none;
|
||||
#card-description > div {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#card-description textarea {
|
||||
@@ -460,6 +482,16 @@ button:hover {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.save-indicator {
|
||||
background-color: #009900;
|
||||
color: #ffffff;
|
||||
border-radius: 3px;
|
||||
float: right;
|
||||
padding: 1px 10px;
|
||||
font-size: 8pt !important;
|
||||
display: none;
|
||||
}
|
||||
|
||||
#sidebar-header,
|
||||
.card-block {
|
||||
padding: 15px;
|
||||
@@ -485,6 +517,8 @@ button:hover {
|
||||
border-left: none;
|
||||
width: 500px;
|
||||
border-left: 1px solid #eeeeee;
|
||||
display:flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#app-sidebar.details-visible {
|
||||
@@ -866,3 +900,16 @@ button:hover {
|
||||
.icon-details-white {
|
||||
background-image: url('../img/details-white.svg');
|
||||
}
|
||||
|
||||
.icon-description-marker {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 2px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hotfix for https://github.com/angular-ui/ui-select/issues/1652
|
||||
*/
|
||||
.ui-select-dropdown.select2-drop-active {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
### Steps to reproduce
|
||||
|
||||
1.
|
||||
2.
|
||||
2.
|
||||
3.
|
||||
|
||||
### Expected behaviour
|
||||
@@ -11,6 +10,10 @@ Tell us what should happen
|
||||
Tell us what happens instead
|
||||
|
||||
### Server configuration
|
||||
<!--
|
||||
You can use the Issue Template application to prefill most of the required information: https://apps.nextcloud.com/apps/issuetemplate
|
||||
-->
|
||||
|
||||
**Operating system**:
|
||||
|
||||
**Web server:**
|
||||
@@ -19,17 +22,15 @@ Tell us what happens instead
|
||||
|
||||
**PHP version:**
|
||||
|
||||
**Server version:** (see your admin page)
|
||||
**Nextcloud version:** (see Nextcloud admin page)
|
||||
|
||||
**Deck version:** (see the apps page)
|
||||
|
||||
**Updated from an older installed version or fresh install:**
|
||||
**Where did you install Nextcloud from:**
|
||||
|
||||
**Signing status:**
|
||||
|
||||
```
|
||||
Login as admin user into your cloud and access
|
||||
http://example.com/index.php/settings/integrity/failed
|
||||
Login as admin user into your Nextcloud and access
|
||||
http://example.com/index.php/settings/integrity/failed
|
||||
paste the results here.
|
||||
```
|
||||
|
||||
@@ -38,57 +39,34 @@ paste the results here.
|
||||
```
|
||||
If you have access to your command line run e.g.:
|
||||
sudo -u www-data php occ app:list
|
||||
from within your instance's installation folder
|
||||
from within your Nextcloud installation folder
|
||||
```
|
||||
|
||||
**The content of config/config.php:**
|
||||
**Nextcloud configuration:**
|
||||
|
||||
```
|
||||
If you have access to your command line run e.g.:
|
||||
sudo -u www-data php occ config:list system
|
||||
from within your instance's installation folder
|
||||
from within your Nextcloud installation folder
|
||||
|
||||
or
|
||||
or
|
||||
|
||||
Insert your config.php content here
|
||||
(Without the database password, passwordsalt and secret)
|
||||
Make sure to remove all sensitive content such as passwords. (e.g. database password, passwordsalt, secret, smtp password, …)
|
||||
```
|
||||
|
||||
**Are you using external storage, if yes which one:** local/smb/sftp/...
|
||||
|
||||
**Are you using encryption:** yes/no
|
||||
|
||||
**Are you using an external user-backend, if yes which one:** LDAP/ActiveDirectory/Webdav/...
|
||||
|
||||
#### LDAP configuration (delete this part if not used)
|
||||
|
||||
```
|
||||
With access to your command line run e.g.:
|
||||
sudo -u www-data php occ ldap:show-config
|
||||
from within your instance's installation folder
|
||||
|
||||
Without access to your command line download the data/owncloud.db to your local
|
||||
computer or access your SQL server remotely and run the select query:
|
||||
SELECT * FROM `oc_appconfig` WHERE `appid` = 'user_ldap';
|
||||
|
||||
|
||||
Eventually replace sensitive data as the name/IP-address of your LDAP server or groups.
|
||||
```
|
||||
|
||||
### Client configuration
|
||||
**Browser:**
|
||||
|
||||
**Operating system:**
|
||||
|
||||
### Logs
|
||||
#### Web server error log
|
||||
```
|
||||
Insert your webserver log here
|
||||
```
|
||||
|
||||
#### Log file (data/nextcloud.log)
|
||||
#### Nextcloud log (data/nextcloud.log)
|
||||
```
|
||||
Insert your nextcloud.log file here
|
||||
Insert your Nextcloud log here
|
||||
```
|
||||
|
||||
#### Browser log
|
||||
@@ -96,6 +74,6 @@ Insert your nextcloud.log file here
|
||||
Insert your browser log here, this could for example include:
|
||||
|
||||
a) The javascript console log
|
||||
b) The network log
|
||||
b) The network log
|
||||
c) ...
|
||||
```
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
build:
|
||||
grunt build
|
||||
./node_modules/.bin/grunt build
|
||||
|
||||
watch:
|
||||
grunt watch
|
||||
./node_modules/.bin/grunt watch
|
||||
|
||||
install:
|
||||
npm install
|
||||
bower install
|
||||
./node_modules/.bin/bower install
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
"angular-mocks": "~1.6.1",
|
||||
"angular-sanitize": "~1.6.1",
|
||||
"angular-animate": "~1.6.1",
|
||||
"ng-sortable": "1.3.6",
|
||||
"jquery": "3.1.x",
|
||||
"ng-sortable": "1.3.8",
|
||||
"jquery": "3.2.x",
|
||||
"es6-shim": "~0.*",
|
||||
"js-url": "~2.*",
|
||||
"angular-ui-select": "~0.19.6",
|
||||
|
||||
@@ -119,12 +119,7 @@ app.controller('BoardController', function ($rootScope, $scope, $stateParams, St
|
||||
$scope.statusservice.setError('Error occured', error);
|
||||
});
|
||||
|
||||
BoardService.searchUsers('%25');
|
||||
|
||||
$scope.searchForUser = function (search) {
|
||||
if (search == "") {
|
||||
search = "%25";
|
||||
}
|
||||
BoardService.searchUsers(search);
|
||||
};
|
||||
|
||||
|
||||
@@ -26,50 +26,101 @@ app.factory('BoardService', function(ApiService, $http, $q){
|
||||
};
|
||||
BoardService.prototype = angular.copy(ApiService.prototype);
|
||||
|
||||
BoardService.prototype.searchUsers = function(search) {
|
||||
var url = OC.generateUrl('/apps/deck/share/search/'+search);
|
||||
var deferred = $q.defer();
|
||||
var self = this;
|
||||
$http.get(url).then(function (response) {
|
||||
BoardService.prototype.searchUsers = function (search) {
|
||||
var deferred = $q.defer();
|
||||
var self = this;
|
||||
var searchData = {
|
||||
format: 'json',
|
||||
perPage: 4,
|
||||
itemType: [0, 1]
|
||||
};
|
||||
if (search !== "") {
|
||||
searchData.search = search;
|
||||
}
|
||||
$http({
|
||||
method: 'GET',
|
||||
url: OC.linkToOCS('apps/files_sharing/api/v1') + 'sharees',
|
||||
params: searchData
|
||||
})
|
||||
.then(function (result) {
|
||||
var response = result.data;
|
||||
if (response.ocs.meta.statuscode !== 100) {
|
||||
deferred.reject('Error while searching for sharees');
|
||||
return;
|
||||
}
|
||||
self.sharees = [];
|
||||
|
||||
self.sharees = [];
|
||||
// filter out everyone who is already in the share list
|
||||
angular.forEach(response.data, function(item) {
|
||||
var exists = false;
|
||||
angular.forEach(self.getCurrent().acl, function(acl) {
|
||||
if (acl.participant === item.participant) {
|
||||
exists = true;
|
||||
}
|
||||
});
|
||||
if(!exists) {
|
||||
self.sharees.push(item);
|
||||
}
|
||||
});
|
||||
var users = response.ocs.data.exact.users.concat(response.ocs.data.users);
|
||||
var groups = response.ocs.data.exact.groups.concat(response.ocs.data.groups);
|
||||
|
||||
deferred.resolve(response.data);
|
||||
}, function (error) {
|
||||
deferred.reject('Error while update ' + self.endpoint);
|
||||
});
|
||||
return deferred.promise;
|
||||
};
|
||||
// filter out everyone who is already in the share list
|
||||
angular.forEach(users, function (item) {
|
||||
var acl = self.generateAcl('user', item);
|
||||
var exists = false;
|
||||
angular.forEach(self.getCurrent().acl, function (acl) {
|
||||
if (acl.participant.primaryKey === item.value.shareWith) {
|
||||
exists = true;
|
||||
}
|
||||
});
|
||||
if (!exists && OC.getCurrentUser().uid !== item.value.shareWith) {
|
||||
self.sharees.push(acl);
|
||||
}
|
||||
});
|
||||
angular.forEach(groups, function (item) {
|
||||
var acl = self.generateAcl('group', item);
|
||||
var exists = false;
|
||||
angular.forEach(self.getCurrent().acl, function (acl) {
|
||||
if (acl.participant.primaryKey === item.value.shareWith) {
|
||||
exists = true;
|
||||
}
|
||||
});
|
||||
if (!exists) {
|
||||
self.sharees.push(acl);
|
||||
}
|
||||
});
|
||||
|
||||
BoardService.prototype.addAcl = function(acl) {
|
||||
var board = this.getCurrent();
|
||||
var deferred = $q.defer();
|
||||
var self = this;
|
||||
var _acl = acl;
|
||||
$http.post(this.baseUrl + '/' + acl.boardId + '/acl', _acl).then(function (response) {
|
||||
if(!board.acl) {
|
||||
board.acl = {};
|
||||
}
|
||||
board.acl[response.data.id] = response.data;
|
||||
deferred.resolve(response.data);
|
||||
}, function (error) {
|
||||
deferred.reject('Error creating ACL ' + _acl);
|
||||
});
|
||||
acl = null;
|
||||
return deferred.promise;
|
||||
};
|
||||
deferred.resolve(self.sharees);
|
||||
}, function () {
|
||||
deferred.reject('Error while searching for sharees');
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
BoardService.prototype.generateAcl = function(type, ocsItem) {
|
||||
return {
|
||||
boardId: null,
|
||||
id: null,
|
||||
owner: false,
|
||||
participant: {
|
||||
primaryKey: ocsItem.value.shareWith,
|
||||
uid: ocsItem.value.shareWith,
|
||||
displayname: ocsItem.label
|
||||
},
|
||||
permissionEdit: true,
|
||||
permissionManage: true,
|
||||
permissionShare: true,
|
||||
type: type
|
||||
}
|
||||
};
|
||||
|
||||
BoardService.prototype.addAcl = function (acl) {
|
||||
var board = this.getCurrent();
|
||||
var deferred = $q.defer();
|
||||
var self = this;
|
||||
var _acl = acl;
|
||||
$http.post(this.baseUrl + '/' + acl.boardId + '/acl', _acl).then(function (response) {
|
||||
if (!board.acl || board.acl.length === 0) {
|
||||
board.acl = {};
|
||||
}
|
||||
board.acl[response.data.id] = response.data;
|
||||
deferred.resolve(response.data);
|
||||
}, function (error) {
|
||||
deferred.reject('Error creating ACL ' + _acl);
|
||||
});
|
||||
acl = null;
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
BoardService.prototype.deleteAcl = function(acl) {
|
||||
var board = this.getCurrent();
|
||||
@@ -105,7 +156,6 @@ app.factory('BoardService', function(ApiService, $http, $q){
|
||||
var deferred = $q.defer();
|
||||
$http.get(this.baseUrl + '/' + board.id + '/permissions').then(function (response) {
|
||||
board.permissions = response.data;
|
||||
console.log(board.permissions);
|
||||
deferred.resolve(response.data);
|
||||
}, function (error) {
|
||||
deferred.reject('Error fetching board permissions ' + board);
|
||||
|
||||
26
l10n/ca.js
Normal file
26
l10n/ca.js
Normal file
@@ -0,0 +1,26 @@
|
||||
OC.L10N.register(
|
||||
"deck",
|
||||
{
|
||||
"Finished" : "Acabat",
|
||||
"To review" : "Per revisar",
|
||||
"Action needed" : "Acció necessaria",
|
||||
"Later" : "Més tard",
|
||||
"Archive" : "Arxiu",
|
||||
"Delete" : "Esborra",
|
||||
"Sharing" : "Compartir",
|
||||
"Labels" : "Etiqueta",
|
||||
"Share" : "Comparteix",
|
||||
"Edit" : "Edita",
|
||||
"Create a new label" : "Crea una nova etiqueta",
|
||||
"Members" : "Membres",
|
||||
"Modified:" : "Modificat:",
|
||||
"Created:" : "Creat:",
|
||||
"by" : "per",
|
||||
"Saved" : "Desat",
|
||||
"Comments" : "Comentaris",
|
||||
"History" : "Història",
|
||||
"Shared with you" : "Us han compartit",
|
||||
"edit" : "edita",
|
||||
"delete" : "esborra"
|
||||
},
|
||||
"nplurals=2; plural=(n != 1);");
|
||||
24
l10n/ca.json
Normal file
24
l10n/ca.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{ "translations": {
|
||||
"Finished" : "Acabat",
|
||||
"To review" : "Per revisar",
|
||||
"Action needed" : "Acció necessaria",
|
||||
"Later" : "Més tard",
|
||||
"Archive" : "Arxiu",
|
||||
"Delete" : "Esborra",
|
||||
"Sharing" : "Compartir",
|
||||
"Labels" : "Etiqueta",
|
||||
"Share" : "Comparteix",
|
||||
"Edit" : "Edita",
|
||||
"Create a new label" : "Crea una nova etiqueta",
|
||||
"Members" : "Membres",
|
||||
"Modified:" : "Modificat:",
|
||||
"Created:" : "Creat:",
|
||||
"by" : "per",
|
||||
"Saved" : "Desat",
|
||||
"Comments" : "Comentaris",
|
||||
"History" : "Història",
|
||||
"Shared with you" : "Us han compartit",
|
||||
"edit" : "edita",
|
||||
"delete" : "esborra"
|
||||
},"pluralForm" :"nplurals=2; plural=(n != 1);"
|
||||
}
|
||||
30
l10n/el.js
Normal file
30
l10n/el.js
Normal file
@@ -0,0 +1,30 @@
|
||||
OC.L10N.register(
|
||||
"deck",
|
||||
{
|
||||
"Finished" : "Ολοκληρώθηκε",
|
||||
"To review" : "Προς επισκόπηση",
|
||||
"Action needed" : "Απαιτείται ενέργεια",
|
||||
"Later" : "Αργότερα",
|
||||
"Show archived cards" : "Εμφάνιση καρτελών που αρχειοθετήθηκαν",
|
||||
"Hide archived cards" : "Απόκρυψη καρτελών που αρχειοθετήθηκαν",
|
||||
"Archive" : "Αρχειοθέτηση",
|
||||
"Unarchive" : "Αναίρεση αρχειοθέτησης",
|
||||
"Delete" : "Διαγραφή",
|
||||
"Labels" : "Ετικέτες",
|
||||
"Share" : "Διαμοιρασμός",
|
||||
"Edit" : "Επεξεργασία",
|
||||
"Manage" : "Διαχείριση",
|
||||
"Create a new label" : "Δημιουργία νέας ετικέτας",
|
||||
"Members" : "Μέλη",
|
||||
"Modified:" : "Τροποποιήθηκε:",
|
||||
"Created:" : "Δημιουργήθηκε:",
|
||||
"by" : "από",
|
||||
"Saved" : "Αποθηκεύτηκε",
|
||||
"Add a card description ..." : "Προσθήκη περιγραφής καρτέλας ...",
|
||||
"Attachments" : "Συνημμένα",
|
||||
"Comments" : "Σχόλια",
|
||||
"History" : "Ιστορικό",
|
||||
"edit" : "επεξεργασία",
|
||||
"delete" : "διαγραφή"
|
||||
},
|
||||
"nplurals=2; plural=(n != 1);");
|
||||
28
l10n/el.json
Normal file
28
l10n/el.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{ "translations": {
|
||||
"Finished" : "Ολοκληρώθηκε",
|
||||
"To review" : "Προς επισκόπηση",
|
||||
"Action needed" : "Απαιτείται ενέργεια",
|
||||
"Later" : "Αργότερα",
|
||||
"Show archived cards" : "Εμφάνιση καρτελών που αρχειοθετήθηκαν",
|
||||
"Hide archived cards" : "Απόκρυψη καρτελών που αρχειοθετήθηκαν",
|
||||
"Archive" : "Αρχειοθέτηση",
|
||||
"Unarchive" : "Αναίρεση αρχειοθέτησης",
|
||||
"Delete" : "Διαγραφή",
|
||||
"Labels" : "Ετικέτες",
|
||||
"Share" : "Διαμοιρασμός",
|
||||
"Edit" : "Επεξεργασία",
|
||||
"Manage" : "Διαχείριση",
|
||||
"Create a new label" : "Δημιουργία νέας ετικέτας",
|
||||
"Members" : "Μέλη",
|
||||
"Modified:" : "Τροποποιήθηκε:",
|
||||
"Created:" : "Δημιουργήθηκε:",
|
||||
"by" : "από",
|
||||
"Saved" : "Αποθηκεύτηκε",
|
||||
"Add a card description ..." : "Προσθήκη περιγραφής καρτέλας ...",
|
||||
"Attachments" : "Συνημμένα",
|
||||
"Comments" : "Σχόλια",
|
||||
"History" : "Ιστορικό",
|
||||
"edit" : "επεξεργασία",
|
||||
"delete" : "διαγραφή"
|
||||
},"pluralForm" :"nplurals=2; plural=(n != 1);"
|
||||
}
|
||||
44
l10n/es_MX.js
Normal file
44
l10n/es_MX.js
Normal file
@@ -0,0 +1,44 @@
|
||||
OC.L10N.register(
|
||||
"deck",
|
||||
{
|
||||
"Deck" : "Deck",
|
||||
"Finished" : "Terminado",
|
||||
"To review" : "Para revisar",
|
||||
"Action needed" : "Acción requerida",
|
||||
"Later" : "Después",
|
||||
"Show archived cards" : "Mostrar tarjetas archivadas",
|
||||
"Hide archived cards" : "Ocultar tarjetas archivadas",
|
||||
"Board details" : "Detalles del tablero",
|
||||
"Archive" : "Archivar",
|
||||
"Unarchive" : "Desarchivar",
|
||||
"Delete" : "Borrar",
|
||||
"Enter a card title" : "Ingrese el títilo de la tarjeta",
|
||||
"Sharing" : "Compartiendo",
|
||||
"Labels" : "Etiquetas",
|
||||
"Select users or groups to share with" : "Seleccione los usuarios o grupos con los cuales compartir",
|
||||
"No matching user or group found." : "No se encontraron coincidencias de usuarios o grupos.",
|
||||
"Share" : "Compartir",
|
||||
"Edit" : "Editar",
|
||||
"Manage" : "Administrar",
|
||||
"Discard share" : "Descartar elemento compartido",
|
||||
"Create a new label" : "Crear nueva etiqueta",
|
||||
"Board title" : "Título del tablero",
|
||||
"Members" : "Miembros",
|
||||
"Create new board" : "Crear un nuevo tablero",
|
||||
"New board title" : "Nuevo título de tablero",
|
||||
"Modified:" : "Modificado:",
|
||||
"Created:" : "Creado:",
|
||||
"by" : "por",
|
||||
"Saved" : "Guardado",
|
||||
"Add a card description ..." : "Agregar una descripción de la tarjeta ...",
|
||||
"Attachments" : "Adjuntos",
|
||||
"Comments" : "Comentarios",
|
||||
"History" : "Histórico",
|
||||
"All Boards" : "Todos los Tablero",
|
||||
"Shared with you" : "Compartido con usted",
|
||||
"edit" : "editar",
|
||||
"delete" : "borrar",
|
||||
"remove share" : "eliminar elemento compartido",
|
||||
"Create a new board" : "Crear nuevo tablero"
|
||||
},
|
||||
"nplurals=2; plural=(n != 1);");
|
||||
42
l10n/es_MX.json
Normal file
42
l10n/es_MX.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{ "translations": {
|
||||
"Deck" : "Deck",
|
||||
"Finished" : "Terminado",
|
||||
"To review" : "Para revisar",
|
||||
"Action needed" : "Acción requerida",
|
||||
"Later" : "Después",
|
||||
"Show archived cards" : "Mostrar tarjetas archivadas",
|
||||
"Hide archived cards" : "Ocultar tarjetas archivadas",
|
||||
"Board details" : "Detalles del tablero",
|
||||
"Archive" : "Archivar",
|
||||
"Unarchive" : "Desarchivar",
|
||||
"Delete" : "Borrar",
|
||||
"Enter a card title" : "Ingrese el títilo de la tarjeta",
|
||||
"Sharing" : "Compartiendo",
|
||||
"Labels" : "Etiquetas",
|
||||
"Select users or groups to share with" : "Seleccione los usuarios o grupos con los cuales compartir",
|
||||
"No matching user or group found." : "No se encontraron coincidencias de usuarios o grupos.",
|
||||
"Share" : "Compartir",
|
||||
"Edit" : "Editar",
|
||||
"Manage" : "Administrar",
|
||||
"Discard share" : "Descartar elemento compartido",
|
||||
"Create a new label" : "Crear nueva etiqueta",
|
||||
"Board title" : "Título del tablero",
|
||||
"Members" : "Miembros",
|
||||
"Create new board" : "Crear un nuevo tablero",
|
||||
"New board title" : "Nuevo título de tablero",
|
||||
"Modified:" : "Modificado:",
|
||||
"Created:" : "Creado:",
|
||||
"by" : "por",
|
||||
"Saved" : "Guardado",
|
||||
"Add a card description ..." : "Agregar una descripción de la tarjeta ...",
|
||||
"Attachments" : "Adjuntos",
|
||||
"Comments" : "Comentarios",
|
||||
"History" : "Histórico",
|
||||
"All Boards" : "Todos los Tablero",
|
||||
"Shared with you" : "Compartido con usted",
|
||||
"edit" : "editar",
|
||||
"delete" : "borrar",
|
||||
"remove share" : "eliminar elemento compartido",
|
||||
"Create a new board" : "Crear nuevo tablero"
|
||||
},"pluralForm" :"nplurals=2; plural=(n != 1);"
|
||||
}
|
||||
@@ -2,6 +2,7 @@ OC.L10N.register(
|
||||
"deck",
|
||||
{
|
||||
"Finished" : "Kész",
|
||||
"Action needed" : "Művelet szükséges",
|
||||
"Later" : "Később",
|
||||
"Delete" : "Törlés",
|
||||
"Sharing" : "Megosztás",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{ "translations": {
|
||||
"Finished" : "Kész",
|
||||
"Action needed" : "Művelet szükséges",
|
||||
"Later" : "Később",
|
||||
"Delete" : "Törlés",
|
||||
"Sharing" : "Megosztás",
|
||||
|
||||
44
l10n/ko.js
Normal file
44
l10n/ko.js
Normal file
@@ -0,0 +1,44 @@
|
||||
OC.L10N.register(
|
||||
"deck",
|
||||
{
|
||||
"Deck" : "덱",
|
||||
"Finished" : "완료됨",
|
||||
"To review" : "리뷰할 항목",
|
||||
"Action needed" : "동작 필요",
|
||||
"Later" : "나중에",
|
||||
"Show archived cards" : "보관된 카드 보기",
|
||||
"Hide archived cards" : "보관된 카드 숨기기",
|
||||
"Board details" : "게시판 정보",
|
||||
"Archive" : "보관",
|
||||
"Unarchive" : "보관 해제",
|
||||
"Delete" : "삭제",
|
||||
"Enter a card title" : "카드 제목 입력",
|
||||
"Sharing" : "공유",
|
||||
"Labels" : "이름표",
|
||||
"Select users or groups to share with" : "공유할 사용자나 그룹 선택",
|
||||
"No matching user or group found." : "일치하는 사용자나 그룹이 없습니다.",
|
||||
"Share" : "공유",
|
||||
"Edit" : "편집",
|
||||
"Manage" : "관리",
|
||||
"Discard share" : "공유 무시",
|
||||
"Create a new label" : "새 이름표 만들기",
|
||||
"Board title" : "게시판 제목",
|
||||
"Members" : "구성원",
|
||||
"Create new board" : "새 게시판 만들기",
|
||||
"New board title" : "새 게시판 제목",
|
||||
"Modified:" : "수정한 날짜:",
|
||||
"Created:" : "만든 날짜:",
|
||||
"by" : "by",
|
||||
"Saved" : "저장됨",
|
||||
"Add a card description ..." : "카드 설명 추가 ...",
|
||||
"Attachments" : "첨부",
|
||||
"Comments" : "댓글",
|
||||
"History" : "과거 기록",
|
||||
"All Boards" : "모든 게시판",
|
||||
"Shared with you" : "내게 공유됨",
|
||||
"edit" : "편집",
|
||||
"delete" : "삭제",
|
||||
"remove share" : "공유 삭제",
|
||||
"Create a new board" : "새 게시판 만들기"
|
||||
},
|
||||
"nplurals=1; plural=0;");
|
||||
42
l10n/ko.json
Normal file
42
l10n/ko.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{ "translations": {
|
||||
"Deck" : "덱",
|
||||
"Finished" : "완료됨",
|
||||
"To review" : "리뷰할 항목",
|
||||
"Action needed" : "동작 필요",
|
||||
"Later" : "나중에",
|
||||
"Show archived cards" : "보관된 카드 보기",
|
||||
"Hide archived cards" : "보관된 카드 숨기기",
|
||||
"Board details" : "게시판 정보",
|
||||
"Archive" : "보관",
|
||||
"Unarchive" : "보관 해제",
|
||||
"Delete" : "삭제",
|
||||
"Enter a card title" : "카드 제목 입력",
|
||||
"Sharing" : "공유",
|
||||
"Labels" : "이름표",
|
||||
"Select users or groups to share with" : "공유할 사용자나 그룹 선택",
|
||||
"No matching user or group found." : "일치하는 사용자나 그룹이 없습니다.",
|
||||
"Share" : "공유",
|
||||
"Edit" : "편집",
|
||||
"Manage" : "관리",
|
||||
"Discard share" : "공유 무시",
|
||||
"Create a new label" : "새 이름표 만들기",
|
||||
"Board title" : "게시판 제목",
|
||||
"Members" : "구성원",
|
||||
"Create new board" : "새 게시판 만들기",
|
||||
"New board title" : "새 게시판 제목",
|
||||
"Modified:" : "수정한 날짜:",
|
||||
"Created:" : "만든 날짜:",
|
||||
"by" : "by",
|
||||
"Saved" : "저장됨",
|
||||
"Add a card description ..." : "카드 설명 추가 ...",
|
||||
"Attachments" : "첨부",
|
||||
"Comments" : "댓글",
|
||||
"History" : "과거 기록",
|
||||
"All Boards" : "모든 게시판",
|
||||
"Shared with you" : "내게 공유됨",
|
||||
"edit" : "편집",
|
||||
"delete" : "삭제",
|
||||
"remove share" : "공유 삭제",
|
||||
"Create a new board" : "새 게시판 만들기"
|
||||
},"pluralForm" :"nplurals=1; plural=0;"
|
||||
}
|
||||
@@ -12,10 +12,10 @@ OC.L10N.register(
|
||||
"Archive" : "Arquivar",
|
||||
"Unarchive" : "Desarquivar",
|
||||
"Delete" : "Excluir",
|
||||
"Enter a card title" : "Digite um título do cartão",
|
||||
"Enter a card title" : "Digite um título de cartão",
|
||||
"Sharing" : "Compartilhar",
|
||||
"Labels" : "Etiquetas",
|
||||
"Select users or groups to share with" : "Selecionar usuários ou grupos para compartilhar com",
|
||||
"Select users or groups to share with" : "Selecionar usuários ou grupos para compartilhar",
|
||||
"No matching user or group found." : "Uusuário ou grupo não encontrado.",
|
||||
"Share" : "Compartilhar",
|
||||
"Edit" : "Editar",
|
||||
@@ -30,7 +30,7 @@ OC.L10N.register(
|
||||
"Created:" : "Criado:",
|
||||
"by" : "por",
|
||||
"Saved" : "Salvo",
|
||||
"Add a card description ..." : "Adicionar uma descrição de cartão",
|
||||
"Add a card description ..." : "Adicionar uma descrição de cartão...",
|
||||
"Attachments" : "Anexos",
|
||||
"Comments" : "Comentários",
|
||||
"History" : "Histórico",
|
||||
@@ -38,7 +38,7 @@ OC.L10N.register(
|
||||
"Shared with you" : "Compartilhado com você",
|
||||
"edit" : "editar",
|
||||
"delete" : "excluir",
|
||||
"remove share" : "remover compartilhamento",
|
||||
"remove share" : "excluir compartilhamento",
|
||||
"Create a new board" : "Criar um novo quadro"
|
||||
},
|
||||
"nplurals=2; plural=(n > 1);");
|
||||
|
||||
@@ -10,10 +10,10 @@
|
||||
"Archive" : "Arquivar",
|
||||
"Unarchive" : "Desarquivar",
|
||||
"Delete" : "Excluir",
|
||||
"Enter a card title" : "Digite um título do cartão",
|
||||
"Enter a card title" : "Digite um título de cartão",
|
||||
"Sharing" : "Compartilhar",
|
||||
"Labels" : "Etiquetas",
|
||||
"Select users or groups to share with" : "Selecionar usuários ou grupos para compartilhar com",
|
||||
"Select users or groups to share with" : "Selecionar usuários ou grupos para compartilhar",
|
||||
"No matching user or group found." : "Uusuário ou grupo não encontrado.",
|
||||
"Share" : "Compartilhar",
|
||||
"Edit" : "Editar",
|
||||
@@ -28,7 +28,7 @@
|
||||
"Created:" : "Criado:",
|
||||
"by" : "por",
|
||||
"Saved" : "Salvo",
|
||||
"Add a card description ..." : "Adicionar uma descrição de cartão",
|
||||
"Add a card description ..." : "Adicionar uma descrição de cartão...",
|
||||
"Attachments" : "Anexos",
|
||||
"Comments" : "Comentários",
|
||||
"History" : "Histórico",
|
||||
@@ -36,7 +36,7 @@
|
||||
"Shared with you" : "Compartilhado com você",
|
||||
"edit" : "editar",
|
||||
"delete" : "excluir",
|
||||
"remove share" : "remover compartilhamento",
|
||||
"remove share" : "excluir compartilhamento",
|
||||
"Create a new board" : "Criar um novo quadro"
|
||||
},"pluralForm" :"nplurals=2; plural=(n > 1);"
|
||||
}
|
||||
@@ -130,13 +130,13 @@ class BoardController extends Controller {
|
||||
* @param $boardId
|
||||
* @param $type
|
||||
* @param $participant
|
||||
* @param $edit
|
||||
* @param $share
|
||||
* @param $manage
|
||||
* @param $permissionEdit
|
||||
* @param $permissionShare
|
||||
* @param $permissionManage
|
||||
* @return \OCP\AppFramework\Db\Entity
|
||||
*/
|
||||
public function addAcl($boardId, $type, $participant, $edit, $share, $manage) {
|
||||
return $this->boardService->addAcl($boardId, $type, $participant, $edit, $share, $manage);
|
||||
public function addAcl($boardId, $type, $participant, $permissionEdit, $permissionShare, $permissionManage) {
|
||||
return $this->boardService->addAcl($boardId, $type, $participant, $permissionEdit, $permissionShare, $permissionManage);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Deck\Controller;
|
||||
|
||||
use OCA\Deck\Db\Acl;
|
||||
|
||||
use OCA\Deck\Service\BoardService;
|
||||
use OCP\IGroupManager;
|
||||
use OCP\IRequest;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\IUserManager;
|
||||
|
||||
class ShareController extends Controller {
|
||||
|
||||
private $userManager;
|
||||
private $groupManager;
|
||||
private $boardService;
|
||||
private $userId;
|
||||
|
||||
public function __construct($appName, IRequest $request, IUserManager $userManager, IGroupManager $groupManager, BoardService $boardService, $userId) {
|
||||
parent::__construct($appName, $request);
|
||||
$this->userManager = $userManager;
|
||||
$this->groupManager = $groupManager;
|
||||
$this->userId = $userId;
|
||||
$this->boardService = $boardService;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
* @param $search
|
||||
* @return array
|
||||
*/
|
||||
public function searchUser($search) {
|
||||
$limit = 3;
|
||||
$offset = null;
|
||||
$result = [];
|
||||
foreach ($this->groupManager->search($search, $limit, $offset) as $idx => $group) {
|
||||
$acl = new Acl();
|
||||
$acl->setType('group');
|
||||
$acl->setParticipant($group->getGID());
|
||||
$acl->setPermissionEdit(true);
|
||||
$acl->setPermissionShare(true);
|
||||
$acl->setPermissionManage(true);
|
||||
$result[] = $acl;
|
||||
}
|
||||
$limit = 10;
|
||||
foreach ($this->userManager->searchDisplayName($search, $limit, $offset) as $idx => $user) {
|
||||
if ($user->getUID() === $this->userId) {
|
||||
continue;
|
||||
}
|
||||
$acl = new Acl();
|
||||
$acl->setType('user');
|
||||
$acl->setParticipant($user->getUID());
|
||||
$acl->setPermissionEdit(true);
|
||||
$acl->setPermissionShare(true);
|
||||
$acl->setPermissionManage(true);
|
||||
$result[] = $acl;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -48,12 +48,10 @@ class Acl extends RelationalEntity implements \JsonSerializable {
|
||||
$this->addType('permissionEdit', 'boolean');
|
||||
$this->addType('permissionShare', 'boolean');
|
||||
$this->addType('permissionManage', 'boolean');
|
||||
$this->addType('owner', 'boolean');
|
||||
$this->addType('type', 'integer');
|
||||
$this->addType('owner', 'boolean');
|
||||
$this->addRelation('owner');
|
||||
$this->setPermissionEdit(false);
|
||||
$this->setPermissionShare(false);
|
||||
$this->setPermissionManage(false);
|
||||
$this->addResolvable('participant');
|
||||
}
|
||||
|
||||
public function getPermission($permission) {
|
||||
@@ -93,4 +91,5 @@ class Acl extends RelationalEntity implements \JsonSerializable {
|
||||
$this->markFieldUpdated('type');
|
||||
$this->type = $typeInt;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,7 +25,6 @@ namespace OCA\Deck\Db;
|
||||
|
||||
use OCP\IDBConnection;
|
||||
|
||||
|
||||
class AclMapper extends DeckMapper implements IPermissionMapper {
|
||||
|
||||
public function __construct(IDBConnection $db) {
|
||||
|
||||
@@ -32,8 +32,8 @@ class Board extends RelationalEntity implements JsonSerializable {
|
||||
protected $owner;
|
||||
protected $color;
|
||||
protected $archived = false;
|
||||
protected $labels;
|
||||
protected $acl;
|
||||
protected $labels = [];
|
||||
protected $acl = [];
|
||||
protected $shared;
|
||||
|
||||
public function __construct() {
|
||||
@@ -43,6 +43,7 @@ class Board extends RelationalEntity implements JsonSerializable {
|
||||
$this->addRelation('labels');
|
||||
$this->addRelation('acl');
|
||||
$this->addRelation('shared');
|
||||
$this->addResolvable('owner');
|
||||
$this->shared = -1;
|
||||
}
|
||||
|
||||
@@ -51,6 +52,7 @@ class Board extends RelationalEntity implements JsonSerializable {
|
||||
if ($this->shared === -1) {
|
||||
unset($json['shared']);
|
||||
}
|
||||
$json['owner'] = $this->resolveOwner();
|
||||
return $json;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,19 +24,31 @@
|
||||
namespace OCA\Deck\Db;
|
||||
|
||||
use OCP\IDBConnection;
|
||||
|
||||
use OCP\IUserManager;
|
||||
use OCP\IGroupManager;
|
||||
|
||||
class BoardMapper extends DeckMapper implements IPermissionMapper {
|
||||
|
||||
private $labelMapper;
|
||||
private $aclMapper;
|
||||
private $stackMapper;
|
||||
private $userManager;
|
||||
private $groupManager;
|
||||
|
||||
public function __construct(IDBConnection $db, LabelMapper $labelMapper, AclMapper $aclMapper, StackMapper $stackMapper) {
|
||||
public function __construct(
|
||||
IDBConnection $db,
|
||||
LabelMapper $labelMapper,
|
||||
AclMapper $aclMapper,
|
||||
StackMapper $stackMapper,
|
||||
IUserManager $userManager,
|
||||
IGroupManager $groupManager
|
||||
) {
|
||||
parent::__construct($db, 'deck_boards', '\OCA\Deck\Db\Board');
|
||||
$this->labelMapper = $labelMapper;
|
||||
$this->aclMapper = $aclMapper;
|
||||
$this->stackMapper = $stackMapper;
|
||||
$this->userManager = $userManager;
|
||||
$this->groupManager = $groupManager;
|
||||
}
|
||||
|
||||
|
||||
@@ -149,5 +161,29 @@ class BoardMapper extends DeckMapper implements IPermissionMapper {
|
||||
return $id;
|
||||
}
|
||||
|
||||
public function mapAcl(Acl &$acl) {
|
||||
$userManager = $this->userManager;
|
||||
$groupManager = $this->groupManager;
|
||||
$acl->resolveRelation('participant', function($participant) use (&$acl, &$userManager, &$groupManager) {
|
||||
if($acl->getType() === Acl::PERMISSION_TYPE_USER) {
|
||||
return new User($userManager->get($acl->getParticipant($participant)));
|
||||
}
|
||||
if($acl->getType() === Acl::PERMISSION_TYPE_GROUP) {
|
||||
return new Group($groupManager->get($acl->getParticipant($participant)));
|
||||
}
|
||||
throw new \Exception('Unknown permission type for mapping Acl');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Board $board
|
||||
*/
|
||||
public function mapOwner(Board &$board) {
|
||||
$userManager = $this->userManager;
|
||||
$board->resolveRelation('owner', function($owner) use (&$userManager) {
|
||||
return new User($userManager->get($owner));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -48,6 +48,7 @@ class Card extends RelationalEntity implements JsonSerializable {
|
||||
$this->addType('createdAt', 'integer');
|
||||
$this->addType('archived', 'boolean');
|
||||
$this->addRelation('labels');
|
||||
$this->addResolvable('owner');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,15 +25,17 @@ namespace OCA\Deck\Db;
|
||||
|
||||
use OCP\AppFramework\Db\Entity;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\IUserManager;
|
||||
|
||||
|
||||
class CardMapper extends DeckMapper implements IPermissionMapper {
|
||||
|
||||
private $labelMapper;
|
||||
|
||||
public function __construct(IDBConnection $db, LabelMapper $labelMapper) {
|
||||
public function __construct(IDBConnection $db, LabelMapper $labelMapper, IUserManager $userManager) {
|
||||
parent::__construct($db, 'deck_cards', '\OCA\Deck\Db\Card');
|
||||
$this->labelMapper = $labelMapper;
|
||||
$this->userManager = $userManager;
|
||||
}
|
||||
|
||||
public function insert(Entity $entity) {
|
||||
@@ -57,6 +59,7 @@ class CardMapper extends DeckMapper implements IPermissionMapper {
|
||||
$card = $this->findEntity($sql, [$id]);
|
||||
$labels = $this->labelMapper->findAssignedLabelsForCard($card->id);
|
||||
$card->setLabels($labels);
|
||||
$this->mapOwner($card);
|
||||
return $card;
|
||||
}
|
||||
|
||||
@@ -125,5 +128,12 @@ class CardMapper extends DeckMapper implements IPermissionMapper {
|
||||
return $row['id'];
|
||||
}
|
||||
|
||||
public function mapOwner(Card &$card) {
|
||||
$userManager = $this->userManager;
|
||||
$card->resolveRelation('owner', function($owner) use (&$userManager) {
|
||||
return new User($userManager->get($owner));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
41
lib/Db/Group.php
Normal file
41
lib/Db/Group.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/**
|
||||
* @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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Deck\Db;
|
||||
|
||||
use OCP\IGroup;
|
||||
|
||||
class Group extends RelationalObject {
|
||||
|
||||
public function __construct(IGroup $group) {
|
||||
$primaryKey = $group->getGID();
|
||||
parent::__construct($primaryKey, $group);
|
||||
}
|
||||
|
||||
public function getObjectSerialization() {
|
||||
return [
|
||||
'uid' => $this->object->getGID(),
|
||||
'displayname' => $this->object->getDisplayName()
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
|
||||
* @copyright Copyright (c) 2017 Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
@@ -23,20 +23,31 @@
|
||||
|
||||
namespace OCA\Deck\Db;
|
||||
|
||||
use OCP\AppFramework\Db\Entity;
|
||||
|
||||
class RelationalEntity extends \OCP\AppFramework\Db\Entity implements \JsonSerializable {
|
||||
class RelationalEntity extends Entity implements \JsonSerializable {
|
||||
|
||||
private $_relations = array();
|
||||
private $_resolvedProperties = [];
|
||||
|
||||
/**
|
||||
* Mark a property as relation so it will not get updated using Mapper::update
|
||||
* @param string $property Name of the property
|
||||
* @param $property string Name of the property
|
||||
*/
|
||||
public function addRelation($property) {
|
||||
if (!in_array($property, $this->_relations)) {
|
||||
$this->_relations[] = $property;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a property as resolvable via resolveRelation()
|
||||
* @param $property string Name of the property
|
||||
*/
|
||||
public function addResolvable($property) {
|
||||
$this->_resolvedProperties[$property] = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark am attribute as updated
|
||||
* overwritten from \OCP\AppFramework\Db\Entity to avoid writing relational attributes
|
||||
@@ -56,14 +67,75 @@ class RelationalEntity extends \OCP\AppFramework\Db\Entity implements \JsonSeria
|
||||
$properties = get_object_vars($this);
|
||||
$reflection = new \ReflectionClass($this);
|
||||
$json = [];
|
||||
foreach($properties as $property=>$value) {
|
||||
if(substr($property, 0, 1) !== '_' && $reflection->hasProperty($property)) {
|
||||
foreach ($properties as $property => $value) {
|
||||
if (substr($property, 0, 1) !== '_' && $reflection->hasProperty($property)) {
|
||||
$propertyReflection = $reflection->getProperty($property);
|
||||
if(!$propertyReflection->isPrivate()) {
|
||||
if (!$propertyReflection->isPrivate()) {
|
||||
$json[$property] = $this->getter($property);
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($this->_resolvedProperties as $property => $value) {
|
||||
if($value !== null) {
|
||||
$json[$property] = $value;
|
||||
}
|
||||
}
|
||||
return $json;
|
||||
}
|
||||
|
||||
/*
|
||||
* Resolve relational data from external methods
|
||||
*
|
||||
* example usage:
|
||||
*
|
||||
* in Board::__construct()
|
||||
* $this->addResolvable('owner')
|
||||
*
|
||||
* in BoardMapper
|
||||
* $board->resolveRelation('owner', function($owner) use (&$userManager) {
|
||||
* return new \OCA\Deck\Db\User($userManager->get($owner));
|
||||
* });
|
||||
*
|
||||
* resolved values can be obtained by calling resolveProperty
|
||||
* e.g. $board->resolveOwner()
|
||||
*
|
||||
* @param string $property name of the property
|
||||
* @param callable $resolver anonymous function to resolve relational
|
||||
* data defined by $property as unique identifier
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function resolveRelation($property, $resolver) {
|
||||
$result = null;
|
||||
if($property !== null && $this->$property !== null) {
|
||||
$result = $resolver($this->$property);
|
||||
}
|
||||
|
||||
if($result instanceof RelationalObject || $result === null) {
|
||||
$this->_resolvedProperties[$property] = $result;
|
||||
} else {
|
||||
throw new \Exception('resolver must return an instance of RelationalObject');
|
||||
}
|
||||
}
|
||||
|
||||
public function __call($methodName, $args){
|
||||
$attr = lcfirst( substr($methodName, 7) );
|
||||
if(strpos($methodName, 'resolve') === 0 && array_key_exists($attr, $this->_resolvedProperties)) {
|
||||
if($this->_resolvedProperties[$attr] !== null) {
|
||||
return $this->_resolvedProperties[$attr];
|
||||
} else {
|
||||
return $this->getter($attr);
|
||||
}
|
||||
}
|
||||
|
||||
$attr = lcfirst( substr($methodName, 3) );
|
||||
if(strpos($methodName, 'set') === 0 && array_key_exists($attr, $this->_resolvedProperties)) {
|
||||
if(!is_scalar($args[0])) {
|
||||
$args[0] = $args[0]['primaryKey'];
|
||||
}
|
||||
parent::setter($attr, $args);
|
||||
return null;
|
||||
}
|
||||
return parent::__call($methodName, $args);
|
||||
}
|
||||
|
||||
}
|
||||
59
lib/Db/RelationalObject.php
Normal file
59
lib/Db/RelationalObject.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/**
|
||||
* @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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Deck\Db;
|
||||
|
||||
class RelationalObject implements \JsonSerializable {
|
||||
|
||||
private $primaryKey;
|
||||
|
||||
/**
|
||||
* RelationalObject constructor.
|
||||
*
|
||||
* @param $primaryKey string
|
||||
* @param $object
|
||||
*/
|
||||
public function __construct($primaryKey, $object) {
|
||||
$this->primaryKey = $primaryKey;
|
||||
$this->object = $object;
|
||||
}
|
||||
|
||||
public function jsonSerialize() {
|
||||
return array_merge(
|
||||
['primaryKey' => $this->primaryKey],
|
||||
$this->getObjectSerialization()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should be overwritten if object doesn't implement \JsonSerializable
|
||||
*/
|
||||
public function getObjectSerialization() {
|
||||
if($this->object instanceof \JsonSerializable) {
|
||||
$this->object->jsonSerialize();
|
||||
} else {
|
||||
throw new \Exception('jsonSerialize is not implemented on ' . get_class($this));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
41
lib/Db/User.php
Normal file
41
lib/Db/User.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/**
|
||||
* @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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Deck\Db;
|
||||
|
||||
use OCP\IUser;
|
||||
|
||||
class User extends RelationalObject {
|
||||
|
||||
public function __construct(IUser $user) {
|
||||
$primaryKey = $user->getUID();
|
||||
parent::__construct($primaryKey, $user);
|
||||
}
|
||||
|
||||
public function getObjectSerialization() {
|
||||
return [
|
||||
'uid' => $this->object->getUID(),
|
||||
'displayname' => $this->object->getDisplayName()
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -26,10 +26,7 @@ namespace OCA\Deck\Service;
|
||||
use OCA\Deck\Db\Acl;
|
||||
use OCA\Deck\Db\AclMapper;
|
||||
use OCA\Deck\Db\Label;
|
||||
|
||||
|
||||
use OCP\IL10N;
|
||||
|
||||
use OCA\Deck\Db\Board;
|
||||
use OCA\Deck\Db\BoardMapper;
|
||||
use OCA\Deck\Db\LabelMapper;
|
||||
@@ -55,14 +52,36 @@ class BoardService {
|
||||
$userBoards = $this->boardMapper->findAllByUser($userInfo['user']);
|
||||
$groupBoards = $this->boardMapper->findAllByGroups($userInfo['user'], $userInfo['groups']);
|
||||
$complete = array_merge($userBoards, $groupBoards);
|
||||
return array_map("unserialize", array_unique(array_map("serialize", $complete)));
|
||||
$result = [];
|
||||
foreach($complete as &$item) {
|
||||
if(!array_key_exists($item->getId(), $result)) {
|
||||
$this->boardMapper->mapOwner($item);
|
||||
if($item->getAcl() !== null) {
|
||||
foreach ($item->getAcl() as &$acl) {
|
||||
$this->boardMapper->mapAcl($acl);
|
||||
}
|
||||
}
|
||||
$result[$item->getId()] = $item;
|
||||
}
|
||||
}
|
||||
return array_values($result);
|
||||
}
|
||||
|
||||
public function find($boardId) {
|
||||
$this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_READ);
|
||||
return $this->boardMapper->find($boardId, true, true);
|
||||
/** @var Board $board */
|
||||
$board = $this->boardMapper->find($boardId, true, true);
|
||||
$this->boardMapper->mapOwner($board);
|
||||
foreach ($board->getAcl() as &$acl) {
|
||||
if($acl !== null) {
|
||||
$this->boardMapper->mapAcl($acl);
|
||||
}
|
||||
}
|
||||
return $board;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function create($title, $userId, $color) {
|
||||
$board = new Board();
|
||||
$board->setTitle($title);
|
||||
@@ -86,6 +105,7 @@ class BoardService {
|
||||
$labels[] = $this->labelMapper->insert($label);
|
||||
}
|
||||
$new_board->setLabels($labels);
|
||||
$this->boardMapper->mapOwner($new_board);
|
||||
return $new_board;
|
||||
|
||||
}
|
||||
@@ -100,6 +120,7 @@ class BoardService {
|
||||
$board = $this->find($id);
|
||||
$board->setTitle($title);
|
||||
$board->setColor($color);
|
||||
$this->boardMapper->mapOwner($board);
|
||||
return $this->boardMapper->update($board);
|
||||
}
|
||||
|
||||
@@ -113,21 +134,27 @@ class BoardService {
|
||||
$acl->setPermissionEdit($edit);
|
||||
$acl->setPermissionShare($share);
|
||||
$acl->setPermissionManage($manage);
|
||||
return $this->aclMapper->insert($acl);
|
||||
$newAcl = $this->aclMapper->insert($acl);
|
||||
$this->boardMapper->mapAcl($newAcl);
|
||||
return $newAcl;
|
||||
}
|
||||
|
||||
public function updateAcl($id, $edit, $share, $manage) {
|
||||
$this->permissionService->checkPermission($this->aclMapper, $id, Acl::PERMISSION_SHARE);
|
||||
/** @var Acl $acl */
|
||||
$acl = $this->aclMapper->find($id);
|
||||
$acl->setPermissionEdit($edit);
|
||||
$acl->setPermissionShare($share);
|
||||
$acl->setPermissionManage($manage);
|
||||
$this->boardMapper->mapAcl($acl);
|
||||
return $this->aclMapper->update($acl);
|
||||
}
|
||||
|
||||
public function deleteAcl($id) {
|
||||
$this->permissionService->checkPermission($this->aclMapper, $id, Acl::PERMISSION_SHARE);
|
||||
/** @var Acl $acl */
|
||||
$acl = $this->aclMapper->find($id);
|
||||
$this->boardMapper->mapAcl($acl);
|
||||
return $this->aclMapper->delete($acl);
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,8 @@ class CardService {
|
||||
|
||||
public function find($cardId) {
|
||||
$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_READ);
|
||||
return $this->cardMapper->find($cardId);
|
||||
$card = $this->cardMapper->find($cardId);
|
||||
return $card;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,14 +9,26 @@
|
||||
<h1>
|
||||
{{ boardservice.getCurrent().title }}
|
||||
</h1>
|
||||
<div id="board-actions">
|
||||
<div class="board-action-button" ng-if="filter!='archive'"><a ng-click="switchFilter('archive')" style="opacity:0.5;" title="<?php p($l->t('Show archived cards')); ?>"><i class="icon icon-archive{{ boardservice.getCurrent().color | iconWhiteFilter }}"></i></a></div>
|
||||
<div class="board-action-button" ng-if="filter=='archive'"><a ng-click="switchFilter('')" title="<?php p($l->t('Hide archived cards')); ?>"><i class="icon icon-archive{{ boardservice.getCurrent().color | iconWhiteFilter }}"></i></a></div>
|
||||
<div class="board-action-button"><a ui-sref="board.detail({ id: id })" title="<?php p($l->t('Board details')); ?>"><i class="icon icon-details{{ boardservice.getCurrent().color | iconWhiteFilter }}"></i></a>
|
||||
<div id="board-actions">
|
||||
<div class="board-action-button" ng-if="filter!='archive'"><a ng-click="switchFilter('archive')" style="opacity:0.5;" title="<?php p($l->t('Show archived cards')); ?>"><i class="icon icon-archive{{ boardservice.getCurrent().color | iconWhiteFilter }}"></i></a></div>
|
||||
<div class="board-action-button" ng-if="filter=='archive'"><a ng-click="switchFilter('')" title="<?php p($l->t('Hide archived cards')); ?>"><i class="icon icon-archive{{ boardservice.getCurrent().color | iconWhiteFilter }}"></i></a></div>
|
||||
<div class="board-action-button"><a ui-sref="board.detail({ id: id })" title="<?php p($l->t('Board details')); ?>"><i class="icon icon-details{{ boardservice.getCurrent().color | iconWhiteFilter }}"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="board" class="scroll-container">
|
||||
|
||||
<div id="stack-add" ng-if="boardservice.canEdit() && checkCanEdit()">
|
||||
<form class="ng-pristine ng-valid" ng-submit="createStack()">
|
||||
<input type="text" placeholder="Add a new stack"
|
||||
ng-focus="status.addStack=true"
|
||||
ng-blur="status.addStack=false"
|
||||
ng-model="newStack.title" required
|
||||
maxlength="100" />
|
||||
<button class="icon icon-add" style="opacity: {{status.addStack ? 1: 0.5}};"
|
||||
type="submit"></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div id="board" class="scroll-container" ng-click="sidebar.show=false" ui-sref="board">
|
||||
|
||||
<search on-search="search" class="ng-hide"></search>
|
||||
|
||||
@@ -29,7 +41,7 @@
|
||||
<input type="text" placeholder="Add a new stack"
|
||||
ng-blur="s.status.editStack=false" ng-model="s.title"
|
||||
ng-if="s.status.editStack" autofocus-on-insert
|
||||
required/>
|
||||
required maxlength="100"/>
|
||||
</form>
|
||||
<div class="stack-actions">
|
||||
<button class="icon icon-confirm" ng-if="s.status.editStack"
|
||||
@@ -45,10 +57,12 @@
|
||||
<li class="card as-sortable-item"
|
||||
ng-repeat="c in s.cards"
|
||||
data-as-sortable-item
|
||||
ng-click="$event.stopPropagation()"
|
||||
ui-sref="board.card({boardId: id, cardId: c.id})"
|
||||
ng-class="{'archived': c.archived, 'has-labels': c.labels.length>0 }">
|
||||
<div data-as-sortable-item-handle>
|
||||
<div class="card-upper">
|
||||
<i class="icon icon-filetype-text icon-description-marker" ng-if="c.description" title="{{ c.description }}"></i>
|
||||
<h3>{{ c.title }}</h3>
|
||||
<ul class="labels">
|
||||
<li ng-repeat="label in c.labels"
|
||||
@@ -103,6 +117,7 @@
|
||||
ng-model="newCard.title"
|
||||
ng-blur="status.addCard[s.id]=false"
|
||||
style="color:{{ boardservice.getCurrent().color | textColorFilter }}; border-color:{{ boardservice.getCurrent().color | textColorFilter }};"
|
||||
maxlength="100"
|
||||
required placeholder="<?php p($l->t('Enter a card title')); ?>"/>
|
||||
</h3>
|
||||
</form>
|
||||
@@ -111,18 +126,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stack" style="display: inline-block;" ng-if="boardservice.canEdit() && checkCanEdit()">
|
||||
<form class="ng-pristine ng-valid" ng-submit="createStack()">
|
||||
<h2>
|
||||
<input type="text" placeholder="Add a new stack"
|
||||
ng-focus="status.addStack=true"
|
||||
ng-blur="status.addStack=false"
|
||||
ng-model="newStack.title" required/>
|
||||
<button class="icon icon-add" ng-show="status.addStack"
|
||||
type="submit"></button>
|
||||
</h2>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -17,12 +17,12 @@
|
||||
<div class="tabsContainer">
|
||||
<div id="commentsTabView" class="tab commentsTabView" ng-if="status.boardtab==0 || !status.boardtab">
|
||||
|
||||
<ui-select ng-if="boardservice.canShare()" ng-model="status.addSharee" theme="select2" style="width:100%;" title="Choose a user to assign" placeholder="Assign users ..." on-select="aclAdd(status.addSharee)">
|
||||
<ui-select ng-if="boardservice.canShare()" ng-model="status.addSharee" theme="select2" style="width:100%;" title="Choose a user to assign" placeholder="Assign users ..." on-select="aclAdd(status.addSharee)" search-enabled="true">
|
||||
<ui-select-match placeholder="<?php p($l->t('Select users or groups to share with')); ?>">
|
||||
<span><i class="icon icon-{{$item.type}}"></i> {{ $item.participant }}</span>
|
||||
<span><i class="icon icon-{{$item.type}}"></i> {{ $item.participant.displayname }}</span>
|
||||
</ui-select-match>
|
||||
<ui-select-choices refresh="searchForUser($select.search)" refresh-delay="0" repeat="sharee in boardservice.sharees">
|
||||
<span><i class="icon icon-{{sharee.type}}"></i> {{ sharee.participant }}</span>
|
||||
<span><i class="icon icon-{{sharee.type}}"></i> {{ sharee.participant.displayname }}</span>
|
||||
</ui-select-choices>
|
||||
<ui-select-no-choice>
|
||||
<?php p($l->t('No matching user or group found.')); ?>
|
||||
@@ -32,18 +32,18 @@
|
||||
<ul id="shareWithList" class="shareWithList">
|
||||
<li>
|
||||
<span class="icon-loading-small" style="display:none;"></span>
|
||||
<div class="avatardiv" avatar ng-attr-displayname="{{ boardservice.getCurrent().owner }}" ng-if="boardservice.id"></div>
|
||||
<div class="avatardiv" avatar ng-attr-displayname="{{ boardservice.getCurrent().owner.uid }}" ng-if="boardservice.id"></div>
|
||||
<span class="has-tooltip username">
|
||||
{{ boardservice.getCurrent().owner }}
|
||||
{{ boardservice.getCurrent().owner.displayname }}
|
||||
</span>
|
||||
</li>
|
||||
<li ng-repeat="acl in boardservice.getCurrent().acl track by $index">
|
||||
<span class="icon-loading-small" style="display:none;"></span>
|
||||
<div class="avatardiv" avatar displayname="{{ acl.participant }}" ng-if="acl.type=='user'"></div>
|
||||
<div class="avatardiv" avatar displayname="{{ acl.participant.uid }}" ng-if="acl.type=='user'"></div>
|
||||
<div class="avatardiv" ng-if="acl.type=='group'"><i class="icon icon-{{acl.type}}"></i></div>
|
||||
|
||||
<span class="has-tooltip username">
|
||||
{{ acl.participant }}
|
||||
{{ acl.participant.displayname }}
|
||||
</span>
|
||||
<span class="sharingOptionsGroup">
|
||||
<span class="shareOption" ng-if="boardservice.canManage()">
|
||||
@@ -73,7 +73,7 @@
|
||||
</span>
|
||||
<span class="label-title" style="background-color:#{{label.color}}; color:{{ textColor(label.color) }}; width:178px;" ng-if="label.edit">
|
||||
<form ng-submit="labelUpdate(label)">
|
||||
<input type="text" ng-model="label.title" class="input-inline" style="background-color:#{{label.color}}; color:{{ label.color|textColorFilter }};" autofocus-on-insert />
|
||||
<input type="text" ng-model="label.title" class="input-inline" style="background-color:#{{label.color}}; color:{{ label.color|textColorFilter }};" autofocus-on-insert maxlength="100"/>
|
||||
</form>
|
||||
</span>
|
||||
<div class="colorselect" ng-if="label.edit">
|
||||
@@ -87,7 +87,7 @@
|
||||
<li ng-if="status.createLabel">
|
||||
<form ng-submit="labelCreate(newLabel)">
|
||||
<span class="label-title" style="background-color:#{{newLabel.color}}; color:{{ textColor(newLabel.color) }}; width:178px;">
|
||||
<input type="text" class="input-inline" ng-model="newLabel.title" style="color:{{ newLabel.color|textColorFilter }};" autofocus-on-insert />
|
||||
<input type="text" class="input-inline" ng-model="newLabel.title" style="color:{{ newLabel.color|textColorFilter }};" autofocus-on-insert maxlength="100" />
|
||||
</span>
|
||||
<div class="colorselect">
|
||||
<div class="color" ng-repeat="c in defaultColors" style="background-color:#{{ c }};" ng-click="newLabel.color=c" ng-class="{'selected': (c == newLabel.color) }"><br /></div>
|
||||
|
||||
@@ -17,11 +17,8 @@
|
||||
<td><a href="#/board/{{b.id}}">{{ b.title }}</a></td>
|
||||
<td>
|
||||
<div id="assigned-users">
|
||||
<div class="avatardiv" avatar
|
||||
displayname="{{ b.owner }}"></div>
|
||||
<div class="avatardiv" avatar
|
||||
displayname="{{ acl.participant }}"
|
||||
ng-repeat="acl in b.acl | limitTo: 7"></div>
|
||||
<div class="avatardiv" avatar displayname="{{ b.owner.uid }}" title="{{ b.owner.displayname }}"></div>
|
||||
<div class="avatardiv" avatar displayname="{{ acl.participant.uid }}" title="{{ acl.participant.uid }}" ng-repeat="acl in b.acl | limitTo: 7"></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -36,7 +33,7 @@
|
||||
class="ng-pristine ng-valid" ng-submit="boardCreate()">
|
||||
<input id="newTitle" class="edit ng-valid ng-empty"
|
||||
type="text" placeholder="<?php p($l->t('New board title')); ?>"
|
||||
autofocus-on-insert ng-model="newBoard.title">
|
||||
autofocus-on-insert ng-model="newBoard.title" maxlength="100">
|
||||
<div class="colorselect">
|
||||
<div class="color" ng-repeat="c in colors"
|
||||
style="background-color:#{{ c }};"
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<input class="input-inline" type="text" ng-if="status.cardRename"
|
||||
ng-model="cardservice.getCurrent().title"
|
||||
ng-blur="cardRename(cardservice.getCurrent())"
|
||||
autofocus-on-insert required>
|
||||
autofocus-on-insert required maxlength="100">
|
||||
</form>
|
||||
<div ng-click="cardRenameShow()" ng-show="!status.cardRename">
|
||||
{{ cardservice.getCurrent().title }}
|
||||
@@ -26,7 +26,7 @@
|
||||
<?php p($l->t('Modified:')); ?> <span class="live-relative-timestamp" data-timestamp="{{cardservice.getCurrent().lastModified*1000}}">{{ cardservice.getCurrent().lastModified|relativeDateFilter }}</span>
|
||||
<?php p($l->t('Created:')); ?> <span class="live-relative-timestamp" data-timestamp="{{cardservice.getCurrent().createdAt*1000}}">{{ cardservice.getCurrent().createdAt|relativeDateFilter }}</span>
|
||||
<?php p($l->t('by')); ?>
|
||||
<span>{{ cardservice.getCurrent().owner }}</span>
|
||||
<span>{{ cardservice.getCurrent().owner.displayname }}</span>
|
||||
</div>
|
||||
|
||||
<div id="labels">
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
</div>
|
||||
<div class="app-navigation-entry-edit" ng-show="b.status.edit">
|
||||
<form ng-disabled="isAddingList" class="ng-pristine ng-valid" ng-submit="boardUpdate(b)">
|
||||
<input id="newTitle" class="edit ng-valid ng-empty" type="text" autofocus-on-insert ng-model="b.title">
|
||||
<input id="newTitle" class="edit ng-valid ng-empty" type="text" autofocus-on-insert ng-model="b.title" maxlength="100">
|
||||
<input type="submit" value="" class="action icon-checkmark svg">
|
||||
<div class="colorselect">
|
||||
<div class="color" ng-repeat="c in colors" style="background-color:#{{ c }};" ng-click="b.color=c" ng-class="{'selected': (c == b.color) }"><br /></div>
|
||||
@@ -39,7 +39,7 @@
|
||||
</a>
|
||||
<div class="app-navigation-entry-edit" ng-if="status.addBoard">
|
||||
<form ng-disabled="isAddingList" class="ng-pristine ng-valid" ng-submit="boardCreate()">
|
||||
<input id="newTitle" class="edit ng-valid ng-empty" type="text" placeholder="<?php p($l->t('Board title')); ?>" autofocus-on-insert ng-model="newBoard.title">
|
||||
<input id="newTitle" class="edit ng-valid ng-empty" type="text" placeholder="<?php p($l->t('Board title')); ?>" autofocus-on-insert ng-model="newBoard.title" maxlength="100">
|
||||
<input type="submit" value="" class="action icon-checkmark svg">
|
||||
<div class="colorselect">
|
||||
<div class="color" ng-repeat="c in colors" style="background-color:#{{ c }};" ng-click="selectColor(c)" ng-class="{'selected': (c == newBoard.color) }"><br /></div>
|
||||
|
||||
23
tests/integration/composer.json
Normal file
23
tests/integration/composer.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.6",
|
||||
"behat/behat": "^3.0",
|
||||
"guzzlehttp/guzzle": "~5.0",
|
||||
"jarnaiz/behat-junit-formatter": "^1.3",
|
||||
"sabre/dav": "3.2"
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"../../../../build/integration/features/bootstrap/Auth.php",
|
||||
"../../../../build/integration/features/bootstrap/Provisioning.php",
|
||||
"../../../../build/integration/features/bootstrap/Sharing.php",
|
||||
"../../../../build/integration/features/bootstrap/Trashbin.php",
|
||||
"../../../../build/integration/features/bootstrap/WebDav.php"
|
||||
],
|
||||
"psr-0": {
|
||||
"": [
|
||||
"features/bootstrap/"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
12
tests/integration/config/behat.yml
Normal file
12
tests/integration/config/behat.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
default:
|
||||
suites:
|
||||
test:
|
||||
paths:
|
||||
- %paths.base%/../features/
|
||||
contexts:
|
||||
- FeatureContext:
|
||||
baseUrl: http://localhost:8080/index.php/ocs/
|
||||
admin:
|
||||
- admin
|
||||
- admin
|
||||
regular_user_password: 123456
|
||||
36
tests/integration/features/acl.feature
Normal file
36
tests/integration/features/acl.feature
Normal file
@@ -0,0 +1,36 @@
|
||||
Feature: acl
|
||||
Routes should check for permissions when a user sends a requests
|
||||
|
||||
Background:
|
||||
Given user "admin" exists
|
||||
And user "user0" exists
|
||||
And user "user1" exists
|
||||
And user "user2" exists
|
||||
Given group "group0" exists
|
||||
And group "group1" exists
|
||||
Given user "user1" belongs to group "group1"
|
||||
|
||||
Scenario: Request the main frontend page
|
||||
Given Logging in using web as "user0"
|
||||
When Sending a "GET" to "/index.php/apps/deck" without requesttoken
|
||||
Then the HTTP status code should be "200"
|
||||
|
||||
Scenario: Fetch the board list
|
||||
Given Logging in using web as "user0"
|
||||
When Sending a "GET" to "/index.php/apps/deck/boards" with requesttoken
|
||||
Then the HTTP status code should be "200"
|
||||
And the Content-Type should be "application/json; charset=utf-8"
|
||||
|
||||
Scenario: Fetch board details of owned board
|
||||
Given Logging in using web as "admin"
|
||||
And creates a board named "MyPrivateAdminBoard" with color "fafafa"
|
||||
When "admin" fetches the board named "MyPrivateAdminBoard"
|
||||
Then the HTTP status code should be "200"
|
||||
And the Content-Type should be "application/json; charset=utf-8"
|
||||
|
||||
Scenario: Fetch board details of an other users board
|
||||
Given Logging in using web as "admin"
|
||||
And creates a board named "MyPrivateAdminBoard" with color "fafafa"
|
||||
When "user0" fetches the board named "MyPrivateAdminBoard"
|
||||
Then the HTTP status code should be "403"
|
||||
And the Content-Type should be "application/json; charset=utf-8"
|
||||
450
tests/integration/features/bootstrap/BasicStructure.php
Normal file
450
tests/integration/features/bootstrap/BasicStructure.php
Normal file
@@ -0,0 +1,450 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* @author Christoph Wurst <christoph@owncloud.com>
|
||||
* @author Joas Schilling <coding@schilljs.com>
|
||||
* @author Lukas Reschke <lukas@statuscode.ch>
|
||||
* @author Sergio Bertolin <sbertolin@solidgear.es>
|
||||
* @author Thomas Müller <thomas.mueller@tmit.eu>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Cookie\CookieJar;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use GuzzleHttp\Message\ResponseInterface;
|
||||
|
||||
require __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
trait BasicStructure {
|
||||
|
||||
use Auth;
|
||||
|
||||
/** @var string */
|
||||
private $currentUser = '';
|
||||
|
||||
/** @var string */
|
||||
private $currentServer = '';
|
||||
|
||||
/** @var string */
|
||||
private $baseUrl = '';
|
||||
|
||||
/** @var int */
|
||||
private $apiVersion = 1;
|
||||
|
||||
/** @var ResponseInterface */
|
||||
private $response = null;
|
||||
|
||||
/** @var CookieJar */
|
||||
private $cookieJar;
|
||||
|
||||
/** @var string */
|
||||
private $requestToken;
|
||||
|
||||
public function __construct($baseUrl, $admin, $regular_user_password) {
|
||||
|
||||
// Initialize your context here
|
||||
$this->baseUrl = $baseUrl;
|
||||
$this->adminUser = $admin;
|
||||
$this->regularUser = $regular_user_password;
|
||||
$this->localBaseUrl = $this->baseUrl;
|
||||
$this->remoteBaseUrl = $this->baseUrl;
|
||||
$this->currentServer = 'LOCAL';
|
||||
$this->cookieJar = new CookieJar();
|
||||
|
||||
// in case of ci deployment we take the server url from the environment
|
||||
$testServerUrl = getenv('TEST_SERVER_URL');
|
||||
if ($testServerUrl !== false) {
|
||||
$this->baseUrl = $testServerUrl;
|
||||
$this->localBaseUrl = $testServerUrl;
|
||||
}
|
||||
|
||||
// federated server url from the environment
|
||||
$testRemoteServerUrl = getenv('TEST_SERVER_FED_URL');
|
||||
if ($testRemoteServerUrl !== false) {
|
||||
$this->remoteBaseUrl = $testRemoteServerUrl;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given /^using api version "(\d+)"$/
|
||||
* @param string $version
|
||||
*/
|
||||
public function usingApiVersion($version) {
|
||||
$this->apiVersion = (int) $version;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given /^As an "([^"]*)"$/
|
||||
* @param string $user
|
||||
*/
|
||||
public function asAn($user) {
|
||||
$this->currentUser = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given /^Using server "(LOCAL|REMOTE)"$/
|
||||
* @param string $server
|
||||
* @return string Previous used server
|
||||
*/
|
||||
public function usingServer($server) {
|
||||
$previousServer = $this->currentServer;
|
||||
if ($server === 'LOCAL'){
|
||||
$this->baseUrl = $this->localBaseUrl;
|
||||
$this->currentServer = 'LOCAL';
|
||||
return $previousServer;
|
||||
} else {
|
||||
$this->baseUrl = $this->remoteBaseUrl;
|
||||
$this->currentServer = 'REMOTE';
|
||||
return $previousServer;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^sending "([^"]*)" to "([^"]*)"$/
|
||||
* @param string $verb
|
||||
* @param string $url
|
||||
*/
|
||||
public function sendingTo($verb, $url) {
|
||||
$this->sendingToWith($verb, $url, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the xml answer to get ocs response which doesn't match with
|
||||
* http one in v1 of the api.
|
||||
* @param ResponseInterface $response
|
||||
* @return string
|
||||
*/
|
||||
public function getOCSResponse($response) {
|
||||
return $response->xml()->meta[0]->statuscode;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is needed to use a vertical fashion in the gherkin tables.
|
||||
* @param array $arrayOfArrays
|
||||
* @return array
|
||||
*/
|
||||
public function simplifyArray($arrayOfArrays){
|
||||
$a = array_map(function($subArray) { return $subArray[0]; }, $arrayOfArrays);
|
||||
return $a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^sending "([^"]*)" to "([^"]*)" with$/
|
||||
* @param string $verb
|
||||
* @param string $url
|
||||
* @param \Behat\Gherkin\Node\TableNode $body
|
||||
*/
|
||||
public function sendingToWith($verb, $url, $body) {
|
||||
$fullUrl = $this->baseUrl . "v{$this->apiVersion}.php" . $url;
|
||||
$client = new Client();
|
||||
$options = [];
|
||||
if ($this->currentUser === 'admin') {
|
||||
$options['auth'] = $this->adminUser;
|
||||
} else {
|
||||
$options['auth'] = [$this->currentUser, $this->regularUser];
|
||||
}
|
||||
$options['headers'] = [
|
||||
'OCS_APIREQUEST' => 'true'
|
||||
];
|
||||
if ($body instanceof \Behat\Gherkin\Node\TableNode) {
|
||||
$fd = $body->getRowsHash();
|
||||
$options['body'] = $fd;
|
||||
}
|
||||
|
||||
// TODO: Fix this hack!
|
||||
if ($verb === 'PUT' && $body === null) {
|
||||
$options['body'] = [
|
||||
'foo' => 'bar',
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
$this->response = $client->send($client->createRequest($verb, $fullUrl, $options));
|
||||
} catch (ClientException $ex) {
|
||||
$this->response = $ex->getResponse();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^sending "([^"]*)" with exact url to "([^"]*)"$/
|
||||
* @param string $verb
|
||||
* @param string $url
|
||||
*/
|
||||
public function sendingToDirectUrl($verb, $url) {
|
||||
$this->sendingToWithDirectUrl($verb, $url, null);
|
||||
}
|
||||
|
||||
public function sendingToWithDirectUrl($verb, $url, $body) {
|
||||
$fullUrl = substr($this->baseUrl, 0, -5) . $url;
|
||||
$client = new Client();
|
||||
$options = [];
|
||||
if ($this->currentUser === 'admin') {
|
||||
$options['auth'] = $this->adminUser;
|
||||
} else {
|
||||
$options['auth'] = [$this->currentUser, $this->regularUser];
|
||||
}
|
||||
if ($body instanceof \Behat\Gherkin\Node\TableNode) {
|
||||
$fd = $body->getRowsHash();
|
||||
$options['body'] = $fd;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->response = $client->send($client->createRequest($verb, $fullUrl, $options));
|
||||
} catch (ClientException $ex) {
|
||||
$this->response = $ex->getResponse();
|
||||
}
|
||||
}
|
||||
|
||||
public function isExpectedUrl($possibleUrl, $finalPart){
|
||||
$baseUrlChopped = substr($this->baseUrl, 0, -4);
|
||||
$endCharacter = strlen($baseUrlChopped) + strlen($finalPart);
|
||||
return (substr($possibleUrl,0,$endCharacter) == "$baseUrlChopped" . "$finalPart");
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^the OCS status code should be "([^"]*)"$/
|
||||
* @param int $statusCode
|
||||
*/
|
||||
public function theOCSStatusCodeShouldBe($statusCode) {
|
||||
PHPUnit_Framework_Assert::assertEquals($statusCode, $this->getOCSResponse($this->response));
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^the HTTP status code should be "([^"]*)"$/
|
||||
* @param int $statusCode
|
||||
*/
|
||||
public function theHTTPStatusCodeShouldBe($statusCode) {
|
||||
PHPUnit_Framework_Assert::assertEquals($statusCode, $this->response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^the Content-Type should be "([^"]*)"$/
|
||||
* @param string $contentType
|
||||
*/
|
||||
public function theContentTypeShouldbe($contentType) {
|
||||
PHPUnit_Framework_Assert::assertEquals($contentType, $this->response->getHeader('Content-Type'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ResponseInterface $response
|
||||
*/
|
||||
private function extracRequestTokenFromResponse(ResponseInterface $response) {
|
||||
$this->requestToken = substr(preg_replace('/(.*)data-requesttoken="(.*)">(.*)/sm', '\2', $response->getBody()->getContents()), 0, 89);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given Logging in using web as :user
|
||||
* @param string $user
|
||||
*/
|
||||
public function loggingInUsingWebAs($user) {
|
||||
$loginUrl = substr($this->baseUrl, 0, -5) . '/login';
|
||||
// Request a new session and extract CSRF token
|
||||
$client = new Client();
|
||||
$response = $client->get(
|
||||
$loginUrl,
|
||||
[
|
||||
'cookies' => $this->cookieJar,
|
||||
]
|
||||
);
|
||||
$this->extracRequestTokenFromResponse($response);
|
||||
|
||||
// Login and extract new token
|
||||
$password = ($user === 'admin') ? 'admin' : '123456';
|
||||
$client = new Client();
|
||||
$response = $client->post(
|
||||
$loginUrl,
|
||||
[
|
||||
'body' => [
|
||||
'user' => $user,
|
||||
'password' => $password,
|
||||
'requesttoken' => $this->requestToken,
|
||||
],
|
||||
'cookies' => $this->cookieJar,
|
||||
]
|
||||
);
|
||||
$this->extracRequestTokenFromResponse($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When Sending a :method to :url with requesttoken
|
||||
* @param string $method
|
||||
* @param string $url
|
||||
*/
|
||||
public function sendingAToWithRequesttoken($method, $url) {
|
||||
$baseUrl = substr($this->baseUrl, 0, -5);
|
||||
|
||||
$client = new Client();
|
||||
$request = $client->createRequest(
|
||||
$method,
|
||||
$baseUrl . $url,
|
||||
[
|
||||
'cookies' => $this->cookieJar,
|
||||
]
|
||||
);
|
||||
$request->addHeader('requesttoken', $this->requestToken);
|
||||
try {
|
||||
$this->response = $client->send($request);
|
||||
} catch (ClientException $e) {
|
||||
$this->response = $e->getResponse();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @When Sending a :method to :url without requesttoken
|
||||
* @param string $method
|
||||
* @param string $url
|
||||
*/
|
||||
public function sendingAToWithoutRequesttoken($method, $url) {
|
||||
$baseUrl = substr($this->baseUrl, 0, -5);
|
||||
|
||||
$client = new Client();
|
||||
$request = $client->createRequest(
|
||||
$method,
|
||||
$baseUrl . $url,
|
||||
[
|
||||
'cookies' => $this->cookieJar,
|
||||
]
|
||||
);
|
||||
try {
|
||||
$this->response = $client->send($request);
|
||||
} catch (ClientException $e) {
|
||||
$this->response = $e->getResponse();
|
||||
}
|
||||
}
|
||||
|
||||
public static function removeFile($path, $filename){
|
||||
if (file_exists("$path" . "$filename")) {
|
||||
unlink("$path" . "$filename");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given User :user modifies text of :filename with text :text
|
||||
* @param string $user
|
||||
* @param string $filename
|
||||
* @param string $text
|
||||
*/
|
||||
public function modifyTextOfFile($user, $filename, $text) {
|
||||
self::removeFile("../../data/$user/files", "$filename");
|
||||
file_put_contents("../../data/$user/files" . "$filename", "$text");
|
||||
}
|
||||
|
||||
public function createFileSpecificSize($name, $size) {
|
||||
$file = fopen("work/" . "$name", 'w');
|
||||
fseek($file, $size - 1 ,SEEK_CUR);
|
||||
fwrite($file,'a'); // write a dummy char at SIZE position
|
||||
fclose($file);
|
||||
}
|
||||
|
||||
public function createFileWithText($name, $text){
|
||||
$file = fopen("work/" . "$name", 'w');
|
||||
fwrite($file, $text);
|
||||
fclose($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given file :filename of size :size is created in local storage
|
||||
* @param string $filename
|
||||
* @param string $size
|
||||
*/
|
||||
public function fileIsCreatedInLocalStorageWithSize($filename, $size) {
|
||||
$this->createFileSpecificSize("local_storage/$filename", $size);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given file :filename with text :text is created in local storage
|
||||
* @param string $filename
|
||||
* @param string $text
|
||||
*/
|
||||
public function fileIsCreatedInLocalStorageWithText($filename, $text) {
|
||||
$this->createFileWithText("local_storage/$filename", $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When Sleep for :seconds seconds
|
||||
* @param int $seconds
|
||||
*/
|
||||
public function sleepForSeconds($seconds) {
|
||||
sleep((int)$seconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* @BeforeSuite
|
||||
*/
|
||||
public static function addFilesToSkeleton(){
|
||||
for ($i=0; $i<5; $i++){
|
||||
file_put_contents("../../core/skeleton/" . "textfile" . "$i" . ".txt", "Nextcloud test text file\n");
|
||||
}
|
||||
if (!file_exists("../../core/skeleton/FOLDER")) {
|
||||
mkdir("../../core/skeleton/FOLDER", 0777, true);
|
||||
}
|
||||
if (!file_exists("../../core/skeleton/PARENT")) {
|
||||
mkdir("../../core/skeleton/PARENT", 0777, true);
|
||||
}
|
||||
file_put_contents("../../core/skeleton/PARENT/" . "parent.txt", "Nextcloud test text file\n");
|
||||
if (!file_exists("../../core/skeleton/PARENT/CHILD")) {
|
||||
mkdir("../../core/skeleton/PARENT/CHILD", 0777, true);
|
||||
}
|
||||
file_put_contents("../../core/skeleton/PARENT/CHILD/" . "child.txt", "Nextcloud test text file\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* @AfterSuite
|
||||
*/
|
||||
public static function removeFilesFromSkeleton(){
|
||||
for ($i=0; $i<5; $i++){
|
||||
self::removeFile("../../core/skeleton/", "textfile" . "$i" . ".txt");
|
||||
}
|
||||
if (is_dir("../../core/skeleton/FOLDER")) {
|
||||
rmdir("../../core/skeleton/FOLDER");
|
||||
}
|
||||
self::removeFile("../../core/skeleton/PARENT/CHILD/", "child.txt");
|
||||
if (is_dir("../../core/skeleton/PARENT/CHILD")) {
|
||||
rmdir("../../core/skeleton/PARENT/CHILD");
|
||||
}
|
||||
self::removeFile("../../core/skeleton/PARENT/", "parent.txt");
|
||||
if (is_dir("../../core/skeleton/PARENT")) {
|
||||
rmdir("../../core/skeleton/PARENT");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @BeforeScenario @local_storage
|
||||
*/
|
||||
public static function removeFilesFromLocalStorageBefore(){
|
||||
$dir = "./work/local_storage/";
|
||||
$di = new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS);
|
||||
$ri = new RecursiveIteratorIterator($di, RecursiveIteratorIterator::CHILD_FIRST);
|
||||
foreach ( $ri as $file ) {
|
||||
$file->isDir() ? rmdir($file) : unlink($file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @AfterScenario @local_storage
|
||||
*/
|
||||
public static function removeFilesFromLocalStorageAfter(){
|
||||
$dir = "./work/local_storage/";
|
||||
$di = new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS);
|
||||
$ri = new RecursiveIteratorIterator($di, RecursiveIteratorIterator::CHILD_FIRST);
|
||||
foreach ( $ri as $file ) {
|
||||
$file->isDir() ? rmdir($file) : unlink($file);
|
||||
}
|
||||
}
|
||||
}
|
||||
194
tests/integration/features/bootstrap/FeatureContext.php
Normal file
194
tests/integration/features/bootstrap/FeatureContext.php
Normal file
@@ -0,0 +1,194 @@
|
||||
<?php
|
||||
|
||||
use Behat\Behat\Context\Context;
|
||||
use Behat\Gherkin\Node\TableNode;
|
||||
use GuzzleHttp\Client;
|
||||
use Behat\Behat\Tester\Exception\PendingException;
|
||||
use Behat\Gherkin\Node\PyStringNode;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
class FeatureContext implements Context {
|
||||
|
||||
use WebDav;
|
||||
|
||||
/** @var string */
|
||||
private $mappedUserId;
|
||||
|
||||
private $lastInsertIds = array();
|
||||
|
||||
/**
|
||||
* @BeforeSuite
|
||||
*/
|
||||
public static function addFilesToSkeleton() {
|
||||
}
|
||||
/**
|
||||
* @When :user requests the deck list
|
||||
*/
|
||||
/**
|
||||
* @When Sending a :method to :url with JSON
|
||||
*/
|
||||
public function sendingAToWithJSON($method, $url, \Behat\Gherkin\Node\PyStringNode $data) {
|
||||
$baseUrl = substr($this->baseUrl, 0, -5);
|
||||
|
||||
$client = new Client;
|
||||
$request = $client->createRequest(
|
||||
$method,
|
||||
$baseUrl . $url,
|
||||
[
|
||||
'cookies' => $this->cookieJar,
|
||||
'json' => json_decode($data)
|
||||
]
|
||||
);
|
||||
$request->addHeader('requesttoken', $this->requestToken);
|
||||
try {
|
||||
$this->response = $client->send($request);
|
||||
} catch (ClientException $e) {
|
||||
$this->response = $e->getResponse();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @When :user creates a new deck with name :name
|
||||
*/
|
||||
public function createsANewDeckWithName($user, $content) {
|
||||
$client = new GuzzleHttp\Client();
|
||||
$this->response = $client->post(
|
||||
'http://localhost:8080/index.php/apps/deck/boards',
|
||||
[
|
||||
'form_params' => [
|
||||
'name' => $name,
|
||||
],
|
||||
'auth' => [
|
||||
$this->mappedUserId,
|
||||
'test',
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then the response should have a status code :code
|
||||
* @param string $code
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function theResponseShouldHaveAStatusCode($code) {
|
||||
$currentCode = $this->response->getStatusCode();
|
||||
if ($currentCode !== (int)$code) {
|
||||
throw new InvalidArgumentException(
|
||||
sprintf(
|
||||
'Expected %s as code got %s',
|
||||
$code,
|
||||
$currentCode
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then the response should be a JSON array with the following mandatory values
|
||||
* @param TableNode $table
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function theResponseShouldBeAJsonArrayWithTheFollowingMandatoryValues(TableNode $table) {
|
||||
$expectedValues = $table->getColumnsHash();
|
||||
$realResponseArray = json_decode($this->response->getBody()->getContents(), true);
|
||||
|
||||
foreach ($expectedValues as $value) {
|
||||
if ((string)$realResponseArray[$value['key']] !== (string)$value['value']) {
|
||||
throw new InvalidArgumentException(
|
||||
sprintf(
|
||||
'Expected %s for key %s got %s',
|
||||
(string)$value['value'],
|
||||
$value['key'],
|
||||
(string)$realResponseArray[$value['key']]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then the response should be a JSON array with a length of :length
|
||||
* @param int $length
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function theResponseShouldBeAJsonArrayWithALengthOf($length) {
|
||||
$realResponseArray = json_decode($this->response->getBody()->getContents(), true);
|
||||
PHPUnit_Framework_Assert::assertEquals($realResponseArray, "foo");
|
||||
if((int)count($realResponseArray) !== (int)$length) {
|
||||
throw new InvalidArgumentException(
|
||||
sprintf(
|
||||
'Expected %d as length got %d',
|
||||
$length,
|
||||
count($realResponseArray)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^I should get:$/
|
||||
*
|
||||
* @param PyStringNode $string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function iShouldGet(PyStringNode $string)
|
||||
{
|
||||
if ((string) $string !== trim($this->cliOutput)) {
|
||||
throw new Exception(sprintf(
|
||||
'Expected "%s" but received "%s".',
|
||||
$string,
|
||||
$this->cliOutput
|
||||
));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private function sendJSONrequest($method, $url, $data) {
|
||||
$baseUrl = substr($this->baseUrl, 0, -5);
|
||||
|
||||
$client = new Client;
|
||||
$request = $client->createRequest(
|
||||
$method,
|
||||
$baseUrl . $url,
|
||||
[
|
||||
'cookies' => $this->cookieJar,
|
||||
'json' => $data
|
||||
]
|
||||
);
|
||||
$request->addHeader('requesttoken', $this->requestToken);
|
||||
try {
|
||||
$this->response = $client->send($request);
|
||||
} catch (ClientException $e) {
|
||||
$this->response = $e->getResponse();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given /^creates a board named "([^"]*)" with color "([^"]*)"$/
|
||||
*/
|
||||
public function createsABoardNamedWithColor($title, $color) {
|
||||
|
||||
$this->sendJSONrequest('POST', '/index.php/apps/deck/boards', [
|
||||
'title' => $title,
|
||||
'color' => $color
|
||||
]
|
||||
);
|
||||
$response = json_decode($this->response->getBody()->getContents(), true);
|
||||
$this->lastInsertIds[$title] = $response['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^"([^"]*)" fetches the board named "([^"]*)"$/
|
||||
*/
|
||||
public function fetchesTheBoardNamed($user, $boardName) {
|
||||
$this->loggingInUsingWebAs($user);
|
||||
$id = $this->lastInsertIds[$boardName];
|
||||
$this->sendJSONrequest('GET', '/index.php/apps/deck/boards/'.$id, []);
|
||||
}
|
||||
|
||||
}
|
||||
37
tests/integration/features/decks.feature
Normal file
37
tests/integration/features/decks.feature
Normal file
@@ -0,0 +1,37 @@
|
||||
Feature: decks
|
||||
|
||||
Background:
|
||||
Given user "user0" exists
|
||||
|
||||
Scenario: Request the main frontend page
|
||||
Given Logging in using web as "admin"
|
||||
When Sending a "GET" to "/index.php/apps/deck" without requesttoken
|
||||
Then the HTTP status code should be "200"
|
||||
|
||||
Scenario: Fetch the board list
|
||||
Given Logging in using web as "admin"
|
||||
When Sending a "GET" to "/index.php/apps/deck/boards" with requesttoken
|
||||
Then the HTTP status code should be "200"
|
||||
And the Content-Type should be "application/json; charset=utf-8"
|
||||
|
||||
Scenario: Fetch board details of a nonexisting board
|
||||
Given Logging in using web as "admin"
|
||||
When Sending a "GET" to "/index.php/apps/deck/boards/13379" with requesttoken
|
||||
Then the HTTP status code should be "403"
|
||||
And the Content-Type should be "application/json; charset=utf-8"
|
||||
|
||||
Scenario: Create a new board
|
||||
Given Logging in using web as "admin"
|
||||
When Sending a "POST" to "/index.php/apps/deck/boards" with JSON
|
||||
"""
|
||||
{
|
||||
"title": "MyBoard",
|
||||
"color": "000000"
|
||||
}
|
||||
"""
|
||||
Then the HTTP status code should be "200"
|
||||
And the Content-Type should be "application/json; charset=utf-8"
|
||||
And the response should be a JSON array with the following mandatory values
|
||||
|key|value|
|
||||
|title|MyBoard|
|
||||
|color|000000|
|
||||
45
tests/integration/run.sh
Executable file
45
tests/integration/run.sh
Executable file
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
OC_PATH=../../../../
|
||||
OCC=${OC_PATH}occ
|
||||
SCENARIO_TO_RUN=$1
|
||||
HIDE_OC_LOGS=$2
|
||||
|
||||
# Nextcloud integration tests composer
|
||||
(
|
||||
cd ${OC_PATH}build/integration
|
||||
composer install
|
||||
)
|
||||
INSTALLED=$($OCC status | grep installed: | cut -d " " -f 5)
|
||||
|
||||
if [ "$INSTALLED" == "true" ]; then
|
||||
$OCC app:enable deck
|
||||
else
|
||||
if [ "$SCENARIO_TO_RUN" != "setup_features/setup.feature" ]; then
|
||||
echo "Nextcloud instance needs to be installed" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
composer install
|
||||
composer dump-autoload
|
||||
|
||||
# avoid port collision on jenkins - use $EXECUTOR_NUMBER
|
||||
if [ -z "$EXECUTOR_NUMBER" ]; then
|
||||
EXECUTOR_NUMBER=0
|
||||
fi
|
||||
PORT=$((8080 + $EXECUTOR_NUMBER))
|
||||
echo $PORT
|
||||
php -S localhost:$PORT -t $OC_PATH &
|
||||
PHPPID=$!
|
||||
echo $PHPPID
|
||||
|
||||
export TEST_SERVER_URL="http://localhost:$PORT/ocs/"
|
||||
|
||||
vendor/bin/behat
|
||||
RESULT=$?
|
||||
|
||||
kill $PHPPID
|
||||
|
||||
echo "runsh: Exit code: $RESULT"
|
||||
exit $RESULT
|
||||
@@ -1,7 +1,10 @@
|
||||
<phpunit bootstrap="bootstrap.php" colors="true">
|
||||
<testsuites>
|
||||
<testsuite name="integration">
|
||||
<directory>./integration</directory>
|
||||
<testsuite name="integration-database">
|
||||
<directory>./integration/database</directory>
|
||||
</testsuite>
|
||||
<testsuite name="integration-app">
|
||||
<directory>./integration/app</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
|
||||
namespace OCA\Deck\Db;
|
||||
|
||||
use OCP\IGroupManager;
|
||||
use OCP\IUserManager;
|
||||
use Test\AppFramework\Db\MapperTestUtility;
|
||||
|
||||
/**
|
||||
@@ -33,6 +35,8 @@ class AclMapperTest extends MapperTestUtility {
|
||||
private $dbConnection;
|
||||
private $aclMapper;
|
||||
private $boardMapper;
|
||||
private $userManager;
|
||||
private $groupManager;
|
||||
|
||||
// Data
|
||||
private $acls;
|
||||
@@ -43,11 +47,16 @@ class AclMapperTest extends MapperTestUtility {
|
||||
|
||||
$this->dbConnection = \OC::$server->getDatabaseConnection();
|
||||
$this->aclMapper = new AclMapper($this->dbConnection);
|
||||
$this->userManager = $this->createMock(IUserManager::class);
|
||||
$this->groupManager = $this->createMock(IGroupManager::class);
|
||||
$this->boardMapper = new BoardMapper(
|
||||
$this->dbConnection,
|
||||
\OC::$server->query(LabelMapper::class),
|
||||
$this->aclMapper,
|
||||
\OC::$server->query(StackMapper::class));
|
||||
\OC::$server->query(StackMapper::class),
|
||||
$this->userManager,
|
||||
$this->groupManager
|
||||
);
|
||||
|
||||
$this->boards = [
|
||||
$this->boardMapper->insert($this->getBoard('MyBoard 1', 'user1')),
|
||||
|
||||
@@ -23,7 +23,8 @@
|
||||
|
||||
namespace OCA\Deck\Db;
|
||||
|
||||
class AclTest extends \PHPUnit_Framework_TestCase {
|
||||
class AclTest extends \Test\TestCase {
|
||||
|
||||
private function createAclUser() {
|
||||
$acl = new Acl();
|
||||
$acl->setId(1);
|
||||
@@ -35,6 +36,7 @@ class AclTest extends \PHPUnit_Framework_TestCase {
|
||||
$acl->setPermissionManage(true);
|
||||
return $acl;
|
||||
}
|
||||
|
||||
private function createAclGroup() {
|
||||
$acl = new Acl();
|
||||
$acl->setId(1);
|
||||
@@ -46,6 +48,7 @@ class AclTest extends \PHPUnit_Framework_TestCase {
|
||||
$acl->setPermissionManage(true);
|
||||
return $acl;
|
||||
}
|
||||
|
||||
public function testJsonSerialize() {
|
||||
$acl = $this->createAclUser();
|
||||
$this->assertEquals([
|
||||
@@ -70,6 +73,7 @@ class AclTest extends \PHPUnit_Framework_TestCase {
|
||||
'owner' => false
|
||||
], $acl->jsonSerialize());
|
||||
}
|
||||
|
||||
public function testSetOwner() {
|
||||
$acl = $this->createAclUser();
|
||||
$acl->setOwner(1);
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
|
||||
namespace OCA\Deck\Db;
|
||||
|
||||
use OCP\IGroupManager;
|
||||
use OCP\IUserManager;
|
||||
use Test\AppFramework\Db\MapperTestUtility;
|
||||
|
||||
/**
|
||||
@@ -33,6 +35,8 @@ class BoardMapperTest extends MapperTestUtility {
|
||||
private $dbConnection;
|
||||
private $aclMapper;
|
||||
private $boardMapper;
|
||||
private $userManager;
|
||||
private $groupManager;
|
||||
|
||||
// Data
|
||||
private $acls;
|
||||
@@ -41,12 +45,17 @@ class BoardMapperTest extends MapperTestUtility {
|
||||
public function setup(){
|
||||
parent::setUp();
|
||||
|
||||
$this->userManager = $this->createMock(IUserManager::class);
|
||||
$this->groupManager = $this->createMock(IGroupManager::class);
|
||||
|
||||
$this->dbConnection = \OC::$server->getDatabaseConnection();
|
||||
$this->boardMapper = new BoardMapper(
|
||||
$this->dbConnection,
|
||||
\OC::$server->query(LabelMapper::class),
|
||||
\OC::$server->query(AclMapper::class),
|
||||
\OC::$server->query(StackMapper::class)
|
||||
\OC::$server->query(StackMapper::class),
|
||||
$this->userManager,
|
||||
$this->groupManager
|
||||
);
|
||||
$this->aclMapper = \OC::$server->query(AclMapper::class);
|
||||
$this->labelMapper = \OC::$server->query(LabelMapper::class);
|
||||
|
||||
@@ -20,8 +20,8 @@ class BoardTest extends \PHPUnit_Framework_TestCase {
|
||||
'title' => "My Board",
|
||||
'owner' => "admin",
|
||||
'color' => "000000",
|
||||
'labels' => null,
|
||||
'acl' => null,
|
||||
'labels' => array(),
|
||||
'acl' => array(),
|
||||
'archived' => false
|
||||
], $board->jsonSerialize());
|
||||
}
|
||||
@@ -35,7 +35,7 @@ class BoardTest extends \PHPUnit_Framework_TestCase {
|
||||
'owner' => "admin",
|
||||
'color' => "000000",
|
||||
'labels' => array("foo", "bar"),
|
||||
'acl' => null,
|
||||
'acl' => array(),
|
||||
'archived' => false
|
||||
], $board->jsonSerialize());
|
||||
}
|
||||
@@ -55,8 +55,8 @@ class BoardTest extends \PHPUnit_Framework_TestCase {
|
||||
'title' => "My Board",
|
||||
'owner' => "admin",
|
||||
'color' => "000000",
|
||||
'labels' => null,
|
||||
'acl' => null,
|
||||
'labels' => array(),
|
||||
'acl' => array(),
|
||||
'archived' => false,
|
||||
'shared' => 1,
|
||||
], $board->jsonSerialize());
|
||||
|
||||
@@ -29,34 +29,35 @@ use OCA\Deck\Db\AclMapper;
|
||||
use OCA\Deck\Db\Board;
|
||||
use OCA\Deck\Db\BoardMapper;
|
||||
use OCA\Deck\Db\LabelMapper;
|
||||
use OCA\Deck\Db\User;
|
||||
use OCP\IGroupManager;
|
||||
use OCP\ILogger;
|
||||
use OCP\IUser;
|
||||
use OCP\IUserManager;
|
||||
|
||||
class BoardServiceTest extends \PHPUnit_Framework_TestCase {
|
||||
class BoardServiceTest extends \Test\TestCase {
|
||||
|
||||
/** @var BoardService */
|
||||
private $service;
|
||||
private $logger;
|
||||
/** @var L10N */
|
||||
private $l10n;
|
||||
/** @var LabelMapper */
|
||||
private $labelMapper;
|
||||
/** @var AclMapper */
|
||||
private $aclMapper;
|
||||
/** @var BoardMapper */
|
||||
private $boardMapper;
|
||||
private $groupManager;
|
||||
/** @var PermissionService */
|
||||
private $permissionService;
|
||||
|
||||
private $userId = 'admin';
|
||||
|
||||
public function setUp() {
|
||||
$this->l10n = $this->getMockBuilder(L10N::class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->aclMapper = $this->getMockBuilder(AclMapper::class)
|
||||
->disableOriginalConstructor()->getMock();
|
||||
$this->boardMapper = $this->getMockBuilder(BoardMapper::class)
|
||||
->disableOriginalConstructor()->getMock();
|
||||
$this->labelMapper = $this->getMockBuilder(LabelMapper::class)
|
||||
->disableOriginalConstructor()->getMock();
|
||||
$this->permissionService = $this->getMockBuilder(PermissionService::class)
|
||||
->disableOriginalConstructor()->getMock();
|
||||
$this->l10n = $this->createMock(L10N::class);
|
||||
$this->aclMapper = $this->createMock(AclMapper::class);
|
||||
$this->boardMapper = $this->createMock(BoardMapper::class);
|
||||
$this->labelMapper = $this->createMock(LabelMapper::class);
|
||||
$this->permissionService = $this->createMock(PermissionService::class);
|
||||
|
||||
$this->service = new BoardService(
|
||||
$this->boardMapper,
|
||||
@@ -65,32 +66,43 @@ class BoardServiceTest extends \PHPUnit_Framework_TestCase {
|
||||
$this->aclMapper,
|
||||
$this->permissionService
|
||||
);
|
||||
|
||||
$user = $this->createMock(IUser::class);
|
||||
$user->method('getUID')->willReturn('admin');
|
||||
}
|
||||
|
||||
public function testFindAll() {
|
||||
$b1 = new Board();
|
||||
$b1->setId(1);
|
||||
$b2 = new Board();
|
||||
$b2->setId(2);
|
||||
$b3 = new Board();
|
||||
$b3->setId(3);
|
||||
$this->boardMapper->expects($this->once())
|
||||
->method('findAllByUser')
|
||||
->with('admin')
|
||||
->willReturn([1,2,3,6,7]);
|
||||
->willReturn([$b1, $b2]);
|
||||
$this->boardMapper->expects($this->once())
|
||||
->method('findAllByGroups')
|
||||
->with('admin', ['a', 'b', 'c'])
|
||||
->willReturn([4,5,6,7,8]);
|
||||
->willReturn([$b2, $b3]);
|
||||
$userinfo = [
|
||||
'user' => 'admin',
|
||||
'groups' => ['a', 'b', 'c']
|
||||
];
|
||||
$result = $this->service->findAll($userinfo);
|
||||
sort($result);
|
||||
$this->assertEquals([1,2,3,4,5,6,7,8], $result);
|
||||
$this->assertEquals([$b1, $b2, $b3], $result);
|
||||
}
|
||||
|
||||
public function testFind() {
|
||||
$b1 = new Board();
|
||||
$b1->setId(1);
|
||||
$this->boardMapper->expects($this->once())
|
||||
->method('find')
|
||||
->with(123)
|
||||
->willReturn(1);
|
||||
$this->assertEquals(1, $this->service->find(123));
|
||||
->with(1)
|
||||
->willReturn($b1);
|
||||
$this->assertEquals($b1, $this->service->find(1));
|
||||
}
|
||||
|
||||
public function testCreate() {
|
||||
@@ -130,9 +142,11 @@ class BoardServiceTest extends \PHPUnit_Framework_TestCase {
|
||||
}
|
||||
|
||||
public function testDelete() {
|
||||
$board = new Board();
|
||||
$board->setOwner('admin');
|
||||
$this->boardMapper->expects($this->once())
|
||||
->method('find')
|
||||
->willReturn(new Board());
|
||||
->willReturn($board);
|
||||
$this->boardMapper->expects($this->once())
|
||||
->method('delete')
|
||||
->willReturn(1);
|
||||
@@ -140,6 +154,8 @@ class BoardServiceTest extends \PHPUnit_Framework_TestCase {
|
||||
}
|
||||
|
||||
public function testAddAcl() {
|
||||
$user = $this->createMock(IUser::class);
|
||||
$user->method('getUID')->willReturn('admin');
|
||||
$acl = new Acl();
|
||||
$acl->setBoardId(123);
|
||||
$acl->setType('user');
|
||||
@@ -147,6 +163,9 @@ class BoardServiceTest extends \PHPUnit_Framework_TestCase {
|
||||
$acl->setPermissionEdit(true);
|
||||
$acl->setPermissionShare(true);
|
||||
$acl->setPermissionManage(true);
|
||||
$acl->resolveRelation('participant', function($participant) use (&$user) {
|
||||
return null;
|
||||
});
|
||||
$this->aclMapper->expects($this->once())
|
||||
->method('insert')
|
||||
->with($acl)
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Deck\Controller;
|
||||
|
||||
use OCA\Deck\Db\Acl;
|
||||
use OCP\IGroup;
|
||||
use OCP\IUser;
|
||||
|
||||
class ShareControllerTest extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
private $controller;
|
||||
private $request;
|
||||
private $userManager;
|
||||
private $groupManager;
|
||||
private $boardService;
|
||||
private $permissionService;
|
||||
private $userId = 'user';
|
||||
|
||||
public function setUp() {
|
||||
$this->l10n = $this->request = $this->getMockBuilder(
|
||||
'\OCP\IL10n')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->request = $this->getMockBuilder(
|
||||
'\OCP\IRequest')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->userManager = $this->getMockBuilder(
|
||||
'\OCP\IUserManager')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->groupManager = $this->getMockBuilder(
|
||||
'\OCP\IGroupManager')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->boardService = $this->getMockBuilder(
|
||||
'\OCA\Deck\Service\BoardService')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$this->groupManager->method('getUserGroupIds')
|
||||
->willReturn(['admin', 'group1', 'group2']);
|
||||
$this->userManager->method('get')
|
||||
->with($this->userId)
|
||||
->willReturn('user');
|
||||
|
||||
$this->controller = new ShareController(
|
||||
'deck',
|
||||
$this->request,
|
||||
$this->userManager,
|
||||
$this->groupManager,
|
||||
$this->boardService,
|
||||
$this->userId
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function testSearchGroup() {
|
||||
$group = $this->getMockBuilder(IGroup::class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$group->expects($this->once())
|
||||
->method('getGID')
|
||||
->willReturn('foo');
|
||||
$groups = [$group];
|
||||
$this->groupManager->expects($this->once())
|
||||
->method('search')
|
||||
->with('foo')
|
||||
->willReturn($groups);
|
||||
$this->userManager->expects($this->once())
|
||||
->method('searchDisplayName')
|
||||
->willReturn([]);
|
||||
$actual = $this->controller->searchUser('foo');
|
||||
|
||||
$acl = new Acl();
|
||||
$acl->setType('group');
|
||||
$acl->setParticipant('foo');
|
||||
$acl->setPermissionEdit(true);
|
||||
$acl->setPermissionShare(true);
|
||||
$acl->setPermissionManage(true);
|
||||
$this->assertEquals([$acl], $actual);
|
||||
}
|
||||
public function testSearchUser() {
|
||||
$user = $this->getMockBuilder(IUser::class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$user->expects($this->any())
|
||||
->method('getUID')
|
||||
->willReturn('foo');
|
||||
$users = [$user];
|
||||
$this->groupManager->expects($this->once())
|
||||
->method('search')
|
||||
->willReturn([]);
|
||||
|
||||
$this->userManager->expects($this->once())
|
||||
->method('searchDisplayName')
|
||||
->with('foo')
|
||||
->willReturn($users);
|
||||
$actual = $this->controller->searchUser('foo');
|
||||
|
||||
$acl = new Acl();
|
||||
$acl->setType('user');
|
||||
$acl->setParticipant('foo');
|
||||
$acl->setPermissionEdit(true);
|
||||
$acl->setPermissionShare(true);
|
||||
$acl->setPermissionManage(true);
|
||||
$this->assertEquals([$acl], $actual);
|
||||
}
|
||||
|
||||
public function testSearchUserExcludeOwn() {
|
||||
$user = $this->getMockBuilder(IUser::class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$user->expects($this->any())
|
||||
->method('getUID')
|
||||
->willReturn('user');
|
||||
$users = [$user];
|
||||
$this->groupManager->expects($this->once())
|
||||
->method('search')
|
||||
->willReturn([]);
|
||||
|
||||
$this->userManager->expects($this->once())
|
||||
->method('searchDisplayName')
|
||||
->with('user')
|
||||
->willReturn($users);
|
||||
$actual = $this->controller->searchUser('user');
|
||||
$this->assertEquals([], $actual);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user