Skip to content
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
10 changes: 4 additions & 6 deletions lib/Command/Share/Add.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@
use OCA\Polls\AppInfo\Application;
use OCA\Polls\Db\Poll;
use OCA\Polls\Exceptions\ShareAlreadyExistsException;
use OCA\Polls\Model\Group\Group;
use OCA\Polls\Model\User\Email;
use OCA\Polls\Model\User\User;
use OCA\Polls\Model\UserBase;
use OCP\AppFramework\Db\DoesNotExistException;
use Stecman\Component\Symfony\Console\BashCompletion\CompletionContext;
use Symfony\Component\Console\Input\InputArgument;
Expand Down Expand Up @@ -86,7 +84,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
private function inviteUsers(Poll $poll, array $userIds): void {
foreach ($userIds as $userId) {
try {
$share = $this->shareService->add($poll->getId(), User::TYPE, $userId);
$share = $this->shareService->add($poll->getId(), UserBase::TYPE_USER, $userId);
$this->shareService->sendInvitation($share);
} catch (ShareAlreadyExistsException $e) {
// silently ignore already existing shares
Expand All @@ -102,7 +100,7 @@ private function inviteUsers(Poll $poll, array $userIds): void {
private function inviteGroups(Poll $poll, array $groupIds): void {
foreach ($groupIds as $groupId) {
try {
$share = $this->shareService->add($poll->getId(), Group::TYPE, $groupId);
$share = $this->shareService->add($poll->getId(), UserBase::TYPE_GROUP, $groupId);
$this->shareService->sendInvitation($share);
} catch (ShareAlreadyExistsException $e) {
// silently ignore already existing shares
Expand All @@ -118,7 +116,7 @@ private function inviteGroups(Poll $poll, array $groupIds): void {
private function inviteEmails(Poll $poll, array $emails): void {
foreach ($emails as $email) {
try {
$share = $this->shareService->add($poll->getId(), Email::TYPE, $email);
$share = $this->shareService->add($poll->getId(), UserBase::TYPE_EMAIL, $email);
$this->shareService->sendInvitation($share);
} catch (ShareAlreadyExistsException $e) {
// silently ignore already existing shares
Expand Down
14 changes: 5 additions & 9 deletions lib/Command/Share/Remove.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,7 @@
use OCA\Polls\AppInfo\Application;
use OCA\Polls\Db\Poll;
use OCA\Polls\Db\Share;
use OCA\Polls\Model\Group\Group;
use OCA\Polls\Model\User\Contact;
use OCA\Polls\Model\User\Email;
use OCA\Polls\Model\User\GenericUser;
use OCA\Polls\Model\User\User;
use OCA\Polls\Model\UserBase;
use OCP\AppFramework\Db\DoesNotExistException;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
Expand Down Expand Up @@ -126,7 +122,7 @@ private function removeEmails(Poll $poll, array $emails): void {
private function getUserShares(Poll $poll): array {
$shares = $this->shareMapper->findByPoll($poll->getId());
return array_values(array_filter($shares, static function (Share $share): bool {
return ($share->getType() === User::TYPE);
return ($share->getType() === UserBase::TYPE_USER);
}));
}

Expand All @@ -138,7 +134,7 @@ private function getUserShares(Poll $poll): array {
private function getGroupShares(Poll $poll): array {
$shares = $this->shareMapper->findByPoll($poll->getId());
return array_values(array_filter($shares, static function (Share $share): bool {
return ($share->getType() === Group::TYPE);
return ($share->getType() === UserBase::TYPE_GROUP);
}));
}

Expand All @@ -150,11 +146,11 @@ private function getGroupShares(Poll $poll): array {
private function getEmailShares(Poll $poll): array {
$shares = $this->shareMapper->findByPoll($poll->getId());
return array_values(array_filter($shares, static function (Share $share): bool {
if (($share->getType() === GenericUser::TYPE) && $share->getEmailAddress()) {
if (($share->getType() === UserBase::TYPE_GENERIC) && $share->getEmailAddress()) {
return true;
}

return (($share->getType() === Email::TYPE) || ($share->getType() === Contact::TYPE));
return (($share->getType() === UserBase::TYPE_EMAIL) || ($share->getType() === UserBase::TYPE_CONTACT));
}));
}
}
15 changes: 15 additions & 0 deletions lib/Controller/PublicController.php
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,21 @@ public function validatePublicDisplayName(string $displayName, string $token): J
]);
}

/**
* Validate it the user name is reserved
* return false, if this username already exists as a user or as a participant of the poll
* @param string $token Share token
*/
#[PublicPage]
#[ShareTokenRequired]
#[OpenAPI(OpenAPI::SCOPE_IGNORE)]
#[FrontpageRoute(verb: 'POST', url: '/randomname')]
public function getRandomUserName(string $token): JSONResponse {
return $this->response(fn () => [
'name' => $this->systemService->getRandomDisplayName($token)
]);
}

/**
* Validate email address (simple validation)
* @param string $emailAddress Email address string to check for validation
Expand Down
2 changes: 1 addition & 1 deletion lib/Db/Comment.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public function getRecipientUser() : ?UserBase {
try {
/* @var UserMapper $userMapper */
$userMapper = (Container::queryClass(UserMapper::class));
$user = $userMapper->getParticipant($this->getRecipient(), $this->getPollId());
$user = $userMapper->getUser($this->getRecipient(), $this->getPollId());
} catch (Exception $e) {
return null;
}
Expand Down
23 changes: 11 additions & 12 deletions lib/Db/EntityWithUser.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,30 +91,29 @@ protected function getEntityAnonymization(): bool {
return false;
}

/**
* get the pollId, if relevant for user identification, otherwise return null
* Overwrite if user identificaton needs another pollId than the one of the entity
*/
public function getUserRelevantPollId(): ?int {
return $this->getPollId();
}

/**
* @return UserBase Gets owner of the entity
* @return UserBase Gets user of the entity
*/
public function getUser(): UserBase {
if ($this->getEntityAnonymization()) {
$user = new Anon($this->getUserId());
return $user;
}

/** @var UserMapper */
$userMapper = (Container::queryClass(UserMapper::class));
$userMapper = Container::queryClass(UserMapper::class);

try {
$pollId = $this->getPollId();
if ($pollId === null) {
// If pollId is not set, we assume that the user is not a participant of a poll
return $userMapper->getUserFromUserBase($this->getUserId());
}
$user = $userMapper->getParticipant($this->getUserId(), $pollId);
// Get user from userbase
return $userMapper->getUser($this->getUserId(), $this->getUserRelevantPollId());
} catch (Exception) {
$user = new Ghost($this->getUserId());
return new Ghost($this->getUserId());
}
return $user;
}
}
7 changes: 7 additions & 0 deletions lib/Db/Poll.php
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,13 @@ public function getUserId(): string {
return $this->getOwner();
}

/**
* Return null for user identification for the poll user to support caching
*/
public function getUserRelevantPollId(): ?int {
return null;
}

// alias of setOwner($value)
public function setUserId(string $userId): void {
$this->setOwner($userId);
Expand Down
59 changes: 38 additions & 21 deletions lib/Db/Share.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
* @method void setLocked(int $value)
* @method string getDisplayName()
* @method void setDisplayName(string $value)
* @method string getLabel()
* @method void setLabel(string $value)
* @method string getMiscSettings()
* @method void setMiscSettings(string $value)
* @method ?int getAnonymizedVotes()
Expand All @@ -63,17 +65,17 @@ class Share extends EntityWithUser implements JsonSerializable {
public const EMAIL_DISABLED = 'disabled';

// Only authenticated access
public const TYPE_USER = 'user';
public const TYPE_ADMIN = 'admin';
public const TYPE_GROUP = 'group';
public const TYPE_USER = UserBase::TYPE_USER;
public const TYPE_ADMIN = UserBase::TYPE_ADMIN;
public const TYPE_GROUP = UserBase::TYPE_GROUP;

// Public and authenticated Access
public const TYPE_PUBLIC = 'public';
public const TYPE_PUBLIC = UserBase::TYPE_PUBLIC;

// Only public access
public const TYPE_EMAIL = 'email';
public const TYPE_CONTACT = 'contact';
public const TYPE_EXTERNAL = 'external';
public const TYPE_EMAIL = UserBase::TYPE_EMAIL;
public const TYPE_CONTACT = UserBase::TYPE_CONTACT;
public const TYPE_EXTERNAL = UserBase::TYPE_EXTERNAL;

// no direct Access
public const TYPE_TEAM = 'circle';
Expand Down Expand Up @@ -178,7 +180,7 @@ public function jsonSerialize(): array {
}

public function resolveUser(): Ghost|Group|Team|Contact|ContactGroup|User|Email|GenericUser {
return UserMapper::getUserObject(
return UserMapper::createUserObject(
$this->getType(),
$this->getUserId(),
$this->getDisplayName(),
Expand Down Expand Up @@ -242,35 +244,50 @@ public function setPublicPollEmail(string $value): void {
$this->setMiscSettingsByKey('publicPollEmail', $value);
}

/**
* Share label for public shares, now stored as displayName
* TODO: remove after migration was introduced
*/
public function setLabel(string $label): void {
$this->setDisplayName($label);
$this->label = $label;
}

/**
* Share label for public shares
* TODO: remove after migrating labels to displayName
*/
public function getLabel(): string {
// In case of public poll use label as fallback for displayName
return $this->displayName ?? $this->label ?? '';
return $this->label ?? $this->displayName ?? '';
}

/**
* Sharee's displayName. In case of public poll label is used instead
* TODO: remove public poll check after migration labels to displayName
*/
public function getDisplayName(): string {
if ($this->getType() === self::TYPE_PUBLIC) {
return $this->getLabel();
}
return $this->displayName ?? '';
}

public function setFromUserObject(UserBase $userGroup, string $token, string $purpose = 'poll'): void {
// Contacts are converted: with email → email share, without email → external share
$type = $userGroup->getType();
if ($type === self::TYPE_CONTACT || $type === self::TYPE_EMAIL) {
$type = self::TYPE_EXTERNAL;
}

$this->setType($type);
$this->setDisplayName($userGroup->getDisplayName());
$this->setEmailAddress($userGroup->getEmailAddress());

// ignore if share is for poll Groups
if ($purpose === 'poll') {
if ($type === self::TYPE_PUBLIC) {
// public share to create, set token as userId
$this->setUserId($token);
} else {
$this->setUserId($userGroup->getShareUserId());
}
if ($type === self::TYPE_EXTERNAL) {
$this->setLanguage($userGroup->getLanguageCode());
$this->setTimeZoneName($userGroup->getTimeZoneName());
}
}
}


public function getTimeZoneName(): string {
return $this->getMiscSettingsArray()['timeZone'] ?? '';
}
Expand Down
Loading
Loading