Skip to content

Commit

Permalink
Merge pull request #3523 from nextcloud/fix/hiden-results-and-limited…
Browse files Browse the repository at this point in the history
…-votes

Fix/hiden-results-and-limited-votes
  • Loading branch information
dartcafe authored May 25, 2024
2 parents 158ab85 + d33c82f commit ddb4ac4
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 40 deletions.
9 changes: 5 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,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
- disable registration button while registration is pending
- disable "resolve group share" while resolving
- Fixed counting of orphaned votes
- Disable registration button while registration is pending
- Disable "resolve group share" while resolving
- Fix showing booked up options in polls with hidden results
### Changes
- mainly performance improvements
- 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)
Expand Down
20 changes: 13 additions & 7 deletions lib/Db/Option.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
* @method int getVotesYes()
* @method int getVotesNo()
* @method int getVotesMaybe()
* @method int getShowResults()
*/
class Option extends EntityWithUser implements JsonSerializable {
public const TABLE = 'polls_options';
Expand All @@ -95,6 +96,7 @@ class Option extends EntityWithUser implements JsonSerializable {
protected int $votesYes = 0;
protected int $votesNo = 0;
protected int $votesMaybe = 0;
protected int $showResults = 0;

public function __construct() {
$this->addType('released', 'int');
Expand All @@ -113,6 +115,7 @@ public function __construct() {
$this->addType('votesYes', 'int');
$this->addType('votesNo', 'int');
$this->addType('votesMaybe', 'int');
$this->addType('showResults', 'int');
}

/**
Expand All @@ -132,17 +135,20 @@ public function jsonSerialize(): array {
'duration' => $this->getDuration(),
'locked' => $this->getIsLocked(),
'hash' => $this->getPollOptionHash(),
'votes' => [
'no' => $this->getVotesNo(),
'yes' => $this->getVotesYes(),
'maybe' => $this->getVotesMaybe(),
'count' => $this->getCountOptionVotes(),
'currentUser' => $this->getUserVoteAnswer(),
],
'votes' => $this->getVotes(),
'owner' => $this->getUser(),
];
}

private function getVotes(): array {
return [
'no' => $this->getVotesNo() * $this->getShowResults(),
'yes' => $this->getVotesYes() * $this->getShowResults(),
'maybe' => $this->getVotesMaybe() * $this->getShowResults(),
'count' => $this->getCountOptionVotes() * $this->getShowResults(),
'currentUser' => $this->getUserVoteAnswer(),
];
}
/**
* cumulative Set option entities cumulative and validated
* if timestamp is given, the pollOptionText will be synced according to the timestamp and duration
Expand Down
51 changes: 23 additions & 28 deletions lib/Db/OptionMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,10 @@ public function purgeDeletedOptions(int $offset): void {

/**
* Build the enhanced query with joined tables
* @param bool $hideVotes Whether the votes should be hidden, skips vote counting
* @param bool $hideResults Whether poll results are defined as beeing hidden
* injects the poll permission allowdSeeResults into the query
*/
protected function buildQuery(bool $hideVotes = false): IQueryBuilder {
protected function buildQuery(bool $hideResults = false): IQueryBuilder {
$currentUserId = $this->userSession->getCurrentUserId();
$qb = $this->db->getQueryBuilder();

Expand All @@ -180,7 +181,7 @@ protected function buildQuery(bool $hideVotes = false): IQueryBuilder {
->orderBy('order', 'ASC');


$this->joinVotesCount($qb, self::TABLE, $hideVotes);
$this->joinVotesCount($qb, self::TABLE, $hideResults);
$this->joinPollForLimits($qb, self::TABLE);
$this->joinCurrentUserVote($qb, self::TABLE, $currentUserId);
$this->joinCurrentUserVoteCount($qb, self::TABLE, $currentUserId);
Expand All @@ -193,33 +194,27 @@ protected function buildQuery(bool $hideVotes = false): IQueryBuilder {
/**
* Joins votes to count votes per option and answer
*/
protected function joinVotesCount(IQueryBuilder &$qb, string $fromAlias, bool $hideVotes = false): void {
protected function joinVotesCount(IQueryBuilder &$qb, string $fromAlias, bool $hideResults = false): void {
$joinAlias = 'votes';
if ($hideVotes) {
// hide all vote counts
$qb->addSelect($qb->createFunction('0 AS count_option_votes'))
->addSelect($qb->createFunction('0 AS votes_yes'))
->addSelect($qb->createFunction('0 AS votes_no'))
->addSelect($qb->createFunction('0 AS votes_maybe'));
} else {
$qb->leftJoin(
$fromAlias,
Vote::TABLE,
$joinAlias,
$qb->expr()->andX(
$qb->expr()->eq($fromAlias . '.poll_id', $joinAlias . '.poll_id'),
$qb->expr()->eq($fromAlias . '.poll_option_text', $joinAlias . '.vote_option_text'),
)
$qb->leftJoin(
$fromAlias,
Vote::TABLE,
$joinAlias,
$qb->expr()->andX(
$qb->expr()->eq($fromAlias . '.poll_id', $joinAlias . '.poll_id'),
$qb->expr()->eq($fromAlias . '.poll_option_text', $joinAlias . '.vote_option_text'),
)
// Count number of votes for this option
->addSelect($qb->createFunction('COUNT(DISTINCT(' . $joinAlias . '.id)) AS count_option_votes'))
// Count number of yes votes for this option
->addSelect($qb->createFunction('COUNT(DISTINCT(CASE WHEN ' . $joinAlias . '.vote_answer = \'yes\' THEN ' . $joinAlias . '.id END)) AS votes_yes'))
// Count number of no votes for this option
->addSelect($qb->createFunction('COUNT(DISTINCT(CASE WHEN ' . $joinAlias . '.vote_answer = \'no\' THEN ' . $joinAlias . '.id END)) AS votes_no'))
// Count number of maybe votes for this option
->addSelect($qb->createFunction('COUNT(DISTINCT(CASE WHEN ' . $joinAlias . '.vote_answer = \'maybe\' THEN ' . $joinAlias . '.id END)) AS votes_maybe'));
}
)
// Count number of votes for this option
->addSelect($qb->createFunction('COUNT(DISTINCT(' . $joinAlias . '.id)) AS count_option_votes'))
// Count number of yes votes for this option
->addSelect($qb->createFunction('COUNT(DISTINCT(CASE WHEN ' . $joinAlias . '.vote_answer = \'yes\' THEN ' . $joinAlias . '.id END)) AS votes_yes'))
// Count number of no votes for this option
->addSelect($qb->createFunction('COUNT(DISTINCT(CASE WHEN ' . $joinAlias . '.vote_answer = \'no\' THEN ' . $joinAlias . '.id END)) AS votes_no'))
// Count number of maybe votes for this option
->addSelect($qb->createFunction('COUNT(DISTINCT(CASE WHEN ' . $joinAlias . '.vote_answer = \'maybe\' THEN ' . $joinAlias . '.id END)) AS votes_maybe'))
// inject if the votes should be hidden
->addSelect($qb->createFunction(intval(!$hideResults) . ' as show_results'));
}

/**
Expand Down
4 changes: 4 additions & 0 deletions lib/Db/Poll.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
* @method int getMinDate()
* @method int getMaxDate()
* @method int getShareToken()
* @method int getCountOptions()
*
* Magic functions for subqueried columns
* @method int getCurrentUserCountOrphanedVotes()
Expand Down Expand Up @@ -172,6 +173,7 @@ class Poll extends EntityWithUser implements JsonSerializable {
protected string $userRole = self::ROLE_NONE;
protected string $shareToken = '';
protected ?string $groupShares = '';
protected int $countOptions = 0;

// subqueried columns
protected int $currentUserCountOrphanedVotes = 0;
Expand All @@ -197,6 +199,7 @@ public function __construct() {
$this->addType('isCurrentUserLocked', 'int');
$this->addType('maxDate', 'int');
$this->addType('minDate', 'int');
$this->addType('countOptions', 'int');

// subqueried columns
$this->addType('currentUserCountVotes', 'int');
Expand Down Expand Up @@ -235,6 +238,7 @@ public function getStatusArray(): array {
'deleted' => boolval($this->getDeleted()),
'expired' => $this->getExpired(),
'relevantThreshold' => $this->getRelevantThreshold(),
'countOptions' => $this->getCountOptions(),
];
}
public function getConfigurationArray(): array {
Expand Down
7 changes: 6 additions & 1 deletion lib/Db/PollMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ protected function joinGroupShares(IQueryBuilder &$qb, string $fromAlias): void
* if text poll or no options are set,
* the min value is the current time,
* the max value is null
* and adds the number of available options
*/
protected function joinOptionsForMaxDate(IQueryBuilder &$qb, string $fromAlias): void {
$joinAlias = 'options';
Expand All @@ -293,12 +294,16 @@ protected function joinOptionsForMaxDate(IQueryBuilder &$qb, string $fromAlias):

$qb->addSelect($qb->createFunction('coalesce(MAX(' . $joinAlias . '.timestamp), '. $zero . ') AS max_date'))
->addSelect($qb->createFunction('coalesce(MIN(' . $joinAlias . '.timestamp), ' . $saveMin . ') AS min_date'));
$qb->selectAlias($qb->func()->count($joinAlias . '.id'), 'count_options');

$qb->leftJoin(
$fromAlias,
Option::TABLE,
$joinAlias,
$qb->expr()->eq($fromAlias . '.id', $joinAlias . '.poll_id'),
$qb->expr()->andX(
$qb->expr()->eq($fromAlias . '.id', $joinAlias . '.poll_id'),
$qb->expr()->eq($joinAlias . '.deleted', $qb->expr()->literal(0, IQueryBuilder::PARAM_INT)),
),
);
}

Expand Down
2 changes: 2 additions & 0 deletions src/js/store/modules/poll.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ const defaultPoll = () => ({
created: 0,
deleted: false,
expired: false,
relevantThreshold: 0,
countOptions: 0,
},
currentUserStatus: {
userRole: '',
Expand Down
8 changes: 8 additions & 0 deletions src/js/views/Vote.vue
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ export default {
computed: {
...mapState({
pollType: (state) => state.poll.type,
countOptionsInPoll: (state) => state.poll.status.countOptions,
pollTitle: (state) => state.poll.configuration.title,
pollDescription: (state) => state.poll.configuration.description,
pollAnonymous: (state) => state.poll.configuration.anonymous,
Expand All @@ -126,6 +127,13 @@ export default {
}),

emptyContentProps() {
if (this.countOptionsInPoll > 0) {
return {
name: t('polls', 'We are sorry, but there are no more vote options available'),
description: t('polls', 'All options are booked up.'),
}
}

return {
name: t('polls', 'No vote options available'),
description: this.permissions.edit ? '' : t('polls', 'Maybe the owner did not provide some until now.'),
Expand Down

0 comments on commit ddb4ac4

Please sign in to comment.