From 414f62f0bc714f1526042b3fe7023890fa568b18 Mon Sep 17 00:00:00 2001 From: dartcafe Date: Thu, 16 May 2024 18:49:22 +0200 Subject: [PATCH 01/27] test - add shares Signed-off-by: dartcafe --- lib/Db/Poll.php | 3 +++ lib/Db/PollMapper.php | 46 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/lib/Db/Poll.php b/lib/Db/Poll.php index 0d63fcf04..1cdfc2e02 100644 --- a/lib/Db/Poll.php +++ b/lib/Db/Poll.php @@ -154,6 +154,8 @@ class Poll extends EntityWithUser implements JsonSerializable { protected int $maxDate = 0; protected int $minDate = 0; protected string $userRole = self::ROLE_NONE; + protected string $shareToken = ''; + protected ?string $groupShares = ''; // subqueried columns protected int $currentUserCountOrphanedVotes = 0; @@ -228,6 +230,7 @@ public function jsonSerialize(): array { 'orphanedVotes' => $this->getCurrentUserCountOrphanedVotes(), 'yesVotes' => $this->getCurrentUserCountVotesYes(), 'countVotes' => $this->getCurrentUserCountVotes(), + 'shareToken' => $this->getShareToken(), ], ]; } diff --git a/lib/Db/PollMapper.php b/lib/Db/PollMapper.php index 050506f83..77042df45 100644 --- a/lib/Db/PollMapper.php +++ b/lib/Db/PollMapper.php @@ -26,6 +26,10 @@ namespace OCA\Polls\Db; +// use Doctrine\DBAL\Platforms\MySQLPlatform; +use Doctrine\DBAL\Platforms\OraclePlatform; +use Doctrine\DBAL\Platforms\PostgreSQLPlatform; +// use Doctrine\DBAL\Platforms\SqlitePlatform; use OCA\Polls\UserSession; use OCP\AppFramework\Db\QBMapper; use OCP\DB\QueryBuilder\IParameter; @@ -188,7 +192,8 @@ protected function buildQuery(): IQueryBuilder { $this->joinOptionsForMaxDate($qb, self::TABLE); $this->joinUserRole($qb, self::TABLE, $currentUserId); - + $this->joinGroupShares($qb, self::TABLE); + // \OC::$server->getLogger()->error(json_encode($qb->getSQL()));; return $qb; } @@ -196,15 +201,18 @@ protected function buildQuery(): IQueryBuilder { * Joins shares to evaluate user role */ protected function joinUserRole(IQueryBuilder &$qb, string $fromAlias, string $currentUserId): void { - $joinAlias = 'shares'; - $emptyString = $qb->createNamedParameter("", IQueryBuilder::PARAM_STR); + $joinAlias = 'user_shares'; + $emptyString = $qb->expr()->literal(""); - $qb->addSelect($qb->createFunction('coalesce(' . $joinAlias . '.type, '. $emptyString . ') AS user_role')) + $qb->addSelect($qb->createFunction('coalesce(' . $joinAlias . '.type, ' . $emptyString . ') AS user_role')) ->addGroupBy($joinAlias . '.type'); $qb->selectAlias($joinAlias . '.locked', 'is_current_user_locked') ->addGroupBy($joinAlias . '.locked'); + $qb->addSelect($qb->createFunction('coalesce(' . $joinAlias . '.token, '. $emptyString . ') AS share_token')) + ->addGroupBy($joinAlias . '.token'); + $qb->leftJoin( $fromAlias, Share::TABLE, @@ -218,6 +226,36 @@ protected function joinUserRole(IQueryBuilder &$qb, string $fromAlias, string $c } + /** + * Join group shares + */ + protected function joinGroupShares(IQueryBuilder &$qb, string $fromAlias): void { + $joinAlias = 'group_shares'; + + if ($this->db->getDatabasePlatform() instanceof PostgreSQLPlatform) { + $qb->addSelect($qb->createFunction('string_agg(distinct ' . $joinAlias . '.user_id, \',\') AS group_shares')); + } elseif ($this->db->getDatabasePlatform() instanceof OraclePlatform) { + $qb->addSelect($qb->createFunction('listagg(distinct ' . $joinAlias . '.user_id, \',\') WITHIN GROUP (ORDER BY ' . $joinAlias . '.user_id) AS group_shares')); + // } elseif ($this->db->getDatabasePlatform() instanceof SqlitePlatform) { + // $qb->addSelect($qb->createFunction('group_concat( distinct ' . $joinAlias . '.user_id) AS group_shares')); + // } elseif ($this->db->getDatabasePlatform() instanceof MySQLPlatform) { + // $qb->addSelect($qb->createFunction('group_concat( distinct ' . $joinAlias . '.user_id) AS group_shares')); + } else { + $qb->addSelect($qb->createFunction('group_concat(distinct ' . $joinAlias . '.user_id SEPARATOR "\'") AS group_shares')); + } + + $qb->leftJoin( + $fromAlias, + Share::TABLE, + $joinAlias, + $qb->expr()->andX( + $qb->expr()->eq($fromAlias . '.id', $joinAlias . '.poll_id'), + $qb->expr()->eq($joinAlias . '.type', $qb->expr()->literal(Share::TYPE_GROUP)), + $qb->expr()->eq($joinAlias . '.deleted', $qb->expr()->literal(0, IQueryBuilder::PARAM_INT)), + ) + ); + } + /** * Joins options to evaluate min and max option date for date polls * if text poll or no options are set, From 8200274a191b7ca354585319e5c6242634360239 Mon Sep 17 00:00:00 2001 From: dartcafe Date: Thu, 16 May 2024 19:13:56 +0200 Subject: [PATCH 02/27] fix Signed-off-by: dartcafe --- lib/Db/Poll.php | 2 ++ lib/Db/PollMapper.php | 9 ++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/Db/Poll.php b/lib/Db/Poll.php index 1cdfc2e02..0cdc6acce 100644 --- a/lib/Db/Poll.php +++ b/lib/Db/Poll.php @@ -84,6 +84,8 @@ * Magic functions for joined columns * @method int getMinDate() * @method int getMaxDate() + * @method int getShareToken() + * @method int getGroupShares() * * Magic functions for subqueried columns * @method int getCurrentUserCountOrphanedVotes() diff --git a/lib/Db/PollMapper.php b/lib/Db/PollMapper.php index 77042df45..5ce839c5d 100644 --- a/lib/Db/PollMapper.php +++ b/lib/Db/PollMapper.php @@ -29,7 +29,7 @@ // use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Platforms\OraclePlatform; use Doctrine\DBAL\Platforms\PostgreSQLPlatform; -// use Doctrine\DBAL\Platforms\SqlitePlatform; +use Doctrine\DBAL\Platforms\SqlitePlatform; use OCA\Polls\UserSession; use OCP\AppFramework\Db\QBMapper; use OCP\DB\QueryBuilder\IParameter; @@ -193,7 +193,6 @@ protected function buildQuery(): IQueryBuilder { $this->joinOptionsForMaxDate($qb, self::TABLE); $this->joinUserRole($qb, self::TABLE, $currentUserId); $this->joinGroupShares($qb, self::TABLE); - // \OC::$server->getLogger()->error(json_encode($qb->getSQL()));; return $qb; } @@ -236,12 +235,12 @@ protected function joinGroupShares(IQueryBuilder &$qb, string $fromAlias): void $qb->addSelect($qb->createFunction('string_agg(distinct ' . $joinAlias . '.user_id, \',\') AS group_shares')); } elseif ($this->db->getDatabasePlatform() instanceof OraclePlatform) { $qb->addSelect($qb->createFunction('listagg(distinct ' . $joinAlias . '.user_id, \',\') WITHIN GROUP (ORDER BY ' . $joinAlias . '.user_id) AS group_shares')); - // } elseif ($this->db->getDatabasePlatform() instanceof SqlitePlatform) { - // $qb->addSelect($qb->createFunction('group_concat( distinct ' . $joinAlias . '.user_id) AS group_shares')); + } elseif ($this->db->getDatabasePlatform() instanceof SqlitePlatform) { + $qb->addSelect($qb->createFunction('group_concat( distinct ' . $joinAlias . '.user_id, ",") AS group_shares')); // } elseif ($this->db->getDatabasePlatform() instanceof MySQLPlatform) { // $qb->addSelect($qb->createFunction('group_concat( distinct ' . $joinAlias . '.user_id) AS group_shares')); } else { - $qb->addSelect($qb->createFunction('group_concat(distinct ' . $joinAlias . '.user_id SEPARATOR "\'") AS group_shares')); + $qb->addSelect($qb->createFunction('group_concat(distinct ' . $joinAlias . '.user_id SEPARATOR ",") AS group_shares')); } $qb->leftJoin( From 41f8d9fd9b639ef8bc847fb3c89d27f01a30cf8f Mon Sep 17 00:00:00 2001 From: dartcafe Date: Thu, 16 May 2024 19:20:56 +0200 Subject: [PATCH 03/27] move separator to const Signed-off-by: dartcafe --- lib/Db/PollMapper.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/Db/PollMapper.php b/lib/Db/PollMapper.php index 5ce839c5d..349b33898 100644 --- a/lib/Db/PollMapper.php +++ b/lib/Db/PollMapper.php @@ -42,6 +42,7 @@ */ class PollMapper extends QBMapper { public const TABLE = Poll::TABLE; + public const CONCAT_SEPARATOR = ","; /** * @psalm-suppress PossiblyUnusedMethod @@ -232,15 +233,15 @@ protected function joinGroupShares(IQueryBuilder &$qb, string $fromAlias): void $joinAlias = 'group_shares'; if ($this->db->getDatabasePlatform() instanceof PostgreSQLPlatform) { - $qb->addSelect($qb->createFunction('string_agg(distinct ' . $joinAlias . '.user_id, \',\') AS group_shares')); + $qb->addSelect($qb->createFunction('string_agg(distinct ' . $joinAlias . '.user_id, \''. self::CONCAT_SEPARATOR . '\') AS group_shares')); } elseif ($this->db->getDatabasePlatform() instanceof OraclePlatform) { - $qb->addSelect($qb->createFunction('listagg(distinct ' . $joinAlias . '.user_id, \',\') WITHIN GROUP (ORDER BY ' . $joinAlias . '.user_id) AS group_shares')); + $qb->addSelect($qb->createFunction('listagg(distinct ' . $joinAlias . '.user_id, \''. self::CONCAT_SEPARATOR . '\') WITHIN GROUP (ORDER BY ' . $joinAlias . '.user_id) AS group_shares')); } elseif ($this->db->getDatabasePlatform() instanceof SqlitePlatform) { - $qb->addSelect($qb->createFunction('group_concat( distinct ' . $joinAlias . '.user_id, ",") AS group_shares')); + $qb->addSelect($qb->createFunction('group_concat( distinct ' . $joinAlias . '.user_id, "'. self::CONCAT_SEPARATOR . '") AS group_shares')); // } elseif ($this->db->getDatabasePlatform() instanceof MySQLPlatform) { // $qb->addSelect($qb->createFunction('group_concat( distinct ' . $joinAlias . '.user_id) AS group_shares')); } else { - $qb->addSelect($qb->createFunction('group_concat(distinct ' . $joinAlias . '.user_id SEPARATOR ",") AS group_shares')); + $qb->addSelect($qb->createFunction('group_concat(distinct ' . $joinAlias . '.user_id SEPARATOR "'. self::CONCAT_SEPARATOR . '") AS group_shares')); } $qb->leftJoin( From fcb222eecc340ec9c6b5934fa861e847d06c4bf2 Mon Sep 17 00:00:00 2001 From: dartcafe Date: Thu, 16 May 2024 19:23:40 +0200 Subject: [PATCH 04/27] migrate xml-schema Signed-off-by: dartcafe --- tests/phpunit.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/phpunit.xml b/tests/phpunit.xml index f6bddda1a..197d44b2c 100644 --- a/tests/phpunit.xml +++ b/tests/phpunit.xml @@ -1,13 +1,13 @@ - - - - ../lib - - + ./Unit + + + ../lib + + From b934824871d2bc3e62eefc3ae107a2c824197b27 Mon Sep 17 00:00:00 2001 From: dartcafe Date: Thu, 16 May 2024 19:26:50 +0200 Subject: [PATCH 05/27] quiet cs-fixer Signed-off-by: dartcafe --- lib/Db/PollMapper.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Db/PollMapper.php b/lib/Db/PollMapper.php index 349b33898..84e9b71de 100644 --- a/lib/Db/PollMapper.php +++ b/lib/Db/PollMapper.php @@ -238,8 +238,8 @@ protected function joinGroupShares(IQueryBuilder &$qb, string $fromAlias): void $qb->addSelect($qb->createFunction('listagg(distinct ' . $joinAlias . '.user_id, \''. self::CONCAT_SEPARATOR . '\') WITHIN GROUP (ORDER BY ' . $joinAlias . '.user_id) AS group_shares')); } elseif ($this->db->getDatabasePlatform() instanceof SqlitePlatform) { $qb->addSelect($qb->createFunction('group_concat( distinct ' . $joinAlias . '.user_id, "'. self::CONCAT_SEPARATOR . '") AS group_shares')); - // } elseif ($this->db->getDatabasePlatform() instanceof MySQLPlatform) { - // $qb->addSelect($qb->createFunction('group_concat( distinct ' . $joinAlias . '.user_id) AS group_shares')); + // } elseif ($this->db->getDatabasePlatform() instanceof MySQLPlatform) { + // $qb->addSelect($qb->createFunction('group_concat( distinct ' . $joinAlias . '.user_id) AS group_shares')); } else { $qb->addSelect($qb->createFunction('group_concat(distinct ' . $joinAlias . '.user_id SEPARATOR "'. self::CONCAT_SEPARATOR . '") AS group_shares')); } From d986daf265d695eba3410631c63c057a8b27cb37 Mon Sep 17 00:00:00 2001 From: dartcafe Date: Thu, 16 May 2024 19:41:17 +0200 Subject: [PATCH 06/27] sqlite without delimiter? Signed-off-by: dartcafe --- lib/Db/PollMapper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Db/PollMapper.php b/lib/Db/PollMapper.php index 84e9b71de..096622a7c 100644 --- a/lib/Db/PollMapper.php +++ b/lib/Db/PollMapper.php @@ -237,7 +237,7 @@ protected function joinGroupShares(IQueryBuilder &$qb, string $fromAlias): void } elseif ($this->db->getDatabasePlatform() instanceof OraclePlatform) { $qb->addSelect($qb->createFunction('listagg(distinct ' . $joinAlias . '.user_id, \''. self::CONCAT_SEPARATOR . '\') WITHIN GROUP (ORDER BY ' . $joinAlias . '.user_id) AS group_shares')); } elseif ($this->db->getDatabasePlatform() instanceof SqlitePlatform) { - $qb->addSelect($qb->createFunction('group_concat( distinct ' . $joinAlias . '.user_id, "'. self::CONCAT_SEPARATOR . '") AS group_shares')); + $qb->addSelect($qb->createFunction('group_concat( distinct ' . $joinAlias . '.user_id) AS group_shares')); // } elseif ($this->db->getDatabasePlatform() instanceof MySQLPlatform) { // $qb->addSelect($qb->createFunction('group_concat( distinct ' . $joinAlias . '.user_id) AS group_shares')); } else { From 7888d32cad4743bb52c10a4e27db03649b90b235 Mon Sep 17 00:00:00 2001 From: dartcafe Date: Thu, 16 May 2024 20:44:38 +0200 Subject: [PATCH 07/27] try Signed-off-by: dartcafe --- lib/Db/PollMapper.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Db/PollMapper.php b/lib/Db/PollMapper.php index 096622a7c..14790c69f 100644 --- a/lib/Db/PollMapper.php +++ b/lib/Db/PollMapper.php @@ -237,9 +237,9 @@ protected function joinGroupShares(IQueryBuilder &$qb, string $fromAlias): void } elseif ($this->db->getDatabasePlatform() instanceof OraclePlatform) { $qb->addSelect($qb->createFunction('listagg(distinct ' . $joinAlias . '.user_id, \''. self::CONCAT_SEPARATOR . '\') WITHIN GROUP (ORDER BY ' . $joinAlias . '.user_id) AS group_shares')); } elseif ($this->db->getDatabasePlatform() instanceof SqlitePlatform) { - $qb->addSelect($qb->createFunction('group_concat( distinct ' . $joinAlias . '.user_id) AS group_shares')); + $qb->addSelect($qb->createFunction('group_concat(distinct ' . $joinAlias . '.user_id, \''. self::CONCAT_SEPARATOR . '\') AS group_shares')); // } elseif ($this->db->getDatabasePlatform() instanceof MySQLPlatform) { - // $qb->addSelect($qb->createFunction('group_concat( distinct ' . $joinAlias . '.user_id) AS group_shares')); + // $qb->addSelect($qb->createFunction('group_concat(distinct ' . $joinAlias . '.user_id SEPARATOR "'. self::CONCAT_SEPARATOR . '") AS group_shares')); } else { $qb->addSelect($qb->createFunction('group_concat(distinct ' . $joinAlias . '.user_id SEPARATOR "'. self::CONCAT_SEPARATOR . '") AS group_shares')); } From 964e4633b55b927258e2846ab02c584eb2b5646c Mon Sep 17 00:00:00 2001 From: dartcafe Date: Thu, 16 May 2024 22:09:15 +0200 Subject: [PATCH 08/27] another try Signed-off-by: dartcafe --- lib/Db/PollMapper.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/Db/PollMapper.php b/lib/Db/PollMapper.php index 14790c69f..725fdcd78 100644 --- a/lib/Db/PollMapper.php +++ b/lib/Db/PollMapper.php @@ -26,7 +26,7 @@ namespace OCA\Polls\Db; -// use Doctrine\DBAL\Platforms\MySQLPlatform; +use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Platforms\OraclePlatform; use Doctrine\DBAL\Platforms\PostgreSQLPlatform; use Doctrine\DBAL\Platforms\SqlitePlatform; @@ -234,12 +234,16 @@ protected function joinGroupShares(IQueryBuilder &$qb, string $fromAlias): void if ($this->db->getDatabasePlatform() instanceof PostgreSQLPlatform) { $qb->addSelect($qb->createFunction('string_agg(distinct ' . $joinAlias . '.user_id, \''. self::CONCAT_SEPARATOR . '\') AS group_shares')); + } elseif ($this->db->getDatabasePlatform() instanceof OraclePlatform) { $qb->addSelect($qb->createFunction('listagg(distinct ' . $joinAlias . '.user_id, \''. self::CONCAT_SEPARATOR . '\') WITHIN GROUP (ORDER BY ' . $joinAlias . '.user_id) AS group_shares')); + } elseif ($this->db->getDatabasePlatform() instanceof SqlitePlatform) { - $qb->addSelect($qb->createFunction('group_concat(distinct ' . $joinAlias . '.user_id, \''. self::CONCAT_SEPARATOR . '\') AS group_shares')); - // } elseif ($this->db->getDatabasePlatform() instanceof MySQLPlatform) { - // $qb->addSelect($qb->createFunction('group_concat(distinct ' . $joinAlias . '.user_id SEPARATOR "'. self::CONCAT_SEPARATOR . '") AS group_shares')); + $qb->addSelect($qb->createFunction('group_concat(replace(distinct ' . $joinAlias . '.user_id ,\'\',\'\'), \''. self::CONCAT_SEPARATOR . '\') AS group_shares')); + + } elseif ($this->db->getDatabasePlatform() instanceof MySQLPlatform) { + $qb->addSelect($qb->createFunction('group_concat(distinct ' . $joinAlias . '.user_id SEPARATOR "'. self::CONCAT_SEPARATOR . '") AS group_shares')); + } else { $qb->addSelect($qb->createFunction('group_concat(distinct ' . $joinAlias . '.user_id SEPARATOR "'. self::CONCAT_SEPARATOR . '") AS group_shares')); } From 2915f46883db8446a0bfb6f56ccea1bbd7fe335a Mon Sep 17 00:00:00 2001 From: dartcafe Date: Fri, 17 May 2024 21:04:35 +0200 Subject: [PATCH 09/27] rebuild Acl - Step 1 Signed-off-by: dartcafe --- lib/Controller/BasePublicController.php | 2 +- lib/Controller/PollApiController.php | 2 +- lib/Controller/PollController.php | 2 +- lib/Controller/PublicController.php | 2 +- lib/Model/Acl.php | 417 +------------------- lib/Model/AclLegacy.php | 494 ++++++++++++++++++++++++ lib/Service/CommentService.php | 2 +- lib/Service/OptionService.php | 2 +- lib/Service/PollService.php | 2 +- lib/Service/ShareService.php | 2 +- lib/Service/SubscriptionService.php | 2 +- lib/Service/VoteService.php | 2 +- lib/Service/WatchService.php | 2 +- 13 files changed, 526 insertions(+), 407 deletions(-) create mode 100644 lib/Model/AclLegacy.php diff --git a/lib/Controller/BasePublicController.php b/lib/Controller/BasePublicController.php index ccc5df928..a35986e8c 100644 --- a/lib/Controller/BasePublicController.php +++ b/lib/Controller/BasePublicController.php @@ -29,7 +29,7 @@ use OCA\Polls\Db\ShareMapper; use OCA\Polls\Exceptions\Exception; use OCA\Polls\Exceptions\NoUpdatesException; -use OCA\Polls\Model\Acl; +use OCA\Polls\Model\AclLegacy as Acl; use OCA\Polls\UserSession; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; diff --git a/lib/Controller/PollApiController.php b/lib/Controller/PollApiController.php index d249895c3..27ad39037 100644 --- a/lib/Controller/PollApiController.php +++ b/lib/Controller/PollApiController.php @@ -27,7 +27,7 @@ use OCA\Polls\AppConstants; use OCA\Polls\Exceptions\Exception; -use OCA\Polls\Model\Acl; +use OCA\Polls\Model\AclLegacy as Acl; use OCA\Polls\Service\PollService; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Http; diff --git a/lib/Controller/PollController.php b/lib/Controller/PollController.php index d48f97b96..9eb1b596d 100644 --- a/lib/Controller/PollController.php +++ b/lib/Controller/PollController.php @@ -25,7 +25,7 @@ namespace OCA\Polls\Controller; -use OCA\Polls\Model\Acl; +use OCA\Polls\Model\AclLegacy as Acl; use OCA\Polls\Model\Settings\AppSettings; use OCA\Polls\Service\MailService; use OCA\Polls\Service\OptionService; diff --git a/lib/Controller/PublicController.php b/lib/Controller/PublicController.php index 3277a4aea..92efbf8a9 100644 --- a/lib/Controller/PublicController.php +++ b/lib/Controller/PublicController.php @@ -28,7 +28,7 @@ use OCA\Polls\AppConstants; use OCA\Polls\Attributes\ShareTokenRequired; use OCA\Polls\Db\ShareMapper; -use OCA\Polls\Model\Acl; +use OCA\Polls\Model\AclLegacy as Acl; use OCA\Polls\Service\CommentService; use OCA\Polls\Service\MailService; use OCA\Polls\Service\OptionService; diff --git a/lib/Model/Acl.php b/lib/Model/Acl.php index 113f7e362..9ca3ca4d2 100644 --- a/lib/Model/Acl.php +++ b/lib/Model/Acl.php @@ -23,16 +23,10 @@ * */ - namespace OCA\Polls\Model; use JsonSerializable; -use OCA\Polls\Db\Poll; -use OCA\Polls\Db\PollMapper; -use OCA\Polls\Db\Share; -use OCA\Polls\Db\ShareMapper; use OCA\Polls\Exceptions\ForbiddenException; -use OCA\Polls\Exceptions\InsufficientAttributesException; use OCA\Polls\Model\Settings\AppSettings; use OCA\Polls\UserSession; @@ -43,36 +37,17 @@ */ class Acl implements JsonSerializable { public const PERMISSION_OVERRIDE = 'override_permission'; - public const PERMISSION_POLL_VIEW = 'view'; - public const PERMISSION_POLL_EDIT = 'edit'; - public const PERMISSION_POLL_DELETE = 'delete'; - public const PERMISSION_POLL_ARCHIVE = 'archive'; - public const PERMISSION_POLL_RESULTS_VIEW = 'seeResults'; - public const PERMISSION_POLL_MAILADDRESSES_VIEW = 'seeMailAddresses'; - public const PERMISSION_POLL_USERNAMES_VIEW = 'seeUserNames'; - public const PERMISSION_POLL_TAKEOVER = 'takeOver'; - public const PERMISSION_POLL_SUBSCRIBE = 'subscribe'; + public const PERMISSION_ALL_ACCESS = 'allAccess'; + public const PERMISSION_PUBLIC_SHARES = 'publicShares'; public const PERMISSION_POLL_CREATE = 'pollCreate'; + public const PERMISSION_POLL_MAILADDRESSES_VIEW = 'seeMailAddresses'; public const PERMISSION_POLL_DOWNLOAD = 'pollDownload'; - public const PERMISSION_COMMENT_ADD = 'addComment'; - public const PERMISSION_COMMENT_DELETE = 'deleteComment'; - public const PERMISSION_OPTIONS_ADD = 'addOptions'; - public const PERMISSION_OPTION_DELETE = 'deleteOption'; - public const PERMISSION_VOTE_EDIT = 'vote'; - public const PERMISSION_PUBLIC_SHARES = 'publicShares'; - public const PERMISSION_ALL_ACCESS = 'allAccess'; - - private ?int $pollId = null; - /** * @psalm-suppress PossiblyUnusedMethod */ public function __construct( private AppSettings $appSettings, - private PollMapper $pollMapper, - private ShareMapper $shareMapper, private UserSession $userSession, - private Poll $poll, ) { } @@ -81,86 +56,35 @@ public function __construct( */ public function jsonSerialize(): array { return [ - 'pollId' => $this->getPoll()->getId(), - 'pollExpired' => $this->getPoll()->getExpired(), - 'pollExpire' => $this->getPoll()->getExpire(), - 'currentUser' => $this->getCurrentUserArray(), + 'currentUser' => $this->userSession->getUser(), 'permissions' => $this->getPermissionsArray(), ]; } + /** + * Check perticular rights and inform via boolean value, if the right is granted or denied + */ + public function getIsAllowed(string $permission): bool { + return match ($permission) { + self::PERMISSION_OVERRIDE => true, + self::PERMISSION_ALL_ACCESS => $this->appSettings->getAllAccessAllowed(), + self::PERMISSION_PUBLIC_SHARES => $this->appSettings->getPublicSharesAllowed(), + self::PERMISSION_POLL_CREATE => $this->appSettings->getPollCreationAllowed(), + self::PERMISSION_POLL_MAILADDRESSES_VIEW => $this->appSettings->getAllowSeeMailAddresses(), + self::PERMISSION_POLL_DOWNLOAD => $this->appSettings->getPollDownloadAllowed(), + default => false, + }; + } + public function getPermissionsArray(): array { return [ - 'addOptions' => $this->getIsAllowed(self::PERMISSION_OPTIONS_ADD), 'allAccess' => $this->getIsAllowed(self::PERMISSION_ALL_ACCESS), - 'archive' => $this->getIsAllowed(self::PERMISSION_POLL_ARCHIVE), - 'comment' => $this->getIsAllowed(self::PERMISSION_COMMENT_ADD), - 'delete' => $this->getIsAllowed(self::PERMISSION_POLL_DELETE), - 'edit' => $this->getIsAllowed(self::PERMISSION_POLL_EDIT), - 'pollCreation' => $this->getIsAllowed(self::PERMISSION_POLL_CREATE), - 'pollDownload' => $this->getIsAllowed(self::PERMISSION_POLL_DOWNLOAD), 'publicShares' => $this->getIsAllowed(self::PERMISSION_PUBLIC_SHARES), - 'seeResults' => $this->getIsAllowed(self::PERMISSION_POLL_RESULTS_VIEW), - 'seeUsernames' => $this->getIsAllowed(self::PERMISSION_POLL_USERNAMES_VIEW), + 'pollCreation' => $this->getIsAllowed(self::PERMISSION_POLL_CREATE), 'seeMailAddresses' => $this->getIsAllowed(self::PERMISSION_POLL_MAILADDRESSES_VIEW), - 'subscribe' => $this->getIsAllowed(self::PERMISSION_POLL_SUBSCRIBE), - 'view' => $this->getIsAllowed(self::PERMISSION_POLL_VIEW), - 'vote' => $this->getIsAllowed(self::PERMISSION_VOTE_EDIT), - ]; - } - - public function getCurrentUserArray(): array { - return [ - 'displayName' => $this->getCurrentUser()->getDisplayName(), - 'hasVoted' => $this->getIsParticipant(), - 'isInvolved' => $this->getIsInvolved(), - 'isLoggedIn' => $this->userSession->getIsLoggedIn(), - 'isNoUser' => !$this->userSession->getIsLoggedIn(), - 'isOwner' => $this->getIsPollOwner(), - 'userId' => $this->getUserId(), + 'pollDownload' => $this->getIsAllowed(self::PERMISSION_POLL_DOWNLOAD), ]; } - /** - * Setters - */ - - /** - * Set poll id - */ - public function setPollId(int $pollId, string $permission = self::PERMISSION_POLL_VIEW): void { - $this->pollId = $pollId; - $this->request($permission); - } - - /** - * get poll - * @throws InsufficientAttributesException Thrown if stored pollId is null - */ - public function getPoll(): Poll { - if ($this->userSession->hasShare()) { - // if a share token is set, force usage of the share's pollId - $this->pollId = $this->getShare()->getPollId(); - } - - if (!boolval($this->pollId)) { - throw new InsufficientAttributesException('PollId is invalid: ' . (string) $this->pollId); - } - - if ($this->pollId !== $this->poll->getId()) { - // if pollId differs from pollId from loaded poll (or poll is not loaded), get poll from db - $this->poll = $this->pollMapper->find($this->pollId); - } - - return $this->poll; - } - - /** - * Get share - * load share from db by session stored token or rely on cached share - */ - private function getShare(): Share|null { - return $this->userSession->getShare(); - } /** * loads the current user from the userSession or returns the cached one @@ -176,41 +100,6 @@ public function getUserId(): string { return $this->getCurrentUser()->getId(); } - /** - * Checks, if the current user is the poll owner - */ - public function getIsPollOwner(): bool { - return ($this->getPoll()->getUserRole() === Poll::ROLE_OWNER); - } - - /** - * Check perticular rights and inform via boolean value, if the right is granted or denied - */ - public function getIsAllowed(string $permission): bool { - return match ($permission) { - self::PERMISSION_OVERRIDE => true, - self::PERMISSION_ALL_ACCESS => $this->appSettings->getAllAccessAllowed(), - self::PERMISSION_PUBLIC_SHARES => $this->appSettings->getPublicSharesAllowed(), - self::PERMISSION_POLL_CREATE => $this->appSettings->getPollCreationAllowed(), - self::PERMISSION_POLL_MAILADDRESSES_VIEW => $this->appSettings->getAllowSeeMailAddresses(), - self::PERMISSION_POLL_DOWNLOAD => $this->appSettings->getPollDownloadAllowed(), - self::PERMISSION_POLL_VIEW => $this->getAllowAccessPoll(), - self::PERMISSION_POLL_EDIT => $this->getAllowEditPoll(), - self::PERMISSION_POLL_DELETE => $this->getAllowDeletePoll(), - self::PERMISSION_POLL_ARCHIVE => $this->getAllowEditPoll(), - self::PERMISSION_POLL_TAKEOVER => $this->getAllowEditPoll(), - self::PERMISSION_POLL_SUBSCRIBE => $this->getAllowSubscribeToPoll(), - self::PERMISSION_POLL_RESULTS_VIEW => $this->getShowResults(), - self::PERMISSION_POLL_USERNAMES_VIEW => $this->getAllowEditPoll() || !$this->getPoll()->getAnonymous(), - self::PERMISSION_OPTIONS_ADD => $this->getAllowAddOptions(), - self::PERMISSION_OPTION_DELETE => $this->getAllowDeleteOption(), - self::PERMISSION_COMMENT_ADD => $this->getAllowComment(), - self::PERMISSION_COMMENT_DELETE => $this->getAllowDeleteComment(), - self::PERMISSION_VOTE_EDIT => $this->getAllowVote(), - default => false, - }; - } - /** * Request a permission level and get exception if denied * @throws ForbiddenException Thrown if access is denied @@ -221,274 +110,10 @@ public function request(string $permission): void { } } - /** - * getIsInvolved - Is current user involved in current poll? - * @return bool Returns true, if the current user is involved in the poll via share, as a participant or as the poll owner. - */ - private function getIsInvolved(): bool { - return ( - $this->getIsPollOwner() - || $this->getIsParticipant() - || $this->getIsPersonallyInvited()) - || $this->getIsInvitedViaGroupShare(); - } - - /** - * Check, if poll settings is set to open access for internal users - */ - private function getIsOpenPoll(): bool { - return $this->getPoll()->getAccess() === Poll::ACCESS_OPEN && $this->userSession->getIsLoggedIn(); - } - - /** - * getIsParticipant - Is user a participant? - * @return bool Returns true, if the current user is already a particitipant of the current poll. - */ - private function getIsParticipant(): bool { - return $this->getPoll()->getCurrentUserCountVotes() > 0; - } - - /** - * getIsInvitedViaGroupShare - Is the poll shared via group share? - * where the current user is member of. This only affects logged in users. - * @return bool Returns true, if the current poll contains a group share with a group, - */ - private function getIsInvitedViaGroupShare(): bool { - if (!$this->userSession->getIsLoggedIn()) { - return false; - } - - return count( - array_filter($this->shareMapper->findGroupShareByPoll($this->getPoll()->getId()), function ($item) { - return ($this->getCurrentUser()->getIsInGroup($item->getUserId())); - }) - ) > 0; - } - - /** - * getIsPersonallyInvited - Is the poll shared via user share with the current user? - * Checking via user role - * @return bool Returns true, if the current poll contains a user role which matches a share type - */ - private function getIsPersonallyInvited(): bool { - return in_array($this->getPoll()->getUserRole(), [ - Poll::ROLE_ADMIN, - Poll::ROLE_USER, - Poll::ROLE_EXTERNAL, - Poll::ROLE_EMAIL, - Poll::ROLE_CONTACT, - ]); - } - - /** - * The detailed checks - For the sake of readability, the queries and selections - * were kept detailed and with low complexity - */ - - /** - * Checks, if the user has delegated admin rights to edit poll settings via share - */ - private function getIsDelegatedAdmin(): bool { - return $this->getPoll()->getUserRole() === Poll::ROLE_ADMIN - && !$this->getPoll()->getIsCurrentUserLocked(); - } - - /** - * Checks, if user is allowed to edit the poll configuration - **/ - private function getAllowEditPoll(): bool { - // Console has god mode - if (defined('OC_CONSOLE')) { - return true; - } - - // owner is always allowed to edit the poll configuration - if ($this->getIsPollOwner()) { - return true; - } - - // user has delegated owner rights - if ($this->getIsDelegatedAdmin()) { - return true; - } - - // deny edit rights in all other cases - return false; - } - - /** - * Checks, if user is allowed to access (view) poll - */ - private function getAllowAccessPoll(): bool { - // edit rights include access to poll - if ($this->getAllowEditPoll()) { - return true; - } - - // No further access to poll, if it is deleted - if ($this->getPoll()->getDeleted()) { - return false; - } - - // grant access if poll poll is an open poll (for logged in users) - if ($this->getIsOpenPoll() && $this->userSession->getIsLoggedIn()) { - return true; - } - - // grant access if user is involved in poll in any way - if ($this->getIsInvolved()) { - return true; - } - - // return check result of an existing valid share for this user - return boolval($this->getShare()?->getToken()); - } - - /** - * Checks, if user is allowed to delete the poll - * includes the right to archive and take over - **/ - private function getAllowDeletePoll(): bool { - if ($this->getAllowEditPoll()) { - // users with edit rights are allowed to delete the poll - return true; - } - - // additionally site admins are allowed to delete polls, in all other cases deny poll deletion right - return $this->getCurrentUser()->getIsAdmin(); - } - - /** - * Checks, if user is allowed to add add vote options - **/ - private function getAllowAddOptions(): bool { - // Edit right includes adding new options - if ($this->getAllowEditPoll()) { - return true; - } - - // deny, if user has no access right to this poll - if (!$this->getAllowAccessPoll()) { - return false; - } - - // public shares are not allowed to add options - if ($this->getShare()?->getType() === Share::TYPE_PUBLIC) { - return false; - } - - // Request for option proposals is expired, deny - if ($this->getPoll()->getProposalsExpired()) { - return false; - } - - // Locked Users are not allowed to add options - if (boolval($this->getPoll()->getIsCurrentUserLocked())) { - return false; - } - - // Allow, if poll requests proposals - return $this->getPoll()->getAllowProposals() === Poll::PROPOSAL_ALLOW; - } - - /** - * Is current user allowed to delete options from poll - */ - private function getAllowDeleteOption(): bool { - return $this->getIsPollOwner() || $this->getIsDelegatedAdmin(); - } - /** * Compare $userId with current user's id */ public function matchUser(string $userId): bool { return $this->getCurrentUser()->getId() === $userId; } - - /** - * Checks, if user is allowed to see and write comments - **/ - private function getAllowComment(): bool { - // user has no access right to this poll - if (!$this->getAllowAccessPoll()) { - return false; - } - - // public shares are not allowed to comment - if ($this->getShare()?->getType() === Share::TYPE_PUBLIC) { - return false; - } - - // public shares are not allowed to comment - if (boolval($this->getPoll()->getIsCurrentUserLocked())) { - return false; - } - - return (bool) $this->getPoll()->getAllowComment(); - } - - /** - * Checks, if user is allowed to delete comments from poll - **/ - private function getAllowDeleteComment(): bool { - return $this->getAllowEditPoll(); - } - - /** - * Checks, if user is allowed to vote - **/ - private function getAllowVote(): bool { - // user has no access right to this poll - if (!$this->getAllowAccessPoll()) { - return false; - } - - // public shares are not allowed to vote - if ($this->getShare()?->getType() === Share::TYPE_PUBLIC) { - return false; - } - - // Locked users are not allowed to vote - if (boolval($this->getPoll()->getIsCurrentUserLocked())) { - return false; - } - - // deny votes, if poll is expired - return !$this->getPoll()->getExpired(); - } - - /** - * Checks, if user is allowed to subscribe to updates - **/ - private function getAllowSubscribeToPoll(): bool { - // user with access to poll are always allowed to subscribe - if (!$this->getAllowAccessPoll()) { - return false; - } - - return $this->getCurrentUser()->getHasEmail(); - } - - /** - * Checks, if user is allowed to see results of current poll - **/ - private function getShowResults(): bool { - // edit rights include access to results - if ($this->getAllowEditPoll()) { - return true; - } - - // no access to poll, deny - if (!$this->getAllowAccessPoll()) { - return false; - } - - // show results, when poll is closed - if ($this->getPoll()->getShowResults() === Poll::SHOW_RESULTS_CLOSED && $this->getPoll()->getExpired()) { - return true; - } - - // return poll settings - return $this->getPoll()->getShowResults() === Poll::SHOW_RESULTS_ALWAYS; - } } diff --git a/lib/Model/AclLegacy.php b/lib/Model/AclLegacy.php new file mode 100644 index 000000000..4021c7cb6 --- /dev/null +++ b/lib/Model/AclLegacy.php @@ -0,0 +1,494 @@ + + * + * @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\Model; + +use JsonSerializable; +use OCA\Polls\Db\Poll; +use OCA\Polls\Db\PollMapper; +use OCA\Polls\Db\Share; +use OCA\Polls\Db\ShareMapper; +use OCA\Polls\Exceptions\ForbiddenException; +use OCA\Polls\Exceptions\InsufficientAttributesException; +use OCA\Polls\Model\Settings\AppSettings; +use OCA\Polls\UserSession; + +/** + * Class AclLegacy + * @package OCA\Polls\Model\AclLegacy + * @deprecated + */ +class AclLegacy implements JsonSerializable { + public const PERMISSION_OVERRIDE = 'override_permission'; + public const PERMISSION_POLL_VIEW = 'view'; + public const PERMISSION_POLL_EDIT = 'edit'; + public const PERMISSION_POLL_DELETE = 'delete'; + public const PERMISSION_POLL_ARCHIVE = 'archive'; + public const PERMISSION_POLL_RESULTS_VIEW = 'seeResults'; + public const PERMISSION_POLL_MAILADDRESSES_VIEW = 'seeMailAddresses'; + public const PERMISSION_POLL_USERNAMES_VIEW = 'seeUserNames'; + public const PERMISSION_POLL_TAKEOVER = 'takeOver'; + public const PERMISSION_POLL_SUBSCRIBE = 'subscribe'; + public const PERMISSION_POLL_CREATE = 'pollCreate'; + public const PERMISSION_POLL_DOWNLOAD = 'pollDownload'; + public const PERMISSION_COMMENT_ADD = 'addComment'; + public const PERMISSION_COMMENT_DELETE = 'deleteComment'; + public const PERMISSION_OPTIONS_ADD = 'addOptions'; + public const PERMISSION_OPTION_DELETE = 'deleteOption'; + public const PERMISSION_VOTE_EDIT = 'vote'; + public const PERMISSION_PUBLIC_SHARES = 'publicShares'; + public const PERMISSION_ALL_ACCESS = 'allAccess'; + + private ?int $pollId = null; + + /** + * @psalm-suppress PossiblyUnusedMethod + */ + public function __construct( + private AppSettings $appSettings, + private PollMapper $pollMapper, + private ShareMapper $shareMapper, + private UserSession $userSession, + private Poll $poll, + ) { + } + + /** + * @psalm-suppress PossiblyUnusedMethod + */ + public function jsonSerialize(): array { + return [ + 'pollId' => $this->getPoll()->getId(), + 'pollExpired' => $this->getPoll()->getExpired(), + 'pollExpire' => $this->getPoll()->getExpire(), + 'currentUser' => $this->getCurrentUserArray(), + 'permissions' => $this->getPermissionsArray(), + ]; + } + + public function getPermissionsArray(): array { + return [ + 'addOptions' => $this->getIsAllowed(self::PERMISSION_OPTIONS_ADD), + 'allAccess' => $this->getIsAllowed(self::PERMISSION_ALL_ACCESS), + 'archive' => $this->getIsAllowed(self::PERMISSION_POLL_ARCHIVE), + 'comment' => $this->getIsAllowed(self::PERMISSION_COMMENT_ADD), + 'delete' => $this->getIsAllowed(self::PERMISSION_POLL_DELETE), + 'edit' => $this->getIsAllowed(self::PERMISSION_POLL_EDIT), + 'pollCreation' => $this->getIsAllowed(self::PERMISSION_POLL_CREATE), + 'pollDownload' => $this->getIsAllowed(self::PERMISSION_POLL_DOWNLOAD), + 'publicShares' => $this->getIsAllowed(self::PERMISSION_PUBLIC_SHARES), + 'seeResults' => $this->getIsAllowed(self::PERMISSION_POLL_RESULTS_VIEW), + 'seeUsernames' => $this->getIsAllowed(self::PERMISSION_POLL_USERNAMES_VIEW), + 'seeMailAddresses' => $this->getIsAllowed(self::PERMISSION_POLL_MAILADDRESSES_VIEW), + 'subscribe' => $this->getIsAllowed(self::PERMISSION_POLL_SUBSCRIBE), + 'view' => $this->getIsAllowed(self::PERMISSION_POLL_VIEW), + 'vote' => $this->getIsAllowed(self::PERMISSION_VOTE_EDIT), + ]; + } + + public function getCurrentUserArray(): array { + return [ + 'displayName' => $this->getCurrentUser()->getDisplayName(), + 'hasVoted' => $this->getIsParticipant(), + 'isInvolved' => $this->getIsInvolved(), + 'isLoggedIn' => $this->userSession->getIsLoggedIn(), + 'isNoUser' => !$this->userSession->getIsLoggedIn(), + 'isOwner' => $this->getIsPollOwner(), + 'userId' => $this->getUserId(), + ]; + } + /** + * Setters + */ + + /** + * Set poll id + */ + public function setPollId(int $pollId, string $permission = self::PERMISSION_POLL_VIEW): void { + $this->pollId = $pollId; + $this->request($permission); + } + + /** + * get poll + * @throws InsufficientAttributesException Thrown if stored pollId is null + */ + public function getPoll(): Poll { + if ($this->userSession->hasShare()) { + // if a share token is set, force usage of the share's pollId + $this->pollId = $this->getShare()->getPollId(); + } + + if (!boolval($this->pollId)) { + throw new InsufficientAttributesException('PollId is invalid: ' . (string) $this->pollId); + } + + if ($this->pollId !== $this->poll->getId()) { + // if pollId differs from pollId from loaded poll (or poll is not loaded), get poll from db + $this->poll = $this->pollMapper->find($this->pollId); + } + + return $this->poll; + } + + /** + * Get share + * load share from db by session stored token or rely on cached share + */ + private function getShare(): Share|null { + return $this->userSession->getShare(); + } + + /** + * loads the current user from the userSession or returns the cached one + */ + private function getCurrentUser(): UserBase { + return $this->userSession->getUser(); + } + + /** + * Shortcut for currentUser->userId + */ + public function getUserId(): string { + return $this->getCurrentUser()->getId(); + } + + /** + * Checks, if the current user is the poll owner + */ + public function getIsPollOwner(): bool { + return ($this->getPoll()->getUserRole() === Poll::ROLE_OWNER); + } + + /** + * Check perticular rights and inform via boolean value, if the right is granted or denied + */ + public function getIsAllowed(string $permission): bool { + return match ($permission) { + self::PERMISSION_OVERRIDE => true, + self::PERMISSION_ALL_ACCESS => $this->appSettings->getAllAccessAllowed(), + self::PERMISSION_PUBLIC_SHARES => $this->appSettings->getPublicSharesAllowed(), + self::PERMISSION_POLL_CREATE => $this->appSettings->getPollCreationAllowed(), + self::PERMISSION_POLL_MAILADDRESSES_VIEW => $this->appSettings->getAllowSeeMailAddresses(), + self::PERMISSION_POLL_DOWNLOAD => $this->appSettings->getPollDownloadAllowed(), + self::PERMISSION_POLL_VIEW => $this->getAllowAccessPoll(), + self::PERMISSION_POLL_EDIT => $this->getAllowEditPoll(), + self::PERMISSION_POLL_DELETE => $this->getAllowDeletePoll(), + self::PERMISSION_POLL_ARCHIVE => $this->getAllowEditPoll(), + self::PERMISSION_POLL_TAKEOVER => $this->getAllowEditPoll(), + self::PERMISSION_POLL_SUBSCRIBE => $this->getAllowSubscribeToPoll(), + self::PERMISSION_POLL_RESULTS_VIEW => $this->getShowResults(), + self::PERMISSION_POLL_USERNAMES_VIEW => $this->getAllowEditPoll() || !$this->getPoll()->getAnonymous(), + self::PERMISSION_OPTIONS_ADD => $this->getAllowAddOptions(), + self::PERMISSION_OPTION_DELETE => $this->getAllowDeleteOption(), + self::PERMISSION_COMMENT_ADD => $this->getAllowComment(), + self::PERMISSION_COMMENT_DELETE => $this->getAllowDeleteComment(), + self::PERMISSION_VOTE_EDIT => $this->getAllowVote(), + default => false, + }; + } + + /** + * Request a permission level and get exception if denied + * @throws ForbiddenException Thrown if access is denied + */ + public function request(string $permission): void { + if (!$this->getIsAllowed($permission)) { + throw new ForbiddenException('denied permission ' . $permission); + } + } + + /** + * getIsInvolved - Is current user involved in current poll? + * @return bool Returns true, if the current user is involved in the poll via share, as a participant or as the poll owner. + */ + private function getIsInvolved(): bool { + return ( + $this->getIsPollOwner() + || $this->getIsParticipant() + || $this->getIsPersonallyInvited()) + || $this->getIsInvitedViaGroupShare(); + } + + /** + * Check, if poll settings is set to open access for internal users + */ + private function getIsOpenPoll(): bool { + return $this->getPoll()->getAccess() === Poll::ACCESS_OPEN && $this->userSession->getIsLoggedIn(); + } + + /** + * getIsParticipant - Is user a participant? + * @return bool Returns true, if the current user is already a particitipant of the current poll. + */ + private function getIsParticipant(): bool { + return $this->getPoll()->getCurrentUserCountVotes() > 0; + } + + /** + * getIsInvitedViaGroupShare - Is the poll shared via group share? + * where the current user is member of. This only affects logged in users. + * @return bool Returns true, if the current poll contains a group share with a group, + */ + private function getIsInvitedViaGroupShare(): bool { + if (!$this->userSession->getIsLoggedIn()) { + return false; + } + + return count( + array_filter($this->shareMapper->findGroupShareByPoll($this->getPoll()->getId()), function ($item) { + return ($this->getCurrentUser()->getIsInGroup($item->getUserId())); + }) + ) > 0; + } + + /** + * getIsPersonallyInvited - Is the poll shared via user share with the current user? + * Checking via user role + * @return bool Returns true, if the current poll contains a user role which matches a share type + */ + private function getIsPersonallyInvited(): bool { + return in_array($this->getPoll()->getUserRole(), [ + Poll::ROLE_ADMIN, + Poll::ROLE_USER, + Poll::ROLE_EXTERNAL, + Poll::ROLE_EMAIL, + Poll::ROLE_CONTACT, + ]); + } + + /** + * The detailed checks - For the sake of readability, the queries and selections + * were kept detailed and with low complexity + */ + + /** + * Checks, if the user has delegated admin rights to edit poll settings via share + */ + private function getIsDelegatedAdmin(): bool { + return $this->getPoll()->getUserRole() === Poll::ROLE_ADMIN + && !$this->getPoll()->getIsCurrentUserLocked(); + } + + /** + * Checks, if user is allowed to edit the poll configuration + **/ + private function getAllowEditPoll(): bool { + // Console has god mode + if (defined('OC_CONSOLE')) { + return true; + } + + // owner is always allowed to edit the poll configuration + if ($this->getIsPollOwner()) { + return true; + } + + // user has delegated owner rights + if ($this->getIsDelegatedAdmin()) { + return true; + } + + // deny edit rights in all other cases + return false; + } + + /** + * Checks, if user is allowed to access (view) poll + */ + private function getAllowAccessPoll(): bool { + // edit rights include access to poll + if ($this->getAllowEditPoll()) { + return true; + } + + // No further access to poll, if it is deleted + if ($this->getPoll()->getDeleted()) { + return false; + } + + // grant access if poll poll is an open poll (for logged in users) + if ($this->getIsOpenPoll() && $this->userSession->getIsLoggedIn()) { + return true; + } + + // grant access if user is involved in poll in any way + if ($this->getIsInvolved()) { + return true; + } + + // return check result of an existing valid share for this user + return boolval($this->getShare()?->getToken()); + } + + /** + * Checks, if user is allowed to delete the poll + * includes the right to archive and take over + **/ + private function getAllowDeletePoll(): bool { + if ($this->getAllowEditPoll()) { + // users with edit rights are allowed to delete the poll + return true; + } + + // additionally site admins are allowed to delete polls, in all other cases deny poll deletion right + return $this->getCurrentUser()->getIsAdmin(); + } + + /** + * Checks, if user is allowed to add add vote options + **/ + private function getAllowAddOptions(): bool { + // Edit right includes adding new options + if ($this->getAllowEditPoll()) { + return true; + } + + // deny, if user has no access right to this poll + if (!$this->getAllowAccessPoll()) { + return false; + } + + // public shares are not allowed to add options + if ($this->getShare()?->getType() === Share::TYPE_PUBLIC) { + return false; + } + + // Request for option proposals is expired, deny + if ($this->getPoll()->getProposalsExpired()) { + return false; + } + + // Locked Users are not allowed to add options + if (boolval($this->getPoll()->getIsCurrentUserLocked())) { + return false; + } + + // Allow, if poll requests proposals + return $this->getPoll()->getAllowProposals() === Poll::PROPOSAL_ALLOW; + } + + /** + * Is current user allowed to delete options from poll + */ + private function getAllowDeleteOption(): bool { + return $this->getIsPollOwner() || $this->getIsDelegatedAdmin(); + } + + /** + * Compare $userId with current user's id + */ + public function matchUser(string $userId): bool { + return $this->getCurrentUser()->getId() === $userId; + } + + /** + * Checks, if user is allowed to see and write comments + **/ + private function getAllowComment(): bool { + // user has no access right to this poll + if (!$this->getAllowAccessPoll()) { + return false; + } + + // public shares are not allowed to comment + if ($this->getShare()?->getType() === Share::TYPE_PUBLIC) { + return false; + } + + // public shares are not allowed to comment + if (boolval($this->getPoll()->getIsCurrentUserLocked())) { + return false; + } + + return (bool) $this->getPoll()->getAllowComment(); + } + + /** + * Checks, if user is allowed to delete comments from poll + **/ + private function getAllowDeleteComment(): bool { + return $this->getAllowEditPoll(); + } + + /** + * Checks, if user is allowed to vote + **/ + private function getAllowVote(): bool { + // user has no access right to this poll + if (!$this->getAllowAccessPoll()) { + return false; + } + + // public shares are not allowed to vote + if ($this->getShare()?->getType() === Share::TYPE_PUBLIC) { + return false; + } + + // Locked users are not allowed to vote + if (boolval($this->getPoll()->getIsCurrentUserLocked())) { + return false; + } + + // deny votes, if poll is expired + return !$this->getPoll()->getExpired(); + } + + /** + * Checks, if user is allowed to subscribe to updates + **/ + private function getAllowSubscribeToPoll(): bool { + // user with access to poll are always allowed to subscribe + if (!$this->getAllowAccessPoll()) { + return false; + } + + return $this->getCurrentUser()->getHasEmail(); + } + + /** + * Checks, if user is allowed to see results of current poll + **/ + private function getShowResults(): bool { + // edit rights include access to results + if ($this->getAllowEditPoll()) { + return true; + } + + // no access to poll, deny + if (!$this->getAllowAccessPoll()) { + return false; + } + + // show results, when poll is closed + if ($this->getPoll()->getShowResults() === Poll::SHOW_RESULTS_CLOSED && $this->getPoll()->getExpired()) { + return true; + } + + // return poll settings + return $this->getPoll()->getShowResults() === Poll::SHOW_RESULTS_ALWAYS; + } +} diff --git a/lib/Service/CommentService.php b/lib/Service/CommentService.php index 7eaeee5e3..879e8b1b3 100644 --- a/lib/Service/CommentService.php +++ b/lib/Service/CommentService.php @@ -30,7 +30,7 @@ use OCA\Polls\Event\CommentAddEvent; use OCA\Polls\Event\CommentDeleteEvent; use OCA\Polls\Exceptions\ForbiddenException; -use OCA\Polls\Model\Acl; +use OCA\Polls\Model\AclLegacy as Acl; use OCP\EventDispatcher\IEventDispatcher; class CommentService { diff --git a/lib/Service/OptionService.php b/lib/Service/OptionService.php index 70110104e..bbf079b73 100644 --- a/lib/Service/OptionService.php +++ b/lib/Service/OptionService.php @@ -37,7 +37,7 @@ use OCA\Polls\Event\PollOptionReorderedEvent; use OCA\Polls\Exceptions\DuplicateEntryException; use OCA\Polls\Exceptions\InvalidPollTypeException; -use OCA\Polls\Model\Acl; +use OCA\Polls\Model\AclLegacy as Acl; use OCA\Polls\UserSession; use OCP\AppFramework\Db\DoesNotExistException; use OCP\DB\Exception; diff --git a/lib/Service/PollService.php b/lib/Service/PollService.php index f226e933c..9580d3b3d 100644 --- a/lib/Service/PollService.php +++ b/lib/Service/PollService.php @@ -46,7 +46,7 @@ use OCA\Polls\Exceptions\InvalidShowResultsException; use OCA\Polls\Exceptions\InvalidUsernameException; use OCA\Polls\Exceptions\UserNotFoundException; -use OCA\Polls\Model\Acl; +use OCA\Polls\Model\AclLegacy as Acl; use OCA\Polls\Model\Settings\AppSettings; use OCA\Polls\Model\UserBase; use OCA\Polls\UserSession; diff --git a/lib/Service/ShareService.php b/lib/Service/ShareService.php index d81845ad3..366b2e6c3 100644 --- a/lib/Service/ShareService.php +++ b/lib/Service/ShareService.php @@ -43,7 +43,7 @@ use OCA\Polls\Exceptions\NotFoundException; use OCA\Polls\Exceptions\ShareAlreadyExistsException; use OCA\Polls\Exceptions\ShareNotFoundException; -use OCA\Polls\Model\Acl; +use OCA\Polls\Model\AclLegacy as Acl; use OCA\Polls\Model\SentResult; use OCA\Polls\Model\UserBase; use OCA\Polls\UserSession; diff --git a/lib/Service/SubscriptionService.php b/lib/Service/SubscriptionService.php index 7f5339757..a6b2f1e33 100644 --- a/lib/Service/SubscriptionService.php +++ b/lib/Service/SubscriptionService.php @@ -28,7 +28,7 @@ use OCA\Polls\Db\Subscription; use OCA\Polls\Db\SubscriptionMapper; use OCA\Polls\Exceptions\ForbiddenException; -use OCA\Polls\Model\Acl; +use OCA\Polls\Model\AclLegacy as Acl; use OCP\AppFramework\Db\DoesNotExistException; use OCP\DB\Exception; diff --git a/lib/Service/VoteService.php b/lib/Service/VoteService.php index 7c0fcca6b..e91f417c6 100644 --- a/lib/Service/VoteService.php +++ b/lib/Service/VoteService.php @@ -31,7 +31,7 @@ use OCA\Polls\Db\VoteMapper; use OCA\Polls\Event\VoteSetEvent; use OCA\Polls\Exceptions\VoteLimitExceededException; -use OCA\Polls\Model\Acl; +use OCA\Polls\Model\AclLegacy as Acl; use OCA\Polls\UserSession; use OCP\AppFramework\Db\DoesNotExistException; use OCP\EventDispatcher\IEventDispatcher; diff --git a/lib/Service/WatchService.php b/lib/Service/WatchService.php index bce66f78f..64d173722 100644 --- a/lib/Service/WatchService.php +++ b/lib/Service/WatchService.php @@ -28,7 +28,7 @@ use OCA\Polls\Db\Watch; use OCA\Polls\Db\WatchMapper; use OCA\Polls\Exceptions\NoUpdatesException; -use OCA\Polls\Model\Acl; +use OCA\Polls\Model\AclLegacy as Acl; use OCA\Polls\Model\Settings\AppSettings; use OCA\Polls\UserSession; use OCP\AppFramework\Db\DoesNotExistException; From 96672b308ab3223111a3e4d6c9074c2df299c8b0 Mon Sep 17 00:00:00 2001 From: dartcafe Date: Fri, 17 May 2024 21:34:16 +0200 Subject: [PATCH 10/27] transfer permissions check to poll Signed-off-by: dartcafe --- lib/Db/Poll.php | 353 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 352 insertions(+), 1 deletion(-) diff --git a/lib/Db/Poll.php b/lib/Db/Poll.php index 0cdc6acce..4c585390f 100644 --- a/lib/Db/Poll.php +++ b/lib/Db/Poll.php @@ -29,6 +29,7 @@ use JsonSerializable; use OCA\Polls\AppConstants; +use OCA\Polls\Exceptions\ForbiddenException; use OCA\Polls\Exceptions\NoDeadLineException; use OCA\Polls\Helper\Container; use OCA\Polls\UserSession; @@ -85,7 +86,6 @@ * @method int getMinDate() * @method int getMaxDate() * @method int getShareToken() - * @method int getGroupShares() * * Magic functions for subqueried columns * @method int getCurrentUserCountOrphanedVotes() @@ -122,6 +122,20 @@ class Poll extends EntityWithUser implements JsonSerializable { public const ROLE_OWNER = 'owner'; public const ROLE_NONE = 'none'; + public const PERMISSION_OVERRIDE = 'override_permission'; + public const PERMISSION_POLL_VIEW = 'view'; + public const PERMISSION_POLL_EDIT = 'edit'; + public const PERMISSION_POLL_DELETE = 'delete'; + public const PERMISSION_POLL_ARCHIVE = 'archive'; + public const PERMISSION_POLL_RESULTS_VIEW = 'seeResults'; + public const PERMISSION_POLL_USERNAMES_VIEW = 'seeUserNames'; + public const PERMISSION_POLL_TAKEOVER = 'takeOver'; + public const PERMISSION_POLL_SUBSCRIBE = 'subscribe'; + public const PERMISSION_COMMENT_ADD = 'addComment'; + public const PERMISSION_COMMENT_DELETE = 'deleteComment'; + public const PERMISSION_OPTIONS_ADD = 'addOptions'; + public const PERMISSION_OPTION_DELETE = 'deleteOption'; + public const PERMISSION_VOTE_EDIT = 'vote'; private IURLGenerator $urlGenerator; @@ -315,6 +329,15 @@ public function setUserId(string $userId): void { $this->setOwner($userId); } + public function getGroupShares(): array { + if ($this->groupShares) { + // explode with separator and remove empty elements + return array_filter(explode(PollMapper::CONCAT_SEPARATOR, PollMapper::CONCAT_SEPARATOR . $this->groupShares)); + } + + return []; + } + public function getAccess() { if ($this->access === self::ACCESS_PUBLIC) { return self::ACCESS_OPEN; @@ -418,4 +441,332 @@ private function setMiscSettingsByKey(string $key, $value): void { $miscSettings[$key] = $value; $this->setMiscSettingsArray($miscSettings); } + + /** + * + * Check Permissions + * + */ + + /** + * Request a permission level and get exception if denied + * @throws ForbiddenException Thrown if access is denied + */ + public function request(string $permission): void { + if (!$this->getIsAllowed($permission)) { + throw new ForbiddenException('denied permission ' . $permission); + } + } + + + /** + * Check perticular rights and inform via boolean value, if the right is granted or denied + */ + public function getIsAllowed(string $permission): bool { + return match ($permission) { + self::PERMISSION_OVERRIDE => true, + self::PERMISSION_POLL_VIEW => $this->getAllowAccessPoll(), + self::PERMISSION_POLL_EDIT => $this->getAllowEditPoll(), + self::PERMISSION_POLL_DELETE => $this->getAllowDeletePoll(), + self::PERMISSION_POLL_ARCHIVE => $this->getAllowEditPoll(), + self::PERMISSION_POLL_TAKEOVER => $this->getAllowEditPoll(), + self::PERMISSION_POLL_SUBSCRIBE => $this->getAllowSubscribeToPoll(), + self::PERMISSION_POLL_RESULTS_VIEW => $this->getAllowShowResults(), + self::PERMISSION_POLL_USERNAMES_VIEW => $this->getAllowEditPoll() || !$this->getAnonymous(), + self::PERMISSION_OPTIONS_ADD => $this->getAllowAddOptions(), + self::PERMISSION_OPTION_DELETE => $this->getAllowDeleteOption(), + self::PERMISSION_COMMENT_ADD => $this->getAllowCommenting(), + self::PERMISSION_COMMENT_DELETE => $this->getAllowDeleteComment(), + self::PERMISSION_VOTE_EDIT => $this->getAllowVote(), + default => false, + }; + } + + public function getPermissionsArray(): array { + return [ + 'addOptions' => $this->getIsAllowed(self::PERMISSION_OPTIONS_ADD), + 'archive' => $this->getIsAllowed(self::PERMISSION_POLL_ARCHIVE), + 'comment' => $this->getIsAllowed(self::PERMISSION_COMMENT_ADD), + 'delete' => $this->getIsAllowed(self::PERMISSION_POLL_DELETE), + 'edit' => $this->getIsAllowed(self::PERMISSION_POLL_EDIT), + 'seeResults' => $this->getIsAllowed(self::PERMISSION_POLL_RESULTS_VIEW), + 'seeUsernames' => $this->getIsAllowed(self::PERMISSION_POLL_USERNAMES_VIEW), + 'subscribe' => $this->getIsAllowed(self::PERMISSION_POLL_SUBSCRIBE), + 'view' => $this->getIsAllowed(self::PERMISSION_POLL_VIEW), + 'vote' => $this->getIsAllowed(self::PERMISSION_VOTE_EDIT), + ]; + } + + /** + * getIsInvolved - Is current user involved in current poll? + * @return bool Returns true, if the current user is involved in the poll via share, as a participant or as the poll owner. + */ + private function getIsInvolved(): bool { + return ( + $this->getIsPollOwner() + || $this->getIsParticipant() + || $this->getIsPersonallyInvited()) + || $this->getIsInvitedViaGroupShare(); + } + + /** + * Check, if poll settings is set to open access for internal users + */ + private function getIsOpenPoll(): bool { + return $this->getAccess() === Poll::ACCESS_OPEN && $this->userSession->getIsLoggedIn(); + } + + /** + * getIsParticipant - Is user a participant? + * @return bool Returns true, if the current user is already a particitipant of the current poll. + */ + private function getIsParticipant(): bool { + return $this->getCurrentUserCountVotes() > 0; + } + + /** + * getIsInvitedViaGroupShare - Is the poll shared via group share? + * where the current user is member of. This only affects logged in users. + * @return bool Returns true, if the current poll contains a group share with a group, + */ + private function getIsInvitedViaGroupShare(): bool { + if (!$this->userSession->getIsLoggedIn()) { + return false; + } + + return count( + array_filter($this->getGroupShares(), function ($groupName) { + return ($this->getCurrentUser()->getIsInGroup($groupName)); + }) + ) > 0; + } + + /** + * getIsPersonallyInvited - Is the poll shared via user share with the current user? + * Checking via user role + * @return bool Returns true, if the current poll contains a user role which matches a share type + */ + private function getIsPersonallyInvited(): bool { + return in_array($this->getUserRole(), [ + Poll::ROLE_ADMIN, + Poll::ROLE_USER, + Poll::ROLE_EXTERNAL, + Poll::ROLE_EMAIL, + Poll::ROLE_CONTACT, + ]); + } + + /** + * The detailed checks - For the sake of readability, the queries and selections + * were kept detailed and with low complexity + */ + + /** + * Checks, if the user has delegated admin rights to edit poll settings via share + */ + private function getIsDelegatedAdmin(): bool { + return $this->getUserRole() === Poll::ROLE_ADMIN + && !$this->getIsCurrentUserLocked(); + } + + /** + * Checks, if user is allowed to edit the poll configuration + **/ + private function getAllowEditPoll(): bool { + // Console has god mode + if (defined('OC_CONSOLE')) { + return true; + } + + // owner is always allowed to edit the poll configuration + if ($this->getIsPollOwner()) { + return true; + } + + // user has delegated owner rights + if ($this->getIsDelegatedAdmin()) { + return true; + } + + // deny edit rights in all other cases + return false; + } + + /** + * Checks, if user is allowed to access (view) poll + */ + private function getAllowAccessPoll(): bool { + // edit rights include access to poll + if ($this->getAllowEditPoll()) { + return true; + } + + // No further access to poll, if it is deleted + if ($this->getDeleted()) { + return false; + } + + // grant access if poll poll is an open poll (for logged in users) + if ($this->getIsOpenPoll() && $this->userSession->getIsLoggedIn()) { + return true; + } + + // grant access if user is involved in poll in any way + if ($this->getIsInvolved()) { + return true; + } + + // return check result of an existing valid share for this user + return boolval($this->userSession->getShare()?->getPollId() === $this->getId()); + } + + /** + * Checks, if user is allowed to delete the poll + * includes the right to archive and take over + **/ + private function getAllowDeletePoll(): bool { + if ($this->getAllowEditPoll()) { + // users with edit rights are allowed to delete the poll + return true; + } + + // additionally site admins are allowed to delete polls, in all other cases deny poll deletion right + return $this->getCurrentUser()->getIsAdmin(); + } + + /** + * Checks, if user is allowed to add add vote options + **/ + private function getAllowAddOptions(): bool { + // Edit right includes adding new options + if ($this->getAllowEditPoll()) { + return true; + } + + // deny, if user has no access right to this poll + if (!$this->getAllowAccessPoll()) { + return false; + } + + // public shares are not allowed to add options + if ($this->userSession->getShare()?->getType() === Share::TYPE_PUBLIC) { + return false; + } + + // Request for option proposals is expired, deny + if ($this->getProposalsExpired()) { + return false; + } + + // Locked Users are not allowed to add options + if (boolval($this->getIsCurrentUserLocked())) { + return false; + } + + // Allow, if poll requests proposals + return $this->getAllowProposals() === Poll::PROPOSAL_ALLOW; + } + + /** + * Is current user allowed to delete options from poll + */ + private function getAllowDeleteOption(): bool { + return $this->getIsPollOwner() || $this->getIsDelegatedAdmin(); + } + + /** + * Compare $userId with current user's id + */ + public function matchUser(string $userId): bool { + return $this->getCurrentUser()->getId() === $userId; + } + + /** + * Checks, if user is allowed to see and write comments + **/ + private function getAllowCommenting(): bool { + // user has no access right to this poll + if (!$this->getAllowAccessPoll()) { + return false; + } + + // public shares are not allowed to comment + if ($this->userSession->getShare()?->getType() === Share::TYPE_PUBLIC) { + return false; + } + + // public shares are not allowed to comment + if (boolval($this->getIsCurrentUserLocked())) { + return false; + } + + return (bool) $this->getAllowComment(); + } + + /** + * Checks, if user is allowed to delete comments from poll + **/ + private function getAllowDeleteComment(): bool { + return $this->getAllowEditPoll(); + } + + /** + * Checks, if user is allowed to vote + **/ + private function getAllowVote(): bool { + // user has no access right to this poll + if (!$this->getAllowAccessPoll()) { + return false; + } + + // public shares are not allowed to vote + if ($this->userSession->getShare()?->getType() === Share::TYPE_PUBLIC) { + return false; + } + + // Locked users are not allowed to vote + if (boolval($this->getIsCurrentUserLocked())) { + return false; + } + + // deny votes, if poll is expired + return !$this->getExpired(); + } + + /** + * Checks, if user is allowed to subscribe to updates + **/ + private function getAllowSubscribeToPoll(): bool { + // user with access to poll are always allowed to subscribe + if (!$this->getAllowAccessPoll()) { + return false; + } + + return $this->getCurrentUser()->getHasEmail(); + } + + /** + * Checks, if user is allowed to see results of current poll + **/ + private function getAllowShowResults(): bool { + // edit rights include access to results + if ($this->getAllowEditPoll()) { + return true; + } + + // no access to poll, deny + if (!$this->getAllowAccessPoll()) { + return false; + } + + // show results, when poll is closed + if ($this->getShowResults() === Poll::SHOW_RESULTS_CLOSED && $this->getExpired()) { + return true; + } + + // return poll settings + return $this->getShowResults() === Poll::SHOW_RESULTS_ALWAYS; + } + + } From 3be2c049a39e2e1d9767cf8fd3dce746d02f0f42 Mon Sep 17 00:00:00 2001 From: dartcafe Date: Sun, 19 May 2024 22:00:49 +0200 Subject: [PATCH 11/27] changing poll structure and acl Signed-off-by: dartcafe --- CHANGELOG.md | 2 + docs/API_v1.0.md | 87 ++- lib/Controller/BasePublicController.php | 6 - lib/Controller/CommentApiController.php | 8 +- lib/Controller/CommentController.php | 8 +- lib/Controller/OptionApiController.php | 2 +- lib/Controller/OptionController.php | 2 +- lib/Controller/PollApiController.php | 9 +- lib/Controller/PollController.php | 15 +- lib/Controller/PublicController.php | 46 +- lib/Controller/VoteApiController.php | 4 +- lib/Controller/VoteController.php | 4 +- lib/Db/Poll.php | 148 +++--- lib/Db/Preferences.php | 6 +- lib/Model/Acl.php | 8 +- lib/Model/AclLegacy.php | 494 ------------------ lib/Service/CommentService.php | 56 +- lib/Service/MailService.php | 2 + lib/Service/OptionService.php | 100 ++-- lib/Service/PollService.php | 82 +-- lib/Service/ShareService.php | 22 +- lib/Service/SubscriptionService.php | 28 +- lib/Service/VoteService.php | 73 ++- lib/Service/WatchService.php | 17 +- src/js/Api/modules/polls.js | 4 +- src/js/App.vue | 5 +- src/js/components/Calendar/CalendarPeek.vue | 12 +- src/js/components/Cards/VoteInfoCards.vue | 26 +- .../Cards/modules/CardAddProposals.vue | 6 +- .../Cards/modules/CardClosedPoll.vue | 2 +- .../Cards/modules/CardHiddenParticipants.vue | 6 +- .../Cards/modules/CardLimitedVotes.vue | 16 +- .../Cards/modules/CardSendConfirmations.vue | 12 - src/js/components/Combo/ComboTable.vue | 2 +- src/js/components/Combo/VoteColumn.vue | 4 +- .../Configuration/ConfigAllowComment.vue | 4 +- .../Configuration/ConfigAllowMayBe.vue | 4 +- .../Configuration/ConfigAnonymous.vue | 4 +- .../Configuration/ConfigAutoReminder.vue | 4 +- .../Configuration/ConfigClosing.vue | 18 +- .../Configuration/ConfigDescription.vue | 4 +- .../Configuration/ConfigOptionLimit.vue | 8 +- .../Configuration/ConfigProposals.vue | 15 +- .../Configuration/ConfigShowResults.vue | 4 +- .../components/Configuration/ConfigTitle.vue | 6 +- .../components/Configuration/ConfigUseNo.vue | 4 +- .../Configuration/ConfigVoteLimit.vue | 6 +- src/js/components/Create/CreateDlg.vue | 12 +- src/js/components/Export/ExportPoll.vue | 32 +- .../Navigation/PollNavigationItems.vue | 10 +- src/js/components/Options/OptionItemOwner.vue | 2 +- src/js/components/Options/OptionProposals.vue | 7 +- src/js/components/Options/OptionsDate.vue | 11 +- .../components/Options/OptionsDateShift.vue | 6 +- src/js/components/Options/OptionsText.vue | 10 +- src/js/components/Poll/PollHeaderButtons.vue | 2 +- src/js/components/Poll/PollInfoLine.vue | 41 +- src/js/components/Poll/PollInformation.vue | 66 ++- src/js/components/PollList/PollItem.vue | 38 +- .../components/Public/PublicRegisterModal.vue | 1 - src/js/components/Shares/ShareItem.vue | 1 - .../components/Shares/ShareItemAllUsers.vue | 2 +- src/js/components/Shares/SharesList.vue | 8 +- src/js/components/Shares/SharesListLocked.vue | 13 +- src/js/components/Shares/SharesListUnsent.vue | 1 - .../components/SideBar/SideBarTabActivity.vue | 1 - .../components/SideBar/SideBarTabComments.vue | 2 +- .../SideBar/SideBarTabConfiguration.vue | 14 +- .../SideBar/SideBarTabDatePolls.vue | 2 +- .../components/SideBar/SideBarTabOptions.vue | 8 +- src/js/components/SideBar/SideBarTabShare.vue | 9 - src/js/components/User/UserMenu.vue | 2 +- src/js/components/VoteTable/VoteColumn.vue | 18 +- src/js/components/VoteTable/VoteItem.vue | 10 +- src/js/components/VoteTable/VoteTable.vue | 8 +- src/js/mixins/writePoll.js | 2 +- src/js/store/modules/poll.js | 107 ++-- src/js/store/modules/polls.js | 66 +-- src/js/store/modules/subModules/acl.js | 40 +- src/js/views/SideBar.vue | 4 +- src/js/views/SideBarCombo.vue | 7 - src/js/views/Vote.vue | 19 +- 82 files changed, 688 insertions(+), 1299 deletions(-) delete mode 100644 lib/Model/AclLegacy.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 506f60ebf..69ab3f1b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,12 @@ All notable changes to this project will be documented in this file. ## ## [7.1.0] - tbd +### !!! changed API structure, please refer to the documentation ### Fix - fixed counting of orphaned votes ### Changes - mainly performance improvements + - Changed API structure for polls, please refer to the documentation ### Performance - Added an option to allow to add polls to the navigation (default) - Limited polls inside the navigation to 6 items diff --git a/docs/API_v1.0.md b/docs/API_v1.0.md index 5dd5d33e6..0c3a6609e 100644 --- a/docs/API_v1.0.md +++ b/docs/API_v1.0.md @@ -49,32 +49,57 @@ Example calls: ``` ### Update poll -Send the full or a partial structure. -`expire' field is set to 0 to make it an endless poll (without an expiration date). This field can be set to a date in the future to automatically close the poll on that date. -A poll can be closed immediately by using the endpoint `poll/{pollId}/close` and reopened by using `poll/{pollId}/reopen` or by setting `expire` to 0. +Send the full or a partial structure of "configuration" (see return strucure below). +`expire` field is set to 0 to make it an endless poll (without an expiration date). This field can be set to a date in the future to automatically close the poll on that date. +A poll can be closed immediately by using the endpoint `poll/{pollId}/close` or by setting `expire` to a negative number and reopened by using `poll/{pollId}/reopen` or by setting `expire` to 0. ```json { "poll": { "title": "Changed Title", "description": "Updated description", - "expire": 1, + "expire": 0, "access": "private", "anonymous": true, "allowMaybe": true, "allowComment": true, "allowProposals": true, - "showResults": "never" - } + "showResults": "never", + "autoReminder": false, + "hideBookedUp": false, + "proposalsExpire": 0, + "useNo": true, + "maxVotesPerOption": 0, + "maxVotesPerUser": 0 + } } ``` ## Return value A poll newly created will look like this. +#### Notice: Only the attributes of the "configuration" section are changeable. + ```json { "poll": { - "title": "New Poll", - "description": "", - "descriptionSafe": "", + "id": 1, + "type": "datePoll", + "configuration": { + "title": "New Poll", + "description": "Description of poll", + "access":"private", + "allowComment":false, + "allowMaybe":false, + "allowProposals":"", + "anonymous":false, + "autoReminder":false, + "expire":0, + "hideBookedUp":false, + "proposalsExpire":0, + "showResults":"always", + "useNo":false, + "maxVotesPerOption":0, + "maxVotesPerUser":0 + }, + "descriptionSafe": "Description of poll", "owner": { "userId": "username", "displayName": "Username", @@ -93,33 +118,37 @@ A poll newly created will look like this. "icon":"icon-user", "categories":[] }, - "access":"private", - "allowComment":false, - "allowMaybe":false, - "allowProposals":"", - "anonymous":false, - "autoReminder":false, - "created":1714078369, - "deleted":false, - "expire":0, - "hideBookedUp":false, - "proposalsExpire":0, - "showResults":"always", - "useNo":false, - "limits": { - "maxVotesPerOption":0, - "maxVotesPerUser":0 - }, "status": { - "lastInteraction":1714078369 + "lastInteraction":1714078369, + "created":1714078369, + "deleted":false, + "expired": false, + "relevandThreshold" : 1714078369 }, "currentUserStatus": { "userRole":"owner", "isLocked":false, + "isLoggedIn": true, + "isNoUser": false, + "isOwner": true, + "userId": "username", "orphanedVotes":0, "yesVotes":0, - "countVotes":0 - } + "countVotes":0, + "shareToken": "" + }, + "permissions": { + "addOptions": true, + "archive": true, + "comment": true, + "delete": true, + "edit": true, + "seeResults": true, + "seeUsernames": true, + "subscribe": true, + "view": true, + "vote": true + } } } ``` diff --git a/lib/Controller/BasePublicController.php b/lib/Controller/BasePublicController.php index a35986e8c..45f5a7c29 100644 --- a/lib/Controller/BasePublicController.php +++ b/lib/Controller/BasePublicController.php @@ -26,11 +26,8 @@ namespace OCA\Polls\Controller; use Closure; -use OCA\Polls\Db\ShareMapper; use OCA\Polls\Exceptions\Exception; use OCA\Polls\Exceptions\NoUpdatesException; -use OCA\Polls\Model\AclLegacy as Acl; -use OCA\Polls\UserSession; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\Attribute\NoAdminRequired; @@ -44,9 +41,6 @@ class BasePublicController extends Controller { public function __construct( string $appName, IRequest $request, - protected Acl $acl, - protected ShareMapper $shareMapper, - protected UserSession $userSession, ) { parent::__construct($appName, $request); } diff --git a/lib/Controller/CommentApiController.php b/lib/Controller/CommentApiController.php index f30bdffd7..0bb1bd415 100644 --- a/lib/Controller/CommentApiController.php +++ b/lib/Controller/CommentApiController.php @@ -79,10 +79,8 @@ public function add(int $pollId, string $comment): JSONResponse { #[NoAdminRequired] #[NoCSRFRequired] public function delete(int $commentId): JSONResponse { - $comment = $this->commentService->get($commentId); - return $this->response(fn () => [ - 'comment' => $this->commentService->delete($comment)]); + 'comment' => $this->commentService->delete($commentId)]); } /** @@ -93,10 +91,8 @@ public function delete(int $commentId): JSONResponse { #[NoAdminRequired] #[NoCSRFRequired] public function restore(int $commentId): JSONResponse { - $comment = $this->commentService->get($commentId); - return $this->response(fn () => [ - 'comment' => $this->commentService->delete($comment, true) + 'comment' => $this->commentService->delete($commentId, true) ]); } } diff --git a/lib/Controller/CommentController.php b/lib/Controller/CommentController.php index d7054d01a..a6ad13896 100644 --- a/lib/Controller/CommentController.php +++ b/lib/Controller/CommentController.php @@ -71,10 +71,8 @@ public function add(int $pollId, string $message): JSONResponse { */ #[NoAdminRequired] public function delete(int $commentId): JSONResponse { - $comment = $this->commentService->get($commentId); - return $this->response(fn () => [ - 'comment' => $this->commentService->delete($comment) + 'comment' => $this->commentService->delete($commentId) ]); } @@ -84,10 +82,8 @@ public function delete(int $commentId): JSONResponse { */ #[NoAdminRequired] public function restore(int $commentId): JSONResponse { - $comment = $this->commentService->get($commentId); - return $this->response(fn () => [ - 'comment' => $this->commentService->delete($comment, true) + 'comment' => $this->commentService->delete($commentId, true) ]); } } diff --git a/lib/Controller/OptionApiController.php b/lib/Controller/OptionApiController.php index 903fbbca2..dd7f3cc23 100644 --- a/lib/Controller/OptionApiController.php +++ b/lib/Controller/OptionApiController.php @@ -66,7 +66,7 @@ public function list(int $pollId): JSONResponse { #[NoAdminRequired] #[NoCSRFRequired] public function add(int $pollId, int $timestamp = 0, string $pollOptionText = '', int $duration = 0): JSONResponse { - return $this->responseCreate(fn () => ['option' => $this->optionService->addForPoll($pollId, $timestamp, $pollOptionText, $duration)]); + return $this->responseCreate(fn () => ['option' => $this->optionService->add($pollId, $timestamp, $pollOptionText, $duration)]); } diff --git a/lib/Controller/OptionController.php b/lib/Controller/OptionController.php index 8135c4bf6..aaa9007f1 100644 --- a/lib/Controller/OptionController.php +++ b/lib/Controller/OptionController.php @@ -64,7 +64,7 @@ public function list(int $pollId): JSONResponse { */ #[NoAdminRequired] public function add(int $pollId, int $timestamp = 0, string $text = '', int $duration = 0): JSONResponse { - return $this->responseCreate(fn () => ['option' => $this->optionService->addForPoll($pollId, $timestamp, $text, $duration)]); + return $this->responseCreate(fn () => ['option' => $this->optionService->add($pollId, $timestamp, $text, $duration)]); } /** diff --git a/lib/Controller/PollApiController.php b/lib/Controller/PollApiController.php index 27ad39037..012872219 100644 --- a/lib/Controller/PollApiController.php +++ b/lib/Controller/PollApiController.php @@ -27,7 +27,7 @@ use OCA\Polls\AppConstants; use OCA\Polls\Exceptions\Exception; -use OCA\Polls\Model\AclLegacy as Acl; +use OCA\Polls\Model\Acl as Acl; use OCA\Polls\Service\PollService; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Http; @@ -90,9 +90,8 @@ public function get(int $pollId): JSONResponse { #[CORS] #[NoAdminRequired] #[NoCSRFRequired] - public function getAcl(int $pollId): JSONResponse { + public function getAcl(): JSONResponse { try { - $this->acl->setPollId($pollId); return new JSONResponse(['acl' => $this->acl], Http::STATUS_OK); } catch (DoesNotExistException $e) { return new JSONResponse(['error' => 'Not found'], Http::STATUS_NOT_FOUND); @@ -120,15 +119,13 @@ public function add(string $type, string $title): JSONResponse { /** * Update poll configuration * @param int $pollId Poll id - * @param array $poll poll config + * @param array $pollConfiguration poll config */ #[CORS] #[NoAdminRequired] #[NoCSRFRequired] public function update(int $pollId, array $poll): JSONResponse { try { - $this->acl->setPollId($pollId, Acl::PERMISSION_POLL_EDIT); - return new JSONResponse([ 'poll' => $this->pollService->update($pollId, $poll), 'acl' => $this->acl, diff --git a/lib/Controller/PollController.php b/lib/Controller/PollController.php index 9eb1b596d..7eac1cfa7 100644 --- a/lib/Controller/PollController.php +++ b/lib/Controller/PollController.php @@ -25,7 +25,8 @@ namespace OCA\Polls\Controller; -use OCA\Polls\Model\AclLegacy as Acl; +use OCA\Polls\Db\Poll; +use OCA\Polls\Model\Acl as Acl; use OCA\Polls\Model\Settings\AppSettings; use OCA\Polls\Service\MailService; use OCA\Polls\Service\OptionService; @@ -73,11 +74,9 @@ public function list(): JSONResponse { */ #[NoAdminRequired] public function get(int $pollId): JSONResponse { - $poll = $this->pollService->get($pollId); - $this->acl->setPollId($pollId); return $this->response(fn () => [ + 'poll' => $this->pollService->get($pollId), 'acl' => $this->acl, - 'poll' => $poll, ]); } @@ -98,10 +97,9 @@ public function add(string $type, string $title): JSONResponse { */ #[NoAdminRequired] public function update(int $pollId, array $poll): JSONResponse { - $this->acl->setPollId($pollId, Acl::PERMISSION_POLL_EDIT); return $this->response(fn () => [ 'poll' => $this->pollService->update($pollId, $poll), - 'acl' => $this->acl->setPollId($pollId), + 'acl' => $this->acl, ]); } @@ -111,7 +109,6 @@ public function update(int $pollId, array $poll): JSONResponse { */ #[NoAdminRequired] public function sendConfirmation(int $pollId): JSONResponse { - $this->acl->setPollId($pollId, Acl::PERMISSION_POLL_EDIT); return $this->response(fn () => [ 'confirmations' => $this->mailService->sendConfirmations($pollId), ]); @@ -144,7 +141,7 @@ public function delete(int $pollId): JSONResponse { public function close(int $pollId): JSONResponse { return $this->response(fn () => [ 'poll' => $this->pollService->close($pollId), - 'acl' => $this->acl->setPollId($pollId), + 'acl' => $this->acl, ]); } @@ -156,7 +153,7 @@ public function close(int $pollId): JSONResponse { public function reopen(int $pollId): JSONResponse { return $this->response(fn () => [ 'poll' => $this->pollService->reopen($pollId), - 'acl' => $this->acl->setPollId($pollId), + 'acl' => $this->acl, ]); } diff --git a/lib/Controller/PublicController.php b/lib/Controller/PublicController.php index 92efbf8a9..2311524fe 100644 --- a/lib/Controller/PublicController.php +++ b/lib/Controller/PublicController.php @@ -27,11 +27,11 @@ use OCA\Polls\AppConstants; use OCA\Polls\Attributes\ShareTokenRequired; -use OCA\Polls\Db\ShareMapper; -use OCA\Polls\Model\AclLegacy as Acl; +use OCA\Polls\Model\Acl as Acl; use OCA\Polls\Service\CommentService; use OCA\Polls\Service\MailService; use OCA\Polls\Service\OptionService; +use OCA\Polls\Service\PollService; use OCA\Polls\Service\ShareService; use OCA\Polls\Service\SubscriptionService; use OCA\Polls\Service\SystemService; @@ -57,19 +57,19 @@ class PublicController extends BasePublicController { public function __construct( string $appName, IRequest $request, - Acl $acl, - ShareMapper $shareMapper, - UserSession $userSession, + private Acl $acl, + private UserSession $userSession, private CommentService $commentService, private MailService $mailService, private OptionService $optionService, + private PollService $pollService, private ShareService $shareService, private SubscriptionService $subscriptionService, private SystemService $systemService, private VoteService $voteService, private WatchService $watchService ) { - parent::__construct($appName, $request, $acl, $shareMapper, $userSession); + parent::__construct($appName, $request); } /** @@ -96,11 +96,9 @@ public function votePage() { #[ShareTokenRequired] public function getPoll(): JSONResponse { return $this->response(function () { - $this->acl->request(Acl::PERMISSION_POLL_VIEW); - // load poll through acl return [ + 'poll' => $this->pollService->get($this->userSession->getShare()->getPollId()), 'acl' => $this->acl, - 'poll' => $this->acl->getPoll(), ]; }); } @@ -113,7 +111,7 @@ public function getPoll(): JSONResponse { #[ShareTokenRequired] public function watchPoll(?int $offset): JSONResponse { return $this->responseLong(fn () => [ - 'updates' => $this->watchService->watchUpdates(offset: $offset) + 'updates' => $this->watchService->watchUpdates($this->userSession->getShare()->getPollId(), $offset) ]); } @@ -136,7 +134,7 @@ public function getShare(string $token): JSONResponse { #[ShareTokenRequired] public function getVotes(): JSONResponse { return $this->response(fn () => [ - 'votes' => $this->voteService->list() + 'votes' => $this->voteService->list($this->userSession->getShare()->getPollId()) ]); } @@ -147,7 +145,7 @@ public function getVotes(): JSONResponse { #[ShareTokenRequired] public function deleteUser(): JSONResponse { return $this->response(fn () => [ - 'deleted' => $this->voteService->deleteCurrentUserFromPoll() + 'deleted' => $this->voteService->deleteUserFromPoll($this->userSession->getShare()->getPollId()) ]); } @@ -158,7 +156,7 @@ public function deleteUser(): JSONResponse { #[ShareTokenRequired] public function deleteOrphanedVotes(): JSONResponse { return $this->response(fn () => [ - 'deleted' => $this->voteService->deleteCurrentUserFromPoll(deleteOnlyOrphaned: true) + 'deleted' => $this->voteService->deleteUserFromPoll($this->userSession->getShare()->getPollId(), deleteOnlyOrphaned: true) ]); } @@ -169,7 +167,7 @@ public function deleteOrphanedVotes(): JSONResponse { #[ShareTokenRequired] public function getOptions(): JSONResponse { return $this->response(fn () => [ - 'options' => $this->optionService->list() + 'options' => $this->optionService->list($this->userSession->getShare()->getPollId()) ]); } @@ -183,7 +181,8 @@ public function getOptions(): JSONResponse { #[ShareTokenRequired] public function addOption(int $timestamp = 0, string $text = '', int $duration = 0): JSONResponse { return $this->responseCreate(fn () => [ - 'option' => $this->optionService->addForCurrentPoll( + 'option' => $this->optionService->add( + pollId: $this->userSession->getShare()->getPollId(), timestamp: $timestamp, pollOptionText: $text, duration: $duration, @@ -235,7 +234,7 @@ public function setVote(int $optionId, string $setTo): JSONResponse { #[ShareTokenRequired] public function getComments(): JSONResponse { return $this->response(fn () => [ - 'comments' => $this->commentService->list() + 'comments' => $this->commentService->list($this->userSession->getShare()->getPollId()) ]); } @@ -247,7 +246,7 @@ public function getComments(): JSONResponse { #[ShareTokenRequired] public function addComment(string $message): JSONResponse { return $this->response(fn () => [ - 'comment' => $this->commentService->add($message) + 'comment' => $this->commentService->add($message, $this->userSession->getShare()->getPollId()) ]); } @@ -258,9 +257,8 @@ public function addComment(string $message): JSONResponse { #[PublicPage] #[ShareTokenRequired] public function deleteComment(int $commentId): JSONResponse { - $comment = $this->commentService->get($commentId); return $this->response(fn () => [ - 'comment' => $this->commentService->delete($comment) + 'comment' => $this->commentService->delete($commentId) ]); } @@ -271,10 +269,8 @@ public function deleteComment(int $commentId): JSONResponse { #[PublicPage] #[ShareTokenRequired] public function restoreComment(int $commentId): JSONResponse { - $comment = $this->commentService->get($commentId); - return $this->response(fn () => [ - 'comment' => $this->commentService->delete($comment, true) + 'comment' => $this->commentService->delete($commentId, true) ]); } @@ -285,7 +281,7 @@ public function restoreComment(int $commentId): JSONResponse { #[ShareTokenRequired] public function getSubscription(): JSONResponse { return $this->response(fn () => [ - 'subscribed' => $this->subscriptionService->get() + 'subscribed' => $this->subscriptionService->get($this->userSession->getShare()->getPollId()) ]); } @@ -296,7 +292,7 @@ public function getSubscription(): JSONResponse { #[ShareTokenRequired] public function subscribe(): JSONResponse { return $this->response(fn () => [ - 'subscribed' => $this->subscriptionService->set(true) + 'subscribed' => $this->subscriptionService->set(true, $this->userSession->getShare()->getPollId()) ]); } @@ -307,7 +303,7 @@ public function subscribe(): JSONResponse { #[ShareTokenRequired] public function unsubscribe(): JSONResponse { return $this->response(fn () => [ - 'subscribed' => $this->subscriptionService->set(false) + 'subscribed' => $this->subscriptionService->set(false, $this->userSession->getShare()->getPollId()) ]); } diff --git a/lib/Controller/VoteApiController.php b/lib/Controller/VoteApiController.php index e56031ad9..90566c1e5 100644 --- a/lib/Controller/VoteApiController.php +++ b/lib/Controller/VoteApiController.php @@ -91,7 +91,7 @@ public function set(int $optionId, string $answer): JSONResponse { #[NoAdminRequired] #[NoCSRFRequired] public function delete(int $pollId, string $userId = ''): JSONResponse { - return $this->response(fn () => ['deleted' => $this->voteService->deletUserFromPoll($pollId, $userId)]); + return $this->response(fn () => ['deleted' => $this->voteService->deleteUserFromPoll($pollId, $userId)]); } /** @@ -103,6 +103,6 @@ public function delete(int $pollId, string $userId = ''): JSONResponse { #[NoAdminRequired] #[NoCSRFRequired] public function deleteOrphaned(int $pollId, string $userId = ''): JSONResponse { - return $this->response(fn () => ['deleted' => $this->voteService->deletUserFromPoll($pollId, $userId, deleteOnlyOrphaned: true)]); + return $this->response(fn () => ['deleted' => $this->voteService->deleteUserFromPoll($pollId, $userId, deleteOnlyOrphaned: true)]); } } diff --git a/lib/Controller/VoteController.php b/lib/Controller/VoteController.php index b5670e799..125ac5ba8 100644 --- a/lib/Controller/VoteController.php +++ b/lib/Controller/VoteController.php @@ -71,7 +71,7 @@ public function set(int $optionId, string $setTo): JSONResponse { */ #[NoAdminRequired] public function delete(int $pollId, string $userId = ''): JSONResponse { - return $this->response(fn () => ['deleted' => $this->voteService->deletUserFromPoll($pollId, $userId)]); + return $this->response(fn () => ['deleted' => $this->voteService->deleteUserFromPoll($pollId, $userId)]); } /** @@ -81,6 +81,6 @@ public function delete(int $pollId, string $userId = ''): JSONResponse { */ #[NoAdminRequired] public function deleteOrphaned(int $pollId, string $userId = ''): JSONResponse { - return $this->response(fn () => ['deleted' => $this->voteService->deletUserFromPoll($pollId, $userId, deleteOnlyOrphaned: true)]); + return $this->response(fn () => ['deleted' => $this->voteService->deleteUserFromPoll($pollId, $userId, deleteOnlyOrphaned: true)]); } } diff --git a/lib/Db/Poll.php b/lib/Db/Poll.php index 4c585390f..69fc025da 100644 --- a/lib/Db/Poll.php +++ b/lib/Db/Poll.php @@ -216,61 +216,97 @@ public function jsonSerialize(): array { return [ 'id' => $this->getId(), 'type' => $this->getType(), - 'title' => $this->getTitle(), - 'description' => $this->getDescription(), + // editable settings + 'configuration' => $this->getConfigurationArray(), + // read only properties 'descriptionSafe' => $this->getDescriptionSafe(), + // read only properties 'owner' => $this->getUser(), + 'status' => $this->getStatusArray(), + 'currentUserStatus' => $this->getCurrentUserStatus(), + 'permissions' => $this->getPermissionsArray(), + ]; + } + + public function getStatusArray(): array { + return [ + 'lastInteraction' => $this->getLastInteraction(), + 'created' => $this->getCreated(), + 'deleted' => boolval($this->getDeleted()), + 'expired' => $this->getExpired(), + 'relevantThreshold' => $this->getRelevantThreshold(), + ]; + } + public function getConfigurationArray(): array { + return [ + 'title' => $this->getTitle(), + 'description' => $this->getDescription(), 'access' => $this->getAccess(), 'allowComment' => boolval($this->getAllowComment()), 'allowMaybe' => boolval($this->getAllowMaybe()), 'allowProposals' => $this->getAllowProposals(), 'anonymous' => boolval($this->getAnonymous()), 'autoReminder' => $this->getAutoReminder(), - 'created' => $this->getCreated(), - 'deleted' => boolval($this->getDeleted()), 'expire' => $this->getExpire(), 'hideBookedUp' => boolval($this->getHideBookedUp()), 'proposalsExpire' => $this->getProposalsExpire(), 'showResults' => $this->getShowResults(), 'useNo' => boolval($this->getUseNo()), - 'limits' => [ - 'maxVotesPerOption' => $this->getOptionLimit(), - 'maxVotesPerUser' => $this->getVoteLimit(), - ], - 'status' => [ - 'lastInteraction' => $this->getLastInteraction(), - ], - 'currentUserStatus' => [ - 'userRole' => $this->getUserRole(), - 'isLocked' => boolval($this->getIsCurrentUserLocked()), - 'orphanedVotes' => $this->getCurrentUserCountOrphanedVotes(), - 'yesVotes' => $this->getCurrentUserCountVotesYes(), - 'countVotes' => $this->getCurrentUserCountVotes(), - 'shareToken' => $this->getShareToken(), - ], + 'maxVotesPerOption' => $this->getOptionLimit(), + 'maxVotesPerUser' => $this->getVoteLimit(), ]; } + public function getCurrentUserStatus(): array { + return [ + 'userRole' => $this->getUserRole(), + 'isLocked' => boolval($this->getIsCurrentUserLocked()), + 'isInvolved' => $this->getIsInvolved(), + 'isLoggedIn' => $this->userSession->getIsLoggedIn(), + 'isNoUser' => !$this->userSession->getIsLoggedIn(), + 'isOwner' => $this->getIsPollOwner(), + 'userId' => $this->getUserId(), + 'orphanedVotes' => $this->getCurrentUserCountOrphanedVotes(), + 'yesVotes' => $this->getCurrentUserCountVotesYes(), + 'countVotes' => $this->getCurrentUserCountVotes(), + 'shareToken' => $this->getShareToken(), + ]; + } + public function getPermissionsArray(): array { + return [ + 'addOptions' => $this->getIsAllowed(self::PERMISSION_OPTIONS_ADD), + 'archive' => $this->getIsAllowed(self::PERMISSION_POLL_ARCHIVE), + 'comment' => $this->getIsAllowed(self::PERMISSION_COMMENT_ADD), + 'delete' => $this->getIsAllowed(self::PERMISSION_POLL_DELETE), + 'edit' => $this->getIsAllowed(self::PERMISSION_POLL_EDIT), + 'seeResults' => $this->getIsAllowed(self::PERMISSION_POLL_RESULTS_VIEW), + 'seeUsernames' => $this->getIsAllowed(self::PERMISSION_POLL_USERNAMES_VIEW), + 'subscribe' => $this->getIsAllowed(self::PERMISSION_POLL_SUBSCRIBE), + 'view' => $this->getIsAllowed(self::PERMISSION_POLL_VIEW), + 'vote' => $this->getIsAllowed(self::PERMISSION_VOTE_EDIT), + ]; + } + + /** * @return static */ - public function deserializeArray(array $array): self { - $this->setAccess($array['access'] ?? $this->getAccess()); - $this->setAllowComment($array['allowComment'] ?? $this->getAllowComment()); - $this->setAllowMaybe($array['allowMaybe'] ?? $this->getAllowMaybe()); - $this->setAllowProposals($array['allowProposals'] ?? $this->getAllowProposals()); - $this->setAnonymous($array['anonymous'] ?? $this->getAnonymous()); - $this->setAutoReminder($array['autoReminder'] ?? $this->getAutoReminder()); - $this->setDescription($array['description'] ?? $this->getDescription()); - $this->setDeleted($array['deleted'] ?? $this->getDeleted()); - $this->setExpire($array['expire'] ?? $this->getExpire()); - $this->setHideBookedUp($array['hideBookedUp'] ?? $this->getHideBookedUp()); - $this->setProposalsExpire($array['proposalsExpire'] ?? $this->getProposalsExpire()); - $this->setShowResults($array['showResults'] ?? $this->getShowResults()); - $this->setTitle($array['title'] ?? $this->getTitle()); - $this->setUseNo($array['useNo'] ?? $this->getUseNo()); - $this->setOptionLimit($array['limits']['maxVotesPerOption'] ?? $this->getOptionLimit()); - $this->setVoteLimit($array['limits']['maxVotesPerUser'] ?? $this->getVoteLimit()); + public function deserializeArray(array $pollConfiguration): self { + $this->setTitle($pollConfiguration['title'] ?? $this->getTitle()); + $this->setDescription($pollConfiguration['description'] ?? $this->getDescription()); + $this->setAccess($pollConfiguration['access'] ?? $this->getAccess()); + $this->setAllowComment($pollConfiguration['allowComment'] ?? $this->getAllowComment()); + $this->setAllowMaybe($pollConfiguration['allowMaybe'] ?? $this->getAllowMaybe()); + $this->setAllowProposals($pollConfiguration['allowProposals'] ?? $this->getAllowProposals()); + $this->setAnonymous($pollConfiguration['anonymous'] ?? $this->getAnonymous()); + $this->setAutoReminder($pollConfiguration['autoReminder'] ?? $this->getAutoReminder()); + $this->setExpire($pollConfiguration['expire'] ?? $this->getExpire()); + $this->setHideBookedUp($pollConfiguration['hideBookedUp'] ?? $this->getHideBookedUp()); + $this->setProposalsExpire($pollConfiguration['proposalsExpire'] ?? $this->getProposalsExpire()); + $this->setShowResults($pollConfiguration['showResults'] ?? $this->getShowResults()); + $this->setUseNo($pollConfiguration['useNo'] ?? $this->getUseNo()); + $this->setOptionLimit($pollConfiguration['maxVotesPerOption'] ?? $this->getOptionLimit()); + $this->setVoteLimit($pollConfiguration['maxVotesPerUser'] ?? $this->getVoteLimit()); return $this; } @@ -406,7 +442,7 @@ public function getTimeToDeadline(int $time = 0): int { throw new NoDeadLineException(); } - public function getRelevantThresholdNet(): int { + public function getRelevantThreshold(): int { return max( $this->getCreated(), $this->getLastInteraction(), @@ -443,12 +479,12 @@ private function setMiscSettingsByKey(string $key, $value): void { } /** - * + * * Check Permissions - * + * */ - /** + /** * Request a permission level and get exception if denied * @throws ForbiddenException Thrown if access is denied */ @@ -460,7 +496,7 @@ public function request(string $permission): void { /** - * Check perticular rights and inform via boolean value, if the right is granted or denied + * Check particular rights and inform via boolean value, if the right is granted or denied */ public function getIsAllowed(string $permission): bool { return match ($permission) { @@ -482,21 +518,6 @@ public function getIsAllowed(string $permission): bool { }; } - public function getPermissionsArray(): array { - return [ - 'addOptions' => $this->getIsAllowed(self::PERMISSION_OPTIONS_ADD), - 'archive' => $this->getIsAllowed(self::PERMISSION_POLL_ARCHIVE), - 'comment' => $this->getIsAllowed(self::PERMISSION_COMMENT_ADD), - 'delete' => $this->getIsAllowed(self::PERMISSION_POLL_DELETE), - 'edit' => $this->getIsAllowed(self::PERMISSION_POLL_EDIT), - 'seeResults' => $this->getIsAllowed(self::PERMISSION_POLL_RESULTS_VIEW), - 'seeUsernames' => $this->getIsAllowed(self::PERMISSION_POLL_USERNAMES_VIEW), - 'subscribe' => $this->getIsAllowed(self::PERMISSION_POLL_SUBSCRIBE), - 'view' => $this->getIsAllowed(self::PERMISSION_POLL_VIEW), - 'vote' => $this->getIsAllowed(self::PERMISSION_VOTE_EDIT), - ]; - } - /** * getIsInvolved - Is current user involved in current poll? * @return bool Returns true, if the current user is involved in the poll via share, as a participant or as the poll owner. @@ -536,7 +557,7 @@ private function getIsInvitedViaGroupShare(): bool { return count( array_filter($this->getGroupShares(), function ($groupName) { - return ($this->getCurrentUser()->getIsInGroup($groupName)); + return ($this->userSession->getUser()->getIsInGroup($groupName)); }) ) > 0; } @@ -631,7 +652,7 @@ private function getAllowDeletePoll(): bool { } // additionally site admins are allowed to delete polls, in all other cases deny poll deletion right - return $this->getCurrentUser()->getIsAdmin(); + return $this->userSession->getUser()->getIsAdmin(); } /** @@ -678,7 +699,11 @@ private function getAllowDeleteOption(): bool { * Compare $userId with current user's id */ public function matchUser(string $userId): bool { - return $this->getCurrentUser()->getId() === $userId; + return $this->userSession->getUser()->getId() === $userId; + } + + public function getIsPollOwner(): bool { + return ($this->getUserRole() === Poll::ROLE_OWNER); } /** @@ -742,7 +767,7 @@ private function getAllowSubscribeToPoll(): bool { return false; } - return $this->getCurrentUser()->getHasEmail(); + return $this->userSession->getUser()->getHasEmail(); } /** @@ -758,12 +783,11 @@ private function getAllowShowResults(): bool { if (!$this->getAllowAccessPoll()) { return false; } - + // show results, when poll is closed if ($this->getShowResults() === Poll::SHOW_RESULTS_CLOSED && $this->getExpired()) { return true; } - // return poll settings return $this->getShowResults() === Poll::SHOW_RESULTS_ALWAYS; } diff --git a/lib/Db/Preferences.php b/lib/Db/Preferences.php index 9d704d760..1e86c2e8f 100644 --- a/lib/Db/Preferences.php +++ b/lib/Db/Preferences.php @@ -86,9 +86,9 @@ public function getRelevantOffset(): int { /** * getRelevantOffsetTimestamp - Offset for relevant polls in seconds (unix timestamp) */ - public function getRelevantOffsetTimestamp(): int { - return $this->getRelevantOffset() * 24 * 60 * 60; - } + // public function getRelevantOffsetTimestamp(): int { + // return $this->getRelevantOffset() * 24 * 60 * 60; + // } public function getCheckCalendarsBefore(): int { if (isset($this->getPreferences_decoded()->checkCalendarsBefore)) { diff --git a/lib/Model/Acl.php b/lib/Model/Acl.php index 9ca3ca4d2..6a900877d 100644 --- a/lib/Model/Acl.php +++ b/lib/Model/Acl.php @@ -57,7 +57,7 @@ public function __construct( public function jsonSerialize(): array { return [ 'currentUser' => $this->userSession->getUser(), - 'permissions' => $this->getPermissionsArray(), + 'appPermissions' => $this->getPermissionsArray(), ]; } @@ -94,10 +94,10 @@ private function getCurrentUser(): UserBase { } /** - * Shortcut for currentUser->userId + * Shortcut for UserSession::getCurrentUserId() */ - public function getUserId(): string { - return $this->getCurrentUser()->getId(); + public function getCurrentUserId(): string { + return $this->userSession->getCurrentUserId(); } /** diff --git a/lib/Model/AclLegacy.php b/lib/Model/AclLegacy.php deleted file mode 100644 index 4021c7cb6..000000000 --- a/lib/Model/AclLegacy.php +++ /dev/null @@ -1,494 +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\Model; - -use JsonSerializable; -use OCA\Polls\Db\Poll; -use OCA\Polls\Db\PollMapper; -use OCA\Polls\Db\Share; -use OCA\Polls\Db\ShareMapper; -use OCA\Polls\Exceptions\ForbiddenException; -use OCA\Polls\Exceptions\InsufficientAttributesException; -use OCA\Polls\Model\Settings\AppSettings; -use OCA\Polls\UserSession; - -/** - * Class AclLegacy - * @package OCA\Polls\Model\AclLegacy - * @deprecated - */ -class AclLegacy implements JsonSerializable { - public const PERMISSION_OVERRIDE = 'override_permission'; - public const PERMISSION_POLL_VIEW = 'view'; - public const PERMISSION_POLL_EDIT = 'edit'; - public const PERMISSION_POLL_DELETE = 'delete'; - public const PERMISSION_POLL_ARCHIVE = 'archive'; - public const PERMISSION_POLL_RESULTS_VIEW = 'seeResults'; - public const PERMISSION_POLL_MAILADDRESSES_VIEW = 'seeMailAddresses'; - public const PERMISSION_POLL_USERNAMES_VIEW = 'seeUserNames'; - public const PERMISSION_POLL_TAKEOVER = 'takeOver'; - public const PERMISSION_POLL_SUBSCRIBE = 'subscribe'; - public const PERMISSION_POLL_CREATE = 'pollCreate'; - public const PERMISSION_POLL_DOWNLOAD = 'pollDownload'; - public const PERMISSION_COMMENT_ADD = 'addComment'; - public const PERMISSION_COMMENT_DELETE = 'deleteComment'; - public const PERMISSION_OPTIONS_ADD = 'addOptions'; - public const PERMISSION_OPTION_DELETE = 'deleteOption'; - public const PERMISSION_VOTE_EDIT = 'vote'; - public const PERMISSION_PUBLIC_SHARES = 'publicShares'; - public const PERMISSION_ALL_ACCESS = 'allAccess'; - - private ?int $pollId = null; - - /** - * @psalm-suppress PossiblyUnusedMethod - */ - public function __construct( - private AppSettings $appSettings, - private PollMapper $pollMapper, - private ShareMapper $shareMapper, - private UserSession $userSession, - private Poll $poll, - ) { - } - - /** - * @psalm-suppress PossiblyUnusedMethod - */ - public function jsonSerialize(): array { - return [ - 'pollId' => $this->getPoll()->getId(), - 'pollExpired' => $this->getPoll()->getExpired(), - 'pollExpire' => $this->getPoll()->getExpire(), - 'currentUser' => $this->getCurrentUserArray(), - 'permissions' => $this->getPermissionsArray(), - ]; - } - - public function getPermissionsArray(): array { - return [ - 'addOptions' => $this->getIsAllowed(self::PERMISSION_OPTIONS_ADD), - 'allAccess' => $this->getIsAllowed(self::PERMISSION_ALL_ACCESS), - 'archive' => $this->getIsAllowed(self::PERMISSION_POLL_ARCHIVE), - 'comment' => $this->getIsAllowed(self::PERMISSION_COMMENT_ADD), - 'delete' => $this->getIsAllowed(self::PERMISSION_POLL_DELETE), - 'edit' => $this->getIsAllowed(self::PERMISSION_POLL_EDIT), - 'pollCreation' => $this->getIsAllowed(self::PERMISSION_POLL_CREATE), - 'pollDownload' => $this->getIsAllowed(self::PERMISSION_POLL_DOWNLOAD), - 'publicShares' => $this->getIsAllowed(self::PERMISSION_PUBLIC_SHARES), - 'seeResults' => $this->getIsAllowed(self::PERMISSION_POLL_RESULTS_VIEW), - 'seeUsernames' => $this->getIsAllowed(self::PERMISSION_POLL_USERNAMES_VIEW), - 'seeMailAddresses' => $this->getIsAllowed(self::PERMISSION_POLL_MAILADDRESSES_VIEW), - 'subscribe' => $this->getIsAllowed(self::PERMISSION_POLL_SUBSCRIBE), - 'view' => $this->getIsAllowed(self::PERMISSION_POLL_VIEW), - 'vote' => $this->getIsAllowed(self::PERMISSION_VOTE_EDIT), - ]; - } - - public function getCurrentUserArray(): array { - return [ - 'displayName' => $this->getCurrentUser()->getDisplayName(), - 'hasVoted' => $this->getIsParticipant(), - 'isInvolved' => $this->getIsInvolved(), - 'isLoggedIn' => $this->userSession->getIsLoggedIn(), - 'isNoUser' => !$this->userSession->getIsLoggedIn(), - 'isOwner' => $this->getIsPollOwner(), - 'userId' => $this->getUserId(), - ]; - } - /** - * Setters - */ - - /** - * Set poll id - */ - public function setPollId(int $pollId, string $permission = self::PERMISSION_POLL_VIEW): void { - $this->pollId = $pollId; - $this->request($permission); - } - - /** - * get poll - * @throws InsufficientAttributesException Thrown if stored pollId is null - */ - public function getPoll(): Poll { - if ($this->userSession->hasShare()) { - // if a share token is set, force usage of the share's pollId - $this->pollId = $this->getShare()->getPollId(); - } - - if (!boolval($this->pollId)) { - throw new InsufficientAttributesException('PollId is invalid: ' . (string) $this->pollId); - } - - if ($this->pollId !== $this->poll->getId()) { - // if pollId differs from pollId from loaded poll (or poll is not loaded), get poll from db - $this->poll = $this->pollMapper->find($this->pollId); - } - - return $this->poll; - } - - /** - * Get share - * load share from db by session stored token or rely on cached share - */ - private function getShare(): Share|null { - return $this->userSession->getShare(); - } - - /** - * loads the current user from the userSession or returns the cached one - */ - private function getCurrentUser(): UserBase { - return $this->userSession->getUser(); - } - - /** - * Shortcut for currentUser->userId - */ - public function getUserId(): string { - return $this->getCurrentUser()->getId(); - } - - /** - * Checks, if the current user is the poll owner - */ - public function getIsPollOwner(): bool { - return ($this->getPoll()->getUserRole() === Poll::ROLE_OWNER); - } - - /** - * Check perticular rights and inform via boolean value, if the right is granted or denied - */ - public function getIsAllowed(string $permission): bool { - return match ($permission) { - self::PERMISSION_OVERRIDE => true, - self::PERMISSION_ALL_ACCESS => $this->appSettings->getAllAccessAllowed(), - self::PERMISSION_PUBLIC_SHARES => $this->appSettings->getPublicSharesAllowed(), - self::PERMISSION_POLL_CREATE => $this->appSettings->getPollCreationAllowed(), - self::PERMISSION_POLL_MAILADDRESSES_VIEW => $this->appSettings->getAllowSeeMailAddresses(), - self::PERMISSION_POLL_DOWNLOAD => $this->appSettings->getPollDownloadAllowed(), - self::PERMISSION_POLL_VIEW => $this->getAllowAccessPoll(), - self::PERMISSION_POLL_EDIT => $this->getAllowEditPoll(), - self::PERMISSION_POLL_DELETE => $this->getAllowDeletePoll(), - self::PERMISSION_POLL_ARCHIVE => $this->getAllowEditPoll(), - self::PERMISSION_POLL_TAKEOVER => $this->getAllowEditPoll(), - self::PERMISSION_POLL_SUBSCRIBE => $this->getAllowSubscribeToPoll(), - self::PERMISSION_POLL_RESULTS_VIEW => $this->getShowResults(), - self::PERMISSION_POLL_USERNAMES_VIEW => $this->getAllowEditPoll() || !$this->getPoll()->getAnonymous(), - self::PERMISSION_OPTIONS_ADD => $this->getAllowAddOptions(), - self::PERMISSION_OPTION_DELETE => $this->getAllowDeleteOption(), - self::PERMISSION_COMMENT_ADD => $this->getAllowComment(), - self::PERMISSION_COMMENT_DELETE => $this->getAllowDeleteComment(), - self::PERMISSION_VOTE_EDIT => $this->getAllowVote(), - default => false, - }; - } - - /** - * Request a permission level and get exception if denied - * @throws ForbiddenException Thrown if access is denied - */ - public function request(string $permission): void { - if (!$this->getIsAllowed($permission)) { - throw new ForbiddenException('denied permission ' . $permission); - } - } - - /** - * getIsInvolved - Is current user involved in current poll? - * @return bool Returns true, if the current user is involved in the poll via share, as a participant or as the poll owner. - */ - private function getIsInvolved(): bool { - return ( - $this->getIsPollOwner() - || $this->getIsParticipant() - || $this->getIsPersonallyInvited()) - || $this->getIsInvitedViaGroupShare(); - } - - /** - * Check, if poll settings is set to open access for internal users - */ - private function getIsOpenPoll(): bool { - return $this->getPoll()->getAccess() === Poll::ACCESS_OPEN && $this->userSession->getIsLoggedIn(); - } - - /** - * getIsParticipant - Is user a participant? - * @return bool Returns true, if the current user is already a particitipant of the current poll. - */ - private function getIsParticipant(): bool { - return $this->getPoll()->getCurrentUserCountVotes() > 0; - } - - /** - * getIsInvitedViaGroupShare - Is the poll shared via group share? - * where the current user is member of. This only affects logged in users. - * @return bool Returns true, if the current poll contains a group share with a group, - */ - private function getIsInvitedViaGroupShare(): bool { - if (!$this->userSession->getIsLoggedIn()) { - return false; - } - - return count( - array_filter($this->shareMapper->findGroupShareByPoll($this->getPoll()->getId()), function ($item) { - return ($this->getCurrentUser()->getIsInGroup($item->getUserId())); - }) - ) > 0; - } - - /** - * getIsPersonallyInvited - Is the poll shared via user share with the current user? - * Checking via user role - * @return bool Returns true, if the current poll contains a user role which matches a share type - */ - private function getIsPersonallyInvited(): bool { - return in_array($this->getPoll()->getUserRole(), [ - Poll::ROLE_ADMIN, - Poll::ROLE_USER, - Poll::ROLE_EXTERNAL, - Poll::ROLE_EMAIL, - Poll::ROLE_CONTACT, - ]); - } - - /** - * The detailed checks - For the sake of readability, the queries and selections - * were kept detailed and with low complexity - */ - - /** - * Checks, if the user has delegated admin rights to edit poll settings via share - */ - private function getIsDelegatedAdmin(): bool { - return $this->getPoll()->getUserRole() === Poll::ROLE_ADMIN - && !$this->getPoll()->getIsCurrentUserLocked(); - } - - /** - * Checks, if user is allowed to edit the poll configuration - **/ - private function getAllowEditPoll(): bool { - // Console has god mode - if (defined('OC_CONSOLE')) { - return true; - } - - // owner is always allowed to edit the poll configuration - if ($this->getIsPollOwner()) { - return true; - } - - // user has delegated owner rights - if ($this->getIsDelegatedAdmin()) { - return true; - } - - // deny edit rights in all other cases - return false; - } - - /** - * Checks, if user is allowed to access (view) poll - */ - private function getAllowAccessPoll(): bool { - // edit rights include access to poll - if ($this->getAllowEditPoll()) { - return true; - } - - // No further access to poll, if it is deleted - if ($this->getPoll()->getDeleted()) { - return false; - } - - // grant access if poll poll is an open poll (for logged in users) - if ($this->getIsOpenPoll() && $this->userSession->getIsLoggedIn()) { - return true; - } - - // grant access if user is involved in poll in any way - if ($this->getIsInvolved()) { - return true; - } - - // return check result of an existing valid share for this user - return boolval($this->getShare()?->getToken()); - } - - /** - * Checks, if user is allowed to delete the poll - * includes the right to archive and take over - **/ - private function getAllowDeletePoll(): bool { - if ($this->getAllowEditPoll()) { - // users with edit rights are allowed to delete the poll - return true; - } - - // additionally site admins are allowed to delete polls, in all other cases deny poll deletion right - return $this->getCurrentUser()->getIsAdmin(); - } - - /** - * Checks, if user is allowed to add add vote options - **/ - private function getAllowAddOptions(): bool { - // Edit right includes adding new options - if ($this->getAllowEditPoll()) { - return true; - } - - // deny, if user has no access right to this poll - if (!$this->getAllowAccessPoll()) { - return false; - } - - // public shares are not allowed to add options - if ($this->getShare()?->getType() === Share::TYPE_PUBLIC) { - return false; - } - - // Request for option proposals is expired, deny - if ($this->getPoll()->getProposalsExpired()) { - return false; - } - - // Locked Users are not allowed to add options - if (boolval($this->getPoll()->getIsCurrentUserLocked())) { - return false; - } - - // Allow, if poll requests proposals - return $this->getPoll()->getAllowProposals() === Poll::PROPOSAL_ALLOW; - } - - /** - * Is current user allowed to delete options from poll - */ - private function getAllowDeleteOption(): bool { - return $this->getIsPollOwner() || $this->getIsDelegatedAdmin(); - } - - /** - * Compare $userId with current user's id - */ - public function matchUser(string $userId): bool { - return $this->getCurrentUser()->getId() === $userId; - } - - /** - * Checks, if user is allowed to see and write comments - **/ - private function getAllowComment(): bool { - // user has no access right to this poll - if (!$this->getAllowAccessPoll()) { - return false; - } - - // public shares are not allowed to comment - if ($this->getShare()?->getType() === Share::TYPE_PUBLIC) { - return false; - } - - // public shares are not allowed to comment - if (boolval($this->getPoll()->getIsCurrentUserLocked())) { - return false; - } - - return (bool) $this->getPoll()->getAllowComment(); - } - - /** - * Checks, if user is allowed to delete comments from poll - **/ - private function getAllowDeleteComment(): bool { - return $this->getAllowEditPoll(); - } - - /** - * Checks, if user is allowed to vote - **/ - private function getAllowVote(): bool { - // user has no access right to this poll - if (!$this->getAllowAccessPoll()) { - return false; - } - - // public shares are not allowed to vote - if ($this->getShare()?->getType() === Share::TYPE_PUBLIC) { - return false; - } - - // Locked users are not allowed to vote - if (boolval($this->getPoll()->getIsCurrentUserLocked())) { - return false; - } - - // deny votes, if poll is expired - return !$this->getPoll()->getExpired(); - } - - /** - * Checks, if user is allowed to subscribe to updates - **/ - private function getAllowSubscribeToPoll(): bool { - // user with access to poll are always allowed to subscribe - if (!$this->getAllowAccessPoll()) { - return false; - } - - return $this->getCurrentUser()->getHasEmail(); - } - - /** - * Checks, if user is allowed to see results of current poll - **/ - private function getShowResults(): bool { - // edit rights include access to results - if ($this->getAllowEditPoll()) { - return true; - } - - // no access to poll, deny - if (!$this->getAllowAccessPoll()) { - return false; - } - - // show results, when poll is closed - if ($this->getPoll()->getShowResults() === Poll::SHOW_RESULTS_CLOSED && $this->getPoll()->getExpired()) { - return true; - } - - // return poll settings - return $this->getPoll()->getShowResults() === Poll::SHOW_RESULTS_ALWAYS; - } -} diff --git a/lib/Service/CommentService.php b/lib/Service/CommentService.php index 879e8b1b3..46458de99 100644 --- a/lib/Service/CommentService.php +++ b/lib/Service/CommentService.php @@ -27,10 +27,11 @@ use OCA\Polls\Db\Comment; use OCA\Polls\Db\CommentMapper; +use OCA\Polls\Db\Poll; +use OCA\Polls\Db\PollMapper; use OCA\Polls\Event\CommentAddEvent; use OCA\Polls\Event\CommentDeleteEvent; -use OCA\Polls\Exceptions\ForbiddenException; -use OCA\Polls\Model\AclLegacy as Acl; +use OCA\Polls\Model\Acl as Acl; use OCP\EventDispatcher\IEventDispatcher; class CommentService { @@ -42,6 +43,7 @@ public function __construct( private Comment $comment, private IEventDispatcher $eventDispatcher, protected Acl $acl, + private PollMapper $pollMapper, ) { } @@ -50,18 +52,10 @@ public function __construct( * Read all comments of a poll based on the poll id and return list as array * @return Comment[] */ - public function list(?int $pollId = null): array { - try { - if ($pollId !== null) { - $this->acl->setPollId($pollId); - } - - $this->acl->request(Acl::PERMISSION_COMMENT_ADD); - } catch (ForbiddenException $e) { - return []; - } + public function list(int $pollId): array { + $this->pollMapper->find($pollId)->request(Poll::PERMISSION_COMMENT_ADD); - $comments = $this->commentMapper->findByPoll($this->acl->getPoll()->getId()); + $comments = $this->commentMapper->findByPoll($pollId); // treat comments from the same user within 5 minutes as grouped comments $timeTolerance = 5 * 60; // init predecessor as empty Comment @@ -81,22 +75,12 @@ public function list(?int $pollId = null): array { /** * Add comment */ - public function get(int $commentId): Comment { - return $this->commentMapper->find($commentId); - } - /** - * Add comment - */ - public function add(string $message, ?int $pollId = null): Comment { - if ($pollId !== null) { - $this->acl->setPollId($pollId); - } - - $this->acl->request(Acl::PERMISSION_COMMENT_ADD); + public function add(string $message, int $pollId): Comment { + $this->pollMapper->find($pollId)->request(Poll::PERMISSION_COMMENT_ADD); $this->comment = new Comment(); - $this->comment->setPollId($this->acl->getPoll()->getId()); - $this->comment->setUserId($this->acl->getUserId()); + $this->comment->setPollId($pollId); + $this->comment->setUserId($this->acl->getCurrentUserId()); $this->comment->setComment($message); $this->comment->setTimestamp(time()); $this->comment = $this->commentMapper->insert($this->comment); @@ -108,20 +92,20 @@ public function add(string $message, ?int $pollId = null): Comment { /** * Delete or restore comment - * @param Comment $comment Comment to delete or restore + * @param int $commentId id of Comment to delete or restore * @param bool $restore Set true, if comment is to be restored */ - public function delete(Comment $comment, bool $restore = false): Comment { - $this->acl->setPollId($comment->getPollId()); + public function delete(int $commentId, bool $restore = false): Comment { + $this->comment = $this->commentMapper->find($commentId); - if (!$this->acl->matchUser($comment->getUserId())) { - $this->acl->request(Acl::PERMISSION_COMMENT_DELETE); + if (!$this->acl->matchUser($this->comment->getUserId())) { + $this->pollMapper->find($this->comment->getPollId())->request(Poll::PERMISSION_COMMENT_DELETE); } - $comment->setDeleted($restore ? 0 : time()); - $this->commentMapper->update($comment); - $this->eventDispatcher->dispatchTyped(new CommentDeleteEvent($comment)); + $this->comment->setDeleted($restore ? 0 : time()); + $this->commentMapper->update($this->comment); + $this->eventDispatcher->dispatchTyped(new CommentDeleteEvent($this->comment)); - return $comment; + return $this->comment; } } diff --git a/lib/Service/MailService.php b/lib/Service/MailService.php index 1194b3308..d1cead027 100644 --- a/lib/Service/MailService.php +++ b/lib/Service/MailService.php @@ -236,7 +236,9 @@ public function sendAutoReminder(): void { * Send a confirmation mail for the poll to all participants */ public function sendConfirmations(int $pollId): SentResult { + $this->pollMapper->find($pollId)->request(Poll::PERMISSION_POLL_EDIT); $sentResult = new SentResult(); + $participants = $this->userMapper->getParticipants($pollId); foreach ($participants as $participant) { diff --git a/lib/Service/OptionService.php b/lib/Service/OptionService.php index bbf079b73..77991cf37 100644 --- a/lib/Service/OptionService.php +++ b/lib/Service/OptionService.php @@ -29,6 +29,7 @@ use OCA\Polls\Db\Option; use OCA\Polls\Db\OptionMapper; use OCA\Polls\Db\Poll; +use OCA\Polls\Db\PollMapper; use OCA\Polls\Event\OptionConfirmedEvent; use OCA\Polls\Event\OptionCreatedEvent; use OCA\Polls\Event\OptionDeletedEvent; @@ -37,7 +38,7 @@ use OCA\Polls\Event\PollOptionReorderedEvent; use OCA\Polls\Exceptions\DuplicateEntryException; use OCA\Polls\Exceptions\InvalidPollTypeException; -use OCA\Polls\Model\AclLegacy as Acl; +use OCA\Polls\Model\Acl as Acl; use OCA\Polls\UserSession; use OCP\AppFramework\Db\DoesNotExistException; use OCP\DB\Exception; @@ -57,6 +58,7 @@ public function __construct( private LoggerInterface $logger, private Option $option, private OptionMapper $optionMapper, + private PollMapper $pollMapper, private UserSession $userSession, ) { $this->options = []; @@ -69,15 +71,14 @@ public function __construct( * * @psalm-return array */ - public function list(?int $pollId = null): array { - try { - if ($pollId !== null) { - $this->acl->setPollId($pollId); - } + public function list(int $pollId): array { + $poll = $this->pollMapper->find($pollId); + $poll->request(Poll::PERMISSION_POLL_VIEW); - $this->options = $this->optionMapper->findByPoll($this->acl->getPoll()->getId(), !$this->acl->getIsAllowed(Acl::PERMISSION_POLL_RESULTS_VIEW)); + try { + $this->options = $this->optionMapper->findByPoll($pollId, !$poll->getIsAllowed(Poll::PERMISSION_POLL_RESULTS_VIEW)); - if ($this->acl->getPoll()->getHideBookedUp() && !$this->acl->getIsAllowed(Acl::PERMISSION_POLL_EDIT)) { + if ($poll->getHideBookedUp() && !$poll->getIsAllowed(Poll::PERMISSION_POLL_EDIT)) { // hide booked up options, except the user has edit permission $this->filterBookedUp(); } @@ -88,19 +89,6 @@ public function list(?int $pollId = null): array { return array_values($this->options); } - /** - * Add a new option to the current poll, stored in Acl - * - * @param int $timestamp timestamp in case of date poll - * @param string $pollOptionText option text in case of text poll - * @param int $duration duration of option in case of date poll - * @return Option - */ - public function addForCurrentPoll(int $timestamp = 0, string $pollOptionText = '', int $duration = 0): Option { - $pollId = $this->acl->getPoll()->getId(); - return $this->add($pollId, $timestamp, $pollOptionText, $duration); - } - /** * Add a new option to a poll * @@ -110,22 +98,9 @@ public function addForCurrentPoll(int $timestamp = 0, string $pollOptionText = ' * @param int $duration duration of option in case of date poll * @return Option */ - public function addForPoll(int $pollId, int $timestamp = 0, string $pollOptionText = '', int $duration = 0): Option { - $this->acl->setPollId($pollId); - return $this->add($pollId, $timestamp, $pollOptionText, $duration); - } - - /** - * Add a new option - * - * @param int $pollId poll id of poll to add option to - * @param int $timestamp timestamp in case of date poll - * @param string $pollOptionText option text in case of text poll - * @param int $duration duration of option in case of date poll - * @return Option - */ - private function add(int $pollId, int $timestamp, string $pollOptionText, int $duration): Option { - $this->acl->request(Acl::PERMISSION_OPTIONS_ADD); + public function add(int $pollId, int $timestamp = 0, string $pollOptionText = '', int $duration = 0): Option { + $poll = $this->pollMapper->find($pollId); + $poll->request(Poll::PERMISSION_OPTIONS_ADD); $this->option = new Option(); $this->option->setPollId($pollId); @@ -133,8 +108,8 @@ private function add(int $pollId, int $timestamp, string $pollOptionText, int $d $this->option->setOption($timestamp, $duration, $pollOptionText, $order); - if (!$this->acl->getIsPollOwner()) { - $this->option->setOwner($this->acl->getUserId()); + if (!$poll->getIsPollOwner()) { + $this->option->setOwner($this->acl->getCurrentUserId()); } try { @@ -170,13 +145,13 @@ private function add(int $pollId, int $timestamp, string $pollOptionText, int $d * @return Option[] */ public function addBulk(int $pollId, string $pollOptionText = ''): array { - $this->acl->setPollId($pollId, Acl::PERMISSION_OPTIONS_ADD); + $this->pollMapper->find($pollId)->request(Poll::PERMISSION_OPTIONS_ADD); $newOptions = array_unique(explode(PHP_EOL, $pollOptionText)); foreach ($newOptions as $option) { if ($option) { try { - $this->addForPoll($pollId, pollOptionText: $option); + $this->add($pollId, pollOptionText: $option); } catch (DuplicateEntryException $e) { continue; } @@ -192,7 +167,7 @@ public function addBulk(int $pollId, string $pollOptionText = ''): array { */ public function update(int $optionId, int $timestamp = 0, string $pollOptionText = '', int $duration = 0): Option { $this->option = $this->optionMapper->find($optionId); - $this->acl->setPollId($this->option->getPollId(), Acl::PERMISSION_POLL_EDIT); + $this->pollMapper->find($this->option->getPollId())->request(Poll::PERMISSION_POLL_EDIT); $this->option->setOption($timestamp, $duration, $pollOptionText); @@ -210,10 +185,8 @@ public function update(int $optionId, int $timestamp = 0, string $pollOptionText public function delete(int $optionId, bool $restore = false): Option { $this->option = $this->optionMapper->find($optionId); - $this->acl->setPollId($this->option->getPollId()); - if (!$this->acl->matchUser($this->option->getUserId())) { - $this->acl->request(Acl::PERMISSION_OPTION_DELETE); + $this->pollMapper->find($this->option->getPollId())->request(Poll::PERMISSION_OPTION_DELETE); } $this->option->setDeleted($restore ? 0 : time()); @@ -230,7 +203,7 @@ public function delete(int $optionId, bool $restore = false): Option { */ public function confirm(int $optionId): Option { $this->option = $this->optionMapper->find($optionId); - $this->acl->setPollId($this->option->getPollId(), Acl::PERMISSION_POLL_EDIT); + $this->pollMapper->find($this->option->getPollId())->request(Poll::PERMISSION_POLL_EDIT); $this->option->setConfirmed($this->option->getConfirmed() ? 0 : time()); $this->option = $this->optionMapper->update($this->option); @@ -253,9 +226,10 @@ public function confirm(int $optionId): Option { */ public function sequence(int $optionId, int $step, string $unit, int $amount): array { $this->option = $this->optionMapper->find($optionId); - $this->acl->setPollId($this->option->getPollId(), Acl::PERMISSION_POLL_EDIT); + $poll = $this->pollMapper->find($this->option->getPollId()); + $poll->request(Poll::PERMISSION_POLL_EDIT); - if ($this->acl->getPoll()->getType() !== Poll::TYPE_DATE) { + if ($poll->getType() !== Poll::TYPE_DATE) { throw new InvalidPollTypeException('Sequences are only available in date polls'); } @@ -280,7 +254,7 @@ public function sequence(int $optionId, int $step, string $unit, int $amount): a $this->eventDispatcher->dispatchTyped(new OptionCreatedEvent($this->option)); - return $this->optionMapper->findByPoll($this->acl->getPoll()->getId()); + return $this->optionMapper->findByPoll($poll->getId()); } /** @@ -291,10 +265,11 @@ public function sequence(int $optionId, int $step, string $unit, int $amount): a * @psalm-return array */ public function shift(int $pollId, int $step, string $unit): array { - $this->acl->setPollId($pollId, Acl::PERMISSION_POLL_EDIT); + $poll = $this->pollMapper->find($pollId); + $poll->request(Poll::PERMISSION_POLL_EDIT); $timezone = new DateTimeZone($this->userSession->getClientTimeZone()); - if ($this->acl->getPoll()->getType() !== Poll::TYPE_DATE) { + if ($poll->getType() !== Poll::TYPE_DATE) { throw new InvalidPollTypeException('Shifting is only available in date polls'); } @@ -318,7 +293,8 @@ public function shift(int $pollId, int $step, string $unit): array { * Copy options from $fromPoll to $toPoll */ public function clone(int $fromPollId, int $toPollId): void { - $this->acl->setPollId($fromPollId); + $this->pollMapper->find($fromPollId)->request(Poll::PERMISSION_POLL_VIEW); + $this->pollMapper->find($toPollId)->request(Poll::PERMISSION_POLL_EDIT); foreach ($this->optionMapper->findByPoll($fromPollId) as $origin) { $option = new Option(); @@ -343,9 +319,10 @@ public function clone(int $fromPollId, int $toPollId): void { * @psalm-return array */ public function reorder(int $pollId, array $options): array { - $this->acl->setPollId($pollId, Acl::PERMISSION_POLL_EDIT); + $poll = $this->pollMapper->find($pollId); + $poll->request(Poll::PERMISSION_POLL_EDIT); - if ($this->acl->getPoll()->getType() === Poll::TYPE_DATE) { + if ($poll->getType() === Poll::TYPE_DATE) { throw new InvalidPollTypeException('Not allowed in date polls'); } @@ -372,26 +349,27 @@ public function reorder(int $pollId, array $options): array { */ public function setOrder(int $optionId, int $newOrder): array { $this->option = $this->optionMapper->find($optionId); - $this->acl->setPollId($this->option->getPollId(), Acl::PERMISSION_POLL_EDIT); + $poll = $this->pollMapper->find($this->option->getPollId()); + $poll->request(Poll::PERMISSION_POLL_EDIT); - if ($this->acl->getPoll()->getType() === Poll::TYPE_DATE) { + if ($poll->getType() === Poll::TYPE_DATE) { throw new InvalidPollTypeException('Not allowed in date polls'); } if ($newOrder < 1) { $newOrder = 1; - } elseif ($newOrder > $this->getHighestOrder($this->acl->getPoll()->getId())) { - $newOrder = $this->getHighestOrder($this->acl->getPoll()->getId()); + } elseif ($newOrder > $this->getHighestOrder($poll->getId())) { + $newOrder = $this->getHighestOrder($poll->getId()); } - foreach ($this->optionMapper->findByPoll($this->acl->getPoll()->getId()) as $option) { + foreach ($this->optionMapper->findByPoll($poll->getId()) as $option) { $option->setOrder($this->moveModifier($this->option->getOrder(), $newOrder, $option->getOrder())); $this->optionMapper->update($option); } - $this->eventDispatcher->dispatchTyped(new PollOptionReorderedEvent($this->acl->getPoll())); + $this->eventDispatcher->dispatchTyped(new PollOptionReorderedEvent($poll)); - return $this->optionMapper->findByPoll($this->acl->getPoll()->getId()); + return $this->optionMapper->findByPoll($poll->getId()); } /** diff --git a/lib/Service/PollService.php b/lib/Service/PollService.php index 9580d3b3d..8e3407ddd 100644 --- a/lib/Service/PollService.php +++ b/lib/Service/PollService.php @@ -27,7 +27,6 @@ use OCA\Polls\Db\Poll; use OCA\Polls\Db\PollMapper; -use OCA\Polls\Db\Preferences; use OCA\Polls\Db\UserMapper; use OCA\Polls\Db\VoteMapper; use OCA\Polls\Event\PollArchivedEvent; @@ -46,7 +45,7 @@ use OCA\Polls\Exceptions\InvalidShowResultsException; use OCA\Polls\Exceptions\InvalidUsernameException; use OCA\Polls\Exceptions\UserNotFoundException; -use OCA\Polls\Model\AclLegacy as Acl; +use OCA\Polls\Model\Acl as Acl; use OCA\Polls\Model\Settings\AppSettings; use OCA\Polls\Model\UserBase; use OCA\Polls\UserSession; @@ -65,8 +64,6 @@ public function __construct( private IEventDispatcher $eventDispatcher, private Poll $poll, private PollMapper $pollMapper, - private Preferences $preferences, - private PreferencesService $preferencesService, private UserMapper $userMapper, private UserSession $userSession, private VoteMapper $voteMapper, @@ -77,32 +74,7 @@ public function __construct( * Get list of polls including acl and Threshold for "relevant polls" */ public function list(): array { - $pollList = []; - try { - $polls = $this->pollMapper->findForMe($this->userSession->getCurrentUserId()); - $this->preferences = $this->preferencesService->get(); - foreach ($polls as $poll) { - try { - $this->acl->setPollId($poll->getId()); - $relevantThreshold = $poll->getRelevantThresholdNet() + $this->preferences->getRelevantOffsetTimestamp(); - - // mix poll settings, currentUser attributes, permissions and relevantThreshold into one array - $pollList[] = (object) array_merge( - (array) json_decode(json_encode($poll)), - [ - 'relevantThreshold' => $relevantThreshold, - 'relevantThresholdNet' => $poll->getRelevantThresholdNet(), - 'permissions' => $this->acl->getPermissionsArray(), - 'currentUser' => $this->acl->getCurrentUserArray(), - ], - ); - } catch (ForbiddenException $e) { - continue; - } - } - } catch (DoesNotExistException $e) { - // silent catch - } + $pollList = $this->pollMapper->findForMe($this->userSession->getCurrentUserId()); return $pollList; } @@ -116,8 +88,7 @@ public function search(ISearchQuery $query): array { foreach ($polls as $poll) { try { - $this->acl->setPollId($poll->getId()); - // TODO: Not the elegant way. Improvement neccessary + $poll->request(Poll::PERMISSION_POLL_VIEW); $pollList[] = $poll; } catch (ForbiddenException $e) { continue; @@ -208,8 +179,8 @@ private function executeTransfer(Poll $poll, string $targetUser): Poll { * @return Poll */ public function get(int $pollId) { - $this->acl->setPollId($pollId); $this->poll = $this->pollMapper->find($pollId); + $this->poll->request(Poll::PERMISSION_POLL_VIEW); return $this->poll; } @@ -256,35 +227,36 @@ public function add(string $type, string $title): Poll { * Update poll configuration * @return Poll */ - public function update(int $pollId, array $poll): Poll { + public function update(int $pollId, array $pollConfiguration): Poll { $this->poll = $this->pollMapper->find($pollId); + $this->poll->request(Poll::PERMISSION_POLL_EDIT); // Validate valuess - if (isset($poll['showResults']) && !in_array($poll['showResults'], $this->getValidShowResults())) { + if (isset($pollConfiguration['showResults']) && !in_array($pollConfiguration['showResults'], $this->getValidShowResults())) { throw new InvalidShowResultsException('Invalid value for prop showResults'); } - if (isset($poll['title']) && !$poll['title']) { + if (isset($pollConfiguration['title']) && !$pollConfiguration['title']) { throw new EmptyTitleException('Title must not be empty'); } - if (isset($poll['access']) && !in_array($poll['access'], $this->getValidAccess())) { - if (!in_array($poll['access'], $this->getValidAccess())) { - throw new InvalidAccessException('Invalid value for prop access ' . $poll['access']); + if (isset($pollConfiguration['access']) && !in_array($pollConfiguration['access'], $this->getValidAccess())) { + if (!in_array($pollConfiguration['access'], $this->getValidAccess())) { + throw new InvalidAccessException('Invalid value for prop access ' . $pollConfiguration['access']); } - if ($poll['access'] === (Poll::ACCESS_OPEN)) { - $this->acl->setPollId($pollId, Acl::PERMISSION_ALL_ACCESS); + if ($pollConfiguration['access'] === (Poll::ACCESS_OPEN)) { + $this->acl->request(Acl::PERMISSION_ALL_ACCESS); } } // Set the expiry time to the actual servertime to avoid an // expiry misinterpration when using acl - if (isset($poll['expire']) && $poll['expire'] < 0) { - $poll['expire'] = time(); + if (isset($pollConfiguration['expire']) && $pollConfiguration['expire'] < 0) { + $pollConfiguration['expire'] = time(); } - $this->poll->deserializeArray($poll); + $this->poll->deserializeArray($pollConfiguration); $this->pollMapper->update($this->poll); $this->eventDispatcher->dispatchTyped(new PollUpdatedEvent($this->poll)); @@ -306,8 +278,8 @@ public function setLastInteraction(int $pollId): void { * @return Poll */ public function toggleArchive(int $pollId): Poll { - $this->acl->setPollId($pollId, Acl::PERMISSION_POLL_DELETE); - $this->poll = $this->acl->getPoll(); + $this->poll = $this->pollMapper->find($pollId); + $this->poll->request(Poll::PERMISSION_POLL_DELETE); $this->poll->setDeleted($this->poll->getDeleted() ? 0 : time()); $this->poll = $this->pollMapper->update($this->poll); @@ -326,8 +298,8 @@ public function toggleArchive(int $pollId): Poll { * @return Poll */ public function delete(int $pollId): Poll { - $this->acl->setPollId($pollId, Acl::PERMISSION_POLL_DELETE); - $this->poll = $this->acl->getPoll(); + $this->poll = $this->pollMapper->find($pollId); + $this->poll->request(Poll::PERMISSION_POLL_DELETE); $this->eventDispatcher->dispatchTyped(new PollDeletedEvent($this->poll)); @@ -341,6 +313,7 @@ public function delete(int $pollId): Poll { * @return Poll */ public function close(int $pollId): Poll { + $this->pollMapper->find($pollId)->request(Poll::PERMISSION_POLL_EDIT); return $this->toggleClose($pollId, time() - 5); } @@ -349,6 +322,7 @@ public function close(int $pollId): Poll { * @return Poll */ public function reopen(int $pollId): Poll { + $this->pollMapper->find($pollId)->request(Poll::PERMISSION_POLL_EDIT); return $this->toggleClose($pollId, 0); } @@ -358,7 +332,8 @@ public function reopen(int $pollId): Poll { */ private function toggleClose(int $pollId, int $expiry): Poll { $this->poll = $this->pollMapper->find($pollId); - $this->acl->setPollId($this->poll->getId(), Acl::PERMISSION_POLL_EDIT); + $this->poll->request(Poll::PERMISSION_POLL_EDIT); + $this->poll->setExpire($expiry); if ($expiry > 0) { $this->eventDispatcher->dispatchTyped(new PollCloseEvent($this->poll)); @@ -376,8 +351,9 @@ private function toggleClose(int $pollId, int $expiry): Poll { * @return Poll */ public function clone(int $pollId): Poll { - $this->acl->setPollId($pollId); - $origin = $this->acl->getPoll(); + $origin = $this->pollMapper->find($pollId); + $origin->request(Poll::PERMISSION_POLL_VIEW); + $this->acl->request(Acl::PERMISSION_POLL_CREATE); $this->poll = new Poll(); $this->poll->setCreated(time()); @@ -405,8 +381,8 @@ public function clone(int $pollId): Poll { * */ public function getParticipantsEmailAddresses(int $pollId): array { - $this->acl->setPollId($pollId, Acl::PERMISSION_POLL_EDIT); - $this->poll = $this->acl->getPoll(); + $this->poll = $this->pollMapper->find($pollId); + $this->poll->request(Poll::PERMISSION_POLL_EDIT); $votes = $this->voteMapper->findParticipantsByPoll($this->poll->getId()); $list = []; diff --git a/lib/Service/ShareService.php b/lib/Service/ShareService.php index 366b2e6c3..ad847254e 100644 --- a/lib/Service/ShareService.php +++ b/lib/Service/ShareService.php @@ -25,6 +25,8 @@ namespace OCA\Polls\Service; +use OCA\Polls\Db\Poll; +use OCA\Polls\Db\PollMapper; use OCA\Polls\Db\Share; use OCA\Polls\Db\ShareMapper; use OCA\Polls\Db\UserMapper; @@ -43,7 +45,7 @@ use OCA\Polls\Exceptions\NotFoundException; use OCA\Polls\Exceptions\ShareAlreadyExistsException; use OCA\Polls\Exceptions\ShareNotFoundException; -use OCA\Polls\Model\AclLegacy as Acl; +use OCA\Polls\Model\Acl as Acl; use OCA\Polls\Model\SentResult; use OCA\Polls\Model\UserBase; use OCA\Polls\UserSession; @@ -70,6 +72,7 @@ public function __construct( private MailService $mailService, private Acl $acl, private NotificationService $notificationService, + private PollMapper $pollMapper, private UserMapper $userMapper, private UserSession $userSession, ) { @@ -85,7 +88,7 @@ public function __construct( */ public function list(int $pollId): array { try { - $this->acl->setPollId($pollId, Acl::PERMISSION_POLL_EDIT); + $this->pollMapper->find($pollId)->request(Poll::PERMISSION_POLL_EDIT); $this->shares = $this->shareMapper->findByPoll($pollId); } catch (ForbiddenException $e) { return []; @@ -105,7 +108,7 @@ public function list(int $pollId): array { */ public function listNotInvited(int $pollId): array { try { - $this->acl->setPollId($pollId, Acl::PERMISSION_POLL_EDIT); + $this->pollMapper->find($pollId)->request(Poll::PERMISSION_POLL_EDIT); $this->shares = $this->shareMapper->findByPollNotInvited($pollId); } catch (ForbiddenException $e) { return []; @@ -172,7 +175,7 @@ public function get(string $token): Share { */ public function setType(string $token, string $type): Share { $this->share = $this->shareMapper->findByToken($token); - $this->acl->setPollId($this->share->getPollId(), Acl::PERMISSION_POLL_EDIT); + $this->pollMapper->find($this->share->getPollId())->request(Poll::PERMISSION_POLL_EDIT); // ATM only type user can transform to type admin and vice versa if (($type === Share::TYPE_ADMIN && $this->share->getType() === Share::TYPE_USER) @@ -191,7 +194,7 @@ public function setType(string $token, string $type): Share { public function setPublicPollEmail(string $token, string $value): Share { try { $this->share = $this->shareMapper->findByToken($token); - $this->acl->setPollId($this->share->getPollId(), Acl::PERMISSION_POLL_EDIT); + $this->pollMapper->find($this->share->getPollId())->request(Poll::PERMISSION_POLL_EDIT); $this->share->setPublicPollEmail($value); $this->share = $this->shareMapper->update($this->share); } catch (ShareNotFoundException $e) { @@ -254,7 +257,7 @@ public function setLabel(string $label, string $token): Share { $this->share = $this->shareMapper->findByToken($token); if ($this->share->getType() === Share::TYPE_PUBLIC) { - $this->acl->setPollId($this->share->getPollId(), Acl::PERMISSION_POLL_EDIT); + $this->pollMapper->find($this->share->getPollId())->request(Poll::PERMISSION_POLL_EDIT); $this->share->setLabel($label); // overwrite any possible displayName @@ -363,7 +366,7 @@ public function deleteByToken(string $token, bool $restore = false): Share { * @param bool $restore Set true, if share is to be restored */ public function delete(Share $share, bool $restore = false): Share { - $this->acl->setPollId($share->getPollId(), Acl::PERMISSION_POLL_EDIT); + $this->pollMapper->find($share->getPollId())->request(Poll::PERMISSION_POLL_EDIT); $share->setDeleted($restore ? 0 : time()); $this->shareMapper->update($share); @@ -387,8 +390,7 @@ public function lockByToken(string $token, bool $unlock = false): Share { * @param bool $unlock Set true, if share is to be unlocked */ private function lock(Share $share, bool $unlock = false): Share { - - $this->acl->setPollId($share->getPollId(), Acl::PERMISSION_POLL_EDIT); + $this->pollMapper->find($share->getPollId())->request(Poll::PERMISSION_POLL_EDIT); $share->setLocked($unlock ? 0 : time()); $this->shareMapper->update($share); @@ -502,7 +504,7 @@ public function add( string $displayName = '', string $emailAddress = '' ): Share { - $this->acl->setPollId($pollId, Acl::PERMISSION_POLL_EDIT); + $this->pollMapper->find($pollId)->request(Poll::PERMISSION_POLL_EDIT); if ($type === UserBase::TYPE_PUBLIC) { diff --git a/lib/Service/SubscriptionService.php b/lib/Service/SubscriptionService.php index a6b2f1e33..349eed5eb 100644 --- a/lib/Service/SubscriptionService.php +++ b/lib/Service/SubscriptionService.php @@ -25,10 +25,12 @@ namespace OCA\Polls\Service; +use OCA\Polls\Db\Poll; +use OCA\Polls\Db\PollMapper; use OCA\Polls\Db\Subscription; use OCA\Polls\Db\SubscriptionMapper; use OCA\Polls\Exceptions\ForbiddenException; -use OCA\Polls\Model\AclLegacy as Acl; +use OCA\Polls\Model\Acl as Acl; use OCP\AppFramework\Db\DoesNotExistException; use OCP\DB\Exception; @@ -38,18 +40,16 @@ class SubscriptionService { */ public function __construct( private SubscriptionMapper $subscriptionMapper, + private PollMapper $pollMapper, private Acl $acl, ) { } - public function get(?int $pollId = null): bool { - try { - if ($pollId !== null) { - $this->acl->setPollId($pollId); - } + public function get(int $pollId): bool { + $this->pollMapper->find($pollId)->request(Poll::PERMISSION_POLL_VIEW); - $this->acl->request(Acl::PERMISSION_POLL_SUBSCRIBE); - $this->subscriptionMapper->findByPollAndUser($this->acl->getPoll()->getId(), $this->acl->getUserId()); + try { + $this->subscriptionMapper->findByPollAndUser($pollId, $this->acl->getCurrentUserId()); // Subscription exists return true; } catch (DoesNotExistException $e) { @@ -59,15 +59,11 @@ public function get(?int $pollId = null): bool { } } - public function set(bool $setToSubscribed, ?int $pollId = null): bool { - if ($pollId !== null) { - $this->acl->setPollId($pollId); - } - + public function set(bool $setToSubscribed, int $pollId): bool { if (!$setToSubscribed) { // user wants to unsubscribe, allow unsubscribe neverteheless the permissions are set try { - $subscription = $this->subscriptionMapper->findByPollAndUser($this->acl->getPoll()->getId(), $this->acl->getUserId()); + $subscription = $this->subscriptionMapper->findByPollAndUser($pollId, $this->acl->getCurrentUserId()); $this->subscriptionMapper->delete($subscription); } catch (DoesNotExistException $e) { // Not found, assume already unsubscribed @@ -75,8 +71,8 @@ public function set(bool $setToSubscribed, ?int $pollId = null): bool { } } else { try { - $this->acl->request(Acl::PERMISSION_POLL_SUBSCRIBE); - $this->add($this->acl->getPoll()->getId(), $this->acl->getUserId()); + $this->pollMapper->find($pollId)->request(Poll::PERMISSION_POLL_SUBSCRIBE); + $this->add($pollId, $this->acl->getCurrentUserId()); } catch (ForbiddenException $e) { return false; } catch (Exception $e) { diff --git a/lib/Service/VoteService.php b/lib/Service/VoteService.php index e91f417c6..c4905dfe5 100644 --- a/lib/Service/VoteService.php +++ b/lib/Service/VoteService.php @@ -27,12 +27,13 @@ use OCA\Polls\Db\Option; use OCA\Polls\Db\OptionMapper; +use OCA\Polls\Db\Poll; +use OCA\Polls\Db\PollMapper; use OCA\Polls\Db\Vote; use OCA\Polls\Db\VoteMapper; use OCA\Polls\Event\VoteSetEvent; use OCA\Polls\Exceptions\VoteLimitExceededException; -use OCA\Polls\Model\AclLegacy as Acl; -use OCA\Polls\UserSession; +use OCA\Polls\Model\Acl as Acl; use OCP\AppFramework\Db\DoesNotExistException; use OCP\EventDispatcher\IEventDispatcher; @@ -44,9 +45,9 @@ public function __construct( private Acl $acl, private IEventDispatcher $eventDispatcher, private OptionMapper $optionMapper, + private PollMapper $pollMapper, private Vote $vote, private VoteMapper $voteMapper, - private UserSession $userSession, ) { } @@ -55,23 +56,17 @@ public function __construct( * * @return Vote[] */ - public function list(?int $pollId = null): array { - try { - if ($pollId !== null) { - $this->acl->setPollId($pollId); - } - - if (!$this->acl->getIsAllowed(Acl::PERMISSION_POLL_RESULTS_VIEW)) { - // Just return the participants votes, no further anoymizing or obfuscating is nessecary - return $this->voteMapper->findByPollAndUser($this->acl->getPoll()->getId(), ($this->userSession->getCurrentUserId())); - } - - $votes = $this->voteMapper->findByPoll($this->acl->getPoll()->getId()); - - } catch (DoesNotExistException $e) { - $votes = []; + public function list(int $pollId): array { + $poll = $this->pollMapper->find($pollId); + $poll->request(Poll::PERMISSION_POLL_VIEW); + + if (!$poll->getIsAllowed(Poll::PERMISSION_POLL_RESULTS_VIEW)) { + // Just return the participants votes, no further anoymizing or obfuscating is nessecary + return $this->voteMapper->findByPollAndUser($pollId, ($this->acl->getCurrentUserId())); } + $votes = $this->voteMapper->findByPoll($pollId); + return $votes; } @@ -92,16 +87,17 @@ private function checkLimits(Option $option): void { */ public function set(int $optionId, string $setTo): ?Vote { $option = $this->optionMapper->find($optionId); - $this->acl->setPollId($option->getPollId(), Acl::PERMISSION_VOTE_EDIT); + $poll = $this->pollMapper->find($option->getPollId()); + $poll->request(Poll::PERMISSION_VOTE_EDIT); if ($setTo === Vote::VOTE_YES) { $this->checkLimits($option); } // delete no votes, if poll setting is set to useNo === 0 - $deleteVoteInsteadOfNoVote = in_array(trim($setTo), [Vote::VOTE_NO, '']) && !boolval($this->acl->getPoll()->getUseNo()); + $deleteVoteInsteadOfNoVote = in_array(trim($setTo), [Vote::VOTE_NO, '']) && !boolval($poll->getUseNo()); try { - $this->vote = $this->voteMapper->findSingleVote($this->acl->getPoll()->getId(), $option->getPollOptionText(), $this->userSession->getCurrentUserId()); + $this->vote = $this->voteMapper->findSingleVote($poll->getId(), $option->getPollOptionText(), $this->acl->getCurrentUserId()); if ($deleteVoteInsteadOfNoVote) { $this->vote->setVoteAnswer(''); @@ -114,8 +110,8 @@ public function set(int $optionId, string $setTo): ?Vote { // Vote does not exist, insert as new Vote $this->vote = new Vote(); - $this->vote->setPollId($this->acl->getPoll()->getId()); - $this->vote->setUserId($this->userSession->getCurrentUserId()); + $this->vote->setPollId($poll->getId()); + $this->vote->setUserId($this->acl->getCurrentUserId()); $this->vote->setVoteOptionText($option->getPollOptionText()); $this->vote->setVoteOptionId($option->getId()); $this->vote->setVoteAnswer($setTo); @@ -126,20 +122,6 @@ public function set(int $optionId, string $setTo): ?Vote { return $this->vote; } - - - - /** - * Remove current user from poll - * @param bool $deleteOnlyOrphaned - false deletes all votes of the current user, true only the orphaned votes aka votes without an option - */ - public function deleteCurrentUserFromPoll(bool $deleteOnlyOrphaned = false): string { - $this->acl->request(Acl::PERMISSION_VOTE_EDIT); - $pollId = $this->acl->getPoll()->getId(); - $userId = $this->userSession->getCurrentUserId(); - return $this->delete($pollId, $userId, $deleteOnlyOrphaned); - } - /** * Remove user from poll * @@ -147,16 +129,21 @@ public function deleteCurrentUserFromPoll(bool $deleteOnlyOrphaned = false): str * @param string $userId user id of the user, the votes get deleted from * @param bool $deleteOnlyOrphaned - false deletes all votes of the specified user, true only the orphaned votes aka votes without an option */ - public function deletUserFromPoll(int $pollId, string $userId, bool $deleteOnlyOrphaned = false): string { + public function deleteUserFromPoll(int $pollId, string $userId = '', bool $deleteOnlyOrphaned = false): string { if ($userId === '') { - $userId = $this->userSession->getCurrentUserId(); + // if no user set, use current user + $userId = $this->acl->getCurrentUserId(); } - if ($userId === $this->userSession->getCurrentUserId()) { - $this->acl->setPollId($pollId, Acl::PERMISSION_VOTE_EDIT); - } else { - $this->acl->setPollId($pollId, Acl::PERMISSION_POLL_EDIT); + + // Set default right to delete all votes of the user + $checkRight = Poll::PERMISSION_POLL_EDIT; + if ($userId === $this->acl->getCurrentUserId()) { + // allow current user to remove his votes + $checkRight = Poll::PERMISSION_VOTE_EDIT; } + $this->pollMapper->find($pollId)->request($checkRight); + return $this->delete($pollId, $userId, $deleteOnlyOrphaned); } diff --git a/lib/Service/WatchService.php b/lib/Service/WatchService.php index 64d173722..ade9890d5 100644 --- a/lib/Service/WatchService.php +++ b/lib/Service/WatchService.php @@ -25,10 +25,11 @@ namespace OCA\Polls\Service; +use OCA\Polls\Db\Poll; +use OCA\Polls\Db\PollMapper; use OCA\Polls\Db\Watch; use OCA\Polls\Db\WatchMapper; use OCA\Polls\Exceptions\NoUpdatesException; -use OCA\Polls\Model\AclLegacy as Acl; use OCA\Polls\Model\Settings\AppSettings; use OCA\Polls\UserSession; use OCP\AppFramework\Db\DoesNotExistException; @@ -40,22 +41,20 @@ class WatchService { * @psalm-suppress PossiblyUnusedMethod */ public function __construct( - private WatchMapper $watchMapper, - private Acl $acl, private AppSettings $appSettings, - private Watch $watch, + private PollMapper $pollMapper, private UserSession $userSession, + private Watch $watch, + private WatchMapper $watchMapper, ) { } /** * Watch poll for updates */ - public function watchUpdates(int $pollId = 0, ?int $offset = null): array { - if ($pollId !== 0) { - $this->acl->setPollId($pollId); - } - + public function watchUpdates(int $pollId, ?int $offset = null): array { + $this->pollMapper->find($pollId)->request(Poll::PERMISSION_POLL_VIEW); + $start = time(); $timeout = 30; $offset = $offset ?? $start; diff --git a/src/js/Api/modules/polls.js b/src/js/Api/modules/polls.js index 53bdc5fcd..a7fd55ed5 100644 --- a/src/js/Api/modules/polls.js +++ b/src/js/Api/modules/polls.js @@ -79,10 +79,10 @@ const polls = { }) }, - updatePoll(poll) { + updatePoll(pollId, poll) { return httpInstance.request({ method: 'PUT', - url: `poll/${poll.id}`, + url: `poll/${pollId}`, data: { poll }, cancelToken: cancelTokenHandlerObject[this.updatePoll.name].handleRequestCancellation().token, }) diff --git a/src/js/App.vue b/src/js/App.vue index 16fd779ec..61e0aaeb2 100644 --- a/src/js/App.vue +++ b/src/js/App.vue @@ -66,10 +66,7 @@ export default { computed: { ...mapState({ - settings: (state) => state.settings.user, - appSettings: (state) => state.appSettings, - poll: (state) => state.poll, - permissions: (state) => state.poll.acl.permissions, + permissions: (state) => state.poll.permissions, }), appClass() { diff --git a/src/js/components/Calendar/CalendarPeek.vue b/src/js/components/Calendar/CalendarPeek.vue index 1057b5b7e..8d79568c1 100644 --- a/src/js/components/Calendar/CalendarPeek.vue +++ b/src/js/components/Calendar/CalendarPeek.vue @@ -71,7 +71,7 @@ export default { computed: { ...mapState({ - poll: (state) => state.poll, + pollConfiguration: (state) => state.poll.configuration, }), detectAllDay() { @@ -86,25 +86,25 @@ export default { sortedEvents() { const sortedEvents = [...this.events] - sortedEvents.push(this.thisOption) + sortedEvents.push(this.currentOption) return orderBy(sortedEvents, ['start', 'end'], ['asc', 'asc']) }, - thisOption() { + currentOption() { return { id: this.option.id, UID: this.option.id, - calendarUri: this.poll.uri, + calendarUri: '', calendarKey: 0, calendarName: 'Polls', displayColor: 'transparent', allDay: this.detectAllDay.allDay, - description: this.poll.description, + description: this.pollConfiguration.description, start: this.option.timestamp, location: '', end: this.option.timestamp + this.option.duration, status: 'self', - summary: this.poll.title, + summary: this.pollConfiguration.title, type: this.detectAllDay.type, } }, diff --git a/src/js/components/Cards/VoteInfoCards.vue b/src/js/components/Cards/VoteInfoCards.vue index 92469c37d..2b95e7dd7 100644 --- a/src/js/components/Cards/VoteInfoCards.vue +++ b/src/js/components/Cards/VoteInfoCards.vue @@ -52,49 +52,47 @@ export default { computed: { ...mapState({ - pollAccess: (state) => state.poll.access, + pollAccess: (state) => state.poll.configuration.access, pollId: (state) => state.poll.id, - allowEdit: (state) => state.poll.acl.permissions.edit, - allowVote: (state) => state.poll.acl.permissions.vote, - allowAddOptions: (state) => state.poll.acl.permissions.addOptions, - maxVotesPerOption: (state) => state.poll.limits.maxVotesPerOption, - maxVotesPerUser: (state) => state.poll.limits.maxVotesPerUser, + permissions: (state) => state.poll.permissions, + maxVotesPerOption: (state) => state.poll.configuration.maxVotesPerOption, + maxVotesPerUser: (state) => state.poll.configuration.maxVotesPerUser, optionsCount: (state) => state.options.list.length, isLocked: (state) => state.poll.currentUserStatus.isLocked, userRole: (state) => state.poll.currentUserStatus.userRole, }), ...mapGetters({ - closed: 'poll/isClosed', + isPollClosed: 'poll/isClosed', confirmedOptions: 'options/confirmed', hasShares: 'shares/hasShares', - proposalsOpen: 'poll/proposalsOpen', + isProposalOpen: 'poll/isProposalOpen', }), showUnpublishedPollCard() { - return this.pollAccess === 'private' && !this.hasShares && this.allowEdit && this.optionsCount + return this.pollAccess === 'private' && !this.hasShares && this.permissions.edit && this.optionsCount }, showAddProposalsCard() { - return this.allowAddOptions && this.proposalsOpen && !this.closed + return this.permissions.addOptions && this.isProposalOpen && !this.isPollClosed }, showClosedCard() { - return this.closed && !this.showSendConfirmationsCard + return this.isPollClosed && !this.showSendConfirmationsCard }, showSendConfirmationsCard() { - return this.allowEdit && this.closed && this.confirmedOptions.length > 0 + return this.permissions.edit && this.isPollClosed && this.confirmedOptions.length > 0 }, showLimitCard() { - return this.allowVote && !this.closed && (this.maxVotesPerOption || this.maxVotesPerUser) + return this.permissions.vote && !this.isPollClosed && (this.maxVotesPerOption || this.maxVotesPerUser) }, showRegisterCard() { return (this.$route.name === 'publicVote' && ['public', 'email', 'contact'].includes(this.userRole) - && !this.closed + && !this.isPollClosed && !this.isLocked && !!this.pollId ) diff --git a/src/js/components/Cards/modules/CardAddProposals.vue b/src/js/components/Cards/modules/CardAddProposals.vue index cb155872f..7cde52f34 100644 --- a/src/js/components/Cards/modules/CardAddProposals.vue +++ b/src/js/components/Cards/modules/CardAddProposals.vue @@ -23,7 +23,7 @@ diff --git a/src/js/components/Cards/modules/CardSendConfirmations.vue b/src/js/components/Cards/modules/CardSendConfirmations.vue index 21e29c766..548d08cc7 100644 --- a/src/js/components/Cards/modules/CardSendConfirmations.vue +++ b/src/js/components/Cards/modules/CardSendConfirmations.vue @@ -31,7 +31,6 @@ diff --git a/src/js/components/PollList/PollItem.vue b/src/js/components/PollList/PollItem.vue index 7e88d0154..e8904ad06 100644 --- a/src/js/components/PollList/PollItem.vue +++ b/src/js/components/PollList/PollItem.vue @@ -64,11 +64,11 @@
- {{ poll.title }} + {{ pollConfiguration.title }}
- {{ poll.description ? poll.description : t('polls', 'No description provided') }} + {{ pollConfiguration.description ? pollConfiguration.description : t('polls', 'No description provided') }}
@@ -77,18 +77,18 @@ :to="{ name: 'vote', params: { id: poll.id }}" :class="{ closed: closed, active: (poll.id === $store.state.poll.id) }">
- {{ poll.title }} + {{ pollConfiguration.title }}
- {{ poll.description ? poll.description : t('polls', 'No description provided') }} + {{ pollConfiguration.description ? pollConfiguration.description : t('polls', 'No description provided') }}
- - + +
@@ -159,19 +159,27 @@ export default { }), closeToClosing() { - return (!this.closed && this.poll.expire && moment.unix(this.poll.expire).diff() < 86400000) + return (!this.closed && this.pollConfiguration.expire && moment.unix(this.pollConfiguration.expire).diff() < 86400000) }, closed() { - return this.poll.pollExpired + return this.pollStatus.expired + }, + + pollConfiguration() { + return this.poll.configuration + }, + + pollStatus() { + return this.poll.status }, accessType() { - if (this.poll.deleted) { + if (this.pollConfiguration.deleted) { return t('polls', 'Archived') } - if (this.poll.access === 'open') { + if (this.pollConfiguration.access === 'open') { return t('polls', 'Openly accessible poll') } @@ -190,8 +198,8 @@ export default { }, timeExpirationRelative() { - if (this.poll.expire) { - return moment.unix(this.poll.expire).fromNow() + if (this.pollConfiguration.expire) { + return moment.unix(this.pollConfiguration.expire).fromNow() } return t('polls', 'never') @@ -202,11 +210,11 @@ export default { return 'error' } - if (this.poll.expire && this.closeToClosing) { + if (this.pollConfiguration.expire && this.closeToClosing) { return 'warning' } - if (this.poll.expire && !this.closed) { + if (this.pollConfiguration.expire && !this.closed) { return 'success' } @@ -214,7 +222,7 @@ export default { }, timeCreatedRelative() { - return moment.unix(this.poll.created).fromNow() + return moment.unix(this.pollStatus.created).fromNow() }, }, } diff --git a/src/js/components/Public/PublicRegisterModal.vue b/src/js/components/Public/PublicRegisterModal.vue index a213f49f7..4e4a1c2b7 100644 --- a/src/js/components/Public/PublicRegisterModal.vue +++ b/src/js/components/Public/PublicRegisterModal.vue @@ -132,7 +132,6 @@ export default { computed: { ...mapState({ - poll: (state) => state.poll, share: (state) => state.share, privacyUrl: (state) => state.appSettings.usePrivacyUrl, imprintUrl: (state) => state.appSettings.useImprintUrl, diff --git a/src/js/components/Shares/ShareItem.vue b/src/js/components/Shares/ShareItem.vue index 769c57f5f..0ebc71cde 100644 --- a/src/js/components/Shares/ShareItem.vue +++ b/src/js/components/Shares/ShareItem.vue @@ -251,7 +251,6 @@ export default { switchAdmin: 'shares/switchAdmin', setPublicPollEmail: 'shares/setPublicPollEmail', writeLabel: 'shares/writeLabel', - deleteUser: 'votes/deleteUser', }), async switchLocked(share) { diff --git a/src/js/components/Shares/ShareItemAllUsers.vue b/src/js/components/Shares/ShareItemAllUsers.vue index 6cbf86763..cf0c70d47 100644 --- a/src/js/components/Shares/ShareItemAllUsers.vue +++ b/src/js/components/Shares/ShareItemAllUsers.vue @@ -45,7 +45,7 @@ export default { computed: { ...mapState({ - access: (state) => state.poll.access, + access: (state) => state.poll.configuration.access, }), userItemProps() { diff --git a/src/js/components/Shares/SharesList.vue b/src/js/components/Shares/SharesList.vue index 74e521bc1..cd8f2c3ea 100644 --- a/src/js/components/Shares/SharesList.vue +++ b/src/js/components/Shares/SharesList.vue @@ -89,11 +89,11 @@ export default { computed: { ...mapState({ - permissions: (state) => state.poll.acl.permissions, - pollAccess: (state) => state.poll.access, - pollTitle: (state) => state.poll.title, - pollDescription: (state) => state.poll.description, + permissions: (state) => state.poll.permissions, + pollTitle: (state) => state.poll.configuration.title, + pollDescription: (state) => state.poll.configuration.description, }), + ...mapGetters({ activeShares: 'shares/active', }), diff --git a/src/js/components/Shares/SharesListLocked.vue b/src/js/components/Shares/SharesListLocked.vue index 497ad6459..b306b3751 100644 --- a/src/js/components/Shares/SharesListLocked.vue +++ b/src/js/components/Shares/SharesListLocked.vue @@ -37,7 +37,7 @@ diff --git a/src/js/components/Shares/SharesListUnsent.vue b/src/js/components/Shares/SharesListUnsent.vue index 809af8151..a049bba7c 100644 --- a/src/js/components/Shares/SharesListUnsent.vue +++ b/src/js/components/Shares/SharesListUnsent.vue @@ -78,7 +78,6 @@ export default { methods: { ...mapActions({ - removeShare: 'shares/delete', inviteAll: 'shares/inviteAll', }), diff --git a/src/js/components/SideBar/SideBarTabActivity.vue b/src/js/components/SideBar/SideBarTabActivity.vue index c4f6d33de..bcb9588c5 100644 --- a/src/js/components/SideBar/SideBarTabActivity.vue +++ b/src/js/components/SideBar/SideBarTabActivity.vue @@ -47,7 +47,6 @@ export default { computed: { ...mapState({ - acl: (state) => state.poll.acl, activities: (state) => state.activity.list, }), diff --git a/src/js/components/SideBar/SideBarTabComments.vue b/src/js/components/SideBar/SideBarTabComments.vue index 5aecffaf2..a82eacdbb 100644 --- a/src/js/components/SideBar/SideBarTabComments.vue +++ b/src/js/components/SideBar/SideBarTabComments.vue @@ -52,7 +52,7 @@ export default { computed: { ...mapState({ - permissions: (state) => state.poll.acl.permissions, + permissions: (state) => state.poll.permissions, }), ...mapGetters({ diff --git a/src/js/components/SideBar/SideBarTabConfiguration.vue b/src/js/components/SideBar/SideBarTabConfiguration.vue index 021ed50b1..eb6ddbee0 100644 --- a/src/js/components/SideBar/SideBarTabConfiguration.vue +++ b/src/js/components/SideBar/SideBarTabConfiguration.vue @@ -59,11 +59,11 @@ - @@ -166,16 +166,16 @@ export default { computed: { ...mapState({ - isPollArchived: (state) => state.poll.deleted, - pollType: (state) => state.poll.type, pollId: (state) => state.poll.id, - hasEpiration: (state) => state.poll.expire, + pollType: (state) => state.poll.type, + isPollArchived: (state) => state.poll.status.deleted, + hasExpiration: (state) => state.poll.configuration.expire, + showResults: (state) => state.poll.configuration.showResults, currentUser: (state) => state.poll.acl.currentUser, - showResults: (state) => state.poll.showResults, }), ...mapGetters({ - closed: 'poll/isClosed', + isPollClosed: 'poll/isClosed', hasVotes: 'votes/hasVotes', }), }, diff --git a/src/js/components/SideBar/SideBarTabDatePolls.vue b/src/js/components/SideBar/SideBarTabDatePolls.vue index 0d0856ad8..4d755060b 100644 --- a/src/js/components/SideBar/SideBarTabDatePolls.vue +++ b/src/js/components/SideBar/SideBarTabDatePolls.vue @@ -28,7 +28,7 @@ @click="toggle(poll.id)">
- {{ poll.title }} + {{ poll.configuration.title }}
diff --git a/src/js/components/SideBar/SideBarTabOptions.vue b/src/js/components/SideBar/SideBarTabOptions.vue index 3003f1830..f4eb60ff4 100644 --- a/src/js/components/SideBar/SideBarTabOptions.vue +++ b/src/js/components/SideBar/SideBarTabOptions.vue @@ -30,7 +30,7 @@ - + @@ -45,7 +45,7 @@ diff --git a/src/js/components/User/UserMenu.vue b/src/js/components/User/UserMenu.vue index c31c92e3a..313e61134 100644 --- a/src/js/components/User/UserMenu.vue +++ b/src/js/components/User/UserMenu.vue @@ -183,7 +183,7 @@ export default { computed: { ...mapState({ - permissions: (state) => state.poll.acl.permissions, + permissions: (state) => state.poll.permissions, share: (state) => state.share, subscribed: (state) => state.subscription.subscribed, emailAddress: (state) => state.share.user.emailAddress, diff --git a/src/js/components/VoteTable/VoteColumn.vue b/src/js/components/VoteTable/VoteColumn.vue index bf80aacbb..2527d0c3f 100644 --- a/src/js/components/VoteTable/VoteColumn.vue +++ b/src/js/components/VoteTable/VoteColumn.vue @@ -22,10 +22,10 @@