ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
class.ilObjPoll.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
23use ILIAS\Poll\Image\I\FactoryInterface as PollImageFactoryInterface;
24use ILIAS\Poll\Image\Factory as PollImageFactory;
25use ILIAS\Data\Factory as DataFactory;
26use ILIAS\Notes\Service as NotesService;
27
33class ilObjPoll extends ilObject2
34{
35 protected PollImageFactory $poll_image_factory;
36 protected NotesService $notes;
37 protected DataFactory $data_factory;
38 protected int $access_type = 0;
39 protected int $access_begin = 0;
40 protected int $access_end = 0;
41 protected bool $access_visibility = false;
42 protected string $question = "";
43 protected string $image = "";
44 protected int $view_results = 0;
45 protected bool $period = false;
46 protected int $period_begin = 0;
47 protected int $period_end = 0;
48
49 protected int $max_number_answers = 0;
50 protected bool $result_sort_by_votes = false;
51 protected bool $mode_non_anonymous = false;
52 protected bool $show_comments = false;
53 protected int $show_results_as = 1;
54
55 public const int VIEW_RESULTS_ALWAYS = 1;
56 public const int VIEW_RESULTS_NEVER = 2;
57 public const int VIEW_RESULTS_AFTER_VOTE = 3;
58 public const int VIEW_RESULTS_AFTER_PERIOD = 4;
59
60 public const int SHOW_RESULTS_AS_BARCHART = 1;
61 public const int SHOW_RESULTS_AS_STACKED_CHART = 2;
62
63 public function __construct(int $a_id = 0, bool $a_reference = true)
64 {
65 global $DIC;
66
67 $this->db = $DIC->database();
68 // default
69 $this->setViewResults(self::VIEW_RESULTS_AFTER_VOTE);
71 $this->setVotingPeriod(false);
72 $this->notes = $DIC->notes();
73 $this->poll_image_factory = new PollImageFactory();
74 $this->data_factory = new DataFactory();
75 parent::__construct($a_id, $a_reference);
76 }
77
78 protected function initType(): void
79 {
80 $this->type = "poll";
81 }
82
83 public function setAccessType(int $a_value): void
84 {
85 $this->access_type = $a_value;
86 }
87
88 public function getAccessType(): int
89 {
90 return $this->access_type;
91 }
92
93 public function setAccessBegin(int $a_value): void
94 {
95 $this->access_begin = $a_value;
96 }
97
98 public function getAccessBegin(): int
99 {
100 return $this->access_begin;
101 }
102
103 public function setAccessEnd(int $a_value): void
104 {
105 $this->access_end = $a_value;
106 }
107
108 public function getAccessEnd(): int
109 {
110 return $this->access_end;
111 }
112
113 public function setAccessVisibility(bool $a_value): void
114 {
115 $this->access_visibility = $a_value;
116 }
117
118 public function getAccessVisibility(): bool
119 {
121 }
122
123 public function setQuestion(string $a_value): void
124 {
125 $this->question = $a_value;
126 }
127
128 public function getQuestion(): string
129 {
130 return $this->question;
131 }
132
133 public function setViewResults(int $a_value): void
134 {
135 $this->view_results = $a_value;
136 }
137
138 public function getViewResults(): int
139 {
140 return $this->view_results;
141 }
142
143 public function setVotingPeriod(bool $a_value): void
144 {
145 $this->period = $a_value;
146 }
147
148 public function getVotingPeriod(): bool
149 {
150 return $this->period;
151 }
152
153 public function setVotingPeriodBegin(int $a_value): void
154 {
155 $this->period_begin = $a_value;
156 }
157
158 public function getVotingPeriodBegin(): int
159 {
160 return $this->period_begin;
161 }
162
163 public function setVotingPeriodEnd(int $a_value): void
164 {
165 $this->period_end = $a_value;
166 }
167
168 public function getVotingPeriodEnd(): int
169 {
170 return $this->period_end;
171 }
172
173 public function setMaxNumberOfAnswers(int $a_value): void
174 {
175 $this->max_number_answers = $a_value;
176 }
177
178 public function getMaxNumberOfAnswers(): int
179 {
181 }
182
183 public function setSortResultByVotes(bool $a_value): void
184 {
185 $this->result_sort_by_votes = $a_value;
186 }
187
188 public function getSortResultByVotes(): bool
189 {
191 }
192
193 public function setNonAnonymous(bool $a_value): void
194 {
195 $this->mode_non_anonymous = $a_value;
196 }
197
198 public function getNonAnonymous(): bool
199 {
201 }
202
203 public function setShowComments(bool $a_value): void
204 {
205 $this->show_comments = $a_value;
206 }
207
208 public function getShowComments(): bool
209 {
211 }
212
213 public function setShowResultsAs(int $a_value): void
214 {
215 $this->show_results_as = $a_value;
216 }
217
218 public function getShowResultsAs(): int
219 {
221 }
222
223 protected function doRead(): void
224 {
226
227 $set = $ilDB->query("SELECT * FROM il_poll" .
228 " WHERE id = " . $ilDB->quote($this->getId(), "integer"));
229 $row = $ilDB->fetchAssoc($set);
230 $this->setQuestion((string) ($row["question"] ?? ''));
231 $this->setViewResults((int) ($row["view_results"] ?? self::VIEW_RESULTS_AFTER_VOTE));
232 $this->setVotingPeriod((bool) ($row["period"] ?? 0));
233 $this->setVotingPeriodBegin((int) ($row["period_begin"] ?? 0));
234 $this->setVotingPeriodEnd((int) ($row["period_end"] ?? 0));
235 $this->setMaxNumberOfAnswers((int) ($row["max_answers"] ?? 0));
236 $this->setSortResultByVotes((bool) ($row["result_sort"] ?? 0));
237 $this->setNonAnonymous((bool) ($row["non_anon"] ?? 0));
238 $this->setShowResultsAs((int) ($row["show_results_as"] ?? self::SHOW_RESULTS_AS_BARCHART));
239
240 // #14661
241 $this->setShowComments($this->notes->domain()->commentsActive($this->getId()));
242
243 if ($this->ref_id) {
244 $activation = ilObjectActivation::getItem($this->ref_id);
245 $this->setAccessType((int) ($activation["timing_type"] ?? ilObjectActivation::TIMINGS_DEACTIVATED));
247 // default entry values should not be loaded if not activated
248 $this->setAccessBegin((int) ($activation["timing_start"] ?? time()));
249 $this->setAccessEnd((int) ($activation["timing_end"] ?? time()));
250 $this->setAccessVisibility((bool) ($activation["visible"] ?? false));
251 }
252 }
253 }
254
255 protected function propertiesToDB(): array
256 {
257 return array(
258 "question" => array("text", $this->getQuestion()),
259 "view_results" => array("integer", $this->getViewResults()),
260 "period" => array("integer", $this->getVotingPeriod()),
261 "period_begin" => array("integer", $this->getVotingPeriodBegin()),
262 "period_end" => array("integer", $this->getVotingPeriodEnd()),
263 "max_answers" => array("integer", $this->getMaxNumberOfAnswers()),
264 "result_sort" => array("integer", $this->getSortResultByVotes()),
265 "non_anon" => array("integer", $this->getNonAnonymous()),
266 "show_results_as" => array("integer", $this->getShowResultsAs()),
267 );
268 }
269
270 protected function doCreate(bool $clone_mode = false): void
271 {
273
274 if ($this->getId()) {
275 $fields = $this->propertiesToDB();
276 $fields["id"] = array("integer", $this->getId());
277 $fields["migrated"] = array("integer", "1");
278
279 $ilDB->insert("il_poll", $fields);
280
281
282 // object activation default entry will be created on demand
283
284
285 // block handling
286 $block = new ilPollBlock();
287 $block->setType("poll");
288 $block->setContextObjId($this->getId());
289 $block->setContextObjType("poll");
290 $block->create();
291 }
292 }
293
294 protected function doUpdate(): void
295 {
297
298 if ($this->getId()) {
299 $fields = $this->propertiesToDB();
300
301 $ilDB->update(
302 "il_poll",
303 $fields,
304 array("id" => array("integer", $this->getId()))
305 );
306
307 // #14661
308 $this->notes->domain()->activateComments($this->getId(), $this->getShowComments());
309
310 if ($this->getRefId()) {
311 $activation = new ilObjectActivation();
312 $activation->setTimingType($this->getAccessType());
313 $activation->setTimingStart($this->getAccessBegin());
314 $activation->setTimingEnd($this->getAccessEnd());
315 $activation->toggleVisible($this->getAccessVisibility());
316 $activation->update($this->ref_id);
317 }
318 }
319 }
320
321 protected function doDelete(): void
322 {
324
325 if ($this->getId()) {
326 $this->poll_image_factory->handler()->deleteImage(
327 $this->data_factory->objId($this->getId()),
328 $this->user->getId()
329 );
330 $this->deleteAllAnswers();
331
332 if ($this->ref_id) {
334 }
335
336 $ilDB->manipulate("DELETE FROM il_poll" .
337 " WHERE id = " . $ilDB->quote($this->id, "integer"));
338 }
339 }
340
341 protected function doCloneObject(ilObject2 $new_obj, int $a_target_id, ?int $a_copy_id = 0): void
342 {
343 assert($new_obj instanceof ilObjPoll);
344
345 // question/image
346 $new_obj->setQuestion($this->getQuestion());
347
348 $this->poll_image_factory->handler()->cloneImage(
349 $this->data_factory->objId($this->id),
350 $this->data_factory->objId($new_obj->getId()),
351 $this->user->getId()
352 );
353
354 //copy online status if object is not the root copy object
355 $cp_options = ilCopyWizardOptions::_getInstance($a_copy_id);
356
357 if ($cp_options->isRootNode($this->getRefId())) {
358 $new_obj->setOfflineStatus(true);
359 }
360
361 $view_results = $this->getViewResults();
362 if ($view_results === ilObjPoll::VIEW_RESULTS_AFTER_PERIOD) {
363 // default view results setting to always, since
364 // voting period is not copied.
366 }
367 $new_obj->setViewResults($view_results);
368 $new_obj->setShowComments($this->getShowComments());
369 $new_obj->setShowResultsAs($this->getShowResultsAs());
370 $new_obj->setMaxNumberOfAnswers($this->getMaxNumberOfAnswers());
371 $new_obj->setSortResultByVotes($this->getSortResultByVotes());
372 $new_obj->setNonAnonymous($this->getNonAnonymous());
373 $new_obj->update();
374
375 // answers
376 $answers = $this->getAnswers();
377 if ($answers) {
378 foreach ($answers as $item) {
379 $new_obj->saveAnswer($item["answer"]);
380 }
381 }
382 }
383
384 public function uploadImage(string $file_path, string $file_name): void
385 {
386 if (!$this->getId() or $file_path === "") {
387 return;
388 }
389 $this->poll_image_factory->handler()->uploadImage(
390 $this->data_factory->objId($this->getId()),
391 $file_path,
392 $file_name,
393 $this->user->getId()
394 );
395 }
396
397 public static function getImageSize(): string
398 {
399 // :TODO:
400 return "600x600";
401 }
402
403
404 //
405 // Answer
406 //
407
408 public function getAnswers(): array
409 {
411
412 $res = [];
413
414 $sql = "SELECT * FROM il_poll_answer" .
415 " WHERE poll_id = " . $ilDB->quote($this->getId(), "integer") .
416 " ORDER BY pos ASC";
417 $set = $ilDB->query($sql);
418 while ($row = $ilDB->fetchAssoc($set)) {
419 $res[] = $row;
420 }
421 return $res;
422 }
423
424 public function getAnswer(int $a_id): array
425 {
427
428 $sql = "SELECT * FROM il_poll_answer" .
429 " WHERE id = " . $ilDB->quote($a_id, "integer");
430 $set = $ilDB->query($sql);
431 return (array) $ilDB->fetchAssoc($set);
432 }
433
434 public function saveAnswer(string $a_text, ?int $a_pos = null): ?int
435 {
437
438 if (!trim($a_text)) {
439 return null;
440 }
441
442 $id = $ilDB->nextId("il_poll_answer");
443
444 if (!$a_pos) {
445 // append
446 $sql = "SELECT max(pos) pos" .
447 " FROM il_poll_answer" .
448 " WHERE poll_id = " . $ilDB->quote($this->getId(), "integer");
449 $set = $ilDB->query($sql);
450 $a_pos = $ilDB->fetchAssoc($set);
451 $a_pos = (int) ($a_pos["pos"] ?? 0) + 10;
452 }
453
454 $fields = array(
455 "id" => array("integer", $id),
456 "poll_id" => array("integer", $this->getId()),
457 "answer" => array("text", trim($a_text)),
458 "pos" => array("integer", $a_pos)
459 );
460 $ilDB->insert("il_poll_answer", $fields);
461
462 return $id;
463 }
464
465 public function updateAnswer(int $a_id, string $a_text): void
466 {
468
469 $ilDB->update(
470 "il_poll_answer",
471 array("answer" => array("text", $a_text)),
472 array("id" => array("integer", $a_id))
473 );
474 }
475
476 public function rebuildAnswerPositions(): void
477 {
478 $answers = $this->getAnswers();
479
480 $pos = [];
481 foreach ($answers as $item) {
482 $id = (int) ($item['id'] ?? 0);
483 $pos[$id] = (int) ($item["pos"] ?? 10);
484 }
485
486 $this->updateAnswerPositions($pos);
487 }
488
489 public function updateAnswerPositions(array $a_pos): void
490 {
492
493 asort($a_pos);
494
495 $pos = 0;
496 foreach (array_keys($a_pos) as $id) {
497 $pos += 10;
498
499 $ilDB->update(
500 "il_poll_answer",
501 array("pos" => array("integer", $pos)),
502 array("id" => array("integer", $id))
503 );
504 }
505 }
506
507 public function deleteAnswer(int $a_id): void
508 {
510
511 if ($a_id) {
512 $ilDB->manipulate("DELETE FROM il_poll_vote" .
513 " WHERE answer_id = " . $ilDB->quote($this->getId(), "integer"));
514
515 $ilDB->manipulate("DELETE FROM il_poll_answer" .
516 " WHERE id = " . $ilDB->quote($a_id, "integer"));
517 }
518 }
519
520 protected function deleteAllAnswers(): void
521 {
523
524 if ($this->getId()) {
525 $this->deleteAllVotes();
526
527 $ilDB->manipulate("DELETE FROM il_poll_answer" .
528 " WHERE poll_id = " . $ilDB->quote($this->getId(), "integer"));
529 }
530 }
531
532 public function deleteAllVotes(): void
533 {
535
536 if ($this->getId()) {
537 $ilDB->manipulate("DELETE FROM il_poll_vote" .
538 " WHERE poll_id = " . $ilDB->quote($this->getId(), "integer"));
539 }
540 }
541
542 public function saveAnswers(array $a_answers): int
543 {
544 $existing = $this->getAnswers();
545
546 $ids = [];
547 $pos = 0;
548 $id = null;
549 foreach ($a_answers as $answer) {
550 if (trim($answer)) {
551 // existing answer?
552 $found = false;
553 foreach ($existing as $idx => $item) {
554 if (trim($answer) === (string) ($item["answer"] ?? '')) {
555 $found = true;
556 unset($existing[$idx]);
557
558 $id = (int) ($item["id"] ?? 0);
559 }
560 }
561
562 // create new answer
563 if (!$found) {
564 $id = $this->saveAnswer($answer);
565 }
566
567 // add existing answer id to order
568 if (isset($id) && is_int($id)) {
569 $ids[$id] = ++$pos;
570 }
571 }
572 }
573
574 // remove obsolete answers
575 if (count($existing)) {
576 foreach ($existing as $item) {
577 if (isset($item["id"])) {
578 $this->deleteAnswer((int) $item["id"]);
579 }
580 }
581 }
582
583 // save current order
584 if (count($ids)) {
585 $this->updateAnswerPositions($ids);
586 }
587
588 return count($ids);
589 }
590
591
592 //
593 // votes
594 //
595
596 public function saveVote(int $a_user_id, array $a_answers): void
597 {
598 if ($this->hasUserVoted($a_user_id)) {
599 return;
600 }
601
602 foreach ($a_answers as $answer_id) {
603 $fields = array("user_id" => array("integer", $a_user_id),
604 "poll_id" => array("integer", $this->getId()),
605 "answer_id" => array("integer", $answer_id));
606 $this->db->insert("il_poll_vote", $fields);
607 }
608 }
609
610 public function hasUserVoted(int $a_user_id): bool
611 {
612 $sql = "SELECT user_id" .
613 " FROM il_poll_vote" .
614 " WHERE poll_id = " . $this->db->quote($this->getId(), "integer") .
615 " AND user_id = " . $this->db->quote($a_user_id, "integer");
616 $this->db->setLimit(1, 0);
617 $set = $this->db->query($sql);
618 return (bool) $this->db->numRows($set);
619 }
620
621 public function countVotes(): int
622 {
623 $sql = "SELECT COUNT(DISTINCT(user_id)) cnt" .
624 " FROM il_poll_vote" .
625 " WHERE poll_id = " . $this->db->quote($this->getId(), "integer");
626 $set = $this->db->query($sql);
627 $row = $this->db->fetchAssoc($set);
628 return (int) $row["cnt"];
629 }
630
631 public function getVotePercentages(): array
632 {
633 $res = [];
634 $cnt = 0;
635
636 $sql = "SELECT answer_id, count(*) cnt" .
637 " FROM il_poll_vote" .
638 " WHERE poll_id = " . $this->db->quote($this->getId(), "integer") .
639 " GROUP BY answer_id";
640 $set = $this->db->query($sql);
641 while ($row = $this->db->fetchAssoc($set)) {
642 $cnt += (int) $row["cnt"];
643 $res[(int) $row["answer_id"]] = array("abs" => (int) $row["cnt"], "perc" => 0);
644 }
645
646 foreach ($res as $id => $item) {
647 $abs = (int) ($item['abs'] ?? 0);
648 $id = (int) ($id ?? 0);
649 if ($cnt === 0) {
650 $res[$id]["perc"] = 0;
651 } else {
652 $res[$id]["perc"] = $abs / $cnt * 100;
653 }
654 }
655
656 return array("perc" => $res, "total" => $this->countVotes());
657 }
658
659 public function getVotesByUsers(): array
660 {
662
663 $res = [];
664
665 $sql = "SELECT answer_id, user_id, firstname, lastname, login" .
666 " FROM il_poll_vote" .
667 " JOIN usr_data ON (usr_data.usr_id = il_poll_vote.user_id)" .
668 " WHERE poll_id = " . $ilDB->quote($this->getId(), "integer");
669 $set = $ilDB->query($sql);
670 while ($row = $ilDB->fetchAssoc($set)) {
671 $user_id = (int) ($row["user_id"] ?? 0);
672 if (!isset($res[$user_id])) {
673 $res[$user_id] = $row;
674 }
675 $res[$user_id]["answers"][] = (int) ($row["answer_id"] ?? 0);
676 }
677
678 return $res;
679 }
680}
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
Builds data types.
Definition: Factory.php:36
static _getInstance(int $a_copy_id)
Class ilObjPoll.
setVotingPeriodBegin(int $a_value)
PollImageFactory $poll_image_factory
const int VIEW_RESULTS_AFTER_VOTE
const int VIEW_RESULTS_ALWAYS
setViewResults(int $a_value)
const int SHOW_RESULTS_AS_STACKED_CHART
hasUserVoted(int $a_user_id)
setVotingPeriodEnd(int $a_value)
updateAnswerPositions(array $a_pos)
setAccessBegin(int $a_value)
setShowComments(bool $a_value)
saveVote(int $a_user_id, array $a_answers)
static getImageSize()
bool $show_comments
saveAnswers(array $a_answers)
setNonAnonymous(bool $a_value)
doCreate(bool $clone_mode=false)
const int VIEW_RESULTS_NEVER
const int VIEW_RESULTS_AFTER_PERIOD
doCloneObject(ilObject2 $new_obj, int $a_target_id, ?int $a_copy_id=0)
deleteAnswer(int $a_id)
setAccessType(int $a_value)
getAnswer(int $a_id)
bool $mode_non_anonymous
NotesService $notes
setShowResultsAs(int $a_value)
uploadImage(string $file_path, string $file_name)
const int SHOW_RESULTS_AS_BARCHART
saveAnswer(string $a_text, ?int $a_pos=null)
setVotingPeriod(bool $a_value)
int $max_number_answers
string $question
updateAnswer(int $a_id, string $a_text)
DataFactory $data_factory
bool $result_sort_by_votes
__construct(int $a_id=0, bool $a_reference=true)
Constructor.
setMaxNumberOfAnswers(int $a_value)
setAccessEnd(int $a_value)
setQuestion(string $a_value)
setSortResultByVotes(bool $a_value)
int $show_results_as
bool $access_visibility
setAccessVisibility(bool $a_value)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Class ilObjectActivation.
static getItem(int $ref_id)
static deleteAllEntries(int $ref_id)
Delete all db entries for ref id.
setOfflineStatus(bool $status)
ilDBInterface $db
Custom block for polls.
$res
Definition: ltiservices.php:69
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc
global $DIC
Definition: shib_login.php:26