* tidy
* add userService as proxy layer
* fetch displayname from share in mapper
This commit is contained in:
René Gieling 2023-12-04 12:11:11 +01:00 коммит произвёл GitHub
Родитель 11b2f00ddb
Коммит 8de7c93b1c
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
13 изменённых файлов: 422 добавлений и 225 удалений

1
.gitignore поставляемый
Просмотреть файл

@ -5,6 +5,7 @@
.psalm.cache
.project/
.idea/
.vscode/
build/
css/*.map
js/*

Просмотреть файл

@ -37,7 +37,7 @@ class CommentMapper extends QBMapper {
public const TABLE = Comment::TABLE;
public function __construct(IDBConnection $db) {
parent::__construct($db, Comment::TABLE, Comment::class);
parent::__construct($db, self::TABLE, Comment::class);
}
/**
@ -46,14 +46,8 @@ class CommentMapper extends QBMapper {
* @return Comment
*/
public function find(int $id): Comment {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->where(
$qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT))
);
$qb = $this->buildQuery();
$qb->where($qb->expr()->eq(self::TABLE . '.id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
return $this->findEntity($qb);
}
@ -62,14 +56,8 @@ class CommentMapper extends QBMapper {
* @return Comment[]
*/
public function findByPoll(int $pollId): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->where(
$qb->expr()->eq('poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT))
);
$qb = $this->buildQuery();
$qb->where($qb->expr()->eq(self::TABLE . '.poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)));
return $this->findEntities($qb);
}
@ -78,12 +66,8 @@ class CommentMapper extends QBMapper {
*/
public function deleteByPoll(int $pollId): void {
$qb = $this->db->getQueryBuilder();
$qb->delete($this->getTableName())
->where(
$qb->expr()->eq('poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT))
);
->where($qb->expr()->eq('poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)));
$qb->executeStatement();
}
@ -93,7 +77,7 @@ class CommentMapper extends QBMapper {
public function deleteComment(int $id): void {
$qb = $this->db->getQueryBuilder();
$qb->delete($this->getTableName())
$qb->delete($this->getTableName(), self::TABLE)
->where(
$qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT))
);
@ -106,9 +90,41 @@ class CommentMapper extends QBMapper {
*/
public function renameUserId(string $userId, string $replacementName): void {
$query = $this->db->getQueryBuilder();
$query->update($this->getTableName())
$query->update($this->getTableName(), self::TABLE)
->set('user_id', $query->createNamedParameter($replacementName))
->where($query->expr()->eq('user_id', $query->createNamedParameter($userId)))
->executeStatement();
}
/**
* Build the enhanced query with joined tables
*/
protected function buildQuery(): IQueryBuilder {
$qb = $this->db->getQueryBuilder();
$qb->select(self::TABLE . '.*')
->from($this->getTableName(), self::TABLE)
->groupby(self::TABLE . '.id');
$this->joinDisplayNameFromShare($qb, self::TABLE);
return $qb;
}
/**
* Joins shares to fetch displayName from shares
*/
protected function joinDisplayNameFromShare(IQueryBuilder &$qb, string $fromAlias): void {
$joinAlias = 'shares';
// force value into a MIN function to avoid grouping errors
$qb->selectAlias($qb->func()->min($joinAlias . '.display_name'), 'display_name');
$qb->leftJoin(
$fromAlias,
Share::TABLE,
$joinAlias,
$qb->expr()->andX(
$qb->expr()->eq($fromAlias . '.poll_id', $joinAlias . '.poll_id'),
$qb->expr()->eq($fromAlias . '.user_id', $joinAlias . '.user_id'),
)
);
}
}

Просмотреть файл

@ -25,40 +25,35 @@ declare(strict_types=1);
namespace OCA\Polls\Db;
use OCA\Polls\Exceptions\ShareNotFoundException;
use OCA\Polls\Helper\Container;
use OCP\AppFramework\Db\Entity;
use OCP\IUser;
use OCP\IUserManager;
/**
* @method string getUserId()
* @method int getPollId()
* @method string getUserId()
* @method string getDisplayName()
*/
abstract class EntityWithUser extends Entity {
protected string $publicUserId = '';
protected ?string $displayName = '';
public function getIsNoUser(): bool {
return !(Container::queryClass(IUserManager::class)->get($this->getUserId()) instanceof IUser);
}
public function getDisplayName(): string {
if (!$this->getUserId()) {
return '';
}
if ($this->getIsNoUser()) {
// get displayName from share
try {
$share = Container::queryClass(ShareMapper::class)->findByPollAndUser($this->getPollId(), $this->getUserId());
} catch (ShareNotFoundException $e) {
// User seems to be probaly deleted, use fake share
$share = Container::queryClass(ShareMapper::class)->getReplacement($this->getPollId(), $this->getUserId());
}
return $share->getDisplayName();
public function getDisplayName(): ?string {
if ($this->displayName) {
return $this->displayName;
}
return Container::queryClass(IUserManager::class)->get($this->getUserId())->getDisplayName();
// if (!$this->getUserId()) {
// return 'No UserId';
// }
return Container::queryClass(IUserManager::class)->get($this->getUserId())?->getDisplayName() ?? 'Deleted User';
}
private function getPublicUserId(): string {

Просмотреть файл

@ -83,98 +83,6 @@ class OptionMapper extends QBMapper {
return $this->findEntities($qb);
}
/**
* Build the enhanced query with joined tables
* @param bool $hideResults Whether the results should be hidden, skips vote counting
*/
private function buildQuery(bool $hideVotes = false): IQueryBuilder {
$currentUserId = $this->userMapper->getCurrentUserId();
$qb = $this->db->getQueryBuilder();
$qb->select('options.*')
->from($this->getTableName(), 'options')
->groupBy('options.id')
->orderBy('order', 'ASC');
// +++++++++
// The joins
// +++++++++
// left join this options' votes
// to count votes per option and answer
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(
'options',
Vote::TABLE,
'votes',
$qb->expr()->andX(
$qb->expr()->eq('options.poll_id', 'votes.poll_id'),
$qb->expr()->eq('options.poll_option_text', 'votes.vote_option_text'),
)
)
// Count number of votes for this option
->addSelect($qb->createFunction('COUNT(DISTINCT(votes.id)) AS count_option_votes'))
// Count number of yes votes for this option
->addSelect($qb->createFunction('COUNT(DISTINCT(CASE WHEN votes.vote_answer = \'yes\' THEN votes.id END)) AS votes_yes'))
// Count number of no votes for this option
->addSelect($qb->createFunction('COUNT(DISTINCT(CASE WHEN votes.vote_answer = \'no\' THEN votes.id END)) AS votes_no'))
// Count number of maybe votes for this option
->addSelect($qb->createFunction('COUNT(DISTINCT(CASE WHEN votes.vote_answer = \'maybe\' THEN votes.id END)) AS votes_maybe'));
}
// left join poll
// to fetch option_limit and vote_limit
$qb->leftJoin(
'options',
Poll::TABLE,
'polls',
$qb->expr()->eq('options.poll_id', 'polls.id'),
)
// force value into a MIN function to avoid grouping errors
->selectAlias($qb->func()->min('polls.option_limit'), 'option_limit')
->selectAlias($qb->func()->min('polls.vote_limit'), 'vote_limit')
// Votes table join (#1)
// left join options' votes of the current user this option to get the current user's answer to this option
->leftJoin(
'options',
Vote::TABLE,
'option_vote_user',
$qb->expr()->andX(
$qb->expr()->eq('option_vote_user.poll_id', 'options.poll_id'),
$qb->expr()->eq('option_vote_user.user_id', $qb->createNamedParameter($currentUserId, IQueryBuilder::PARAM_STR)),
$qb->expr()->eq('option_vote_user.vote_option_text', 'options.poll_option_text'),
)
)
// force value into a MIN function to avoid grouping errors
->selectAlias($qb->func()->min('option_vote_user.vote_answer'), 'user_vote_answer')
// Votes table join (#2)
// left join votes of user to be able to check against polls_polls.vote_limit
// in other words: returns all votes of current user and count them
->leftJoin(
'options',
Vote::TABLE,
'votes_user',
$qb->expr()->andX(
$qb->expr()->eq('votes_user.poll_id', 'options.poll_id'),
$qb->expr()->eq('votes_user.user_id', $qb->createNamedParameter($currentUserId, IQueryBuilder::PARAM_STR)),
$qb->expr()->eq('votes_user.vote_answer', $qb->createNamedParameter(Vote::VOTE_YES, IQueryBuilder::PARAM_STR)),
)
)
// Count yes votes of the user in this poll
->addSelect($qb->createFunction('COUNT(DISTINCT(votes_user.id)) AS user_count_yes_votes'));
return $qb;
}
/**
* @return Option[]
* @param bool $hideResults Whether the results should be hidden
@ -182,7 +90,7 @@ class OptionMapper extends QBMapper {
*/
public function findByPoll(int $pollId, bool $hideResults = false): array {
$qb = $this->buildQuery($hideResults);
$qb->where($qb->expr()->eq('options.poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)));
$qb->where($qb->expr()->eq(self::TABLE . '.poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)));
return $this->findEntities($qb);
}
@ -192,7 +100,7 @@ class OptionMapper extends QBMapper {
*/
public function find(int $id): Option {
$qb = $this->buildQuery();
$qb->where($qb->expr()->eq('options.id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
$qb->where($qb->expr()->eq(self::TABLE . '.id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
return $this->findEntity($qb);
}
@ -203,8 +111,8 @@ class OptionMapper extends QBMapper {
*/
public function findConfirmed(int $pollId): array {
$qb = $this->buildQuery();
$qb->where($qb->expr()->eq('options.poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->gt('options.confirmed', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
$qb->where($qb->expr()->eq(self::TABLE . '.poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->gt(self::TABLE . '.confirmed', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
return $this->findEntities($qb);
}
@ -261,4 +169,139 @@ class OptionMapper extends QBMapper {
->where($query->expr()->eq('owner', $query->createNamedParameter($userId)))
->executeStatement();
}
/**
* Build the enhanced query with joined tables
* @param bool $hideResults Whether the results should be hidden, skips vote counting
*/
protected function buildQuery(bool $hideVotes = false): IQueryBuilder {
$currentUserId = $this->userMapper->getCurrentUserId();
$qb = $this->db->getQueryBuilder();
$qb->select(self::TABLE . '.*')
->from($this->getTableName(), self::TABLE)
->groupBy(self::TABLE . '.id')
->orderBy('order', 'ASC');
$this->joinVotesCount($qb, self::TABLE, $hideVotes);
$this->joinPollForLimits($qb, self::TABLE);
$this->joinCurrentUserVote($qb, self::TABLE, $currentUserId);
$this->joinCurrentUserVoteCount($qb, self::TABLE, $currentUserId);
$this->joinDisplayNameFromShare($qb, self::TABLE);
return $qb;
}
/**
* Joins shares to fetch displayName from shares
*
* @psalm-param 'polls_options' $fromAlias
*/
protected function joinDisplayNameFromShare(IQueryBuilder &$qb, string $fromAlias): void {
$joinAlias = 'shares';
// force value into a MIN function to avoid grouping errors
$qb->selectAlias($qb->func()->min($joinAlias . '.display_name'), 'display_name');
$qb->leftJoin(
$fromAlias,
Share::TABLE,
$joinAlias,
$qb->expr()->andX(
$qb->expr()->eq($fromAlias . '.poll_id', $joinAlias . '.poll_id'),
$qb->expr()->eq($fromAlias . '.owner', $joinAlias . '.user_id'),
)
);
}
/**
* Joins votes to count votes per option and answer
*/
protected function joinVotesCount(IQueryBuilder &$qb, string $fromAlias, bool $hideVotes = 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'),
)
)
// 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'));
}
}
/**
* Joins poll to fetch option_limit and vote_limit
*/
protected function joinPollForLimits(IQueryBuilder &$qb, string $fromAlias): void {
$joinAlias = 'polls';
// force value into a MIN function to avoid grouping errors
$qb->selectAlias($qb->func()->min($joinAlias . '.option_limit'), 'option_limit')
->selectAlias($qb->func()->min($joinAlias . '.vote_limit'), 'vote_limit');
$qb->leftJoin(
$fromAlias,
Poll::TABLE,
$joinAlias,
$qb->expr()->eq($joinAlias . '.id', $fromAlias . '.poll_id'),
);
}
/**
* Joins votes to get the current user's answer to this option
*/
protected function joinCurrentUserVote(IQueryBuilder &$qb, string $fromAlias, string $currentUserId): void {
$joinAlias = 'user_vote';
// force value into a MIN function to avoid grouping errors
$qb->selectAlias($qb->func()->min($joinAlias . '.vote_answer'), 'user_vote_answer');
$qb->leftJoin(
$fromAlias,
Vote::TABLE,
$joinAlias,
$qb->expr()->andX(
$qb->expr()->eq($joinAlias . '.poll_id', $fromAlias . '.poll_id'),
$qb->expr()->eq($joinAlias . '.user_id', $qb->createNamedParameter($currentUserId, IQueryBuilder::PARAM_STR)),
$qb->expr()->eq($joinAlias . '.vote_option_text', $fromAlias . '.poll_option_text'),
)
);
}
/**
* Joins votes to be able to check against polls_polls.vote_limit of the current user
* in other words: returns all votes of current user and count them
*/
protected function joinCurrentUserVoteCount(IQueryBuilder &$qb, string $fromAlias, string $currentUserId): void {
$joinAlias = 'votes_user';
// Count yes votes of the user in this poll
$qb->addSelect($qb->createFunction('COUNT(DISTINCT(votes_user.id)) AS user_count_yes_votes'));
$qb->leftJoin(
$fromAlias,
Vote::TABLE,
$joinAlias,
$qb->expr()->andX(
$qb->expr()->eq($joinAlias . '.poll_id', $fromAlias . '.poll_id'),
$qb->expr()->eq($joinAlias . '.user_id', $qb->createNamedParameter($currentUserId, IQueryBuilder::PARAM_STR)),
$qb->expr()->eq($joinAlias . '.vote_answer', $qb->createNamedParameter(Vote::VOTE_YES, IQueryBuilder::PARAM_STR)),
)
);
}
}

Просмотреть файл

@ -47,12 +47,8 @@ class PollMapper extends QBMapper {
* @return Poll
*/
public function find(int $id): Poll {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->where(
$qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT))
);
$qb = $this->buildQuery();
$qb->where($qb->expr()->eq(self::TABLE . '.id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
return $this->findEntity($qb);
}
@ -62,13 +58,11 @@ class PollMapper extends QBMapper {
*/
public function findAutoReminderPolls(): array {
$autoReminderSearchString = '%"autoReminder":true%';
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->where($qb->expr()->like(
'misc_settings',
$qb->createNamedParameter($autoReminderSearchString, IQueryBuilder::PARAM_STR)
));
$qb = $this->buildQuery();
$qb->where($qb->expr()->like(
self::TABLE . '.misc_settings',
$qb->createNamedParameter($autoReminderSearchString, IQueryBuilder::PARAM_STR)
));
return $this->findEntities($qb);
}
@ -77,11 +71,9 @@ class PollMapper extends QBMapper {
* @return Poll[]
*/
public function findForMe(string $userId): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->where($qb->expr()->eq('deleted', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)))
->orWhere($qb->expr()->eq('owner', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
$qb = $this->buildQuery();
$qb->where($qb->expr()->eq(self::TABLE . '.deleted', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)))
->orWhere($qb->expr()->eq(self::TABLE . '.owner', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
return $this->findEntities($qb);
}
@ -90,10 +82,8 @@ class PollMapper extends QBMapper {
* @return Poll[]
*/
public function findOwner(string $userId): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->where($qb->expr()->eq('owner', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
$qb = $this->buildQuery();
$qb->where($qb->expr()->eq(self::TABLE . '.owner', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
return $this->findEntities($qb);
}
@ -102,20 +92,18 @@ class PollMapper extends QBMapper {
* @return Poll[]
*/
public function search(ISearchQuery $query): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->where($qb->expr()->eq('deleted', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)))
$qb = $this->buildQuery();
$qb->where($qb->expr()->eq(self::TABLE . '..deleted', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->orX(
...array_map(function (string $token) use ($qb) {
return $qb->expr()->orX(
$qb->expr()->iLike(
'title',
self::TABLE . '.title',
$qb->createNamedParameter('%' . $this->db->escapeLikeParameter($token) . '%', IQueryBuilder::PARAM_STR),
IQueryBuilder::PARAM_STR
),
$qb->expr()->iLike(
'description',
self::TABLE . '.description',
$qb->createNamedParameter('%' . $this->db->escapeLikeParameter($token) . '%', IQueryBuilder::PARAM_STR),
IQueryBuilder::PARAM_STR
)
@ -130,12 +118,8 @@ class PollMapper extends QBMapper {
* @return Poll[]
*/
public function findForAdmin(string $userId): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->where(
$qb->expr()->neq('owner', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR))
);
$qb = $this->buildQuery();
$qb->where($qb->expr()->neq(self::TABLE . '.owner', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
return $this->findEntities($qb);
}
@ -175,4 +159,38 @@ class PollMapper extends QBMapper {
->setParameter('userId', $userId);
$qb->executeStatement();
}
/**
* Build the enhanced query with joined tables
* @param bool $hideResults Whether the results should be hidden, skips vote counting
*/
protected function buildQuery(): IQueryBuilder {
$qb = $this->db->getQueryBuilder();
$qb->select(self::TABLE . '.*')
->from($this->getTableName(), self::TABLE)
->groupby(self::TABLE . '.id');
$this->joinDisplayNameFromShare($qb, self::TABLE);
return $qb;
}
/**
* Joins shares to fetch displayName from shares
*/
protected function joinDisplayNameFromShare(IQueryBuilder & $qb, string $fromAlias): void {
$joinAlias = 'shares';
// force value into a MIN function to avoid grouping errors
$qb->selectAlias($qb->func()->min($joinAlias . '.display_name'), 'display_name');
$qb->leftJoin(
$fromAlias,
Share::TABLE,
$joinAlias,
$qb->expr()->andX(
$qb->expr()->eq($fromAlias . '.id', $joinAlias . '.poll_id'),
$qb->expr()->eq($fromAlias . '.owner', $joinAlias . '.user_id'),
)
);
}
}

Просмотреть файл

@ -133,7 +133,7 @@ class Share extends Entity implements JsonSerializable {
public function __construct() {
$this->addType('pollId', 'int');
$this->addType('invitationSent', 'int');
$this->addType('Locked', 'int');
$this->addType('locked', 'int');
$this->addType('reminderSent', 'int');
$this->urlGenerator = Container::queryClass(IURLGenerator::class);
$this->appSettings = new AppSettings;

Просмотреть файл

@ -84,7 +84,7 @@ class TableManager {
$messages[] = 'Dropped ' . $this->dbPrefix . $tableName;
}
}
// drop parent table
if ($this->connection->tableExists(TableSchema::FK_PARENT_TABLE)) {
$this->connection->dropTable(TableSchema::FK_PARENT_TABLE);
@ -115,8 +115,6 @@ class TableManager {
$messages[] = '';
$messages[] = 'Please call \'occ app:remove polls\' now!';
// $this->refreshSchema();
return $messages;
}

Просмотреть файл

@ -53,8 +53,8 @@ class Vote extends EntityWithUser implements JsonSerializable {
public $id = null;
protected int $pollId = 0;
protected string $userId = '';
protected int $voteOptionId = 0;
protected string $userId = '';
protected string $voteOptionText = '';
protected string $voteOptionHash = '';
protected string $voteAnswer = '';

Просмотреть файл

@ -38,7 +38,7 @@ class VoteMapper extends QBMapper {
public const TABLE = Vote::TABLE;
public function __construct(IDBConnection $db) {
parent::__construct($db, Vote::TABLE, Vote::class);
parent::__construct($db, self::TABLE, Vote::class);
}
public function update(Entity $entity): Entity {
@ -57,24 +57,20 @@ class VoteMapper extends QBMapper {
* @psalm-return array<array-key, Vote>
*/
public function getAll(): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')->from($this->getTableName());
$qb = $this->buildQuery();
return $this->findEntities($qb);
}
/**
* @throws \OCP\AppFramework\Db\DoesNotExistException if not found
* @return Vote[]
* @psalm-return array<array-key, Vote>
*/
public function findByPoll(int $pollId): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->where($qb->expr()->eq('poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)));
$qb = $this->buildQuery();
$qb->where($qb->expr()->eq(self::TABLE . '.poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)));
return $this->findEntities($qb);
}
@ -84,12 +80,10 @@ class VoteMapper extends QBMapper {
* @psalm-return array<array-key, Vote>
*/
public function findByPollAndUser(int $pollId, string $userId): array {
$qb = $this->db->getQueryBuilder();
$qb = $this->buildQuery();
$qb->select('*')
->from($this->getTableName())
->where($qb->expr()->eq('poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq('user_id', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
$qb->where($qb->expr()->eq(self::TABLE . '.poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq(self::TABLE . '.user_id', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
return $this->findEntities($qb);
}
@ -97,13 +91,11 @@ class VoteMapper extends QBMapper {
* @throws \OCP\AppFramework\Db\DoesNotExistException if not found
*/
public function findSingleVote(int $pollId, string $optionText, string $userId): Vote {
$qb = $this->db->getQueryBuilder();
$qb = $this->buildQuery();
$qb->select('*')
->from($this->getTableName())
->where($qb->expr()->eq('poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq('vote_option_text', $qb->createNamedParameter($optionText, IQueryBuilder::PARAM_STR)))
->andWhere($qb->expr()->eq('user_id', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
$qb->where($qb->expr()->eq(self::TABLE . '.poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq(self::TABLE . '.vote_option_text', $qb->createNamedParameter($optionText, IQueryBuilder::PARAM_STR)))
->andWhere($qb->expr()->eq(self::TABLE . '.user_id', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
return $this->findEntity($qb);
}
@ -115,26 +107,27 @@ class VoteMapper extends QBMapper {
public function findParticipantsByPoll(int $pollId): array {
$qb = $this->db->getQueryBuilder();
$qb->selectDistinct(['user_id', 'poll_id'])
->from($this->getTableName())
$qb->selectDistinct([self::TABLE . '.user_id', self::TABLE . '.poll_id'])
->from($this->getTableName(), self::TABLE)
->where(
$qb->expr()->eq('poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT))
$qb->expr()->eq(self::TABLE . '.poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT))
);
$qb->addGroupBy(self::TABLE . '.user_id', self::TABLE . '.poll_id');
$this->joinDisplayNameFromShare($qb, self::TABLE);
return $this->findEntities($qb);
}
/**
* @throws \OCP\AppFramework\Db\DoesNotExistException if not found
* @return Vote[]
* @psalm-return array<array-key, Vote>
*/
public function findParticipantsVotes(int $pollId, string $userId): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->where($qb->expr()->eq('poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq('user_id', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
$qb = $this->buildQuery();
$qb->where($qb->expr()->eq(self::TABLE . '.poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq(self::TABLE . '.user_id', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
return $this->findEntities($qb);
}
@ -152,12 +145,10 @@ class VoteMapper extends QBMapper {
* @psalm-return array<array-key, Vote>
*/
public function getYesVotesByParticipant(int $pollId, string $userId): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->where($qb->expr()->eq('poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq('user_id', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)))
->andWhere($qb->expr()->eq('vote_answer', $qb->createNamedParameter(Vote::VOTE_YES, IQueryBuilder::PARAM_STR)));
$qb = $this->buildQuery();
$qb->where($qb->expr()->eq(self::TABLE . '.poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq(self::TABLE . '.user_id', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)))
->andWhere($qb->expr()->eq(self::TABLE . '.vote_answer', $qb->createNamedParameter(Vote::VOTE_YES, IQueryBuilder::PARAM_STR)));
return $this->findEntities($qb);
}
@ -167,13 +158,11 @@ class VoteMapper extends QBMapper {
* @psalm-return array<array-key, Vote>
*/
public function getYesVotesByOption(int $pollId, string $pollOptionText): array {
$qb = $this->db->getQueryBuilder();
$qb = $this->buildQuery();
$qb->select('*')
->from($this->getTableName())
->where($qb->expr()->eq('poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq('vote_option_text', $qb->createNamedParameter($pollOptionText, IQueryBuilder::PARAM_STR)))
->andWhere($qb->expr()->eq('vote_answer', $qb->createNamedParameter(Vote::VOTE_YES, IQueryBuilder::PARAM_STR)));
$qb->where($qb->expr()->eq(self::TABLE . '.poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq(self::TABLE . '.vote_option_text', $qb->createNamedParameter($pollOptionText, IQueryBuilder::PARAM_STR)))
->andWhere($qb->expr()->eq(self::TABLE . '.vote_answer', $qb->createNamedParameter(Vote::VOTE_YES, IQueryBuilder::PARAM_STR)));
return $this->findEntities($qb);
}
@ -194,4 +183,38 @@ class VoteMapper extends QBMapper {
->andWhere($query->expr()->eq('vote_option_id', $query->createNamedParameter($optionId)))
->executeStatement();
}
/**
* Build the enhanced query with joined tables
*/
protected function buildQuery(): IQueryBuilder {
$qb = $this->db->getQueryBuilder();
$qb->select(self::TABLE . '.*')
->from($this->getTableName(), self::TABLE)
->groupby(self::TABLE . '.id');
$this->joinDisplayNameFromShare($qb, self::TABLE);
return $qb;
}
/**
* Joins shares to fetch displayName from shares
*/
protected function joinDisplayNameFromShare(IQueryBuilder &$qb, string $fromAlias): void {
$joinAlias = 'shares';
// force value into a MIN function to avoid grouping errors
$qb->selectAlias($qb->func()->min($joinAlias . '.display_name'), 'display_name');
$qb->leftJoin(
$fromAlias,
Share::TABLE,
$joinAlias,
$qb->expr()->andX(
$qb->expr()->eq(self::TABLE . '.poll_id', $joinAlias . '.poll_id'),
$qb->expr()->eq(self::TABLE . '.user_id', $joinAlias . '.user_id'),
)
);
}
}

Просмотреть файл

@ -27,9 +27,9 @@ use OCA\Polls\Db\Comment;
use OCA\Polls\Db\Option;
use OCA\Polls\Db\Poll;
use OCA\Polls\Db\Share;
use OCA\Polls\Db\UserMapper;
use OCA\Polls\Db\Vote;
use OCA\Polls\Helper\Container;
use OCA\Polls\Service\UserService;
use OCP\EventDispatcher\Event;
use OCP\IUserSession;
@ -40,14 +40,14 @@ abstract class BaseEvent extends Event {
protected bool $log = true;
protected Poll $poll;
protected IUserSession $userSession;
protected UserMapper $userMapper;
protected UserService $userService;
public function __construct(
protected Poll|Comment|Share|Option|Vote $eventObject,
) {
parent::__construct();
$this->poll = Container::queryPoll($this->getPollId());
$this->userMapper = Container::queryClass(UserMapper::class);
$this->userService = Container::queryClass(UserService::class);
$this->userSession = Container::queryClass(IUserSession::class);
$this->activitySubjectParams['pollTitle'] = [

Просмотреть файл

@ -50,6 +50,6 @@ abstract class ShareEvent extends BaseEvent {
}
protected function getSharee() : UserBase {
return $this->userMapper->getUserFromShare($this->share);
return $this->userService->getUserFromShare($this->share);
}
}

Просмотреть файл

@ -33,11 +33,11 @@ use League\CommonMark\MarkdownConverter;
use OCA\Polls\AppConstants;
use OCA\Polls\Db\OptionMapper;
use OCA\Polls\Db\Poll;
use OCA\Polls\Db\UserMapper;
use OCA\Polls\Exceptions\InvalidEmailAddress;
use OCA\Polls\Helper\Container;
use OCA\Polls\Model\Settings\AppSettings;
use OCA\Polls\Model\UserBase;
use OCA\Polls\Service\UserService;
use OCP\IL10N;
use OCP\L10N\IFactory;
use OCP\Mail\IEMailTemplate;
@ -59,7 +59,7 @@ abstract class MailBase {
protected Poll $poll;
protected UserBase $recipient;
protected IFactory $transFactory;
protected UserMapper $userMapper;
protected UserService $userService;
public function __construct(
protected string $recipientId,
@ -71,7 +71,7 @@ abstract class MailBase {
$this->mailer = Container::queryClass(IMailer::class);
$this->optionMapper = Container::queryClass(OptionMapper::class);
$this->transFactory = Container::queryClass(IFactory::class);
$this->userMapper = Container::queryClass(UserMapper::class);
$this->userService = Container::queryClass(UserService::class);
$this->poll = $this->getPoll($pollId);
$this->recipient = $this->getUser($recipientId);
$this->url = $url === '' ? $this->poll->getVoteUrl() : '';
@ -183,7 +183,7 @@ abstract class MailBase {
}
protected function getUser(string $userId) : UserBase {
return $this->userMapper->getParticipant($userId, $this->poll->getId());
return $this->userService->getParticipant($userId, $this->poll->getId());
}
protected function getRichDescription() : string {

103
lib/Service/UserService.php Normal file
Просмотреть файл

@ -0,0 +1,103 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2017 Vinzenz Rosenkranz <vinzenz.rosenkranz@gmail.com>
*
* @author René Gieling <github@dartcafe.de>
*
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Polls\Service;
use OCA\Polls\Db\Share;
use OCA\Polls\Db\UserMapper;
use OCA\Polls\Model\UserBase;
class UserService {
public function __construct(
private UserMapper $userMapper,
) {
}
/**
* Get current userId
*
* Returns userId of the current (share|nextcloud) user
**/
public function getCurrentUserId(): string {
return $this->userMapper->getCurrentUserId();
}
/**
* Get current user
*
* Returns a UserBase child for the current (share|nextcloud) user based on
* - the session stored share token or
* - the user session stored userId
* and stores userId to session
*
* !! The share is prioritised to tell User from Admin class
*/
public function getCurrentUser(): UserBase {
return $this->userMapper->getCurrentUser();
}
public function isLoggedIn(): bool {
return $this->userMapper->isLoggedIn();
}
/**
* Get poll participant
*
* Returns a UserBase child from share determined by userId and pollId
*
* @param string $userId Get internal user. If pollId is given, the user who participates in the particulair poll will be returned
* @param int $pollId Can only be used together with $userId and will return the internal user or the share user
* @return UserBase
**/
public function getParticipant(string $userId, int $pollId = null): UserBase {
return $this->userMapper->getParticipant($userId, $pollId);
}
/**
* Get participans of a poll as array of user objects
* @return UserBase[]
*/
public function getParticipants(int $pollId): array {
return $this->userMapper->getParticipants($pollId);
}
/**
* Get participans of a poll as array of user objects
*
* Returns a UserBase child build from a share
*/
public function getUserFromShare(Share $share): UserBase {
return $this->userMapper->getUserFromShare($share);
}
public function getUserFromUserBase(string $userId): UserBase {
return $this->userMapper->getUserFromUserBase($userId);
}
public function getUserObject(string $type, string $id, string $displayName = '', ?string $emailAddress = '', string $language = '', string $locale = '', string $timeZoneName = ''): UserBase {
return $this->userMapper->getUserObject($type, $id, $displayName, $emailAddress, $language, $locale, $timeZoneName);
}
}