diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml
index 11f7333cf..d132734ca 100644
--- a/.github/workflows/phpunit.yml
+++ b/.github/workflows/phpunit.yml
@@ -24,7 +24,7 @@ jobs:
fail-fast: false
matrix:
php-versions: ['8.0']
- server-versions: ['master']
+ server-versions: ['stable26']
name: SQLite
@@ -56,7 +56,7 @@ jobs:
fail-fast: false
matrix:
php-versions: ['8.1']
- server-versions: ['master']
+ server-versions: ['stable26']
name: MySQL
@@ -99,7 +99,7 @@ jobs:
fail-fast: false
matrix:
php-versions: ['8.2']
- server-versions: ['master']
+ server-versions: ['stable26']
name: PostgreSQL
diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml
index 8fa4c8839..7bd905a6e 100644
--- a/.github/workflows/static-analysis.yml
+++ b/.github/workflows/static-analysis.yml
@@ -1,37 +1,37 @@
-name: Static analysis
-
-on:
- pull_request:
- push:
- branches:
- - master
- - next
- - stable*
-
-jobs:
- psalm-master:
- runs-on: ubuntu-latest
- strategy:
- matrix:
- ocp-version: ['master']
- php-versions: ['8.0', '8.1', '8.2']
-
- name: Psalm
-
- steps:
- - uses: actions/checkout@v3
-
- - name: Setup composer and PHP
- uses: ./.github/actions/setup-composer
- with:
- php-version: ${{ matrix.php-versions }}
- php-tools: composer, psalm
-
- - name: Install Nextcloud API
- run: composer require --dev nextcloud/ocp:dev-${{ matrix.ocp-version }}
-
- - name: Install symfony/console
- run: composer require symfony/console
-
- - name: Run coding standards check
- run: composer run psalm
+name: Static analysis
+
+on:
+ pull_request:
+ push:
+ branches:
+ - master
+ - next
+ - stable*
+
+jobs:
+ psalm-master:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ ocp-version: ['stable26']
+ php-versions: ['8.0', '8.1', '8.2']
+
+ name: Psalm
+
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Setup composer and PHP
+ uses: ./.github/actions/setup-composer
+ with:
+ php-version: ${{ matrix.php-versions }}
+ php-tools: composer, psalm
+
+ - name: Install Nextcloud API
+ run: composer require --dev nextcloud/ocp:dev-${{ matrix.ocp-version }}
+
+ - name: Install symfony/console
+ run: composer require symfony/console
+
+ - name: Run coding standards check
+ run: composer run psalm
diff --git a/appinfo/info.xml b/appinfo/info.xml
index b672411b5..f332319fe 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -23,7 +23,7 @@
https://raw.githubusercontent.com/nextcloud/polls/master/screenshots/edit-poll.png
-
+
@@ -79,4 +79,4 @@
77
-
\ No newline at end of file
+
diff --git a/composer.json b/composer.json
index 9f84556df..1a53fbfab 100644
--- a/composer.json
+++ b/composer.json
@@ -19,7 +19,10 @@
"autoloader-suffix": "Polls",
"platform": {
"php": "8.0"
- }
+ },
+ "allow-plugins": {
+ "bamarni/composer-bin-plugin": true
+ }
},
"autoload": {
"psr-4": {
@@ -32,11 +35,11 @@
}
},
"require-dev": {
+ "doctrine/dbal": "^3.6",
"league/factory-muffin": "^3.0",
"league/factory-muffin-faker": "^2.0",
"nextcloud/coding-standard": "^1.0",
- "nextcloud/ocp": "dev-stable27",
- "doctrine/dbal": "^3.6"
+ "nextcloud/ocp": "dev-stable26"
},
"scripts": {
"cs:check": "php-cs-fixer fix --dry-run --diff",
diff --git a/composer.lock b/composer.lock
index 4e2aafdbe..11ab0b155 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "e0df7b7aa1b1a99be92fcddebc226176",
+ "content-hash": "fe7685b3d6c9b219254d39619780c806",
"packages": [
{
"name": "dflydev/dot-access-data",
@@ -1259,29 +1259,28 @@
},
{
"name": "nextcloud/ocp",
- "version": "dev-stable27",
+ "version": "dev-stable26",
"source": {
"type": "git",
"url": "https://github.com/nextcloud-deps/ocp.git",
- "reference": "3beb4e9456fd71c91c299ee416e142ad89901deb"
+ "reference": "692e8fb9d10e591600048723c6ed5f7d33fa4275"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/3beb4e9456fd71c91c299ee416e142ad89901deb",
- "reference": "3beb4e9456fd71c91c299ee416e142ad89901deb",
+ "url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/692e8fb9d10e591600048723c6ed5f7d33fa4275",
+ "reference": "692e8fb9d10e591600048723c6ed5f7d33fa4275",
"shasum": ""
},
"require": {
"php": "^7.4 || ~8.0 || ~8.1",
- "psr/clock": "^1.0",
- "psr/container": "^2.0.2",
+ "psr/container": "^1.1.1",
"psr/event-dispatcher": "^1.0",
- "psr/log": "^1.1.4"
+ "psr/log": "^1.1"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-stable27": "27.0.0-dev"
+ "dev-master": "26.0.0-dev"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -1297,9 +1296,9 @@
"description": "Composer package containing Nextcloud's public API (classes, interfaces)",
"support": {
"issues": "https://github.com/nextcloud-deps/ocp/issues",
- "source": "https://github.com/nextcloud-deps/ocp/tree/stable27"
+ "source": "https://github.com/nextcloud-deps/ocp/tree/stable26"
},
- "time": "2024-03-05T00:31:26+00:00"
+ "time": "2024-01-03T00:33:21+00:00"
},
{
"name": "php-cs-fixer/shim",
@@ -1402,77 +1401,24 @@
},
"time": "2021-02-03T23:26:27+00:00"
},
- {
- "name": "psr/clock",
- "version": "1.0.0",
- "source": {
- "type": "git",
- "url": "https://github.com/php-fig/clock.git",
- "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d",
- "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d",
- "shasum": ""
- },
- "require": {
- "php": "^7.0 || ^8.0"
- },
- "type": "library",
- "autoload": {
- "psr-4": {
- "Psr\\Clock\\": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "PHP-FIG",
- "homepage": "https://www.php-fig.org/"
- }
- ],
- "description": "Common interface for reading the clock.",
- "homepage": "https://github.com/php-fig/clock",
- "keywords": [
- "clock",
- "now",
- "psr",
- "psr-20",
- "time"
- ],
- "support": {
- "issues": "https://github.com/php-fig/clock/issues",
- "source": "https://github.com/php-fig/clock/tree/1.0.0"
- },
- "time": "2022-11-25T14:36:26+00:00"
- },
{
"name": "psr/container",
- "version": "2.0.2",
+ "version": "1.1.2",
"source": {
"type": "git",
"url": "https://github.com/php-fig/container.git",
- "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963"
+ "reference": "513e0666f7216c7459170d56df27dfcefe1689ea"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963",
- "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+ "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea",
+ "reference": "513e0666f7216c7459170d56df27dfcefe1689ea",
"shasum": ""
},
"require": {
"php": ">=7.4.0"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "2.0.x-dev"
- }
- },
"autoload": {
"psr-4": {
"Psr\\Container\\": "src/"
@@ -1499,9 +1445,9 @@
],
"support": {
"issues": "https://github.com/php-fig/container/issues",
- "source": "https://github.com/php-fig/container/tree/2.0.2"
+ "source": "https://github.com/php-fig/container/tree/1.1.2"
},
- "time": "2021-11-05T16:47:00+00:00"
+ "time": "2021-11-05T16:50:12+00:00"
},
{
"name": "psr/log",
@@ -1566,5 +1512,5 @@
"platform-overrides": {
"php": "8.0"
},
- "plugin-api-version": "2.3.0"
+ "plugin-api-version": "2.6.0"
}
diff --git a/lib/Controller/AdminController.php b/lib/Controller/AdminController.php
index c3b1574e3..7a4667f26 100644
--- a/lib/Controller/AdminController.php
+++ b/lib/Controller/AdminController.php
@@ -28,7 +28,6 @@
use OCA\Polls\AppConstants;
use OCA\Polls\Db\UserMapper;
use OCA\Polls\Service\PollService;
-use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\Collaboration\Resources\LoadAdditionalScriptsEvent;
@@ -52,7 +51,9 @@ public function __construct(
parent::__construct($appName, $request);
}
- #[NoCSRFRequired]
+ /**
+ * @NoCSRFRequired
+ */
public function index(): TemplateResponse {
Util::addScript(AppConstants::APP_ID, 'polls-main');
$this->eventDispatcher->dispatchTyped(new LoadAdditionalScriptsEvent());
diff --git a/lib/Controller/BaseApiController.php b/lib/Controller/BaseApiController.php
index 508de30c8..9eb452bb5 100644
--- a/lib/Controller/BaseApiController.php
+++ b/lib/Controller/BaseApiController.php
@@ -50,8 +50,8 @@ public function __construct(
/**
* response
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
protected function response(Closure $callback): JSONResponse {
try {
return new JSONResponse($callback(), Http::STATUS_OK);
@@ -62,8 +62,8 @@ protected function response(Closure $callback): JSONResponse {
/**
* response
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
protected function responseLong(Closure $callback): JSONResponse {
try {
return new JSONResponse($callback(), Http::STATUS_OK);
@@ -74,8 +74,8 @@ protected function responseLong(Closure $callback): JSONResponse {
/**
* responseCreate
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
protected function responseCreate(Closure $callback): JSONResponse {
try {
return new JSONResponse($callback(), Http::STATUS_CREATED);
diff --git a/lib/Controller/BaseController.php b/lib/Controller/BaseController.php
index b8ee9be63..9fda8fb21 100644
--- a/lib/Controller/BaseController.php
+++ b/lib/Controller/BaseController.php
@@ -48,8 +48,8 @@ public function __construct(
/**
* response
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
protected function response(Closure $callback): JSONResponse {
try {
return new JSONResponse($callback(), Http::STATUS_OK);
@@ -60,8 +60,8 @@ protected function response(Closure $callback): JSONResponse {
/**
* response
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
protected function responseLong(Closure $callback): JSONResponse {
try {
return new JSONResponse($callback(), Http::STATUS_OK);
@@ -72,8 +72,8 @@ protected function responseLong(Closure $callback): JSONResponse {
/**
* responseCreate
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
protected function responseCreate(Closure $callback): JSONResponse {
try {
return new JSONResponse($callback(), Http::STATUS_CREATED);
@@ -84,8 +84,8 @@ protected function responseCreate(Closure $callback): JSONResponse {
/**
* responseDeleteTolerant
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
protected function responseDeleteTolerant(Closure $callback): JSONResponse {
try {
return new JSONResponse($callback(), Http::STATUS_OK);
diff --git a/lib/Controller/BasePublicController.php b/lib/Controller/BasePublicController.php
index 6a4ab4098..ac038488e 100644
--- a/lib/Controller/BasePublicController.php
+++ b/lib/Controller/BasePublicController.php
@@ -32,7 +32,6 @@
use OCA\Polls\Model\Acl;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
-use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
use OCP\ISession;
@@ -52,8 +51,8 @@ public function __construct(
/**
* response
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
protected function response(Closure $callback, string $token): JSONResponse {
$this->updateSessionToken($token);
@@ -66,8 +65,8 @@ protected function response(Closure $callback, string $token): JSONResponse {
/**
* response
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
protected function responseLong(Closure $callback, string $token): JSONResponse {
$this->updateSessionToken($token);
@@ -79,8 +78,8 @@ protected function responseLong(Closure $callback, string $token): JSONResponse
}
/**
* responseCreate
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
protected function responseCreate(Closure $callback, string $token): JSONResponse {
$this->updateSessionToken($token);
diff --git a/lib/Controller/CommentApiController.php b/lib/Controller/CommentApiController.php
index ee1f72b72..f76074983 100644
--- a/lib/Controller/CommentApiController.php
+++ b/lib/Controller/CommentApiController.php
@@ -26,9 +26,6 @@
namespace OCA\Polls\Controller;
use OCA\Polls\Service\CommentService;
-use OCP\AppFramework\Http\Attribute\CORS;
-use OCP\AppFramework\Http\Attribute\NoAdminRequired;
-use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
@@ -46,10 +43,10 @@ public function __construct(
/**
* Read all comments of a poll based on the poll id and return list as array
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function list(int $pollId): JSONResponse {
return $this->response(fn () => [
'comments' => $this->commentService->list($pollId)
@@ -58,10 +55,10 @@ public function list(int $pollId): JSONResponse {
/**
* Add comment
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function add(int $pollId, string $comment): JSONResponse {
return $this->response(fn () => [
'comment' => $this->commentService->add($comment, $pollId)
@@ -70,10 +67,10 @@ public function add(int $pollId, string $comment): JSONResponse {
/**
* Delete comment
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function delete(int $commentId): JSONResponse {
$comment = $this->commentService->get($commentId);
@@ -83,10 +80,10 @@ public function delete(int $commentId): JSONResponse {
/**
* Restore comment
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function restore(int $commentId): JSONResponse {
$comment = $this->commentService->get($commentId);
diff --git a/lib/Controller/CommentController.php b/lib/Controller/CommentController.php
index fdded403e..ad1a194b3 100644
--- a/lib/Controller/CommentController.php
+++ b/lib/Controller/CommentController.php
@@ -26,7 +26,6 @@
namespace OCA\Polls\Controller;
use OCA\Polls\Service\CommentService;
-use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
@@ -44,8 +43,8 @@ public function __construct(
/**
* Write a new comment to the db and returns the new comment as array
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function list(int $pollId): JSONResponse {
return $this->response(fn () => [
'comments' => $this->commentService->list($pollId)
@@ -54,8 +53,8 @@ public function list(int $pollId): JSONResponse {
/**
* Write a new comment to the db and returns the new comment as array
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function add(int $pollId, string $message): JSONResponse {
return $this->response(fn () => [
'comment' => $this->commentService->add($message, $pollId)
@@ -64,8 +63,8 @@ public function add(int $pollId, string $message): JSONResponse {
/**
* Delete Comment
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function delete(int $commentId): JSONResponse {
$comment = $this->commentService->get($commentId);
@@ -76,8 +75,8 @@ public function delete(int $commentId): JSONResponse {
/**
* Restore deleted Comment
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function restore(int $commentId): JSONResponse {
$comment = $this->commentService->get($commentId);
diff --git a/lib/Controller/OptionApiController.php b/lib/Controller/OptionApiController.php
index 44b6eab81..181f4d2cb 100644
--- a/lib/Controller/OptionApiController.php
+++ b/lib/Controller/OptionApiController.php
@@ -26,9 +26,6 @@
namespace OCA\Polls\Controller;
use OCA\Polls\Service\OptionService;
-use OCP\AppFramework\Http\Attribute\CORS;
-use OCP\AppFramework\Http\Attribute\NoAdminRequired;
-use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
@@ -46,20 +43,20 @@ public function __construct(
/**
* Get all options of given poll
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function list(int $pollId): JSONResponse {
return $this->response(fn () => ['options' => $this->optionService->list($pollId)]);
}
/**
* Add a new option
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function add(int $pollId, int $timestamp = 0, string $pollOptionText = '', int $duration = 0): JSONResponse {
return $this->responseCreate(fn () => ['option' => $this->optionService->add($pollId, $timestamp, $pollOptionText, $duration)]);
}
@@ -67,50 +64,50 @@ public function add(int $pollId, int $timestamp = 0, string $pollOptionText = ''
/**
* Update option
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function update(int $optionId, int $timestamp = 0, string $pollOptionText = '', int $duration = 0): JSONResponse {
return $this->response(fn () => ['option' => $this->optionService->update($optionId, $timestamp, $pollOptionText, $duration)]);
}
/**
* Delete option
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function delete(int $optionId): JSONResponse {
return $this->response(fn () => ['option' => $this->optionService->delete($optionId)]);
}
/**
* Restore option
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function restore(int $optionId): JSONResponse {
return $this->response(fn () => ['option' => $this->optionService->delete($optionId, true)]);
}
/**
* Switch option confirmation
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function confirm(int $optionId): JSONResponse {
return $this->response(fn () => ['option' => $this->optionService->confirm($optionId)]);
}
/**
* Set order position for option
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function setOrder(int $optionId, int $order): JSONResponse {
return $this->response(fn () => ['option' => $this->optionService->setOrder($optionId, $order)]);
}
diff --git a/lib/Controller/OptionController.php b/lib/Controller/OptionController.php
index f4be9a655..604e4c08d 100644
--- a/lib/Controller/OptionController.php
+++ b/lib/Controller/OptionController.php
@@ -27,7 +27,6 @@
use OCA\Polls\Service\CalendarService;
use OCA\Polls\Service\OptionService;
-use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
@@ -46,8 +45,8 @@ public function __construct(
/**
* Get all options of given poll
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function list(int $pollId): JSONResponse {
return $this->response(function () use ($pollId) {
return ['options' => $this->optionService->list($pollId)];
@@ -56,80 +55,80 @@ public function list(int $pollId): JSONResponse {
/**
* Add a new option
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function add(int $pollId, int $timestamp = 0, string $text = '', int $duration = 0): JSONResponse {
return $this->responseCreate(fn () => ['option' => $this->optionService->add($pollId, $timestamp, $text, $duration)]);
}
/**
* Add mulitple new option
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function addBulk(int $pollId, string $text = ''): JSONResponse {
return $this->responseCreate(fn () => ['options' => $this->optionService->addBulk($pollId, $text)]);
}
/**
* Update option
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function update(int $optionId, int $timestamp, string $text, int $duration): JSONResponse {
return $this->response(fn () => ['option' => $this->optionService->update($optionId, $timestamp, $text, $duration)]);
}
/**
* Delete option
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function delete(int $optionId): JSONResponse {
return $this->response(fn () => ['option' => $this->optionService->delete($optionId)]);
}
/**
* Restore option
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function restore(int $optionId): JSONResponse {
return $this->response(fn () => ['option' => $this->optionService->delete($optionId, true)]);
}
/**
* Switch option confirmation
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function confirm(int $optionId): JSONResponse {
return $this->response(fn () => ['option' => $this->optionService->confirm($optionId)]);
}
/**
* Reorder options
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function reorder(int $pollId, array $options): JSONResponse {
return $this->response(fn () => ['options' => $this->optionService->reorder($pollId, $options)]);
}
/**
* Reorder options
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function sequence(int $optionId, int $step, string $unit, int $amount): JSONResponse {
return $this->response(fn () => ['options' => $this->optionService->sequence($optionId, $step, $unit, $amount)]);
}
/**
* Reorder options
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function shift(int $pollId, int $step, string $unit): JSONResponse {
return $this->response(fn () => ['options' => $this->optionService->shift($pollId, $step, $unit)]);
}
/**
* findCalendarEvents
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function findCalendarEvents(int $optionId, string $tz): JSONResponse {
return $this->response(fn () => ['events' => $this->calendarService->getEvents($optionId)]);
}
diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php
index b994a006c..87cdbfe72 100644
--- a/lib/Controller/PageController.php
+++ b/lib/Controller/PageController.php
@@ -29,8 +29,6 @@
use OCA\Polls\AppConstants;
use OCA\Polls\Service\NotificationService;
use OCP\AppFramework\Controller;
-use OCP\AppFramework\Http\Attribute\NoAdminRequired;
-use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\Collaboration\Resources\LoadAdditionalScriptsEvent;
use OCP\EventDispatcher\IEventDispatcher;
@@ -52,17 +50,20 @@ public function __construct(
parent::__construct($appName, $request);
}
-
- #[NoAdminRequired]
- #[NoCSRFRequired]
+ /**
+ * @NoAdminRequired
+ * @NoCSRFRequired
+ */
public function index(): TemplateResponse {
Util::addScript(AppConstants::APP_ID, 'polls-main');
$this->eventDispatcher->dispatchTyped(new LoadAdditionalScriptsEvent());
return new TemplateResponse(AppConstants::APP_ID, 'main');
}
- #[NoAdminRequired]
- #[NoCSRFRequired]
+ /**
+ * @NoAdminRequired
+ * @NoCSRFRequired
+ */
public function vote(int $id): TemplateResponse {
$this->notificationService->removeNotification($id);
Util::addScript(AppConstants::APP_ID, 'polls-main');
diff --git a/lib/Controller/PollApiController.php b/lib/Controller/PollApiController.php
index 425c0d9d6..9be990cbf 100644
--- a/lib/Controller/PollApiController.php
+++ b/lib/Controller/PollApiController.php
@@ -31,9 +31,6 @@
use OCA\Polls\Service\PollService;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Http;
-use OCP\AppFramework\Http\Attribute\CORS;
-use OCP\AppFramework\Http\Attribute\NoAdminRequired;
-use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
@@ -52,10 +49,10 @@ public function __construct(
/**
* Get list of polls
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function list(): JSONResponse {
try {
return new JSONResponse([AppConstants::APP_ID => $this->pollService->list()], Http::STATUS_OK);
@@ -68,10 +65,10 @@ public function list(): JSONResponse {
/**
* get poll configuration
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function get(int $pollId): JSONResponse {
try {
return new JSONResponse(['poll' => $this->pollService->get($pollId)], Http::STATUS_OK);
@@ -84,10 +81,10 @@ public function get(int $pollId): JSONResponse {
/**
* Add poll
+ * @NoAdminRequired
+ * @NoCSRFRequired
+ * @CORS
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function add(string $type, string $title): JSONResponse {
try {
return new JSONResponse(['poll' => $this->pollService->add($type, $title)], Http::STATUS_CREATED);
@@ -98,10 +95,10 @@ public function add(string $type, string $title): JSONResponse {
/**
* Update poll configuration
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function update(int $pollId, array $poll): JSONResponse {
try {
$this->acl->setPollId($pollId, Acl::PERMISSION_POLL_EDIT);
@@ -119,10 +116,10 @@ public function update(int $pollId, array $poll): JSONResponse {
/**
* Switch deleted status (move to deleted polls)
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function toggleArchive(int $pollId): JSONResponse {
try {
return new JSONResponse(['poll' => $this->pollService->toggleArchive($pollId)], Http::STATUS_OK);
@@ -135,10 +132,10 @@ public function toggleArchive(int $pollId): JSONResponse {
/**
* Close poll
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function close(int $pollId): JSONResponse {
try {
return new JSONResponse(['poll' => $this->pollService->close($pollId)], Http::STATUS_OK);
@@ -151,10 +148,10 @@ public function close(int $pollId): JSONResponse {
/**
* Reopen poll
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function reopen(int $pollId): JSONResponse {
try {
return new JSONResponse(['poll' => $this->pollService->reopen($pollId)], Http::STATUS_OK);
@@ -167,10 +164,10 @@ public function reopen(int $pollId): JSONResponse {
/**
* Delete poll
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function delete(int $pollId): JSONResponse {
try {
return new JSONResponse(['poll' => $this->pollService->delete($pollId)], Http::STATUS_OK);
@@ -183,10 +180,10 @@ public function delete(int $pollId): JSONResponse {
/**
* Clone poll
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function clone(int $pollId): JSONResponse {
try {
return new JSONResponse(['poll' => $this->pollService->clone($pollId)], Http::STATUS_CREATED);
@@ -199,11 +196,11 @@ public function clone(int $pollId): JSONResponse {
/**
* Transfer all polls from one user to another (change owner of poll)
+ * @CORS
+ * @NoCSRFRequired
* @param string $sourceUser User to transfer polls from
* @param string $destinationUser User to transfer polls to
*/
- #[CORS]
- #[NoCSRFRequired]
public function transferPolls(string $sourceUser, string $destinationUser): JSONResponse {
try {
return new JSONResponse(['transferred' => $this->pollService->transferPolls($sourceUser, $destinationUser)], Http::STATUS_CREATED);
@@ -214,11 +211,11 @@ public function transferPolls(string $sourceUser, string $destinationUser): JSON
/**
* Transfer singe poll to another user (change owner of poll)
+ * @CORS
+ * @NoCSRFRequired
* @param int $pollId Poll to transfer
* @param string $destinationUser User to transfer the poll to
*/
- #[CORS]
- #[NoCSRFRequired]
public function transferPoll(int $pollId, string $destinationUser): JSONResponse {
try {
return new JSONResponse(['transferred' => $this->pollService->transferPoll($pollId, $destinationUser)], Http::STATUS_CREATED);
@@ -229,10 +226,10 @@ public function transferPoll(int $pollId, string $destinationUser): JSONResponse
/**
* Collect email addresses from particitipants
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function getParticipantsEmailAddresses(int $pollId): JSONResponse {
try {
return new JSONResponse($this->pollService->getParticipantsEmailAddresses($pollId), Http::STATUS_OK);
@@ -245,10 +242,10 @@ public function getParticipantsEmailAddresses(int $pollId): JSONResponse {
/**
* Get valid values for configuration options
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function enum(): JSONResponse {
return new JSONResponse($this->pollService->getValidEnum(), Http::STATUS_OK);
}
diff --git a/lib/Controller/PollController.php b/lib/Controller/PollController.php
index c0b3deff2..49a6de067 100644
--- a/lib/Controller/PollController.php
+++ b/lib/Controller/PollController.php
@@ -31,9 +31,9 @@
use OCA\Polls\Service\MailService;
use OCA\Polls\Service\OptionService;
use OCA\Polls\Service\PollService;
-use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
+use OCP\Server;
/**
* @psalm-api
@@ -52,11 +52,11 @@ public function __construct(
/**
* Get list of polls
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function list(): JSONResponse {
return $this->response(function () {
- $appSettings = new AppSettings;
+ $appSettings = Server::get(AppSettings::class);
return [
'list' => $this->pollService->list(),
'pollCreationAllowed' => $appSettings->getPollCreationAllowed(),
@@ -67,8 +67,8 @@ public function list(): JSONResponse {
/**
* get complete poll
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function get(int $pollId): JSONResponse {
$poll = $this->pollService->get($pollId);
$this->acl->setPollId($pollId);
@@ -80,16 +80,16 @@ public function get(int $pollId): JSONResponse {
/**
* Add poll
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function add(string $type, string $title): JSONResponse {
return $this->responseCreate(fn () => $this->pollService->add($type, $title));
}
/**
* Update poll configuration
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function update(int $pollId, array $poll): JSONResponse {
$this->acl->setPollId($pollId, Acl::PERMISSION_POLL_EDIT);
return $this->response(fn () => [
@@ -100,8 +100,8 @@ public function update(int $pollId, array $poll): JSONResponse {
/**
* Send confirmation mails
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function sendConfirmation(int $pollId): JSONResponse {
$this->acl->setPollId($pollId, Acl::PERMISSION_POLL_EDIT);
return $this->response(fn () => [
@@ -111,16 +111,16 @@ public function sendConfirmation(int $pollId): JSONResponse {
/**
* Switch deleted status (move to deleted polls)
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function toggleArchive(int $pollId): JSONResponse {
return $this->response(fn () => $this->pollService->toggleArchive($pollId));
}
/**
* Delete poll
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function delete(int $pollId): JSONResponse {
return $this->responseDeleteTolerant(fn () => $this->pollService->delete($pollId));
@@ -128,8 +128,8 @@ public function delete(int $pollId): JSONResponse {
/**
* Close poll
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function close(int $pollId): JSONResponse {
return $this->response(fn () => [
'poll' => $this->pollService->close($pollId),
@@ -139,8 +139,8 @@ public function close(int $pollId): JSONResponse {
/**
* Reopen poll
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function reopen(int $pollId): JSONResponse {
return $this->response(fn () => [
'poll' => $this->pollService->reopen($pollId),
@@ -150,8 +150,8 @@ public function reopen(int $pollId): JSONResponse {
/**
* Clone poll
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function clone(int $pollId): JSONResponse {
return $this->response(fn () => $this->clonePoll($pollId));
}
@@ -171,8 +171,8 @@ public function transferPolls(string $sourceUser, string $targetUser): JSONRespo
/**
* Collect email addresses from particitipants
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function getParticipantsEmailAddresses(int $pollId): JSONResponse {
return $this->response(fn () => $this->pollService->getParticipantsEmailAddresses($pollId));
}
diff --git a/lib/Controller/PreferencesController.php b/lib/Controller/PreferencesController.php
index 350d0a515..e72ae1825 100644
--- a/lib/Controller/PreferencesController.php
+++ b/lib/Controller/PreferencesController.php
@@ -29,8 +29,6 @@
use OCA\Polls\Service\CalendarService;
use OCA\Polls\Service\PreferencesService;
use OCP\AppFramework\Http;
-use OCP\AppFramework\Http\Attribute\NoAdminRequired;
-use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
@@ -50,17 +48,17 @@ public function __construct(
/**
* Read all preferences
+ * @NoAdminRequired
+ * @NoCSRFRequired
*/
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function get(): JSONResponse {
return $this->response(fn () => $this->preferencesService->get());
}
/**
* Write preferences
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function write(array $preferences): JSONResponse {
if (!$this->userMapper->getCurrentUser()->getIsLoggedIn()) {
return new JSONResponse([], Http::STATUS_OK);
@@ -70,9 +68,9 @@ public function write(array $preferences): JSONResponse {
/**
* Read all preferences
+ * @NoAdminRequired
+ * @NoCSRFRequired
*/
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function getCalendars(): JSONResponse {
return new JSONResponse(['calendars' => $this->calendarService->getCalendars()], Http::STATUS_OK);
}
diff --git a/lib/Controller/PublicController.php b/lib/Controller/PublicController.php
index b96a27e76..110b58685 100644
--- a/lib/Controller/PublicController.php
+++ b/lib/Controller/PublicController.php
@@ -35,8 +35,6 @@
use OCA\Polls\Service\SystemService;
use OCA\Polls\Service\VoteService;
use OCA\Polls\Service\WatchService;
-use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
-use OCP\AppFramework\Http\Attribute\PublicPage;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Http\Template\PublicTemplateResponse;
use OCP\AppFramework\Http\TemplateResponse;
@@ -47,6 +45,10 @@
use OCP\Util;
/**
+ * Always use parent's classe response* methods to make sure, the token gets set correctly.
+ * Requesting the token inside the controller is not possible, because the token is submitted
+ * as a paramter and not known while contruction time
+ * i.e. ACL requests are not valid before calling the response* method
* @psalm-api
*/
class PublicController extends BasePublicController {
@@ -70,10 +72,10 @@ public function __construct(
}
/**
+ * @NoCSRFRequired
+ * @PublicPage
* @return TemplateResponse|PublicTemplateResponse
*/
- #[PublicPage]
- #[NoCSRFRequired]
public function votePage(string $token) {
Util::addScript(AppConstants::APP_ID, 'polls-main');
if ($this->userSession->isLoggedIn()) {
@@ -87,22 +89,23 @@ public function votePage(string $token) {
/**
* get complete poll via token
+ * @PublicPage
*/
- #[PublicPage]
public function getPoll(string $token): JSONResponse {
- $this->acl->request(Acl::PERMISSION_POLL_VIEW);
-
- // load poll through acl
- return $this->response(fn () => [
- 'acl' => $this->acl,
- 'poll' => $this->acl->getPoll(),
- ], $token);
+ return $this->response(function () {
+ $this->acl->request(Acl::PERMISSION_POLL_VIEW);
+ // load poll through acl
+ return [
+ 'acl' => $this->acl,
+ 'poll' => $this->acl->getPoll(),
+ ];
+ }, $token);
}
/**
* Watch poll for updates
+ * @PublicPage
*/
- #[PublicPage]
public function watchPoll(string $token, ?int $offset): JSONResponse {
return $this->responseLong(fn () => [
'updates' => $this->watchService->watchUpdates(offset: $offset)
@@ -111,8 +114,8 @@ public function watchPoll(string $token, ?int $offset): JSONResponse {
/**
* Get share
+ * @PublicPage
*/
- #[PublicPage]
public function getShare(string $token): JSONResponse {
return $this->response(fn () => [
'share' => $this->shareService->request($token)
@@ -121,8 +124,8 @@ public function getShare(string $token): JSONResponse {
/**
* Get votes
+ * @PublicPage
*/
- #[PublicPage]
public function getVotes(string $token): JSONResponse {
return $this->response(fn () => [
'votes' => $this->voteService->list()
@@ -131,8 +134,8 @@ public function getVotes(string $token): JSONResponse {
/**
* Delete current user's votes
+ * @PublicPage
*/
- #[PublicPage]
public function deleteUser(string $token): JSONResponse {
return $this->response(fn () => [
'deleted' => $this->voteService->delete()
@@ -141,8 +144,8 @@ public function deleteUser(string $token): JSONResponse {
/**
* Delete current user's orphaned votes
+ * @PublicPage
*/
- #[PublicPage]
public function deleteOrphanedVotes(string $token): JSONResponse {
return $this->response(fn () => [
'deleted' => $this->voteService->delete(deleteOnlyOrphaned: true)
@@ -151,8 +154,8 @@ public function deleteOrphanedVotes(string $token): JSONResponse {
/**
* Get options
+ * @PublicPage
*/
- #[PublicPage]
public function getOptions(string $token): JSONResponse {
return $this->response(fn () => [
'options' => $this->optionService->list()
@@ -161,8 +164,8 @@ public function getOptions(string $token): JSONResponse {
/**
* Add options
+ * @PublicPage
*/
- #[PublicPage]
public function addOption(string $token, int $timestamp = 0, string $text = '', int $duration = 0): JSONResponse {
return $this->responseCreate(fn () => [
'option' => $this->optionService->add(
@@ -175,8 +178,8 @@ public function addOption(string $token, int $timestamp = 0, string $text = '',
/**
* Delete option
+ * @PublicPage
*/
- #[PublicPage]
public function deleteOption(string $token, int $optionId): JSONResponse {
return $this->response(fn () => [
'option' => $this->optionService->delete($optionId)
@@ -185,8 +188,8 @@ public function deleteOption(string $token, int $optionId): JSONResponse {
/**
* Restore option
+ * @PublicPage
*/
- #[PublicPage]
public function restoreOption(string $token, int $optionId): JSONResponse {
return $this->response(fn () => [
'option' => $this->optionService->delete($optionId, true)
@@ -195,8 +198,8 @@ public function restoreOption(string $token, int $optionId): JSONResponse {
/**
* Set Vote
+ * @PublicPage
*/
- #[PublicPage]
public function setVote(int $optionId, string $setTo, string $token): JSONResponse {
return $this->response(fn () => [
'vote' => $this->voteService->set($optionId, $setTo)
@@ -205,8 +208,8 @@ public function setVote(int $optionId, string $setTo, string $token): JSONRespon
/**
* Get Comments
+ * @PublicPage
*/
- #[PublicPage]
public function getComments(string $token): JSONResponse {
return $this->response(fn () => [
'comments' => $this->commentService->list()
@@ -215,8 +218,8 @@ public function getComments(string $token): JSONResponse {
/**
* Write a new comment to the db and returns the new comment as array
+ * @PublicPage
*/
- #[PublicPage]
public function addComment(string $token, string $message): JSONResponse {
return $this->response(fn () => [
'comment' => $this->commentService->add($message)
@@ -225,8 +228,8 @@ public function addComment(string $token, string $message): JSONResponse {
/**
* Delete Comment
+ * @PublicPage
*/
- #[PublicPage]
public function deleteComment(int $commentId, string $token): JSONResponse {
$comment = $this->commentService->get($commentId);
return $this->response(fn () => [
@@ -236,8 +239,8 @@ public function deleteComment(int $commentId, string $token): JSONResponse {
/**
* Restore deleted Comment
+ * @PublicPage
*/
- #[PublicPage]
public function restoreComment(int $commentId, string $token): JSONResponse {
$comment = $this->commentService->get($commentId);
@@ -248,8 +251,8 @@ public function restoreComment(int $commentId, string $token): JSONResponse {
/**
* Get subscription status
+ * @PublicPage
*/
- #[PublicPage]
public function getSubscription(string $token): JSONResponse {
return $this->response(fn () => [
'subscribed' => $this->subscriptionService->get()
@@ -258,8 +261,8 @@ public function getSubscription(string $token): JSONResponse {
/**
* subscribe
+ * @PublicPage
*/
- #[PublicPage]
public function subscribe(string $token): JSONResponse {
return $this->response(fn () => [
'subscribed' => $this->subscriptionService->set(true)
@@ -268,8 +271,8 @@ public function subscribe(string $token): JSONResponse {
/**
* Unsubscribe
+ * @PublicPage
*/
- #[PublicPage]
public function unsubscribe(string $token): JSONResponse {
return $this->response(fn () => [
'subscribed' => $this->subscriptionService->set(false)
@@ -279,8 +282,8 @@ public function unsubscribe(string $token): JSONResponse {
/**
* Validate it the user name is reserved
* return false, if this username already exists as a user or as a participant of the poll
+ * @PublicPage
*/
- #[PublicPage]
public function validatePublicDisplayName(string $displayName, string $token): JSONResponse {
return $this->response(fn () => [
'name' => $this->systemService->validatePublicUsername($displayName, token: $token)
@@ -289,8 +292,8 @@ public function validatePublicDisplayName(string $displayName, string $token): J
/**
* Validate email address (simple validation)
+ * @PublicPage
*/
- #[PublicPage]
public function validateEmailAddress(string $emailAddress, string $token = ''): JSONResponse {
return $this->response(fn () => [
'result' => MailService::validateEmailAddress($emailAddress), 'emailAddress' => $emailAddress
@@ -299,8 +302,8 @@ public function validateEmailAddress(string $emailAddress, string $token = ''):
/**
* Change displayName
+ * @PublicPage
*/
- #[PublicPage]
public function setDisplayName(string $token, string $displayName): JSONResponse {
return $this->response(fn () => [
'share' => $this->shareService->setDisplayname($displayName, $token)
@@ -310,8 +313,8 @@ public function setDisplayName(string $token, string $displayName): JSONResponse
/**
* Set EmailAddress
+ * @PublicPage
*/
- #[PublicPage]
public function setEmailAddress(string $token, string $emailAddress = ''): JSONResponse {
return $this->response(fn () => [
'share' => $this->shareService->setEmailAddress($this->shareService->get($token), $emailAddress)
@@ -320,8 +323,8 @@ public function setEmailAddress(string $token, string $emailAddress = ''): JSONR
/**
* Set EmailAddress
+ * @PublicPage
*/
- #[PublicPage]
public function deleteEmailAddress(string $token): JSONResponse {
return $this->response(fn () => [
'share' => $this->shareService->deleteEmailAddress($this->shareService->get($token))
@@ -331,8 +334,8 @@ public function deleteEmailAddress(string $token): JSONResponse {
/**
* Create a personal share from a public share
* or update an email share with the username
+ * @PublicPage
*/
- #[PublicPage]
public function register(string $token, string $displayName, string $emailAddress = '', string $timeZone = ''): JSONResponse {
return $this->responseCreate(fn () => [
'share' => $this->shareService->register($token, $displayName, $emailAddress, $timeZone),
@@ -342,8 +345,8 @@ public function register(string $token, string $displayName, string $emailAddres
/**
* Sent invitation mails for a share
* Additionally send notification via notifications
+ * @PublicPage
*/
- #[PublicPage]
public function resendInvitation(string $token): JSONResponse {
$share = $this->shareService->get($token);
return $this->response(fn () => [
diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php
index a921fcdd5..594f5ac31 100644
--- a/lib/Controller/SettingsController.php
+++ b/lib/Controller/SettingsController.php
@@ -26,8 +26,6 @@
namespace OCA\Polls\Controller;
use OCA\Polls\Service\SettingsService;
-use OCP\AppFramework\Http\Attribute\NoAdminRequired;
-use OCP\AppFramework\Http\Attribute\PublicPage;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
@@ -45,9 +43,9 @@ public function __construct(
/**
* Read app settings
+ * @PublicPage
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
- #[PublicPage]
public function getAppSettings(): JSONResponse {
return $this->response(fn () => ['appSettings' => $this->settingsService->getAppSettings()]);
}
diff --git a/lib/Controller/ShareApiController.php b/lib/Controller/ShareApiController.php
index f6324d6ea..1e79ed90d 100644
--- a/lib/Controller/ShareApiController.php
+++ b/lib/Controller/ShareApiController.php
@@ -27,9 +27,6 @@
use OCA\Polls\Service\MailService;
use OCA\Polls\Service\ShareService;
-use OCP\AppFramework\Http\Attribute\CORS;
-use OCP\AppFramework\Http\Attribute\NoAdminRequired;
-use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
@@ -48,80 +45,80 @@ public function __construct(
/**
* Read all shares of a poll based on the poll id and return list as array
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function list(int $pollId): JSONResponse {
return $this->response(fn () => ['shares' => $this->shareService->list($pollId)]);
}
/**
* Get share by token
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function get(string $token): JSONResponse {
return $this->response(fn () => ['share' => $this->shareService->get($token)]);
}
/**
* Add share
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function add(int $pollId, string $type, string $userId = ''): JSONResponse {
return $this->responseCreate(fn () => ['share' => $this->shareService->add($pollId, $type, $userId)]);
}
/**
* Delete share
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function delete(string $token): JSONResponse {
return $this->response(fn () => ['share' => $this->shareService->delete(token: $token)]);
}
/**
* Delete share
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function restore(string $token): JSONResponse {
return $this->response(fn () => ['share' => $this->shareService->delete(token: $token, restore: true)]);
}
/**
* Lock share
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function lock(string $token): JSONResponse {
return $this->response(fn () => ['share' => $this->shareService->lock(token: $token)]);
}
/**
* Unlock share
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function unlock(string $token): JSONResponse {
return $this->response(fn () => ['share' => $this->shareService->lock(token: $token, unlock: true)]);
}
/**
* Sent invitation mails for a share
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function sendInvitation(string $token): JSONResponse {
$share = $this->shareService->get($token);
return $this->response(fn () => [
diff --git a/lib/Controller/ShareController.php b/lib/Controller/ShareController.php
index 6870ff1e5..6a1848dd1 100644
--- a/lib/Controller/ShareController.php
+++ b/lib/Controller/ShareController.php
@@ -27,7 +27,6 @@
use OCA\Polls\Db\Share;
use OCA\Polls\Service\ShareService;
-use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
@@ -45,32 +44,34 @@ public function __construct(
/**
* List shares
+ * @NoAdminRequired
+ *
+ * @return JSONResponse
*/
- #[NoAdminRequired]
public function list(int $pollId): JSONResponse {
return $this->response(fn () => ['shares' => $this->shareService->list($pollId)]);
}
/**
* Add share
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function add(int $pollId, string $type, string $userId = '', string $displayName = '', string $emailAddress = ''): JSONResponse {
return $this->responseCreate(fn () => ['share' => $this->shareService->add($pollId, $type, $userId, $displayName, $emailAddress)]);
}
/**
* Change the contraints for email addresses in public polls
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function setPublicPollEmail(string $token, string $value): JSONResponse {
return $this->response(fn () => ['share' => $this->shareService->setPublicPollEmail($token, $value)]);
}
/**
* Change Label of a public share
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function setLabel(string $token, string $label = ''): JSONResponse {
return $this->response(fn () => [
'share' => $this->shareService->setLabel($label, $token)
@@ -79,24 +80,24 @@ public function setLabel(string $token, string $label = ''): JSONResponse {
/**
* Convert poll admin to user
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function adminToUser(string $token): JSONResponse {
return $this->responseCreate(fn () => ['share' => $this->shareService->setType($token, Share::TYPE_USER)]);
}
/**
* Convert user to poll admin
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function userToAdmin(string $token): JSONResponse {
return $this->responseCreate(fn () => ['share' => $this->shareService->setType($token, Share::TYPE_ADMIN)]);
}
/**
* Set email address
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function setEmailAddress(string $token, string $emailAddress = ''): JSONResponse {
return $this->response(fn () => [
'share' => $this->shareService->setEmailAddress($this->shareService->get($token),
@@ -106,32 +107,32 @@ public function setEmailAddress(string $token, string $emailAddress = ''): JSONR
/**
* Delete share
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function delete(string $token): JSONResponse {
return $this->response(fn () => ['share' => $this->shareService->delete(token: $token)]);
}
/**
* Delete share
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function restore(string $token): JSONResponse {
return $this->response(fn () => ['share' => $this->shareService->delete(token: $token, restore: true)]);
}
/**
* Delete or restore share
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function lock(string $token): JSONResponse {
return $this->response(fn () => ['share' => $this->shareService->lock(token: $token)]);
}
/**
* Lock or unlock share
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function unlock(string $token): JSONResponse {
return $this->response(fn () => ['share' => $this->shareService->lock(token: $token, unlock: true)]);
}
@@ -139,8 +140,8 @@ public function unlock(string $token): JSONResponse {
/**
* Send invitation mails for a share
* Additionally send notification via notifications
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function sendInvitation(string $token): JSONResponse {
$share = $this->shareService->get($token);
return $this->response(fn () => [
@@ -152,8 +153,8 @@ public function sendInvitation(string $token): JSONResponse {
/**
* Send all invitation mails for a share and resolve groups
* Additionally send notification via notifications
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function sendAllInvitations(int $pollId): JSONResponse {
return $this->response(fn () => [
'poll' => $pollId,
@@ -163,8 +164,8 @@ public function sendAllInvitations(int $pollId): JSONResponse {
/**
* resolve contact group to individual shares
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function resolveGroup(string $token): JSONResponse {
return $this->response(fn () => [
'shares' => $this->shareService->resolveGroup($token)
diff --git a/lib/Controller/SubscriptionApiController.php b/lib/Controller/SubscriptionApiController.php
index 2fcb66c34..3c88742c6 100644
--- a/lib/Controller/SubscriptionApiController.php
+++ b/lib/Controller/SubscriptionApiController.php
@@ -28,9 +28,6 @@
use OCA\Polls\Exceptions\Exception;
use OCA\Polls\Service\SubscriptionService;
use OCP\AppFramework\Http;
-use OCP\AppFramework\Http\Attribute\CORS;
-use OCP\AppFramework\Http\Attribute\NoAdminRequired;
-use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
@@ -48,10 +45,10 @@ public function __construct(
/**
* Get subscription status
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function get(int $pollId): JSONResponse {
try {
return new JSONResponse([
@@ -65,10 +62,10 @@ public function get(int $pollId): JSONResponse {
/**
* Subscribe to poll
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function subscribe(int $pollId): JSONResponse {
try {
$this->subscriptionService->set(true, $pollId);
@@ -83,10 +80,10 @@ public function subscribe(int $pollId): JSONResponse {
/**
* Unsubscribe from poll
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function unsubscribe(int $pollId): JSONResponse {
try {
$this->subscriptionService->set(false, $pollId);
diff --git a/lib/Controller/SubscriptionController.php b/lib/Controller/SubscriptionController.php
index c66157ed4..fd60b5fd1 100644
--- a/lib/Controller/SubscriptionController.php
+++ b/lib/Controller/SubscriptionController.php
@@ -26,7 +26,6 @@
namespace OCA\Polls\Controller;
use OCA\Polls\Service\SubscriptionService;
-use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
@@ -44,8 +43,8 @@ public function __construct(
/**
* Get subscription status
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function get(int $pollId): JSONResponse {
return $this->response(fn () => [
'subscribed' => $this->subscriptionService->get($pollId)
@@ -54,8 +53,8 @@ public function get(int $pollId): JSONResponse {
/**
* subscribe
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function subscribe(int $pollId): JSONResponse {
return $this->response(fn () => [
'subscribed' => $this->subscriptionService->set(true, $pollId)
@@ -64,8 +63,8 @@ public function subscribe(int $pollId): JSONResponse {
/**
* Unsubscribe
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function unsubscribe(int $pollId): JSONResponse {
return $this->response(fn () => [
'subscribed' => $this->subscriptionService->set(false, $pollId)
diff --git a/lib/Controller/SystemController.php b/lib/Controller/SystemController.php
index c4ae75fa1..ae0354ad7 100644
--- a/lib/Controller/SystemController.php
+++ b/lib/Controller/SystemController.php
@@ -27,7 +27,6 @@
use OCA\Polls\Service\SystemService;
use OCP\AppFramework\Http;
-use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
@@ -45,8 +44,8 @@ public function __construct(
/**
* Get a combined list of NC users, groups and contacts
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function userSearch(string $query = ''): JSONResponse {
return new JSONResponse(['siteusers' => $this->systemService->getSiteUsersAndGroups(
$query)], Http::STATUS_OK);
diff --git a/lib/Controller/VoteApiController.php b/lib/Controller/VoteApiController.php
index 36dcc7ecd..0236d2271 100644
--- a/lib/Controller/VoteApiController.php
+++ b/lib/Controller/VoteApiController.php
@@ -29,9 +29,6 @@
use OCA\Polls\Service\VoteService;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Http;
-use OCP\AppFramework\Http\Attribute\CORS;
-use OCP\AppFramework\Http\Attribute\NoAdminRequired;
-use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
@@ -49,10 +46,10 @@ public function __construct(
/**
* Read all votes of a poll based on the poll id and return list as array
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function list(int $pollId): JSONResponse {
try {
return new JSONResponse(['votes' => $this->voteService->list($pollId)], Http::STATUS_OK);
@@ -65,10 +62,10 @@ public function list(int $pollId): JSONResponse {
/**
* Set vote answer
+ * @NoAdminRequired
+ * @CORS
+ * @NoCSRFRequired
*/
- #[CORS]
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function set(int $optionId, string $answer): JSONResponse {
try {
return new JSONResponse(['vote' => $this->voteService->set($optionId, $answer)], Http::STATUS_OK);
diff --git a/lib/Controller/VoteController.php b/lib/Controller/VoteController.php
index e908fe07e..315b2b8ab 100644
--- a/lib/Controller/VoteController.php
+++ b/lib/Controller/VoteController.php
@@ -26,8 +26,6 @@
namespace OCA\Polls\Controller;
use OCA\Polls\Service\VoteService;
-use OCP\AppFramework\Http\Attribute\NoAdminRequired;
-use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
@@ -45,33 +43,33 @@ public function __construct(
/**
* list votes per poll
+ * @NoAdminRequired
+ * @NoCSRFRequired
*/
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function list(int $pollId): JSONResponse {
return $this->response(fn () => ['votes' => $this->voteService->list($pollId)]);
}
/**
* set vote answer
+ * @NoAdminRequired
+ * @NoCSRFRequired
*/
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function set(int $optionId, string $setTo): JSONResponse {
return $this->response(fn () => ['vote' => $this->voteService->set($optionId, $setTo)]);
}
/**
* Remove user from poll
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function delete(int $pollId, string $userId = ''): JSONResponse {
return $this->response(fn () => ['deleted' => $this->voteService->delete($pollId, $userId)]);
}
/**
* Relete orphaned votes
+ * @NoAdminRequired
*/
- #[NoAdminRequired]
public function deleteOrphaned(int $pollId, string $userId = ''): JSONResponse {
return $this->response(fn () => ['deleted' => $this->voteService->delete($pollId, $userId, true)]);
}
diff --git a/lib/Controller/WatchController.php b/lib/Controller/WatchController.php
index 725cfb520..566fdb626 100644
--- a/lib/Controller/WatchController.php
+++ b/lib/Controller/WatchController.php
@@ -26,8 +26,6 @@
namespace OCA\Polls\Controller;
use OCA\Polls\Service\WatchService;
-use OCP\AppFramework\Http\Attribute\NoAdminRequired;
-use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
@@ -45,9 +43,9 @@ public function __construct(
/**
* Watch poll for updates
+ * @NoAdminRequired
+ * @NoCSRFRequired
*/
- #[NoAdminRequired]
- #[NoCSRFRequired]
public function watchPoll(int $pollId, ?int $offset): JSONResponse {
return $this->responseLong(fn () => ['updates' => $this->watchService->watchUpdates($pollId, $offset)]);
}
diff --git a/lib/Cron/JanitorCron.php b/lib/Cron/JanitorCron.php
index e2c4fcd81..b922a9569 100644
--- a/lib/Cron/JanitorCron.php
+++ b/lib/Cron/JanitorCron.php
@@ -34,6 +34,7 @@
use OCA\Polls\Model\Settings\AppSettings;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\TimedJob;
+use OCP\Server;
/**
* @psalm-api
@@ -52,10 +53,7 @@ public function __construct(
) {
parent::__construct($time);
parent::setInterval(86400); // run once a day
- $this->logMapper = $logMapper;
- $this->pollMapper = $pollMapper;
- $this->watchMapper = $watchMapper;
- $this->appSettings = new AppSettings;
+ $this->appSettings = Server::get(AppSettings::class);
}
/**
diff --git a/lib/Db/Poll.php b/lib/Db/Poll.php
index 62442d162..fb9f56277 100644
--- a/lib/Db/Poll.php
+++ b/lib/Db/Poll.php
@@ -135,6 +135,9 @@ class Poll extends EntityWithUser implements JsonSerializable {
protected bool $hasOrphanedVotes = false;
protected int $maxDate = 0;
protected int $minDate = 0;
+ protected int $currentUserVotes = 0;
+ protected string $userRole = "none";
+ protected ?int $isCurrentUserLocked = 0;
public function __construct() {
$this->addType('created', 'int');
@@ -152,6 +155,7 @@ public function __construct() {
$this->addType('lastInteraction', 'int');
$this->addType('maxDate', 'int');
$this->addType('minDate', 'int');
+ $this->addType('currentUserVotes', 'int');
$this->urlGenerator = Container::queryClass(IURLGenerator::class);
$this->userMapper = Container::queryClass(UserMapper::class);
$this->voteMapper = Container::queryClass(VoteMapper::class);
@@ -165,6 +169,7 @@ public function __construct() {
public function jsonSerialize(): array {
return [
'id' => $this->getId(),
+ 'type' => $this->getType(),
'title' => $this->getTitle(),
'description' => $this->getDescription(),
'descriptionSafe' => $this->getDescriptionSafe(),
@@ -182,13 +187,14 @@ public function jsonSerialize(): array {
'optionLimit' => $this->getOptionLimit(),
'proposalsExpire' => $this->getProposalsExpire(),
'showResults' => $this->getShowResults() === 'expired' ? Poll::SHOW_RESULTS_CLOSED : $this->getShowResults(),
- 'type' => $this->getType(),
'useNo' => $this->getUseNo(),
'voteLimit' => $this->getVoteLimit(),
'lastInteraction' => $this->getLastInteraction(),
'summary' => [
- 'orphanedVotes' => count($this->voteMapper->findOrphanedByPollandUser($this->id, $this->userMapper->getCurrentUserCached()->getId())),
- 'yesByCurrentUser' => count($this->voteMapper->getYesVotesByParticipant($this->getPollId(), $this->userMapper->getCurrentUserCached()->getId())),
+ 'orphanedVotes' => $this->getCurrentUserOrphanedVotes(),
+ 'yesByCurrentUser' => $this->getCurrentUserYesVotes(),
+ 'countVotes' => $this->getCurrentUserCountVotes(),
+ 'userRole' => $this->getUserRole(),
],
];
}
@@ -227,6 +233,13 @@ public function getExpired(): bool {
);
}
+ public function getUserRole(): string {
+ if ($this->userMapper->getCurrentUser()->getId() === $this->getOwner()) {
+ return 'owner';
+ }
+ return $this->userRole;
+ }
+
public function getVoteUrl(): string {
return $this->urlGenerator->linkToRouteAbsolute(
AppConstants::APP_ID . '.page.vote',
@@ -329,13 +342,28 @@ public function getRelevantThresholdNet(): int {
);
}
+ public function getCurrentUserCountVotes(): int {
+ return $this->currentUserVotes;
+ }
+
+ public function getIsCurrentUserLocked(): bool {
+ return (bool) $this->isCurrentUserLocked;
+ }
+
/**
* @psalm-return int<0, max>
*/
- public function getOrphanedVotes(): int {
+ public function getCurrentUserOrphanedVotes(): int {
return count($this->voteMapper->findOrphanedByPollandUser($this->id, $this->userMapper->getCurrentUserCached()->getId()));
}
+ /**
+ * @psalm-return int<0, max>
+ */
+ public function getCurrentUserYesVotes(): int {
+ return count($this->voteMapper->getYesVotesByParticipant($this->getPollId(), $this->userMapper->getCurrentUserCached()->getId()));
+ }
+
public function getDeadline(): int {
// if expiration is set return expiration date
if ($this->getExpire()) {
diff --git a/lib/Db/PollMapper.php b/lib/Db/PollMapper.php
index 93718d24c..12726301f 100644
--- a/lib/Db/PollMapper.php
+++ b/lib/Db/PollMapper.php
@@ -40,7 +40,10 @@ class PollMapper extends QBMapper {
/**
* @psalm-suppress PossiblyUnusedMethod
*/
- public function __construct(IDBConnection $db) {
+ public function __construct(
+ IDBConnection $db,
+ private UserMapper $userMapper,
+ ) {
parent::__construct($db, Poll::TABLE, Poll::class);
}
@@ -167,6 +170,7 @@ public function deleteByUserId(string $userId): void {
* Build the enhanced query with joined tables
*/
protected function buildQuery(): IQueryBuilder {
+ $currentUserId = $this->userMapper->getCurrentUser()->getId();
$qb = $this->db->getQueryBuilder();
$qb->select(self::TABLE . '.*')
@@ -174,10 +178,33 @@ protected function buildQuery(): IQueryBuilder {
// ->groupBy(self::TABLE . '.id')
->from($this->getTableName(), self::TABLE);
$this->joinOptionsForMaxDate($qb, self::TABLE);
+ $this->joinCurrentUserVotes($qb, self::TABLE, $currentUserId);
+ $this->joinUserRole($qb, self::TABLE, $currentUserId);
$qb->groupBy(self::TABLE . '.id');
return $qb;
}
+ /**
+ * Joins options to evaluate min and max option date for date polls
+ * if text poll or no options are set,
+ * the min value is the current time,
+ * the max value is null
+ */
+ protected function joinUserRole(IQueryBuilder &$qb, string $fromAlias, string $currentUserId): void {
+ $joinAlias = 'shares';
+ $qb->addSelect($qb->createFunction('coalesce(' . $joinAlias . '.type, "") AS user_role'));
+
+ $qb->leftJoin(
+ $fromAlias,
+ Share::TABLE,
+ $joinAlias,
+ $qb->expr()->andX(
+ $qb->expr()->eq($fromAlias . '.id', $joinAlias . '.poll_id'),
+ $qb->expr()->eq($joinAlias . '.user_id', $qb->createNamedParameter($currentUserId, IQueryBuilder::PARAM_STR)),
+ )
+ );
+ }
+
/**
* Joins options to evaluate min and max option date for date polls
* if text poll or no options are set,
@@ -188,8 +215,6 @@ protected function joinOptionsForMaxDate(IQueryBuilder &$qb, string $fromAlias):
$joinAlias = 'options';
$saveMin = (string) time();
- // force value into a MIN function to avoid grouping errors
- // $qb->selectAlias($qb->func()->max($joinAlias . '.timestamp'), 'max_date');
$qb->addSelect($qb->createFunction('coalesce(MAX(' . $joinAlias . '.timestamp), 0) AS max_date'))
->addSelect($qb->createFunction('coalesce(MIN(' . $joinAlias . '.timestamp), ' . $saveMin . ') AS min_date'));
@@ -201,4 +226,26 @@ protected function joinOptionsForMaxDate(IQueryBuilder &$qb, string $fromAlias):
);
}
+ /**
+ * Joins options to evaluate min and max option date for date polls
+ * if text poll or no options are set,
+ * the min value is the current time,
+ * the max value is null
+ */
+ protected function joinCurrentUserVotes(IQueryBuilder &$qb, string $fromAlias, $currentUserId): void {
+ $joinAlias = 'user_vote';
+ // force value into a MIN function to avoid grouping errors
+ $qb->selectAlias($qb->func()->count($joinAlias . '.vote_answer'), 'current_user_votes');
+
+ $qb->leftJoin(
+ $fromAlias,
+ Vote::TABLE,
+ $joinAlias,
+ $qb->expr()->andX(
+ $qb->expr()->eq($joinAlias . '.poll_id', $fromAlias . '.id'),
+ $qb->expr()->eq($joinAlias . '.user_id', $qb->createNamedParameter($currentUserId, IQueryBuilder::PARAM_STR)),
+ )
+ );
+ }
+
}
diff --git a/lib/Db/Share.php b/lib/Db/Share.php
index 868f7b42c..6c077681b 100644
--- a/lib/Db/Share.php
+++ b/lib/Db/Share.php
@@ -27,9 +27,9 @@
use JsonSerializable;
use OCA\Polls\AppConstants;
-use OCA\Polls\Helper\Container;
use OCA\Polls\Model\Settings\AppSettings;
use OCP\IURLGenerator;
+use OCP\Server;
/**
* @method int getId()
@@ -145,8 +145,8 @@ public function __construct() {
$this->addType('locked', 'int');
$this->addType('reminderSent', 'int');
$this->addType('deleted', 'int');
- $this->urlGenerator = Container::queryClass(IURLGenerator::class);
- $this->appSettings = new AppSettings;
+ $this->urlGenerator = Server::get(IURLGenerator::class);
+ $this->appSettings = Server::get(AppSettings::class);
}
/**
diff --git a/lib/Db/TableManager.php b/lib/Db/TableManager.php
index 730ff3172..56c88b808 100644
--- a/lib/Db/TableManager.php
+++ b/lib/Db/TableManager.php
@@ -179,14 +179,14 @@ public function createTable(string $tableName, array $columns): array {
foreach ($columns as $columnName => $columnDefinition) {
if ($table->hasColumn($columnName)) {
$column = $table->getColumn($columnName);
- if (Type::lookupName($column->getType()) !== $columnDefinition['type']) {
- $messages[] = 'Migrated type of ' . $table->getName() . '[\'' . $columnName . '\'] from ' . Type::lookupName($column->getType()) . ' to ' . $columnDefinition['type'];
+ if ($column->getType()->getName() !== $columnDefinition['type']) {
+ $messages[] = 'Migrated type of ' . $table->getName() . '[\'' . $columnName . '\'] from ' . $column->getType()->getName() . ' to ' . $columnDefinition['type'];
$column->setType(Type::getType($columnDefinition['type']));
}
$column->setOptions($columnDefinition['options']);
// force change to current options definition
- $table->modifyColumn($columnName, $columnDefinition['options']);
+ $table->changeColumn($columnName, $columnDefinition['options']);
} else {
$table->addColumn($columnName, $columnDefinition['type'], $columnDefinition['options']);
$messages[] = 'Added ' . $table->getName() . ', ' . $columnName . ' (' . $columnDefinition['type'] . ')';
diff --git a/lib/Db/UserMapper.php b/lib/Db/UserMapper.php
index 628aedd23..6a2e03ec9 100644
--- a/lib/Db/UserMapper.php
+++ b/lib/Db/UserMapper.php
@@ -127,13 +127,16 @@ public function getParticipant(string $userId, ?int $pollId = null): UserBase {
// just catch and continue if not found and try to find user by share;
}
- try {
- $share = $this->getShareByPollAndUser($userId, $pollId);
- return $this->getUserFromShare($share);
- } catch (ShareNotFoundException $e) {
- // User seems to be probaly deleted, use fake share
- return new Ghost($userId);
+ if ($pollId !== null) {
+ try {
+ $share = $this->getShareByPollAndUser($userId, $pollId);
+ return $this->getUserFromShare($share);
+ } catch (ShareNotFoundException $e) {
+ // User seems to be probably deleted, use fake share
+ return new Ghost($userId);
+ }
}
+ return new Ghost($userId);
}
/**
@@ -175,7 +178,7 @@ public function getUserFromUserBase(string $userId, ?int $pollId = null): User {
if ($user instanceof IUser) {
try {
// check if we find a share, where the user got admin rights for the particular poll
- if ($this->getShareByPollAndUser($userId, $pollId)->getType() === Share::TYPE_ADMIN) {
+ if (($pollId !== null) && $this->getShareByPollAndUser($userId, $pollId)->getType() === Share::TYPE_ADMIN) {
return new Admin($userId);
}
} catch (Exception $e) {
@@ -215,15 +218,15 @@ public function getUserObject(string $type, string $id, string $displayName = ''
private function getShareByToken(string $token): Share {
$qb = $this->db->getQueryBuilder();
-
+
$qb->select('*')
->from($this->getTableName())
->where($qb->expr()->eq('token', $qb->createNamedParameter($token, IQueryBuilder::PARAM_STR)));
-
+
return $this->findEntity($qb);
}
- private function getShareByPollAndUser(string $userId, ?int $pollId = null): Share {
+ private function getShareByPollAndUser(string $userId, int $pollId): Share {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
diff --git a/lib/Db/VoteMapper.php b/lib/Db/VoteMapper.php
index f614aa60e..85119df66 100644
--- a/lib/Db/VoteMapper.php
+++ b/lib/Db/VoteMapper.php
@@ -126,19 +126,6 @@ public function findParticipantsByPoll(int $pollId): array {
return $this->findEntities($qb);
}
-
- /**
- * @throws \OCP\AppFramework\Db\DoesNotExistException if not found
- * @return Vote[]
- * @psalm-return array
- */
- public function findParticipantsVotes(int $pollId, string $userId): array {
- $qb = $this->buildQuery();
- $qb->andWhere($qb->expr()->eq(self::TABLE . '.poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)))
- ->andWhere($qb->expr()->eq(self::TABLE . '.user_id', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
- return $this->findEntities($qb);
- }
-
public function deleteByPollAndUserId(int $pollId, string $userId): void {
$qb = $this->db->getQueryBuilder();
$qb->delete($this->getTableName())
diff --git a/lib/Helper/Container.php b/lib/Helper/Container.php
index 0a1d37a29..0faa8b2da 100644
--- a/lib/Helper/Container.php
+++ b/lib/Helper/Container.php
@@ -31,34 +31,27 @@
use OCA\Polls\Db\Share;
use OCA\Polls\Db\ShareMapper;
use OCP\App\IAppManager;
-use OCP\AppFramework\App;
use OCP\IL10N;
use OCP\L10N\IFactory;
-use Psr\Container\ContainerInterface;
+use OCP\Server;
abstract class Container {
- public static function getContainer(): ContainerInterface {
- $app = new App(AppConstants::APP_ID);
- return $app->getContainer();
- }
-
public static function queryClass(string $class): mixed {
- return self::getContainer()->get($class);
+ return Server::get($class);
}
public static function queryPoll(int $pollId): Poll {
- return self::queryClass(PollMapper::class)->find($pollId);
+ return Server::get(PollMapper::class)->find($pollId);
}
public static function findShare(int $pollId, string $userId): Share {
- return self::queryClass(ShareMapper::class)
- ->findByPollAndUser($pollId, $userId);
+ return Server::get(ShareMapper::class)->findByPollAndUser($pollId, $userId);
}
public static function getL10N(?string $lang = null): IL10N {
- return self::queryClass(IFactory::class)->get(AppConstants::APP_ID, $lang);
+ return Server::get(IFactory::class)->get(AppConstants::APP_ID, $lang);
}
public static function isAppEnabled(string $app): bool {
- return self::queryClass(IAppManager::class)->isEnabledForUser($app);
+ return Server::get(IAppManager::class)->isEnabledForUser($app);
}
}
diff --git a/lib/Middleware/RequestAttributesMiddleware.php b/lib/Middleware/RequestAttributesMiddleware.php
index 29f732c6a..845aa17e9 100644
--- a/lib/Middleware/RequestAttributesMiddleware.php
+++ b/lib/Middleware/RequestAttributesMiddleware.php
@@ -3,7 +3,6 @@
namespace OCA\Polls\Middleware;
use OCA\Polls\AppConstants;
-use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Attribute\PublicPage;
use OCP\AppFramework\Middleware;
use OCP\IRequest;
@@ -24,7 +23,7 @@ public function __construct(
) {
}
- public function beforeController(Controller $controller, string $methodName): void {
+ public function beforeController($controller, $methodName): void {
$reflectionMethod = new ReflectionMethod($controller, $methodName);
$clientId = $this->request->getHeader(self::CLIENT_ID_KEY);
$clientTimeZone = $this->request->getHeader(self::TIME_ZONE_KEY);
diff --git a/lib/Migration/TableSchema.php b/lib/Migration/TableSchema.php
index 0bd43720e..c9d9cab5d 100644
--- a/lib/Migration/TableSchema.php
+++ b/lib/Migration/TableSchema.php
@@ -283,13 +283,13 @@ public static function createOrUpdateSchema(ISchemaWrapper &$schema): array {
if ($table->hasColumn($columnName)) {
$column = $table->getColumn($columnName);
$column->setOptions($columnDefinition['options']);
- if (Type::lookupName($column->getType()) !== $columnDefinition['type']) {
+ if ($column->getType()->getName() !== $columnDefinition['type']) {
$messages[] = 'Migrating type of ' . $tableName . ', ' . $columnName . ' to ' . $columnDefinition['type'];
$column->setType(Type::getType($columnDefinition['type']));
}
// force change to current options definition
- $table->modifyColumn($columnName, $columnDefinition['options']);
+ $table->changeColumn($columnName, $columnDefinition['options']);
} else {
$table->addColumn($columnName, $columnDefinition['type'], $columnDefinition['options']);
}
diff --git a/lib/Migration/Version060100Date20240209073304.php b/lib/Migration/Version060100Date20240209073304.php
index b904caa01..7e1bbab2f 100644
--- a/lib/Migration/Version060100Date20240209073304.php
+++ b/lib/Migration/Version060100Date20240209073304.php
@@ -98,14 +98,14 @@ public function createTable(string $tableName, array $columns): array {
foreach ($columns as $columnName => $columnDefinition) {
if ($table->hasColumn($columnName)) {
$column = $table->getColumn($columnName);
- if (Type::lookupName($column->getType()) !== $columnDefinition['type']) {
- $messages[] = 'Migrated type of ' . $table->getName() . '[\'' . $columnName . '\'] from ' . Type::lookupName($column->getType()) . ' to ' . $columnDefinition['type'];
+ if ($column->getType()->getName() !== $columnDefinition['type']) {
+ $messages[] = 'Migrated type of ' . $table->getName() . '[\'' . $columnName . '\'] from ' . $column->getType()->getName() . ' to ' . $columnDefinition['type'];
$column->setType(Type::getType($columnDefinition['type']));
}
$column->setOptions($columnDefinition['options']);
// force change to current options definition
- $table->modifyColumn($columnName, $columnDefinition['options']);
+ $table->changeColumn($columnName, $columnDefinition['options']);
} else {
$table->addColumn($columnName, $columnDefinition['type'], $columnDefinition['options']);
$messages[] = 'Added ' . $table->getName() . ', ' . $columnName . ' (' . $columnDefinition['type'] . ')';
diff --git a/lib/Model/Acl.php b/lib/Model/Acl.php
index 00148e954..e4ea7087e 100644
--- a/lib/Model/Acl.php
+++ b/lib/Model/Acl.php
@@ -33,7 +33,6 @@
use OCA\Polls\Db\Share;
use OCA\Polls\Db\ShareMapper;
use OCA\Polls\Db\UserMapper;
-use OCA\Polls\Db\VoteMapper;
use OCA\Polls\Exceptions\ForbiddenException;
use OCA\Polls\Exceptions\InvalidPollIdException;
use OCA\Polls\Exceptions\NotFoundException;
@@ -70,6 +69,8 @@ class Acl implements JsonSerializable {
public const PERMISSION_ALL_ACCESS = 'allAccess';
private ?int $pollId = null;
private ?UserBase $currentUser = null;
+ // Cache whether the current poll has shares
+ private bool $noShare = false;
/**
@@ -82,14 +83,10 @@ public function __construct(
private ISession $session,
private ShareMapper $shareMapper,
private UserMapper $userMapper,
- private VoteMapper $voteMapper,
private ?Poll $poll = null,
private ?Share $share = null,
) {
$this->pollId = null;
- $this->poll = $poll;
- $this->share = $share;
- $this->appSettings = new AppSettings;
}
/**
@@ -148,13 +145,26 @@ public function setPollId(?int $pollId = null, string $permission = self::PERMIS
} else {
$this->pollId = $pollId;
}
-
+
$this->loadPoll();
$this->request($permission);
return $this;
}
+ /**
+ * Set poll id and load poll
+ * @return $this
+ */
+ public function setPoll(Poll $poll, string $permission = self::PERMISSION_POLL_VIEW): static {
+ $this->pollId = $poll->getId();
+ $this->poll = $poll;
+ $this->noShare = false;
+ $this->request($permission);
+
+ return $this;
+ }
+
public function getPoll(): ?Poll {
if ($this->getToken()) {
// first verify working share
@@ -173,7 +183,7 @@ public function getShare(): ?Share {
return $this->share;
}
-
+
/**
* load poll
* @throws NotFoundException Thrown if poll not found
@@ -187,6 +197,7 @@ private function loadPoll(): void {
try {
// otherwise load poll from db
$this->poll = $this->pollMapper->find((int) $this->pollId);
+ $this->noShare = false;
} catch (DoesNotExistException $e) {
throw new NotFoundException('Error loading poll with id ' . $this->pollId);
}
@@ -199,21 +210,31 @@ private function loadPoll(): void {
* and the pollId will get set to the share's pollId
*/
private function loadShare(): void {
+ if ($this->noShare) {
+ throw new ShareNotFoundException('No token was set for ACL');
+ }
+
// no token in session, try to find a user, who matches
if (!$this->getToken()) {
if ($this->getCurrentUser()->getIsLoggedIn()) {
// search for logged in user's share, load it and return
- $this->share = $this->shareMapper->findByPollAndUser($this->getPollId(), $this->getUserId());
+ try {
+ $this->share = $this->shareMapper->findByPollAndUser($this->getPollId(), $this->getUserId());
+ } catch (\Throwable $ex) {
+ $this->noShare = true;
+ throw $ex;
+ }
// store share in session for further validations
// $this->session->set(AppConstants::SESSION_KEY_SHARE_TOKEN, $this->share->getToken());
return;
} else {
$this->share = new Share();
+ $this->noShare = true;
// must fail, if no token is present and not logged in
throw new ShareNotFoundException('No token was set for ACL');
}
}
-
+
// if share is already cached, verify against session token
if ($this->share?->getToken() === $this->getToken()) {
return;
@@ -224,7 +245,7 @@ private function loadShare(): void {
// ensure, poll and currentUser get reset
$this->poll = null;
$this->currentUser = null;
-
+
// set the poll id based on the share
$this->pollId = $this->share->getPollId();
}
@@ -342,9 +363,7 @@ private function getIsInvolved(): bool {
* Returns true, if the current user is already a particitipant of the current poll.
*/
private function getIsParticipant(): bool {
- return count(
- $this->voteMapper->findParticipantsVotes($this->getPollId(), $this->getUserId())
- ) > 0;
+ return $this->getPoll()->getCurrentUserCountVotes() > 0;
}
/**
@@ -502,7 +521,7 @@ private function getAllowAddOptions(): bool {
* @return bool|null
*/
private function getAllowDeleteOption(?string $optionOwner, ?int $pollId) {
-
+
if (!$pollId) {
$this->logger->warning('Poll id missing');
return false;
@@ -519,7 +538,7 @@ private function getAllowDeleteOption(?string $optionOwner, ?int $pollId) {
$this->logger->warning('Option owner missing');
return false;
}
-
+
if ($this->matchUser($optionOwner)) {
return true;
diff --git a/lib/Model/Settings/AppSettings.php b/lib/Model/Settings/AppSettings.php
index d0451c763..6fce8d364 100644
--- a/lib/Model/Settings/AppSettings.php
+++ b/lib/Model/Settings/AppSettings.php
@@ -27,7 +27,6 @@
use JsonSerializable;
use OCA\Polls\AppConstants;
-use OCA\Polls\Helper\Container;
use OCA\Polls\Model\Group\Group;
use OCP\IConfig;
use OCP\IGroupManager;
@@ -64,16 +63,15 @@ class AppSettings implements JsonSerializable {
public const SETTING_UPDATE_TYPE_PERIODIC_POLLING = 'periodicPolling';
public const SETTING_UPDATE_TYPE_DEFAULT = self::SETTING_UPDATE_TYPE_NO_POLLING;
- private IConfig $config;
- private IGroupManager $groupManager;
- private IUserSession $session;
private string $userId = '';
+
+ public function __construct(
+ private IConfig $config,
+ private IGroupManager $groupManager,
+ private IUserSession $session,
- public function __construct() {
- $this->config = Container::queryClass(IConfig::class);
- $this->session = Container::queryClass(IUserSession::class);
- $this->userId = Container::queryClass(IUserSession::class)->getUser()?->getUId() ?? '';
- $this->groupManager = Container::queryClass(IGroupManager::class);
+ ) {
+ $this->userId = $this->session->getUser()?->getUId() ?? '';
}
// Getters
diff --git a/lib/Model/UserBase.php b/lib/Model/UserBase.php
index 875014ad8..a757d2805 100644
--- a/lib/Model/UserBase.php
+++ b/lib/Model/UserBase.php
@@ -43,6 +43,7 @@
use OCP\IGroupManager;
use OCP\IL10N;
use OCP\IUserSession;
+use OCP\Server;
use OCP\Share\IShare;
class UserBase implements \JsonSerializable {
@@ -89,11 +90,11 @@ public function __construct(
) {
$this->icon = 'icon-share';
$this->l10n = Container::getL10N();
- $this->groupManager = Container::queryClass(IGroupManager::class);
- $this->timeZone = Container::queryClass(IDateTimeZone::class);
- $this->userMapper = Container::queryClass(UserMapper::class);
- $this->userSession = Container::queryClass(IUserSession::class);
- $this->appSettings = Container::queryClass(AppSettings::class);
+ $this->groupManager = Server::get(IGroupManager::class);
+ $this->timeZone = Server::get(IDateTimeZone::class);
+ $this->userMapper = Server::get(UserMapper::class);
+ $this->userSession = Server::get(IUserSession::class);
+ $this->appSettings = Server::get(AppSettings::class);
}
public function getId(): string {
diff --git a/lib/Service/PollService.php b/lib/Service/PollService.php
index c8027c804..22e6d5ffa 100644
--- a/lib/Service/PollService.php
+++ b/lib/Service/PollService.php
@@ -81,7 +81,7 @@ public function list(): array {
$this->preferences = $this->preferencesService->get();
foreach ($polls as $poll) {
try {
- $this->acl->setPollId($poll->getId());
+ $this->acl->setPoll($poll);
$relevantThreshold = $poll->getRelevantThresholdNet() + $this->preferences->getRelevantOffsetTimestamp();
// mix poll settings, currentUser attributes, permissions and relevantThreshold into one array
diff --git a/lib/Service/SettingsService.php b/lib/Service/SettingsService.php
index f27cc2558..2df736761 100644
--- a/lib/Service/SettingsService.php
+++ b/lib/Service/SettingsService.php
@@ -28,13 +28,11 @@
use OCA\Polls\Model\Settings\AppSettings;
class SettingsService {
- private AppSettings $appSettings;
-
+
/**
* @psalm-suppress PossiblyUnusedMethod
*/
- public function __construct() {
- $this->appSettings = new AppSettings;
+ public function __construct(private AppSettings $appSettings) {
}
/**
diff --git a/lib/Service/WatchService.php b/lib/Service/WatchService.php
index a60af2cdb..c4518efdd 100644
--- a/lib/Service/WatchService.php
+++ b/lib/Service/WatchService.php
@@ -36,9 +36,7 @@
use OCP\ISession;
class WatchService {
- private AppSettings $appSettings;
- private Watch $watch;
-
+
/**
* @psalm-suppress PossiblyUnusedMethod
*/
@@ -46,9 +44,9 @@ public function __construct(
private ISession $session,
private WatchMapper $watchMapper,
private Acl $acl,
+ private AppSettings $appSettings,
+ private Watch $watch,
) {
- $this->appSettings = new AppSettings;
- $this->watch = new Watch;
}
/**
diff --git a/src/js/store/modules/polls.js b/src/js/store/modules/polls.js
index 515cdbda6..951805fac 100644
--- a/src/js/store/modules/polls.js
+++ b/src/js/store/modules/polls.js
@@ -32,6 +32,7 @@ const state = {
isPollCreationAllowed: false,
isComboAllowed: false,
currentCategoryId: 'all',
+ pollsLoading: false,
sort: {
by: 'created',
reverse: true,
@@ -141,6 +142,10 @@ const mutations = {
Object.assign(state, payload)
},
+ setLoading(state, loading) {
+ state.pollsLoading = loading ?? true
+ },
+
setFilter(state, payload) {
state.currentCategoryId = payload.currentCategoryId
},
@@ -195,6 +200,7 @@ const actions = {
async list(context) {
try {
+ context.commit('setLoading')
const response = await PollsAPI.getPolls()
context.commit('set', { list: response.data.list })
context.commit('setPollCreationAllowed', { pollCreationAllowed: response.data.pollCreationAllowed })
@@ -203,6 +209,8 @@ const actions = {
if (e?.code === 'ERR_CANCELED') return
console.error('Error loading polls', { error: e.response })
throw e
+ } finally {
+ context.commit('setLoading', false)
}
},
}
diff --git a/src/js/views/PollList.vue b/src/js/views/PollList.vue
index c19a96706..38f30ed74 100644
--- a/src/js/views/PollList.vue
+++ b/src/js/views/PollList.vue
@@ -30,7 +30,11 @@
-
+
+
+
@@ -38,8 +42,8 @@
-
-