3 namespace wcf\data\poll
;
5 use wcf\data\AbstractDatabaseObjectAction
;
6 use wcf\data\IGroupedUserListAction
;
7 use wcf\data\
object\type\ObjectTypeCache
;
8 use wcf\data\poll\option\PollOptionEditor
;
9 use wcf\data\poll\option\PollOptionList
;
10 use wcf\system\exception\PermissionDeniedException
;
11 use wcf\system\exception\UserInputException
;
12 use wcf\system\poll\PollManager
;
13 use wcf\system\user\GroupedUserList
;
17 * Executes poll-related actions.
19 * @author Alexander Ebert
20 * @copyright 2001-2019 WoltLab GmbH
21 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
22 * @package WoltLabSuite\Core\Data\Poll
24 * @method PollEditor[] getObjects()
25 * @method PollEditor getSingleObject()
27 class PollAction
extends AbstractDatabaseObjectAction
implements IGroupedUserListAction
32 protected $allowGuestAccess = ['getGroupedUserList'];
37 protected $className = PollEditor
::class;
49 public function create()
51 if (!isset($this->parameters
['data']['time'])) {
52 $this->parameters
['data']['time'] = TIME_NOW
;
55 /** @var Poll $poll */
56 $poll = parent
::create();
59 $sql = "INSERT INTO wcf" . WCF_N
. "_poll_option
60 (pollID, optionValue, showOrder)
62 $statement = WCF
::getDB()->prepareStatement($sql);
64 WCF
::getDB()->beginTransaction();
65 foreach ($this->parameters
['options'] as $showOrder => $option) {
68 $option['optionValue'],
72 WCF
::getDB()->commitTransaction();
80 public function update()
85 $pollEditor = \reset
($this->objects
);
87 // get current options
88 $optionList = new PollOptionList();
89 $optionList->getConditionBuilder()->add("poll_option.pollID = ?", [$pollEditor->pollID
]);
90 $optionList->sqlOrderBy
= "poll_option.showOrder ASC";
91 $optionList->readObjects();
92 $options = $optionList->getObjects();
94 $newOptions = $updateOptions = [];
95 foreach ($this->parameters
['options'] as $showOrder => $option) {
96 // check if editing an existing option
97 if ($option['optionID']) {
98 // check if an update is required
99 if ($options[$option['optionID']]->showOrder
!= $showOrder ||
$options[$option['optionID']]->optionValue
!= $option['optionValue']) {
100 $updateOptions[$option['optionID']] = [
101 'optionValue' => $option['optionValue'],
102 'showOrder' => $showOrder,
107 unset($options[$option['optionID']]);
110 'optionValue' => $option['optionValue'],
111 'showOrder' => $showOrder,
116 if (!empty($newOptions) ||
!empty($updateOptions) ||
!empty($options)) {
117 WCF
::getDB()->beginTransaction();
119 // check if new options should be created
120 if (!empty($newOptions)) {
121 $sql = "INSERT INTO wcf" . WCF_N
. "_poll_option
122 (pollID, optionValue, showOrder)
124 $statement = WCF
::getDB()->prepareStatement($sql);
125 foreach ($newOptions as $option) {
126 $statement->execute([
128 $option['optionValue'],
129 $option['showOrder'],
134 // check if existing options should be updated
135 if (!empty($updateOptions)) {
136 $sql = "UPDATE wcf" . WCF_N
. "_poll_option
140 $statement = WCF
::getDB()->prepareStatement($sql);
141 foreach ($updateOptions as $optionID => $option) {
142 $statement->execute([
143 $option['optionValue'],
144 $option['showOrder'],
150 // check if options should be removed
151 if (!empty($options)) {
152 $sql = "DELETE FROM wcf" . WCF_N
. "_poll_option
154 $statement = WCF
::getDB()->prepareStatement($sql);
155 foreach ($options as $option) {
156 $statement->execute([$option->optionID
]);
160 // force recalculation of poll stats
161 $pollEditor->calculateVotes();
163 WCF
::getDB()->commitTransaction();
168 * Executes a user's vote.
170 public function vote()
172 $poll = \
current($this->objects
);
175 $sql = "SELECT optionID
176 FROM wcf" . WCF_N
. "_poll_option_vote
179 $statement = WCF
::getDB()->prepareStatement($sql);
180 $statement->execute([
182 WCF
::getUser()->userID
,
184 $optionIDs = $statement->fetchAll(\PDO
::FETCH_COLUMN
);
185 $alreadyVoted = !empty($optionIDs);
187 // calculate the difference
188 foreach ($this->parameters
['optionIDs'] as $index => $optionID) {
189 $optionsIndex = \array_search
($optionID, $optionIDs);
190 if ($optionsIndex !== false) {
191 // ignore this option
192 unset($this->parameters
['optionIDs'][$index]);
193 unset($optionIDs[$optionsIndex]);
197 // insert new vote options
198 if (!empty($this->parameters
['optionIDs'])) {
199 $sql = "INSERT INTO wcf" . WCF_N
. "_poll_option_vote
200 (pollID, optionID, userID)
202 $statement = WCF
::getDB()->prepareStatement($sql);
203 foreach ($this->parameters
['optionIDs'] as $optionID) {
204 $statement->execute([
207 WCF
::getUser()->userID
,
211 // increase votes per option
212 $sql = "UPDATE wcf" . WCF_N
. "_poll_option
213 SET votes = votes + 1
215 $statement = WCF
::getDB()->prepareStatement($sql);
216 foreach ($this->parameters
['optionIDs'] as $optionID) {
217 $statement->execute([$optionID]);
221 // remove previous options
222 if (!empty($optionIDs)) {
223 $sql = "DELETE FROM wcf" . WCF_N
. "_poll_option_vote
226 $statement = WCF
::getDB()->prepareStatement($sql);
227 foreach ($optionIDs as $optionID) {
228 $statement->execute([
230 WCF
::getUser()->userID
,
234 // decrease votes per option
235 $sql = "UPDATE wcf" . WCF_N
. "_poll_option
236 SET votes = votes - 1
238 $statement = WCF
::getDB()->prepareStatement($sql);
239 foreach ($optionIDs as $optionID) {
240 $statement->execute([$optionID]);
244 // increase poll votes
245 if (!$alreadyVoted) {
246 $poll->increaseVotes();
253 public function validateGetGroupedUserList()
255 $this->readInteger('pollID');
258 $this->poll
= new Poll($this->parameters
['pollID']);
259 if (!$this->poll
->pollID
) {
260 throw new UserInputException('pollID');
261 } elseif (!$this->poll
->canViewParticipants()) {
262 throw new PermissionDeniedException();
267 * Validates the 'getResultTemplate' method.
269 * @throws UserInputException If not exactly one valid poll is given.
270 * @throws PermissionDeniedException If the current user cannot see the result of the poll.
273 public function validateGetResultTemplate(): void
275 $this->poll
= $this->getSingleObject();
276 $this->loadRelatedObject();
278 if (!$this->poll
->pollID
) {
279 throw new UserInputException('pollID');
282 if (!$this->poll
->canSeeResult()) {
283 throw new PermissionDeniedException();
288 * Returns the result template for a specific poll.
292 public function getResultTemplate(): array
294 \assert
($this->poll
instanceof PollEditor
);
297 'template' => WCF
::getTPL()->fetch('pollResult', 'wcf', [
298 'poll' => $this->poll
->getDecoratedObject(),
304 * Validates the 'getVoteTemplate' method.
306 * @throws UserInputException If not exactly one valid poll is given.
307 * @throws PermissionDeniedException If the current user cannot vote the poll.
310 public function validateGetVoteTemplate(): void
312 $this->poll
= $this->getSingleObject();
313 $this->loadRelatedObject();
315 if (!$this->poll
->pollID
) {
316 throw new UserInputException('pollID');
319 if (!$this->poll
->canVote()) {
320 throw new PermissionDeniedException();
325 * Returns the result template for a specific poll.
329 public function getVoteTemplate(): array
332 'template' => WCF
::getTPL()->fetch('pollVote', 'wcf', [
333 'poll' => $this->poll
,
338 private function loadRelatedObject(): void
340 \assert
($this->poll
instanceof PollEditor
);
342 $relatedObject = PollManager
::getInstance()->getRelatedObject($this->poll
->getDecoratedObject());
344 $this->poll
->setRelatedObject($relatedObject);
350 public function getGroupedUserList()
353 $sql = "SELECT optionID, optionValue
354 FROM wcf" . WCF_N
. "_poll_option
356 ORDER BY " . ($this->poll
->sortByVotes ?
"votes DESC" : "showOrder ASC");
357 $statement = WCF
::getDB()->prepareStatement($sql);
358 $statement->execute([$this->poll
->pollID
]);
360 while ($row = $statement->fetchArray()) {
361 $options[$row['optionID']] = new GroupedUserList($row['optionValue'], 'wcf.poll.noVotes');
365 $sql = "SELECT userID, optionID
366 FROM wcf" . WCF_N
. "_poll_option_vote
368 $statement = WCF
::getDB()->prepareStatement($sql);
369 $statement->execute([$this->poll
->pollID
]);
370 $voteData = $statement->fetchMap('optionID', 'userID', false);
373 foreach ($voteData as $optionID => $userIDs) {
374 $options[$optionID]->addUserIDs($userIDs);
377 // load user profiles
378 GroupedUserList
::loadUsers();
380 WCF
::getTPL()->assign([
381 'groupedUsers' => $options,
386 'template' => WCF
::getTPL()->fetch('groupedUserList'),
391 * Copies a poll from one object id to another.
393 public function copy()
395 $sourceObjectType = ObjectTypeCache
::getInstance()->getObjectTypeByName(
396 'com.woltlab.wcf.poll',
397 $this->parameters
['sourceObjectType']
399 $targetObjectType = ObjectTypeCache
::getInstance()->getObjectTypeByName(
400 'com.woltlab.wcf.poll',
401 $this->parameters
['targetObjectType']
410 FROM wcf" . WCF_N
. "_poll
411 WHERE objectTypeID = ?
413 $statement = WCF
::getDB()->prepareStatement($sql);
414 $statement->execute([
415 $sourceObjectType->objectTypeID
,
416 $this->parameters
['sourceObjectID'],
418 $row = $statement->fetchArray();
420 if ($row === false) {
427 $pollOptionList = new PollOptionList();
428 $pollOptionList->getConditionBuilder()->add("poll_option.pollID = ?", [$row['pollID']]);
429 $pollOptionList->readObjects();
437 $pollData['objectTypeID'] = $targetObjectType->objectTypeID
;
438 $pollData['objectID'] = $this->parameters
['targetObjectID'];
439 unset($pollData['pollID']);
441 $newPoll = PollEditor
::create($pollData);
445 foreach ($pollOptionList as $pollOption) {
446 $newOption = PollOptionEditor
::create([
447 'pollID' => $newPoll->pollID
,
448 'optionValue' => $pollOption->optionValue
,
449 'votes' => $pollOption->votes
,
450 'showOrder' => $pollOption->showOrder
,
453 $newOptionIDs[$pollOption->optionID
] = $newOption->optionID
;
457 WCF
::getDB()->beginTransaction();
458 foreach ($newOptionIDs as $oldOptionID => $newOptionID) {
459 $sql = "INSERT INTO wcf" . WCF_N
. "_poll_option_vote
460 (pollID, optionID, userID)
461 SELECT " . $newPoll->pollID
. ", " . $newOptionID . ", userID
462 FROM wcf" . WCF_N
. "_poll_option_vote
464 $statement = WCF
::getDB()->prepareStatement($sql);
465 $statement->execute([$oldOptionID]);
467 WCF
::getDB()->commitTransaction();
470 'pollID' => $newPoll->pollID
,