Signed-off-by: dartcafe <github@dartcafe.de>
This commit is contained in:
dartcafe 2022-08-20 21:46:35 +02:00
Родитель efa6b84d3f
Коммит 75dfad8b22
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: CCE73CEF3035D3C8
39 изменённых файлов: 752 добавлений и 522 удалений

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

@ -26,9 +26,9 @@ namespace OCA\Polls\Command\Share;
use OC\Core\Command\Base;
use OCA\Polls\Db\Poll;
use OCA\Polls\Exceptions\ShareAlreadyExistsException;
use OCA\Polls\Model\UserGroup\Email;
use OCA\Polls\Model\UserGroup\Group;
use OCA\Polls\Model\UserGroup\User;
use OCA\Polls\Model\Group\Group;
use OCA\Polls\Model\User\Email;
use OCA\Polls\Model\User\User;
use OCP\AppFramework\Db\DoesNotExistException;
use Stecman\Component\Symfony\Console\BashCompletion\CompletionContext;
use Symfony\Component\Console\Input\InputArgument;

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

@ -26,11 +26,11 @@ namespace OCA\Polls\Command\Share;
use OC\Core\Command\Base;
use OCA\Polls\Db\Poll;
use OCA\Polls\Db\Share;
use OCA\Polls\Model\UserGroup\Contact;
use OCA\Polls\Model\UserGroup\Email;
use OCA\Polls\Model\UserGroup\GenericUser;
use OCA\Polls\Model\UserGroup\Group;
use OCA\Polls\Model\UserGroup\User;
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 OCP\AppFramework\Db\DoesNotExistException;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;

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

@ -23,6 +23,7 @@
namespace OCA\Polls\Controller;
use OCA\Polls\Model\Acl;
use OCA\Polls\Service\CommentService;
use OCP\IRequest;
use OCP\AppFramework\Http\JSONResponse;
@ -32,12 +33,17 @@ class CommentApiController extends BaseApiController {
/** @var CommentService */
private $commentService;
/** @var Acl */
private $acl;
public function __construct(
string $appName,
Acl $acl,
IRequest $request,
CommentService $commentService
) {
parent::__construct($appName, $request);
$this->acl = $acl;
$this->commentService = $commentService;
}
@ -48,7 +54,9 @@ class CommentApiController extends BaseApiController {
* @NoCSRFRequired
*/
public function list(int $pollId): JSONResponse {
return $this->response(fn () => ['comments' => $this->commentService->list($pollId)]);
return $this->response(fn () => [
'comments' => $this->commentService->list($$this->acl->setPollId($pollId))
]);
}
/**
@ -58,7 +66,9 @@ class CommentApiController extends BaseApiController {
* @NoCSRFRequired
*/
public function add(int $pollId, string $message): JSONResponse {
return $this->response(fn () => ['comment' => $this->commentService->add($message, $pollId)]);
return $this->response(fn () => [
'comment' => $this->commentService->add($message, $this->acl->setPollId($pollId, Acl::PERMISSION_COMMENT_ADD))
]);
}
/**
@ -68,6 +78,10 @@ class CommentApiController extends BaseApiController {
* @NoCSRFRequired
*/
public function delete(int $commentId): JSONResponse {
return $this->responseDeleteTolerant(fn () => ['comment' => $this->commentService->delete($commentId)]);
$comment = $this->commentService->get($commentId);
return $this->responseDeleteTolerant(fn () => [
'comment' => $this->commentService->delete($comment, $this->acl->setPollId($comment->getPollId()))
]);
}
}

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

@ -23,6 +23,7 @@
namespace OCA\Polls\Controller;
use OCA\Polls\Model\Acl;
use OCA\Polls\Service\CommentService;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
@ -33,13 +34,18 @@ class CommentController extends BaseController {
/** @var CommentService */
private $commentService;
/** @var Acl */
private $acl;
public function __construct(
string $appName,
Acl $acl,
ISession $session,
IRequest $request,
CommentService $commentService
) {
parent::__construct($appName, $request, $session);
$this->acl = $acl;
$this->commentService = $commentService;
}
@ -48,7 +54,9 @@ class CommentController extends BaseController {
* @NoAdminRequired
*/
public function list(int $pollId): JSONResponse {
return $this->response(fn () => ['comments' => $this->commentService->list($pollId)]);
return $this->response(fn () => [
'comments' => $this->commentService->list($this->acl->setPollId($pollId))
]);
}
/**
@ -56,7 +64,9 @@ class CommentController extends BaseController {
* @NoAdminRequired
*/
public function add(int $pollId, string $message): JSONResponse {
return $this->response(fn () => ['comment' => $this->commentService->add($message, $pollId)]);
return $this->response(fn () => [
'comment' => $this->commentService->add($message, $this->acl->setPollId($pollId, Acl::PERMISSION_COMMENT_ADD))
]);
}
/**
@ -64,6 +74,10 @@ class CommentController extends BaseController {
* @NoAdminRequired
*/
public function delete(int $commentId): JSONResponse {
return $this->responseDeleteTolerant(fn () => ['comment' => $this->commentService->delete($commentId)]);
$comment = $this->commentService->get($commentId);
return $this->responseDeleteTolerant(fn () => [
'comment' => $this->commentService->delete($comment, $this->acl->setPollId($comment->getPollId()))
]);
}
}

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

@ -83,10 +83,11 @@ class PollController extends BaseController {
* @NoAdminRequired
*/
public function get(int $pollId): JSONResponse {
$this->acl->setPollId($pollId);
$poll = $this->pollService->get($pollId);
$this->acl->setPoll($poll);
return $this->response(fn () => [
'acl' => $this->acl,
'poll' => $this->acl->getPoll(),
'poll' => $poll,
]);
}
@ -117,7 +118,7 @@ class PollController extends BaseController {
public function sendConfirmation(int $pollId): JSONResponse {
$this->acl->setPollId($pollId, Acl::PERMISSION_POLL_EDIT);
return $this->response(fn () => [
'confirmations' => $this->mailService->sendConfirmation($pollId),
'confirmations' => $this->mailService->sendConfirmations($pollId),
]);
}

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

@ -132,6 +132,7 @@ class PublicController extends BaseController {
*/
public function getPoll(string $token): JSONResponse {
$this->acl->setToken($token);
return $this->response(fn () => [
'acl' => $this->acl,
'poll' => $this->pollService->get($this->acl->getPollId()),
@ -144,9 +145,8 @@ class PublicController extends BaseController {
* @NoAdminRequired
*/
public function watchPoll(string $token, ?int $offset): JSONResponse {
$pollId = $this->acl->setToken($token)->getPollId();
return $this->responseLong(fn () => [
'updates' => $this->watchService->watchUpdates($pollId, $offset)
'updates' => $this->watchService->watchUpdates($this->acl->setToken($token)->getPollId(), $offset)
], $token);
}
@ -162,25 +162,16 @@ class PublicController extends BaseController {
], $token);
}
/**
* Get Comments
* @NoAdminRequired
* @PublicPage
*/
public function getComments(string $token): JSONResponse {
return $this->response(fn () => [
'comments' => $this->commentService->list(null, $token)
], $token);
}
/**
* Get votes
* @NoAdminRequired
* @PublicPage
*/
public function getVotes(string $token): JSONResponse {
$this->acl->setToken($token);
return $this->response(fn () => [
'votes' => $this->voteService->list(null, $token)
'votes' => $this->voteService->list(null, $this->acl)
], $token);
}
@ -191,7 +182,7 @@ class PublicController extends BaseController {
*/
public function deleteUser(string $token): JSONResponse {
return $this->response(fn () => [
'deleted' => $this->voteService->delete(null, null, $token)
'deleted' => $this->voteService->delete(null, null, $this->acl->setToken($token, Acl::PERMISSION_VOTE_EDIT))
], $token);
}
@ -202,7 +193,7 @@ class PublicController extends BaseController {
*/
public function getOptions(string $token): JSONResponse {
return $this->response(fn () => [
'options' => $this->optionService->list(null, $token)
'options' => $this->optionService->list(null, $this->acl->setToken($token))
], $token);
}
@ -213,7 +204,13 @@ class PublicController extends BaseController {
*/
public function addOption(string $token, int $timestamp = 0, string $text = '', int $duration = 0): JSONResponse {
return $this->responseCreate(fn () => [
'option' => $this->optionService->add(null, $timestamp, $text, $duration, $token)
'option' => $this->optionService->add(
null,
$timestamp,
$text,
$duration,
$this->acl->setToken($token, Acl::PERMISSION_OPTIONS_ADD)
)
], $token);
}
@ -224,18 +221,7 @@ class PublicController extends BaseController {
*/
public function deleteOption(string $token, int $optionId): JSONResponse {
return $this->responseDeleteTolerant(fn () => [
'option' => $this->optionService->delete($optionId, $token)
], $token);
}
/**
* Get subscription status
* @PublicPage
* @NoAdminRequired
*/
public function getSubscription(string $token): JSONResponse {
return $this->response(fn () => [
'subscribed' => $this->subscriptionService->get(null, $token)
'option' => $this->optionService->delete($optionId, $this->acl->setToken($token, Acl::PERMISSION_POLL_VIEW))
], $token);
}
@ -246,7 +232,18 @@ class PublicController extends BaseController {
*/
public function setVote(int $optionId, string $setTo, string $token): JSONResponse {
return $this->response(fn () => [
'vote' => $this->voteService->set($optionId, $setTo, $token)
'vote' => $this->voteService->set($optionId, $setTo, $this->acl->setToken($token, Acl::PERMISSION_COMMENT_ADD))
], $token);
}
/**
* Get Comments
* @NoAdminRequired
* @PublicPage
*/
public function getComments(string $token): JSONResponse {
return $this->response(fn () => [
'comments' => $this->commentService->list($this->acl->setToken($token))
], $token);
}
@ -257,7 +254,7 @@ class PublicController extends BaseController {
*/
public function addComment(string $token, string $message): JSONResponse {
return $this->response(fn () => [
'comment' => $this->commentService->add($message, null, $token)
'comment' => $this->commentService->add($message, $this->acl->setToken($token, Acl::PERMISSION_COMMENT_ADD))
], $token);
}
@ -267,8 +264,20 @@ class PublicController extends BaseController {
* @PublicPage
*/
public function deleteComment(int $commentId, string $token): JSONResponse {
$comment = $this->commentService->get($commentId);
return $this->responseDeleteTolerant(fn () => [
'comment' => $this->commentService->delete($commentId, $token)
'comment' => $this->commentService->delete($comment, $this->acl->setToken($token, Acl::PERMISSION_COMMENT_ADD))
], $token);
}
/**
* Get subscription status
* @PublicPage
* @NoAdminRequired
*/
public function getSubscription(string $token): JSONResponse {
return $this->response(fn () => [
'subscribed' => $this->subscriptionService->get($this->acl->setToken($token))
], $token);
}
@ -279,7 +288,7 @@ class PublicController extends BaseController {
*/
public function subscribe(string $token): JSONResponse {
return $this->response(fn () => [
'subscribed' => $this->subscriptionService->set(true, null, $token)
'subscribed' => $this->subscriptionService->set(true, $this->acl->setToken($token))
], $token);
}
@ -290,7 +299,7 @@ class PublicController extends BaseController {
*/
public function unsubscribe(string $token): JSONResponse {
return $this->response(fn () => [
'subscribed' => $this->subscriptionService->set(false, null, $token)
'subscribed' => $this->subscriptionService->set(false, $this->acl->setToken($token))
], $token);
}
@ -303,7 +312,7 @@ class PublicController extends BaseController {
*/
public function validatePublicUsername(string $userName, string $token): JSONResponse {
return $this->response(fn () => [
'result' => $this->systemService->validatePublicUsername($userName, $token), 'name' => $userName
'result' => $this->systemService->validatePublicUsername($userName, $this->shareService->get($token)), 'name' => $userName
], $token);
}
@ -325,7 +334,7 @@ class PublicController extends BaseController {
*/
public function setDisplayName(string $token, string $displayName): JSONResponse {
return $this->response(fn () => [
'share' => $this->shareService->setDisplayname($token, $displayName)
'share' => $this->shareService->setDisplayname($this->shareService->get($token), $displayName)
], $token);
}
@ -337,7 +346,7 @@ class PublicController extends BaseController {
*/
public function setEmailAddress(string $token, string $emailAddress = ''): JSONResponse {
return $this->response(fn () => [
'share' => $this->shareService->setEmailAddress($token, $emailAddress, true)
'share' => $this->shareService->setEmailAddress($this->shareService->get($token), $emailAddress, true)
], $token);
}
@ -348,7 +357,7 @@ class PublicController extends BaseController {
*/
public function deleteEmailAddress(string $token): JSONResponse {
return $this->response(fn () => [
'share' => $this->shareService->deleteEmailAddress($token)
'share' => $this->shareService->deleteEmailAddress($this->shareService->get($token))
], $token);
}
@ -358,9 +367,11 @@ class PublicController extends BaseController {
* @NoAdminRequired
* @PublicPage
*/
public function register(string $token, string $userName, string $emailAddress = ''): JSONResponse {
public function register(string $token, string $userName, string $emailAddress = '', string $timeZone = ''): JSONResponse {
$language = explode(',', $this->request->getHeader('Accept-Language'))[0];
return $this->responseCreate(fn () => [
'share' => $this->shareService->register($token, $userName, $emailAddress)
'share' => $this->shareService->register($this->shareService->get($token), $userName, $emailAddress, $language, $language, $timeZone)
], $token);
}

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

@ -54,7 +54,6 @@ class ShareController extends BaseController {
/**
* List shares
*
* @NoAdminRequired
*
* @return JSONResponse
@ -100,16 +99,36 @@ class ShareController extends BaseController {
* @NoAdminRequired
*/
public function setEmailAddress(string $token, string $emailAddress = ''): JSONResponse {
return $this->response(fn () => ['share' => $this->shareService->setEmailAddress($token, $emailAddress)]);
return $this->response(fn () => [
'share' => $this->shareService->setEmailAddress($this->shareService->get($token),
$emailAddress)
]);
}
/**
* Create a personal share from a public share
* or update an email share with the username
* @deprecated not used?
* @NoAdminRequired
*/
public function register(string $token, string $userName, string $emailAddress = ''): JSONResponse {
return $this->responseCreate(fn () => ['share' => $this->shareService->register($token, $userName, $emailAddress)]);
public function register(
string $token,
string $userName,
string $emailAddress = '',
string $timeZone = ''
): JSONResponse {
$language = explode(',', $this->request->getHeader('Accept-Language'))[0];
return $this->responseCreate(fn () =>
['share' => $this->shareService->register(
$this->shareService->get($token),
$userName,
$emailAddress,
$language,
$language,
$timeZone
)]
);
}
/**

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

@ -24,6 +24,7 @@
namespace OCA\Polls\Controller;
use OCA\Polls\Exceptions\Exception;
use OCA\Polls\Model\Acl;
use OCA\Polls\Service\SubscriptionService;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Http\JSONResponse;
@ -35,13 +36,17 @@ class SubscriptionApiController extends BaseApiController {
/** @var SubscriptionService */
private $subscriptionService;
/** @var Acl */
private $acl;
public function __construct(
string $appName,
Acl $acl,
SubscriptionService $subscriptionService,
IRequest $request
) {
parent::__construct($appName, $request);
$this->acl = $acl;
$this->subscriptionService = $subscriptionService;
}
@ -53,7 +58,7 @@ class SubscriptionApiController extends BaseApiController {
*/
public function get(int $pollId): JSONResponse {
try {
$this->subscriptionService->get($pollId, '');
$this->subscriptionService->get($this->acl->setPollId($pollId));
return new JSONResponse(['status' => 'Subscribed to poll ' . $pollId], Http::STATUS_OK);
} catch (DoesNotExistException $e) {
return new JSONResponse(['status' => 'Not subscribed to poll ' . $pollId], Http::STATUS_NOT_FOUND);
@ -70,7 +75,7 @@ class SubscriptionApiController extends BaseApiController {
*/
public function subscribe(int $pollId): JSONResponse {
try {
$this->subscriptionService->set(true, $pollId, '');
$this->subscriptionService->set(true, $this->acl->setPollId($pollId));
return new JSONResponse(['status' => 'Subscribed to poll ' . $pollId], Http::STATUS_OK);
} catch (Exception $e) {
return new JSONResponse(['message' => $e->getMessage()], $e->getStatus());
@ -85,7 +90,7 @@ class SubscriptionApiController extends BaseApiController {
*/
public function unsubscribe(int $pollId): JSONResponse {
try {
$this->subscriptionService->set(false, $pollId, '');
$this->subscriptionService->set(false, $this->acl->setPollId($pollId));
return new JSONResponse(['status' => 'Unsubscribed from poll ' . $pollId], Http::STATUS_OK);
} catch (Exception $e) {
return new JSONResponse(['message' => $e->getMessage()], $e->getStatus());

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

@ -23,6 +23,7 @@
namespace OCA\Polls\Controller;
use OCA\Polls\Model\Acl;
use OCA\Polls\Service\SubscriptionService;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
@ -33,13 +34,18 @@ class SubscriptionController extends BaseController {
/** @var SubscriptionService */
private $subscriptionService;
/** @var Acl */
private $acl;
public function __construct(
string $appName,
Acl $acl,
ISession $session,
IRequest $request,
SubscriptionService $subscriptionService
) {
parent::__construct($appName, $request, $session);
$this->acl = $acl;
$this->subscriptionService = $subscriptionService;
}
@ -48,7 +54,9 @@ class SubscriptionController extends BaseController {
* @NoAdminRequired
*/
public function get(int $pollId): JSONResponse {
return $this->response(fn () => ['subscribed' => $this->subscriptionService->get($pollId)]);
return $this->response(fn () => [
'subscribed' => $this->subscriptionService->get($this->acl->setPollId($pollId))
]);
}
/**
@ -56,7 +64,9 @@ class SubscriptionController extends BaseController {
* @NoAdminRequired
*/
public function subscribe(int $pollId): JSONResponse {
return $this->response(fn () => ['subscribed' => $this->subscriptionService->set(true, $pollId, '')]);
return $this->response(fn () => [
'subscribed' => $this->subscriptionService->set(true, $this->acl->setPollId($pollId))
]);
}
/**
@ -64,6 +74,8 @@ class SubscriptionController extends BaseController {
* @NoAdminRequired
*/
public function unsubscribe(int $pollId): JSONResponse {
return $this->response(fn () => ['subscribed' => $this->subscriptionService->set(false, $pollId, '')]);
return $this->response(fn () => [
'subscribed' => $this->subscriptionService->set(false, $this->acl->setPollId($pollId))
]);
}
}

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

@ -25,6 +25,10 @@
namespace OCA\Polls\Db;
use DateInterval;
use DateTime;
use DateTimeImmutable;
use DateTimeZone;
use JsonSerializable;
use OCA\Polls\Helper\Container;
@ -32,6 +36,7 @@ use OCP\AppFramework\Db\Entity;
use OCP\IUser;
use OCP\IUserManager;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\IL10N;
/**
* @method int getId()
@ -145,6 +150,7 @@ class Option extends Entity implements JsonSerializable {
];
}
public function getPollOptionText(): string {
if ($this->getTimestamp() && $this->getDuration()) {
return date('c', $this->getTimestamp()) . ' - ' . date('c', $this->getTimestamp() + $this->getDuration());
@ -197,13 +203,7 @@ class Option extends Entity implements JsonSerializable {
if (!strncmp($this->getOwner(), 'deleted_', 8)) {
return 'Deleted User';
}
// if ($this->getOwner() === '') {
// return '';
// }
// return $this->getOwnerIsNoUser()
// ? $this->getOwner()
// : $this->userManager->get($this->getOwner())->getDisplayName();
if ($this->getOwnerIsNoUser()) {
try {
$share = $this->shareMapper->findByPollAndUser($this->getPollId(), $this->getOwner());
@ -215,7 +215,68 @@ class Option extends Entity implements JsonSerializable {
return $this->userManager->get($this->getOwner())->getDisplayName();
}
public function getDateStringLocalized(DateTimeZone $timeZone, IL10N $l10n) {
$mutableFrom = DateTime::createFromImmutable($this->getDateObjectFrom($timeZone));
$mutableTo = DateTime::createFromImmutable($this->getDateObjectTo($timeZone));
$dayLongSecond = new DateInterval('PT1S');
$sameDay = $this->getDateObjectFrom($timeZone)->format('Y-m-d') === $this->getDateObjectTo($timeZone)->format('Y-m-d');
// If duration is zero, the option represents a moment with day and time
if ($this->getDuration() === 0) {
return $l10n->l('datetime', $mutableFrom);
}
$dateTimeFrom = $l10n->l('datetime', $mutableFrom);
$dateTimeTo = $l10n->l('datetime', $mutableTo);
// If the option spans over on or more whole days, the option represents only the days without time
// adjust the end by substracting a second, to represent the last moment at the day and not the first moment of the following day
// which is calculated by adding the duration
if ($this->getDaylong($timeZone)) {
$dateTimeFrom = $l10n->l('date', $mutableFrom);
$dateTimeTo = $l10n->l('date', $mutableTo->sub($dayLongSecond));
// if start and end day are identiacal, just return the start day
if ($dateTimeFrom === $dateTimeTo) {
return $dateTimeFrom;
}
}
if ($sameDay) {
$dateTimeTo = $dateTimeTo = $l10n->l('time', $mutableTo);
}
return $dateTimeFrom . ' - ' . $dateTimeTo;
}
private function getOwnerIsNoUser(): bool {
return !$this->userManager->get($this->getOwner()) instanceof IUser;
}
/**
* Check, if the date option spans one or more whole days (from 00:00 to 24:00)
*/
private function getDaylong(DateTimeZone $timeZone = null): bool {
$from = $this->getDateObjectFrom($timeZone);
$to = $this->getDateObjectTo($timeZone);
$dateInterval = $from->diff($to);
if (
$this->getDuration() > 0
&& $from->format('H') === '00'
&& $dateInterval->h + $dateInterval->i + $dateInterval->h === 0
) {
return true;
}
return false;
}
private function getDateObjectFrom(DateTimeZone $timeZone): DateTimeImmutable {
$dateTime = (new DateTimeImmutable())->setTimestamp($this->getTimestamp());
return $dateTime->setTimezone($timeZone);
}
private function getDateObjectTo(DateTimeZone $timeZone): DateTimeImmutable {
$dateTime = (new DateTimeImmutable())->setTimestamp($this->getTimestamp() + $this->getDuration());
return $dateTime->setTimezone($timeZone);
}
}

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

@ -28,7 +28,6 @@ namespace OCA\Polls\Db;
use JsonSerializable;
use OCA\Polls\Exceptions\NoDeadLineException;
use OCA\Polls\Helper\Container;
use OCA\Polls\Model\UserGroup\User;
use OCP\AppFramework\Db\Entity;
use OCP\IUser;
use OCP\IUserManager;
@ -326,10 +325,6 @@ class Poll extends Entity implements JsonSerializable {
: $this->owner;
}
public function getOwnerUserObject(): User {
return new User($this->owner);
}
private function setMiscSettingsArray(array $value) : void {
$this->setMiscSettings(json_encode($value));
}

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

@ -27,10 +27,8 @@ use JsonSerializable;
use OCP\AppFramework\Db\Entity;
use OCP\IURLGenerator;
use OCA\Polls\Model\UserGroup\UserBase;
use OCA\Polls\Model\Settings\AppSettings;
use OCA\Polls\Helper\Container;
use OCA\Polls\Service\UserService;
/**
* @method int getId()
@ -126,15 +124,11 @@ class Share extends Entity implements JsonSerializable {
/** @var AppSettings */
protected $appSettings;
/** @var UserService */
protected $userService;
public function __construct() {
$this->addType('pollId', 'int');
$this->addType('invitationSent', 'int');
$this->addType('reminderSent', 'int');
$this->urlGenerator = Container::queryClass(IURLGenerator::class);
$this->userService = Container::queryClass(UserService::class);
$this->appSettings = new AppSettings;
}
@ -164,6 +158,27 @@ class Share extends Entity implements JsonSerializable {
$this->setMiscSettingsByKey('publicPollEmail', $value);
}
public function getTimeZoneName() : ?string {
return $this->getMiscSettingsArray()['timeZone'] ?? '';
}
public function setTimeZoneName(string $value) : void {
$this->setMiscSettingsByKey('timeZone', $value);
}
public function getLanguage(): ?string {
return $this->getMiscSettingsArray()['language'] ?? '';
}
// Fallback for now; use language as locale
public function getLocale(): ?string {
return $this->getLanguage();
}
public function setLanguage(string $value) : void {
$this->setMiscSettingsByKey('language', $value);
}
public function getURL(): string {
if (in_array($this->type, [self::TYPE_USER, self::TYPE_ADMIN, self::TYPE_GROUP], true)) {
return $this->urlGenerator->linkToRouteAbsolute(
@ -191,15 +206,6 @@ class Share extends Entity implements JsonSerializable {
return $this->userId;
}
public function getUserObject(): UserBase {
return $this->userService->getUser(
$this->type,
$this->userId,
$this->displayName,
$this->emailAddress
);
}
public function getRichObjectString(): array {
return [
'type' => 'highlight',

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

@ -31,6 +31,7 @@ use OCA\Polls\Db\Poll;
use OCA\Polls\Db\Share;
use OCA\Polls\Db\Vote;
use OCA\Polls\Helper\Container;
use OCA\Polls\Service\UserService;
use OCP\IUserSession;
abstract class BaseEvent extends Event {
@ -55,12 +56,16 @@ abstract class BaseEvent extends Event {
/** @var IUserSession */
private $userSession;
/** @var UserService */
protected $userService;
public function __construct(
$eventObject
) {
parent::__construct();
$this->eventObject = $eventObject;
$this->poll = Container::queryPoll($this->getPollId());
$this->userService = Container::queryClass(UserService::class);
$this->userSession = Container::queryClass(IUserSession::class);
$this->activitySubjectParams['pollTitle'] = [

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

@ -24,6 +24,7 @@
namespace OCA\Polls\Event;
use OCA\Polls\Db\Share;
use OCA\Polls\Model\UserBase;
abstract class ShareEvent extends BaseEvent {
public const ADD = 'share_add';
@ -38,16 +39,23 @@ abstract class ShareEvent extends BaseEvent {
/** @var Share */
private $share;
/** @var UserBase */
protected $sharee;
public function __construct(Share $share) {
parent::__construct($share);
$this->activityObject = 'poll';
$this->log = false;
$this->share = $share;
$this->activitySubjectParams['shareType'] = $this->share->getRichObjectString();
$this->activitySubjectParams['sharee'] = $this->share->getUserObject()->getRichObjectString();
$this->activitySubjectParams['sharee'] = $this->getSharee()->getRichObjectString();
}
public function getShare(): Share {
return $this->share;
}
protected function getSharee() : UserBase {
return $this->userService->getUserFromShare($this->share);
}
}

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

@ -116,7 +116,10 @@ class Acl implements JsonSerializable {
/**
* load share via token and than call setShare
*/
public function setToken(string $token = '', string $permission = self::PERMISSION_POLL_VIEW, ?int $pollIdToValidate = null): Acl {
public function setToken(string $token = '',
string $permission = self::PERMISSION_POLL_VIEW,
?int $pollIdToValidate = null
): Acl {
try {
$this->share = $this->shareMapper->findByToken($token);
@ -134,6 +137,10 @@ class Acl implements JsonSerializable {
return $this;
}
public function getShare() : Share {
return $this->share;
}
public function setPollId(?int $pollId = 0, string $permission = self::PERMISSION_POLL_VIEW): Acl {
try {
$this->poll = $this->pollMapper->find($pollId);
@ -145,6 +152,10 @@ class Acl implements JsonSerializable {
return $this;
}
public function setPoll(Poll $poll) {
$this->poll = $poll;
}
public function getToken(): string {
return strval($this->share->getToken());
}
@ -161,10 +172,18 @@ class Acl implements JsonSerializable {
return $this->getIsLoggedIn() ? $this->userSession->getUser()->getUID() : $this->share->getUserId();
}
public function validateUserId(string $userId): void {
public function validateUserId(string $userId): bool {
if ($this->getUserId() !== $userId) {
throw new NotAuthorizedException;
}
return true;
}
public function validatePollId(int $pollId): bool {
if ($this->getPollId() !== $pollId) {
throw new NotAuthorizedException;
}
return true;
}
public function getIsOwner(): bool {
@ -441,10 +460,10 @@ class Acl implements JsonSerializable {
private function validateShareAccess(): void {
if ($this->getIsLoggedIn() && !$this->getIsShareValidForUsers()) {
throw new NotAuthorizedException('Share type "' . $this->share->getType() . '"only valid for guests');
throw new NotAuthorizedException('Share type "' . $this->share->getType() . '" is only valid for guests');
}
if (!$this->getIsShareValidForGuests()) {
throw new NotAuthorizedException('Share type "' . $this->share->getType() . '"only valid for registered users');
throw new NotAuthorizedException('Share type "' . $this->share->getType() . '" is only valid for registered users');
};
}

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

@ -21,12 +21,16 @@
*
*/
namespace OCA\Polls\Model\UserGroup;
namespace OCA\Polls\Model\Group;
use OCA\Circles\Api\v1\Circles;
use OCA\Circles\Model\Circle as CirclesCircle;
use OCA\Polls\Exceptions\CirclesNotEnabledException;
use OCA\Polls\Helper\Container;
use OCA\Polls\Model\User\Contact;
use OCA\Polls\Model\User\Email;
use OCA\Polls\Model\User\User;
use OCA\Polls\Model\UserBase;
class Circle extends UserBase {
public const TYPE = 'circle';
@ -40,7 +44,7 @@ class Circle extends UserBase {
) {
parent::__construct($id, self::TYPE);
$this->icon = self::ICON;
$this->description = Container::getL10N()->t('Circle');
$this->description = $this->l10n->t('Circle');
$this->richObjectType = 'circle';
if (self::isEnabled()) {

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

@ -21,10 +21,12 @@
*
*/
namespace OCA\Polls\Model\UserGroup;
namespace OCA\Polls\Model\Group;
use OCA\Polls\Helper\Container;
use OCA\Polls\Exceptions\ContactsNotEnabledExceptions;
use OCA\Polls\Model\User\Contact;
use OCA\Polls\Model\UserBase;
use OCP\Contacts\IManager as IContactsManager;
class ContactGroup extends UserBase {
@ -36,7 +38,7 @@ class ContactGroup extends UserBase {
) {
parent::__construct($id, self::TYPE);
$this->icon = self::ICON;
$this->description = Container::getL10N()->t('Contact group');
$this->description = $this->l10n->t('Contact group');
$this->richObjectType = 'addressbook-contact';
if (!self::isEnabled()) {

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

@ -21,9 +21,11 @@
*
*/
namespace OCA\Polls\Model\UserGroup;
namespace OCA\Polls\Model\Group;
use OCA\Polls\Helper\Container;
use OCA\Polls\Model\User\User;
use OCA\Polls\Model\UserBase;
use OCP\IGroupManager;
use OCP\IGroup;
@ -39,7 +41,7 @@ class Group extends UserBase {
) {
parent::__construct($id, self::TYPE);
$this->icon = self::ICON;
$this->description = Container::getL10N()->t('Group');
$this->description = $this->l10n->t('Group');
$this->richObjectType = 'user-group';
$this->group = Container::queryClass(IGroupManager::class)->get($this->id);

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

@ -24,25 +24,25 @@
namespace OCA\Polls\Model\Mail;
use OCA\Polls\Db\OptionMapper;
use OCA\Polls\Helper\Container;
use OCA\Polls\Db\Option;
use OCA\Polls\Db\Poll;
class ConfirmationMail extends MailBase {
private const TEMPLATE_CLASS = 'polls.Confirmation';
/** @var OptionMapper */
protected $optionMapper;
/** @var Option[] */
protected $confirmedOptions;
public function __construct(
string $recipientId,
int $pollId
) {
parent::__construct($recipientId, $pollId);
$this->optionMapper = Container::queryClass(OptionMapper::class);
$this->confirmedOptions = $this->optionMapper->findConfirmed($pollId);
}
protected function getSubject(): string {
return $this->l10n->t('Poll "%s" has been closed', $this->poll->getTitle());
return $this->l10n->t('Poll "%s" - Confirmation', $this->poll->getTitle());
}
protected function getFooter(): string {
@ -56,20 +56,32 @@ class ConfirmationMail extends MailBase {
$this->l10n->t('{owner} wants to inform you about the final result of the poll "{title}"')
));
$confirmedOptions = $this->optionMapper->findConfirmed($this->poll->getId());
$countConfirmed = count($confirmedOptions);
$this->emailTemplate->addBodyText(
$this->l10n->n('Confirmed option:', 'Confirmed options:', $countConfirmed)
$this->l10n->n('Confirmed option:', 'Confirmed options:', count($this->confirmedOptions))
);
foreach ($confirmedOptions as $option) {
$this->emailTemplate->addBodyListItem(
$option->getPollOptionText()
foreach ($this->confirmedOptions as $option) {
if ($this->poll->getType() === Poll::TYPE_DATE) {
$this->emailTemplate->addBodyListItem($option->getDateStringLocalized($this->recipient->getTimeZone(), $this->l10n));
} else {
$this->emailTemplate->addBodyListItem($option->getPollOptionText());
}
}
if ($this->poll->getType() === Poll::TYPE_DATE) {
$this->emailTemplate->addBodyText(
$this->l10n->t('The used time zone is "%s", based on the detected time zone at your registration time. To view the times in your current time zone, enter the poll by clicking the button below.', $this->recipient->getTimeZoneName())
);
}
$this->emailTemplate->addBodyText($this->getRichDescription(), $this->poll->getDescription());
// $this->emailTemplate->addBodyText(
// $this->l10n->t('Used languageCode is %1$s, %2$s, %3$s', [$this->l10n->getLanguageCode(), $this->l10n->getLocaleCode(), $this->transFactory->localeExists($this->l10n->getLanguageCode())])
// );
if ($this->poll->getDescription()) {
$this->emailTemplate->addBodyText($this->getRichDescription(), $this->poll->getDescription());
}
$this->addButtonToPoll();
$this->emailTemplate->addBodyText($this->l10n->t('This link gives you personal access to the poll named above. Press the button above or copy the following link and add it in your browser\'s location bar:'));

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

@ -24,12 +24,13 @@
namespace OCA\Polls\Model\Mail;
use OCA\Polls\Db\OptionMapper;
use OCA\Polls\Db\Poll;
use OCA\Polls\Exceptions\InvalidEmailAddress;
use OCA\Polls\Helper\Container;
use OCA\Polls\Model\Settings\AppSettings;
use OCA\Polls\Model\UserGroup\UserBase;
use OCA\Polls\Model\UserGroup\User;
use OCA\Polls\Model\UserBase;
use OCA\Polls\Model\User\User;
use OCA\Polls\Service\UserService;
use OCP\IL10N;
use OCP\L10N\IFactory;
@ -61,7 +62,10 @@ abstract class MailBase {
/** @var IMailer */
protected $mailer;
/** @var OptionMapper */
protected $optionMapper;
/** @var User */
protected $owner;
@ -88,9 +92,9 @@ abstract class MailBase {
$this->appSettings = Container::queryClass(AppSettings::class);
$this->logger = Container::queryClass(LoggerInterface::class);
$this->mailer = Container::queryClass(IMailer::class);
$this->optionMapper = Container::queryClass(OptionMapper::class);
$this->transFactory = Container::queryClass(IFactory::class);
$this->userService = Container::queryClass(UserService::class);
$this->poll = $this->getPoll($pollId);
$this->recipient = $this->getUser($recipientId);
$this->url = $url ?? $this->poll->getVoteUrl();
@ -114,7 +118,7 @@ abstract class MailBase {
}
protected function initializeClass(): void {
$this->owner = $this->poll->getOwnerUserObject();
$this->owner = $this->getUser($this->poll->getOwner());
if ($this->recipient->getIsNoUser()) {
$this->url = $this->getShareURL();
@ -122,9 +126,8 @@ abstract class MailBase {
$this->l10n = $this->transFactory->get(
'polls',
$this->recipient->getLanguage()
? $this->recipient->getLanguage()
: $this->owner->getLanguage()
$this->recipient->getLanguageCode() ? $this->recipient->getLanguageCode() : $this->owner->getLanguageCode(),
$this->recipient->getLocaleCode()
);
}
@ -201,7 +204,7 @@ abstract class MailBase {
return $legal;
}
protected function getUser(string $userId) : UserBase {
protected function getUser(string $userId) : ?UserBase {
return $this->userService->evaluateUser($userId, $this->poll->getId());
}

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

@ -24,7 +24,7 @@
namespace OCA\Polls\Model\Settings;
use JsonSerializable;
use OCA\Polls\Model\UserGroup\Group;
use OCA\Polls\Model\Group\Group;
use OCA\Polls\Helper\Container;
use OCP\IConfig;
use OCP\IGroupManager;

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

@ -21,7 +21,7 @@
*
*/
namespace OCA\Polls\Model\UserGroup;
namespace OCA\Polls\Model\User;
class Admin extends User {
public const TYPE = 'admin';

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

@ -21,11 +21,12 @@
*
*/
namespace OCA\Polls\Model\UserGroup;
namespace OCA\Polls\Model\User;
use OCA\Polls\Exceptions\MultipleContactsFound;
use OCA\Polls\Exceptions\ContactsNotEnabledExceptions;
use OCA\Polls\Helper\Container;
use OCA\Polls\Model\UserBase;
use OCP\Contacts\IManager as IContactsManager;
use Psr\Log\LoggerInterface;
@ -44,7 +45,7 @@ class Contact extends UserBase {
) {
parent::__construct($id, self::TYPE);
$this->icon = self::ICON;
$this->description = Container::getL10N()->t('Contact');
$this->description = $this->l10n->t('Contact');
$this->richObjectType = 'addressbook-contact';
$this->logger = Container::queryClass(LoggerInterface::class);
@ -120,7 +121,7 @@ class Contact extends UserBase {
array_unshift($description, $this->getEmailAddress());
}
return count($description) ? implode(", ", $description) : Container::getL10N()->t('Contact');
return count($description) ? implode(", ", $description) : $this->l10n->t('Contact');
}

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

@ -21,7 +21,7 @@
*
*/
namespace OCA\Polls\Model\UserGroup;
namespace OCA\Polls\Model\User;
use OCP\IUserSession;
use OCA\Polls\Helper\Container;

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

@ -22,9 +22,9 @@
*/
namespace OCA\Polls\Model\UserGroup;
namespace OCA\Polls\Model\User;
use OCA\Polls\Helper\Container;
use OCA\Polls\Model\UserBase;
class Email extends UserBase {
public const TYPE = 'email';
@ -33,13 +33,15 @@ class Email extends UserBase {
public function __construct(
string $id,
string $displayName = '',
string $emailAddress = ''
string $emailAddress = '',
string $languageCode = ''
) {
parent::__construct($id, self::TYPE);
$this->icon = self::ICON;
$this->description = $emailAddress ? $emailAddress : Container::getL10N()->t('External Email');
$this->description = $emailAddress ? $emailAddress : $this->l10n->t('External Email');
$this->richObjectType = 'email';
$this->languageCode = $languageCode;
$this->emailAddress = $emailAddress ? $emailAddress : $id;
$this->displayName = $displayName ? $displayName : $this->displayName;
}

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

@ -22,9 +22,9 @@
*/
namespace OCA\Polls\Model\UserGroup;
namespace OCA\Polls\Model\User;
use OCA\Polls\Helper\Container;
use OCA\Polls\Model\UserBase;
class GenericUser extends UserBase {
public const TYPE = 'external';
@ -35,19 +35,20 @@ class GenericUser extends UserBase {
string $id,
string $type = self::TYPE,
string $displayName = '',
string $emailAddress = ''
string $emailAddress = '',
string $languageCode = '',
string $localeCode = '',
string $timeZoneName = ''
) {
parent::__construct($id, $type);
parent::__construct($id, $type, $displayName, $emailAddress, $languageCode, $localeCode, $timeZoneName);
$this->icon = self::ICON_DEFAULT;
$this->description = Container::getL10N()->t('External user');
$this->description = $this->l10n->t('External user');
$this->richObjectType = 'guest';
if ($type === UserBase::TYPE_PUBLIC) {
$this->icon = self::ICON_PUBLIC;
$this->description = Container::getL10N()->t('Public link');
$this->description = $this->l10n->t('Public link');
}
$this->displayName = $displayName;
$this->emailAddress = $emailAddress;
}
}

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

@ -21,11 +21,11 @@
*
*/
namespace OCA\Polls\Model\UserGroup;
namespace OCA\Polls\Model\User;
use DateTimeZone;
use OCA\Polls\Helper\Container;
use OCA\Polls\Model\Settings\AppSettings;
use OCA\Polls\Model\UserBase;
use OCP\IConfig;
use OCP\IUserManager;
use OCP\IUser;
@ -51,14 +51,15 @@ class User extends UserBase {
parent::__construct($id, $type);
$this->icon = self::ICON;
$this->isNoUser = false;
$this->description = Container::getL10N()->t('User');
$this->description = $this->l10n->t('User');
$this->config = Container::queryClass(IConfig::class);
$this->user = Container::queryClass(IUserManager::class)->get($this->id);
$this->displayName = $this->user->getDisplayName();
$this->emailAddress = $this->user->getEmailAddress();
$this->language = $this->config->getUserValue($this->id, 'core', 'lang');
$this->locale = $this->config->getUserValue($this->id, 'core', 'locale');
$this->languageCode = $this->config->getUserValue($this->id, 'core', 'lang');
$this->localeCode = $this->config->getUserValue($this->id, 'core', 'locale');
$this->timeZoneName = $this->config->getUserValue($this->id, 'core', 'timezone');
$this->appSettings = new AppSettings;
}
@ -73,14 +74,6 @@ class User extends UserBase {
return '';
}
public function getTimeZone(): DateTimeZone {
$tz = $this->config->getUserValue($this->getId(), 'core', 'timezone');
if ($tz) {
return new DateTimeZone($tz);
}
return new DateTimeZone($this->timezone->getTimeZone()->getName());
}
public function getDescription(): string {
if ($this->getEmailAddress()) {
return $this->getEmailAddress();

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

@ -21,11 +21,18 @@
*
*/
namespace OCA\Polls\Model\UserGroup;
namespace OCA\Polls\Model;
use DateTimeZone;
use OCP\IL10N;
use OCA\Polls\Helper\Container;
use OCA\Polls\Model\Group\Circle;
use OCA\Polls\Model\Group\ContactGroup;
use OCA\Polls\Model\User\Admin;
use OCA\Polls\Model\User\Contact;
use OCA\Polls\Model\User\Email;
use OCA\Polls\Model\User\User;
use OCA\Polls\Model\Group\Group;
use OCP\Collaboration\Collaborators\ISearch;
use OCP\Share\IShare;
use OCP\IDateTimeZone;
@ -47,7 +54,7 @@ class UserBase implements \JsonSerializable {
protected $richObjectType = 'user';
/** @var IL10N */
private $l10n;
protected $l10n;
/** @var string */
protected $id;
@ -65,10 +72,10 @@ class UserBase implements \JsonSerializable {
protected $emailAddress = null;
/** @var string */
protected $language = '';
protected $languageCode = '';
/** @var string */
protected $locale = '';
protected $localeCode = '';
/** @var string */
protected $organisation = '';
@ -83,7 +90,10 @@ class UserBase implements \JsonSerializable {
protected $categories = [];
/** @var IDateTimeZone */
protected $timezone;
protected $timeZone;
/** @var string */
protected $timeZoneName;
/** @var IUserSession */
protected $userSession;
@ -93,21 +103,23 @@ class UserBase implements \JsonSerializable {
string $type,
string $displayName = '',
string $emailAddress = '',
string $language = '',
string $locale = ''
string $languageCode = '',
string $localeCode = '',
string $timeZoneName = ''
) {
$this->icon = 'icon-share';
$this->l10n = Container::getL10N();
$this->timezone = Container::queryClass(IDateTimeZone::class);
$this->timeZone = Container::queryClass(IDateTimeZone::class);
$this->userSession = Container::queryClass(IUserSession::class);
$this->id = $id;
$this->type = $type;
$this->displayName = $displayName;
$this->emailAddress = $emailAddress;
$this->language = $language;
$this->locale = $locale;
$this->timeZoneName = $timeZoneName;
$this->localeCode = $localeCode;
$this->languageCode = $languageCode;
}
public function getId(): string {
@ -122,16 +134,27 @@ class UserBase implements \JsonSerializable {
return $this->type;
}
public function getLanguage(): string {
return $this->language;
public function getLanguageCode(): string {
return $this->languageCode;
}
public function getLocaleCode(): string {
if (!$this->localeCode) {
return $this->languageCode;
}
return $this->localeCode;
}
public function getTimeZone(): DateTimeZone {
return new DateTimeZone($this->timezone->getTimeZone()->getName());
if ($this->timeZoneName) {
return new DateTimeZone($this->timeZoneName);
}
return new DateTimeZone($this->timeZone->getTimeZone()->getName());
}
public function getLocale(): string {
return $this->locale;
public function getTimeZoneName(): string {
return $this->timeZoneName;
}
public function getDisplayName(): string {
@ -196,14 +219,14 @@ class UserBase implements \JsonSerializable {
return $this->emailAddress;
}
public function setLanguage(string $language): string {
$this->language = $language;
return $this->language;
public function setLanguageCode(string $languageCode): string {
$this->languageCode = $languageCode;
return $this->languageCode;
}
public function setLocale(string $locale): string {
$this->locale = $locale;
return $this->locale;
public function setLocaleCode(string $localeCode): string {
$this->localeCode = $localeCode;
return $this->localeCode;
}
public function setOrganisation(string $organisation): string {
@ -283,7 +306,9 @@ class UserBase implements \JsonSerializable {
'displayName' => $this->getDisplayName(),
'organisation' => $this->getOrganisation(),
'emailAddress' => $this->getEmailAddressMasked(),
'language' => $this->getLanguage(),
'languageCode' => $this->getLanguageCode(),
'localeCode' => $this->getLocaleCode(),
'timeZone' => $this->getTimeZoneName(),
'desc' => $this->getDescription(),
'subtitle' => $this->getDescription(),
'icon' => $this->getIcon(),

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

@ -32,7 +32,7 @@ use DateTimeZone;
use OCA\Polls\Db\OptionMapper;
use OCA\Polls\Db\Preferences;
use OCA\Polls\Model\CalendarEvent;
use OCA\Polls\Model\UserGroup\CurrentUser;
use OCA\Polls\Model\User\CurrentUser;
use OCP\Calendar\ICalendar;
use OCP\Calendar\IManager as CalendarManager;
use OCP\Util;

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

@ -68,21 +68,15 @@ class CommentService {
* @return Comment[]
*
*/
public function listFlat(?int $pollId, string $token = '') : array {
if ($token) {
$this->acl->setToken($token);
} else {
$this->acl->setPollId($pollId);
}
private function listFlat(Acl $acl) : array {
$comments = $this->commentMapper->findByPoll($acl->getPollId());
$comments = $this->commentMapper->findByPoll($this->acl->getPollId());
if (!$this->acl->getIsAllowed(Acl::PERMISSION_POLL_USERNAMES_VIEW)) {
$this->anonymizer->set($this->acl->getPollId(), $this->acl->getUserId());
if (!$acl->getIsAllowed(Acl::PERMISSION_POLL_USERNAMES_VIEW)) {
$this->anonymizer->set($acl->getPollId(), $acl->getUserId());
$this->anonymizer->anonymize($comments);
} elseif (!$this->acl->getIsLoggedIn()) {
} elseif (!$acl->getIsLoggedIn()) {
// if participant is not logged in avoid leaking user ids
AnonymizeService::replaceUserId($comments, $this->acl->getUserId());
AnonymizeService::replaceUserId($comments, $acl->getUserId());
}
return $comments;
@ -92,8 +86,8 @@ class CommentService {
* Get comments
* Read all comments of a poll based on the poll id and return list as array
*/
public function list(?int $pollId, string $token = ''): array {
$comments = $this->listFlat($pollId, $token);
public function list(Acl $acl): array {
$comments = $this->listFlat($acl);
$timeTolerance = 5 * 60; // treat comments within 5 minutes as one comment
$groupedComments = [];
@ -116,15 +110,16 @@ class CommentService {
/**
* Add comment
*/
public function add(string $message, ?int $pollId = null, ?string $token = null): Comment {
if ($token) {
$this->acl->setToken($token, Acl::PERMISSION_COMMENT_ADD);
} else {
$this->acl->setPollId($pollId, Acl::PERMISSION_COMMENT_ADD);
}
public function get(int $commentId): Comment {
return $this->commentMapper->find($commentId);
}
/**
* Add comment
*/
public function add(string $message, Acl $acl): Comment {
$this->comment = new Comment();
$this->comment->setPollId($this->acl->getPollId());
$this->comment->setUserId($this->acl->getUserId());
$this->comment->setPollId($acl->getPollId());
$this->comment->setUserId($acl->getUserId());
$this->comment->setComment($message);
$this->comment->setTimestamp(time());
$this->comment = $this->commentMapper->insert($this->comment);
@ -137,23 +132,16 @@ class CommentService {
/**
* Delete comment
*/
public function delete(int $commentId, string $token = ''): Comment {
$this->comment = $this->commentMapper->find($commentId);
public function delete(Comment $comment, Acl $acl): Comment {
$acl->validatePollId($comment->getPollId());
if ($token) {
$this->acl->setToken($token, Acl::PERMISSION_COMMENT_ADD, $this->comment->getPollId());
} else {
$this->acl->setPollId($this->comment->getPollId());
if (!$acl->getIsOwner()) {
$acl->validateUserId($comment->getUserId());
}
if (!$this->acl->getIsOwner()) {
$this->acl->validateUserId($this->comment->getUserId());
}
$this->commentMapper->delete($comment);
$this->eventDispatcher->dispatchTyped(new CommentDeleteEvent($comment));
$this->commentMapper->delete($this->comment);
$this->eventDispatcher->dispatchTyped(new CommentDeleteEvent($this->comment));
return $this->comment;
return $comment;
}
}

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

@ -36,7 +36,7 @@ use OCA\Polls\Model\Mail\ConfirmationMail;
use OCA\Polls\Model\Mail\InvitationMail;
use OCA\Polls\Model\Mail\NotificationMail;
use OCA\Polls\Model\Mail\ReminderMail;
use OCA\Polls\Model\UserGroup\UserBase;
use OCA\Polls\Model\UserBase;
use Psr\Log\LoggerInterface;
class MailService {
@ -130,7 +130,7 @@ class MailService {
$sentMails = [];
$abortedMails = [];
foreach ($share->getUserObject()->getMembers() as $recipient) {
foreach ($this->userService->getUserFromShare($share)->getMembers() as $recipient) {
$invitation = new InvitationMail($recipient->getId(), $share);
try {
@ -162,18 +162,20 @@ class MailService {
}
}
public function sendConfirmation($pollId): array {
/**
* Send a confirmation mail for the poll to all participants
*/
public function sendConfirmations($pollId): array {
$sentMails = [];
$abortedMails = [];
$participants = $this->userService->getParticipants($pollId);
foreach ($participants as $participant) {
if ($this->sendConfirmationToParticipant($participant, $pollId)) {
if ($this->sendConfirmationMail($participant, $pollId)) {
$sentMails[] = $participant->getDisplayName();
} else {
$abortedMails[] = $participant->getDisplayName();
}
}
return [
@ -195,11 +197,8 @@ class MailService {
}
}
private function sendConfirmationToParticipant(UserBase $participant, int $pollId) : bool {
$confirmation = new ConfirmationMail(
$participant->getId(),
$pollId
);
private function sendConfirmationMail(UserBase $participant, int $pollId) : bool {
$confirmation = new ConfirmationMail($participant->getId(), $pollId);
try {
$confirmation->send();
@ -209,11 +208,12 @@ class MailService {
} catch (\Exception $e) {
$this->logger->error('Error sending confirmation to ' . json_encode($participant));
}
return false;
}
private function sendAutoReminderToRecipients(Share $share, Poll $poll) {
foreach ($share->getUserObject()->getMembers() as $recipient) {
foreach ($this->userService->getUserFromShare($share)->getMembers() as $recipient) {
$reminder = new ReminderMail(
$recipient->getId(),
$poll->getId()

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

@ -39,6 +39,7 @@ use OCA\Polls\Event\PollOptionReorderedEvent;
use OCA\Polls\Exceptions\DuplicateEntryException;
use OCA\Polls\Exceptions\InvalidPollTypeException;
use OCA\Polls\Exceptions\InvalidOptionPropertyException;
use OCA\Polls\Exceptions\NotAuthorizedException;
use OCA\Polls\Model\Acl;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\DB\Exception;
@ -105,9 +106,9 @@ class OptionService {
*
* @psalm-return array<array-key, Option>
*/
public function list(?int $pollId, ?string $token = null): array {
if ($token) {
$this->acl->setToken($token);
public function list(?int $pollId, ?Acl $acl = null): array {
if ($acl) {
$this->acl = $acl;
} else {
$this->acl->setPollId($pollId);
}
@ -159,9 +160,9 @@ class OptionService {
*
* @return Option
*/
public function add(?int $pollId, int $timestamp = 0, string $pollOptionText = '', ?int $duration = 0, string $token = ''): Option {
if ($token) {
$this->acl->setToken($token, Acl::PERMISSION_OPTIONS_ADD);
public function add(?int $pollId, int $timestamp = 0, string $pollOptionText = '', ?int $duration = 0, ?Acl $acl = null): Option {
if ($acl) {
$this->acl = $acl;
} else {
$this->acl->setPollId($pollId, Acl::PERMISSION_OPTIONS_ADD);
}
@ -234,15 +235,19 @@ class OptionService {
*
* @return Option
*/
public function delete(int $optionId, string $token = ''): Option {
public function delete(int $optionId, ?Acl $acl = null): Option {
$this->option = $this->optionMapper->find($optionId);
if ($token) {
$this->acl->setToken($token, Acl::PERMISSION_POLL_VIEW, $this->option->getPollId());
if ($acl) {
$this->acl = $acl;
} else {
$this->acl->setPollId($this->option->getPollId());
}
if ($this->option->getPollId() !== $this->acl->getPollid()) {
throw new NotAuthorizedException();
}
if ($this->option->getOwner() !== $this->acl->getUserId()) {
$this->acl->request(Acl::PERMISSION_POLL_EDIT);
}

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

@ -23,7 +23,6 @@
namespace OCA\Polls\Service;
use OCA\Polls\Db\PollMapper;
use OCA\Polls\Db\ShareMapper;
use OCA\Polls\Db\Share;
use OCA\Polls\Event\ShareChangedDisplayNameEvent;
@ -39,7 +38,7 @@ use OCA\Polls\Exceptions\ShareAlreadyExistsException;
use OCA\Polls\Exceptions\NotFoundException;
use OCA\Polls\Exceptions\InvalidUsernameException;
use OCA\Polls\Model\Acl;
use OCA\Polls\Model\UserGroup\UserBase;
use OCA\Polls\Model\UserBase;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
use OCP\EventDispatcher\IEventDispatcher;
@ -49,6 +48,8 @@ use OCP\Security\ISecureRandom;
use Psr\Log\LoggerInterface;
class ShareService {
// Define user types, which can carry locale informations
public const SHARE_ALLOW_LOCALE = [UserBase::TYPE_EXTERNAL, UserBase::TYPE_CONTACT];
/** @var Acl */
private $acl;
@ -68,9 +69,6 @@ class ShareService {
/** @var NotificationService */
private $notificationService;
/** @var PollMapper */
private $pollMapper;
/** @var ShareMapper */
private $shareMapper;
@ -104,7 +102,6 @@ class ShareService {
IUserSession $userSession,
ShareMapper $shareMapper,
SystemService $systemService,
PollMapper $pollMapper,
Share $share,
MailService $mailService,
Acl $acl,
@ -118,7 +115,6 @@ class ShareService {
$this->secureRandom = $secureRandom;
$this->shareMapper = $shareMapper;
$this->systemService = $systemService;
$this->pollMapper = $pollMapper;
$this->share = $share;
$this->mailService = $mailService;
$this->acl = $acl;
@ -147,61 +143,8 @@ class ShareService {
return $this->shares;
}
private function sortByCategory() : void {
$sortedShares = [];
foreach (Share::TYPE_SORT_ARRAY as $shareType) {
$filteredShares = array_filter($this->shares, function ($share) use ($shareType) {
return $share->getType() === $shareType;
});
$sortedShares = array_merge($sortedShares, $filteredShares);
}
$this->shares = $sortedShares;
}
/**
* Validate if share type is allowed to be used in a public poll
* or is accessibale for use by the current user
*/
private function validateShareType() : void {
$currentUser = $this->userService->getCurrentUser();
switch ($this->share->getType()) {
case Share::TYPE_PUBLIC:
// public shares are always valid
break;
case Share::TYPE_USER:
if ($this->share->getUserId() !== $currentUser->getId()) {
// share is not valid for user
throw new NotAuthorizedException;
}
break;
case Share::TYPE_ADMIN:
if ($this->share->getUserId() !== $currentUser->getId()) {
// share is not valid for user
throw new NotAuthorizedException;
}
break;
case Share::TYPE_GROUP:
if (!$currentUser->getIsLoggedIn()) {
throw new NotAuthorizedException;
}
if (!$this->groupManager->isInGroup($this->share->getUserId(), $this->userId)) {
throw new NotAuthorizedException;
}
break;
case Share::TYPE_EMAIL:
break;
case Share::TYPE_EXTERNAL:
break;
default:
$this->logger->alert(json_encode('invalid share type ' . $this->share->getType()));
throw new NotAuthorizedException;
}
}
/**
* Get share by token
* Get share by token for accessing the poll
*/
public function get(string $token, bool $validateShareType = false): Share {
try {
@ -212,15 +155,15 @@ class ShareService {
} catch (DoesNotExistException $e) {
throw new NotFoundException('Token ' . $token . ' does not exist');
}
// Allow users entering the poll with a public share access
// Exception: logged in user accesses the poll via public share link
if ($this->share->getType() === Share::TYPE_PUBLIC && $this->userSession->isLoggedIn()) {
try {
// Test if the user has already access.
// Check, if he is already authorized for this poll
$this->acl->setPollId($this->share->getPollId());
} catch (NotAuthorizedException $e) {
// If he is not authorized until now, createNewShare a new personal share for this user.
// Return the created share
// If he is not authorized for this poll, create a personal share
// for this user and return the created share instead of the public share
return $this->createNewShare(
$this->share->getPollId(),
$this->userService->getUser(Share::TYPE_USER, $this->userSession->getUser()->getUID()),
@ -232,9 +175,7 @@ class ShareService {
}
/**
* Get share by token
*
* @return Share
* Flag invitation of this share as sent
*/
public function setInvitationSent(string $token): Share {
$share = $this->shareMapper->findByToken($token);
@ -242,71 +183,6 @@ class ShareService {
return $this->shareMapper->update($share);
}
/**
* crate a new share
*
* @return Share
*/
private function createNewShare(int $pollId, UserBase $userGroup, bool $preventInvitation = false): Share {
$preventInvitation = $userGroup->getType() === UserBase::TYPE_PUBLIC ?: $preventInvitation;
$token = $this->secureRandom->generate(
16,
ISecureRandom::CHAR_DIGITS .
ISecureRandom::CHAR_LOWER .
ISecureRandom::CHAR_UPPER
);
$this->share = new Share();
$this->share->setToken($token);
$this->share->setPollId($pollId);
// Convert user type contact to share type email
if ($userGroup->getType() === UserBase::TYPE_CONTACT) {
$this->share->setType(Share::TYPE_EMAIL);
$this->share->setUserId($userGroup->getEmailAddress());
} else {
$this->share->setType($userGroup->getType());
$this->share->setUserId($userGroup->getType() === UserBase::TYPE_PUBLIC ? $token : $userGroup->getPublicId());
}
$this->share->setInvitationSent($preventInvitation ? time() : 0);
$this->share->setDisplayName($userGroup->getDisplayName());
$this->share->setEmailAddress($userGroup->getEmailAddress());
$this->share = $this->shareMapper->insert($this->share);
return $this->share;
}
/**
* Add share
*
* @return Share
*/
public function add(int $pollId, string $type, string $userId = '', string $displayName = '', string $emailAddress = ''): Share {
$this->acl->setPollId($pollId, Acl::PERMISSION_POLL_EDIT);
if ($type === UserBase::TYPE_PUBLIC) {
$this->acl->request(ACL::PERMISSION_PUBLIC_SHARES);
} else {
try {
$this->shareMapper->findByPollAndUser($pollId, $userId);
throw new ShareAlreadyExistsException;
} catch (MultipleObjectsReturnedException $e) {
throw new ShareAlreadyExistsException;
} catch (DoesNotExistException $e) {
// continue
}
}
$this->createNewShare($pollId, $this->userService->getUser($type, $userId, $displayName, $emailAddress));
$this->eventDispatcher->dispatchTyped(new ShareCreateEvent($this->share));
return $this->share;
}
/**
* Change share type
*/
@ -343,29 +219,23 @@ class ShareService {
}
/**
* Set emailAddress of personal share
* Set emailAddress of personal public share
*
* @return Share
*/
public function setEmailAddress(string $token, string $emailAddress, bool $emptyIsValid = false): Share {
try {
$this->share = $this->shareMapper->findByToken($token);
} catch (DoesNotExistException $e) {
throw new NotFoundException('Token ' . $token . ' does not exist');
}
if ($this->share->getType() === Share::TYPE_EXTERNAL) {
public function setEmailAddress(Share $share, string $emailAddress, bool $emptyIsValid = false): Share {
if ($share->getType() === Share::TYPE_EXTERNAL) {
$this->systemService->validateEmailAddress($emailAddress, $emptyIsValid);
$this->share->setEmailAddress($emailAddress);
$share->setEmailAddress($emailAddress);
// TODO: Send confirmation
$this->share = $this->shareMapper->update($this->share);
$share = $this->shareMapper->update($share);
} else {
throw new InvalidShareTypeException('Email address can only be set in external shares.');
}
$this->eventDispatcher->dispatchTyped(new ShareChangedEmailEvent($this->share));
$this->eventDispatcher->dispatchTyped(new ShareChangedEmailEvent($share));
return $this->share;
return $share;
}
/**
@ -373,15 +243,11 @@ class ShareService {
*
* @return Share
*/
public function setDisplayName(string $token, string $displayName): Share {
try {
$this->share = $this->shareMapper->findByToken($token);
} catch (DoesNotExistException $e) {
throw new NotFoundException('Token ' . $token . ' does not exist');
}
public function setDisplayName(Share $share, string $displayName): Share {
$this->share = $share;
if ($this->share->getType() === Share::TYPE_EXTERNAL) {
$this->systemService->validatePublicUsername($displayName, $token);
$this->systemService->validatePublicUsername($displayName, $this->share);
$this->share->setDisplayName($displayName);
// TODO: Send confirmation
$this->share = $this->shareMapper->update($this->share);
@ -395,85 +261,58 @@ class ShareService {
}
/**
* Delete emailAddress of personal share
*
* @return Share
* Delete emailAddress of the personal share
*/
public function deleteEmailAddress(string $token): Share {
try {
$this->share = $this->shareMapper->findByToken($token);
} catch (DoesNotExistException $e) {
throw new NotFoundException('Token ' . $token . ' does not exist');
}
if ($this->share->getType() === Share::TYPE_EXTERNAL) {
$this->share->setEmailAddress('');
return $this->shareMapper->update($this->share);
public function deleteEmailAddress(Share $share): Share {
if ($share->getType() === Share::TYPE_EXTERNAL) {
$share->setEmailAddress('');
return $this->shareMapper->update($share);
} else {
throw new InvalidShareTypeException('Email address can only be set in external shares.');
}
}
private function generatePublicUserId(string $token, string $prefix = 'ex_'): string {
$publicUserId = '';
while ($publicUserId === '') {
$publicUserId = $prefix . $this->secureRandom->generate(
8,
ISecureRandom::CHAR_DIGITS .
ISecureRandom::CHAR_LOWER .
ISecureRandom::CHAR_UPPER
);
try {
// make sure, the user id is unique
$this->systemService->validatePublicUsername($publicUserId, $token);
} catch (InvalidUsernameException $th) {
$publicUserId = '';
}
}
return $publicUserId;
}
/**
* Create a personal share from a public share
* or update an email share with the username
*
* @return Share
*/
public function register(string $token, string $userName, string $emailAddress = ''): Share {
try {
$this->share = $this->shareMapper->findByToken($token);
$poll = $this->pollMapper->find($this->share->getPollId());
} catch (DoesNotExistException $e) {
throw new NotFoundException('Token ' . $token . ' does not exist');
}
$this->systemService->validatePublicUsername($userName, $token);
public function register(
Share $share,
string $userName,
string $emailAddress = '',
string $language = '',
string $locale = '',
string $timeZone = ''
): Share {
$this->share = $share;
$this->systemService->validatePublicUsername($userName, $this->share);
if ($this->share->getPublicPollEmail() !== Share::EMAIL_DISABLED) {
$this->systemService->validateEmailAddress($emailAddress, $this->share->getPublicPollEmail() !== Share::EMAIL_MANDATORY);
}
$userId = $this->generatePublicUserId($token);
$userId = $this->generatePublicUserId();
if ($this->share->getType() === Share::TYPE_PUBLIC) {
// Create new external share for user, who entered the poll via public link,
// prevent invtation sending, when no email address is given
$this->createNewShare(
$this->share->getPollId(),
$this->userService->getUser(Share::TYPE_EXTERNAL, $userId, $userName, $emailAddress),
!$emailAddress
$this->userService->getUser(Share::TYPE_EXTERNAL, $userId, $userName, $emailAddress, $language, $locale, $timeZone),
!$emailAddress,
$timeZone
);
$this->eventDispatcher->dispatchTyped(new ShareRegistrationEvent($this->share));
} elseif ($this->share->getType() === Share::TYPE_EMAIL
|| $this->share->getType() === Share::TYPE_CONTACT) {
$this->userService->getUser(Share::TYPE_EXTERNAL, $userId, $userName, $emailAddress, $language, $locale, $timeZone);
// Convert email and contact shares to external share, if user registers
$this->share->setType(Share::TYPE_EXTERNAL);
$this->share->setUserId($userId);
$this->share->setDisplayName($userName);
$this->share->setTimeZoneName($timeZone);
$this->share->setLanguage($language);
// prepare for resending invitation to new email address
if ($emailAddress !== $this->share->getEmailAddress()) {
@ -527,11 +366,199 @@ class ShareService {
// $sentResult = ['sentMails' => [new User($share->getuserId())]];
// $this->shareService->setInvitationSent($token);
} elseif ($share->getType() === Share::TYPE_GROUP) {
foreach ($share->getUserObject()->getMembers() as $member) {
foreach ($this->userService->getUserFromShare($share)->getMembers() as $member) {
$this->notificationService->sendInvitation($share->getPollId(), $member->getId());
}
}
return $this->mailService->sendInvitation($token);
}
private function generatePublicUserId(string $prefix = 'ex_'): string {
$publicUserId = '';
while ($publicUserId === '') {
$publicUserId = $prefix . $this->secureRandom->generate(
8,
ISecureRandom::CHAR_DIGITS .
ISecureRandom::CHAR_LOWER .
ISecureRandom::CHAR_UPPER
);
try {
// make sure, the user id is unique
$this->systemService->validatePublicUsername($publicUserId, $this->share);
} catch (InvalidUsernameException $th) {
$publicUserId = '';
}
}
return $publicUserId;
}
/**
* convert this share to personal public (aka external) share
*/
private function convertToExternal(
UserBase $userGroup
): void {
$this->share->setType(Share::TYPE_EXTERNAL);
$this->share->setUserId($userGroup->getPublicId());
$this->share->setDisplayName($userGroup->getDisplayName());
$this->share->setTimeZoneName($userGroup->getTimeZoneName());
$this->share->setLanguage($userGroup->getLanguageCode());
// prepare for resending invitation to new email address
if ($userGroup->getEmailAddress() !== $this->share->getEmailAddress()) {
$this->share->setInvitationSent(0);
}
$this->share->setEmailAddress($userGroup->getEmailAddress());
}
/**
* Add share
*
* @return Share
*/
public function add(
int $pollId,
string $type,
string $userId = '',
string $displayName = '',
string $emailAddress = ''
): Share {
$this->acl->setPollId($pollId, Acl::PERMISSION_POLL_EDIT);
if ($type === UserBase::TYPE_PUBLIC) {
$this->acl->request(Acl::PERMISSION_PUBLIC_SHARES);
} else {
try {
$this->shareMapper->findByPollAndUser($pollId, $userId);
throw new ShareAlreadyExistsException;
} catch (MultipleObjectsReturnedException $e) {
throw new ShareAlreadyExistsException;
} catch (DoesNotExistException $e) {
// continue
}
}
$this->createNewShare($pollId, $this->userService->getUser($type, $userId, $displayName, $emailAddress));
$this->eventDispatcher->dispatchTyped(new ShareCreateEvent($this->share));
return $this->share;
}
private function sortByCategory(): void {
$sortedShares = [];
foreach (Share::TYPE_SORT_ARRAY as $shareType) {
$filteredShares = array_filter($this->shares, function ($share) use ($shareType) {
return $share->getType() === $shareType;
});
$sortedShares = array_merge($sortedShares, $filteredShares);
}
$this->shares = $sortedShares;
}
/**
* Validate if share type is allowed to be used in a public poll
* or is accessibale for use by the current user
*/
private function validateShareType(): void {
$currentUser = $this->userService->getCurrentUser();
switch ($this->share->getType()) {
case Share::TYPE_PUBLIC:
// public shares are always valid
break;
case Share::TYPE_USER:
if ($this->share->getUserId() !== $currentUser->getId()) {
// share is not valid for user
throw new NotAuthorizedException;
}
break;
case Share::TYPE_ADMIN:
if ($this->share->getUserId() !== $currentUser->getId()) {
// share is not valid for user
throw new NotAuthorizedException;
}
break;
case Share::TYPE_GROUP:
if (!$currentUser->getIsLoggedIn()) {
throw new NotAuthorizedException;
}
if (!$this->groupManager->isInGroup($this->share->getUserId(), $this->userId)) {
throw new NotAuthorizedException;
}
break;
case Share::TYPE_EMAIL:
break;
case Share::TYPE_EXTERNAL:
break;
default:
$this->logger->alert(json_encode('invalid share type ' . $this->share->getType()));
throw new NotAuthorizedException;
}
}
/**
* crate a new share
*/
private function createNewShare(
int $pollId,
UserBase $userGroup,
bool $preventInvitation = false,
string $timeZone = ''
): Share {
$preventInvitation = $userGroup->getType() === UserBase::TYPE_PUBLIC ?: $preventInvitation;
$token = $this->secureRandom->generate(
16,
ISecureRandom::CHAR_DIGITS .
ISecureRandom::CHAR_LOWER .
ISecureRandom::CHAR_UPPER
);
$this->share = new Share();
$this->share->setToken($token);
$this->share->setPollId($pollId);
$this->share->setType($userGroup->getType());
$this->share->setDisplayName($userGroup->getDisplayName());
$this->share->setInvitationSent($preventInvitation ? time() : 0);
$this->share->setEmailAddress($userGroup->getEmailAddress());
$this->share->setUserId($userGroup->getPublicId());
if (
$userGroup->getType() === UserBase::TYPE_USER
|| $userGroup->getType() === UserBase::TYPE_ADMIN
) {
$this->share = $this->shareMapper->insert($this->share);
return $this->share;
}
if ($userGroup->getType() === UserBase::TYPE_PUBLIC) {
$this->share->setUserId($token);
$this->share = $this->shareMapper->insert($this->share);
return $this->share;
}
// Convert user type contact to share type email
if ($userGroup->getType() === UserBase::TYPE_CONTACT) {
$this->share->setType(Share::TYPE_EMAIL);
$this->share->setUserId($userGroup->getEmailAddress());
$this->share = $this->shareMapper->insert($this->share);
return $this->share;
}
// user is created from public share. Store locale information for
// usage in server side actions, i.e. scheduled emails
if ($userGroup->getType() === UserBase::TYPE_EXTERNAL) {
$this->share->setLanguage($userGroup->getLanguageCode());
$this->share->setTimeZoneName($timeZone);
}
$this->share = $this->shareMapper->insert($this->share);
return $this->share;
}
}

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

@ -46,15 +46,9 @@ class SubscriptionService {
$this->acl = $acl;
}
public function get(?int $pollId = null, ?string $token = null): bool {
if ($token) {
$this->acl->setToken($token);
} else {
$this->acl->setPollId($pollId);
}
public function get(Acl $acl): bool {
try {
$this->subscriptionMapper->findByPollAndUser($this->acl->getPollId(), $this->acl->getUserId());
$this->subscriptionMapper->findByPollAndUser($acl->getPollId(), $acl->getUserId());
// Subscription exists
return true;
} catch (DoesNotExistException $e) {
@ -62,30 +56,17 @@ class SubscriptionService {
}
}
public function add(int $pollId, string $userId): void {
$subscription = new Subscription();
$subscription->setPollId($pollId);
$subscription->setUserId($userId);
$this->subscriptionMapper->insert($subscription);
}
public function set(bool $subscribed, ?int $pollId = null, ?string $token = null): bool {
if ($token) {
$this->acl->setToken($token);
} else {
$this->acl->setPollId($pollId);
}
public function set(bool $subscribed, Acl $acl): bool {
if (!$subscribed) {
try {
$subscription = $this->subscriptionMapper->findByPollAndUser($this->acl->getPollId(), $this->acl->getUserId());
$subscription = $this->subscriptionMapper->findByPollAndUser($acl->getPollId(), $acl->getUserId());
$this->subscriptionMapper->delete($subscription);
} catch (DoesNotExistException $e) {
// catch silently (assume already unsubscribed)
}
} else {
try {
$this->add($this->acl->getPollId(), $this->acl->getUserId());
$this->add($acl->getPollId(), $acl->getUserId());
} catch (UniqueConstraintViolationException $e) {
// deprecated NC22
// catch silently (assume already subscribed)
@ -98,4 +79,11 @@ class SubscriptionService {
}
return $subscribed;
}
private function add(int $pollId, string $userId): void {
$subscription = new Subscription();
$subscription->setPollId($pollId);
$subscription->setUserId($userId);
$this->subscriptionMapper->insert($subscription);
}
}

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

@ -23,19 +23,19 @@
namespace OCA\Polls\Service;
use OCA\Polls\Db\Share;
use OCA\Polls\Db\ShareMapper;
use OCA\Polls\Db\VoteMapper;
use OCA\Polls\Exceptions\TooShortException;
use OCA\Polls\Exceptions\InvalidUsernameException;
use OCA\Polls\Exceptions\InvalidEmailAddress;
use OCA\Polls\Exceptions\NotAuthorizedException;
use OCA\Polls\Helper\Container;
use OCA\Polls\Model\UserGroup\Circle;
use OCA\Polls\Model\UserGroup\Contact;
use OCA\Polls\Model\UserGroup\ContactGroup;
use OCA\Polls\Model\UserGroup\Email;
use OCA\Polls\Model\UserGroup\Group;
use OCA\Polls\Model\UserGroup\User;
use OCA\Polls\Model\Group\Circle;
use OCA\Polls\Model\User\Contact;
use OCA\Polls\Model\Group\ContactGroup;
use OCA\Polls\Model\User\Email;
use OCA\Polls\Model\Group\Group;
use OCA\Polls\Model\User\User;
use OCP\IUserManager;
class SystemService {
@ -144,14 +144,7 @@ class SystemService {
*
* @return true
*/
public function validatePublicUsername(string $userName, string $token): bool {
try {
$share = $this->shareMapper->findByToken($token);
} catch (\Exception $e) {
throw new NotAuthorizedException('Token invalid');
}
public function validatePublicUsername(string $userName, Share $share): bool {
if (!$userName) {
throw new TooShortException('Username must not be empty');
}

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

@ -23,19 +23,20 @@
namespace OCA\Polls\Service;
use OCA\Polls\Db\Share;
use OCA\Polls\Db\ShareMapper;
use OCA\Polls\Db\VoteMapper;
use OCA\Polls\Exceptions\Exception;
use OCA\Polls\Exceptions\InvalidShareTypeException;
use OCA\Polls\Model\UserGroup\Admin;
use OCA\Polls\Model\UserGroup\Circle;
use OCA\Polls\Model\UserGroup\Contact;
use OCA\Polls\Model\UserGroup\ContactGroup;
use OCA\Polls\Model\UserGroup\Email;
use OCA\Polls\Model\UserGroup\GenericUser;
use OCA\Polls\Model\UserGroup\Group;
use OCA\Polls\Model\UserGroup\User;
use OCA\Polls\Model\UserGroup\UserBase;
use OCA\Polls\Model\User\Admin;
use OCA\Polls\Model\Group\Circle;
use OCA\Polls\Model\Group\ContactGroup;
use OCA\Polls\Model\User\Contact;
use OCA\Polls\Model\User\Email;
use OCA\Polls\Model\User\GenericUser;
use OCA\Polls\Model\Group\Group;
use OCA\Polls\Model\User\User;
use OCA\Polls\Model\UserBase;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\Collaboration\Collaborators\ISearch;
use OCP\ISession;
@ -81,55 +82,58 @@ class UserService {
/**
* getCurrentUser - Get current user from userbase or from share in public polls
* @return Admin|Circle|Contact|ContactGroup|Email|GenericUser|Group|User
*/
public function getCurrentUser() {
public function getCurrentUser() : UserBase {
// If there is a valid user session, get current user from session
if ($this->userSession->getUser()) {
return $this->getUserFromShare($this->userSession->getUser()->getUID());
return new User($this->userSession->getUser()->getUID());
}
// Retrieve token and get current user from share
$token = $this->session->get('publicPollToken');
if ($token) {
return $this->getUserFromShare($token);
$share = $this->shareMapper->findByToken($token);
return $this->getUserFromShare($share);
}
throw new DoesNotExistException('User not found');
}
/**
* evaluateUser - Get user by name; and poll in case of a share user of the particulair poll
* evaluateUser - Get user by name and poll in case of a share user of the particulair poll
*/
public function evaluateUser(string $userId, int $pollId = 0): ?UserBase {
// if a user with this name exists, return from the user base
$user = $this->userManager->get($userId);
if ($user) {
return new User($userId);
}
// Otherwise get it from a share that belongs to the poll and return the share user
try {
$share = $this->shareMapper->findByPollAndUser($pollId, $userId);
return $this->getUser(
$share->getType(),
$share->getUserId(),
$share->getDisplayName(),
$share->getEmailAddress()
);
return $this->getUserFromShare($share);
} catch (Exception $e) {
return null;
}
}
/**
* Get participans of a poll as array of users
* @return Admin|Circle|Contact|ContactGroup|Email|GenericUser|Group|User
* Get participans of a poll as array of user objects
* @return array<array-key, mixed>
*/
public function getParticipants($pollId) : array {
$users = [];
// get the distict list of usernames from the votes
$participants = $this->voteMapper->findParticipantsByPoll($pollId);
foreach ($participants as &$participant) {
$user = $this->evaluateUser($participant->getUserId(), $pollId);
if ($user) {
// replace every entry with a user object
$users[] = $this->evaluateUser($participant->getUserId(), $pollId);
}
}
@ -140,19 +144,21 @@ class UserService {
* Create user from share
* @return Admin|Circle|Contact|ContactGroup|Email|GenericUser|Group|User
*/
public function getUserFromShare(string $token) {
$share = $this->shareMapper->findByToken($token);
public function getUserFromShare(Share $share) : UserBase {
return $this->getUser(
$share->getType(),
$share->getUserId(),
$share->getDisplayName(),
$share->getEmailAddress()
$share->getEmailAddress(),
$share->getLanguage(),
$share->getLocale(),
$share->getTimeZoneName()
);
}
/**
* search all possible sharees - use ISearch to respect autocomplete restrictions
* get a list of user objects from the backend matching the query string
*/
public function search(string $query = ''): array {
$items = [];
@ -162,6 +168,7 @@ class UserService {
IShare::TYPE_EMAIL
];
if (Circle::isEnabled() && class_exists('\OCA\Circles\ShareByCircleProvider')) {
// Add circles to the search, if app is enabled
$types[] = IShare::TYPE_CIRCLE;
}
@ -199,10 +206,10 @@ class UserService {
}
/**
* create a new user object
* create a new user object based on $type
* @return Circle|Contact|ContactGroup|Email|GenericUser|Group|User|Admin
*/
public function getUser(string $type, string $id, string $displayName = '', string $emailAddress = ''): UserBase {
public function getUser(string $type, string $id, string $displayName = '', string $emailAddress = '', ?string $language = '', string $locale = '', string $timeZoneName = ''): UserBase {
switch ($type) {
case Group::TYPE:
return new Group($id);
@ -217,11 +224,11 @@ class UserService {
case Admin::TYPE:
return new Admin($id);
case Email::TYPE:
return new Email($id, $displayName, $emailAddress);
return new Email($id, $displayName, $emailAddress, $language);
case UserBase::TYPE_PUBLIC:
return new GenericUser($id, UserBase::TYPE_PUBLIC);
case UserBase::TYPE_EXTERNAL:
return new GenericUser($id, UserBase::TYPE_EXTERNAL, $displayName, $emailAddress);
return new GenericUser($id, UserBase::TYPE_EXTERNAL, $displayName, $emailAddress, $language, $locale, $timeZoneName);
default:
throw new InvalidShareTypeException('Invalid user type (' . $type . ')');
}

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

@ -74,9 +74,9 @@ class VoteService {
*
* @return Vote[]
*/
public function list(?int $pollId, string $token = '') : array {
if ($token) {
$this->acl->setToken($token);
public function list(?int $pollId, ?Acl $acl = null) : array {
if ($acl) {
$this->acl = $acl;
} else {
$this->acl->setPollId($pollId);
}
@ -119,11 +119,12 @@ class VoteService {
/**
* Set vote
*/
public function set(int $optionId, string $setTo, string $token = ''): ?Vote {
public function set(int $optionId, string $setTo, ?Acl $acl = null): ?Vote {
$option = $this->optionMapper->find($optionId);
if ($token) {
$this->acl->setToken($token, Acl::PERMISSION_VOTE_EDIT, $option->getPollId());
if ($acl) {
$this->acl = $acl; // ->setToken($token, Acl::PERMISSION_VOTE_EDIT, $option->getPollId());
// $this->acl->setToken($token, Acl::PERMISSION_VOTE_EDIT, $option->getPollId());
} else {
$this->acl->setPollId($option->getPollId(), Acl::PERMISSION_VOTE_EDIT);
}
@ -161,9 +162,9 @@ class VoteService {
/**
* Remove user from poll
*/
public function delete(?int $pollId, ?string $userId, string $token = ''): string {
if ($token) {
$this->acl->setToken($token, Acl::PERMISSION_VOTE_EDIT);
public function delete(?int $pollId, ?string $userId, ?Acl $acl = null): string {
if ($acl) {
$this->acl = $acl;
$userId = $this->acl->getUserId();
$pollId = $this->acl->getPollId();
} else {

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

@ -89,6 +89,7 @@ const actions = {
headers: { Accept: 'application/json' },
userName: payload.userName,
emailAddress: payload.emailAddress,
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
})
if (payload.saveCookie && context.state.type === 'public') {

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

@ -40,7 +40,7 @@
<div v-if="acl.allowAddOptions && proposalsOpen && !closed" class="area__proposal">
<OptionProposals />
</div>
<div class="area__confirmation">
<div v-if="showConfirmationMail" class="area__confirmation">
<ActionSendConfirmedOptions />
</div>
@ -135,8 +135,13 @@ export default {
proposalsOpen: 'poll/proposalsOpen',
countHiddenParticipants: 'poll/countHiddenParticipants',
safeTable: 'poll/safeTable',
confirmedOptions: 'options/confirmed',
}),
showConfirmationMail() {
return this.acl.isOwner && this.closed && this.confirmedOptions.length > 0
},
/* eslint-disable-next-line vue/no-unused-properties */
windowTitle() {
return `${t('polls', 'Polls')} - ${this.poll.title}`