Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Define user role via joined shares #3381

Merged
merged 2 commits into from
Mar 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions lib/Controller/PublicController.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@
use OCP\Util;

/**
* Always use parent's classe response* methods to make sure, the token gets set correctly.
* Requesting the token inside the controller is not possible, because the token is submitted
* as a paramter and not known while contruction time
* i.e. ACL requests are not valid before calling the response* method
* @psalm-api
*/
class PublicController extends BasePublicController {
Expand Down Expand Up @@ -89,13 +93,14 @@ public function votePage() {
*/
#[PublicPage]
public function getPoll(string $token): JSONResponse {
$this->acl->request(Acl::PERMISSION_POLL_VIEW);

// load poll through acl
return $this->response(fn () => [
'acl' => $this->acl,
'poll' => $this->acl->getPoll(),
], $token);
return $this->response(function () {
$this->acl->request(Acl::PERMISSION_POLL_VIEW);
// load poll through acl
return [
'acl' => $this->acl,
'poll' => $this->acl->getPoll(),
];
}, $token);
}

/**
Expand Down
47 changes: 39 additions & 8 deletions lib/Db/Poll.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,16 @@ class Poll extends EntityWithUser implements JsonSerializable {
public const TWO_DAYS = 172800;
public const ONE_AND_HALF_DAY = 129600;

public const ROLE_USER = Share::TYPE_USER;
public const ROLE_ADMIN = Share::TYPE_ADMIN;
public const ROLE_EMAIL = Share::TYPE_EMAIL;
public const ROLE_CONTACT = Share::TYPE_CONTACT;
public const ROLE_EXTERNAL = Share::TYPE_EXTERNAL;
public const ROLE_OWNER = 'owner';
public const ROLE_NONE = 'none';



private IURLGenerator $urlGenerator;
protected UserMapper $userMapper;
private VoteMapper $voteMapper;
Expand Down Expand Up @@ -137,6 +147,8 @@ class Poll extends EntityWithUser implements JsonSerializable {
protected int $maxDate = 0;
protected int $minDate = 0;
protected int $currentUserVotes = 0;
protected string $userRole = self::ROLE_NONE;
protected ?int $isCurrentUserLocked = 0;

public function __construct() {
$this->addType('created', 'int');
Expand Down Expand Up @@ -168,6 +180,7 @@ public function __construct() {
public function jsonSerialize(): array {
return [
'id' => $this->getId(),
'type' => $this->getType(),
'title' => $this->getTitle(),
'description' => $this->getDescription(),
'descriptionSafe' => $this->getDescriptionSafe(),
Expand All @@ -185,14 +198,14 @@ public function jsonSerialize(): array {
'optionLimit' => $this->getOptionLimit(),
'proposalsExpire' => $this->getProposalsExpire(),
'showResults' => $this->getShowResults() === 'expired' ? Poll::SHOW_RESULTS_CLOSED : $this->getShowResults(),
'type' => $this->getType(),
'useNo' => $this->getUseNo(),
'voteLimit' => $this->getVoteLimit(),
'lastInteraction' => $this->getLastInteraction(),
'summary' => [
'orphanedVotes' => count($this->voteMapper->findOrphanedByPollandUser($this->id, $this->userMapper->getCurrentUserCached()->getId())),
'yesByCurrentUser' => count($this->voteMapper->getYesVotesByParticipant($this->getPollId(), $this->userMapper->getCurrentUserCached()->getId())),
'orphanedVotes' => $this->getCurrentUserOrphanedVotes(),
'yesByCurrentUser' => $this->getCurrentUserYesVotes(),
'countVotes' => $this->getCurrentUserCountVotes(),
'userRole' => $this->getUserRole(),
],
];
}
Expand Down Expand Up @@ -231,6 +244,13 @@ public function getExpired(): bool {
);
}

public function getUserRole(): string {
if ($this->userMapper->getCurrentUser()->getId() === $this->getOwner()) {
return self::ROLE_OWNER;
}
return $this->userRole;
}

public function getVoteUrl(): string {
return $this->urlGenerator->linkToRouteAbsolute(
AppConstants::APP_ID . '.page.vote',
Expand Down Expand Up @@ -278,10 +298,6 @@ public function getProposalsExpired(): bool {
);
}

public function getCurrentUserCountVotes(): int {
return $this->currentUserVotes;
}

public function getDescription(): string {
return $this->description ?? '';
}
Expand Down Expand Up @@ -337,13 +353,28 @@ public function getRelevantThresholdNet(): int {
);
}

public function getCurrentUserCountVotes(): int {
return $this->currentUserVotes;
}

public function getIsCurrentUserLocked(): bool {
return (bool) $this->isCurrentUserLocked;
}

/**
* @psalm-return int<0, max>
*/
public function getOrphanedVotes(): int {
public function getCurrentUserOrphanedVotes(): int {
return count($this->voteMapper->findOrphanedByPollandUser($this->id, $this->userMapper->getCurrentUserCached()->getId()));
}

/**
* @psalm-return int<0, max>
*/
public function getCurrentUserYesVotes(): int {
return count($this->voteMapper->getYesVotesByParticipant($this->getPollId(), $this->userMapper->getCurrentUserCached()->getId()));
}

public function getDeadline(): int {
// if expiration is set return expiration date
if ($this->getExpire()) {
Expand Down
24 changes: 22 additions & 2 deletions lib/Db/PollMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -179,10 +179,32 @@ protected function buildQuery(): IQueryBuilder {
->from($this->getTableName(), self::TABLE);
$this->joinOptionsForMaxDate($qb, self::TABLE);
$this->joinCurrentUserVotes($qb, self::TABLE, $currentUserId);
$this->joinUserRole($qb, self::TABLE, $currentUserId);
$qb->groupBy(self::TABLE . '.id');
return $qb;
}

/**
* Joins options to evaluate min and max option date for date polls
* if text poll or no options are set,
* the min value is the current time,
* the max value is null
*/
protected function joinUserRole(IQueryBuilder &$qb, string $fromAlias, string $currentUserId): void {
$joinAlias = 'shares';
$qb->addSelect($qb->createFunction('coalesce(' . $joinAlias . '.type, "") AS user_role'));

$qb->leftJoin(
$fromAlias,
Share::TABLE,
$joinAlias,
$qb->expr()->andX(
$qb->expr()->eq($fromAlias . '.id', $joinAlias . '.poll_id'),
$qb->expr()->eq($joinAlias . '.user_id', $qb->createNamedParameter($currentUserId, IQueryBuilder::PARAM_STR)),
)
);
}

/**
* Joins options to evaluate min and max option date for date polls
* if text poll or no options are set,
Expand All @@ -193,8 +215,6 @@ protected function joinOptionsForMaxDate(IQueryBuilder &$qb, string $fromAlias):
$joinAlias = 'options';
$saveMin = (string) time();

// force value into a MIN function to avoid grouping errors
// $qb->selectAlias($qb->func()->max($joinAlias . '.timestamp'), 'max_date');
$qb->addSelect($qb->createFunction('coalesce(MAX(' . $joinAlias . '.timestamp), 0) AS max_date'))
->addSelect($qb->createFunction('coalesce(MIN(' . $joinAlias . '.timestamp), ' . $saveMin . ') AS min_date'));

Expand Down
23 changes: 23 additions & 0 deletions lib/Db/ShareMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,29 @@ public function findByPoll(int $pollId, bool $getDeleted = false): array {
return $this->findEntities($qb);
}

/**
* @throws \OCP\AppFramework\Db\DoesNotExistException if not found
* @return Share[]
* @psalm-return array<array-key, Share>
*/
public function findGroupShareByPoll(int $pollId, bool $getDeleted = false): array {
$qb = $this->db->getQueryBuilder();

$qb->select(self::TABLE . '.*')
->from($this->getTableName(), self::TABLE)
->groupBy(self::TABLE . '.id')
->where($qb->expr()->eq(self::TABLE . '.poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq(self::TABLE . '.type', $qb->createNamedParameter(Share::TYPE_GROUP, IQueryBuilder::PARAM_STR)));

if (!$getDeleted) {
$qb->andWhere($qb->expr()->eq(self::TABLE . '.deleted', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
}

$this->joinUserVoteCount($qb, self::TABLE);

return $this->findEntities($qb);
}

/**
* @throws \OCP\AppFramework\Db\DoesNotExistException if not found
* @return Share[]
Expand Down
4 changes: 2 additions & 2 deletions lib/Db/UserMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public function getCurrentUser(): UserBase {

} else {
try {
$this->currentUser = $this->getUserFromShareToken($this->getToken());
$this->currentUser = $this->getUserFromShareToken($this->getSessionStoredShareToken());
} catch (DoesNotExistException $e) {
$this->logger->debug('no user found, returned fake user');
$this->currentUser = new GenericUser('', Share::TYPE_PUBLIC);
Expand All @@ -114,7 +114,7 @@ public function getCurrentUserCached(): UserBase {
return $this->currentUser ?? $this->getCurrentUser();
}

private function getToken(): string {
public function getSessionStoredShareToken(): string {
return (string) $this->session->get(AppConstants::SESSION_KEY_SHARE_TOKEN);
}

Expand Down
36 changes: 0 additions & 36 deletions lib/Exceptions/InvalidPollIdException.php

This file was deleted.

Loading
Loading