ILIAS  release_8 Revision v8.24
class.ilObjPoll.php
Go to the documentation of this file.
1<?php
2
3declare(strict_types=1);
4
26class ilObjPoll extends ilObject2
27{
28 protected \ILIAS\Notes\Service $notes;
29 protected int $access_type = 0;
30 protected int $access_begin = 0;
31 protected int $access_end = 0;
32 protected bool $access_visibility = false;
33 protected string $question = "";
34 protected string $image = "";
35 protected int $view_results = 0;
36 protected bool $period = false;
37 protected int $period_begin = 0;
38 protected int $period_end = 0;
39
40 // 4.5
41 protected int $max_number_answers = 1;
42 protected bool $result_sort_by_votes = false;
43 protected bool $mode_non_anonymous = false;
44 protected bool $show_comments = false;
45 protected int $show_results_as = 1;
46
47 public const VIEW_RESULTS_ALWAYS = 1;
48 public const VIEW_RESULTS_NEVER = 2;
49 public const VIEW_RESULTS_AFTER_VOTE = 3;
50 public const VIEW_RESULTS_AFTER_PERIOD = 4;
51
52 public const SHOW_RESULTS_AS_BARCHART = 1;
53 public const SHOW_RESULTS_AS_PIECHART = 2;
54
55 public function __construct(int $a_id = 0, bool $a_reference = true)
56 {
57 global $DIC;
58
59 $this->db = $DIC->database();
60 // default
61 $this->setViewResults(self::VIEW_RESULTS_AFTER_VOTE);
63 $this->setVotingPeriod(false);
64 $this->notes = $DIC->notes();
65
66 parent::__construct($a_id, $a_reference);
67 }
68
69 protected function initType(): void
70 {
71 $this->type = "poll";
72 }
73
74 public function setAccessType(int $a_value): void
75 {
76 $this->access_type = $a_value;
77 }
78
79 public function getAccessType(): int
80 {
81 return $this->access_type;
82 }
83
84 public function setAccessBegin(int $a_value): void
85 {
86 $this->access_begin = $a_value;
87 }
88
89 public function getAccessBegin(): int
90 {
92 }
93
94 public function setAccessEnd(int $a_value): void
95 {
96 $this->access_end = $a_value;
97 }
98
99 public function getAccessEnd(): int
100 {
101 return $this->access_end;
102 }
103
104 public function setAccessVisibility(bool $a_value): void
105 {
106 $this->access_visibility = $a_value;
107 }
108
109 public function getAccessVisibility(): bool
110 {
112 }
113
114 public function setQuestion(string $a_value): void
115 {
116 $this->question = $a_value;
117 }
118
119 public function getQuestion(): string
120 {
121 return $this->question;
122 }
123
124 public function setImage(string $a_value): void
125 {
126 $this->image = $a_value;
127 }
128
129 public function getImage(): string
130 {
131 return $this->image;
132 }
133
134 public function setViewResults(int $a_value): void
135 {
136 $this->view_results = $a_value;
137 }
138
139 public function getViewResults(): int
140 {
141 return $this->view_results;
142 }
143
144 public function setVotingPeriod(bool $a_value): void
145 {
146 $this->period = $a_value;
147 }
148
149 public function getVotingPeriod(): bool
150 {
151 return $this->period;
152 }
153
154 public function setVotingPeriodBegin(int $a_value): void
155 {
156 $this->period_begin = $a_value;
157 }
158
159 public function getVotingPeriodBegin(): int
160 {
161 return $this->period_begin;
162 }
163
164 public function setVotingPeriodEnd(int $a_value): void
165 {
166 $this->period_end = $a_value;
167 }
168
169 public function getVotingPeriodEnd(): int
170 {
171 return $this->period_end;
172 }
173
174 public function setMaxNumberOfAnswers(int $a_value): void
175 {
176 $this->max_number_answers = $a_value;
177 }
178
179 public function getMaxNumberOfAnswers(): int
180 {
182 }
183
184 public function setSortResultByVotes(bool $a_value): void
185 {
186 $this->result_sort_by_votes = $a_value;
187 }
188
189 public function getSortResultByVotes(): bool
190 {
192 }
193
194 public function setNonAnonymous(bool $a_value): void
195 {
196 $this->mode_non_anonymous = $a_value;
197 }
198
199 public function getNonAnonymous(): bool
200 {
202 }
203
204 public function setShowComments(bool $a_value): void
205 {
206 $this->show_comments = $a_value;
207 }
208
209 public function getShowComments(): bool
210 {
212 }
213
214 public function setShowResultsAs(int $a_value): void
215 {
216 $this->show_results_as = $a_value;
217 }
218
219 public function getShowResultsAs(): int
220 {
222 }
223
224 protected function doRead(): void
225 {
227
228 $set = $ilDB->query("SELECT * FROM il_poll" .
229 " WHERE id = " . $ilDB->quote($this->getId(), "integer"));
230 $row = $ilDB->fetchAssoc($set);
231 $this->setQuestion((string) ($row["question"] ?? ''));
232 $this->setImage((string) ($row["image"] ?? ''));
233 $this->setViewResults((int) ($row["view_results"] ?? self::VIEW_RESULTS_AFTER_VOTE));
234 $this->setVotingPeriod((bool) ($row["period"] ?? 0));
235 $this->setVotingPeriodBegin((int) ($row["period_begin"] ?? 0));
236 $this->setVotingPeriodEnd((int) ($row["period_end"] ?? 0));
237 $this->setMaxNumberOfAnswers((int) ($row["max_answers"] ?? 0));
238 $this->setSortResultByVotes((bool) ($row["result_sort"] ?? 0));
239 $this->setNonAnonymous((bool) ($row["non_anon"] ?? 0));
240 $this->setShowResultsAs((int) ($row["show_results_as"] ?? self::SHOW_RESULTS_AS_BARCHART));
241
242 // #14661
243 $this->setShowComments($this->notes->domain()->commentsActive($this->getId()));
244
245 if ($this->ref_id) {
246 $activation = ilObjectActivation::getItem($this->ref_id);
247 $this->setAccessType((int) ($activation["timing_type"] ?? ilObjectActivation::TIMINGS_DEACTIVATED));
249 // default entry values should not be loaded if not activated
250 $this->setAccessBegin((int) ($activation["timing_start"] ?? time()));
251 $this->setAccessEnd((int) ($activation["timing_end"] ?? time()));
252 $this->setAccessVisibility((bool) ($activation["visible"] ?? false));
253 }
254 }
255 }
256
257 protected function propertiesToDB(): array
258 {
259 return array(
260 "question" => array("text", $this->getQuestion()),
261 "image" => array("text", $this->getImage()),
262 "view_results" => array("integer", $this->getViewResults()),
263 "period" => array("integer", $this->getVotingPeriod()),
264 "period_begin" => array("integer", $this->getVotingPeriodBegin()),
265 "period_end" => array("integer", $this->getVotingPeriodEnd()),
266 "max_answers" => array("integer", $this->getMaxNumberOfAnswers()),
267 "result_sort" => array("integer", $this->getSortResultByVotes()),
268 "non_anon" => array("integer", $this->getNonAnonymous()),
269 "show_results_as" => array("integer", $this->getShowResultsAs()),
270 );
271 }
272
273 protected function doCreate(bool $clone_mode = false): void
274 {
276
277 if ($this->getId()) {
278 $fields = $this->propertiesToDB();
279 $fields["id"] = array("integer", $this->getId());
280
281 $ilDB->insert("il_poll", $fields);
282
283
284 // object activation default entry will be created on demand
285
286
287 // block handling
288 $block = new ilPollBlock();
289 $block->setType("poll");
290 $block->setContextObjId($this->getId());
291 $block->setContextObjType("poll");
292 $block->create();
293 }
294 }
295
296 protected function doUpdate(): void
297 {
299
300 if ($this->getId()) {
301 $fields = $this->propertiesToDB();
302
303 $ilDB->update(
304 "il_poll",
305 $fields,
306 array("id" => array("integer", $this->getId()))
307 );
308
309 // #14661
310 $this->notes->domain()->activateComments($this->getId(), $this->getShowComments());
311
312 if ($this->getRefId()) {
313 $activation = new ilObjectActivation();
314 $activation->setTimingType($this->getAccessType());
315 $activation->setTimingStart($this->getAccessBegin());
316 $activation->setTimingEnd($this->getAccessEnd());
317 $activation->toggleVisible($this->getAccessVisibility());
318 $activation->update($this->ref_id);
319 }
320 }
321 }
322
323 protected function doDelete(): void
324 {
326
327 if ($this->getId()) {
328 $this->deleteImage();
329 $this->deleteAllAnswers();
330
331 if ($this->ref_id) {
333 }
334
335 $ilDB->manipulate("DELETE FROM il_poll" .
336 " WHERE id = " . $ilDB->quote($this->id, "integer"));
337 }
338 }
339
340 protected function doCloneObject(ilObject2 $new_obj, int $a_target_id, ?int $a_copy_id = 0): void
341 {
342 assert($new_obj instanceof ilObjPoll);
343
344 // question/image
345 $new_obj->setQuestion($this->getQuestion());
346 $image = $this->getImageFullPath();
347 if ($image) {
348 $image = array("tmp_name" => $image,
349 "name" => $this->getImage());
350 $new_obj->uploadImage($image, true);
351 }
352
353 //copy online status if object is not the root copy object
354 $cp_options = ilCopyWizardOptions::_getInstance($a_copy_id);
355
356 if ($cp_options->isRootNode($this->getRefId())) {
357 $new_obj->setOfflineStatus(true);
358 }
359
360 $new_obj->setViewResults($this->getViewResults());
361 $new_obj->setShowComments($this->getShowComments());
362 $new_obj->setShowResultsAs($this->getShowResultsAs());
363 $new_obj->update();
364
365 // answers
366 $answers = $this->getAnswers();
367 if ($answers) {
368 foreach ($answers as $item) {
369 $new_obj->saveAnswer($item["answer"]);
370 }
371 }
372 }
373
374
375 //
376 // image
377 //
378
379 public function getImageFullPath(bool $a_as_thumb = false): ?string
380 {
381 $img = $this->getImage();
382 if ($img) {
383 $path = self::initStorage($this->id);
384 if (!$a_as_thumb) {
385 return $path . $img;
386 } else {
387 return $path . "thb_" . $img;
388 }
389 }
390
391 return null;
392 }
393
394 public function deleteImage(): void
395 {
396 if ($this->id) {
397 $storage = new ilFSStoragePoll($this->id);
398 $storage->delete();
399
400 $this->setImage("");
401 }
402 }
403
404 public static function initStorage(int $a_id, ?string $a_subdir = null): string
405 {
406 $storage = new ilFSStoragePoll($a_id);
407 $storage->create();
408
409 $path = $storage->getAbsolutePath() . "/";
410
411 if ($a_subdir) {
412 $path .= $a_subdir . "/";
413
414 if (!is_dir($path)) {
415 mkdir($path);
416 }
417 }
418
419 return $path;
420 }
421
422 public function uploadImage(array $a_upload, bool $a_clone = false): bool
423 {
424 if (!$this->id) {
425 return false;
426 }
427
428 $this->deleteImage();
429
430 // #10074
431 $name = (string) ($a_upload['name'] ?? '');
432 $tmp_name = (string) ($a_upload['tmp_name'] ?? '');
433 $clean_name = preg_replace("/[^a-zA-Z0-9\_\.\-]/", "", $name);
434
435 $path = self::initStorage($this->id);
436 $original = "org_" . $this->id . "_" . $clean_name;
437 $thumb = "thb_" . $this->id . "_" . $clean_name;
438 $processed = $this->id . "_" . $clean_name;
439
440 $success = false;
441 if (!$a_clone) {
442 $success = ilFileUtils::moveUploadedFile($tmp_name, $original, $path . $original);
443 } else {
444 $success = copy($tmp_name, $path . $original);
445 }
446 if ($success) {
447 chmod($path . $original, 0770);
448
449 // take quality 100 to avoid jpeg artefacts when uploading jpeg files
450 // taking only frame [0] to avoid problems with animated gifs
451 $original_file = ilShellUtil::escapeShellArg($path . $original);
452 $thumb_file = ilShellUtil::escapeShellArg($path . $thumb);
453 $processed_file = ilShellUtil::escapeShellArg($path . $processed);
454
455 // -geometry "100x100>" is escaped by -geometry "100x100>"
456 // re-replace ">" with ">"
457 $convert_100 = $original_file . "[0] -geometry \"100x100>\" -quality 100 PNG:" . $thumb_file;
458 $escaped_convert_100 = ilShellUtil::escapeShellCmd($convert_100);
459 $escaped_convert_100 = str_replace('-geometry "100x100>', '-geometry "100x100>', $escaped_convert_100);
460 ilShellUtil::execQuoted(PATH_TO_CONVERT, $escaped_convert_100);
461
462 $convert_300 = $original_file . "[0] -geometry \"" . self::getImageSize() . ">\" -quality 100 PNG:" . $processed_file;
463 $escaped_convert_300 = ilShellUtil::escapeShellCmd($convert_300);
464 $escaped_convert_300 = str_replace('-geometry "' . self::getImageSize() . '>"', '-geometry "' . self::getImageSize() . '>"', $escaped_convert_300);
465 ilShellUtil::execQuoted(PATH_TO_CONVERT, $escaped_convert_300);
466
467 $this->setImage($processed);
468 return true;
469 }
470 return false;
471 }
472
473 public static function getImageSize(): string
474 {
475 // :TODO:
476 return "300x300";
477 }
478
479
480 //
481 // Answer
482 //
483
484 public function getAnswers(): array
485 {
487
488 $res = [];
489
490 $sql = "SELECT * FROM il_poll_answer" .
491 " WHERE poll_id = " . $ilDB->quote($this->getId(), "integer") .
492 " ORDER BY pos ASC";
493 $set = $ilDB->query($sql);
494 while ($row = $ilDB->fetchAssoc($set)) {
495 $res[] = $row;
496 }
497 return $res;
498 }
499
500 public function getAnswer(int $a_id): array
501 {
503
504 $sql = "SELECT * FROM il_poll_answer" .
505 " WHERE id = " . $ilDB->quote($a_id, "integer");
506 $set = $ilDB->query($sql);
507 return (array) $ilDB->fetchAssoc($set);
508 }
509
510 public function saveAnswer(string $a_text, ?int $a_pos = null): ?int
511 {
513
514 if (!trim($a_text)) {
515 return null;
516 }
517
518 $id = $ilDB->nextId("il_poll_answer");
519
520 if (!$a_pos) {
521 // append
522 $sql = "SELECT max(pos) pos" .
523 " FROM il_poll_answer" .
524 " WHERE poll_id = " . $ilDB->quote($this->getId(), "integer");
525 $set = $ilDB->query($sql);
526 $a_pos = $ilDB->fetchAssoc($set);
527 $a_pos = (int) ($a_pos["pos"] ?? 0) + 10;
528 }
529
530 $fields = array(
531 "id" => array("integer", $id),
532 "poll_id" => array("integer", $this->getId()),
533 "answer" => array("text", trim($a_text)),
534 "pos" => array("integer", $a_pos)
535 );
536 $ilDB->insert("il_poll_answer", $fields);
537
538 return $id;
539 }
540
541 public function updateAnswer(int $a_id, string $a_text): void
542 {
544
545 $ilDB->update(
546 "il_poll_answer",
547 array("answer" => array("text", $a_text)),
548 array("id" => array("integer", $a_id))
549 );
550 }
551
552 public function rebuildAnswerPositions(): void
553 {
554 $answers = $this->getAnswers();
555
556 $pos = [];
557 foreach ($answers as $item) {
558 $id = (int) ($item['id'] ?? 0);
559 $pos[$id] = (int) ($item["pos"] ?? 10);
560 }
561
562 $this->updateAnswerPositions($pos);
563 }
564
565 public function updateAnswerPositions(array $a_pos): void
566 {
568
569 asort($a_pos);
570
571 $pos = 0;
572 foreach (array_keys($a_pos) as $id) {
573 $pos += 10;
574
575 $ilDB->update(
576 "il_poll_answer",
577 array("pos" => array("integer", $pos)),
578 array("id" => array("integer", $id))
579 );
580 }
581 }
582
583 public function deleteAnswer(int $a_id): void
584 {
586
587 if ($a_id) {
588 $ilDB->manipulate("DELETE FROM il_poll_vote" .
589 " WHERE answer_id = " . $ilDB->quote($this->getId(), "integer"));
590
591 $ilDB->manipulate("DELETE FROM il_poll_answer" .
592 " WHERE id = " . $ilDB->quote($a_id, "integer"));
593 }
594 }
595
596 protected function deleteAllAnswers(): void
597 {
599
600 if ($this->getId()) {
601 $this->deleteAllVotes();
602
603 $ilDB->manipulate("DELETE FROM il_poll_answer" .
604 " WHERE poll_id = " . $ilDB->quote($this->getId(), "integer"));
605 }
606 }
607
608 public function deleteAllVotes(): void
609 {
611
612 if ($this->getId()) {
613 $ilDB->manipulate("DELETE FROM il_poll_vote" .
614 " WHERE poll_id = " . $ilDB->quote($this->getId(), "integer"));
615 }
616 }
617
618 public function saveAnswers(array $a_answers): int
619 {
620 $existing = $this->getAnswers();
621
622 $ids = [];
623 $pos = 0;
624 $id = null;
625 foreach ($a_answers as $answer) {
626 if (trim($answer)) {
627 // existing answer?
628 $found = false;
629 foreach ($existing as $idx => $item) {
630 if (trim($answer) === (string) ($item["answer"] ?? '')) {
631 $found = true;
632 unset($existing[$idx]);
633
634 $id = (int) ($item["id"] ?? 0);
635 }
636 }
637
638 // create new answer
639 if (!$found) {
640 $id = $this->saveAnswer($answer);
641 }
642
643 // add existing answer id to order
644 if (isset($id) && is_int($id)) {
645 $ids[$id] = ++$pos;
646 }
647 }
648 }
649
650 // remove obsolete answers
651 if (count($existing)) {
652 foreach ($existing as $item) {
653 if (isset($item["id"])) {
654 $this->deleteAnswer((int) $item["id"]);
655 }
656 }
657 }
658
659 // save current order
660 if (count($ids)) {
661 $this->updateAnswerPositions($ids);
662 }
663
664 return count($ids);
665 }
666
667
668 //
669 // votes
670 //
671
672 public function saveVote(int $a_user_id, array $a_answers): void
673 {
674 if ($this->hasUserVoted($a_user_id)) {
675 return;
676 }
677
678 foreach ($a_answers as $answer_id) {
679 $fields = array("user_id" => array("integer", $a_user_id),
680 "poll_id" => array("integer", $this->getId()),
681 "answer_id" => array("integer", $answer_id));
682 $this->db->insert("il_poll_vote", $fields);
683 }
684 }
685
686 public function hasUserVoted(int $a_user_id): bool
687 {
688 $sql = "SELECT user_id" .
689 " FROM il_poll_vote" .
690 " WHERE poll_id = " . $this->db->quote($this->getId(), "integer") .
691 " AND user_id = " . $this->db->quote($a_user_id, "integer");
692 $this->db->setLimit(1, 0);
693 $set = $this->db->query($sql);
694 return (bool) $this->db->numRows($set);
695 }
696
697 public function countVotes(): int
698 {
699 $sql = "SELECT COUNT(DISTINCT(user_id)) cnt" .
700 " FROM il_poll_vote" .
701 " WHERE poll_id = " . $this->db->quote($this->getId(), "integer");
702 $set = $this->db->query($sql);
703 $row = $this->db->fetchAssoc($set);
704 return (int) $row["cnt"];
705 }
706
707 public function getVotePercentages(): array
708 {
709 $res = [];
710 $cnt = 0;
711
712 $sql = "SELECT answer_id, count(*) cnt" .
713 " FROM il_poll_vote" .
714 " WHERE poll_id = " . $this->db->quote($this->getId(), "integer") .
715 " GROUP BY answer_id";
716 $set = $this->db->query($sql);
717 while ($row = $this->db->fetchAssoc($set)) {
718 $cnt += (int) $row["cnt"];
719 $res[(int) $row["answer_id"]] = array("abs" => (int) $row["cnt"], "perc" => 0);
720 }
721
722 foreach ($res as $id => $item) {
723 $abs = (int) ($item['abs'] ?? 0);
724 $id = (int) ($id ?? 0);
725 if ($cnt === 0) {
726 $res[$id]["perc"] = 0;
727 } else {
728 $res[$id]["perc"] = $abs / $cnt * 100;
729 }
730 }
731
732 return array("perc" => $res, "total" => $this->countVotes());
733 }
734
735 public function getVotesByUsers(): array
736 {
738
739 $res = [];
740
741 $sql = "SELECT answer_id, user_id, firstname, lastname, login" .
742 " FROM il_poll_vote" .
743 " JOIN usr_data ON (usr_data.usr_id = il_poll_vote.user_id)" .
744 " WHERE poll_id = " . $ilDB->quote($this->getId(), "integer");
745 $set = $ilDB->query($sql);
746 while ($row = $ilDB->fetchAssoc($set)) {
747 $user_id = (int) ($row["user_id"] ?? 0);
748 if (!isset($res[$user_id])) {
749 $res[$user_id] = $row;
750 }
751 $res[$user_id]["answers"][] = (int) ($row["answer_id"] ?? 0);
752 }
753
754 return $res;
755 }
756}
static _getInstance(int $a_copy_id)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static moveUploadedFile(string $a_file, string $a_name, string $a_target, bool $a_raise_errors=true, string $a_mode="move_uploaded")
move uploaded file
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setVotingPeriodBegin(int $a_value)
ILIAS Notes Service $notes
const VIEW_RESULTS_ALWAYS
const SHOW_RESULTS_AS_PIECHART
uploadImage(array $a_upload, bool $a_clone=false)
setViewResults(int $a_value)
const VIEW_RESULTS_AFTER_VOTE
hasUserVoted(int $a_user_id)
setVotingPeriodEnd(int $a_value)
updateAnswerPositions(array $a_pos)
setAccessBegin(int $a_value)
setImage(string $a_value)
setShowComments(bool $a_value)
saveVote(int $a_user_id, array $a_answers)
const SHOW_RESULTS_AS_BARCHART
static getImageSize()
bool $show_comments
saveAnswers(array $a_answers)
getImageFullPath(bool $a_as_thumb=false)
setNonAnonymous(bool $a_value)
const VIEW_RESULTS_AFTER_PERIOD
doCreate(bool $clone_mode=false)
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)
const VIEW_RESULTS_NEVER
bool $mode_non_anonymous
setShowResultsAs(int $a_value)
static initStorage(int $a_id, ?string $a_subdir=null)
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)
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
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static escapeShellArg(string $a_arg)
static escapeShellCmd(string $a_arg)
static execQuoted(string $cmd, ?string $args=null)
global $DIC
Definition: feed.php:28
$img
Definition: imgupload.php:83
$path
Definition: ltiservices.php:32
$res
Definition: ltiservices.php:69
if($format !==null) $name
Definition: metadata.php:247
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc