diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index d2bf8786d..8fcdf2ef9 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -6,21 +6,73 @@ on: branches: - master - stable* + - dev* jobs: + php-cs-fixer: + runs-on: ubuntu-latest + + strategy: + matrix: + php-versions: [7.4] + + name: php-cs + + steps: + - name: Checkout + uses: actions/checkout@master + - name: Set up php + uses: shivammathur/setup-php@master + with: + php-version: ${{ matrix.php-versions }} + coverage: none + - name: Install dependencies + run: composer i + - name: Run coding standards check + run: composer run cs:check + + app-code-check: + runs-on: ubuntu-latest + + strategy: + matrix: + php-versions: ['7.2', '7.3', '7.4'] + nextcloud-versions: ['master'] + + name: Nextcloud AppCode check + + steps: + - name: Set up php ${{ matrix.php-versions }} + uses: shivammathur/setup-php@master + with: + php-version: ${{ matrix.php-versions }} + extensions: ctype,curl,dom,gd,iconv,intl,json,mbstring,openssl,posix,sqlite,xml,zip + coverage: xdebug + - name: Checkout Nextcloud ${{ matrix.nextcloud-versions }} + run: git clone https://github.com/nextcloud/server.git --recursive --depth 1 -b ${{ matrix.nextcloud-versions }} nextcloud + - name: Run tests + run: php -f nextcloud/occ maintenance:install --database-name oc_autotest --database-user oc_autotest --admin-user admin --admin-pass admin --database sqlite --database-pass='' + - name: Checkout + uses: actions/checkout@master + with: + path: nextcloud/apps/mail + - name: Run tests + run: php -f nextcloud/occ app:check-code mail + node: runs-on: ubuntu-latest strategy: matrix: - node-version: [12.x] + node-versions: [12.x] + name: eslint node ${{ matrix.node-versions }} steps: - uses: actions/checkout@v2 - - name: Use node ${{ matrix.node-version }} + - name: Use node ${{ matrix.node-versions }} uses: actions/setup-node@v1 with: - node-version: ${{ matrix.node-version }} + node-version: ${{ matrix.node-versions }} - name: Install dependencies run: npm ci - name: ESLint @@ -33,14 +85,14 @@ jobs: matrix: node-versions: [12.x] - name: stylelint node${{ matrix.node-versions }} + name: stylelint node ${{ matrix.node-versions }} steps: - uses: actions/checkout@v2 - name: Set up node ${{ matrix.node-versions }} uses: actions/setup-node@v1 with: - node-versions: ${{ matrix.node-versions }} + node-version: ${{ matrix.node-versions }} - name: Install dependencies run: npm ci diff --git a/.gitignore b/.gitignore index 499354f3d..1893c8baf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .DS_Store .sass-cache/ +.php_cs.cache .project/ .idea/ build/ diff --git a/.php_cs.dist b/.php_cs.dist new file mode 100644 index 000000000..71fd56908 --- /dev/null +++ b/.php_cs.dist @@ -0,0 +1,20 @@ +getFinder() + ->ignoreVCSIgnored(true) + ->notPath('build') + ->notPath('l10n') + ->notPath('lib/Vendor') + ->notPath('src') + ->notPath('vendor') + ->notPath('tests') + ->in(__DIR__); +return $config; diff --git a/.stylelintignore b/.stylelintignore new file mode 100644 index 000000000..2d0c92986 --- /dev/null +++ b/.stylelintignore @@ -0,0 +1 @@ +*.gif diff --git a/CHANGELOG.md b/CHANGELOG.md index bc8a63481..fd2eac906 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,18 @@ # Changelog All notable changes to this project will be documented in this file. -## [1.5.0] - tbd +## [1.5.1 - beta2] - 2020-08-17 + - Lookup calendars for conflict #1056 #747 + - convert URIs in description into clickable links #1067 + - added a poll to force poll appear under relevant polls navigation entry for all users #1072 + - move cloning of options to backend #1058 + - add user settings + - some style fixes + - updated dependencies + - load app icons via url-loader + +## [1.5.0 - beta1] - 2020-08-17 + - Drop support for Nextcloud 16 - Stop immediatley sending of invitation mails after adding a share #1007 #935 - Fix: Hide usernames in notification mail, if results in poll are hidden #990 #980 - Adding a REST-API #966 @@ -13,7 +24,8 @@ All notable changes to this project will be documented in this file. - Changed some icons #862 - Added the ability to confirm options #939 #136 - A lot of refactoring - + - Don't invite disabled users #997 + - add time zone info to date polls #1076 ## [1.4.3] - 2020-05-03 diff --git a/appinfo/info.xml b/appinfo/info.xml index 37665b1dc..4aaafc77b 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -5,7 +5,7 @@ Polls A polls app, similar to doodle/dudle with the possibility to restrict access. A polls app, similar to doodle/dudle with the possibility to restrict access (members, certain groups/users, hidden and public). - 1.5.0 + 1.5.2 agpl Vinzenz Rosenkranz René Gieling @@ -23,7 +23,7 @@ https://raw.githubusercontent.com/nextcloud/polls/master/screenshots/vote.png https://raw.githubusercontent.com/nextcloud/polls/master/screenshots/edit-poll.png - + OCA\Polls\Cron\NotificationCron diff --git a/appinfo/routes.php b/appinfo/routes.php index 35c44bd66..3191fce5c 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -27,73 +27,79 @@ ['name' => 'page#index', 'url' => '/not-found', 'verb' => 'GET', 'postfix' => 'notfound'], ['name' => 'page#index', 'url' => '/list/{id}', 'verb' => 'GET', 'postfix' => 'list'], ['name' => 'page#index', 'url' => '/vote/{id}', 'verb' => 'GET', 'postfix' => 'vote'], + ['name' => 'page#vote_public', 'url' => '/s/{token}', 'verb' => 'GET', 'postfix' => 'public'], ['name' => 'page#vote_public', 'url' => '/poll/{token}', 'verb' => 'GET', 'postfix' => 'oldpublic'], - ['name' => 'subscription#get', 'url' => '/subscription/{pollId}', 'verb' => 'GET'], - ['name' => 'subscription#set', 'url' => '/subscription', 'verb' => 'POST'], - - ['name' => 'comment#getByToken', 'url' => '/comments/s/{token}', 'verb' => 'GET'], - ['name' => 'comment#get', 'url' => '/comments/{pollId}', 'verb' => 'GET'], - ['name' => 'comment#add', 'url' => '/comment/add', 'verb' => 'POST'], - ['name' => 'comment#delete', 'url' => '/comment/delete', 'verb' => 'POST'], - - ['name' => 'vote#getByToken', 'url' => '/votes/get/s/{token}', 'verb' => 'GET'], - ['name' => 'vote#setByToken', 'url' => '/vote/set/s', 'verb' => 'POST'], - ['name' => 'vote#get', 'url' => '/votes/get/{pollId}', 'verb' => 'GET'], - ['name' => 'vote#set', 'url' => '/vote/set', 'verb' => 'POST'], - // ['name' => 'vote#write', 'url' => '/vote/write/', 'verb' => 'POST'], - ['name' => 'vote#delete', 'url' => '/votes/delete', 'verb' => 'POST'], - - ['name' => 'option#get', 'url' => '/options/get/{pollId}', 'verb' => 'GET'], - ['name' => 'option#add', 'url' => '/option/add', 'verb' => 'POST'], - ['name' => 'option#update', 'url' => '/option/update', 'verb' => 'POST'], - ['name' => 'option#reorder', 'url' => '/option/reorder', 'verb' => 'POST'], - ['name' => 'option#remove', 'url' => '/option/remove', 'verb' => 'POST'], - ['name' => 'option#getByToken', 'url' => '/options/get/s/{token}', 'verb' => 'GET'], - ['name' => 'poll#get', 'url' => '/polls/get/{pollId}', 'verb' => 'GET', 'postfix' => 'auth'], ['name' => 'poll#get', 'url' => '/polls/get/s/{token}', 'verb' => 'GET', 'postfix' => 'public'], ['name' => 'poll#add', 'url' => '/polls/add', 'verb' => 'POST'], ['name' => 'poll#update', 'url' => '/polls/update/{pollId}', 'verb' => 'PUT'], - ['name' => 'poll#list', 'url' => '/polls/list', 'verb' => 'GET'], ['name' => 'poll#delete', 'url' => '/polls/delete/{pollId}', 'verb' => 'GET'], ['name' => 'poll#deletePermanently', 'url' => '/polls/delete/permanent/{pollId}', 'verb' => 'GET'], ['name' => 'poll#clone', 'url' => '/polls/clone/{pollId}', 'verb' => 'GET'], + ['name' => 'poll#getParticipantsEmailAddresses', 'url' => '/polls/addresses/{pollId}', 'verb' => 'GET'], + + ['name' => 'option#list', 'url' => '/polls/{pollId}/options', 'verb' => 'GET'], + ['name' => 'option#reorder', 'url' => '/polls/{pollId}/options/reorder', 'verb' => 'POST'], + ['name' => 'option#add', 'url' => '/option', 'verb' => 'POST'], + ['name' => 'option#update', 'url' => '/option/{optionId}', 'verb' => 'PUT'], + ['name' => 'option#delete', 'url' => '/option/{optionId}', 'verb' => 'DELETE'], + ['name' => 'option#confirm', 'url' => '/option/{optionId}/confirm', 'verb' => 'PUT'], + ['name' => 'option#sequence', 'url' => '/option/{optionId}/sequence', 'verb' => 'POST'], + ['name' => 'option#findCalendarEvents', 'url' => '/option/{optionId}/events', 'verb' => 'GET'], + // ['name' => 'option#listByToken', 'url' => '/options/get/s/{token}', 'verb' => 'GET'], + + ['name' => 'vote#set', 'url' => '/vote/set', 'verb' => 'POST'], + ['name' => 'vote#setByToken', 'url' => '/vote/set/s', 'verb' => 'POST'], + ['name' => 'vote#delete', 'url' => '/votes/delete', 'verb' => 'POST'], + // ['name' => 'vote#get', 'url' => '/votes/get/{pollId}', 'verb' => 'GET'], + // ['name' => 'vote#getByToken', 'url' => '/votes/get/s/{token}', 'verb' => 'GET'], ['name' => 'share#add', 'url' => '/share/add', 'verb' => 'POST'], - ['name' => 'share#delete', 'url' => '/share/delete', 'verb' => 'POST'], - ['name' => 'share#createPersonalShare', 'url' => '/share/create/s', 'verb' => 'POST'], + ['name' => 'share#get', 'url' => '/share/{token}', 'verb' => 'GET'], + ['name' => 'share#personal', 'url' => '/share/personal', 'verb' => 'POST'], + ['name' => 'share#delete', 'url' => '/share/delete/{token}', 'verb' => 'DELETE'], ['name' => 'share#sendInvitation', 'url' => '/share/send/{token}', 'verb' => 'POST'], + ['name' => 'share#resolveContactGroup', 'url' => '/share/resolveContactGroup/{token}', 'verb' => 'POST'], - // ['name' => 'share#getShares', 'url' => '/shares/get/{pollId}', 'verb' => 'GET'], - // ['name' => 'share#get', 'url' => '/share/get/{token}', 'verb' => 'GET'], + ['name' => 'subscription#get', 'url' => '/subscription/{pollId}', 'verb' => 'GET', 'postfix' => 'auth'], + ['name' => 'subscription#get', 'url' => '/subscription/s/{token}', 'verb' => 'GET', 'postfix' => 'public'], + ['name' => 'subscription#set', 'url' => '/subscription', 'verb' => 'POST'], - ['name' => 'acl#getByToken', 'url' => '/acl/get/s/{token}', 'verb' => 'GET'], - ['name' => 'acl#get', 'url' => '/acl/get/{id}', 'verb' => 'GET'], + ['name' => 'comment#add', 'url' => '/comment', 'verb' => 'POST'], + ['name' => 'comment#delete', 'url' => '/comment/{commentId}', 'verb' => 'DELETE'], + // ['name' => 'comment#list', 'url' => '/comments/{pollId}', 'verb' => 'GET'], + // ['name' => 'comment#getByToken', 'url' => '/comments/s/{token}', 'verb' => 'GET'], + + // ['name' => 'acl#getByToken', 'url' => '/acl/get/s/{token}', 'verb' => 'GET'], + // ['name' => 'acl#get', 'url' => '/acl/get/{id}', 'verb' => 'GET'], ['name' => 'system#get_site_users_and_groups', 'url' => '/siteusers/get', 'verb' => 'POST'], ['name' => 'system#validate_public_username', 'url' => '/check/username', 'verb' => 'POST'], + ['name' => 'system#validate_email_address', 'url' => '/check/emailaddress/{emailAddress}', 'verb' => 'GET'], // REST-API calls - ['name' => 'poll_api#get', 'url' => '/api/v1.0/poll/{pollId}', 'verb' => 'GET'], ['name' => 'poll_api#list', 'url' => '/api/v1.0/polls', 'verb' => 'GET'], - ['name' => 'poll_api#add', 'url' => '/api/v1.0/poll/add', 'verb' => 'POST'], - ['name' => 'poll_api#clone', 'url' => '/api/v1.0/poll/clone/{pollId}', 'verb' => 'POST'], + ['name' => 'poll_api#get', 'url' => '/api/v1.0/poll/{pollId}', 'verb' => 'GET'], + ['name' => 'poll_api#add', 'url' => '/api/v1.0/poll', 'verb' => 'POST'], ['name' => 'poll_api#update', 'url' => '/api/v1.0/poll/{pollId}', 'verb' => 'PUT'], ['name' => 'poll_api#delete', 'url' => '/api/v1.0/poll/{pollId}', 'verb' => 'DELETE'], - ['name' => 'poll_api#delete', 'url' => '/api/v1.0/poll/permanent/{pollId}', 'verb' => 'DELETE'], - ['name' => 'poll_api#enum', 'url' => '/api/v1.0/poll/enum', 'verb' => 'GET'], + ['name' => 'poll_api#clone', 'url' => '/api/v1.0/poll/{pollId}/clone', 'verb' => 'POST'], + ['name' => 'poll_api#trash', 'url' => '/api/v1.0/poll/{pollId}/trash', 'verb' => 'POST'], + ['name' => 'poll_api#getParticipantsEmailAddresses', 'url' => '/api/v1.0/poll/{pollId}/addresses', 'verb' => 'GET'], + ['name' => 'poll_api#enum', 'url' => '/api/v1.0/enum/poll', 'verb' => 'GET'], ['name' => 'option_api#list', 'url' => '/api/v1.0/poll/{pollId}/options', 'verb' => 'GET'], - ['name' => 'option_api#add', 'url' => '/api/v1.0/option', 'verb' => 'POST'], - ['name' => 'option_api#update', 'url' => '/api/v1.0/option', 'verb' => 'PUT'], + ['name' => 'option_api#add', 'url' => '/api/v1.0/poll/{pollId}/option', 'verb' => 'POST'], + ['name' => 'option_api#update', 'url' => '/api/v1.0/option/{optionId}', 'verb' => 'PUT'], ['name' => 'option_api#delete', 'url' => '/api/v1.0/option/{optionId}', 'verb' => 'DELETE'], + ['name' => 'option_api#setOrder', 'url' => '/api/v1.0/option/{optionId}/setorder/{order}', 'verb' => 'PUT'], + ['name' => 'option_api#confirm', 'url' => '/api/v1.0/option/{optionId}/confirm', 'verb' => 'PUT'], - ['name' => 'comment_api#list', 'url' => '/api/v1.0/poll/{pollId}/comments', 'verb' => 'GET'], - ['name' => 'comment_api#add', 'url' => '/api/v1.0/comment', 'verb' => 'POST'], - ['name' => 'comment_api#delete', 'url' => '/api/v1.0/comment/{commentId}', 'verb' => 'DELETE'], + ['name' => 'vote_api#list', 'url' => '/api/v1.0/poll/{pollId}/votes', 'verb' => 'GET'], + ['name' => 'vote_api#set', 'url' => '/api/v1.0/vote', 'verb' => 'POST'], ['name' => 'share_api#list', 'url' => '/api/v1.0/poll/{pollId}/shares', 'verb' => 'GET'], ['name' => 'share_api#get', 'url' => '/api/v1.0/share/{token}', 'verb' => 'GET'], @@ -105,7 +111,13 @@ ['name' => 'subscription_api#subscribe', 'url' => '/api/v1.0/poll/{pollId}/subscription', 'verb' => 'PUT'], ['name' => 'subscription_api#unsubscribe', 'url' => '/api/v1.0/poll/{pollId}/subscription', 'verb' => 'DELETE'], - ['name' => 'vote_api#list', 'url' => '/api/v1.0/poll/{pollId}/votes', 'verb' => 'GET'], - ['name' => 'vote_api#set', 'url' => '/api/v1.0/vote', 'verb' => 'POST'], + ['name' => 'comment_api#list', 'url' => '/api/v1.0/poll/{pollId}/comments', 'verb' => 'GET'], + ['name' => 'comment_api#add', 'url' => '/api/v1.0/comment', 'verb' => 'POST'], + ['name' => 'comment_api#delete', 'url' => '/api/v1.0/comment/{commentId}', 'verb' => 'DELETE'], + + ['name' => 'preferences#write', 'url' => '/preferences/write/', 'verb' => 'POST'], + ['name' => 'preferences#get', 'url' => '/preferences/get/', 'verb' => 'GET'], + ['name' => 'system#get_site_users_and_groups', 'url' => '/siteusers/get/', 'verb' => 'POST'], + ['name' => 'system#validate_public_username', 'url' => '/check/username', 'verb' => 'POST'] ] ]; diff --git a/composer.json b/composer.json index 28f48e263..d1bf68de7 100644 --- a/composer.json +++ b/composer.json @@ -27,6 +27,11 @@ "christophwurst/nextcloud": "^18.0", "phpunit/phpunit": "^8.2", "league/factory-muffin": "^3.0", - "league/factory-muffin-faker": "^2.0" + "league/factory-muffin-faker": "^2.0", + "nextcloud/coding-standard": "^0.3.0" + }, + "scripts": { + "cs:check": "php-cs-fixer fix --dry-run --diff", + "cs:fix": "php-cs-fixer fix" } } diff --git a/docs/API_v1.0.md b/docs/API_v1.0.md index 2f1c3326f..b26c62f7f 100644 --- a/docs/API_v1.0.md +++ b/docs/API_v1.0.md @@ -16,27 +16,34 @@ Example calls: ``` # Poll -| Method | Endpoint | Description | Return codes | -| --------- | ----------------------------------- | ---------------------------- | ------------------ | -| GET | /api/v1.0/polls | Get polls list as array | 200, 403, 404 | -| GET | /api/v1.0/poll/{pollId} | Get poll with {pollId} | 200, 403, 404 | -| POST | /api/v1.0/poll/add | Add new poll with payload | 201, 403, 404 | -| POST | /api/v1.0/poll/clone/{pollId} | Clone poll {pollId} | 201, 403, 404 | -| PUT | /api/v1.0/poll/{pollId} | Update poll | 200, 403, 404, 409 | -| DELETE | /api/v1.0/poll/{pollId} | Delete poll logical | 200, 403, 404 | -| DELETE | /api/v1.0/poll/permanent/{pollId} | Delete poll permanently | 200, 403, 404 | -| GET | /api/v1.0/poll/enum | Get valid enums | 200, 403, 404 | - -## Add poll - +## Default functions +| Method | Endpoint | Payload | Description | Return codes | Return value | +| --------- | ------------------------ | ------- | ---------------------- | ------------------ | -------------- | +| GET | /api/v1.0/polls | no | Get array of polls | 200, 403, 404 | array | +| GET | /api/v1.0/poll/{pollId} | no | Get poll with {pollId} | 200, 403, 404 | requested poll | +| POST | /api/v1.0/poll | yes | Add new poll | 201, 403, 404 | added poll | +| PUT | /api/v1.0/poll/{pollId} | yes | Update poll | 200, 403, 404, 409 | updated poll | +| DELETE | /api/v1.0/poll/{pollId} | no | Delete poll | 200, 403, 404 | deleted poll | + + +## Special functions +| Method | Endpoint | Payload | Description | Return codes | Return value | +| --------- | ------------------------------| ------- | ---------------------------- | ------------------ | -------------- | +| POST | /api/v1.0/poll/{pollId}/clone | no | Clone poll from {pollId} | 201, 403, 404 | cloned poll | +| POST | /api/v1.0/poll/{pollId}/trash | no | Move to/remome from trash | 200, 403, 404 | updated poll | +| GET | /api/v1.0/enum/poll | no | Get valid enums | 200, 403, 404 | array | + +## Valid payloads +### Add new poll ```json { "type": "datePoll", - "title": "Test" + "title": "Poll Title" } ``` -## Update poll +### Update poll +send the full or a partial structure ```json { "poll": { @@ -52,54 +59,50 @@ Example calls: } } ``` - ### Keys and values | Key | Type | description | | ------- | ------- | -------------------| | expire | integer | unix timestamp | | deleted | integer | unix timestamp | - - # Options -| Method | Endpoint | Description | Return codes | -| --------- | ----------------------------------- | ---------------------------- | ------------------ | -| GET | /api/v1.0/poll/{pollId}/options | Get options as array | 200, 403, 404 | -| POST | /api/v1.0/option | Add new option with Payload | 201, 403, 404, 409 | -| PUT | /api/v1.0/option | Update option with Payload | 200, 403, 404 | -| DELETE | /api/v1.0/option/{optionId} | Delete option | 200, 403, 404 | +## Default functions +| Method | Endpoint | Payload | Description | Return codes | Return value | +| --------- | ------------------------------- | ------- | ----------------- | ------------------ | -------------- | +| GET | /api/v1.0/poll/{pollId}/options | no | Get poll options | 200, 403, 404 | array | +| POST | /api/v1.0/poll/{pollId}/option | yes | Add new option | 201, 403, 404, 409 | added option | +| PUT | /api/v1.0/option/{optionId} | yes | Update option | 200, 403, 404 | updated option | +| DELETE | /api/v1.0/option/{optionId} | no | Delete option | 200, 403, 404 | deleted option | + +## Special functions (no payloads) +| Method | Endpoint | Description | Return codes | Return value | +| --------- | -------------------------------------------- | ----------------------------- | ------------------ | ---------------- | +| PUT | /api/v1.0/option/{optionId}/confirm | Confirm/unconfirm option | 200, 403, 404 | confirmed option | +| PUT | /api/v1.0/option/{optionId}/setorder/{order} | Set order (text poll) | 200, 403, 404 | array | + +## Valid payloads +### Add/update option (text poll) -## add option ```json { - "pollId": 139, - "pollOptionText": "19-06-2020 17:00:00", - "timestamp": 0, + "pollOptionText": "Text of new option" } ``` -## Update option +### Add/update option (date poll) ```json { - "id": 17, - "pollId": 1, - "pollOptionText": "poll option", - "timestamp": 0, - "order": 1, - "confirmed": 1590762104 -}, + "timestamp": 1589195823 +} ``` ### Keys and values -| Key | Type | description | -| -------------- | ------- | ------------------------------------- | -| id | String | id overrides optionID if used | -| pollOptionText | String | poll text or date option in UTC | -| confirmed | Integer | unix timestamp | -| timestamp | Integer | unix timestamp for date option | -| order | Integer | position on option order for textpolls | +| Key | Type | description | +| -------------- | ------- | ----------------------- | +| pollOptionText | String | poll text | +| timestamp | Integer | 10 digit unix timestamp | + -* if timestamp is given in a date poll, the poll option text is ignored # Votes | Method | Endpoint | Description | Return codes | @@ -110,8 +113,7 @@ Example calls: ## set vote ```json { - "pollId": 1, - "pollOptionText": "Saturn", + "optionId": 1, "setTo" :"yes" } ``` diff --git a/img/sidebar-toggle.svg b/img/sidebar-toggle.svg new file mode 100644 index 000000000..f6a635e34 --- /dev/null +++ b/img/sidebar-toggle.svg @@ -0,0 +1,38 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 53922944b..1f27607bb 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -24,7 +24,6 @@ namespace OCA\Polls\AppInfo; use OCP\AppFramework\App; -use OCP\IContainer; class Application extends App { @@ -41,7 +40,7 @@ public function __construct(array $urlParams = []) { */ public function registerNavigationEntry() { $container = $this->getContainer(); - $container->query('OCP\INavigationManager')->add(function() use ($container) { + $container->query('OCP\INavigationManager')->add(function () use ($container) { $urlGenerator = $container->query('OCP\IURLGenerator'); $l10n = $container->query('OCP\IL10N'); return [ diff --git a/lib/Controller/AclController.php b/lib/Controller/AclController.php deleted file mode 100644 index 2bb1561f6..000000000 --- a/lib/Controller/AclController.php +++ /dev/null @@ -1,80 +0,0 @@ - - * - * @author René Gieling - * - * @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 . - * - */ - -namespace OCA\Polls\Controller; - -use OCP\AppFramework\Controller; -use OCP\AppFramework\Http; -use OCP\AppFramework\Http\DataResponse; - -use OCP\IRequest; - -use OCA\Polls\Model\Acl; - - -class AclController extends Controller { - - private $acl; - - /** - * PageController constructor. - * @param string $appName - * @param IRequest $request - * @param Acl $acl - */ - public function __construct( - $appName, - IRequest $request, - Acl $acl - ) { - parent::__construct($appName, $request); - $this->acl = $acl; - } - - /** - * Read acl with poll id for current user - * @NoAdminRequired - * @param integer $pollId - * @return array - */ - public function get($id) { - $acl = $this->acl->setPollId($id); - // $acl = $this->acl->setUserId('dartcafe'); - return new DataResponse($acl, Http::STATUS_OK); - } - - /** - * Read acl with share token - * @NoAdminRequired - * @PublicPage - * @NoCSRFRequired - * @param integer $pollId - * @return array - */ - public function getByToken($token) { - $acl = $this->acl->setToken($token); - return new DataResponse($acl, Http::STATUS_OK); - - } - -} diff --git a/lib/Controller/CommentApiController.php b/lib/Controller/CommentApiController.php index 6cc56bba9..f94c9f3eb 100644 --- a/lib/Controller/CommentApiController.php +++ b/lib/Controller/CommentApiController.php @@ -23,26 +23,23 @@ namespace OCA\Polls\Controller; -use Exception; use OCP\AppFramework\Db\DoesNotExistException; +use OCA\Polls\Exceptions\NotAuthorizedException; use OCP\IRequest; -use \OCP\IURLGenerator; use OCP\AppFramework\ApiController; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; -use OCA\Polls\Exceptions\NotAuthorizedException; - use OCA\Polls\Service\CommentService; - - class CommentApiController extends ApiController { + /** @var CommentService */ private $commentService; + /** - * CommentApiController constructor. + * CommentApiController constructor * @param string $appName * @param IRequest $request * @param CommentService $commentService @@ -56,18 +53,17 @@ public function __construct( parent::__construct($appName, $request, 'POST, GET, DELETE', - 'Authorization, Content-Type, Accept', - 1728000); + 'Authorization, Content-Type, Accept', + 1728000); $this->commentService = $commentService; } /** - * get * Read all comments of a poll based on the poll id and return list as array * @NoAdminRequired * @CORS * @NoCSRFRequired - * @param integer $pollId + * @param int $pollId * @return DataResponse */ public function list($pollId) { @@ -81,7 +77,7 @@ public function list($pollId) { } /** - * Write a new comment to the db and returns the new comment as array + * Add comment * @NoAdminRequired * @CORS * @NoCSRFRequired @@ -100,7 +96,7 @@ public function add($pollId, $message) { } /** - * Delete Comment + * Delete comment * @NoAdminRequired * @CORS * @NoCSRFRequired @@ -116,5 +112,4 @@ public function delete($commentId) { return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); } } - } diff --git a/lib/Controller/CommentController.php b/lib/Controller/CommentController.php index 6f240f053..662b170c9 100644 --- a/lib/Controller/CommentController.php +++ b/lib/Controller/CommentController.php @@ -25,25 +25,22 @@ use Exception; use OCP\AppFramework\Db\DoesNotExistException; +use OCA\Polls\Exceptions\NotAuthorizedException; use OCP\IRequest; -use OCP\ILogger; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; -use OCA\Polls\Exceptions\NotAuthorizedException; - use OCA\Polls\Service\CommentService; - - class CommentController extends Controller { + /** @var CommentService */ private $commentService; /** - * CommentController constructor. + * CommentController constructor * @param string $appName * @param IRequest $request * @param CommentService $commentService @@ -58,28 +55,15 @@ public function __construct( $this->commentService = $commentService; } - /** - * get - * Read all comments of a poll based on the poll id and return list as array - * @NoAdminRequired - * @NoCSRFRequired - * @param integer $pollId - * @return DataResponse - */ - public function list($pollId) { - return new DataResponse($this->commentService->list($pollId), Http::STATUS_OK); - } - // /** - // * Read all comments of a poll based on a share token and return list as array + // * Read all comments of a poll based on the poll id and return list as array // * @NoAdminRequired - // * @NoCSRFRequired - // * @PublicPage + // * @param int $pollId // * @param string $token // * @return DataResponse // */ - // public function getByToken($token) { - // return new DataResponse($this->commentService->get(0, $token), Http::STATUS_OK); + // public function list($pollId) { + // return new DataResponse($this->commentService->list($pollId), Http::STATUS_OK); // } // /** @@ -115,7 +99,5 @@ public function delete($commentId, $token) { } catch (DoesNotExistException $e) { return new DataResponse($e, Http::STATUS_OK); } - } - } diff --git a/lib/Controller/OptionApiController.php b/lib/Controller/OptionApiController.php index e3396a3ed..1e7994c44 100644 --- a/lib/Controller/OptionApiController.php +++ b/lib/Controller/OptionApiController.php @@ -23,21 +23,20 @@ namespace OCA\Polls\Controller; -use Exception; -use Doctrine\DBAL\Exception\UniqueConstraintViolationException; +use \Doctrine\DBAL\Exception\UniqueConstraintViolationException; use OCP\AppFramework\Db\DoesNotExistException; use OCA\Polls\Exceptions\NotAuthorizedException; use OCP\IRequest; + use OCP\AppFramework\ApiController; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; - - use OCA\Polls\Service\OptionService; class OptionApiController extends ApiController { + /** @var OptionService */ private $optionService; /** @@ -55,8 +54,8 @@ public function __construct( parent::__construct($appName, $request, 'POST, PUT, GET, DELETE', - 'Authorization, Content-Type, Accept', - 1728000); + 'Authorization, Content-Type, Accept', + 1728000); $this->optionService = $optionService; } @@ -65,14 +64,14 @@ public function __construct( * @NoAdminRequired * @CORS * @NoCSRFRequired - * @param integer $pollId + * @param int $pollId * @return DataResponse */ public function list($pollId) { try { return new DataResponse(['options' => $this->optionService->list($pollId)], Http::STATUS_OK); } catch (DoesNotExistException $e) { - return new DataResponse(['error' => 'Poll with id ' . $pollId . ' not found'], Http::STATUS_NOT_FOUND); + return new DataResponse([], Http::STATUS_NOT_FOUND); } catch (NotAuthorizedException $e) { return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); } @@ -80,26 +79,18 @@ public function list($pollId) { /** - * Add a new Option to poll + * Add a new option * @NoAdminRequired * @CORS * @NoCSRFRequired - * @param integer $pollId + * @param int $pollId * @param string $pollOptionText - * @param integer $timestamp + * @param int $timestamp * @return DataResponse */ - public function add($pollId, $pollOptionText = '', $timestamp = 0) { - $option = [ - 'pollId' => $pollId, - 'pollOptionText' => $pollOptionText, - 'timestamp' => $timestamp - ]; - + public function add($pollId, $timestamp = 0, $pollOptionText = '') { try { - return new DataResponse(['option' => $this->optionService->add($option)], Http::STATUS_CREATED); - } catch (DoesNotExistException $e) { - return new DataResponse(['error' => 'Poll with id ' . $pollId . ' not found'], Http::STATUS_NOT_FOUND); + return new DataResponse(['option' => $this->optionService->add($pollId, $timestamp, $pollOptionText)], Http::STATUS_CREATED); } catch (UniqueConstraintViolationException $e) { return new DataResponse(['error' => 'Option exists'], Http::STATUS_CONFLICT); } catch (NotAuthorizedException $e) { @@ -109,27 +100,27 @@ public function add($pollId, $pollOptionText = '', $timestamp = 0) { /** - * Update poll option + * Update option * @NoAdminRequired * @CORS * @NoCSRFRequired * @param array $option * @return DataResponse */ - public function update($option) { + public function update($optionId, $timestamp = 0, $pollOptionText = '') { try { - return new DataResponse(['option' => $this->optionService->update($option)], Http::STATUS_OK); + return new DataResponse(['option' => $this->optionService->update($optionId, $timestamp, $pollOptionText)], Http::STATUS_OK); } catch (NotAuthorizedException $e) { return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); } } /** - * Remove a single option + * Delete option * @NoAdminRequired * @CORS * @NoCSRFRequired - * @param integer $optionId + * @param int $optionId * @return DataResponse */ public function delete($optionId) { @@ -141,4 +132,38 @@ public function delete($optionId) { return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); } } + + /** + * Switch option confirmation + * @NoAdminRequired + * @CORS + * @NoCSRFRequired + * @param int $optionId + * @return DataResponse + */ + public function confirm($optionId) { + try { + return new DataResponse(['option' => $this->optionService->confirm($optionId)], Http::STATUS_OK); + } catch (DoesNotExistException $e) { + return new DataResponse(['error' => 'Option does not exist'], Http::STATUS_NOT_FOUND); + } catch (NotAuthorizedException $e) { + return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); + } + } + + /** + * Set order position for option + * @NoAdminRequired + * @CORS + * @NoCSRFRequired + * @param array $option + * @return DataResponse + */ + public function setOrder($optionId, $order) { + try { + return new DataResponse(['option' => $this->optionService->setOrder($optionId, $order)], Http::STATUS_OK); + } catch (NotAuthorizedException $e) { + return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); + } + } } diff --git a/lib/Controller/OptionController.php b/lib/Controller/OptionController.php index 0ec240922..44ddd1faa 100644 --- a/lib/Controller/OptionController.php +++ b/lib/Controller/OptionController.php @@ -23,21 +23,26 @@ namespace OCA\Polls\Controller; -use Exception; +use DateTime; +use DateInterval; +use OCA\Polls\Exceptions\DuplicateEntryException; use OCP\IRequest; + use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; - -use OCA\Polls\Exceptions\NotAuthorizedException; - use OCA\Polls\Service\OptionService; +use OCA\Polls\Service\CalendarService; class OptionController extends Controller { + /** @var OptionService */ private $optionService; + /** @var CalendarService */ + private $calendarService; + /** * OptionController constructor. * @param string $appName @@ -48,79 +53,105 @@ class OptionController extends Controller { public function __construct( string $appName, IRequest $request, - OptionService $optionService + OptionService $optionService, + CalendarService $calendarService ) { parent::__construct($appName, $request); $this->optionService = $optionService; + $this->calendarService = $calendarService; } /** * Get all options of given poll * @NoAdminRequired - * @NoCSRFRequired - * @param integer $pollId + * @param int $pollId * @return DataResponse */ public function list($pollId) { - return new DataResponse($this->optionService->list($pollId), Http::STATUS_OK); + return new DataResponse(['options' => $this->optionService->list($pollId)], Http::STATUS_OK); } - /** - * getByToken - * Read all options of a poll based on a share token and return list as array + * Add a new option * @NoAdminRequired - * @PublicPage - * @NoCSRFRequired - * @param string $token + * @param array $option * @return DataResponse */ - public function getByToken($token) { - return new DataResponse($this->optionService->list(0, $token), Http::STATUS_OK); + public function add($pollId, $timestamp = 0, $pollOptionText = '') { + try { + return new DataResponse(['option' => $this->optionService->add($pollId, $timestamp, $pollOptionText)], Http::STATUS_OK); + } catch (DuplicateEntryException $e) { + return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); + } } /** - * Add a new Option to poll + * Update option * @NoAdminRequired - * @NoCSRFRequired * @param array $option * @return DataResponse */ - public function add($option) { - return new DataResponse($this->optionService->add($option), Http::STATUS_OK); + public function update($optionId, $timestamp, $pollOptionText) { + return new DataResponse(['option' => $this->optionService->update($optionId, $timestamp, $pollOptionText)], Http::STATUS_OK); } /** - * Update poll option + * Delete option * @NoAdminRequired - * @NoCSRFRequired - * @param array $option + * @param Option $option * @return DataResponse */ - public function update($option) { - return new DataResponse($this->optionService->update($option), Http::STATUS_OK); + public function delete($optionId) { + return new DataResponse(['option' => $this->optionService->delete($optionId)], Http::STATUS_OK); } /** - * Remove a single option + * Switch option confirmation * @NoAdminRequired - * @NoCSRFRequired - * @param Option $option + * @param int $optionId * @return DataResponse */ - public function remove($option) { - return new DataResponse($this->optionService->delete($option['id']), Http::STATUS_OK); + public function confirm($optionId) { + return new DataResponse(['option' => $this->optionService->confirm($optionId)], Http::STATUS_OK); } /** - * Set order by order of the given array + * Reorder options * @NoAdminRequired - * @NoCSRFRequired - * @param integer $pollId + * @param int $pollId * @param Array $options * @return DataResponse */ public function reorder($pollId, $options) { - return new DataResponse($this->optionService->reorder($pollId, $options), Http::STATUS_OK); + return new DataResponse(['options' => $this->optionService->reorder($pollId, $options)], Http::STATUS_OK); + } + + /** + * Reorder options + * @NoAdminRequired + * @param int $optionId + * @param int $step + * @param string $unit + * @param int $amount + * @return DataResponse + */ + public function sequence($optionId, $step, $unit, $amount) { + return new DataResponse(['options' => $this->optionService->sequence($optionId, $step, $unit, $amount)], Http::STATUS_OK); + } + + /** + * findCalendarEvents + * @NoAdminRequired + * @param integer $from + * @param integer $to + * @return DataResponse + */ + public function findCalendarEvents($optionId) { + $searchFrom = new DateTime(); + $searchFrom = $searchFrom->setTimestamp($this->optionService->get($optionId)->getTimestamp())->sub(new DateInterval('PT1H')); + $searchTo = clone $searchFrom; + $searchTo = $searchTo->add(new DateInterval('PT3H')); + + return new DataResponse(['events' => array_values($this->calendarService->getEvents($searchFrom, $searchTo))], Http::STATUS_OK); } } diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index 71e0a7cde..63321187f 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -27,16 +27,13 @@ use OCP\IRequest; use OCP\AppFramework\Controller; -use OCP\AppFramework\Db\DoesNotExistException; -use OCP\AppFramework\Http\ContentSecurityPolicy; -use OCP\AppFramework\Http\JSONResponse; -use OCP\AppFramework\Http\RedirectResponse; use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Http\Template\PublicTemplateResponse; use OCP\IURLGenerator; class PageController extends Controller { + /** @var IURLGenerator */ private $urlGenerator; /** @@ -78,7 +75,5 @@ public function votePublic() { return new PublicTemplateResponse('polls', 'polls.tmpl', [ 'urlGenerator' => $this->urlGenerator]); } - } - } diff --git a/lib/Controller/PollApiController.php b/lib/Controller/PollApiController.php index 3c8acc9dc..e0a92ed6a 100644 --- a/lib/Controller/PollApiController.php +++ b/lib/Controller/PollApiController.php @@ -23,7 +23,6 @@ namespace OCA\Polls\Controller; - use Exception; use OCP\AppFramework\Db\DoesNotExistException; use OCA\Polls\Exceptions\EmptyTitleException; use OCA\Polls\Exceptions\InvalidAccessException; @@ -32,7 +31,6 @@ use OCA\Polls\Exceptions\NotAuthorizedException; use OCP\IRequest; - use OCP\ILogger; use OCP\AppFramework\ApiController; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; @@ -41,175 +39,196 @@ class PollApiController extends ApiController { - private $logger; + + /** @var PollService */ private $pollService; /** - * PollController constructor. + * PollApiController constructor * @param string $appName - * @param $userId * @param IRequest $request - * @param ILogger $logger * @param PollService $pollService */ public function __construct( - string $appName, - IRequest $request, - ILogger $logger, + string $appName, + IRequest $request, PollService $pollService - ) { + ) { parent::__construct($appName, $request); - $this->logger = $logger; $this->pollService = $pollService; } - /** - * list - * @NoAdminRequired - * @NoCSRFRequired - * @CORS - * @return DataResponse - */ - - public function list() { - try { - return new DataResponse(['polls' => $this->pollService->list()], Http::STATUS_OK); - } catch (DoesNotExistException $e) { - return new DataResponse([], Http::STATUS_NOT_FOUND); - } catch (NotAuthorizedException $e) { - return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); - } - } - - - /** - * get - * @NoAdminRequired - * @NoCSRFRequired - * @param integer $pollId - * @return array - */ + /** + * Get list of polls + * @NoAdminRequired + * @CORS + * @NoCSRFRequired + * @return DataResponse + */ + + public function list() { + try { + return new DataResponse(['polls' => $this->pollService->list()], Http::STATUS_OK); + } catch (DoesNotExistException $e) { + return new DataResponse([], Http::STATUS_NOT_FOUND); + } catch (NotAuthorizedException $e) { + return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); + } + } + + + /** + * get poll configuration + * @NoAdminRequired + * @CORS + * @NoCSRFRequired + * @param int $pollId + * @return DataResponse + */ public function get($pollId) { - try { - return new DataResponse(['poll' => $this->pollService->get($pollId)], Http::STATUS_OK); - } catch (DoesNotExistException $e) { - return new DataResponse(['error' => 'Not found'], Http::STATUS_NOT_FOUND); - } catch (NotAuthorizedException $e) { - return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); - } + try { + return new DataResponse(['poll' => $this->pollService->get($pollId, '')], Http::STATUS_OK); + } catch (DoesNotExistException $e) { + return new DataResponse(['error' => 'Not found'], Http::STATUS_NOT_FOUND); + } catch (NotAuthorizedException $e) { + return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); + } + } + + /** + * Add poll + * @NoAdminRequired + * @NoCSRFRequired + * @CORS + * @param Array $poll + * @return DataResponse + */ + + public function add($type, $title) { + try { + return new DataResponse(['poll' => $this->pollService->add($type, $title)], Http::STATUS_CREATED); + } catch (NotAuthorizedException $e) { + return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); + } catch (InvalidPollTypeException $e) { + return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); + } catch (EmptyTitleException $e) { + return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); + } + } + + /** + * Update poll configuration + * @NoAdminRequired + * @CORS + * @NoCSRFRequired + * @param int $pollId + * @param array $poll + * @return DataResponse + */ + + public function update($pollId, $poll) { + try { + return new DataResponse(['poll' => $this->pollService->update($pollId, $poll)], Http::STATUS_OK); + } catch (DoesNotExistException $e) { + return new DataResponse(['error' => 'Poll not found'], Http::STATUS_NOT_FOUND); + } catch (NotAuthorizedException $e) { + return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); + } catch (InvalidAccessException $e) { + return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); + } catch (InvalidShowResultsException $e) { + return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); + } catch (EmptyTitleException $e) { + return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); + } + } + + /** + * Switch deleted status (move to deleted polls) + * @NoAdminRequired + * @CORS + * @NoCSRFRequired + * @param int $pollId + * @return DataResponse + */ + + public function trash($pollId) { + try { + return new DataResponse(['poll' => $this->pollService->delete($pollId)], Http::STATUS_OK); + } catch (DoesNotExistException $e) { + return new DataResponse(['error' => 'Poll not found'], Http::STATUS_NOT_FOUND); + } catch (NotAuthorizedException $e) { + return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); + } } - /** - * write - * @NoAdminRequired - * @NoCSRFRequired - * @param Array $poll - * @return DataResponse - */ - - public function add($type, $title) { - try { - return new DataResponse(['poll' => $this->pollService->add($type, $title)], Http::STATUS_CREATED); - } catch (NotAuthorizedException $e) { - return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); - } catch (InvalidPollTypeException $e) { - return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); - } catch (EmptyTitleException $e) { - return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); - } - } - - /** - * write - * @NoAdminRequired - * @NoCSRFRequired - * @param Array $poll - * @return DataResponse - */ - - public function update($pollId, $poll) { - try { - return new DataResponse(['poll' => $this->pollService->update($pollId, $poll)], Http::STATUS_OK); - } catch (DoesNotExistException $e) { - return new DataResponse(['error' => 'Poll not found'], Http::STATUS_NOT_FOUND); - } catch (NotAuthorizedException $e) { - return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); - } catch (InvalidAccessException $e) { - return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); - } catch (InvalidShowResultsException $e) { - return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); - } catch (EmptyTitleException $e) { - return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); - } - } - - /** - * delete - * @NoAdminRequired - * @NoCSRFRequired - * @param Array $poll - * @return DataResponse - */ - - public function delete($pollId) { - try { - return new DataResponse(['poll' => $this->pollService->delete($pollId)], Http::STATUS_OK); - } catch (DoesNotExistException $e) { - return new DataResponse(['error' => 'Poll not found'], Http::STATUS_NOT_FOUND); - } catch (NotAuthorizedException $e) { - return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); - } - } - - /** - * deletePermanently - * @NoAdminRequired - * @NoCSRFRequired - * @param Array $poll - * @return DataResponse - */ - - public function deletePermanently($pollId) { - try { - return new DataResponse(['poll' => $this->pollService->deletePermanently($pollId)], Http::STATUS_OK); - } catch (DoesNotExistException $e) { - return new DataResponse(['error' => 'Poll not found'], Http::STATUS_NOT_FOUND); - } catch (NotAuthorizedException $e) { - return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); - } - - } - - /** - * clone - * @NoAdminRequired - * @NoCSRFRequired - * @param integer $pollId - * @return DataResponse - */ - public function clone($pollId) { - try { - return new DataResponse(['poll' => $this->pollService->clone($pollId)], Http::STATUS_CREATED); - } catch (DoesNotExistException $e) { - return new DataResponse(['error' => 'Poll not found'], Http::STATUS_NOT_FOUND); - } catch (NotAuthorizedException $e) { - return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); - } - } - - /** - * enum - * @NoAdminRequired - * @NoCSRFRequired - * @param Array $poll - * @return DataResponse - */ - - public function enum() { - return new DataResponse($this->pollService->getValidEnum(), Http::STATUS_OK); - } - - -} + /** + * Delete poll + * @NoAdminRequired + * @CORS + * @NoCSRFRequired + * @param int $pollId + * @return DataResponse + */ + + public function delete($pollId) { + try { + return new DataResponse(['poll' => $this->pollService->deletePermanently($pollId)], Http::STATUS_OK); + } catch (DoesNotExistException $e) { + return new DataResponse(['error' => 'Poll not found'], Http::STATUS_NOT_FOUND); + } catch (NotAuthorizedException $e) { + return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); + } + } + + /** + * Clone poll + * @NoAdminRequired + * @CORS + * @NoCSRFRequired + * @param int $pollId + * @return DataResponse + */ + public function clone($pollId) { + try { + return new DataResponse(['poll' => $this->pollService->clone($pollId)], Http::STATUS_CREATED); + } catch (DoesNotExistException $e) { + return new DataResponse(['error' => 'Poll not found'], Http::STATUS_NOT_FOUND); + } catch (NotAuthorizedException $e) { + return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); + } + } + + /** + * Collect email addresses from particitipants + * @NoAdminRequired + * @CORS + * @NoCSRFRequired + * @param Array $poll + * @return DataResponse + */ + + public function getParticipantsEmailAddresses($pollId) { + try { + return new DataResponse($this->pollService->getParticipantsEmailAddresses($pollId), Http::STATUS_OK); + } catch (DoesNotExistException $e) { + return new DataResponse(['error' => 'Poll not found'], Http::STATUS_NOT_FOUND); + } catch (NotAuthorizedException $e) { + return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); + } + } + + /** + * Get valid values for configuration options + * @NoAdminRequired + * @CORS + * @NoCSRFRequired + * @param Array $poll + * @return DataResponse + */ + + public function enum() { + return new DataResponse($this->pollService->getValidEnum(), Http::STATUS_OK); + } + } diff --git a/lib/Controller/PollController.php b/lib/Controller/PollController.php index 27f9c4be1..0357852ec 100644 --- a/lib/Controller/PollController.php +++ b/lib/Controller/PollController.php @@ -21,7 +21,7 @@ * */ - namespace OCA\Polls\Controller; +namespace OCA\Polls\Controller; use Exception; use OCP\AppFramework\Db\DoesNotExistException; @@ -32,7 +32,6 @@ use OCA\Polls\Exceptions\NotAuthorizedException; use OCP\IRequest; -use OCP\ILogger; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; @@ -46,53 +45,59 @@ class PollController extends Controller { - private $logger; + /** @var PollService */ private $pollService; + + /** @var CommentService */ private $commentService; + + /** @var OptionService */ private $optionService; + + /** @var ShareService */ private $shareService; + + /** @var VoteService */ private $voteService; + + /** @var Acl */ private $acl; - /** - * PollController constructor. - * @param string $appName - * @param IRequest $request - * @param ILogger $logger - * @param PollService $pollService + /** + * PollController constructor. + * @param string $appName + * @param IRequest $request + * @param PollService $pollService * @param CommentService $commentService - * @param OptionService $optionService - * @param ShareService $shareService - * @param VoteService $voteService - * @param Acl $acl + * @param OptionService $optionService + * @param ShareService $shareService + * @param VoteService $voteService + * @param Acl $acl */ - public function __construct( + public function __construct( string $appName, - IRequest $request, - ILogger $logger, - PollService $pollService, + IRequest $request, + PollService $pollService, CommentService $commentService, - OptionService $optionService, - ShareService $shareService, - VoteService $voteService, - Acl $acl + OptionService $optionService, + ShareService $shareService, + VoteService $voteService, + Acl $acl ) { - parent::__construct($appName, $request); - $this->logger = $logger; + parent::__construct($appName, $request); $this->pollService = $pollService; $this->commentService = $commentService; - $this->optionService = $optionService; - $this->shareService = $shareService; - $this->voteService = $voteService; - $this->acl = $acl; + $this->optionService = $optionService; + $this->shareService = $shareService; + $this->voteService = $voteService; + $this->acl = $acl; } /** - * list + * Get list of polls * @NoAdminRequired - * @NoCSRFRequired * @return DataResponse */ @@ -108,25 +113,17 @@ public function list() { /** - * get + * get complete poll * @NoAdminRequired - * @NoCSRFRequired * @PublicPage - * @param integer $pollId - * @return array + * @param int $pollId + * @param string $token + * @return DataResponse */ - public function get($pollId, $token) { + public function get($pollId, $token) { try { - if ($token) { - $poll = $this->pollService->getByToken($token); - $acl = $this->acl->setToken($token); - } else { - $poll = $this->pollService->get($pollId); - $acl = $this->acl->setPollId($pollId); - } - - // $this->poll = $this->pollService->get($pollId, $token); - // return new DataResponse($this->pollService->get($pollId, $token), Http::STATUS_OK); + $acl = $this->acl->set($pollId, $token); + $poll = $this->pollService->get($pollId, $token); } catch (DoesNotExistException $e) { return new DataResponse(['error' => 'Not found'], Http::STATUS_NOT_FOUND); } catch (NotAuthorizedException $e) { @@ -134,26 +131,33 @@ public function get($pollId, $token) { } try { - $comments = $this->commentService->list($this->acl->getPollId(), $token); + $comments = $this->commentService->list($pollId, $token); } catch (Exception $e) { $comments = []; } try { - $options = $this->optionService->list($this->acl->getPollId(), $token); + $options = $this->optionService->list($pollId, $token); } catch (Exception $e) { $options = []; } try { - $votes = $this->voteService->list($this->acl->getPollId(), $token); + $votes = $this->voteService->list($pollId, $token); } catch (Exception $e) { $votes = []; } try { - $shares = $this->shareService->list($this->acl->getPollId()); + if ($token) { + $share = $this->shareService->get($token); + $shares = []; + } else { + $share = null; + $shares = $this->shareService->list($pollId, $token); + } } catch (Exception $e) { + $share = null; $shares = []; } @@ -162,104 +166,102 @@ public function get($pollId, $token) { 'poll' => $poll, 'comments' => $comments, 'options' => $options, + 'share' => $share, 'shares' => $shares, - 'votes' => $votes + 'votes' => $votes, ], Http::STATUS_OK); - } + } /** - * delete + * Add poll * @NoAdminRequired - * @NoCSRFRequired - * @param Array $poll + * @param string $type + * @param string $title * @return DataResponse */ - public function delete($pollId) { + public function add($type, $title) { try { - return new DataResponse($this->pollService->delete($pollId), Http::STATUS_OK); - } catch (DoesNotExistException $e) { - return new DataResponse(['error' => 'Poll not found'], Http::STATUS_NOT_FOUND); + return new DataResponse($this->pollService->add($type, $title), Http::STATUS_OK); } catch (NotAuthorizedException $e) { return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); + } catch (InvalidPollTypeException $e) { + return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); + } catch (EmptyTitleException $e) { + return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); } } /** - * deletePermanently + * Update poll configuration * @NoAdminRequired - * @NoCSRFRequired - * @param Array $poll + * @param int $pollId + * @param array $poll * @return DataResponse */ - public function deletePermanently($pollId) { + public function update($pollId, $poll) { try { - return new DataResponse($this->pollService->deletePermanently($pollId), Http::STATUS_OK); + return new DataResponse($this->pollService->update($pollId, $poll), Http::STATUS_OK); } catch (DoesNotExistException $e) { return new DataResponse(['error' => 'Poll not found'], Http::STATUS_NOT_FOUND); } catch (NotAuthorizedException $e) { return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); + } catch (InvalidAccessException $e) { + return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); + } catch (InvalidShowResultsException $e) { + return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); + } catch (EmptyTitleException $e) { + return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); } } - /** - * add + * Switch deleted status (move to deleted polls) * @NoAdminRequired - * @NoCSRFRequired - * @param string $type - * @param string $title + * @param int $pollId * @return DataResponse */ - public function add($type, $title) { + public function delete($pollId) { try { - return new DataResponse($this->pollService->add($type, $title), Http::STATUS_OK); + return new DataResponse($this->pollService->delete($pollId), Http::STATUS_OK); + } catch (DoesNotExistException $e) { + return new DataResponse(['error' => 'Poll not found'], Http::STATUS_NOT_FOUND); } catch (NotAuthorizedException $e) { return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); - } catch (InvalidPollTypeException $e) { - return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); - } catch (EmptyTitleException $e) { - return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); } } /** - * write + * Delete poll * @NoAdminRequired - * @NoCSRFRequired - * @param integer $pollId - * @param array $poll + * @param Array $poll * @return DataResponse */ - public function update($pollId, $poll) { + public function deletePermanently($pollId) { try { - return new DataResponse($this->pollService->update($pollId, $poll), Http::STATUS_OK); + return new DataResponse($this->pollService->deletePermanently($pollId), Http::STATUS_OK); } catch (DoesNotExistException $e) { return new DataResponse(['error' => 'Poll not found'], Http::STATUS_NOT_FOUND); } catch (NotAuthorizedException $e) { return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); - } catch (InvalidAccessException $e) { - return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); - } catch (InvalidShowResultsException $e) { - return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); - } catch (EmptyTitleException $e) { - return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); } } /** - * clone + * Clone poll * @NoAdminRequired - * @NoCSRFRequired - * @param integer $pollId + * @param int $pollId * @return DataResponse */ public function clone($pollId) { try { - return new DataResponse($this->pollService->clone($pollId), Http::STATUS_OK); + $poll = $this->pollService->clone($pollId); + $this->optionService->clone($pollId, $poll->getId()); + + return new DataResponse($poll, Http::STATUS_OK); } catch (DoesNotExistException $e) { return new DataResponse(['error' => 'Poll not found'], Http::STATUS_NOT_FOUND); } catch (NotAuthorizedException $e) { @@ -267,4 +269,20 @@ public function clone($pollId) { } } + /** + * Collect email addresses from particitipants + * @NoAdminRequired + * @param Array $poll + * @return DataResponse + */ + + public function getParticipantsEmailAddresses($pollId) { + try { + return new DataResponse($this->pollService->getParticipantsEmailAddresses($pollId), Http::STATUS_OK); + } catch (DoesNotExistException $e) { + return new DataResponse(['error' => 'Poll not found'], Http::STATUS_NOT_FOUND); + } catch (NotAuthorizedException $e) { + return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); + } + } } diff --git a/lib/Controller/PreferencesController.php b/lib/Controller/PreferencesController.php new file mode 100644 index 000000000..6474b0e02 --- /dev/null +++ b/lib/Controller/PreferencesController.php @@ -0,0 +1,99 @@ + + * + * @author René Gieling + * + * @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 . + * + */ + +namespace OCA\Polls\Controller; + +use OCP\AppFramework\Db\DoesNotExistException; + +use OCP\IRequest; +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\DataResponse; +use OCA\Polls\Db\Preferences; +use OCA\Polls\Db\PreferencesMapper; + +class PreferencesController extends Controller { + private $userId; + private $preferencesMapper; + + /** + * PreferencesController constructor. + * @param string $appName + * @param $UserId + * @param PreferencesMapper $preferencesMapper + */ + + public function __construct( + string $appName, + $userId, + IRequest $request, + PreferencesMapper $preferencesMapper + ) { + parent::__construct($appName, $request); + $this->userId = $userId; + $this->preferencesMapper = $preferencesMapper; + } + + /** + * get + * Read all preferences + * @NoAdminRequired + * @NoCSRFRequired + * @return DataResponse + */ + public function get() { + try { + return new DataResponse($this->preferencesMapper->find($this->userId), Http::STATUS_OK); + } catch (DoesNotExistException $e) { + return new DataResponse($e, Http::STATUS_NOT_FOUND); + } + } + + /** + * write + * Write wreferences + * @NoAdminRequired + * @param int $settings + * @return DataResponse + */ + public function write($settings) { + if (!\OC::$server->getUserSession()->isLoggedIn()) { + return new DataResponse(null, Http::STATUS_UNAUTHORIZED); + } + + try { + $preferences = $this->preferencesMapper->find($this->userId); + $preferences->setPreferences(json_encode($settings)); + $preferences->setTimestamp(time()); + $preferences = $this->preferencesMapper->update($preferences); + } catch (\Exception $e) { + $preferences = new Preferences(); + $preferences->setUserId($this->userId); + $preferences->setPreferences(json_encode($settings)); + $preferences->setTimestamp(time()); + $preferences = $this->preferencesMapper->insert($preferences); + } + + return new DataResponse($preferences, Http::STATUS_OK); + } +} diff --git a/lib/Controller/ShareApiController.php b/lib/Controller/ShareApiController.php index 3d7912da1..de04a5d2d 100644 --- a/lib/Controller/ShareApiController.php +++ b/lib/Controller/ShareApiController.php @@ -26,8 +26,6 @@ use Exception; use OCP\AppFramework\Db\DoesNotExistException; use OCA\Polls\Exceptions\NotAuthorizedException; -use OCA\Polls\Exceptions\InvalidUsername; - use OCP\IRequest; use OCP\AppFramework\ApiController; @@ -39,13 +37,15 @@ class ShareApiController extends ApiController { + /** @var ShareService */ private $shareService; + + /** @var MailService */ private $mailService; /** - * ShareController constructor. + * ShareApiController constructor * @param string $appName - * @param string $userId * @param IRequest $request * @param MailService $mailService * @param ShareService $shareService @@ -59,24 +59,23 @@ public function __construct( parent::__construct($appName, $request, 'POST, PUT, GET, DELETE', - 'Authorization, Content-Type, Accept', - 1728000); + 'Authorization, Content-Type, Accept', + 1728000); $this->shareService = $shareService; $this->mailService = $mailService; } /** - * list * Read all shares of a poll based on the poll id and return list as array * @NoAdminRequired * @CORS * @NoCSRFRequired - * @param integer $pollId + * @param int $pollId * @return DataResponse */ public function list($pollId) { try { - return new DataResponse(['shares' => $this->shareService->list($pollId)], Http::STATUS_OK); + return new DataResponse(['shares' => $this->shareService->list($pollId, '')], Http::STATUS_OK); } catch (DoesNotExistException $e) { return new DataResponse(['error' => 'No shares for poll with id ' . $pollId . ' not found'], Http::STATUS_NOT_FOUND); } catch (NotAuthorizedException $e) { @@ -85,14 +84,13 @@ public function list($pollId) { } /** - * get share by token - * Get pollId by token - * @NoAdminRequired - * @NoCSRFRequired - * @CORS - * @param string $token - * @return DataResponse - */ + * Get share by token + * @NoAdminRequired + * @CORS + * @NoCSRFRequired + * @param string $token + * @return DataResponse + */ public function get($token) { try { return new DataResponse(['share' => $this->shareService->get($token)], Http::STATUS_OK); @@ -104,7 +102,7 @@ public function get($token) { } /** - * Write a new share to the db and returns the new share as array + * Add share * @NoAdminRequired * @CORS * @NoCSRFRequired @@ -117,47 +115,45 @@ public function get($token) { public function add($pollId, $type, $userId = '', $userEmail = '') { try { return new DataResponse(['share' => $this->shareService->add($pollId, $type, $userId, $userEmail)], Http::STATUS_CREATED); - } catch (\Exception $e) { - return new DataResponse(['error' => $e], Http::STATUS_CONFLICT); } catch (NotAuthorizedException $e) { return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); + } catch (Exception $e) { + return new DataResponse(['error' => $e], Http::STATUS_CONFLICT); } - } /** - * SendInvitation - * Sent invitation mails for a share + * Delete share * @NoAdminRequired * @CORS * @NoCSRFRequired * @param string $token * @return DataResponse */ - public function sendInvitation($token) { + + public function delete($token) { try { - return new DataResponse($this->mailService->sendInvitationMail($token), Http::STATUS_OK); - } catch (Exception $e) { + return new DataResponse(['share' => $this->shareService->delete($token)], Http::STATUS_OK); + } catch (NotAuthorizedException $e) { return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); + } catch (DoesNotExistException $e) { + return new DataResponse($e, Http::STATUS_NOT_FOUND); } } /** - * delete share + * Sent invitation mails for a share * @NoAdminRequired * @CORS * @NoCSRFRequired * @param string $token * @return DataResponse */ - - public function delete($token) { + public function sendInvitation($token) { try { - return new DataResponse(['share' => $this->shareService->remove($token)], Http::STATUS_OK); - } catch (NotAuthorizedException $e) { - return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); + return new DataResponse($this->mailService->sendInvitationMail($token), Http::STATUS_OK); } catch (Exception $e) { - return new DataResponse($e, Http::STATUS_NOT_FOUND); + return new DataResponse(['error' => $e], Http::STATUS_CONFLICT); } } } diff --git a/lib/Controller/ShareController.php b/lib/Controller/ShareController.php index bd3dd187e..a64f9f58b 100644 --- a/lib/Controller/ShareController.php +++ b/lib/Controller/ShareController.php @@ -26,63 +26,84 @@ use Exception; use OCP\AppFramework\Db\DoesNotExistException; use OCA\Polls\Exceptions\NotAuthorizedException; -use OCA\Polls\Exceptions\InvalidUsername; +use OCA\Polls\Exceptions\InvalidUsernameException; +use OCA\Polls\Exceptions\InvalidShareType; use OCP\IRequest; -use OCP\ILogger; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; - - -use OCA\Polls\Model\Acl; use OCA\Polls\Service\ShareService; use OCA\Polls\Service\MailService; +use OCA\Polls\Service\SystemService; class ShareController extends Controller { - private $logger; + /** @var ShareService */ private $shareService; + + /** @var MailService */ private $mailService; - private $userId; + + /** @var SystemService */ + private $systemService; /** * ShareController constructor. * @param string $appName - * @param string $userId * @param IRequest $request - * @param ILogger $logger * @param MailService $mailService * @param ShareService $shareService + * @param SystemService $systemService */ public function __construct( string $appName, - $userId, IRequest $request, - ILogger $logger, MailService $mailService, - ShareService $shareService + ShareService $shareService, + SystemService $systemService ) { parent::__construct($appName, $request); - $this->logger = $logger; - $this->userId = $userId; $this->shareService = $shareService; $this->mailService = $mailService; + $this->systemService = $systemService; + } + + /** + * Add share + * @NoAdminRequired + * @param int $pollId + * @param int $pollId + * @param string $type + * @param string $userId + * @param string $userEmail + * @return DataResponse + */ + public function add($pollId, $type, $userId = '', $userEmail = '') { + try { + return new DataResponse(['share' => $this->shareService->add($pollId, $type, $userId, $userEmail)], Http::STATUS_CREATED); + } catch (NotAuthorizedException $e) { + return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); + } catch (\Exception $e) { + return new DataResponse($e, Http::STATUS_CONFLICT); + } } /** - * Write a new share to the db and returns the new share as array + * Get share * @NoAdminRequired - * @NoCSRFRequired * @param int $pollId - * @param Array $share + * @param int $pollId + * @param string $type + * @param string $userId + * @param string $userEmail * @return DataResponse */ - public function add($pollId, $type, $userId = '', $userEmail = '') { - try { - return new DataResponse(['share' => $this->shareService->add($pollId, $type, $userId, $userEmail)], Http::STATUS_CREATED); + public function get($token) { + try { + return new DataResponse(['share' => $this->shareService->get($token)], Http::STATUS_CREATED); } catch (NotAuthorizedException $e) { return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); } catch (\Exception $e) { @@ -91,22 +112,43 @@ public function add($pollId, $type, $userId = '', $userEmail = '') { } /** - * createPersonalShare - * Write a new share to the db and returns the new share as array + * Set email address + * @NoAdminRequired + * @PublicPage + * @param int $pollId + * @param int $pollId + * @param string $type + * @param string $userId + * @param string $userEmail + * @return DataResponse + */ + public function setEmailAddress($token, $userEmail) { + try { + return new DataResponse(['share' => $this->shareService->setEmailAddress($token, $userEmail)], Http::STATUS_OK); + } catch (NotAuthorizedException $e) { + return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); + } catch (InvalidShareType $e) { + return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); + } catch (\Exception $e) { + return new DataResponse($e, Http::STATUS_CONFLICT); + } + } + + /** + * Create a personal share from a public share + * or update an email share with the username * @NoAdminRequired * @PublicPage - * @NoCSRFRequired * @param string $token * @param string $userName * @return DataResponse */ - public function createPersonalShare($token, $userName) { - + public function personal($token, $userName, $emailAddress = '') { try { - return new DataResponse($this->shareService->createPersonalShare($token, $userName), Http::STATUS_CREATED); + return new DataResponse($this->shareService->personal($token, $userName, $emailAddress), Http::STATUS_CREATED); } catch (NotAuthorizedException $e) { return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); - } catch (InvalidUsername $e) { + } catch (InvalidUsernameException $e) { return new DataResponse(['error' => $userName . ' is not valid'], Http::STATUS_CONFLICT); } catch (DoesNotExistException $e) { // return forbidden in all not catched error cases @@ -115,11 +157,26 @@ public function createPersonalShare($token, $userName) { } /** - * SendInvitation + * Delete share + * @NoAdminRequired + * @param string $token + * @return DataResponse + */ + + public function delete($token) { + try { + return new DataResponse($this->shareService->delete($token), Http::STATUS_OK); + } catch (NotAuthorizedException $e) { + return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); + } catch (Exception $e) { + return new DataResponse($e, Http::STATUS_NOT_FOUND); + } + } + + /** * Sent invitation mails for a share * @NoAdminRequired * @PublicPage - * @NoCSRFRequired * @param string $token * @return DataResponse */ @@ -129,29 +186,27 @@ public function sendInvitation($token) { $share = $this->shareService->get($token); return new DataResponse(['share' => $share, 'sentResult' => $sentResult], Http::STATUS_OK); } catch (Exception $e) { - return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); + return new DataResponse(['error' => $e], Http::STATUS_CONFLICT); } } /** - * remove - * remove share + * resolve Contact groupe to individual shares * @NoAdminRequired - * @NoCSRFRequired - * @param Share $share + * @param string $token * @return DataResponse */ - - public function delete($share) { + public function resolveContactGroup($token) { + $shares = []; try { - return new DataResponse(array( - 'action' => 'deleted', - 'shareId' => $this->shareService->remove($share['token'])->getId() - ), Http::STATUS_OK); - } catch (NotAuthorizedException $e) { - return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); + $share = $this->shareService->get($token); + foreach ($this->systemService->getContactsGroupMembers($share->getUserId()) as $member) { + $shares[] = $this->shareService->add($share->getpollId(), 'contact', $member['user'], $member['emailAddress']); + } + + return new DataResponse(['shares' => $shares], Http::STATUS_OK); } catch (Exception $e) { - return new DataResponse($e, Http::STATUS_NOT_FOUND); + return new DataResponse(['error' => $e], Http::STATUS_CONFLICT); } } } diff --git a/lib/Controller/SubscriptionApiController.php b/lib/Controller/SubscriptionApiController.php index 3a23d403b..b50009576 100644 --- a/lib/Controller/SubscriptionApiController.php +++ b/lib/Controller/SubscriptionApiController.php @@ -23,12 +23,10 @@ namespace OCA\Polls\Controller; -use Exception; use OCP\AppFramework\Db\DoesNotExistException; use OCA\Polls\Exceptions\NotAuthorizedException; use OCP\IRequest; -use OCP\ILogger; use OCP\AppFramework\ApiController; use OCP\AppFramework\Http; @@ -38,47 +36,43 @@ class SubscriptionApiController extends ApiController { - private $userId; + /** @var SubscriptionService */ private $subscriptionService; - private $logger; /** - * SubscriptionController constructor. + * SubscriptionApiController constructor * @param string $appName - * @param $UserId * @param SubscriptionService $subscriptionService * @param IRequest $request - * @param ILogger $logger */ public function __construct( string $appName, - $userId, SubscriptionService $subscriptionService, - IRequest $request, - ILogger $logger + IRequest $request ) { parent::__construct($appName, $request, 'PUT, GET, DELETE', - 'Authorization, Content-Type, Accept', - 1728000); - $this->userId = $userId; + 'Authorization, Content-Type, Accept', + 1728000); $this->subscriptionService = $subscriptionService; - $this->logger = $logger; } /** + * Get subscription status * @NoAdminRequired * CORS * @NoCSRFRequired - * @param integer $pollId + * @param int $pollId * @return DataResponse + * @throws DoesNotExistException + * @throws NotAuthorizedException */ public function get($pollId) { try { - $this->subscriptionService->get($pollId); + $this->subscriptionService->get($pollId, ''); return new DataResponse(['status' => 'Subscribed to poll ' . $pollId], Http::STATUS_OK); } catch (DoesNotExistException $e) { return new DataResponse(['status' => 'Not subscribed to poll ' . $pollId], Http::STATUS_NOT_FOUND); @@ -88,28 +82,32 @@ public function get($pollId) { } /** + * Subscribe to poll * @NoAdminRequired * @CORS * @NoCSRFRequired - * @param integer $pollId + * @param int $pollId + * @throws NotAuthorizedException */ public function subscribe($pollId) { try { - $this->subscriptionService->set($pollId, true); + $this->subscriptionService->set($pollId, '', true); return new DataResponse(['status' => 'Subscribed to poll ' . $pollId], Http::STATUS_OK); } catch (NotAuthorizedException $e) { return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); } } /** + * Unsubscribe from poll * @NoAdminRequired * @CORS * @NoCSRFRequired - * @param integer $pollId + * @param int $pollId + * @throws NotAuthorizedException */ public function unsubscribe($pollId) { try { - $this->subscriptionService->set($pollId, false); + $this->subscriptionService->set($pollId, '', false); return new DataResponse(['status' => 'Unsubscribed from poll ' . $pollId], Http::STATUS_OK); } catch (NotAuthorizedException $e) { return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); diff --git a/lib/Controller/SubscriptionController.php b/lib/Controller/SubscriptionController.php index e90f4a66f..2d9fde04c 100644 --- a/lib/Controller/SubscriptionController.php +++ b/lib/Controller/SubscriptionController.php @@ -23,12 +23,10 @@ namespace OCA\Polls\Controller; -use Exception; use OCP\AppFramework\Db\DoesNotExistException; use OCA\Polls\Exceptions\NotAuthorizedException; use OCP\IRequest; -use OCP\ILogger; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; @@ -37,42 +35,37 @@ class SubscriptionController extends Controller { - private $userId; + /** @var SubscriptionService */ private $subscriptionService; - private $logger; /** * SubscriptionController constructor. * @param string $appName - * @param $UserId * @param SubscriptionService $subscriptionService * @param IRequest $request - * @param ILogger $logger */ public function __construct( string $appName, - $userId, SubscriptionService $subscriptionService, - IRequest $request, - ILogger $logger - + IRequest $request ) { parent::__construct($appName, $request); - $this->userId = $userId; $this->subscriptionService = $subscriptionService; - $this->logger = $logger; } /** + * Get subscription status + * @PublicPage * @NoAdminRequired - * @NoCSRFRequired - * @param integer $pollId + * @param int $pollId * @return DataResponse + * @throws DoesNotExistException + * @throws NotAuthorizedException */ - public function get($pollId) { + public function get($pollId, $token) { try { - return new DataResponse($this->subscriptionService->get($pollId), Http::STATUS_OK); + return new DataResponse($this->subscriptionService->get($pollId, $token), Http::STATUS_OK); } catch (NotAuthorizedException $e) { return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); } catch (DoesNotExistException $e) { @@ -81,13 +74,17 @@ public function get($pollId) { } /** + * Switch subscription status + * @PublicPage * @NoAdminRequired - * @NoCSRFRequired - * @param integer $pollId + * @param int $pollId + * @param int $subscribed + * @return DataResponse + * @throws NotAuthorizedException */ - public function set($pollId, $subscribed) { + public function set($pollId, $token, $subscribed) { try { - return new DataResponse($this->subscriptionService->set($pollId, $subscribed), Http::STATUS_OK); + return new DataResponse($this->subscriptionService->set($pollId, $token, $subscribed), Http::STATUS_OK); } catch (NotAuthorizedException $e) { return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); } diff --git a/lib/Controller/SystemController.php b/lib/Controller/SystemController.php index 44dfbf8ab..defe24cf5 100644 --- a/lib/Controller/SystemController.php +++ b/lib/Controller/SystemController.php @@ -26,290 +26,127 @@ use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; +use OCA\Polls\Service\SystemService; -use OCP\IGroupManager; -use OCP\IUser; -use OCP\IUserManager; -use OCP\IConfig; use OCP\IRequest; -use OCA\Polls\Db\Share; -use OCA\Polls\Db\ShareMapper; -use OCA\Polls\Db\Vote; -use OCA\Polls\Db\VoteMapper; -use OCP\ILogger; class SystemController extends Controller { - private $userId; - private $logger; - private $systemConfig; - private $groupManager; - private $userManager; - private $voteMapper; - private $shareMapper; + /** @var SystemService */ + private $systemService; /** * SystemController constructor. * @param string $appName - * @param $userId * @param IRequest $request - * @param ILogger $logger - * @param IConfig $systemConfig - * @param IGroupManager $groupManager - * @param IUserManager $userManager - * @param VoteMapper $voteMapper - * @param ShareMapper $shareMapper + * @param SystemService $systemService */ + public function __construct( string $appName, - $userId, IRequest $request, - ILogger $logger, - IConfig $systemConfig, - IGroupManager $groupManager, - IUserManager $userManager, - VoteMapper $voteMapper, - ShareMapper $shareMapper + SystemService $systemService ) { parent::__construct($appName, $request); - $this->voteMapper = $voteMapper; - $this->shareMapper = $shareMapper; - $this->logger = $logger; - $this->userId = $userId; - $this->systemConfig = $systemConfig; - $this->groupManager = $groupManager; - $this->userManager = $userManager; + $this->systemService = $systemService; } /** - * Validate string as email address + * Get a list of users * @NoAdminRequired * @param string $query - * @return Boolval + * @param array $skipUsers - usernames to skip in return array + * @return DataResponse */ - private function isValidEmail($email) { - return (!preg_match('/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/', $email)) ? false : true; - } + public function getSiteUsers($query = '', $skipUsers = []) { + return new DataResponse(['users' => $this->systemService->getSiteUsers($query, $skipUsers)], Http::STATUS_OK); + } /** - * Get a list of NC users, groups and contacts + * Get a list of user groups * @NoAdminRequired - * @NoCSRFRequired * @param string $query - * @param bool $getGroups - search in groups - * @param bool $getUsers - search in site users - * @param bool $getContacts - search in contacs * @param array $skipGroups - group names to skip in return array - * @param array $skipUsers - user names to skip in return array * @return DataResponse */ - public function getSiteUsersAndGroups($query = '', $getGroups = true, $getUsers = true, $getContacts = true, $getMail = false, $skipGroups = array(), $skipUsers = array()) { - $list = array(); - // if (filter_var($query, FILTER_VALIDATE_EMAIL)) { - if ($this->isValidEmail($query)) { - $list[] = [ - 'id' => '', - 'user' => '', - 'organisation' => '', - 'displayName' => '', - 'emailAddress' => $query, - 'desc' => $query, - 'type' => 'email', - 'icon' => 'icon-mail', - 'avatarURL' => '', - 'avatar' => '', - 'lastLogin' => '', - 'cloudId' => '' - - ]; - } - - - if ($getGroups) { - $groups = $this->groupManager->search($query); - foreach ($groups as $group) { - if (!in_array($group->getGID(), $skipGroups)) { - $list[] = [ - 'id' => $group->getGID(), - 'user' => $group->getGID(), - 'organisation' => '', - 'displayName' => $group->getGID(), - 'emailAddress' => '', - 'desc' => 'Group', - 'type' => 'group', - 'icon' => 'icon-group', - 'avatarURL' => '', - 'avatar' => '', - 'lastLogin' => '', - 'cloudId' => '' - - ]; - } - } - } - - if ($getUsers) { - $users = $this->userManager->searchDisplayName($query); - foreach ($users as $user) { - if (!in_array($user->getUID(), $skipUsers) && $user->isEnabled()) { - $list[] = [ - 'id' => $user->getUID(), - 'user' => $user->getUID(), - 'displayName' => $user->getDisplayName(), - 'organisation' => '', - 'emailAddress' => $user->getEMailAddress(), - 'desc' => 'User', - 'type' => 'user', - 'icon' => 'icon-user', - 'avatarURL' => '', - 'avatar' => '', - 'lastLogin' => $user->getLastLogin(), - 'cloudId' => $user->getCloudId() - ]; - } - } - } - - $contactsManager = \OC::$server->getContactsManager(); - - - if ($getContacts && $contactsManager->isEnabled()) { - $contacts = $contactsManager->search($query, array('FN', 'EMAIL', 'ORG', 'CATEGORIES')); - - foreach ($contacts as $contact) { - if (!array_key_exists('isLocalSystemBook', $contact) && array_key_exists('EMAIL', $contact)) { - - $emailAdresses = $contact['EMAIL']; + public function getSiteGroups($query = '', $skipGroups = []) { + return new DataResponse(['groups' => $this->systemService->getSiteGroups($query, $skipGroups)], Http::STATUS_OK); + } - if (!is_array($emailAdresses)) { - $emailAdresses = array($emailAdresses); - } else { - // take the first eMail address for now - $emailAdresses = array($emailAdresses[0]); - } + /** + * Get a list of contacts + * @NoAdminRequired + * @param string $query + * @return DataResponse + */ + public function getContacts($query = '') { + return new DataResponse(['contacts' => $this->systemService->getContacts($query)], Http::STATUS_OK); + } - foreach ($emailAdresses as $emailAddress) { - $list[] = [ - 'id' => $contact['UID'], - 'user' => $contact['FN'], - 'displayName' => $contact['FN'], - 'organisation' => isset($contact['ORG']) ? $contact['ORG'] : '', - 'emailAddress' => $emailAddress, - 'desc' => 'Contact', - 'type' => 'contact', - 'icon' => 'icon-mail', - 'avatarURL' => '', - 'avatar' => isset($contact['PHOTO']) ? $contact['PHOTO'] : '', - 'lastLogin' => '', - 'cloudId' => '' - ]; - } + /** + * Get a list of contact groups + * @NoAdminRequired + * @param string $query + * @return DataResponse + */ + public function getContactsGroups($query = '') { + return new DataResponse(['contactGroups' => $this->systemService->getContactsGroups($query)], Http::STATUS_OK); + } - } - } - } - - return new DataResponse([ - 'siteusers' => $list - ], Http::STATUS_OK); + /** + * Get a combined list of NC users, groups and contacts + * @NoAdminRequired + * @param string $query + * @param bool $getGroups - search in groups + * @param bool $getUsers - search in site users + * @param bool $getContacts - search in contacs + * @param bool $getContactGroups - search in contacs + * @param array $skipGroups - group names to skip in return array + * @param array $skipUsers - user names to skip in return array + * @return DataResponse + */ + public function getSiteUsersAndGroups( + $query = '', + $getGroups = true, + $getUsers = true, + $getContacts = true, + $getContactGroups = true, + $getMail = false, + $skipGroups = [], + $skipUsers = [] + ) { + return new DataResponse(['siteusers' => $this->systemService->getSiteUsersAndGroups( + $query, $getGroups, $getUsers, $getContacts, $getContactGroups, $getMail, $skipGroups, $skipUsers)], Http::STATUS_OK); } /** * Validate it the user name is reservrd * return false, if this username already exists as a user or as * a participant of the poll - * @NoCSRFRequired * @NoAdminRequired * @PublicPage * @return DataResponse */ public function validatePublicUsername($pollId, $userName, $token) { - - // return forbidden, if $pollId does not match the share's pollId, force int compare - if (intval($this->shareMapper->findByToken($token)->getPollId()) !== intVal($pollId)) { - return new DataResponse(['result' => false, 'error' => 'wrong token'], Http::STATUS_FORBIDDEN); + try { + return new DataResponse(['result' => $this->systemService->validatePublicUsername($pollId, $userName, $token), 'name' => $userName], Http::STATUS_OK); + } catch (\Exception $e) { + return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); } - - // return forbidden, if the length of the userame is lower than 3 characters - if (strlen(trim($userName)) < 3) { - return new DataResponse(['result' => false, 'error' => 'userName too short'], Http::STATUS_FORBIDDEN); - } - - $list = array(); - - // get all groups - $groups = $this->groupManager->search(''); - foreach ($groups as $group) { - $list[] = [ - 'id' => $group->getGID(), - 'user' => $group->getGID(), - 'type' => 'group', - 'displayName' => $group->getGID(), - ]; - } - - // get all users - $users = $this->userManager->searchDisplayName(''); - foreach ($users as $user) { - $list[] = [ - 'id' => $user->getUID(), - 'user' => $user->getUID(), - 'type' => 'user', - 'displayName' => $user->getDisplayName(), - ]; - } - - // get all participants - $votes = $this->voteMapper->findParticipantsByPoll($pollId); - foreach ($votes as $vote) { - if ($vote->getUserId() !== '' && $vote->getUserId() !== null) { - $list[] = [ - 'id' => $vote->getUserId(), - 'user' => $vote->getUserId(), - 'type' => 'participant', - 'displayName' => $vote->getUserId(), - ]; - } - } - - // get all shares for this poll - $shares = $this->shareMapper->findByPoll($pollId); - foreach ($shares as $share) { - if ($share->getUserId() !== '' && $share->getUserId() !== null) { - $list[] = [ - 'id' => $share->getUserId(), - 'user' => $share->getUserId(), - 'type' => 'share', - 'displayName' => $share->getUserId(), - ]; - } - } - - // check if the username is contained inside the generated list - // return forbidden, if list contains requested username - foreach ($list as $element) { - if (strtolower(trim($userName)) === strtolower(trim($element['id'])) || strtolower(trim($userName)) === strtolower(trim($element['displayName']))) { - return new DataResponse([ - 'result' => false - ], Http::STATUS_FORBIDDEN); - } - } - - // return OK, if username is allowed - return new DataResponse([ - 'result' => true, - 'name' => $userName - ], Http::STATUS_OK); } - public function getDisplayName() { - $this->userManager = \OC::$server->getUserManager(); - - if (\OC::$server->getUserManager()->get($this->userId) instanceof IUser) { - return \OC::$server->getUserManager()->get($this->userId)->getDisplayName(); - } else { - return $this->userId; + /** + * Validate email address (simple validation) + * @NoAdminRequired + * @PublicPage + * @return DataResponse + */ + public function validateEmailAddress($emailAddress) { + try { + return new DataResponse(['result' => $this->systemService->validateEmailAddress($emailAddress), 'emailAddress' => $emailAddress], Http::STATUS_OK); + } catch (\Exception $e) { + return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); } } } diff --git a/lib/Controller/VoteApiController.php b/lib/Controller/VoteApiController.php index 21d7762dc..5528ce304 100644 --- a/lib/Controller/VoteApiController.php +++ b/lib/Controller/VoteApiController.php @@ -23,12 +23,10 @@ namespace OCA\Polls\Controller; -use Exception; use OCP\AppFramework\Db\DoesNotExistException; use OCA\Polls\Exceptions\NotAuthorizedException; use OCP\IRequest; -use OCP\ILogger; use OCP\AppFramework\ApiController; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; @@ -37,38 +35,34 @@ class VoteApiController extends ApiController { - private $logger; + /** @var VoteService */ private $voteService; /** - * VoteController constructor. + * VoteAPIController constructor * @param string $appName * @param IRequest $request - * @param ILogger $logger * @param VoteService $voteService */ public function __construct( string $appName, IRequest $request, - ILogger $logger, VoteService $voteService ) { parent::__construct($appName, $request, 'PUT, GET, DELETE', - 'Authorization, Content-Type, Accept', - 1728000); + 'Authorization, Content-Type, Accept', + 1728000); $this->voteService = $voteService; - $this->logger = $logger; } /** - * Get all votes of given poll * Read all votes of a poll based on the poll id and return list as array * @NoAdminRequired - * @NoCSRFRequired * @CORS - * @param integer $pollId + * @NoCSRFRequired + * @param int $pollId * @return DataResponse */ public function list($pollId) { @@ -82,24 +76,21 @@ public function list($pollId) { } /** - * set + * Set vote answer * @NoAdminRequired - * @NoCSRFRequired * @CORS - * @param integer $pollId - * @param Array $option - * @param string $userId + * @NoCSRFRequired + * @param int $optionId * @param string $setTo * @return DataResponse */ - public function set($pollId, $pollOptionText, $setTo) { + public function set($optionId, $setTo) { try { - return new DataResponse(['vote' => $this->voteService->set($pollId, $pollOptionText, $setTo)], Http::STATUS_OK); + return new DataResponse(['vote' => $this->voteService->set($optionId, $setTo)], Http::STATUS_OK); } catch (DoesNotExistException $e) { - return new DataResponse(['error' => 'Option not found'], Http::STATUS_NOT_FOUND); + return new DataResponse(['error' => 'Option or poll not found'], Http::STATUS_NOT_FOUND); } catch (NotAuthorizedException $e) { return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); } - } } diff --git a/lib/Controller/VoteController.php b/lib/Controller/VoteController.php index f666c0046..02db12783 100644 --- a/lib/Controller/VoteController.php +++ b/lib/Controller/VoteController.php @@ -27,7 +27,6 @@ use OCP\AppFramework\Db\DoesNotExistException; use OCA\Polls\Exceptions\NotAuthorizedException; -use OCP\ILogger; use OCP\IRequest; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; @@ -35,37 +34,30 @@ use OCA\Polls\Service\VoteService; - class VoteController extends Controller { + /** @var VoteService */ private $voteService; - private $logger; /** - * VoteController constructor. + * VoteController constructor * @param string $appName * @param IRequest $request - * @param ILogger $logger * @param VoteService $voteService - */ public function __construct( string $appName, - ILogger $logger, IRequest $request, VoteService $voteService ) { parent::__construct($appName, $request); - $this->logger = $logger; $this->voteService = $voteService; } /** - * Get all votes of given poll * Read all votes of a poll based on the poll id and return list as array * @NoAdminRequired - * @NoCSRFRequired - * @param integer $pollId + * @param int $pollId * @return DataResponse */ public function get($pollId) { @@ -82,35 +74,30 @@ public function get($pollId) { * set * @NoAdminRequired * @NoCSRFRequired - * @param integer $pollId - * @param Array $option - * @param string $userId + * @param int $optionId * @param string $setTo * @return DataResponse */ - public function set($pollId, $option, $setTo) { + public function set($optionId, $setTo) { try { - return new DataResponse($this->voteService->set($pollId, $option['pollOptionText'], $setTo), Http::STATUS_OK); + return new DataResponse($this->voteService->set($optionId, $setTo), Http::STATUS_OK); } catch (NotAuthorizedException $e) { return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); } catch (DoesNotExistException $e) { - return new DataResponse(['error' => 'Option not found'], Http::STATUS_NOT_FOUND); + return new DataResponse(['error' => 'Option or poll not found'], Http::STATUS_NOT_FOUND); } } - /** - * delete + * Remove user from poll * @NoAdminRequired - * @NoCSRFRequired - * @param integer $voteId * @param string $userId - * @param integer $pollId + * @param int $pollId * @return DataResponse */ - public function delete($userId, $pollId) { + public function delete($pollId, $userId) { try { - return new DataResponse($this->voteService->delete($pollId, $userId), Http::STATUS_OK); + return new DataResponse(['deleted' => $this->voteService->delete($pollId, $userId)], Http::STATUS_OK); } catch (NotAuthorizedException $e) { return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); } catch (DoesNotExistException $e) { @@ -123,32 +110,28 @@ public function delete($userId, $pollId) { */ /** - * setByToken + * Set vote with token * @NoAdminRequired * @PublicPage - * @NoCSRFRequired * @param Array $option * @param string $setTo * @param string $token * @return DataResponse */ - public function setByToken($option, $setTo, $token) { + public function setByToken($optionId, $setTo, $token) { try { - return new DataResponse($this->voteService->set(0, $option['pollOptionText'], $setTo, $token), Http::STATUS_OK); + return new DataResponse($this->voteService->set($optionId, $setTo, $token), Http::STATUS_OK); } catch (NotAuthorizedException $e) { return new DataResponse(['error' => $e->getMessage()], $e->getStatus()); } catch (DoesNotExistException $e) { return new DataResponse(['error' => 'Option not found'], Http::STATUS_NOT_FOUND); } - } /** - * getByToken * Read all votes of a poll based on a share token and return list as array * @NoAdminRequired * @PublicPage - * @NoCSRFRequired * @param string $token * @return DataResponse */ @@ -160,7 +143,5 @@ public function getByToken($token) { } catch (DoesNotExistException $e) { return new DataResponse(['error' => 'No votes'], Http::STATUS_NOT_FOUND); } - } - } diff --git a/lib/Cron/NotificationCron.php b/lib/Cron/NotificationCron.php index e5ddbe13d..6aa4fb17a 100644 --- a/lib/Cron/NotificationCron.php +++ b/lib/Cron/NotificationCron.php @@ -28,11 +28,10 @@ class NotificationCron extends TimedJob { - /** @var MailService*/ + /** @var MailService */ private $mailService; - /** @param MailService $mailService - */ + /** @param MailService $mailService */ public function __construct( MailService $mailService ) { diff --git a/lib/Db/Comment.php b/lib/Db/Comment.php index 4935c077d..0a9fe9558 100644 --- a/lib/Db/Comment.php +++ b/lib/Db/Comment.php @@ -31,9 +31,9 @@ use OCP\AppFramework\Db\Entity; /** - * @method integer getId() + * @method int getId() * @method void setId(integer $value) - * @method integer getPollId() + * @method int getPollId() * @method void setPollId(integer $value) * @method string getUserId() * @method void setUserId(string $value) @@ -78,16 +78,20 @@ public function jsonSerialize() { 'dt' => $this->dt, 'timestamp' => intval($timestamp), 'comment' => $this->comment, - 'displayName' => $this->getDisplayName() + 'displayName' => $this->getDisplayName(), + 'externalUser' => $this->externalUser() ]; } private function getDisplayName() { - if (\OC::$server->getUserManager()->get($this->userId) instanceof IUser) { return \OC::$server->getUserManager()->get($this->userId)->getDisplayName(); } else { return $this->userId; } } + + private function externalUser() { + return (!\OC::$server->getUserManager()->get($this->userId) instanceof IUser); + } } diff --git a/lib/Db/CommentMapper.php b/lib/Db/CommentMapper.php index 93da02ae1..43663d878 100644 --- a/lib/Db/CommentMapper.php +++ b/lib/Db/CommentMapper.php @@ -86,7 +86,7 @@ public function deleteByPoll($pollId) { $qb->expr()->eq('poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)) ); - $qb->execute(); + $qb->execute(); } /** @@ -100,6 +100,6 @@ public function deleteComment($id) { $qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)) ); - $qb->execute(); + $qb->execute(); } } diff --git a/lib/Db/Log.php b/lib/Db/Log.php index ce2492f34..920566747 100644 --- a/lib/Db/Log.php +++ b/lib/Db/Log.php @@ -29,13 +29,13 @@ use OCP\AppFramework\Db\Entity; /** - * @method integer getPollId() + * @method int getPollId() * @method void setPollId(integer $value) - * @method integer getCreated() + * @method int getCreated() * @method void setCreated(integer $value) - * @method integer getProcessed() + * @method int getProcessed() * @method void setProcessed(integer $value) - * @method integer getUserId() + * @method int getUserId() * @method void setUserId(string $value) * @method string getDisplayName() * @method void setDisplayName(string $value) diff --git a/lib/Db/LogMapper.php b/lib/Db/LogMapper.php index 88f755c6e..ba01beb61 100644 --- a/lib/Db/LogMapper.php +++ b/lib/Db/LogMapper.php @@ -4,7 +4,7 @@ * * @author Vinzenz Rosenkranz * @author René Gieling -* + * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify @@ -44,11 +44,10 @@ public function findByPollId($pollId) { $qb->select('*') ->from($this->getTableName()) ->where( - $qb->expr()->eq('poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)) - ); + $qb->expr()->eq('poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)) + ); return $this->findEntities($qb); - } public function findUnprocessed() { @@ -57,11 +56,10 @@ public function findUnprocessed() { $qb->select('*') ->from($this->getTableName()) ->where( - $qb->expr()->eq('processed', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)) - ); + $qb->expr()->eq('processed', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)) + ); return $this->findEntities($qb); - } public function findUnprocessedPolls() { @@ -71,7 +69,6 @@ public function findUnprocessedPolls() { ->from($this->getTableName()) ->where($qb->expr()->eq('processed', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT))); return $this->findEntities($qb); - } public function getLastRecord($pollId) { @@ -84,9 +81,5 @@ public function getLastRecord($pollId) { ->orderBy('id', 'DESC'); return $this->findEntity($qb); - } - - - } diff --git a/lib/Db/Option.php b/lib/Db/Option.php index 8f4d4fcf5..f3c8bd7ad 100644 --- a/lib/Db/Option.php +++ b/lib/Db/Option.php @@ -5,7 +5,7 @@ * @author Vinzenz Rosenkranz * @author Kai Schröer * @author René Gieling -* + * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify @@ -30,17 +30,17 @@ use OCP\AppFramework\Db\Entity; /** - * @method integer getId() + * @method int getId() * @method void setId(integer $value) - * @method integer getPollId() + * @method int getPollId() * @method void setPollId(integer $value) * @method string getPollOptionText() * @method void setPollOptionText(string $value) - * @method integer getTimestamp() + * @method int getTimestamp() * @method void setTimestamp(integer $value) - * @method integer getOrder() + * @method int getOrder() * @method void setOrder(integer $value) - * @method integer getConfirmed() + * @method int getConfirmed() * @method void setConfirmed(integer $value) */ class Option extends Entity implements JsonSerializable { @@ -83,14 +83,13 @@ public function jsonSerialize() { 'rank' => 0, 'votes' => 0, ]; - } /** * Temporary fix * Make sure, order is eqal to timestamp in date polls */ - // TODO: remove by time + // TODO: remove by time private function orderCorrection($timestamp, $order) { if ($timestamp) { return $timestamp; diff --git a/lib/Db/OptionMapper.php b/lib/Db/OptionMapper.php index 3044c90c7..0cdd8a0a2 100644 --- a/lib/Db/OptionMapper.php +++ b/lib/Db/OptionMapper.php @@ -4,7 +4,7 @@ * * @author Vinzenz Rosenkranz * @author René Gieling -* + * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify @@ -108,7 +108,7 @@ public function remove($optionId) { $qb->expr()->eq('id', $qb->createNamedParameter($optionId, IQueryBuilder::PARAM_INT)) ); - $qb->execute(); + $qb->execute(); } /** @@ -122,6 +122,6 @@ public function deleteByPoll($pollId) { $qb->expr()->eq('poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)) ); - $qb->execute(); + $qb->execute(); } } diff --git a/lib/Db/Poll.php b/lib/Db/Poll.php index 8fa2d0569..dddbb17eb 100644 --- a/lib/Db/Poll.php +++ b/lib/Db/Poll.php @@ -5,7 +5,7 @@ * @author Vinzenz Rosenkranz * @author Kai Schröer * @author René Gieling -* + * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify @@ -39,30 +39,32 @@ * @method void setDescription(string $value) * @method string getOwner() * @method void setOwner(string $value) - * @method integer getCreated() + * @method int getCreated() * @method void setCreated(integer $value) - * @method integer getExpire() + * @method int getExpire() * @method void setExpire(integer $value) - * @method integer getDeleted() + * @method int getDeleted() * @method void setDeleted(integer $value) * @method string getAccess() * @method void setAccess(string $value) - * @method integer getAnonymous() + * @method int getAnonymous() * @method void setAnonymous(integer $value) - * @method integer getFullAnonymous() + * @method int getFullAnonymous() * @method void setFullAnonymous(integer $value) - * @method integer getAllowMaybe() + * @method int getAllowMaybe() * @method void setAllowMaybe(integer $value) * @method string getOptions() * @method void setOptions(string $value) * @method string getSettings() * @method void setSettings(string $value) - * @method integer getVoteLimit() + * @method int getVoteLimit() * @method void setVoteLimit(integer $value) * @method string getShowResults() * @method void setShowResults(string $value) - * @method integer getAdminAccess() + * @method int getAdminAccess() * @method void setAdminAccess(integer $value) + * @method int getImportant() + * @method void setImportant(integer $value) */ class Poll extends Entity implements JsonSerializable { @@ -114,6 +116,9 @@ class Poll extends Entity implements JsonSerializable { /** @var int $adminAccess*/ protected $adminAccess; + /** @var int $important*/ + protected $important; + public function jsonSerialize() { return [ 'id' => intval($this->id), @@ -131,7 +136,8 @@ public function jsonSerialize() { 'voteLimit' => intval($this->voteLimit), 'showResults' => $this->showResults, 'adminAccess' => intVal($this->adminAccess), - 'ownerDisplayName' => $this->getDisplayName() + 'ownerDisplayName' => $this->getDisplayName(), + 'important' => intVal($this->important) ]; } @@ -146,11 +152,11 @@ public function deserializeArray($array) { $this->setShowResults(isset($array['showResults']) ? $array['showResults'] : $this->getShowResults()); $this->setDeleted(isset($array['deleted']) ? $array['deleted'] : $this->getDeleted()); $this->setAdminAccess(isset($array['adminAccess']) ? $array['adminAccess'] : $this->getAdminAccess()); + $this->setImportant(isset($array['important']) ? $array['important'] : $this->getImportant()); return $this; } private function getDisplayName() { - if (\OC::$server->getUserManager()->get($this->owner) instanceof IUser) { return \OC::$server->getUserManager()->get($this->owner)->getDisplayName(); } else { diff --git a/lib/Db/PollMapper.php b/lib/Db/PollMapper.php index 4938c749c..586985b0f 100644 --- a/lib/Db/PollMapper.php +++ b/lib/Db/PollMapper.php @@ -4,7 +4,7 @@ * * @author Vinzenz Rosenkranz * @author René Gieling -* + * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify @@ -27,7 +27,6 @@ use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; use OCP\AppFramework\Db\QBMapper; -use \OCP\AppFramework\Db\DoesNotExistException; class PollMapper extends QBMapper { @@ -69,5 +68,4 @@ public function findAll() { return $this->findEntities($qb); } - } diff --git a/lib/Db/Preferences.php b/lib/Db/Preferences.php new file mode 100644 index 000000000..3c68f398d --- /dev/null +++ b/lib/Db/Preferences.php @@ -0,0 +1,59 @@ + + * + * @author René Gieling + * + * @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 . + * + */ + +namespace OCA\Polls\Db; + +use JsonSerializable; + +use OCP\AppFramework\Db\Entity; + +/** + * @method integer getId() + * @method void setId(integer $value) + * @method string getUserId() + * @method void setUserId(string $value) + * @method string getTimestamp() + * @method void setTimestamp(integer $value) + * @method string getPreferences() + * @method void setPreferences(string $value) + */ +class Preferences extends Entity implements JsonSerializable { + + /** @var string $userId */ + protected $userId; + + /** @var integer $timestamp */ + protected $timestamp; + + /** @var string $preferences */ + protected $preferences; + + public function jsonSerialize() { + return [ + 'id' => intval($this->id), + 'userId' => $this->userId, + 'timestamp' => intval($this->timestamp), + 'preferences' => json_decode($this->preferences), + ]; + } +} diff --git a/lib/Db/PreferencesMapper.php b/lib/Db/PreferencesMapper.php new file mode 100644 index 000000000..651e46d42 --- /dev/null +++ b/lib/Db/PreferencesMapper.php @@ -0,0 +1,73 @@ + + * + * @author Vinzenz Rosenkranz + * @author René Gieling + * + * @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 . + * + */ + +namespace OCA\Polls\Db; + +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IDBConnection; +use OCP\AppFramework\Db\QBMapper; + +class PreferencesMapper extends QBMapper { + + /** + * PreferencesMapper constructor. + * @param IDBConnection $db + */ + public function __construct(IDBConnection $db) { + parent::__construct($db, 'polls_preferences', '\OCA\Polls\Db\Preferences'); + } + + /** + * @param int $id + * @throws \OCP\AppFramework\Db\DoesNotExistException if not found + * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException if more than one result + * @return Preferences + */ + + public function find($userId) { + $qb = $this->db->getQueryBuilder(); + + $qb->select('*') + ->from($this->getTableName()) + ->where( + $qb->expr()->eq('user_id', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_INT)) + ); + + return $this->findEntity($qb); + } + + // /** + // * @param int $userId + // */ + // public function delete($userId) { + // $qb = $this->db->getQueryBuilder(); + // + // $qb->delete($this->getTableName()) + // ->where( + // $qb->expr()->eq('user_id', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_INT)) + // ); + // + // $qb->execute(); + // } +} diff --git a/lib/Db/Share.php b/lib/Db/Share.php index 1a3042d22..ecc5da1d1 100644 --- a/lib/Db/Share.php +++ b/lib/Db/Share.php @@ -35,13 +35,13 @@ * @method void setToken(string $value) * @method string getType() * @method void setType(string $value) - * @method integer getPollId() + * @method int getPollId() * @method void setPollId(integer $value) * @method string getUserId() * @method void setUserId(string $value) * @method string getUserEmail() * @method void setUserEmail(string $value) - * @method integer getInvitationSent() + * @method int getInvitationSent() * @method void setInvitationSent(integer $value) */ class Share extends Entity implements JsonSerializable { @@ -65,7 +65,6 @@ class Share extends Entity implements JsonSerializable { protected $invitationSent; public function jsonSerialize() { - return [ 'id' => intval($this->id), 'token' => $this->token, @@ -73,17 +72,21 @@ public function jsonSerialize() { 'pollId' => intval($this->pollId), 'userId' => $this->userId, 'userEmail' => $this->userEmail, + 'invitationSent' => intval($this->invitationSent), 'displayName' => $this->getDisplayName(), - 'invitationSent' => intval($this->invitationSent) + 'externalUser' => $this->externalUser() ]; } private function getDisplayName() { - if (\OC::$server->getUserManager()->get($this->userId) instanceof IUser) { return \OC::$server->getUserManager()->get($this->userId)->getDisplayName(); } else { return $this->userId; } } + + private function externalUser() { + return (!\OC::$server->getUserManager()->get($this->userId) instanceof IUser); + } } diff --git a/lib/Db/ShareMapper.php b/lib/Db/ShareMapper.php index 3a4408f36..f1e56727c 100644 --- a/lib/Db/ShareMapper.php +++ b/lib/Db/ShareMapper.php @@ -70,6 +70,28 @@ public function findByPoll($pollId) { return $this->findEntities($qb); } + /** + * @param int $pollId + * @param string $userId + * @throws \OCP\AppFramework\Db\DoesNotExistException if not found + * @return array + */ + + public function findByPollAndUser($pollId, $userId) { + $qb = $this->db->getQueryBuilder(); + + $qb->select('*') + ->from($this->getTableName()) + ->where( + $qb->expr()->eq('poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)) + ) + ->andWhere( + $qb->expr()->eq('user_id', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)) + ); + + return $this->findEntity($qb); + } + /** * @param string $token * @throws \OCP\AppFramework\Db\DoesNotExistException if not found @@ -99,7 +121,7 @@ public function deleteByPoll($pollId) { $qb->expr()->eq('poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)) ); - $qb->execute(); + $qb->execute(); } /** @@ -113,7 +135,6 @@ public function remove($shareId) { $qb->expr()->eq('id', $qb->createNamedParameter($shareId, IQueryBuilder::PARAM_INT)) ); - $qb->execute(); + $qb->execute(); } - } diff --git a/lib/Db/Subscription.php b/lib/Db/Subscription.php index 256fcbc15..ace2ebd50 100644 --- a/lib/Db/Subscription.php +++ b/lib/Db/Subscription.php @@ -29,9 +29,9 @@ use OCP\AppFramework\Db\Entity; /** - * @method integer getId() + * @method int getId() * @method void setId(integer $value) - * @method integer getPollId() + * @method int getPollId() * @method void setPollId(integer $value) * @method string getUserId() * @method void setUserId(string $value) diff --git a/lib/Db/SubscriptionMapper.php b/lib/Db/SubscriptionMapper.php index b8b231e0e..dd051fee3 100644 --- a/lib/Db/SubscriptionMapper.php +++ b/lib/Db/SubscriptionMapper.php @@ -4,7 +4,7 @@ * * @author Vinzenz Rosenkranz * @author René Gieling -* + * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify @@ -45,14 +45,14 @@ public function __construct(IDBConnection $db) { * @return array */ - public function findAll() { - $qb = $this->db->getQueryBuilder(); + public function findAll() { + $qb = $this->db->getQueryBuilder(); - $qb->select('*') + $qb->select('*') ->from($this->getTableName()); - return $this->findEntities($qb); - } + return $this->findEntities($qb); + } /** * @param int $pollId @@ -61,17 +61,17 @@ public function findAll() { * @return array */ - public function findAllByPoll($pollId) { - $qb = $this->db->getQueryBuilder(); + public function findAllByPoll($pollId) { + $qb = $this->db->getQueryBuilder(); - $qb->select('*') + $qb->select('*') ->from($this->getTableName()) ->where( $qb->expr()->eq('poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)) ); - return $this->findEntities($qb); - } + return $this->findEntities($qb); + } /** * @param int $pollId @@ -83,7 +83,7 @@ public function findAllByPoll($pollId) { public function findByUserAndPoll($pollId, $userId) { $qb = $this->db->getQueryBuilder(); - $qb->select('*') + $qb->select('*') ->from($this->getTableName()) ->where( $qb->expr()->eq('poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)) @@ -92,25 +92,24 @@ public function findByUserAndPoll($pollId, $userId) { $qb->expr()->eq('user_id', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)) ); - return $this->findEntity($qb); + return $this->findEntity($qb); } /** * @param int $pollId */ - public function unsubscribe($pollId, $currentUser) { - $qb = $this->db->getQueryBuilder(); + public function unsubscribe($pollId, $currentUser) { + $qb = $this->db->getQueryBuilder(); - $qb->delete($this->getTableName()) - ->where( - $qb->expr()->eq('poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)) - ) + $qb->delete($this->getTableName()) + ->where( + $qb->expr()->eq('poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)) + ) ->andWhere( $qb->expr()->eq('user_id', $qb->createNamedParameter($currentUser, IQueryBuilder::PARAM_STR)) ); - $qb->execute(); - } - + $qb->execute(); + } } diff --git a/lib/Db/Vote.php b/lib/Db/Vote.php index b60134676..e8b31ddac 100644 --- a/lib/Db/Vote.php +++ b/lib/Db/Vote.php @@ -5,7 +5,7 @@ * @author Vinzenz Rosenkranz * @author Kai Schröer * @author René Gieling -* + * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify @@ -30,11 +30,11 @@ use OCP\AppFramework\Db\Entity; /** - * @method integer getPollId() + * @method int getPollId() * @method void setPollId(integer $value) * @method string getUserId() * @method void setUserId(string $value) - * @method integer getVoteOptionId() + * @method int getVoteOptionId() * @method void setVoteOptionId(integer $value) * @method string getVoteOptionText() * @method void setVoteOptionText(string $value) @@ -66,16 +66,20 @@ public function jsonSerialize() { 'voteOptionId' => intval($this->voteOptionId), 'voteOptionText' => $this->voteOptionText, 'voteAnswer' => $this->voteAnswer, - 'displayName' => $this->getDisplayName() + 'displayName' => $this->getDisplayName(), + 'externalUser' => $this->externalUser() ]; } - private function getDisplayName() { - + public function getDisplayName() { if (\OC::$server->getUserManager()->get($this->userId) instanceof IUser) { return \OC::$server->getUserManager()->get($this->userId)->getDisplayName(); } else { return $this->userId; } } + + private function externalUser() { + return (!\OC::$server->getUserManager()->get($this->userId) instanceof IUser); + } } diff --git a/lib/Db/VoteMapper.php b/lib/Db/VoteMapper.php index c38d9e4ba..b6cf885db 100644 --- a/lib/Db/VoteMapper.php +++ b/lib/Db/VoteMapper.php @@ -4,7 +4,7 @@ * * @author Vinzenz Rosenkranz * @author René Gieling -* + * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify @@ -141,7 +141,7 @@ public function findParticipantsVotes($pollId, $userId) { * @param int $pollId * @param string $userId */ - public function deleteByPollAndUser($pollId, $userId) { + public function deleteByPollAndUser($pollId, $userId) { $qb = $this->db->getQueryBuilder(); $qb->delete($this->getTableName()) @@ -152,7 +152,7 @@ public function deleteByPollAndUser($pollId, $userId) { $qb->expr()->eq('user_id', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)) ); - $qb->execute(); + $qb->execute(); } /** diff --git a/lib/Exceptions/BadRequestException.php b/lib/Exceptions/BadRequestException.php new file mode 100644 index 000000000..e8414a695 --- /dev/null +++ b/lib/Exceptions/BadRequestException.php @@ -0,0 +1,39 @@ + + * + * @author René Gieling + * + * @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 . + * + */ + +namespace OCA\Polls\Exceptions; + +use OCP\AppFramework\Http; + +class BadRequestException extends \Exception { + /** + * NotAuthorizedException Constructor + * @param string $e exception message + */ + public function __construct($e = 'Not allowed') { + parent::__construct($e); + } + public function getStatus() { + return Http::STATUS_BAD_REQUEST; + } +} diff --git a/lib/Exceptions/DuplicateEntryException.php b/lib/Exceptions/DuplicateEntryException.php new file mode 100644 index 000000000..bdfa27bab --- /dev/null +++ b/lib/Exceptions/DuplicateEntryException.php @@ -0,0 +1,39 @@ + + * + * @author René Gieling + * + * @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 . + * + */ + +namespace OCA\Polls\Exceptions; + +use OCP\AppFramework\Http; + +class DuplicateEntryException extends \Exception { + /** + * DuplicateEntryException Constructor + * @param string $e exception message + */ + public function __construct($e = 'Duplicate Entry') { + parent::__construct($e); + } + public function getStatus() { + return Http::STATUS_CONFLICT; + } +} diff --git a/lib/Exceptions/EmptyTitleException.php b/lib/Exceptions/EmptyTitleException.php index acfc4c57b..66527b1da 100644 --- a/lib/Exceptions/EmptyTitleException.php +++ b/lib/Exceptions/EmptyTitleException.php @@ -36,5 +36,4 @@ public function __construct($message = 'Poll title must not be empty') { public function getStatus() { return Http::STATUS_CONFLICT; } - } diff --git a/lib/Exceptions/InvalidAccessException.php b/lib/Exceptions/InvalidAccessException.php index c5bfd69a7..399d4af57 100644 --- a/lib/Exceptions/InvalidAccessException.php +++ b/lib/Exceptions/InvalidAccessException.php @@ -36,5 +36,4 @@ public function __construct($message = 'Invalid access value') { public function getStatus() { return Http::STATUS_CONFLICT; } - } diff --git a/lib/Exceptions/InvalidEmailAddress.php b/lib/Exceptions/InvalidEmailAddress.php new file mode 100644 index 000000000..452cfe814 --- /dev/null +++ b/lib/Exceptions/InvalidEmailAddress.php @@ -0,0 +1,39 @@ + + * + * @author René Gieling + * + * @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 . + * + */ + +namespace OCA\Polls\Exceptions; + +use OCP\AppFramework\Http; + +class InvalidEmailAddress extends \Exception { + /** + * InvalidEmailAddress Constructor + * @param string $e exception message + */ + public function __construct($e = 'Invalid email address') { + parent::__construct($e); + } + public function getStatus() { + return Http::STATUS_FORBIDDEN; + } +} diff --git a/lib/Exceptions/InvalidPollTypeException.php b/lib/Exceptions/InvalidPollTypeException.php index bef690f37..26368669f 100644 --- a/lib/Exceptions/InvalidPollTypeException.php +++ b/lib/Exceptions/InvalidPollTypeException.php @@ -36,5 +36,4 @@ public function __construct($message = 'Invalid pollType value') { public function getStatus() { return Http::STATUS_CONFLICT; } - } diff --git a/lib/Exceptions/InvalidUsername.php b/lib/Exceptions/InvalidShareType.php similarity index 88% rename from lib/Exceptions/InvalidUsername.php rename to lib/Exceptions/InvalidShareType.php index b75c8ea0d..380b3deb2 100644 --- a/lib/Exceptions/InvalidUsername.php +++ b/lib/Exceptions/InvalidShareType.php @@ -25,16 +25,15 @@ use OCP\AppFramework\Http; -class InvalidUsername extends \Exception { +class InvalidShareType extends \Exception { /** - * InvalidUsername Constructor + * InvalidShareType Constructor * @param string $e exception message */ - public function __construct($e = 'Invalid username') { + public function __construct($e = 'Invalid share type') { parent::__construct($e); } public function getStatus() { return Http::STATUS_CONFLICT; } - } diff --git a/lib/Exceptions/InvalidShowResultsException.php b/lib/Exceptions/InvalidShowResultsException.php index 67b18a49f..e12fb4f4e 100644 --- a/lib/Exceptions/InvalidShowResultsException.php +++ b/lib/Exceptions/InvalidShowResultsException.php @@ -36,5 +36,4 @@ public function __construct($message = 'Invalid showResults value') { public function getStatus() { return Http::STATUS_CONFLICT; } - } diff --git a/lib/Exceptions/InvalidUsernameException.php b/lib/Exceptions/InvalidUsernameException.php new file mode 100644 index 000000000..11c4bed89 --- /dev/null +++ b/lib/Exceptions/InvalidUsernameException.php @@ -0,0 +1,39 @@ + + * + * @author René Gieling + * + * @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 . + * + */ + +namespace OCA\Polls\Exceptions; + +use OCP\AppFramework\Http; + +class InvalidUsernameException extends \Exception { + /** + * InvalidUsernameException Constructor + * @param string $e exception message + */ + public function __construct($e = 'Username not allowed') { + parent::__construct($e); + } + public function getStatus() { + return Http::STATUS_FORBIDDEN; + } +} diff --git a/lib/Exceptions/NotAuthorizedException.php b/lib/Exceptions/NotAuthorizedException.php index 9486790fa..ece69d8e6 100644 --- a/lib/Exceptions/NotAuthorizedException.php +++ b/lib/Exceptions/NotAuthorizedException.php @@ -34,7 +34,6 @@ public function __construct($e = 'Unauthorized') { parent::__construct($e); } public function getStatus() { - return Http::STATUS_FORBIDDEN; + return Http::STATUS_UNAUTHORIZED; } - } diff --git a/lib/Exceptions/TooShortException.php b/lib/Exceptions/TooShortException.php new file mode 100644 index 000000000..67bdd63a5 --- /dev/null +++ b/lib/Exceptions/TooShortException.php @@ -0,0 +1,39 @@ + + * + * @author René Gieling + * + * @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 . + * + */ + +namespace OCA\Polls\Exceptions; + +use OCP\AppFramework\Http; + +class TooShortException extends \Exception { + /** + * TooShortException Constructor + * @param string $e exception message + */ + public function __construct($e = 'String too short') { + parent::__construct($e); + } + public function getStatus() { + return Http::STATUS_FORBIDDEN; + } +} diff --git a/lib/Migration/Version0009Date20181125051900.php b/lib/Migration/Version0009Date20181125051900.php index a1a667832..4622cf618 100644 --- a/lib/Migration/Version0009Date20181125051900.php +++ b/lib/Migration/Version0009Date20181125051900.php @@ -24,7 +24,6 @@ namespace OCA\Polls\Migration; use OCP\DB\ISchemaWrapper; -use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IConfig; use OCP\IDBConnection; use OCP\Migration\SimpleMigrationStep; @@ -65,14 +64,12 @@ public function changeSchema(IOutput $output, \Closure $schemaClosure, array $op if ($schema->hasTable('polls_particip') && $schema->hasTable('polls_particip_text') && $schema->hasTable('polls_votes')) { - $schema->dropTable('polls_votes'); } if ($schema->hasTable('polls_dts') && $schema->hasTable('polls_txts') && $schema->hasTable('polls_options')) { - $schema->dropTable('polls_options'); } return $schema; diff --git a/lib/Migration/Version0009Date20181125061900.php b/lib/Migration/Version0009Date20181125061900.php index ca701d4a2..63178d408 100644 --- a/lib/Migration/Version0009Date20181125061900.php +++ b/lib/Migration/Version0009Date20181125061900.php @@ -25,7 +25,6 @@ use Doctrine\DBAL\Types\Type; use OCP\DB\ISchemaWrapper; -use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IConfig; use OCP\IDBConnection; use OCP\Migration\SimpleMigrationStep; @@ -113,7 +112,6 @@ public function changeSchema(IOutput $output, \Closure $schemaClosure, array $op ]); $table->setPrimaryKey(['id']); } else { - $table = $schema->getTable('polls_events'); if (!$table->hasColumn('allow_maybe')) { $table->addColumn('allow_maybe', Type::INTEGER, [ @@ -121,7 +119,6 @@ public function changeSchema(IOutput $output, \Closure $schemaClosure, array $op 'default' => 1, ]); } - } if (!$schema->hasTable('polls_options')) { diff --git a/lib/Migration/Version0010Date20191227063812.php b/lib/Migration/Version0010Date20191227063812.php index 4b5bfd762..ff2666bde 100644 --- a/lib/Migration/Version0010Date20191227063812.php +++ b/lib/Migration/Version0010Date20191227063812.php @@ -25,7 +25,6 @@ use Doctrine\DBAL\Types\Type; use OCP\DB\ISchemaWrapper; -use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IConfig; use OCP\IDBConnection; use OCP\Migration\SimpleMigrationStep; @@ -328,7 +327,6 @@ protected function migrateEvents() { ->setParameter('show_results', 'always') ->setParameter('admin_access', 0); $insert->execute(); - } $result->closeCursor(); @@ -354,7 +352,7 @@ protected function copyTokens() { $result = $query->execute(); while ($row = $result->fetch()) { - if ($row['access'] == 'public') { + if ($row['access'] === 'public') { // copy the hash to a public share $insert ->setParameter('token', $row['hash']) @@ -364,7 +362,7 @@ protected function copyTokens() { ->setParameter('user_email', null) ->setParameter('user', ''); $insert->execute(); - } elseif ($row['access'] == 'hidden') { + } elseif ($row['access'] === 'hidden') { // copy the hash to a public share // poll stays hidden for registered users $insert @@ -375,7 +373,7 @@ protected function copyTokens() { ->setParameter('user_email', null) ->setParameter('user', ''); $insert->execute(); - } elseif ($row['access'] == 'registered') { + } elseif ($row['access'] === 'registered') { // copy the hash to a public share // to keep the hash $insert @@ -412,5 +410,4 @@ protected function copyTokens() { } $result->closeCursor(); } - } diff --git a/lib/Migration/Version0010Date20200119101800.php b/lib/Migration/Version0010Date20200119101800.php index 0152c9720..b8d2c68b4 100644 --- a/lib/Migration/Version0010Date20200119101800.php +++ b/lib/Migration/Version0010Date20200119101800.php @@ -24,7 +24,6 @@ namespace OCA\Polls\Migration; use OCP\DB\ISchemaWrapper; -use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IConfig; use OCP\IDBConnection; use OCP\Migration\SimpleMigrationStep; @@ -64,7 +63,6 @@ public function changeSchema(IOutput $output, \Closure $schemaClosure, array $op if ($schema->hasTable('polls_polls') && $schema->hasTable('polls_events')) { - $schema->dropTable('polls_events'); } diff --git a/lib/Migration/Version0101Date20200122194300.php b/lib/Migration/Version0101Date20200122194300.php index 381c55bb1..4f656dd75 100644 --- a/lib/Migration/Version0101Date20200122194300.php +++ b/lib/Migration/Version0101Date20200122194300.php @@ -24,7 +24,6 @@ namespace OCA\Polls\Migration; use OCP\DB\ISchemaWrapper; -use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IConfig; use OCP\IDBConnection; use OCP\Migration\SimpleMigrationStep; diff --git a/lib/Migration/Version0103Date20200130171244.php b/lib/Migration/Version0103Date20200130171244.php index 69f162b78..9b7b0e089 100644 --- a/lib/Migration/Version0103Date20200130171244.php +++ b/lib/Migration/Version0103Date20200130171244.php @@ -25,7 +25,6 @@ use Doctrine\DBAL\Types\Type; use OCP\DB\ISchemaWrapper; -use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IConfig; use OCP\IDBConnection; use OCP\Migration\SimpleMigrationStep; diff --git a/lib/Migration/Version0104Date20200205104800.php b/lib/Migration/Version0104Date20200205104800.php index f869fc4b3..076b42cc3 100644 --- a/lib/Migration/Version0104Date20200205104800.php +++ b/lib/Migration/Version0104Date20200205104800.php @@ -24,7 +24,6 @@ namespace OCA\Polls\Migration; use OCP\DB\ISchemaWrapper; -use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IConfig; use OCP\IDBConnection; use OCP\Migration\SimpleMigrationStep; diff --git a/lib/Migration/Version0104Date20200314074611.php b/lib/Migration/Version0104Date20200314074611.php index cd587e516..564e73bac 100644 --- a/lib/Migration/Version0104Date20200314074611.php +++ b/lib/Migration/Version0104Date20200314074611.php @@ -25,7 +25,6 @@ use Doctrine\DBAL\Types\Type; use OCP\DB\ISchemaWrapper; -use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IConfig; use OCP\IDBConnection; use OCP\Migration\SimpleMigrationStep; diff --git a/lib/Migration/Version0105Date20200508211943.php b/lib/Migration/Version0105Date20200508211943.php index cafee1875..a9ce62aca 100644 --- a/lib/Migration/Version0105Date20200508211943.php +++ b/lib/Migration/Version0105Date20200508211943.php @@ -25,7 +25,6 @@ use Doctrine\DBAL\Types\Type; use OCP\DB\ISchemaWrapper; -use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IConfig; use OCP\IDBConnection; use OCP\Migration\SimpleMigrationStep; diff --git a/lib/Migration/Version0105Date20200523142076.php b/lib/Migration/Version0105Date20200523142076.php new file mode 100644 index 000000000..3fd9d3c9c --- /dev/null +++ b/lib/Migration/Version0105Date20200523142076.php @@ -0,0 +1,88 @@ + + * + * @author René Gieling + * + * @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 . + * + */ + +namespace OCA\Polls\Migration; + +use Doctrine\DBAL\Types\Type; +use OCP\DB\ISchemaWrapper; +use OCP\IConfig; +use OCP\IDBConnection; +use OCP\Migration\SimpleMigrationStep; +use OCP\Migration\IOutput; + +/** + * Installation class for the polls app. + * Initial db creation + */ +class Version0105Date20200523142076 extends SimpleMigrationStep { + + /** @var IDBConnection */ + protected $connection; + + /** @var IConfig */ + protected $config; + + /** + * @param IDBConnection $connection + * @param IConfig $config + */ + public function __construct(IDBConnection $connection, IConfig $config) { + $this->connection = $connection; + $this->config = $config; + } + + /** + * @param IOutput $output + * @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` + * @param array $options + * @return null|ISchemaWrapper + * @since 13.0.0 + */ + public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) { + /** @var ISchemaWrapper $schema */ + $schema = $schemaClosure(); + if (!$schema->hasTable('polls_preferences')) { + $table = $schema->createTable('polls_preferences'); + + $table->addColumn('id', Type::INTEGER, [ + 'autoincrement' => true, + 'notnull' => true, + ]); + $table->addColumn('user_id', Type::STRING, [ + 'notnull' => true, + 'length' => 64, + ]); + $table->addColumn('timestamp', Type::INTEGER, [ + 'length' => 11, + 'notnull' => true, + 'default' => 0 + ]); + $table->addColumn('preferences', Type::TEXT, [ + 'notnull' => true, + 'default' => '', + ]); + $table->setPrimaryKey(['id']); + } + return $schema; + } +} diff --git a/lib/Migration/Version0105Date20200704084037.php b/lib/Migration/Version0105Date20200704084037.php index 934d2a59b..76736c369 100644 --- a/lib/Migration/Version0105Date20200704084037.php +++ b/lib/Migration/Version0105Date20200704084037.php @@ -25,7 +25,6 @@ use Doctrine\DBAL\Types\Type; use OCP\DB\ISchemaWrapper; -use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IConfig; use OCP\IDBConnection; use OCP\Migration\SimpleMigrationStep; diff --git a/lib/Migration/Version0105Date20200903172733.php b/lib/Migration/Version0105Date20200903172733.php new file mode 100644 index 000000000..5573f139c --- /dev/null +++ b/lib/Migration/Version0105Date20200903172733.php @@ -0,0 +1,77 @@ + + * + * @author René Gieling + * + * @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 . + * + */ + +namespace OCA\Polls\Migration; + +use Doctrine\DBAL\Types\Type; +use OCP\DB\ISchemaWrapper; +use OCP\IConfig; +use OCP\IDBConnection; +use OCP\Migration\SimpleMigrationStep; +use OCP\Migration\IOutput; + +/** + * Installation class for the polls app. + * Initial db creation + */ +class Version0105Date20200903172733 extends SimpleMigrationStep { + + /** @var IDBConnection */ + protected $connection; + + /** @var IConfig */ + protected $config; + + /** + * @param IDBConnection $connection + * @param IConfig $config + */ + public function __construct(IDBConnection $connection, IConfig $config) { + $this->connection = $connection; + $this->config = $config; + } + + /** + * @param IOutput $output + * @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` + * @param array $options + * @return null|ISchemaWrapper + * @since 13.0.0 + */ + public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) { + /** @var ISchemaWrapper $schema */ + $schema = $schemaClosure(); + if ($schema->hasTable('polls_polls')) { + $table = $schema->getTable('polls_polls'); + if (!$table->hasColumn('important')) { + $table->addColumn('important', Type::INTEGER, [ + 'length' => 11, + 'notnull' => true, + 'default' => 0 + ]); + } + } + + return $schema; + } +} diff --git a/lib/Model/Acl.php b/lib/Model/Acl.php index ebc998f25..e2cb43eef 100644 --- a/lib/Model/Acl.php +++ b/lib/Model/Acl.php @@ -3,7 +3,7 @@ * @copyright Copyright (c) 2017 Vinzenz Rosenkranz * * @author René Gieling -* + * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify @@ -25,12 +25,10 @@ namespace OCA\Polls\Model; use JsonSerializable; -use Exception; -use OCP\AppFramework\Db\DoesNotExistException; +use OCA\Polls\Exceptions\NotAuthorizedException; use OCP\IUserManager; use OCP\IGroupManager; -use OCP\ILogger; use OCP\IUser; use OCA\Polls\Db\Poll; use OCA\Polls\Db\Share; @@ -48,18 +46,9 @@ class Acl implements JsonSerializable { /** @var int */ private $pollId = 0; - /** @var ILogger */ - private $logger; - - /** @var array */ - private $shares = []; - /** @var string */ private $token = ''; - /** @var bool */ - private $foundByToken = false; - /** @var string */ private $userId; @@ -81,99 +70,107 @@ class Acl implements JsonSerializable { /** @var Poll */ private $poll; + /** @var Share */ + private $share; /** * Acl constructor. * @param string $appName * @param string $userId - * @param ILogger $logger * @param IUserManager $userManager * @param IGroupManager $groupManager * @param PollMapper $pollMapper * @param VoteMapper $voteMapper * @param ShareMapper $shareMapper - * @param Poll $pollMapper + * @param Poll $poll + * @param Share $share * */ public function __construct( $userId, - ILogger $logger, IUserManager $userManager, IGroupManager $groupManager, PollMapper $pollMapper, VoteMapper $voteMapper, ShareMapper $shareMapper, - Poll $poll + Poll $poll, + Share $share ) { $this->userId = $userId; - $this->logger = $logger; $this->userManager = $userManager; $this->groupManager = $groupManager; $this->pollMapper = $pollMapper; $this->voteMapper = $voteMapper; $this->shareMapper = $shareMapper; $this->poll = $poll; + $this->share = $share; } - /** * @NoAdminRequired - * @return string + * @return bool */ - public function getUserId() { - return $this->userId; - } + public function set($pollId = 0, $token = ''): Acl { + if ($token) { + \OC::$server->getLogger()->debug('Share token: ' . $token); - /** - * @NoAdminRequired - * @return string - */ - public function getDisplayName() { - if ($this->userManager->get($this->userId) instanceof IUser) { - return $this->userManager->get($this->userId)->getDisplayName(); - } else { - return $this->userId; - } - } + $this->token = $token; + $this->pollId = 0; + $this->userId = null; + $this->share = $this->shareMapper->findByToken($token); + if (\OC::$server->getUserSession()->isLoggedIn()) { + if ($this->share->getType() !== 'group' && $this->share->getType() !== 'public') { + throw new NotAuthorizedException; + } - /** - * @NoAdminRequired - * @return boolean - */ - public function setPollIdOrToken($pollId = 0, $token = '') { + $this->userId = \OC::$server->getUserSession()->getUser()->getUID(); + } else { + if ($this->share->getType() === 'group' || $this->share->getType() === 'user') { + throw new NotAuthorizedException; + } - if ($token) { - $this->setToken($token); + $this->userId = $this->share->getUserId(); + } + + $this->pollId = $this->share->getPollId(); } elseif ($pollId) { - $this->setPollId($pollId); + $this->userId = \OC::$server->getUserSession()->getUser()->getUID(); + $this->pollId = $pollId; + $this->share = null; } + $this->poll = $this->pollMapper->find($this->pollId); + return $this; } /** * @NoAdminRequired - * @return boolean + * @return string */ - public function checkAuthorize($pollId = 0, $token = '') { + public function getUserId() { + return $this->userId; + } - if ($token) { - $this->setToken($token); - } elseif ($pollId) { - $this->setPollId($pollId); + /** + * @NoAdminRequired + * @return string + */ + public function getDisplayName() { + if ($this->userManager->get($this->userId) instanceof IUser) { + return $this->userManager->get($this->userId)->getDisplayName(); + } else { + return $this->userId; } - - return ($this->userId && $this->poll->getId()); } /** * @NoAdminRequired * @return string */ - public function setUserId($userId): Acl { - $this->userId = $userId; - return $this; + public function getIsExternalUser() { + return !($this->userManager->get($this->userId) instanceof IUser); } /** @@ -192,18 +189,6 @@ public function getPollId(): int { return $this->pollId; } - /** - * @NoAdminRequired - * @return int - */ - public function setPollId(int $pollId): Acl { - $this->pollId = $pollId; - $this->poll = $this->pollMapper->find($this->pollId); - $this->shares = $this->shareMapper->findByPoll($this->pollId); - - return $this; - } - /** * @NoAdminRequired * @return bool @@ -252,7 +237,7 @@ public function getAllowView(): bool { */ public function getGroupShare(): bool { return count( - array_filter($this->shareMapper->findByPoll($this->getPollId()), function($item) { + array_filter($this->shareMapper->findByPoll($this->getPollId()), function ($item) { if ($item->getType() === 'group' && $this->groupManager->isInGroup($this->getUserId(), $item->getUserId())) { return true; } @@ -275,9 +260,8 @@ public function getUserHasVoted(): bool { * @return bool */ public function getPersonalShare(): bool { - return count( - array_filter($this->shareMapper->findByPoll($this->getPollId()), function($item) { + array_filter($this->shareMapper->findByPoll($this->getPollId()), function ($item) { if (($item->getType() === 'user' || $item->getType() === 'external' || $item->getType() === 'email' || $item->getType() === 'contact') && $item->getUserId() === $this->getUserId()) { return true; } @@ -290,9 +274,8 @@ public function getPersonalShare(): bool { * @return bool */ public function getPublicShare(): bool { - return count( - array_filter($this->shareMapper->findByPoll($this->getPollId()), function($item) { + array_filter($this->shareMapper->findByPoll($this->getPollId()), function ($item) { if ($item->getType() === 'public' && $item->getToken() === $this->getToken()) { return true; } @@ -316,17 +299,20 @@ public function getExpired(): bool { * @return bool */ public function getAllowVote(): bool { - if ( - ($this->getAllowView() || $this->getFoundByToken()) + return ($this->getAllowView() || $this->getToken()) && !$this->getExpired() && !$this->poll->getDeleted() - && $this->userId + && $this->userId; + } - ) { - return true; - } else { - return false; - } + /** + * @NoAdminRequired + * @return bool + */ + public function getAllowSubscribe(): bool { + return ($this->hasEmail()) + && !$this->poll->getDeleted() + && $this->getAllowView(); } /** @@ -350,15 +336,9 @@ public function getAllowEdit(): bool { * @return bool */ public function getAllowSeeResults(): bool { - if ($this->poll->getShowResults() === 'always' || $this->getIsOwner()) { - return true; - } elseif ($this->poll->getShowResults() === 'never') { - return false; - } elseif ($this->poll->getShowResults() === 'expired') { - return $this->getExpired(); - } else { - return false; - } + return $this->poll->getShowResults() === 'always' + || ($this->poll->getShowResults() === 'expired' && $this->getExpired()) + || $this->getIsOwner(); } /** @@ -366,28 +346,7 @@ public function getAllowSeeResults(): bool { * @return bool */ public function getAllowSeeUsernames(): bool { - return !($this->poll->getAnonymous() && !$this->getIsOwner()); ; - } - - /** - * @NoAdminRequired - * @return bool - */ - public function getAllowSeeAllVotes(): bool { - // TODO: preparation for polls without displaying other votes - if ($this->pollId) { - return true; - } else { - return false; - } - } - - /** - * @NoAdminRequired - * @return bool - */ - public function getFoundByToken(): bool { - return $this->foundByToken; + return !$this->poll->getAnonymous() || $this->getIsOwner(); } /** @@ -398,41 +357,12 @@ public function getToken(): string { return $this->token; } - /** - * @NoAdminRequired - * @return string - */ - public function setToken(string $token): Acl { - $this->logger->debug('Share PollId' . $token); - try { - - $this->token = $token; - $share = $this->shareMapper->findByToken($token); - $this->foundByToken = true; - $this->setPollId($share->getPollId()); - $this->logger->debug('Share PollId' . $share->getPollId()); - - if (($share->getType() === 'group' || $share->getType() === 'user') && !\OC::$server->getUserSession()->isLoggedIn()) { - // User must be logged in for shareType user and group - $this->setPollId(0); - $this->setUserId(null); - $this->token = ''; - $this->foundByToken = false; - } else if (($share->getType() === 'group' || $share->getType() === 'public') && \OC::$server->getUserSession()->isLoggedIn()) { - // Use user name of authorized user shareType public and group if user is logged in - $this->setUserId($this->userId); - } else { - $this->setUserId($share->getUserId()); - } - - - } catch (DoesNotExistException $e) { - $this->setPollId(0); - $this->setUserId(null); - $this->token = ''; - $this->foundByToken = false; + private function hasEmail():bool { + if ($this->share) { + return strlen($this->share->getUserEmail()) > 0; + } else { + return \OC::$server->getUserSession()->isLoggedIn(); } - return $this; } /** @@ -443,6 +373,7 @@ public function jsonSerialize(): array { 'userId' => $this->getUserId(), 'displayName' => $this->getDisplayName(), 'loggedIn' => $this->getLoggedIn(), + 'externalUser' => $this->getIsExternalUser(), 'pollId' => $this->getPollId(), 'token' => $this->getToken(), 'isOwner' => $this->getIsOwner(), @@ -453,12 +384,11 @@ public function jsonSerialize(): array { 'allowEdit' => $this->getAllowEdit(), 'allowSeeResults' => $this->getAllowSeeResults(), 'allowSeeUsernames' => $this->getAllowSeeUsernames(), - 'allowSeeAllVotes' => $this->getAllowSeeAllVotes(), + 'allowSubscribe' => $this->getAllowSubscribe(), 'userHasVoted' => $this->getUserHasVoted(), 'groupShare' => $this->getGroupShare(), 'personalShare' => $this->getPersonalShare(), - 'publicShare' => $this->getPublicShare(), - 'foundByToken' => $this->getFoundByToken() + 'publicShare' => $this->getPublicShare() ]; } } diff --git a/lib/Service/AnonymizeService.php b/lib/Service/AnonymizeService.php index 80f11fa11..fcc5988b1 100644 --- a/lib/Service/AnonymizeService.php +++ b/lib/Service/AnonymizeService.php @@ -3,7 +3,7 @@ * @copyright Copyright (c) 2017 Vinzenz Rosenkranz * * @author René Gieling -* + * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify @@ -31,10 +31,19 @@ class AnonymizeService { + /** @var VoteMapper */ private $voteMapper; + + /** @var CommentMapper */ private $commentMapper; - private $anonList = array(); + + /** @var array */ + private $anonList = []; + + /** @var string */ private $userId; + + /** @var int */ private $pollId; public function __construct( @@ -74,7 +83,7 @@ private function anonymize($array) { * Initialize anonymizer with pollId and userId * Creates a mapping list with unique Anonymous strings based on the partcipants of a poll * @NoAdminRequired - * @param integer $pollId + * @param int $pollId * @param string $userId - usernames, which will not be anonymized */ @@ -117,6 +126,4 @@ public function getComments() { public function getVotes() { return $this->anonymize($this->voteMapper->findByPoll($this->pollId)); } - - } diff --git a/lib/Service/CalendarService.php b/lib/Service/CalendarService.php new file mode 100644 index 000000000..cb9bd29e5 --- /dev/null +++ b/lib/Service/CalendarService.php @@ -0,0 +1,83 @@ + + * + * @author René Gieling + * + * @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 . + * + */ + + +namespace OCA\Polls\Service; + +use DateTime; +use OCP\Calendar\IManager as CalendarManager; + +class CalendarService { + private $calendarManager; + private $calendars; + + public function __construct( + CalendarManager $calendarManager + ) { + $this->calendarManager = $calendarManager; + $this->calendars = $this->calendarManager->getCalendars(); + } + + /** + * getEvents - get events from the user's calendars inside given timespan + * @NoAdminRequired + * @param DateTime $from + * @param DateTime $to + * @return Array + */ + public function getEvents($from, $to) { + $events = []; + + foreach ($this->calendars as $calendar) { + $foundEvents = $calendar->search('', ['SUMMARY'], ['timerange' => ['start' => $from, 'end' => $to]]); + foreach ($foundEvents as $event) { + array_push($events, [ + 'relatedFrom' => $from->getTimestamp(), + 'relatedTo' => $to->getTimestamp(), + 'name' => $calendar->getDisplayName(), + 'key' => $calendar->getKey(), + 'displayColor' => $calendar->getDisplayColor(), + 'permissions' => $calendar->getPermissions(), + 'eventId' => $event['id'], + 'UID' => $event['objects'][0]['UID'][0], + 'summary' => isset($event['objects'][0]['SUMMARY'][0]) ? $event['objects'][0]['SUMMARY'][0] : '', + 'description' => isset($event['objects'][0]['DESCRIPTION'][0]) ? $event['objects'][0]['DESCRIPTION'][0] : '', + 'location' => isset($event['objects'][0]['LOCATION'][0]) ? $event['objects'][0]['LOCATION'][0] : '', + 'eventFrom' => isset($event['objects'][0]['DTSTART'][0]) ? $event['objects'][0]['DTSTART'][0]->getTimestamp() : 0, + 'eventTo' => isset($event['objects'][0]['DTEND'][0]) ? $event['objects'][0]['DTEND'][0]->getTimestamp() : 0, + 'calDav' => $event + ]); + } + } + return $events; + } + + /** + * Get user's calendars + * @NoAdminRequired + * @return Array + */ + public function getCalendars() { + return $this->calendars; + } +} diff --git a/lib/Service/CommentService.php b/lib/Service/CommentService.php index a1cd71318..eeff8a066 100644 --- a/lib/Service/CommentService.php +++ b/lib/Service/CommentService.php @@ -23,28 +23,29 @@ namespace OCA\Polls\Service; -use \Exception; -use OCP\ILogger; - +use Exception; use OCA\Polls\Exceptions\NotAuthorizedException; + use OCA\Polls\Db\Comment; use OCA\Polls\Db\CommentMapper; use OCA\Polls\Model\Acl; -use OCA\Polls\Service\AnonymizeService; - - class CommentService { - private $comment; + /** @var CommentMapper */ private $commentMapper; - private $logger; + + /** @var Comment */ + private $comment; + + /** @var AnonymizeService */ private $anonymizer; + + /** @var Acl */ private $acl; /** * CommentService constructor. - * @param ILogger $logger * @param CommentMapper $commentMapper * @param Comment $comment * @param AnonymizeService $anonymizer @@ -52,7 +53,6 @@ class CommentService { */ public function __construct( - ILogger $logger, CommentMapper $commentMapper, Comment $comment, AnonymizeService $anonymizer, @@ -60,22 +60,21 @@ public function __construct( ) { $this->commentMapper = $commentMapper; $this->comment = $comment; - $this->logger = $logger; $this->anonymizer = $anonymizer; $this->acl = $acl; } /** - * get + * Get comments * Read all comments of a poll based on the poll id and return list as array * @NoAdminRequired - * @param integer $pollId + * @param int $pollId * @param string $token - * @return Array + * @return array + * @throws NotAuthorizedException */ public function list($pollId = 0, $token = '') { - - if (!$this->acl->setPollIdOrToken($pollId, $token)->getAllowView()) { + if (!$this->acl->set($pollId, $token)->getAllowView()) { throw new NotAuthorizedException; } @@ -88,16 +87,16 @@ public function list($pollId = 0, $token = '') { } /** - * Write a new comment to the db and returns the new comment as array + * Add comment * @NoAdminRequired - * @param string $message * @param int $pollId + * @param string $message * @param string $token * @return Comment + * @throws NotAuthorizedException */ public function add($pollId = 0, $message, $token = '') { - - if (!$this->acl->setPollIdOrToken($pollId, $token)->getAllowComment()) { + if (!$this->acl->set($pollId, $token)->getAllowComment()) { throw new NotAuthorizedException; } @@ -113,32 +112,28 @@ public function add($pollId = 0, $message, $token = '') { } else { throw new NotAuthorizedException; } - - } catch (\Exception $e) { - $this->logger->alert('Error writing comment for pollId ' . $pollId . ': '. $e); + } catch (Exception $e) { + \OC::$server->getLogger()->alert('Error writing comment for pollId ' . $pollId . ': ' . $e); throw new NotAuthorizedException($e); } - } /** - * delete - * Delete Comment + * Delete comment * @NoAdminRequired * @param int $commentId * @param string $token * @return Comment + * @throws NotAuthorizedException */ public function delete($commentId, $token = '') { $this->comment = $this->commentMapper->find($commentId); - if ($this->acl->setPollIdOrToken($this->comment->getPollId(), $token)->getUserId() !== $this->acl->getUserId()) { + if ($this->acl->set($this->comment->getPollId(), $token)->getUserId() !== $this->acl->getUserId()) { throw new NotAuthorizedException; } $this->commentMapper->delete($this->comment); return $this->comment; - } - } diff --git a/lib/Service/LogService.php b/lib/Service/LogService.php index d6936c04f..9c0db75c8 100644 --- a/lib/Service/LogService.php +++ b/lib/Service/LogService.php @@ -23,7 +23,6 @@ namespace OCA\Polls\Service; -use Exception; use OCP\AppFramework\Db\DoesNotExistException; use OCA\Polls\Db\Log; @@ -31,36 +30,38 @@ class LogService { - private $mapper; - private $logItem; + /** @var LogMapper */ + private $logMapper; + + /** @var Log */ + private $log; /** * LogService constructor. - * @param LogMapper $mapper - * @param Log $logItem + * @param LogMapper $logMapper + * @param Log $log */ - public function __construct( - LogMapper $mapper, - Log $logItem + LogMapper $logMapper, + Log $log ) { - $this->mapper = $mapper; - $this->logItem = $logItem; + $this->logMapper = $logMapper; + $this->log = $log; } /** * Prevent repetition of the same log event * @NoAdminRequired - * @return Bool + * @return bool */ public function isRepetition() { try { - $lastRecord = $this->mapper->getLastRecord($this->logItem->getPollId()); - return (intval($lastRecord->getPollId()) === intval($this->logItem->getPollId()) - && $lastRecord->getUserId() === $this->logItem->getUserId() - && $lastRecord->getMessageId() === $this->logItem->getMessageId() - && $lastRecord->getMessage() === $this->logItem->getMessage() + $lastRecord = $this->logMapper->getLastRecord($this->log->getPollId()); + return (intval($lastRecord->getPollId()) === intval($this->log->getPollId()) + && $lastRecord->getUserId() === $this->log->getUserId() + && $lastRecord->getMessageId() === $this->log->getMessageId() + && $lastRecord->getMessage() === $this->log->getMessage() ); } catch (DoesNotExistException $e) { return false; @@ -77,24 +78,23 @@ public function isRepetition() { * @return Log */ public function setLog($pollId, $messageId, $userId = null, $message = null) { - $this->logItem = new Log(); - $this->logItem->setPollId($pollId); - $this->logItem->setCreated(time()); - $this->logItem->setMessageId($messageId); - $this->logItem->setMessage($message); + $this->log = new Log(); + $this->log->setPollId($pollId); + $this->log->setCreated(time()); + $this->log->setMessageId($messageId); + $this->log->setMessage($message); if ($userId) { - $this->logItem->setUserId($userId); + $this->log->setUserId($userId); } else { - $this->logItem->setUserId(\OC::$server->getUserSession()->getUser()->getUID()); + $this->log->setUserId(\OC::$server->getUserSession()->getUser()->getUID()); } if ($this->isRepetition()) { return null; } else { - return $this->mapper->insert($this->logItem); + return $this->logMapper->insert($this->log); } } - } diff --git a/lib/Service/MailService.php b/lib/Service/MailService.php index fbd409842..43077414b 100644 --- a/lib/Service/MailService.php +++ b/lib/Service/MailService.php @@ -34,30 +34,46 @@ use OCP\L10N\IFactory; use OCP\Mail\IMailer; use OCP\Mail\IEMailTemplate; -use OCP\ILogger; use OCA\Polls\Db\SubscriptionMapper; -use OCA\Polls\Db\Subscription; use OCA\Polls\Db\PollMapper; -use OCA\Polls\Db\Poll; use OCA\Polls\Db\ShareMapper; use OCA\Polls\Db\Share; use OCA\Polls\Db\LogMapper; class MailService { + /** @var IUserManager */ private $userManager; + + /** @var IGroupManager */ private $groupManager; + + /** @var IConfig */ private $config; + + /** @var IURLGenerator */ private $urlGenerator; + + /** @var IL10N */ private $trans; + + /** @var IFactory */ private $transFactory; + + /** @var IMailer */ private $mailer; - private $logger; - private $shareMapper; + /** @var SubscriptionMapper */ private $subscriptionMapper; + + /** @var ShareMapper */ + private $shareMapper; + + /** @var PollMapper */ private $pollMapper; + + /** @var LogMapper */ private $logMapper; /** @@ -69,7 +85,6 @@ class MailService { * @param IL10N $trans * @param IFactory $transFactory * @param IMailer $mailer - * @param ILogger $logger * @param SubscriptionMapper $subscriptionMapper * @param ShareMapper $shareMapper * @param PollMapper $pollMapper @@ -84,7 +99,6 @@ public function __construct( IL10N $trans, IFactory $transFactory, IMailer $mailer, - ILogger $logger, ShareMapper $shareMapper, SubscriptionMapper $subscriptionMapper, PollMapper $pollMapper, @@ -97,7 +111,6 @@ public function __construct( $this->trans = $trans; $this->transFactory = $transFactory; $this->mailer = $mailer; - $this->logger = $logger; $this->shareMapper = $shareMapper; $this->subscriptionMapper = $subscriptionMapper; $this->pollMapper = $pollMapper; @@ -107,40 +120,61 @@ public function __construct( /** * sendMail - Send eMail and evaluate recipient's mail address - * and displayname if $toUserId is a site user + * and displayname if $userId is a site user * @param IEmailTemplate $emailTemplate - * @param String $toUserId - * @param String $toEmail - * @param String $toDisplayName + * @param String $userId + * @param String $emailAddress, ignored, when $userId is set + * @param String $displayName, ignored, when $userId is set * @return String */ - private function sendMail($emailTemplate, $toUserId = '', $toEmail = '', $toDisplayName = '') { - - if ($this->userManager->get($toUserId) instanceof IUser && !$toEmail) { - $toEmail = \OC::$server->getConfig()->getUserValue($toUserId, 'settings', 'email'); - $toDisplayName = $this->userManager->get($toUserId)->getDisplayName(); + private function sendMail($emailTemplate, $userId = '', $emailAddress = '', $displayName = '') { + if ($this->userManager->get($userId) instanceof IUser) { + $emailAddress = \OC::$server->getConfig()->getUserValue($userId, 'settings', 'email'); + $displayName = $this->userManager->get($userId)->getDisplayName(); } - if (!$toEmail || !filter_var($toEmail, FILTER_VALIDATE_EMAIL)) { - throw new Exception('Invalid email address (' . $toEmail . ')'); + if (!$emailAddress || !filter_var($emailAddress, FILTER_VALIDATE_EMAIL)) { + throw new Exception('Invalid email address (' . $emailAddress . ')'); } try { $message = $this->mailer->createMessage(); - $message->setTo([$toEmail => $toDisplayName]); + $message->setTo([$emailAddress => $displayName]); $message->useTemplate($emailTemplate); $this->mailer->send($message); return null; - } catch (\Exception $e) { - $this->logger->logException($e, ['app' => 'polls']); + \OC::$server->getLogger()->logException($e, ['app' => 'polls']); throw $e; } + } + + + /** + * @param integer $pollId + * @param string $userId + * @return string + */ + public function resolveEmailAddress($pollId, $userId) { + if ($this->userManager->get($userId) instanceof IUser) { + return \OC::$server->getConfig()->getUserValue($userId, 'settings', 'email'); + } + // if $userId is no site user, eval via shares + try { + $share = $this->shareMapper->findByPollAndUser($pollId, $userId); + if ($share->getUserEmail()) { + return $share->getUserEmail(); + } + } catch (\Exception $e) { + // catch silently + } + return $userId; } + /** * @param Share $share * @param String $defaultLang @@ -152,8 +186,7 @@ private function getRecipientsByShare($share, $defaultLang = 'en', $skipUser = n $contactsManager = \OC::$server->getContactsManager(); if ($share->getType() === 'user') { - - $recipients[] = array( + $recipients[] = [ 'userId' => $share->getUserId(), 'eMailAddress' => \OC::$server->getConfig()->getUserValue($share->getUserId(), 'settings', 'email'), 'displayName' => $this->userManager->get($share->getUserId())->getDisplayName(), @@ -164,13 +197,12 @@ private function getRecipientsByShare($share, $defaultLang = 'en', $skipUser = n 'link' => $this->urlGenerator->getAbsoluteURL( $this->urlGenerator->linkToRoute( 'polls.page.indexvote', - array('id' => $share->getPollId()) + ['id' => $share->getPollId()] ) ) - ); - + ]; } elseif ($share->getType() === 'email') { - $recipients[] = array( + $recipients[] = [ 'userId' => $share->getUserEmail(), 'eMailAddress' => $share->getUserEmail(), 'displayName' => $share->getUserEmail(), @@ -178,34 +210,32 @@ private function getRecipientsByShare($share, $defaultLang = 'en', $skipUser = n 'link' => $this->urlGenerator->getAbsoluteURL( $this->urlGenerator->linkToRoute( 'polls.page.vote_publicpublic', - array('token' => $share->getToken()) + ['token' => $share->getToken()] ) ) - ); - + ]; } elseif ($share->getType() === 'contact') { - $contacts = $contactsManager->search($share->getUserId(), array('FN')); + $contacts = $contactsManager->search($share->getUserId(), ['FN']); if (is_array($contacts)) { $contact = $contacts[0]; - $recipients[] = array( + $recipients[] = [ 'userId' => $share->getUserId(), - 'eMailAddress' => $contact['EMAIL'][0], - 'displayName' => $contact['FN'], + 'eMailAddress' => $share->getUserEmail(), + 'displayName' => $share->getUserId(), 'language' => $defaultLang, 'link' => $this->urlGenerator->getAbsoluteURL( $this->urlGenerator->linkToRoute( 'polls.page.vote_publicpublic', - array('token' => $share->getToken()) + ['token' => $share->getToken()] ) ) - ); + ]; } else { return; } - - } elseif ($share->getType() === 'external' || $share->getType() === 'email') { - $recipients[] = array( + } elseif ($share->getType() === 'external') { + $recipients[] = [ 'userId' => $share->getUserId(), 'eMailAddress' => $share->getUserEmail(), 'displayName' => $share->getUserId(), @@ -213,21 +243,19 @@ private function getRecipientsByShare($share, $defaultLang = 'en', $skipUser = n 'link' => $this->urlGenerator->getAbsoluteURL( $this->urlGenerator->linkToRoute( 'polls.page.vote_publicpublic', - array('token' => $share->getToken()) + ['token' => $share->getToken()] ) ) - ); - + ]; } elseif ($share->getType() === 'group') { - $groupMembers = array_keys($this->groupManager->displayNamesInGroup($share->getUserId())); foreach ($groupMembers as $member) { - if ($skipUser === $member || !$this->userManager->get($member)->isEnabled() ) { + if ($skipUser === $member || !$this->userManager->get($member)->isEnabled()) { continue; } - $recipients[] = array( + $recipients[] = [ 'userId' => $member, 'eMailAddress' => \OC::$server->getConfig()->getUserValue($member, 'settings', 'email'), 'displayName' => $this->userManager->get($member)->getDisplayName(), @@ -237,8 +265,7 @@ private function getRecipientsByShare($share, $defaultLang = 'en', $skipUser = n 'polls.page.indexvote', ['id' => $share->getPollId()] ) ) - ); - + ]; } } return $recipients; @@ -248,7 +275,6 @@ private function getRecipientsByShare($share, $defaultLang = 'en', $skipUser = n * @param string $token */ public function sendInvitationMail($token) { - $share = $this->shareMapper->findByToken($token); $poll = $this->pollMapper->find($share->getPollId()); $owner = $this->userManager->get($poll->getOwner()); @@ -305,7 +331,7 @@ public function sendInvitationMail($token) { $sentMails[] = $recipient; } catch (Exception $e) { $abortedMails[] = $recipient; - $this->logger->alert('Error sending Mail to ' . json_encode($recipient)); + \OC::$server->getLogger()->alert('Error sending Mail to ' . json_encode($recipient)); } } return ['sentMails' => $sentMails, 'abortedMails' => $abortedMails]; @@ -322,20 +348,28 @@ public function sendNotifications() { $log = $this->logMapper->findUnprocessed(); foreach ($subscriptions as $subscription) { + $poll = $this->pollMapper->find($subscription->getPollId()); + $emailAddress = ''; + $displayName = ''; if ($this->userManager->get($subscription->getUserId()) instanceof IUser) { $lang = $this->config->getUserValue($subscription->getUserId(), 'core', 'lang'); } else { - continue; + try { + $emailAddress = $this->shareMapper->findByPollAndUser($subscription->getPollId(), $subscription->getUserId())->getUserEmail(); + $displayName = $subscription->getUserId(); + $lang = $this->config->getUserValue($poll->getOwner(), 'core', 'lang'); + } catch (\Exception $e) { + continue; + } } - $poll = $this->pollMapper->find($subscription->getPollId()); $trans = $this->transFactory->get('polls', $lang); $url = $this->urlGenerator->getAbsoluteURL( $this->urlGenerator->linkToRoute( 'polls.page.indexvote', - array('id' => $subscription->getPollId()) + ['id' => $subscription->getPollId()] ) ); @@ -364,49 +398,41 @@ public function sendNotifications() { if ($logItem->getMessage()) { $emailTemplate->addBodyText($logItem->getMessage()); - } elseif ($logItem->getMessageId() === 'setVote') { $emailTemplate->addBodyText($trans->t( '- %s voted.', - array($displayUser) + [$displayUser] )); - } elseif ($logItem->getMessageId() === 'updatePoll') { $emailTemplate->addBodyText($trans->t( '- %s updated the poll configuration. Please check your votes.', - array($displayUser) + [$displayUser] )); - } elseif ($logItem->getMessageId() === 'deletePoll') { $emailTemplate->addBodyText($trans->t( '- %s deleted the poll.', - array($displayUser) + [$displayUser] )); - } elseif ($logItem->getMessageId() === 'restorePoll') { $emailTemplate->addBodyText($trans->t( '- %s restored the poll.', - array($displayUser) + [$displayUser] )); - } elseif ($logItem->getMessageId() === 'expirePoll') { $emailTemplate->addBodyText($trans->t( '- The poll expired.', - array($displayUser) + [$displayUser] )); - } elseif ($logItem->getMessageId() === 'addOption') { $emailTemplate->addBodyText($trans->t( '- %s added a vote option.', - array($displayUser) + [$displayUser] )); - } elseif ($logItem->getMessageId() === 'deleteOption') { $emailTemplate->addBodyText($trans->t( '- %s removed a vote option.', - array($displayUser) + [$displayUser] )); - } else { $emailTemplate->addBodyText( $logItem->getMessageId() . " (" . $displayUser . ")" @@ -426,9 +452,9 @@ public function sendNotifications() { $emailTemplate->addFooter($trans->t('This email is sent to you, because you subscribed to notifications of this poll. To opt out, visit the poll and remove your subscription.')); try { - $this->sendMail($emailTemplate, $subscription->getUserId()); + $this->sendMail($emailTemplate, $subscription->getUserId(), $emailAddress, $displayName); } catch (Exception $e) { - $this->logger->alert('Error sending Mail to ' . $subscription->getUserId()); + \OC::$server->getLogger()->alert('Error sending Mail to ' . $subscription->getUserId()); } } } diff --git a/lib/Service/OptionService.php b/lib/Service/OptionService.php index 0afe4b8c9..298fb8639 100644 --- a/lib/Service/OptionService.php +++ b/lib/Service/OptionService.php @@ -23,26 +23,45 @@ namespace OCA\Polls\Service; -use Exception; +use DateTime; use OCP\AppFramework\Db\DoesNotExistException; use OCA\Polls\Exceptions\NotAuthorizedException; +use OCA\Polls\Exceptions\BadRequestException; +use OCA\Polls\Exceptions\DuplicateEntryException; +use Doctrine\DBAL\Exception\UniqueConstraintViolationException; -use OCA\Polls\Db\Option; use OCA\Polls\Db\OptionMapper; -use OCA\Polls\Service\LogService; +use OCA\Polls\Db\Option; +use OCA\Polls\Db\PollMapper; +use OCA\Polls\Db\Poll; use OCA\Polls\Model\Acl; -class OptionService { +class OptionService { + /** @var OptionMapper */ private $optionMapper; + + /** @var Option */ private $option; + + /** @var PollMapper */ + private $pollMapper; + + /** @var Poll */ + private $poll; + + /** @var LogService */ private $logService; + + /** @var Acl */ private $acl; /** - * OptionController constructor. + * OptionService constructor. * @param OptionMapper $optionMapper * @param Option $option + * @param PollMapper $pollMapper + * @param Poll $poll * @param LogService $logService * @param Acl $acl */ @@ -50,135 +69,234 @@ class OptionService { public function __construct( OptionMapper $optionMapper, Option $option, + PollMapper $pollMapper, + Poll $poll, LogService $logService, Acl $acl ) { $this->optionMapper = $optionMapper; $this->option = $option; + $this->pollMapper = $pollMapper; + $this->poll = $poll; $this->logService = $logService; $this->acl = $acl; } /** - * Set properties from option array + * Get all options of given poll * @NoAdminRequired - * @param Array $option + * @param int $pollId + * @param string $token + * @return array Array of Option objects + * @throws NotAuthorizedException */ - private function set($option) { - - $this->option->setPollId($option['pollId']); - $this->option->setPollOptionText(trim(htmlspecialchars($option['pollOptionText']))); - $this->option->setTimestamp($option['timestamp']); + public function list($pollId = 0, $token = '') { + $acl = $this->acl->set($pollId, $token); - if ($option['timestamp']) { - $this->option->setOrder($option['timestamp']); - } else { - $this->option->setOrder($option['order']); + if (!$acl->getAllowView()) { + throw new NotAuthorizedException; } - if ($option['confirmed']) { - // do not update confirmation date, if option is already confirmed - if (!$this->option->getConfirmed()) { - $this->option->setConfirmed(time()); - } - } else { - $this->option->setConfirmed(0); + try { + return $this->optionMapper->findByPoll($acl->getPollId()); + } catch (DoesNotExistException $e) { + return []; } } /** - * Get all options of given poll + * Get option * @NoAdminRequired - * @param integer $pollId - * @param string $token - * @return array Array of Option objects + * @param int $optionId + * @return Option + * @throws NotAuthorizedException */ - public function list($pollId = 0, $token = '') { + public function get($optionId) { + if (!$this->acl->set($this->optionMapper->find($optionId)->getPollId())->getAllowView()) { + throw new NotAuthorizedException; + } + + return $this->optionMapper->find($optionId); + } - if (!$this->acl->setPollIdOrToken($pollId, $token)->getAllowView()) { + + /** + * Add a new option + * @NoAdminRequired + * @param int $pollId + * @param int $timestamp + * @param string $pollOptionText + * @return Option + * @throws NotAuthorizedException + */ + public function add($pollId, $timestamp = 0, $pollOptionText = '') { + $this->poll = $this->pollMapper->find($pollId); + if (!$this->acl->set($pollId)->getAllowEdit()) { throw new NotAuthorizedException; } - return $this->optionMapper->findByPoll($pollId); + $this->option = new Option(); + $this->option->setPollId($pollId); + $this->setOption($timestamp, $pollOptionText, 0); + try { + return $this->optionMapper->insert($this->option); + } catch (UniqueConstraintViolationException $e) { + throw new DuplicateEntryException('This option already exists'); + } } - /** - * Add a new Option to poll + * Update option * @NoAdminRequired - * @param Array $option + * @param int $optionId + * @param int $timestamp + * @param string $pollOptionText + * @param int $order * @return Option + * @throws NotAuthorizedException */ - public function add($option) { + public function update($optionId, $timestamp = 0, $pollOptionText = '', $order = 0) { + $this->option = $this->optionMapper->find($optionId); + $this->poll = $this->pollMapper->find($this->option->getPollId()); - if (!$this->acl->setPollId($option['pollId'])->getAllowEdit()) { + if (!$this->acl->set($this->option->getPollId())->getAllowEdit()) { throw new NotAuthorizedException; } - $this->option = new Option(); - $this->set($option); - $this->optionMapper->insert($this->option); - $this->logService->setLog($option['pollId'], 'addOption'); + $this->setOption($timestamp, $pollOptionText, $order); - return $this->option; + return $this->optionMapper->update($this->option); } /** - * Remove a single option + * Delete option * @NoAdminRequired - * @param Option $option - * @return array Array of Option objects + * @param int $optionId + * @return Option deleted Option + * @throws NotAuthorizedException */ public function delete($optionId) { $this->option = $this->optionMapper->find($optionId); - if (!$this->acl->setPollId($this->option->getPollId())->getAllowEdit()) { + if (!$this->acl->set($this->option->getPollId())->getAllowEdit()) { throw new NotAuthorizedException; } $this->optionMapper->delete($this->option); return $this->option; + } + + /** + * Switch optoin confirmation + * @NoAdminRequired + * @param int $optionId + * @return Option confirmed Option + * @throws NotAuthorizedException + */ + public function confirm($optionId) { + $this->option = $this->optionMapper->find($optionId); + if (!$this->acl->set($this->option->getPollId())->getAllowEdit()) { + throw new NotAuthorizedException; + } + + if ($this->option->getConfirmed()) { + $this->option->setConfirmed(0); + } else { + $this->option->setConfirmed(time()); + } + + return $this->optionMapper->update($this->option); } /** - * Update poll option + * Make a sequence of date poll options * @NoAdminRequired - * @param array $option - * @return Option + * @param int $optionId + * @param int $step + * @param string $unit + * @param int $amount + * @return array Array of Option objects + * @throws NotAuthorizedException */ - public function update($option) { - if (!$this->acl->setPollId($option['pollId'])->getAllowEdit()) { + public function sequence($optionId, $step, $unit, $amount) { + $baseDate = new DateTime; + $origin = $this->optionMapper->find($optionId); + + if (!$this->acl->set($origin->getPollId())->getAllowEdit()) { throw new NotAuthorizedException; } - try { - $this->option = $this->optionMapper->find($option['id']); - $this->set($option); - $this->optionMapper->update($this->option); - $this->logService->setLog($option['pollId'], 'updateOption'); + if ($step === 0) { + return $this->optionMapper->findByPoll($origin->getPollId()); + } - return $this->option; - } catch (Exception $e) { - return new DoesNotExistException($e); + $baseDate->setTimestamp($origin->getTimestamp()); + + for ($i = 0; $i < $amount; $i++) { + $this->option = new Option(); + $this->option->setPollId($origin->getPollId()); + $this->option->setConfirmed(0); + $this->option->setTimestamp($baseDate->modify($step . ' ' . $unit)->getTimestamp()); + $this->option->setPollOptionText($baseDate->format('c')); + $this->option->setOrder($baseDate->getTimestamp()); + try { + $this->optionMapper->insert($this->option); + } catch (UniqueConstraintViolationException $e) { + \OC::$server->getLogger()->warning('skip adding ' . $baseDate->format('c') . 'for pollId' . $origin->getPollId() . '. Option alredy exists.'); + } } + return $this->optionMapper->findByPoll($origin->getPollId()); + } + /** + * Copy options from $fromPoll to $toPoll + * @NoAdminRequired + * @param int $fromPollId + * @param int $toPollId + * @return array Array of Option objects + * @throws NotAuthorizedException + */ + public function clone($fromPollId, $toPollId) { + if (!$this->acl->set($fromPollId)->getAllowView()) { + throw new NotAuthorizedException; + } + + foreach ($this->optionMapper->findByPoll($fromPollId) as $origin) { + $option = new Option(); + $option->setPollId($toPollId); + $option->setConfirmed(0); + $option->setPollOptionText($origin->getPollOptionText()); + $option->setTimestamp($origin->getTimestamp()); + $option->setOrder($origin->getOrder()); + $this->optionMapper->insert($option); + } + + return $this->optionMapper->findByPoll($toPollId); } /** - * Set order by order of the given array + * Reorder options with the order specified by $options * @NoAdminRequired - * @param array $options + * @param int $pollId + * @param array $options - Array of options * @return array Array of Option objects + * @throws NotAuthorizedException + * @throws BadRequestException */ public function reorder($pollId, $options) { + $this->poll = $this->pollMapper->find($pollId); - if (!$this->acl->setPollId($pollId)->getAllowEdit()) { + if (!$this->acl->set($pollId)->getAllowEdit()) { throw new NotAuthorizedException; } + if ($this->poll->getType() === 'datePoll') { + throw new BadRequestException("Not allowed in date polls"); + } + $i = 0; foreach ($options as $option) { $this->option = $this->optionMapper->find($option['id']); @@ -189,28 +307,104 @@ public function reorder($pollId, $options) { } return $this->optionMapper->findByPoll($pollId); - } /** - * Set order by order of the given array + * Change order for $optionId and reorder the options * @NoAdminRequired - * @param integer $fromPollId - * @param integer $toPollId + * @param int $optionId + * @param int $newOrder * @return array Array of Option objects + * @throws NotAuthorizedException + * @throws BadRequestException */ - public function clone($fromPollId, $toPollId) { + public function setOrder($optionId, $newOrder) { + $this->option = $this->optionMapper->find($optionId); + $pollId = $this->option->getPollId(); + $this->poll = $this->pollMapper->find($pollId); - if (!$this->acl->setPollId($fromPollId)->getAllowView()) { + if (!$this->acl->set($pollId)->getAllowEdit()) { throw new NotAuthorizedException; } - foreach ($this->optionMapper->findByPoll($fromPollId) as $option) { - $option->setPollId($toPollId); - $this->optionMapper->insert($option); + if ($this->poll->getType() === 'datePoll') { + throw new BadRequestException("Not allowed in date polls"); } - return $this->optionMapper->findByPoll($toPollId); + if ($newOrder < 1) { + $newOrder = 1; + } elseif ($newOrder > $this->getHighestOrder($pollId)) { + $newOrder = $this->getHighestOrder($pollId); + } + + $oldOrder = $this->option->getOrder(); + + foreach ($this->optionMapper->findByPoll($pollId) as $option) { + $currentOrder = $option->getOrder(); + if ($currentOrder > $oldOrder && $currentOrder <= $newOrder) { + $option->setOrder($currentOrder - 1); + $this->optionMapper->update($option); + } elseif ( + ($currentOrder < $oldOrder && $currentOrder >= $newOrder) + || ($currentOrder < $oldOrder && $currentOrder = $newOrder) + ) { + $option->setOrder($currentOrder + 1); + $this->optionMapper->update($option); + } elseif ($currentOrder === $oldOrder) { + $option->setOrder($newOrder); + $this->optionMapper->update($option); + } else { + continue; + } + } + + return $this->optionMapper->findByPoll($this->option->getPollId()); + } + + /** + * Set option entities validated + * @NoAdminRequired + * @param int $timestamp + * @param string $pollOptionText + * @param int $order + * @throws BadRequestException + */ + private function setOption($timestamp = 0, $pollOptionText = '', $order = 0) { + if ($this->poll->getType() === 'datePoll') { + if ($timestamp) { + $this->option->setTimestamp($timestamp); + $this->option->setOrder($timestamp); + $this->option->setPollOptionText(date('c', $timestamp)); + } else { + throw new BadRequestException("Date poll must have a timestamp"); + } + } elseif ($this->poll->getType() === 'textPoll') { + if ($pollOptionText) { + $this->option->setPollOptionText($pollOptionText); + } else { + throw new BadRequestException("Text poll must have a pollOptionText"); + } + if (!$order && !$this->option->getOrder()) { + $order = $this->getHighestOrder($this->option->getPollId()) + 1; + $this->option->setOrder($order); + } + } + } + + /** + * Get the highest order number in $pollId + * @NoAdminRequired + * @param int $pollId + * @return int Highest order number + */ + private function getHighestOrder($pollId) { + $order = 0; + foreach ($this->optionMapper->findByPoll($pollId) as $option) { + if ($option->getOrder() > $order) { + $order = $option->getOrder(); + } + } + return $order; } } diff --git a/lib/Service/PollService.php b/lib/Service/PollService.php index d23c85950..fc4595874 100644 --- a/lib/Service/PollService.php +++ b/lib/Service/PollService.php @@ -21,59 +21,79 @@ * */ - namespace OCA\Polls\Service; +namespace OCA\Polls\Service; - use Exception; - use OCP\AppFramework\Db\DoesNotExistException; - use OCA\Polls\Exceptions\EmptyTitleException; - use OCA\Polls\Exceptions\InvalidAccessException; - use OCA\Polls\Exceptions\InvalidShowResultsException; - use OCA\Polls\Exceptions\InvalidPollTypeException; - use OCA\Polls\Exceptions\NotAuthorizedException; +use OCA\Polls\Exceptions\EmptyTitleException; +use OCA\Polls\Exceptions\InvalidAccessException; +use OCA\Polls\Exceptions\InvalidShowResultsException; +use OCA\Polls\Exceptions\InvalidPollTypeException; +use OCA\Polls\Exceptions\NotAuthorizedException; - use OCP\ILogger; - use OCA\Polls\Db\PollMapper; - use OCA\Polls\Db\Poll; - use OCA\Polls\Service\LogService; - use OCA\Polls\Model\Acl; +use OCA\Polls\Db\PollMapper; +use OCA\Polls\Db\Poll; +use OCA\Polls\Db\VoteMapper; +use OCA\Polls\Db\Vote; +use OCA\Polls\Model\Acl; - class PollService { +class PollService { - private $logger; + /** @var PollMapper */ private $pollMapper; - private $poll; - private $logService; - private $acl; - - /** - * PollController constructor. - * @param ILogger $logger - * @param PollMapper $pollMapper - * @param Poll $poll - * @param LogService $logService - * @param Acl $acl - */ - - public function __construct( - ILogger $logger, - PollMapper $pollMapper, - Poll $poll, - LogService $logService, - Acl $acl - ) { - $this->logger = $logger; - $this->pollMapper = $pollMapper; - $this->poll = $poll; - $this->logService = $logService; - $this->acl = $acl; - } + + /** @var Poll */ + private $poll; + + /** @var VoteMapper */ + private $voteMapper; + + /** @var Vote */ + private $vote; + + /** @var LogService */ + private $logService; + + /** @var MailService */ + private $mailService; + + /** @var Acl */ + private $acl; + + /** + * PollController constructor. + * @param PollMapper $pollMapper + * @param Poll $poll + * @param VoteMapper $voteMapper + * @param Vote $vote + * @param LogService $logService + * @param MailService $mailService + * @param Acl $acl + */ + + public function __construct( + PollMapper $pollMapper, + Poll $poll, + VoteMapper $voteMapper, + Vote $vote, + LogService $logService, + MailService $mailService, + Acl $acl + ) { + $this->pollMapper = $pollMapper; + $this->poll = $poll; + $this->voteMapper = $voteMapper; + $this->vote = $vote; + $this->logService = $logService; + $this->mailService = $mailService; + $this->acl = $acl; + } /** - * list + * Get list of polls * @NoAdminRequired - * @return array + * @return array Array of Poll + * @throws NotAuthorizedException */ public function list() { @@ -87,7 +107,7 @@ public function list() { // TODO: Not the elegant way. Improvement neccessary foreach ($polls as $poll) { $combinedPoll = (object) array_merge( - (array) json_decode(json_encode($poll)), (array) json_decode(json_encode($this->acl->setPollId($poll->getId())))); + (array) json_decode(json_encode($poll)), (array) json_decode(json_encode($this->acl->set($poll->getId())))); if ($combinedPoll->allowView) { $pollList[] = $combinedPoll; } @@ -97,89 +117,31 @@ public function list() { } /** - * get - * @NoAdminRequired - * @param integer $pollId - * @return array - */ - public function get($pollId) { - - if (!$this->acl->setPollId($pollId)->getAllowView()) { - throw new NotAuthorizedException; - } - - return $this->pollMapper->find($pollId); - - } - - /** - * get - * @NoAdminRequired - * @param integer $pollId - * @return array - */ - public function getByToken($token) { - - if (!$this->acl->setToken($token)->getAllowView()) { - throw new NotAuthorizedException; - } - - return $this->pollMapper->find($this->acl->getPollId()); - - } - - /** - * delete - * @NoAdminRequired - * @NoCSRFRequired - * @param integer $pollId - * @return Poll - */ - - public function delete($pollId) { - $this->poll = $this->pollMapper->find($pollId); - - if (!$this->acl->setPollId($pollId)->getAllowEdit()) { - throw new NotAuthorizedException; - } - - if ($this->poll->getDeleted()) { - $this->poll->setDeleted(0); - } else { - $this->poll->setDeleted(time()); - } - - $this->poll = $this->pollMapper->update($this->poll); - $this->logService->setLog($this->poll->getId(), 'deletePoll'); - - return $this->poll; - } - - /** - * deletePermanently + * get poll configuration * @NoAdminRequired - * @NoCSRFRequired - * @param integer $pollId + * @param int $pollId * @return Poll + * @throws NotAuthorizedException */ + public function get($pollId, $token) { + $acl = $this->acl->set($pollId, $token); - public function deletePermanently($pollId) { - $this->poll = $this->pollMapper->find($pollId); - - if (!$this->acl->setPollId($pollId)->getAllowEdit() || !$this->poll->getDeleted()) { + if (!$acl->getAllowView()) { throw new NotAuthorizedException; } - return $this->pollMapper->delete($this->poll); + return $this->pollMapper->find($acl->getPollId()); } /** - * write + * Add poll * @NoAdminRequired - * @NoCSRFRequired * @param string $type * @param string $title * @return Poll + * @throws NotAuthorizedException + * @throws InvalidPollTypeException + * @throws EmptyTitleException */ public function add($type, $title) { @@ -213,6 +175,7 @@ public function add($type, $title) { $this->poll->setShowResults('always'); $this->poll->setDeleted(0); $this->poll->setAdminAccess(0); + $this->poll->setImportant(0); $this->poll = $this->pollMapper->insert($this->poll); $this->logService->setLog($this->poll->getId(), 'addPoll'); @@ -221,18 +184,21 @@ public function add($type, $title) { } /** - * update + * Update poll configuration * @NoAdminRequired - * @NoCSRFRequired - * @param Array $poll + * @param int $pollId + * @param array $poll * @return Poll + * @throws NotAuthorizedException + * @throws EmptyTitleException + * @throws InvalidShowResultsException + * @throws InvalidAccessException */ public function update($pollId, $poll) { - $this->poll = $this->pollMapper->find($pollId); - if (!$this->acl->setPollId($this->poll->getId())->getAllowEdit()) { + if (!$this->acl->set($this->poll->getId())->getAllowEdit()) { throw new NotAuthorizedException; } @@ -242,7 +208,7 @@ public function update($pollId, $poll) { } if (isset($poll['access']) && !in_array($poll['access'], $this->getValidAccess())) { - throw new InvalidAccessException('Invalid value for prop access '. $poll['access']); + throw new InvalidAccessException('Invalid value for prop access ' . $poll['access']); } if (isset($poll['title']) && !$poll['title']) { @@ -256,36 +222,115 @@ public function update($pollId, $poll) { return $this->poll; } + /** - * clone + * Switch deleted status (move to deleted polls) * @NoAdminRequired - * @NoCSRFRequired - * @param integer $pollId + * @param int $pollId * @return Poll + * @throws NotAuthorizedException */ - public function clone($pollId) { - if (!$this->acl->setPollId($this->poll->getId())->getAllowView()) { + public function delete($pollId) { + $this->poll = $this->pollMapper->find($pollId); + + if (!$this->acl->set($pollId)->getAllowEdit()) { throw new NotAuthorizedException; } + if ($this->poll->getDeleted()) { + $this->poll->setDeleted(0); + } else { + $this->poll->setDeleted(time()); + } + + $this->poll = $this->pollMapper->update($this->poll); + $this->logService->setLog($this->poll->getId(), 'deletePoll'); + + return $this->poll; + } + + /** + * Delete poll + * @NoAdminRequired + * @param int $pollId + * @return Poll the deleted poll + * @throws NotAuthorizedException + */ + + public function deletePermanently($pollId) { $this->poll = $this->pollMapper->find($pollId); + if (!$this->acl->set($pollId)->getAllowEdit() || !$this->poll->getDeleted()) { + throw new NotAuthorizedException; + } + + return $this->pollMapper->delete($this->poll); + } + + /** + * Clone poll + * @NoAdminRequired + * @param int $pollId + * @return Poll + * @throws NotAuthorizedException + */ + public function clone($pollId) { + $origin = $this->pollMapper->find($pollId); + if (!$this->acl->set($origin->getId())->getAllowView()) { + throw new NotAuthorizedException; + } + + $this->poll = new Poll(); $this->poll->setCreated(time()); $this->poll->setOwner(\OC::$server->getUserSession()->getUser()->getUID()); - $this->poll->setTitle('Clone of ' . $this->poll->getTitle()); + $this->poll->setTitle('Clone of ' . $origin->getTitle()); $this->poll->setDeleted(0); - $this->poll->setId(0); + $this->poll->setAccess('hidden'); - $this->poll = $this->pollMapper->insert($this->poll); - $this->logService->setLog($this->poll->getId(), 'addPoll'); + $this->poll->setType($origin->getType()); + $this->poll->setDescription($origin->getDescription()); + $this->poll->setExpire($origin->getExpire()); + $this->poll->setAnonymous($origin->getAnonymous()); + $this->poll->setFullAnonymous($origin->getFullAnonymous()); + $this->poll->setAllowMaybe($origin->getAllowMaybe()); + $this->poll->setVoteLimit($origin->getVoteLimit()); + $this->poll->setSettings($origin->getSettings()); + $this->poll->setOptions($origin->getOptions()); + $this->poll->setShowResults($origin->getShowResults()); + $this->poll->setAdminAccess($origin->getAdminAccess()); + $this->poll->setImportant($origin->getImportant()); + + return $this->pollMapper->insert($this->poll); + } - $this->optionService->clone($pollId, $this->poll->getId()); + /** + * Collect email addresses from particitipants + * @NoAdminRequired + * @param Array $poll + * @return array + */ - return $this->poll; + public function getParticipantsEmailAddresses($pollId) { + $this->poll = $this->pollMapper->find($pollId); + if (!$this->acl->set($pollId)->getAllowEdit()) { + return []; + } + $votes = $this->voteMapper->findParticipantsByPoll($pollId); + $list = []; + foreach ($votes as $vote) { + $list[] = $vote->getDisplayName() . ' <' . $this->mailService->resolveEmailAddress($pollId, $vote->getUserId()) . '>'; + } + return array_unique($list); } + + /** + * Get valid values for configuration options + * @NoAdminRequired + * @return array + */ public function getValidEnum() { return [ 'pollType' => $this->getValidPollType(), @@ -294,14 +339,29 @@ public function getValidEnum() { ]; } + /** + * Get valid values for pollType + * @NoAdminRequired + * @return array + */ private function getValidPollType() { return ['datePoll', 'textPoll']; } + /** + * Get valid values for access + * @NoAdminRequired + * @return array + */ private function getValidAccess() { return ['hidden', 'public']; } + /** + * Get valid values for showResult + * @NoAdminRequired + * @return array + */ private function getValidShowResults() { return ['always', 'expired', 'never']; } diff --git a/lib/Service/ShareService.php b/lib/Service/ShareService.php index 5d8b47c4b..a72dd611e 100644 --- a/lib/Service/ShareService.php +++ b/lib/Service/ShareService.php @@ -23,58 +23,67 @@ namespace OCA\Polls\Service; -use Exception; +use OCA\Polls\Exceptions\NotAuthorizedException; +use OCA\Polls\Exceptions\InvalidShareType; use OCP\Security\ISecureRandom; -use OCA\Polls\Exceptions\NotAuthorizedException; -use OCA\Polls\Exceptions\InvalidUsername; - -use OCA\Polls\Db\Share; use OCA\Polls\Db\ShareMapper; -use OCA\Polls\Service\MailService; +use OCA\Polls\Db\Share; use OCA\Polls\Model\Acl; -use OCA\Polls\Controller\SystemController; class ShareService { + /** @var SystemService */ + private $systemService; + + /** @var ShareMapper */ private $shareMapper; + + /** @var Share */ private $share; - private $systemController; + + /** @var MailService */ private $mailService; + + /** @var Acl */ private $acl; /** * ShareController constructor. + * @param SystemService $systemService * @param ShareMapper $shareMapper * @param Share $share - * @param SystemController $systemController * @param MailService $mailService * @param Acl $acl */ public function __construct( + SystemService $systemService, ShareMapper $shareMapper, Share $share, - SystemController $systemController, MailService $mailService, Acl $acl ) { + $this->systemService = $systemService; $this->shareMapper = $shareMapper; $this->share = $share; - $this->systemController = $systemController; $this->mailService = $mailService; $this->acl = $acl; } /** - * get * Read all shares of a poll based on the poll id and return list as array * @NoAdminRequired - * @param integer $pollId - * @return array + * @param int $pollId + * @return array array of Share + * @throws NotAuthorizedException */ - public function list($pollId) { - if (!$this->acl->setPollId($pollId)->getAllowEdit()) { + public function list($pollId, $token) { + if ($token) { + return [$this->get($token)]; + } + + if (!$this->acl->set($pollId)->getAllowEdit()) { throw new NotAuthorizedException; } @@ -82,26 +91,29 @@ public function list($pollId) { } /** - * getByToken - * Get pollId by token + * Get share by token * @NoAdminRequired * @param string $token * @return Share */ public function get($token) { - return $this->shareMapper->findByToken($token); + $this->share = $this->shareMapper->findByToken($token); + + return $this->share; } /** - * Write a new share to the db and returns the new share as array + * Add share * @NoAdminRequired * @param int $pollId - * @param string $share - * @return array + * @param string $type + * @param string $userId + * @param string $userEmail + * @return Share + * @throws NotAuthorizedException */ public function add($pollId, $type, $userId, $userEmail = '') { - - if (!$this->acl->setPollId($pollId)->getAllowEdit()) { + if (!$this->acl->set($pollId)->getAllowEdit()) { throw new NotAuthorizedException; } @@ -122,27 +134,46 @@ public function add($pollId, $type, $userId, $userEmail = '') { } /** - * createPersonalShare - * Write a new share to the db and returns the new share as array + * Set emailAddress to personal share + * or update an email share with the username + * @NoAdminRequired + * @param string $token + * @param string $emailAddress + * @return Share + * @throws InvalidShareType + */ + public function setEmailAddress($token, $emailAddress) { + $this->share = $this->shareMapper->findByToken($token); + if ($this->share->getType() === 'external') { + $this->systemService->validateEmailAddress($emailAddress); + $this->share->setUserEmail($emailAddress); + // TODO: Send confirmation + return $this->shareMapper->update($this->share); + } else { + throw new InvalidShareType('Email address can only be set in external shares.'); + } + } + + /** + * Create a personal share from a public share + * or update an email share with the username * @NoAdminRequired * @param string $token * @param string $userName * @return Share + * @throws NotAuthorizedException */ - public function createPersonalShare($token, $userName) { - $publicShare = $this->shareMapper->findByToken($token); + public function personal($token, $userName, $emailAddress = '') { + $this->share = $this->shareMapper->findByToken($token); - // Return of validatePublicUsername is a DataResponse - $checkUsername = $this->systemController->validatePublicUsername($publicShare->getPollId(), $userName, $token); + $this->systemService->validatePublicUsername($this->share->getPollId(), $userName, $token); - // if status is not 200, return DataResponse from validatePublicUsername - if ($checkUsername->getStatus() !== 200) { - throw new InvalidUsername; + if ($emailAddress) { + $this->systemService->validateEmailAddress($emailAddress); } - if ($publicShare->getType() === 'public') { - - + if ($this->share->getType() === 'public') { + $pollId = $this->share->getPollId(); $this->share = new Share(); $this->share->setToken(\OC::$server->getSecureRandom()->generate( 16, @@ -151,34 +182,39 @@ public function createPersonalShare($token, $userName) { ISecureRandom::CHAR_UPPER )); $this->share->setType('external'); - $this->share->setPollId($publicShare->getPollId()); + $this->share->setPollId($pollId); $this->share->setUserId($userName); - $this->share->setUserEmail(''); + $this->share->setUserEmail($emailAddress); $this->share->setInvitationSent(time()); - return $this->shareMapper->insert($this->share); - - } elseif ($publicShare->getType() === 'email') { + $this->shareMapper->insert($this->share); - $publicShare->setType('external'); - $publicShare->setUserId($userName); - return $this->shareMapper->update($publicShare); + if ($emailAddress) { + $this->mailService->sendInvitationMail($this->share->getToken()); + } + return $this->share; + } elseif ($this->share->getType() === 'email') { + $this->share->setType('external'); + $this->share->setUserId($userName); + $this->share->setUserEmail($emailAddress); + return $this->shareMapper->update($this->share); } else { throw new NotAuthorizedException; } } /** - * remove + * Delete share * remove share * @NoAdminRequired * @param string $token * @return Share + * @throws NotAuthorizedException */ - public function remove($token) { + public function delete($token) { $this->share = $this->shareMapper->findByToken($token); - if (!$this->acl->setPollId($this->share->getPollId())->getAllowEdit()) { + if (!$this->acl->set($this->share->getPollId())->getAllowEdit()) { throw new NotAuthorizedException; } diff --git a/lib/Service/SubscriptionService.php b/lib/Service/SubscriptionService.php index f378e476c..ec824b61d 100644 --- a/lib/Service/SubscriptionService.php +++ b/lib/Service/SubscriptionService.php @@ -23,109 +23,102 @@ namespace OCA\Polls\Service; -use Exception; use OCA\Polls\Exceptions\NotAuthorizedException; use OCP\AppFramework\Db\MultipleObjectsReturnedException; use OCP\AppFramework\Db\DoesNotExistException; -use OCP\ILogger; use OCA\Polls\Db\Subscription; use OCA\Polls\Db\SubscriptionMapper; use OCA\Polls\Model\Acl; -class SubscriptionService { +class SubscriptionService { + /** @var Acl */ private $acl; + + /** @var SubscriptionMapper */ private $subscriptionMapper; - private $logger; /** * SubscriptionController constructor. * @param SubscriptionMapper $subscriptionMapper - * @param ILogger $logger * @param Acl $acl */ public function __construct( SubscriptionMapper $subscriptionMapper, - ILogger $logger, Acl $acl ) { $this->subscriptionMapper = $subscriptionMapper; $this->acl = $acl; - $this->logger = $logger; } /** * @NoAdminRequired - * @param integer $pollId + * @param int $pollId * @return array */ - public function get($pollId) { - if (!$this->acl->setPollId($pollId)->getAllowView()) { + public function get($pollId, $token) { + if (!$this->acl->set($pollId, $token)->getAllowView()) { throw new NotAuthorizedException; } try { - return $this->subscriptionMapper->findByUserAndPoll($pollId, $this->acl->getUserId()); + return $this->subscriptionMapper->findByUserAndPoll($this->acl->getPollId(), $this->acl->getUserId()); } catch (MultipleObjectsReturnedException $e) { // subscription should be unique. If duplicates are found resubscribe // duplicates are removed in $this->set() - return $this->set($pollId, true); + return $this->set($pollId, $token, true); } - } /** * @NoAdminRequired - * @param integer $pollId + * @param int $pollId + * @param string $token + * @param bool $subscribed * @return array */ - public function set($pollId, $subscribed) { - if (!$this->acl->setPollId($pollId)->getAllowView()) { + public function set($pollId, $token, $subscribed) { + if (!$this->acl->set($pollId, $token)->getAllowView()) { throw new NotAuthorizedException; } try { - $subscription = $this->subscriptionMapper->findByUserAndPoll($pollId, $this->acl->getUserId()); + $subscription = $this->subscriptionMapper->findByUserAndPoll($this->acl->getPollId(), $this->acl->getUserId()); if (!$subscribed) { $this->subscriptionMapper->delete($subscription); - return ['status' => 'Unsubscribed from poll ' . $pollId]; + return ['status' => 'Unsubscribed from poll ' . $this->acl->getPollId()]; } else { // subscription already exists, just return the existing subscription - return ['status' => 'Subscribed to poll ' . $pollId]; + return ['status' => 'Subscribed to poll ' . $this->acl->getPollId()]; } - - } catch (DoesNotExistException $e){ - + } catch (DoesNotExistException $e) { if ($subscribed) { $subscription = new Subscription(); - $subscription->setPollId($pollId); + $subscription->setPollId($this->acl->getPollId()); $subscription->setUserId($this->acl->getUserId()); $this->subscriptionMapper->insert($subscription); - return ['status' => 'Subscribed to poll ' . $pollId]; + return ['status' => 'Subscribed to poll ' . $this->acl->getPollId()]; } else { // subscription is not found, just approve the unsubscription - return ['status' => 'Unsubscribed from poll ' . $pollId]; + return ['status' => 'Unsubscribed from poll ' . $this->acl->getPollId()]; } - } catch (MultipleObjectsReturnedException $e) { // Duplicates should not exist but if found, fix it // unsubscribe from all and resubscribe, if requested - $this->logger->debug('Multiple subscription (dulpicates) found'); - $this->subscriptionMapper->unsubscribe($pollId, $this->acl->getUserId()); - $this->logger->debug('Unsubscribed all for user ' . $this->acl->getUserId() . 'in poll' . $pollId); + \OC::$server->getLogger()->debug('Multiple subscription (dulpicates) found'); + $this->subscriptionMapper->unsubscribe($this->acl->getPollId(), $this->acl->getUserId()); + \OC::$server->getLogger()->debug('Unsubscribed all for user ' . $this->acl->getUserId() . 'in poll' . $pollId); if ($subscribed) { $subscription = new Subscription(); - $subscription->setPollId($pollId); + $subscription->setPollId($this->acl->getPollId()); $subscription->setUserId($this->acl->getUserId()); $this->subscriptionMapper->insert($subscription); - $this->logger->debug('Added new subscription'); + \OC::$server->getLogger()->debug('Added new subscription'); return $subscription; } else { - return ['status' => 'Unsubscribed from poll ' . $pollId]; + return ['status' => 'Unsubscribed from poll ' . $this->acl->getPollId()]; } - } - } } diff --git a/lib/Service/SystemService.php b/lib/Service/SystemService.php new file mode 100644 index 000000000..1d4b731e1 --- /dev/null +++ b/lib/Service/SystemService.php @@ -0,0 +1,430 @@ + + * + * @author René Gieling + * + * @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 . + * + */ + +namespace OCA\Polls\Service; + +use OCA\Polls\Exceptions\NotAuthorizedException; +use OCA\Polls\Exceptions\TooShortException; +use OCA\Polls\Exceptions\InvalidUsernameException; +use OCA\Polls\Exceptions\InvalidEmailAddress; + +use OCP\IGroupManager; +use OCP\IUserManager; +use OCA\Polls\Db\Share; +use OCA\Polls\Db\ShareMapper; +use OCA\Polls\Db\VoteMapper; + +class SystemService { + + /** @var IGroupManager */ + private $groupManager; + + /** @var IUserManager */ + private $userManager; + + /** @var VoteMapper */ + private $voteMapper; + + /** @var ShareMapper */ + private $shareMapper; + + /** + * SystemService constructor. + * @param IGroupManager $groupManager + * @param IUserManager $userManager + * @param VoteMapper $voteMapper + * @param ShareMapper $shareMapper + */ + public function __construct( + IGroupManager $groupManager, + IUserManager $userManager, + VoteMapper $voteMapper, + ShareMapper $shareMapper + ) { + $this->groupManager = $groupManager; + $this->userManager = $userManager; + $this->voteMapper = $voteMapper; + $this->shareMapper = $shareMapper; + } + + /** + * Validate string as email address + * @NoAdminRequired + * @param string $emailAddress + * @return bool + */ + private function isValidEmail($emailAddress) { + return (!preg_match('/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/', $emailAddress)) ? false : true; + } + + + /** + * Get a list of users + * @NoAdminRequired + * @param string $query + * @param array $skip - usernames to skip in return array + * @return Array + */ + public function getSiteUsers($query = '', $skip = []) { + $users = []; + foreach ($this->userManager->searchDisplayName($query) as $user) { + if (!in_array($user->getUID(), $skip) && $user->isEnabled()) { + $users[] = [ + 'id' => $user->getUID(), + 'user' => $user->getUID(), + 'displayName' => $user->getDisplayName(), + 'organisation' => '', + 'emailAddress' => $user->getEMailAddress(), + 'desc' => 'User', + 'type' => 'user', + 'icon' => 'icon-user', + 'avatarURL' => '', + 'avatar' => '', + 'lastLogin' => $user->getLastLogin(), + 'cloudId' => $user->getCloudId() + ]; + } + } + return $users; + } + + /** + * Get a list of user groups + * @NoAdminRequired + * @param string $query + * @param array $skip - group names to skip in return array + * @return Array + */ + public function getSiteGroups($query = '', $skip = []) { + $groups = []; + foreach ($this->groupManager->search($query) as $group) { + if (!in_array($group->getGID(), $skip)) { + try { + // seems to work only from NC19 on + $displayName = $group->getDisplayName(); + } catch (\Exception $e) { + // fallback + $displayName = $group->getGID(); + } + + $groups[] = [ + 'id' => $group->getGID(), + 'user' => $group->getGID(), + 'organisation' => '', + 'displayName' => $displayName, + 'emailAddress' => '', + 'desc' => 'Group', + 'type' => 'group', + 'icon' => 'icon-group', + 'avatarURL' => '', + 'avatar' => '', + 'lastLogin' => '', + 'cloudId' => '' + + ]; + } + } + return $groups; + } + + /** + * Get a list of contacts + * @NoAdminRequired + * @param string $query + * @return Array + */ + public function getContacts($query = '') { + $contacts = []; + foreach (\OC::$server->getContactsManager()->search($query, ['FN', 'EMAIL', 'ORG', 'CATEGORIES']) as $contact) { + if (!array_key_exists('isLocalSystemBook', $contact) && array_key_exists('EMAIL', $contact)) { + $emailAdresses = $contact['EMAIL']; + + if (!is_array($emailAdresses)) { + $emailAdresses = [$emailAdresses]; + } else { + // take the first eMail address for now + $emailAdresses = [$emailAdresses[0]]; + } + + foreach ($emailAdresses as $emailAddress) { + $contacts[] = [ + 'id' => $contact['UID'], + 'user' => $contact['FN'], + 'displayName' => $contact['FN'], + 'organisation' => isset($contact['ORG']) ? $contact['ORG'] : '', + 'emailAddress' => $emailAddress, + 'desc' => 'Contact', + 'type' => 'contact', + 'icon' => 'icon-mail', + 'avatarURL' => '', + 'avatar' => '', + 'lastLogin' => '', + 'cloudId' => '', + ]; + } + } + } + return $contacts; + } + + /** + * Get a list of contacts + * @NoAdminRequired + * @param string $query + * @return Array + */ + public function getContactsGroupMembers($query = '') { + $contacts = []; + foreach (\OC::$server->getContactsManager()->search($query, ['CATEGORIES']) as $contact) { + if ( + !array_key_exists('isLocalSystemBook', $contact) + && array_key_exists('EMAIL', $contact) + && in_array($query, explode(',', $contact['CATEGORIES'])) + ) { + $emailAdresses = $contact['EMAIL']; + + if (!is_array($emailAdresses)) { + $emailAdresses = [$emailAdresses]; + } else { + // take the first eMail address for now + $emailAdresses = [$emailAdresses[0]]; + } + + foreach ($emailAdresses as $emailAddress) { + $contacts[] = [ + 'id' => $contact['UID'], + 'user' => $contact['FN'], + 'displayName' => $contact['FN'], + 'organisation' => isset($contact['ORG']) ? $contact['ORG'] : '', + 'emailAddress' => $emailAddress, + 'desc' => 'Contact', + 'type' => 'contact', + 'icon' => 'icon-mail', + 'avatarURL' => '', + 'avatar' => '', + 'lastLogin' => '', + 'cloudId' => '', + ]; + } + } + } + return $contacts; + } + + /** + * Get a list of contact groups + * @NoAdminRequired + * @param string $query + * @return Array + */ + public function getContactsGroups($query = '') { + $contactGroups = []; + $foundContacts = []; + + foreach (\OC::$server->getContactsManager()->search($query, ['CATEGORIES']) as $contact) { + foreach (explode(',', $contact['CATEGORIES']) as $contactGroup) { + if (strpos($contactGroup, $query) === 0 && !in_array($contactGroup, $foundContacts)) { + $foundContacts[] = $contactGroup; + $contactGroups[] = [ + 'id' => 'contactgroup_' +$contactGroup, + 'user' => $contactGroup, + 'displayName' => $contactGroup, + 'organisation' => '', + 'emailAddress' => '', + 'desc' => 'Contact Group', + 'type' => 'contactGroup', + 'icon' => 'icon-group', + 'avatarURL' => '', + 'avatar' => '', + 'lastLogin' => '', + 'cloudId' => '', + ]; + }; + } + } + return $contactGroups; + } + + + /** + * Get a combined list of NC users, groups and contacts + * @NoAdminRequired + * @param string $query + * @param bool $getGroups - search in groups + * @param bool $getUsers - search in site users + * @param bool $getContacts - search in contacs + * @param bool $getContactGroups - search in contacs + * @param array $skipGroups - group names to skip in return array + * @param array $skipUsers - user names to skip in return array + * @return Array + */ + public function getSiteUsersAndGroups( + $query = '', + $getGroups = true, + $getUsers = true, + $getContacts = true, + $getContactGroups = true, + $getMail = false, + $skipGroups = [], + $skipUsers = [] + ) { + $list = []; + + if ($getMail && $this->isValidEmail($query)) { + $list[] = [ + 'id' => '', + 'user' => '', + 'organisation' => '', + 'displayName' => '', + 'emailAddress' => $query, + 'desc' => $query, + 'type' => 'email', + 'icon' => 'icon-mail', + 'avatarURL' => '', + 'avatar' => '', + 'lastLogin' => '', + 'cloudId' => '' + + ]; + } + if ($getGroups) { + $list = array_merge($list, $this->getSiteGroups($query, $skipGroups)); + } + + if ($getUsers) { + $list = array_merge($list, $this->getSiteUsers($query, $skipUsers)); + } + + if (\OC::$server->getContactsManager()->isEnabled()) { + if ($getContacts) { + $list = array_merge($list, $this->getContacts($query)); + } + + if ($getContacts) { + $list = array_merge($list, $this->getContactsGroups($query)); + } + } + + return $list; + } + + /** + * Validate it the user name is reservrd + * return false, if this username already exists as a user or as + * a participant of the poll + * @NoAdminRequired + * @return Boolean + * @throws InvalidEmailAddress + */ + public function validateEmailAddress($emailAddress) { + if (!$this->isValidEmail($emailAddress)) { + throw new InvalidEmailAddress; + } + return true; + } + + + /** + * Validate it the user name is reservrd + * return false, if this username already exists as a user or as + * a participant of the poll + * @NoAdminRequired + * @return Boolean + * @throws NotAuthorizedException + * @throws TooShortException + * @throws InvalidUsernameException + */ + public function validatePublicUsername($pollId, $userName, $token) { + + // return forbidden, if $pollId does not match the share's pollId, force int compare + if (intval($this->shareMapper->findByToken($token)->getPollId()) !== intVal($pollId)) { + throw new NotAuthorizedException; + } + + // return forbidden, if the length of the userame is lower than 3 characters + if (strlen(trim($userName)) < 3) { + return new TooShortException('Username must have at least 3 characters'); + } + + $list = []; + + // get all groups + $groups = $this->groupManager->search(''); + foreach ($groups as $group) { + $list[] = [ + 'id' => $group->getGID(), + 'user' => $group->getGID(), + 'type' => 'group', + 'displayName' => $group->getGID(), + ]; + } + + // get all users + $users = $this->userManager->searchDisplayName(''); + foreach ($users as $user) { + $list[] = [ + 'id' => $user->getUID(), + 'user' => $user->getUID(), + 'type' => 'user', + 'displayName' => $user->getDisplayName(), + ]; + } + + // get all participants + $votes = $this->voteMapper->findParticipantsByPoll($pollId); + foreach ($votes as $vote) { + if ($vote->getUserId() !== '' && $vote->getUserId() !== null) { + $list[] = [ + 'id' => $vote->getUserId(), + 'user' => $vote->getUserId(), + 'type' => 'participant', + 'displayName' => $vote->getUserId(), + ]; + } + } + + // get all shares for this poll + $shares = $this->shareMapper->findByPoll($pollId); + foreach ($shares as $share) { + if ($share->getUserId() !== '' && $share->getUserId() !== null) { + $list[] = [ + 'id' => $share->getUserId(), + 'user' => $share->getUserId(), + 'type' => 'share', + 'displayName' => $share->getUserId(), + ]; + } + } + + // check if the username is contained inside the generated list + // return forbidden, if list contains requested username + foreach ($list as $element) { + if (strtolower(trim($userName)) === strtolower(trim($element['id'])) || strtolower(trim($userName)) === strtolower(trim($element['displayName']))) { + throw new InvalidUsernameException; + } + } + + // return true, if username is allowed + return true; + } +} diff --git a/lib/Service/VoteService.php b/lib/Service/VoteService.php index 4df01c706..f2558667a 100644 --- a/lib/Service/VoteService.php +++ b/lib/Service/VoteService.php @@ -23,24 +23,32 @@ namespace OCA\Polls\Service; -use Exception; use OCP\AppFramework\Db\DoesNotExistException; use OCA\Polls\Exceptions\NotAuthorizedException; -use OCA\Polls\Db\Vote; use OCA\Polls\Db\VoteMapper; +use OCA\Polls\Db\Vote; use OCA\Polls\Db\OptionMapper; -use OCA\Polls\Service\AnonymizeService; -use OCA\Polls\Service\LogService; use OCA\Polls\Model\Acl; -class VoteService { +class VoteService { + /** @var VoteMapper */ private $voteMapper; + + /** @var Vote */ private $vote; + + /** @var OptionMapper */ private $optionMapper; + + /** @var AnonymizeService */ private $anonymizer; + + /** @var LogService */ private $logService; + + /** @var Acl */ private $acl; /** @@ -69,15 +77,15 @@ public function __construct( } /** - * Get all votes of given poll * Read all votes of a poll based on the poll id and return list as array * @NoAdminRequired - * @param integer $pollId + * @param int $pollId * @param string $token - * @return Vote + * @return array + * @throws NotAuthorizedException */ public function list($pollId = 0, $token = '') { - if (!$this->acl->setPollIdOrToken($pollId, $token)->getAllowView()) { + if (!$this->acl->set($pollId, $token)->getAllowView()) { throw new NotAuthorizedException; } @@ -92,60 +100,60 @@ public function list($pollId = 0, $token = '') { } /** - * set + * Set vote * @NoAdminRequired - * @param integer $pollId - * @param Array $option + * @param int $optionId * @param string $setTo * @param string $token * @return Vote + * @throws NotAuthorizedException */ - public function set($pollId = 0, $pollOptionText, $setTo, $token = '') { + public function set($optionId, $setTo, $token = '') { + $option = $this->optionMapper->find($optionId); - if (!$this->acl->setPollIdOrToken($pollId, $token)->getAllowVote()) { + if (!$this->acl->set($option->getPollId(), $token)->getAllowVote()) { throw new NotAuthorizedException; } - $option = $this->optionMapper->findByPollAndText($this->acl->getpollId(), $pollOptionText); + if (intval($option->getPollId()) !== $this->acl->getPollId()) { + throw new NotAuthorizedException; + } try { - $this->vote = $this->voteMapper->findSingleVote($this->acl->getpollId(), $option->getPollOptionText(), $this->acl->getUserId()); + $this->vote = $this->voteMapper->findSingleVote($this->acl->getPollId(), $option->getPollOptionText(), $this->acl->getUserId()); $this->vote->setVoteAnswer($setTo); $this->voteMapper->update($this->vote); - } catch (DoesNotExistException $e) { // Vote does not exist, insert as new Vote $this->vote = new Vote(); - $this->vote->setPollId($this->acl->getpollId()); + $this->vote->setPollId($this->acl->getPollId()); $this->vote->setUserId($this->acl->getUserId()); $this->vote->setVoteOptionText($option->getPollOptionText()); $this->vote->setVoteOptionId($option->getId()); $this->vote->setVoteAnswer($setTo); $this->voteMapper->insert($this->vote); - } finally { - $this->logService->setLog($this->vote->getPollId(), 'setVote', $this->vote->getUserId()); + $this->logService->setLog($this->acl->getPollId(), 'setVote', $this->vote->getUserId()); return $this->vote; } } /** - * delete + * Remove user from poll * @NoAdminRequired - * @NoCSRFRequired - * @param integer $voteId + * @param int $voteId * @param string $userId - * @param integer $pollId - * @return Vote + * @param int $pollId + * @return boolean + * @throws NotAuthorizedException */ public function delete($pollId, $userId) { - - if (!$this->acl->setPollId($pollId)->getAllowEdit()) { + if (!$this->acl->set($pollId)->getAllowEdit()) { throw new NotAuthorizedException; } - $votes = $this->voteMapper->deleteByPollAndUser($pollId, $userId); + $this->voteMapper->deleteByPollAndUser($pollId, $userId); + return $userId; } - } diff --git a/package-lock.json b/package-lock.json index de741c432..5013d7efd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,23 +1,30 @@ { "name": "polls", - "version": "1.5.0", + "version": "1.5.2", "lockfileVersion": 1, "requires": true, "dependencies": { "@babel/cli": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.10.4.tgz", - "integrity": "sha512-xX99K4V1BzGJdQANK5cwK+EpF1vP9gvqhn+iWvG+TubCjecplW7RSQimJ2jcCvu6fnK5pY6mZMdu6EWTj32QVA==", + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.11.5.tgz", + "integrity": "sha512-0umMDxxdEZ98EMZtS9Wgnaf4NdgqBcQHaGYaMfAmP+ZicVglZ2+QZwoHNacfnUq4hCmC1V7Ap5Phq7FInpWrWg==", "requires": { "chokidar": "^2.1.8", "commander": "^4.0.1", "convert-source-map": "^1.1.0", "fs-readdir-recursive": "^1.1.0", "glob": "^7.0.0", - "lodash": "^4.17.13", + "lodash": "^4.17.19", "make-dir": "^2.1.0", "slash": "^2.0.0", - "source-map": "^0.5.0" + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } } }, "@babel/code-frame": { @@ -40,12 +47,12 @@ } }, "@babel/core": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.5.tgz", - "integrity": "sha512-fsEANVOcZHzrsV6dMVWqpSeXClq3lNbYrfFGme6DE25FQWe7pyeYpXyx9guqUnpy466JLzZ8z4uwSr2iv60V5Q==", + "version": "7.11.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.6.tgz", + "integrity": "sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg==", "requires": { "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.11.5", + "@babel/generator": "^7.11.6", "@babel/helper-module-transforms": "^7.11.0", "@babel/helpers": "^7.10.4", "@babel/parser": "^7.11.5", @@ -59,7 +66,7 @@ "lodash": "^4.17.19", "resolve": "^1.3.2", "semver": "^5.4.1", - "source-map": "^0.6.1" + "source-map": "^0.5.0" }, "dependencies": { "@babel/code-frame": { @@ -71,13 +78,13 @@ } }, "@babel/generator": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.5.tgz", - "integrity": "sha512-9UqHWJ4IwRTy4l0o8gq2ef8ws8UPzvtMkVKjTLAiRmza9p9V6Z+OfuNd9fB1j5Q67F+dVJtPC2sZXI8NM9br4g==", + "version": "7.11.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.6.tgz", + "integrity": "sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA==", "requires": { "@babel/types": "^7.11.5", "jsesc": "^2.5.1", - "source-map": "^0.6.1" + "source-map": "^0.5.0" } }, "@babel/helper-function-name": { @@ -208,9 +215,9 @@ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", + "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", @@ -234,9 +241,9 @@ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", + "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", @@ -258,12 +265,12 @@ } }, "@babel/helper-create-class-features-plugin": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.4.tgz", - "integrity": "sha512-9raUiOsXPxzzLjCXeosApJItoMnX3uyT4QdM2UldffuGApNrF8e938MwNpDCK9CPoyxrEoCgT+hObJc3mZa6lQ==", + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz", + "integrity": "sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A==", "requires": { "@babel/helper-function-name": "^7.10.4", - "@babel/helper-member-expression-to-functions": "^7.10.4", + "@babel/helper-member-expression-to-functions": "^7.10.5", "@babel/helper-optimise-call-expression": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4", "@babel/helper-replace-supers": "^7.10.4", @@ -296,17 +303,12 @@ "@babel/types": "^7.10.4" } }, - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" - }, "@babel/helper-split-export-declaration": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.4.tgz", - "integrity": "sha512-pySBTeoUff56fL5CBU2hWm9TesA4r/rOkI9DyJLvvgz09MB9YtfIYe3iBriVaYNaPe+Alua0vBIOVOLs2buWhg==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", + "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", "requires": { - "@babel/types": "^7.10.4" + "@babel/types": "^7.11.0" } }, "@babel/helper-validator-identifier": { @@ -325,9 +327,9 @@ } }, "@babel/parser": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.10.4.tgz", - "integrity": "sha512-8jHII4hf+YVDsskTF6WuMB3X4Eh+PsUkC2ljq22so5rHvH+T8BzyL94VOdyFLNR8tBSVXOTbNHOKpR4TfRxVtA==" + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz", + "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==" }, "@babel/template": { "version": "7.10.4", @@ -340,12 +342,12 @@ } }, "@babel/types": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.4.tgz", - "integrity": "sha512-UTCFOxC3FsFHb7lkRMVvgLzaRVamXuAs2Tz4wajva4WxtVY82eZeaUBtC2Zt95FU9TiznuC0Zk35tsim8jeVpg==", + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", + "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", "requires": { "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.13", + "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" } } @@ -413,9 +415,9 @@ } }, "@babel/parser": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.0.tgz", - "integrity": "sha512-qvRvi4oI8xii8NllyEc4MDJjuZiNaRzyb7Y7lup1NqJV8TZHF4O27CcP+72WPn/k1zkgJ6WJfnIbk4jTsVAZHw==" + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz", + "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==" }, "@babel/template": { "version": "7.10.4", @@ -428,9 +430,9 @@ } }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", + "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", @@ -440,126 +442,27 @@ } }, "@babel/helper-explode-assignable-expression": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.10.4.tgz", - "integrity": "sha512-4K71RyRQNPRrR85sr5QY4X3VwG4wtVoXZB9+L3r1Gp38DhELyHCtovqydRi7c1Ovb17eRGiQ/FD5s8JdU0Uy5A==", + "version": "7.11.4", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.11.4.tgz", + "integrity": "sha512-ux9hm3zR4WV1Y3xXxXkdG/0gxF9nvI0YVmKVhvK9AfMoaQkemL3sJpXw+Xbz65azo8qJiEz2XVDUpK3KYhH3ZQ==", "requires": { - "@babel/traverse": "^7.10.4", "@babel/types": "^7.10.4" }, "dependencies": { - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/generator": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.0.tgz", - "integrity": "sha512-fEm3Uzw7Mc9Xi//qU20cBKatTfs2aOtKqmvy/Vm7RkJEGFQ4xc9myCfbXxqK//ZS8MR/ciOHw6meGASJuKmDfQ==", - "requires": { - "@babel/types": "^7.11.0", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "@babel/helper-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", - "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", - "requires": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", - "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", - "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", - "requires": { - "@babel/types": "^7.11.0" - } - }, "@babel/helper-validator-identifier": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.0.tgz", - "integrity": "sha512-qvRvi4oI8xii8NllyEc4MDJjuZiNaRzyb7Y7lup1NqJV8TZHF4O27CcP+72WPn/k1zkgJ6WJfnIbk4jTsVAZHw==" - }, - "@babel/template": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", - "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/traverse": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.0.tgz", - "integrity": "sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg==", - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.11.0", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.11.0", - "@babel/types": "^7.11.0", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.19" - } - }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", + "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" } } }, @@ -597,9 +500,9 @@ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", + "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", @@ -609,11 +512,11 @@ } }, "@babel/helper-member-expression-to-functions": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.10.4.tgz", - "integrity": "sha512-m5j85pK/KZhuSdM/8cHUABQTAslV47OjfIB9Cc7P+PvlAoBzdb79BGNfw8RhT5Mq3p+xGd0ZfAKixbrUZx0C7A==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz", + "integrity": "sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q==", "requires": { - "@babel/types": "^7.10.4" + "@babel/types": "^7.11.0" }, "dependencies": { "@babel/helper-validator-identifier": { @@ -622,12 +525,12 @@ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" }, "@babel/types": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.4.tgz", - "integrity": "sha512-UTCFOxC3FsFHb7lkRMVvgLzaRVamXuAs2Tz4wajva4WxtVY82eZeaUBtC2Zt95FU9TiznuC0Zk35tsim8jeVpg==", + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", + "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", "requires": { "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.13", + "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" } } @@ -647,9 +550,9 @@ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", + "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", @@ -704,9 +607,9 @@ } }, "@babel/parser": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.0.tgz", - "integrity": "sha512-qvRvi4oI8xii8NllyEc4MDJjuZiNaRzyb7Y7lup1NqJV8TZHF4O27CcP+72WPn/k1zkgJ6WJfnIbk4jTsVAZHw==" + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz", + "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==" }, "@babel/template": { "version": "7.10.4", @@ -719,9 +622,9 @@ } }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", + "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", @@ -744,12 +647,12 @@ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" }, "@babel/types": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.4.tgz", - "integrity": "sha512-UTCFOxC3FsFHb7lkRMVvgLzaRVamXuAs2Tz4wajva4WxtVY82eZeaUBtC2Zt95FU9TiznuC0Zk35tsim8jeVpg==", + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", + "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", "requires": { "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.13", + "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" } } @@ -769,14 +672,13 @@ } }, "@babel/helper-remap-async-to-generator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.10.4.tgz", - "integrity": "sha512-86Lsr6NNw3qTNl+TBcF1oRZMaVzJtbWTyTko+CQL/tvNvcGYEFKbLXDPxtW0HKk3McNOk4KzY55itGWCAGK5tg==", + "version": "7.11.4", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.11.4.tgz", + "integrity": "sha512-tR5vJ/vBa9wFy3m5LLv2faapJLnDFxNWff2SAYkSE4rLUdbp7CdObYFgI7wK4T/Mj4UzpjPwzR8Pzmr5m7MHGA==", "requires": { "@babel/helper-annotate-as-pure": "^7.10.4", "@babel/helper-wrap-function": "^7.10.4", "@babel/template": "^7.10.4", - "@babel/traverse": "^7.10.4", "@babel/types": "^7.10.4" }, "dependencies": { @@ -788,42 +690,6 @@ "@babel/highlight": "^7.10.4" } }, - "@babel/generator": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.0.tgz", - "integrity": "sha512-fEm3Uzw7Mc9Xi//qU20cBKatTfs2aOtKqmvy/Vm7RkJEGFQ4xc9myCfbXxqK//ZS8MR/ciOHw6meGASJuKmDfQ==", - "requires": { - "@babel/types": "^7.11.0", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "@babel/helper-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", - "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", - "requires": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", - "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", - "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", - "requires": { - "@babel/types": "^7.11.0" - } - }, "@babel/helper-validator-identifier": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", @@ -840,9 +706,9 @@ } }, "@babel/parser": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.0.tgz", - "integrity": "sha512-qvRvi4oI8xii8NllyEc4MDJjuZiNaRzyb7Y7lup1NqJV8TZHF4O27CcP+72WPn/k1zkgJ6WJfnIbk4jTsVAZHw==" + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz", + "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==" }, "@babel/template": { "version": "7.10.4", @@ -854,44 +720,15 @@ "@babel/types": "^7.10.4" } }, - "@babel/traverse": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.0.tgz", - "integrity": "sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg==", - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.11.0", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.11.0", - "@babel/types": "^7.11.0", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.19" - } - }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", + "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" } } }, @@ -915,14 +752,13 @@ } }, "@babel/generator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.10.4.tgz", - "integrity": "sha512-toLIHUIAgcQygFZRAQcsLQV3CBuX6yOIru1kJk/qqqvcRmZrYe6WavZTSG+bB8MxhnL9YPf+pKQfuiP161q7ng==", + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.5.tgz", + "integrity": "sha512-9UqHWJ4IwRTy4l0o8gq2ef8ws8UPzvtMkVKjTLAiRmza9p9V6Z+OfuNd9fB1j5Q67F+dVJtPC2sZXI8NM9br4g==", "requires": { - "@babel/types": "^7.10.4", + "@babel/types": "^7.11.5", "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" + "source-map": "^0.6.1" } }, "@babel/helper-function-name": { @@ -944,11 +780,11 @@ } }, "@babel/helper-split-export-declaration": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.4.tgz", - "integrity": "sha512-pySBTeoUff56fL5CBU2hWm9TesA4r/rOkI9DyJLvvgz09MB9YtfIYe3iBriVaYNaPe+Alua0vBIOVOLs2buWhg==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", + "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", "requires": { - "@babel/types": "^7.10.4" + "@babel/types": "^7.11.0" } }, "@babel/helper-validator-identifier": { @@ -967,9 +803,9 @@ } }, "@babel/parser": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.10.4.tgz", - "integrity": "sha512-8jHII4hf+YVDsskTF6WuMB3X4Eh+PsUkC2ljq22so5rHvH+T8BzyL94VOdyFLNR8tBSVXOTbNHOKpR4TfRxVtA==" + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz", + "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==" }, "@babel/template": { "version": "7.10.4", @@ -982,28 +818,28 @@ } }, "@babel/traverse": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.10.4.tgz", - "integrity": "sha512-aSy7p5THgSYm4YyxNGz6jZpXf+Ok40QF3aA2LyIONkDHpAcJzDUqlCKXv6peqYUs2gmic849C/t2HKw2a2K20Q==", + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.5.tgz", + "integrity": "sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ==", "requires": { "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.10.4", + "@babel/generator": "^7.11.5", "@babel/helper-function-name": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/parser": "^7.11.5", + "@babel/types": "^7.11.5", "debug": "^4.1.0", "globals": "^11.1.0", - "lodash": "^4.17.13" + "lodash": "^4.17.19" } }, "@babel/types": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.4.tgz", - "integrity": "sha512-UTCFOxC3FsFHb7lkRMVvgLzaRVamXuAs2Tz4wajva4WxtVY82eZeaUBtC2Zt95FU9TiznuC0Zk35tsim8jeVpg==", + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", + "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", "requires": { "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.13", + "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" } }, @@ -1019,6 +855,11 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" } } }, @@ -1055,9 +896,9 @@ } }, "@babel/parser": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.0.tgz", - "integrity": "sha512-qvRvi4oI8xii8NllyEc4MDJjuZiNaRzyb7Y7lup1NqJV8TZHF4O27CcP+72WPn/k1zkgJ6WJfnIbk4jTsVAZHw==" + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz", + "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==" }, "@babel/template": { "version": "7.10.4", @@ -1070,9 +911,9 @@ } }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", + "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", @@ -1095,9 +936,9 @@ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", + "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", @@ -1140,13 +981,13 @@ } }, "@babel/generator": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.0.tgz", - "integrity": "sha512-fEm3Uzw7Mc9Xi//qU20cBKatTfs2aOtKqmvy/Vm7RkJEGFQ4xc9myCfbXxqK//ZS8MR/ciOHw6meGASJuKmDfQ==", + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.5.tgz", + "integrity": "sha512-9UqHWJ4IwRTy4l0o8gq2ef8ws8UPzvtMkVKjTLAiRmza9p9V6Z+OfuNd9fB1j5Q67F+dVJtPC2sZXI8NM9br4g==", "requires": { - "@babel/types": "^7.11.0", + "@babel/types": "^7.11.5", "jsesc": "^2.5.1", - "source-map": "^0.5.0" + "source-map": "^0.6.1" } }, "@babel/helper-function-name": { @@ -1191,9 +1032,9 @@ } }, "@babel/parser": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.0.tgz", - "integrity": "sha512-qvRvi4oI8xii8NllyEc4MDJjuZiNaRzyb7Y7lup1NqJV8TZHF4O27CcP+72WPn/k1zkgJ6WJfnIbk4jTsVAZHw==" + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz", + "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==" }, "@babel/template": { "version": "7.10.4", @@ -1206,25 +1047,25 @@ } }, "@babel/traverse": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.0.tgz", - "integrity": "sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg==", + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.5.tgz", + "integrity": "sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ==", "requires": { "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.11.0", + "@babel/generator": "^7.11.5", "@babel/helper-function-name": "^7.10.4", "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.11.0", - "@babel/types": "^7.11.0", + "@babel/parser": "^7.11.5", + "@babel/types": "^7.11.5", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.19" } }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", + "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", @@ -1243,6 +1084,11 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" } } }, @@ -1265,13 +1111,13 @@ } }, "@babel/generator": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.5.tgz", - "integrity": "sha512-9UqHWJ4IwRTy4l0o8gq2ef8ws8UPzvtMkVKjTLAiRmza9p9V6Z+OfuNd9fB1j5Q67F+dVJtPC2sZXI8NM9br4g==", + "version": "7.11.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.6.tgz", + "integrity": "sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA==", "requires": { "@babel/types": "^7.11.5", "jsesc": "^2.5.1", - "source-map": "^0.6.1" + "source-map": "^0.5.0" } }, "@babel/helper-function-name": { @@ -1615,13 +1461,6 @@ "integrity": "sha512-oSAEz1YkBCAKr5Yiq8/BNtvSAPwkp/IyUnwZogd8p+F0RuYQQrLeRUzIQhueQTTBy/F+a40uS7OFKxnkRvmvFQ==", "requires": { "@babel/helper-plugin-utils": "^7.10.4" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" - } } }, "@babel/plugin-transform-arrow-functions": { @@ -1651,9 +1490,9 @@ } }, "@babel/plugin-transform-block-scoping": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.10.5.tgz", - "integrity": "sha512-6Ycw3hjpQti0qssQcA6AMSFDHeNJ++R6dIMnpRqUjFeBBTmTDPa8zgF90OVfTvAo11mXZTlVUViY1g8ffrURLg==", + "version": "7.11.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.11.1.tgz", + "integrity": "sha512-00dYeDE0EVEHuuM+26+0w/SCL0BH2Qy7LwHuI4Hi4MH5gkC8/AqMN5uWFJIsoXZrAphiMm1iXzBw6L2T+eA0ew==", "requires": { "@babel/helper-plugin-utils": "^7.10.4" } @@ -1723,9 +1562,9 @@ } }, "@babel/parser": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.0.tgz", - "integrity": "sha512-qvRvi4oI8xii8NllyEc4MDJjuZiNaRzyb7Y7lup1NqJV8TZHF4O27CcP+72WPn/k1zkgJ6WJfnIbk4jTsVAZHw==" + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz", + "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==" }, "@babel/template": { "version": "7.10.4", @@ -1738,9 +1577,9 @@ } }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", + "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", @@ -1850,9 +1689,9 @@ } }, "@babel/parser": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.0.tgz", - "integrity": "sha512-qvRvi4oI8xii8NllyEc4MDJjuZiNaRzyb7Y7lup1NqJV8TZHF4O27CcP+72WPn/k1zkgJ6WJfnIbk4jTsVAZHw==" + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz", + "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==" }, "@babel/template": { "version": "7.10.4", @@ -1865,9 +1704,9 @@ } }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", + "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", @@ -1981,9 +1820,9 @@ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", + "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", @@ -2060,20 +1899,13 @@ } }, "@babel/plugin-transform-typescript": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.10.4.tgz", - "integrity": "sha512-3WpXIKDJl/MHoAN0fNkSr7iHdUMHZoppXjf2HJ9/ed5Xht5wNIsXllJXdityKOxeA3Z8heYRb1D3p2H5rfCdPw==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.11.0.tgz", + "integrity": "sha512-edJsNzTtvb3MaXQwj8403B7mZoGu9ElDJQZOKjGUnvilquxBA3IQoEIOvkX/1O8xfAsnHS/oQhe2w/IXrr+w0w==", "requires": { - "@babel/helper-create-class-features-plugin": "^7.10.4", + "@babel/helper-create-class-features-plugin": "^7.10.5", "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-typescript": "^7.10.4" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" - } } }, "@babel/plugin-transform-unicode-escapes": { @@ -2115,9 +1947,9 @@ } }, "@babel/preset-env": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.11.0.tgz", - "integrity": "sha512-2u1/k7rG/gTh02dylX2kL3S0IJNF+J6bfDSp4DI2Ma8QN6Y9x9pmAax59fsCk6QUQG0yqH47yJWA+u1I1LccAg==", + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.11.5.tgz", + "integrity": "sha512-kXqmW1jVcnB2cdueV+fyBM8estd5mlNfaQi6lwLgRwCby4edpavgbFhiBNjmWA3JpB/yZGSISa7Srf+TwxDQoA==", "requires": { "@babel/compat-data": "^7.11.0", "@babel/helper-compilation-targets": "^7.10.4", @@ -2181,7 +2013,7 @@ "@babel/plugin-transform-unicode-escapes": "^7.10.4", "@babel/plugin-transform-unicode-regex": "^7.10.4", "@babel/preset-modules": "^0.1.3", - "@babel/types": "^7.11.0", + "@babel/types": "^7.11.5", "browserslist": "^4.12.0", "core-js-compat": "^3.6.2", "invariant": "^2.2.2", @@ -2195,9 +2027,9 @@ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", + "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", @@ -2207,9 +2039,9 @@ } }, "@babel/preset-modules": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.3.tgz", - "integrity": "sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg==", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.4.tgz", + "integrity": "sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg==", "requires": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", @@ -2225,19 +2057,12 @@ "requires": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-transform-typescript": "^7.10.4" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" - } } }, "@babel/runtime": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.0.tgz", - "integrity": "sha512-qArkXsjJq7H+T86WrIFV0Fnu/tNOkZ4cgXmjkzAu3b/58D5mFIO8JH/y77t7C9q0OdDRdh9s7Ue5GasYssxtXw==", + "version": "7.11.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz", + "integrity": "sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -2297,6 +2122,85 @@ "to-fast-properties": "^2.0.0" } }, + "@eslint/eslintrc": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.1.3.tgz", + "integrity": "sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "lodash": "^4.17.19", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "acorn": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz", + "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==", + "dev": true + }, + "ajv": { + "version": "6.12.4", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz", + "integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + }, + "espree": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.0.tgz", + "integrity": "sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==", + "dev": true, + "requires": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.2.0", + "eslint-visitor-keys": "^1.3.0" + } + }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, "@nextcloud/auth": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@nextcloud/auth/-/auth-1.3.0.tgz", @@ -2308,16 +2212,16 @@ } }, "@nextcloud/axios": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@nextcloud/axios/-/axios-1.3.3.tgz", - "integrity": "sha512-kCGN+0QqrCzTEDsCTpHY2Ze5PGncspC37OOMOYejmGxp+/a2FTg92yJoI5Xhk2y/xJciwVpX1NUWatxBfxAlWw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@nextcloud/axios/-/axios-1.4.0.tgz", + "integrity": "sha512-7ePHUve3++aB0Ma+lc68B/wRn09FmrPFaFKpdUygIxak89lRvixqLc+98AdLtGPQegof+dKTIwwnuNTy0E95HA==", "requires": { "@babel/cli": "^7.8.4", "@babel/core": "^7.9.0", "@babel/preset-env": "^7.9.0", "@babel/preset-typescript": "^7.9.0", "@nextcloud/auth": "^1.2.2", - "axios": "^0.19.2", + "axios": "^0.20.0", "core-js": "^3.6.4" } }, @@ -2436,9 +2340,9 @@ } }, "@nextcloud/router": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@nextcloud/router/-/router-1.1.0.tgz", - "integrity": "sha512-iPHpMG9kajw8D+niR4x/d8s/R9RyUNveDsNURgcZryIjIXhAzSZZra55+Y3yInDmLhCFwboj9ZcC/2S6CzoKYA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@nextcloud/router/-/router-1.2.0.tgz", + "integrity": "sha512-kn9QsL9LuhkIMaSSgdiqRL3SZ6PatuAjXUiyq343BbSnI99Oc5eJH8kU6cT2AHije7wKy/tK8Xe3VQuVO32SZQ==", "requires": { "core-js": "^3.6.4" } @@ -2452,14 +2356,14 @@ } }, "@nextcloud/vue": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@nextcloud/vue/-/vue-2.6.1.tgz", - "integrity": "sha512-8orTDmtilZuFQJ67zqvqzmA20oAOojkfNpq4ERP8rsiAL0eHTUzMQC27dEfDuNq/GuloNsr/RDsimHSCq03DxA==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/@nextcloud/vue/-/vue-2.6.4.tgz", + "integrity": "sha512-M5s2mD40KxYFHERZ+n9Yk3GkDXaKcXXxqOtlcgNNcPltcT7IvPKpm8+a0MaiYlQKBhDLkwRyRH+RHxcJKZjbIg==", "requires": { "@nextcloud/auth": "^1.2.3", "@nextcloud/axios": "^1.3.2", "@nextcloud/capabilities": "^1.0.2", - "@nextcloud/dialogs": "^1.4.0", + "@nextcloud/dialogs": "^2.0.1", "@nextcloud/event-bus": "^1.1.4", "@nextcloud/l10n": "^1.2.3", "@nextcloud/router": "^1.0.2", @@ -2479,14 +2383,10 @@ "vue2-datepicker": "^3.6.2" }, "dependencies": { - "@nextcloud/dialogs": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@nextcloud/dialogs/-/dialogs-1.4.0.tgz", - "integrity": "sha512-Rx4x+al/sy+vXu2p3qvEuVeeUDm5JVwa84S21Hxa+pDV3Pd93E2dJGWlZ6h++5fSXbee1sDX9t957B20kYiP3Q==", - "requires": { - "core-js": "^3.6.4", - "toastify-js": "^1.7.0" - } + "v-click-outside": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/v-click-outside/-/v-click-outside-3.1.1.tgz", + "integrity": "sha512-JJCdOwsJw77bzO37fkQdWX9OnjgLBGQNOM2SVmIdDdUi+n2xc/06dQdctxySIhetd35IGbs3NGU11pkKnBuhJg==" } } }, @@ -2534,9 +2434,9 @@ } }, "@stylelint/postcss-css-in-js": { - "version": "0.37.1", - "resolved": "https://registry.npmjs.org/@stylelint/postcss-css-in-js/-/postcss-css-in-js-0.37.1.tgz", - "integrity": "sha512-UMf2Rni3JGKi3ZwYRGMYJ5ipOA5ENJSKMtYA/pE1ZLURwdh7B5+z2r73RmWvub+N0UuH1Lo+TGfCgYwPvqpXNw==", + "version": "0.37.2", + "resolved": "https://registry.npmjs.org/@stylelint/postcss-css-in-js/-/postcss-css-in-js-0.37.2.tgz", + "integrity": "sha512-nEhsFoJurt8oUmieT8qy4nk81WRHmJynmVwn/Vts08PL9fhgIsMhk1GId5yAN643OzqEEb5S/6At2TZW7pqPDA==", "dev": true, "requires": { "@babel/core": ">=7.9.0" @@ -3010,9 +2910,9 @@ } }, "arrify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", - "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "dev": true }, "asn1": { @@ -3111,26 +3011,20 @@ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" }, "autoprefixer": { - "version": "9.8.1", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.1.tgz", - "integrity": "sha512-zDw9+mkCdWZHloBIGrOgMq1tTUed4qy6ZgNAe2Ze2xERZA7CyTgW5Bw3XZbwSeJe8lfDHZIkw8Hwd/6hI3p0NQ==", + "version": "9.8.6", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz", + "integrity": "sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg==", "dev": true, "requires": { "browserslist": "^4.12.0", - "caniuse-lite": "^1.0.30001084", - "kleur": "^4.0.1", + "caniuse-lite": "^1.0.30001109", + "colorette": "^1.2.1", "normalize-range": "^0.1.2", "num2fraction": "^1.2.2", "postcss": "^7.0.32", "postcss-value-parser": "^4.1.0" }, "dependencies": { - "caniuse-lite": { - "version": "1.0.30001084", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001084.tgz", - "integrity": "sha512-ftdc5oGmhEbLUuMZ/Qp3mOpzfZLCxPYKcvGv6v2dJJ+8EdqcvZRbAGOiLmkM/PV1QGta/uwBs8/nCl6sokDW6w==", - "dev": true - }, "postcss": { "version": "7.0.32", "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz", @@ -3172,11 +3066,11 @@ "dev": true }, "axios": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", - "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.20.0.tgz", + "integrity": "sha512-ANA4rr2BDcmmAQLOKft2fufrtuvlqR+cXNNinUmvfeSNCOF98PZL+7M/v1zIdGo7OLjEA9J2gXJL+j4zGsl0bA==", "requires": { - "follow-redirects": "1.5.10" + "follow-redirects": "^1.10.0" } }, "babel-eslint": { @@ -3475,14 +3369,14 @@ } }, "browserslist": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.12.0.tgz", - "integrity": "sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.0.tgz", + "integrity": "sha512-pUsXKAF2lVwhmtpeA3LJrZ76jXuusrNyhduuQs7CDFf9foT4Y38aQOserd2lMe5DSSrjf3fx34oHwryuvxAUgQ==", "requires": { - "caniuse-lite": "^1.0.30001043", - "electron-to-chromium": "^1.3.413", - "node-releases": "^1.1.53", - "pkg-up": "^2.0.0" + "caniuse-lite": "^1.0.30001111", + "electron-to-chromium": "^1.3.523", + "escalade": "^3.0.2", + "node-releases": "^1.1.60" } }, "buffer": { @@ -3638,9 +3532,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001066", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001066.tgz", - "integrity": "sha512-Gfj/WAastBtfxLws0RCh2sDbTK/8rJuSeZMecrSkNGYxPcv7EzblmDGfWQCFEQcSqYE2BRgQiJh8HOD07N5hIw==" + "version": "1.0.30001120", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001120.tgz", + "integrity": "sha512-JBP68okZs1X8D7MQTY602jxMYBmXEKOFkzTBaNSkubooMPFOAv2TXWaKle7qgHpjLDhUzA/TMT0qsNleVyXGUQ==" }, "caseless": { "version": "0.12.0", @@ -3870,6 +3764,12 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, + "colorette": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz", + "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==", + "dev": true + }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -3999,27 +3899,27 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "cosmiconfig": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", - "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", + "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", "dev": true, "requires": { "@types/parse-json": "^4.0.0", - "import-fresh": "^3.1.0", + "import-fresh": "^3.2.1", "parse-json": "^5.0.0", "path-type": "^4.0.0", - "yaml": "^1.7.2" + "yaml": "^1.10.0" }, "dependencies": { "parse-json": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", - "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", + "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1", + "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, @@ -4121,30 +4021,29 @@ } }, "css-loader": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.6.0.tgz", - "integrity": "sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-4.3.0.tgz", + "integrity": "sha512-rdezjCjScIrsL8BSYszgT4s476IcNKt6yX69t0pHjJVnPUTDpn4WfIpDQTN3wCJvUvfsz/mFjuGOekf3PY3NUg==", "dev": true, "requires": { - "camelcase": "^5.3.1", + "camelcase": "^6.0.0", "cssesc": "^3.0.0", "icss-utils": "^4.1.1", - "loader-utils": "^1.2.3", - "normalize-path": "^3.0.0", + "loader-utils": "^2.0.0", "postcss": "^7.0.32", "postcss-modules-extract-imports": "^2.0.0", - "postcss-modules-local-by-default": "^3.0.2", + "postcss-modules-local-by-default": "^3.0.3", "postcss-modules-scope": "^2.2.0", "postcss-modules-values": "^3.0.0", "postcss-value-parser": "^4.1.0", - "schema-utils": "^2.7.0", - "semver": "^6.3.0" + "schema-utils": "^2.7.1", + "semver": "^7.3.2" }, "dependencies": { "ajv": { - "version": "6.12.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", - "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", + "version": "6.12.4", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz", + "integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -4153,6 +4052,29 @@ "uri-js": "^4.2.2" } }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true + }, + "camelcase": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.0.0.tgz", + "integrity": "sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w==", + "dev": true + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, "postcss": { "version": "7.0.32", "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz", @@ -4165,20 +4087,20 @@ } }, "schema-utils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", - "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", "dev": true, "requires": { - "@types/json-schema": "^7.0.4", - "ajv": "^6.12.2", - "ajv-keywords": "^3.4.1" + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" } }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", "dev": true }, "source-map": { @@ -4485,9 +4407,9 @@ } }, "electron-to-chromium": { - "version": "1.3.453", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.453.tgz", - "integrity": "sha512-IQbCfjJR0NDDn/+vojTlq7fPSREcALtF8M1n01gw7nQghCtfFYrJ2dfhsp8APr8bANoFC8vRTFVXMOGpT0eetw==" + "version": "1.3.555", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.555.tgz", + "integrity": "sha512-/55x3nF2feXFZ5tdGUOr00TxnUjUgdxhrn+eCJ1FAcoAt+cKQTjQkUC5XF4frMWE1R5sjHk+JueuBalimfe5Pg==" }, "elliptic": { "version": "6.5.3", @@ -4644,18 +4566,24 @@ "is-symbol": "^1.0.2" } }, + "escalade": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.0.2.tgz", + "integrity": "sha512-gPYAU37hYCUhW5euPeR+Y74F7BL+IBsV93j5cvGriSaD1aG6MGsqsV1yamRdrWrb2j3aiZvb0X+UBOWpx3JWtQ==" + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "eslint": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.7.0.tgz", - "integrity": "sha512-1KUxLzos0ZVsyL81PnRN335nDtQ8/vZUD6uMtWbF+5zDtjKcsklIi78XoE0MVL93QvWTu+E5y44VyyCsOMBrIg==", + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.8.1.tgz", + "integrity": "sha512-/2rX2pfhyUG0y+A123d0ccXtMm7DV7sH1m3lk9nk2DZ2LReq39FXHueR9xZwshE5MdfSf0xunSaMWRqyIA6M1w==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", + "@eslint/eslintrc": "^0.1.3", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -4665,7 +4593,7 @@ "eslint-scope": "^5.1.0", "eslint-utils": "^2.1.0", "eslint-visitor-keys": "^1.3.0", - "espree": "^7.2.0", + "espree": "^7.3.0", "esquery": "^1.2.0", "esutils": "^2.0.2", "file-entry-cache": "^5.0.1", @@ -4760,12 +4688,12 @@ "dev": true }, "espree": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.2.0.tgz", - "integrity": "sha512-H+cQ3+3JYRMEIOl87e7QdHX70ocly5iW4+dttuR8iYSPr/hXKFb+7dBsZ7+u1adC4VrnPlTkv0+OwuPnDop19g==", + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.0.tgz", + "integrity": "sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==", "dev": true, "requires": { - "acorn": "^7.3.1", + "acorn": "^7.4.0", "acorn-jsx": "^5.2.0", "eslint-visitor-keys": "^1.3.0" } @@ -4816,9 +4744,9 @@ } }, "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { "has-flag": "^4.0.0" @@ -5423,6 +5351,12 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "fastest-levenshtein": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", + "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", + "dev": true + }, "fastq": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.8.0.tgz", @@ -5448,15 +5382,33 @@ } }, "file-loader": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.0.0.tgz", - "integrity": "sha512-/aMOAYEFXDdjG0wytpTL5YQLfZnnTmLNjn+AIrJ/6HVnTfDqLsVKUUwkDf4I4kgex36BvjuXEn/TX9B/1ESyqQ==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.1.0.tgz", + "integrity": "sha512-26qPdHyTsArQ6gU4P1HJbAbnFTyT2r0pG7czh1GFAd9TZbj0n94wWbupgixZH/ET/meqi2/5+F7DhW4OAXD+Lg==", "dev": true, "requires": { "loader-utils": "^2.0.0", - "schema-utils": "^2.6.5" + "schema-utils": "^2.7.1" }, "dependencies": { + "ajv": { + "version": "6.12.4", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz", + "integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true + }, "loader-utils": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", @@ -5467,6 +5419,17 @@ "emojis-list": "^3.0.0", "json5": "^2.1.2" } + }, + "schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + } } } }, @@ -5512,6 +5475,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, "requires": { "locate-path": "^2.0.0" } @@ -5556,22 +5520,9 @@ } }, "follow-redirects": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", - "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", - "requires": { - "debug": "=3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - } - } + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz", + "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==" }, "for-in": { "version": "1.0.2", @@ -7077,6 +7028,12 @@ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "dev": true }, + "json-parse-even-better-errors": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.0.tgz", + "integrity": "sha512-o3aP+RsWDJZayj1SbHNQAI8x0v3T3SKiGoZlNYfbUP1S3omJQ6i9CnqADqkSPaOAxwua4/1YWx5CM7oiChJt2Q==", + "dev": true + }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", @@ -7135,16 +7092,10 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" }, - "kleur": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.0.1.tgz", - "integrity": "sha512-Qs6SqCLm63rd0kNVh+wO4XsWLU6kgfwwaPYsLiClWf0Tewkzsa6MvB21bespb8cz+ANS+2t3So1ge3gintzhlw==", - "dev": true - }, "klona": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/klona/-/klona-1.1.2.tgz", - "integrity": "sha512-xf88rTeHiXk+XE2Vhi6yj8Wm3gMZrygGdKjJqN8HkV+PwF/t50/LdAKHoHpPcxFAlmQszTZ1CugrK25S7qDRLA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.3.tgz", + "integrity": "sha512-CgPOT3ZadDpXxKcfV56lEQ9OQSZ42Mk26gnozI+uN/k39vzD8toUhRQoqsX0m9Q3eMPEfsLWmtyUpK/yqST4yg==", "dev": true }, "known-css-properties": { @@ -7239,6 +7190,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, "requires": { "p-locate": "^2.0.0", "path-exists": "^3.0.0" @@ -7310,9 +7262,9 @@ "dev": true }, "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { "has-flag": "^4.0.0" @@ -7671,12 +7623,6 @@ "kind-of": "^6.0.3" }, "dependencies": { - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true - }, "is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", @@ -7943,9 +7889,9 @@ } }, "node-releases": { - "version": "1.1.57", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.57.tgz", - "integrity": "sha512-ZQmnWS7adi61A9JsllJ2gdj2PauElcjnOwTp2O011iGzoakTxUsDGSe+6vD7wXbKdqhSFymC0OSx35aAMhrSdw==" + "version": "1.1.60", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.60.tgz", + "integrity": "sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA==" }, "node-sass": { "version": "4.14.1", @@ -8057,7 +8003,8 @@ "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "optional": true }, "normalize-range": { "version": "0.1.2", @@ -8245,6 +8192,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, "requires": { "p-try": "^1.0.0" } @@ -8253,6 +8201,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, "requires": { "p-limit": "^1.1.0" } @@ -8269,7 +8218,8 @@ "p-try": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true }, "pako": { "version": "1.0.11", @@ -8360,7 +8310,8 @@ "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true }, "path-is-absolute": { "version": "1.0.1", @@ -8494,14 +8445,6 @@ } } }, - "pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", - "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", - "requires": { - "find-up": "^2.1.0" - } - }, "popper.js": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", @@ -8574,15 +8517,43 @@ } }, "postcss-modules-local-by-default": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.2.tgz", - "integrity": "sha512-jM/V8eqM4oJ/22j0gx4jrp63GSvDH6v86OqyTHHUvk4/k1vceipZsaymiZ5PvocqZOl5SFHiFJqjs3la0wnfIQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz", + "integrity": "sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw==", "dev": true, "requires": { "icss-utils": "^4.1.1", - "postcss": "^7.0.16", + "postcss": "^7.0.32", "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.0.0" + "postcss-value-parser": "^4.1.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.32", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz", + "integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "postcss-modules-scope": { @@ -8605,29 +8576,6 @@ "postcss": "^7.0.6" } }, - "postcss-reporter": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-reporter/-/postcss-reporter-6.0.1.tgz", - "integrity": "sha512-LpmQjfRWyabc+fRygxZjpRxfhRf9u/fdlKf4VHG4TSPbV2XNsuISzYW1KL+1aQzx53CAppa1bKG4APIB/DOXXw==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "lodash": "^4.17.11", - "log-symbols": "^2.2.0", - "postcss": "^7.0.7" - }, - "dependencies": { - "log-symbols": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", - "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", - "dev": true, - "requires": { - "chalk": "^2.0.1" - } - } - } - }, "postcss-resolve-nested-selector": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", @@ -8974,9 +8922,9 @@ } }, "remark": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/remark/-/remark-12.0.0.tgz", - "integrity": "sha512-oX4lMIS0csgk8AEbzY0h2jdR0ngiCHOpwwpxjmRa5TqAkeknY+tkhjRJGZqnCmvyuWh55/0SW5WY3R3nn3PH9A==", + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/remark/-/remark-12.0.1.tgz", + "integrity": "sha512-gS7HDonkdIaHmmP/+shCPejCEEW+liMp/t/QwmF0Xt47Rpuhl32lLtDV1uKWvGoq+kxr5jSgg5oAIpGuyULjUw==", "dev": true, "requires": { "remark-parse": "^8.0.0", @@ -8985,9 +8933,9 @@ } }, "remark-parse": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-8.0.2.tgz", - "integrity": "sha512-eMI6kMRjsAGpMXXBAywJwiwAse+KNpmt+BK55Oofy4KvBZEqUDj6mWbGLJZrujoPIPPxDXzn3T9baRlpsm2jnQ==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-8.0.3.tgz", + "integrity": "sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q==", "dev": true, "requires": { "ccount": "^1.0.0", @@ -9009,9 +8957,9 @@ } }, "remark-stringify": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-8.1.0.tgz", - "integrity": "sha512-FSPZv1ds76oAZjurhhuV5qXSUSoz6QRPuwYK38S41sLHwg4oB7ejnmZshj7qwjgYLf93kdz6BOX9j5aidNE7rA==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-8.1.1.tgz", + "integrity": "sha512-q4EyPZT3PcA3Eq7vPpT6bIdokXzFGp9i85igjmhRyXWmPs0Y6/d2FYwUNotKAWyLch7g0ASZJn/KHHcHZQ163A==", "dev": true, "requires": { "ccount": "^1.0.0", @@ -9265,22 +9213,22 @@ } }, "sass-loader": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-9.0.3.tgz", - "integrity": "sha512-fOwsP98ac1VMme+V3+o0HaaMHp8Q/C9P+MUazLFVi3Jl7ORGHQXL1XeRZt3zLSGZQQPC8xE42Y2WptItvGjDQg==", + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-10.0.2.tgz", + "integrity": "sha512-wV6NDUVB8/iEYMalV/+139+vl2LaRFlZGEd5/xmdcdzQcgmis+npyco6NsDTVOlNA3y2NV9Gcz+vHyFMIT+ffg==", "dev": true, "requires": { - "klona": "^1.1.2", + "klona": "^2.0.3", "loader-utils": "^2.0.0", "neo-async": "^2.6.2", - "schema-utils": "^2.7.0", + "schema-utils": "^2.7.1", "semver": "^7.3.2" }, "dependencies": { "ajv": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", - "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", + "version": "6.12.4", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz", + "integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -9289,6 +9237,12 @@ "uri-js": "^4.2.2" } }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true + }, "loader-utils": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", @@ -9307,14 +9261,14 @@ "dev": true }, "schema-utils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", - "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", "dev": true, "requires": { - "@types/json-schema": "^7.0.4", - "ajv": "^6.12.2", - "ajv-keywords": "^3.4.1" + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" } }, "semver": { @@ -9884,19 +9838,21 @@ "dev": true }, "stylelint": { - "version": "13.6.1", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-13.6.1.tgz", - "integrity": "sha512-XyvKyNE7eyrqkuZ85Citd/Uv3ljGiuYHC6UiztTR6sWS9rza8j3UeQv/eGcQS9NZz/imiC4GKdk1EVL3wst5vw==", + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-13.7.0.tgz", + "integrity": "sha512-1wStd4zVetnlHO98VjcHQbjSDmvcA39smkZQMct2cf+hom40H0xlQNdzzbswoG/jGBh61/Ue9m7Lu99PY51O6A==", "dev": true, "requires": { - "@stylelint/postcss-css-in-js": "^0.37.1", + "@stylelint/postcss-css-in-js": "^0.37.2", "@stylelint/postcss-markdown": "^0.36.1", - "autoprefixer": "^9.8.0", + "autoprefixer": "^9.8.6", "balanced-match": "^1.0.0", "chalk": "^4.1.0", - "cosmiconfig": "^6.0.0", + "cosmiconfig": "^7.0.0", "debug": "^4.1.1", "execall": "^2.0.0", + "fast-glob": "^3.2.4", + "fastest-levenshtein": "^1.0.12", "file-entry-cache": "^5.0.1", "get-stdin": "^8.0.0", "global-modules": "^2.0.0", @@ -9907,18 +9863,16 @@ "import-lazy": "^4.0.0", "imurmurhash": "^0.1.4", "known-css-properties": "^0.19.0", - "leven": "^3.1.0", - "lodash": "^4.17.15", + "lodash": "^4.17.20", "log-symbols": "^4.0.0", "mathml-tag-names": "^2.1.3", - "meow": "^7.0.1", + "meow": "^7.1.1", "micromatch": "^4.0.2", "normalize-selector": "^0.2.0", "postcss": "^7.0.32", "postcss-html": "^0.36.0", "postcss-less": "^3.1.4", "postcss-media-query-parser": "^0.2.3", - "postcss-reporter": "^6.0.1", "postcss-resolve-nested-selector": "^0.1.1", "postcss-safe-parser": "^4.0.2", "postcss-sass": "^0.4.4", @@ -9934,11 +9888,23 @@ "style-search": "^0.1.0", "sugarss": "^2.0.0", "svg-tags": "^1.0.0", - "table": "^5.4.6", + "table": "^6.0.1", "v8-compile-cache": "^2.1.1", "write-file-atomic": "^3.0.3" }, "dependencies": { + "ajv": { + "version": "6.12.4", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz", + "integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, "ansi-styles": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", @@ -9949,6 +9915,12 @@ "color-convert": "^2.0.1" } }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, "braces": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", @@ -9958,12 +9930,6 @@ "fill-range": "^7.0.1" } }, - "camelcase": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.0.0.tgz", - "integrity": "sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w==", - "dev": true - }, "camelcase-keys": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", @@ -9973,14 +9939,6 @@ "camelcase": "^5.3.1", "map-obj": "^4.0.0", "quick-lru": "^4.0.1" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - } } }, "chalk": { @@ -10082,18 +10040,16 @@ "dev": true }, "meow": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/meow/-/meow-7.0.1.tgz", - "integrity": "sha512-tBKIQqVrAHqwit0vfuFPY3LlzJYkEOFyKa3bPgxzNl6q/RtN8KQ+ALYEASYuFayzSAsjlhXj/JZ10rH85Q6TUw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-7.1.1.tgz", + "integrity": "sha512-GWHvA5QOcS412WCo8vwKDlTelGLsCGBVevQB5Kva961rmNfun0PCbv5+xta2kUMFJyR8/oWnn7ddeKdosbAPbA==", "dev": true, "requires": { "@types/minimist": "^1.2.0", - "arrify": "^2.0.1", - "camelcase": "^6.0.0", "camelcase-keys": "^6.2.2", "decamelize-keys": "^1.1.0", "hard-rejection": "^2.1.0", - "minimist-options": "^4.0.2", + "minimist-options": "4.1.0", "normalize-package-data": "^2.5.0", "read-pkg-up": "^7.0.1", "redent": "^3.0.0", @@ -10143,14 +10099,14 @@ "dev": true }, "parse-json": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", - "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", + "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1", + "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, @@ -10295,6 +10251,17 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -10320,14 +10287,26 @@ } }, "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { "has-flag": "^4.0.0" } }, + "table": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.0.1.tgz", + "integrity": "sha512-fmr6168splcy/3XIvhSm5w6hYYOqyr3plAsd7OqoerzyoMnIpoxYuwrpdO2Cm22dh6KCnvirvigPrFZp+tdWFA==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "lodash": "^4.17.20", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.0" + } + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -10349,12 +10328,6 @@ "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", "dev": true }, - "v8-compile-cache": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", - "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", - "dev": true - }, "yargs-parser": { "version": "18.1.3", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", @@ -10363,14 +10336,6 @@ "requires": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - } } } } @@ -10653,9 +10618,9 @@ "dev": true }, "terser": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.1.0.tgz", - "integrity": "sha512-pwC1Jbzahz1ZPU87NQ8B3g5pKbhyJSiHih4gLH6WZiPU8mmS1IlGbB0A2Nuvkj/LCNsgIKctg6GkYwWCeTvXZQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.0.0.tgz", + "integrity": "sha512-olH2DwGINoSuEpSGd+BsPuAQaA3OrHnHnFL/rDB2TVNc3srUbz/rq/j2BlF4zDXI+JqAvGr86bIm1R2cJgZ3FA==", "dev": true, "requires": { "commander": "^2.20.0", @@ -10912,9 +10877,9 @@ "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==" }, "unified": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/unified/-/unified-9.0.0.tgz", - "integrity": "sha512-ssFo33gljU3PdlWLjNp15Inqb77d6JnJSfyplGJPT/a+fNRNyCBeveBAYJdO5khKdF6WVHa/yYCC7Xl6BDwZUQ==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.0.tgz", + "integrity": "sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg==", "dev": true, "requires": { "bail": "^1.0.0", @@ -11002,9 +10967,9 @@ } }, "unist-util-visit": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.2.tgz", - "integrity": "sha512-HoHNhGnKj6y+Sq+7ASo2zpVdfdRifhTgX2KTU3B/sO/TTlZchp7E3S4vjRzDJ7L60KmrCPsQkVK3lEF3cz36XQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz", + "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", "dev": true, "requires": { "@types/unist": "^2.0.0", @@ -11013,9 +10978,9 @@ } }, "unist-util-visit-parents": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.0.2.tgz", - "integrity": "sha512-yJEfuZtzFpQmg1OSCyS9M5NJRrln/9FbYosH3iW0MG402QbdbaB8ZESwUv9RO6nRfLAKvWcMxCwdLWOov36x/g==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.0.tgz", + "integrity": "sha512-0g4wbluTF93npyPrp/ymd3tCDTMnP0yo2akFD2FIBAYXq/Sga3lwaU1D8OYKbtpioaI6CkDcQ6fsMnmtzt7htw==", "dev": true, "requires": { "@types/unist": "^2.0.0", @@ -11102,6 +11067,30 @@ } } }, + "url-loader": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.0.tgz", + "integrity": "sha512-IzgAAIC8wRrg6NYkFIJY09vtktQcsvU8V6HhtQj9PTefbYImzLB1hufqo4m+RyM5N3mLx5BqJKccgxJS+W3kqw==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "mime-types": "^2.1.26", + "schema-utils": "^2.6.5" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + } + } + }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -11136,9 +11125,9 @@ "dev": true }, "v-click-outside": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v-click-outside/-/v-click-outside-3.0.1.tgz", - "integrity": "sha512-FITcAM0R3JEPUSGiO7hfhKDODZHkOQTk/FyI9mwxNcz6LbMbJhABhjevLI5VsU00PRksloQx8vmpFIqlAfX6nw==" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/v-click-outside/-/v-click-outside-3.1.1.tgz", + "integrity": "sha512-JJCdOwsJw77bzO37fkQdWX9OnjgLBGQNOM2SVmIdDdUi+n2xc/06dQdctxySIhetd35IGbs3NGU11pkKnBuhJg==" }, "v-tooltip": { "version": "2.0.3", @@ -11178,9 +11167,9 @@ } }, "vfile": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.1.1.tgz", - "integrity": "sha512-lRjkpyDGjVlBA7cDQhQ+gNcvB1BGaTHYuSOcY3S7OhDmBtnzX95FhtZZDecSTDm6aajFymyve6S5DN4ZHGezdQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.0.tgz", + "integrity": "sha512-a/alcwCvtuc8OX92rqqo7PflxiCgXRFjdyoGVuYV+qbgCb0GgZJRvIgCD4+U/Kl1yhaRsaTwksF88xbPyGsgpw==", "dev": true, "requires": { "@types/unist": "^2.0.0", @@ -11199,9 +11188,9 @@ } }, "vfile-location": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-3.0.1.tgz", - "integrity": "sha512-yYBO06eeN/Ki6Kh1QAkgzYpWT1d3Qln+ZCtSbJqFExPl1S3y2qqotJQXoh6qEvl/jDlgpUJolBn3PItVnnZRqQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-3.1.0.tgz", + "integrity": "sha512-FCZ4AN9xMcjFIG1oGmZKo61PjwJHRVA+0/tPUP2ul4uIwjGGndIxavEMRpWn5p4xwm/ZsdXp9YNygf1ZyE4x8g==", "dev": true }, "vfile-message": { @@ -11757,9 +11746,9 @@ } }, "webpack-merge": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.1.2.tgz", - "integrity": "sha512-/slG0Kh0OKTf0zxdFJlhQHzv8bU9gUYVK5DkBjB3i/yoc1Xx4ADG0KITGO5S/6cqn2Ug43+8VR6Sz8daA/c+5g==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.1.3.tgz", + "integrity": "sha512-fz/xHgfHyxq3uzGGrMryPnpPZ6x3vF1tHtws6vYwYX+8e6Dw+4U4r6rXuEPCqtSwmUIeD8hniWwFem+5FVLjzg==", "dev": true, "requires": { "clone-deep": "^4.0.1", diff --git a/package.json b/package.json index ed3c8750a..b5b65181e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "polls", "description": "Polls app for nextcloud", - "version": "1.5.0", + "version": "1.5.2", "authors": [ { "name": "Vinzenz Rosenkranz", @@ -36,16 +36,17 @@ }, "dependencies": { "@nextcloud/auth": "^1.3.0", - "@nextcloud/axios": "^1.3.3", + "@nextcloud/axios": "^1.4.0", "@nextcloud/dialogs": "^2.0.1", "@nextcloud/event-bus": "^1.1.4", "@nextcloud/moment": "^1.1.1", - "@nextcloud/router": "^1.1.0", - "@nextcloud/vue": "^2.6.1", + "@nextcloud/router": "^1.2.0", + "@nextcloud/vue": "^2.6.4", "core-js": "^3.6.5", + "linkifyjs": "^2.1.9", "lodash": "^4.17.20", "moment": "^2.27.0", - "v-click-outside": "^3.0.0", + "v-click-outside": "^3.1.1", "vue": "^2.6.12", "vue-clipboard2": "^0.3.1", "vue-fragment": "^1.5.1", @@ -62,14 +63,14 @@ "node": ">=10.0.0" }, "devDependencies": { - "@babel/core": "^7.11.4", - "@babel/preset-env": "^7.11.0", + "@babel/core": "^7.11.6", + "@babel/preset-env": "^7.11.5", "@nextcloud/eslint-plugin": "^1.4.0", "babel-eslint": "^10.0.3", "babel-loader": "^8.0.6", "cross-env": "^7.0.0", - "css-loader": "^3.6.0", - "eslint": "^7.7.0", + "css-loader": "^4.3.0", + "eslint": "^7.8.1", "eslint-config-standard": "^14.1.0", "eslint-loader": "^4.0.2", "eslint-plugin-import": "^2.22.0", @@ -77,18 +78,19 @@ "eslint-plugin-promise": "^4.2.1", "eslint-plugin-standard": "^4.0.1", "eslint-plugin-vue": "^6.2.2", - "file-loader": "^6.0.0", + "file-loader": "^6.1.0", "node-sass": "^4.14.1", - "sass-loader": "^9.0.3", - "stylelint": "^13.6.1", + "sass-loader": "^10.0.2", + "stylelint": "^13.7.0", "stylelint-config-recommended-scss": "^4.2.0", "stylelint-scss": "^3.18.0", "terser-webpack-plugin": "^4.1.0", + "url-loader": "^4.1.0", "vue-loader": "^15.9.3", "vue-style-loader": "^4.1.2", "vue-template-compiler": "^2.6.12", "webpack": "^4.44.1", "webpack-cli": "^3.3.12", - "webpack-merge": "^5.1.2" + "webpack-merge": "^5.1.3" } } diff --git a/src/js/App.vue b/src/js/App.vue index 6ce6cc6eb..fe9733737 100644 --- a/src/js/App.vue +++ b/src/js/App.vue @@ -21,19 +21,24 @@ --> - diff --git a/src/js/components/Base/OptionAddDate.vue b/src/js/components/Base/OptionAddDate.vue index 60b3440a3..a7c363c3b 100644 --- a/src/js/components/Base/OptionAddDate.vue +++ b/src/js/components/Base/OptionAddDate.vue @@ -75,7 +75,10 @@ export default { methods: { addOption(pollOptionText) { if (moment(pollOptionText).isValid()) { - this.$store.dispatch('poll/options/add', { pollOptionText: pollOptionText }) + this.$store.dispatch('poll/options/add', { + pollOptionText: pollOptionText, + timestamp: moment(pollOptionText).unix(), + }) } }, }, diff --git a/src/js/components/Base/OptionCloneDate.vue b/src/js/components/Base/OptionCloneDate.vue index 39db64322..08399406c 100644 --- a/src/js/components/Base/OptionCloneDate.vue +++ b/src/js/components/Base/OptionCloneDate.vue @@ -47,6 +47,7 @@ diff --git a/src/js/components/Base/PersonalLink.vue b/src/js/components/Base/PersonalLink.vue new file mode 100644 index 000000000..d78e4fb38 --- /dev/null +++ b/src/js/components/Base/PersonalLink.vue @@ -0,0 +1,92 @@ + + + + + diff --git a/src/js/components/Base/PollInformation.vue b/src/js/components/Base/PollInformation.vue index d80d1eb73..7d6040f07 100644 --- a/src/js/components/Base/PollInformation.vue +++ b/src/js/components/Base/PollInformation.vue @@ -33,11 +33,13 @@ {{ t('polls', 'You can place your vote until {dateString}.', { dateString: dateExpiryString }) }} - {{ t('polls', 'The names of other participants are hidden, as this is an anonymous poll. ') }} + {{ t('polls', 'This is an anonymous poll. Except to the poll owner, participants names are hidden.') }} {{ t('polls', 'Results are hidden. ') }} {{ t('polls', 'They will be revealed after the poll is expired. ') }} + + {{ t('polls', 'The used time zone is {timeZone}. ', { timeZone: currentTimeZone }) }} @@ -72,6 +74,11 @@ export default { dateExpiryString() { return moment.unix(this.poll.expire).format('LLLL') }, + + currentTimeZone() { + return Intl.DateTimeFormat().resolvedOptions().timeZone + }, + }, } diff --git a/src/js/components/Base/PublicRegisterModal.vue b/src/js/components/Base/PublicRegisterModal.vue new file mode 100644 index 000000000..6f780c938 --- /dev/null +++ b/src/js/components/Base/PublicRegisterModal.vue @@ -0,0 +1,269 @@ + + + + + + + diff --git a/src/js/components/Base/UserItem.vue b/src/js/components/Base/UserItem.vue index babeadb96..511560e97 100644 --- a/src/js/components/Base/UserItem.vue +++ b/src/js/components/Base/UserItem.vue @@ -25,8 +25,8 @@ @@ -41,6 +41,7 @@ + + diff --git a/src/js/components/Calendar/CalendarPeek.vue b/src/js/components/Calendar/CalendarPeek.vue new file mode 100644 index 000000000..a3e40e417 --- /dev/null +++ b/src/js/components/Calendar/CalendarPeek.vue @@ -0,0 +1,164 @@ + + + + + + + diff --git a/src/js/components/Comments/CommentAdd.vue b/src/js/components/Comments/CommentAdd.vue index 8bc290824..08ecd8c56 100644 --- a/src/js/components/Comments/CommentAdd.vue +++ b/src/js/components/Comments/CommentAdd.vue @@ -30,6 +30,7 @@ diff --git a/src/js/components/PollList/PollItem.vue b/src/js/components/PollList/PollItem.vue index 88fe996c9..2bad02e2b 100644 --- a/src/js/components/PollList/PollItem.vue +++ b/src/js/components/PollList/PollItem.vue @@ -110,8 +110,9 @@ - - diff --git a/src/js/components/VoteTable/VoteTable.vue b/src/js/components/VoteTable/VoteTable.vue index 7fe1d3dcb..73e0a5652 100644 --- a/src/js/components/VoteTable/VoteTable.vue +++ b/src/js/components/VoteTable/VoteTable.vue @@ -21,7 +21,7 @@ -->