ILIAS  release_8 Revision v8.24
class.ilObjSurvey.php
Go to the documentation of this file.
1<?php
2
21
25class ilObjSurvey extends ilObject
26{
27 public const EVALUATION_ACCESS_OFF = "0";
28 public const EVALUATION_ACCESS_ALL = "1";
30
31 public const ANONYMIZE_OFF = 0; // personalized, no codes
32 public const ANONYMIZE_ON = 1; // anonymized, codes
33 public const ANONYMIZE_FREEACCESS = 2; // anonymized, no codes
34 public const ANONYMIZE_CODE_ALL = 3; // personalized, codes
35
36 public const QUESTIONTITLES_HIDDEN = 0;
37 public const QUESTIONTITLES_VISIBLE = 1;
38
39 // constants to define the print view values.
40 public const PRINT_HIDE_LABELS = 1; // Show only the titles in print view
41 public const PRINT_SHOW_LABELS = 3; // Show titles and labels in print view
42
43 //MODE TYPES
44 public const MODE_STANDARD = 0;
45 public const MODE_360 = 1;
46 public const MODE_SELF_EVAL = 2;
47 public const MODE_IND_FEEDB = 3;
48
49 //self evaluation only access to results
50 public const RESULTS_SELF_EVAL_NONE = 0;
51 public const RESULTS_SELF_EVAL_OWN = 1;
52 public const RESULTS_SELF_EVAL_ALL = 2;
53
54 public const RESULTS_360_NONE = 0;
55 public const RESULTS_360_OWN = 1;
56 public const RESULTS_360_ALL = 2;
57
60 public const NOTIFICATION_APPRAISEES = 3;
61 public const NOTIFICATION_RATERS = 4;
63
64
65 protected ilLogger $svy_log;
66 protected bool $activation_limited = false;
67 protected ilObjUser $user;
70 protected bool $calculate_sum_score = false;
75 public int $survey_id = 0;
80 public string $author = "";
81 public string $introduction = "";
82 public string $outro = "";
83 // Indicates the evaluation access for learners
85 // The start date of the survey
86 public string $start_date = "";
87 // The end date of the survey
88 public string $end_date = "";
89 // The questions contained in this survey
90 public array $questions = [];
91 // Indicates the anonymization of the survey
92 public int $anonymize = 0;
93 // Indicates if the question titles are shown during a query
95 // Indicates if a survey code may be exported with the survey statistics
96 public bool $surveyCodeSecurity = false;
97
98 public bool $mailnotification = false;
99 public string $mailaddresses = "";
100 public string $mailparticipantdata = "";
101 public bool $pool_usage = false;
102
103 protected bool $activation_visibility = false;
104 protected ?int $activation_starting_time = null;
105 protected ?int $activation_ending_time = null;
106
107 // 360°
108 protected bool $mode_360_self_eval = false;
109 protected bool $mode_360_self_appr = false;
110 protected bool $mode_360_self_rate = false;
111 protected int $mode_360_results = 0;
112 protected bool $mode_skill_service = false;
113
114 // reminder/notification
115 protected bool $reminder_status = false;
116 protected ?ilDate $reminder_start = null;
117 protected ?ilDate $reminder_end = null;
118 protected int $reminder_frequency = 0;
119 protected int $reminder_target = 0;
120 protected ?string $reminder_last_sent = null;
121 protected ?int $reminder_tmpl = null;
122 protected bool $tutor_ntf_status = false;
123 protected array $tutor_ntf_recipients = [];
124 protected int $tutor_ntf_target = 0;
125 protected bool $tutor_res_status = false;
126 protected array $tutor_res_recipients = [];
127
128 protected bool $view_own_results = false;
129 protected bool $mail_own_results = false;
130 protected bool $mail_confirmation = false;
131
132 protected bool $anon_user_list = false;
133 protected int $mode = 0;
134 protected int $mode_self_eval_results = 0;
135
137 protected \ILIAS\Survey\InternalService $survey_service;
138 protected \ILIAS\Survey\Code\CodeManager $code_manager;
139 protected \ILIAS\Survey\InternalDataService $data_manager;
141 protected \ILIAS\SurveyQuestionPool\Export\ImportManager $import_manager;
142
143 public function __construct(
144 int $a_id = 0,
145 bool $a_call_by_reference = true
146 ) {
147 global $DIC;
148
149 $this->survey_service = $DIC->survey()->internal();
150
151 $this->user = $DIC->user();
152 $this->lng = $DIC->language();
153 $this->db = $DIC->database();
154 $this->access = $DIC->access();
155 $this->plugin_admin = $DIC["ilPluginAdmin"];
156 $this->tree = $DIC->repositoryTree();
157 $ilUser = $DIC->user();
158 $lng = $DIC->language();
159
160 $this->type = "svy";
161 $this->survey_id = -1;
162 $this->introduction = "";
163 $this->outro = $lng->txt("survey_finished");
164 $this->author = $ilUser->getFullname();
165 $this->evaluation_access = self::EVALUATION_ACCESS_OFF;
166 $this->questions = array();
167 $this->anonymize = self::ANONYMIZE_OFF;
168 $this->display_question_titles = self::QUESTIONTITLES_VISIBLE;
169 $this->surveyCodeSecurity = true;
170 $this->pool_usage = true;
171 $this->mode = self::MODE_STANDARD;
172 $this->mode_self_eval_results = self::RESULTS_SELF_EVAL_OWN;
173
174 $this->invitation_manager = $this
175 ->survey_service
176 ->domain()
177 ->participants()
178 ->invitations();
179
180 $this->import_manager = $DIC->surveyQuestionPool()
181 ->internal()
182 ->domain()
183 ->import();
184
185 parent::__construct($a_id, $a_call_by_reference);
186 $this->svy_log = ilLoggerFactory::getLogger("svy");
187 $this->initServices();
188 }
189
190 protected function initServices(): void
191 {
192 if ($this->getRefId() > 0) {
193 $this->code_manager = $this
194 ->survey_service
195 ->domain()
196 ->code($this, $this->user->getId());
197 }
198 $this->feature_config = $this
199 ->survey_service
200 ->domain()
201 ->modeFeatureConfig($this->getMode());
202
203 $this->data_manager = $this
204 ->survey_service
205 ->data();
206 }
207
208 public function create($a_upload = false): int
209 {
210 $id = parent::create();
211 if (!$a_upload) {
212 $this->createMetaData();
213 }
214 $this->setOfflineStatus(true);
215 $this->update($a_upload);
216 return $id;
217 }
218
219 protected function doCreateMetaData(): void
220 {
221 $this->saveAuthorToMetadata();
222 }
223
224 public function update($a_upload = false): bool
225 {
226 if (!$a_upload) {
227 $this->updateMetaData();
228 }
229
230 if (!parent::update()) {
231 return false;
232 }
233
234 // put here object specific stuff
235
236 return true;
237 }
238
239 public function createReference(): int
240 {
241 $result = parent::createReference();
242 $this->saveToDb();
243 return $result;
244 }
245
246 public function read(): void
247 {
248 parent::read();
249 $this->loadFromDb();
250 $this->initServices();
251 }
252
256 public function addQuestion(int $question_id): void
257 {
258 $this->questions[] = $question_id;
259 }
260
261 public function delete(): bool
262 {
263 $this->svy_log->debug("Deleting Survey, ref id: " . $this->getRefId() . ", obj id: " .
264 $this->getId() . ", title: " . $this->getTitle());
265 $this->svy_log->debug("References: " . $this->countReferences());
266 if ($this->countReferences() === 1) {
267 $this->deleteMetaData();
268
269 // Delete all survey questions, constraints and materials
270 foreach ($this->questions as $question_id) {
271 $this->svy_log->debug("Remove question " . $question_id);
272 $this->removeQuestion($question_id);
273 }
274 $this->deleteSurveyRecord();
275
277 }
278
279 $this->svy_log->debug("Call parent delete.");
280 $remove = parent::delete();
281
282 // always call parent delete function first!!
283 if (!$remove) {
284 return false;
285 }
286 return true;
287 }
288
293 public function deleteSurveyRecord(): void
294 {
296
297 $ilDB->manipulateF(
298 "DELETE FROM svy_svy WHERE survey_id = %s",
299 array('integer'),
300 array($this->getSurveyId())
301 );
302
303 $result = $ilDB->queryF(
304 "SELECT questionblock_fi FROM svy_qblk_qst WHERE survey_fi = %s",
305 array('integer'),
306 array($this->getSurveyId())
307 );
308 $questionblocks = array();
309 while ($row = $ilDB->fetchAssoc($result)) {
310 $questionblocks[] = $row["questionblock_fi"];
311 }
312 if (count($questionblocks)) {
313 $affectedRows = $ilDB->manipulate("DELETE FROM svy_qblk WHERE " . $ilDB->in('questionblock_id', $questionblocks, false, 'integer'));
314 }
315 $ilDB->manipulateF(
316 "DELETE FROM svy_qblk_qst WHERE survey_fi = %s",
317 array('integer'),
318 array($this->getSurveyId())
319 );
320 $this->deleteAllUserData(false);
321
322 $this->code_manager->deleteAll(true);
323
324 // delete export files
325 $svy_data_dir = ilFileUtils::getDataDir() . "/svy_data";
326 $directory = $svy_data_dir . "/svy_" . $this->getId();
327 if (is_dir($directory)) {
328 ilFileUtils::delDir($directory);
329 }
330
331 $mobs = ilObjMediaObject::_getMobsOfObject("svy:html", $this->getId());
332 // remaining usages are not in text anymore -> delete them
333 // and media objects (note: delete method of ilObjMediaObject
334 // checks whether object is used in another context; if yes,
335 // the object is not deleted!)
336 foreach ($mobs as $mob) {
337 ilObjMediaObject::_removeUsage($mob, "svy:html", $this->getId());
338 $mob_obj = new ilObjMediaObject($mob);
339 $mob_obj->delete();
340 }
341 }
342
348 public function deleteAllUserData(
349 bool $reset_LP = true
350 ): void {
351 $ilDB = $this->db;
352
353 $result = $ilDB->queryF(
354 "SELECT finished_id FROM svy_finished WHERE survey_fi = %s",
355 array('integer'),
356 array($this->getSurveyId())
357 );
358 $active_array = array();
359 while ($row = $ilDB->fetchAssoc($result)) {
360 $active_array[] = $row["finished_id"];
361 }
362
363 $affectedRows = $ilDB->manipulateF(
364 "DELETE FROM svy_finished WHERE survey_fi = %s",
365 array('integer'),
366 array($this->getSurveyId())
367 );
368
369 foreach ($active_array as $active_fi) {
370 $affectedRows = $ilDB->manipulateF(
371 "DELETE FROM svy_answer WHERE active_fi = %s",
372 array('integer'),
373 array($active_fi)
374 );
375 $affectedRows = $ilDB->manipulateF(
376 "DELETE FROM svy_times WHERE finished_fi = %s",
377 array('integer'),
378 array($active_fi)
379 );
380 }
381
382 if ($reset_LP) {
383 $lp_obj = ilObjectLP::getInstance($this->getId());
384 $lp_obj->resetLPDataForCompleteObject();
385 }
386
387 $this->invitation_manager->removeAll($this->getSurveyId());
388 }
389
395 array $finished_ids
396 ): void {
397 $ilDB = $this->db;
398
399 $user_ids = [];
400
401 foreach ($finished_ids as $finished_id) {
402 $result = $ilDB->queryF(
403 "SELECT finished_id, user_fi FROM svy_finished WHERE finished_id = %s",
404 array('integer'),
405 array($finished_id)
406 );
407 $row = $ilDB->fetchAssoc($result);
408 if ($row["user_fi"]) {
409 $user_ids[] = (int) $row["user_fi"];
410 }
411
412 $affectedRows = $ilDB->manipulateF(
413 "DELETE FROM svy_answer WHERE active_fi = %s",
414 array('integer'),
415 array($row["finished_id"])
416 );
417
418 $affectedRows = $ilDB->manipulateF(
419 "DELETE FROM svy_finished WHERE finished_id = %s",
420 array('integer'),
421 array($finished_id)
422 );
423
424 $affectedRows = $ilDB->manipulateF(
425 "DELETE FROM svy_times WHERE finished_fi = %s",
426 array('integer'),
427 array($row["finished_id"])
428 );
429 }
430
431 if (count($user_ids)) {
432 $lp_obj = ilObjectLP::getInstance($this->getId());
433 $lp_obj->resetLPDataForUserIds($user_ids);
434
435 // remove invitations, if exist
436 foreach ($user_ids as $user_id) {
437 $this->invitation_manager->remove($this->getSurveyId(), $user_id);
438 }
439 }
440 }
441
445 public function getSurveyParticipants(
446 ?array $finished_ids = null,
447 bool $force_non_anonymous = false,
448 bool $include_invites = false
449 ): array {
450 $ilDB = $this->db;
451 $sql = "SELECT * FROM svy_finished" .
452 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer");
453 if ($finished_ids) {
454 $sql .= " AND " . $ilDB->in("finished_id", $finished_ids, "", "integer");
455 }
456
457 $result = $ilDB->query($sql);
458 $participants = array();
459 if ($result->numRows() > 0) {
460 while ($row = $ilDB->fetchAssoc($result)) {
461 $userdata = $this->getUserDataFromActiveId($row["finished_id"], $force_non_anonymous);
462 $userdata["finished"] = (bool) $row["state"];
463 $userdata["finished_tstamp"] = $row["tstamp"];
464 $participants[$userdata["sortname"] . $userdata["active_id"]] = $userdata;
465 }
466 }
467 $participant_ids = array_column($participants, "usr_id");
468 if ($include_invites) {
469 foreach ($this->invitation_manager->getAllForSurvey($this->getSurveyId()) as $usr_id) {
470 if (!in_array($usr_id, $participant_ids)) {
471 $name = ilObjUser::_lookupName($usr_id);
472 $participants[$name["lastname"] . "," . $name["firstname"] . $usr_id] = [
473 "fullname" => ilObjUser::_lookupFullname($usr_id),
474 "sortname" => $name["lastname"] . "," . $name["firstname"],
475 "fistname" => $name["firstname"],
476 "lastname" => $name["lastname"],
477 "login" => $name["login"],
478 "gender" => "",
479 "usr_id" => $usr_id,
480 "finished" => false,
481 "finished_tstamp" => 0,
482 "invited" => true
483 ];
484 }
485 }
486 }
487 return $participants;
488 }
489
494 public function isComplete(): bool
495 {
496 return ($this->getTitle() && count($this->questions));
497 }
498
503 public function saveCompletionStatus(): void
504 {
505 $db = $this->db;
506 if ($this->getSurveyId() > 0) {
507 $db->manipulateF(
508 "UPDATE svy_svy SET complete = %s, tstamp = %s WHERE survey_id = %s",
509 array('text','integer','integer'),
510 array($this->isComplete(), time(), $this->getSurveyId())
511 );
512 }
513 }
514
520 int $question_id,
521 bool $a_force = false
522 ): int {
523 $questiontype = $this->getQuestionType($question_id);
524 $question_gui = $this->getQuestionGUI($questiontype, $question_id);
525
526 // check if question is a pool question at all, if not do nothing
527 if ($this->getId() === $question_gui->object->getObjId() && !$a_force) {
528 return $question_id;
529 }
530
531 $duplicate_id = $question_gui->object->duplicate(true, "", "", 0, $this->getId());
532 return $duplicate_id;
533 }
534
539 public function insertQuestion(
540 int $question_id
541 ): bool {
542 $ilDB = $this->db;
543
544 $this->svy_log->debug("insert question, id:" . $question_id);
545
546 if (!SurveyQuestion::_isComplete($question_id)) {
547 $this->svy_log->debug("question is not complete");
548 return false;
549 } else {
550 // get maximum sequence index in test
551 $result = $ilDB->queryF(
552 "SELECT survey_question_id FROM svy_svy_qst WHERE survey_fi = %s",
553 array('integer'),
554 array($this->getSurveyId())
555 );
556 $sequence = $result->numRows();
557 $duplicate_id = $this->duplicateQuestionForSurvey($question_id);
558 $this->svy_log->debug("duplicate, id: " . $question_id . ", duplicate id: " . $duplicate_id);
559
560 // check if question is not already in the survey, see #22018
561 if ($this->isQuestionInSurvey($duplicate_id)) {
562 return false;
563 }
564
565 $next_id = $ilDB->nextId('svy_svy_qst');
566 $affectedRows = $ilDB->manipulateF(
567 "INSERT INTO svy_svy_qst (survey_question_id, survey_fi, question_fi, sequence, tstamp) VALUES (%s, %s, %s, %s, %s)",
568 array('integer', 'integer', 'integer', 'integer', 'integer'),
569 array($next_id, $this->getSurveyId(), $duplicate_id, $sequence, time())
570 );
571
572 $this->svy_log->debug("added entry to svy_svy_qst, id: " . $next_id . ", question id: " . $duplicate_id . ", sequence: " . $sequence);
573
574 $this->loadQuestionsFromDb();
575 $this->saveCompletionStatus();
576 return true;
577 }
578 }
579
580 // Check if a question is already in the survey
581 public function isQuestionInSurvey(
582 int $a_question_fi
583 ): bool {
584 global $DIC;
585 //return false;
586 $ilDB = $DIC->database();
587
588 $set = $ilDB->query("SELECT * FROM svy_svy_qst " .
589 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") .
590 " AND question_fi = " . $ilDB->quote($a_question_fi, "integer"));
591 if ($rec = $ilDB->fetchAssoc($set)) {
592 return true;
593 }
594 return false;
595 }
596
597 // Inserts a questionblock in the survey
598 public function insertQuestionblock(
599 int $questionblock_id
600 ): void {
601
602 $sequence_manager = $this->survey_service->domain()->sequence(
603 $this->getSurveyId(),
604 $this
605 );
606
607
608 $ilDB = $this->db;
609 $result = $ilDB->queryF(
610 "SELECT svy_qblk.title, svy_qblk.show_questiontext, svy_qblk.show_blocktitle," .
611 " svy_qblk_qst.question_fi FROM svy_qblk, svy_qblk_qst, svy_svy_qst" .
612 " WHERE svy_qblk.questionblock_id = svy_qblk_qst.questionblock_fi" .
613 " AND svy_svy_qst.question_fi = svy_qblk_qst.question_fi" .
614 " AND svy_qblk.questionblock_id = %s" .
615 " ORDER BY svy_svy_qst.sequence",
616 array('integer'),
617 array($questionblock_id)
618 );
619 $questions = array();
620 $show_questiontext = false;
621 $show_blocktitle = false;
622 $title = "";
623 $this->svy_log->debug("insert block, original id: " . $questionblock_id);
624 while ($row = $ilDB->fetchAssoc($result)) {
625 $this->svy_log->debug("question: " . $row["question_fi"]);
626 $duplicate_id = $sequence_manager->appendQuestion($row["question_fi"], true);
627 //$duplicate_id = $this->duplicateQuestionForSurvey($row["question_fi"]);
628 $this->svy_log->debug("question copy: " . $duplicate_id);
629 $questions[] = $duplicate_id;
630 $title = (string) $row["title"];
631 $this->svy_log->debug("title: " . $title);
632 $show_questiontext = (bool) $row["show_questiontext"];
633 $show_blocktitle = (bool) $row["show_blocktitle"];
634 }
635 $this->createQuestionblock($title, $show_questiontext, $show_blocktitle, $questions);
636 }
637
638 // seems to be only be used for code mails
639 public function saveUserSettings(
640 int $usr_id,
641 string $key,
642 string $title,
643 string $value
644 ): void {
645 $ilDB = $this->db;
646
647 $next_id = $ilDB->nextId('svy_settings');
648 $affectedRows = $ilDB->insert("svy_settings", array(
649 "settings_id" => array("integer", $next_id),
650 "usr_id" => array("integer", $usr_id),
651 "keyword" => array("text", $key),
652 "title" => array("text", $title),
653 "value" => array("clob", $value)
654 ));
655 }
656
657 public function deleteUserSettings(
658 int $id
659 ): void {
660 $ilDB = $this->db;
661
662 $ilDB->manipulateF(
663 "DELETE FROM svy_settings WHERE settings_id = %s",
664 array('integer'),
665 array($id)
666 );
667 }
668
669 public function getUserSettings(
670 int $usr_id,
671 string $key
672 ): array {
673 $ilDB = $this->db;
674
675 $result = $ilDB->queryF(
676 "SELECT * FROM svy_settings WHERE usr_id = %s AND keyword = %s",
677 array('integer', 'text'),
678 array($usr_id, $key)
679 );
680 $found = array();
681 if ($result->numRows()) {
682 while ($row = $ilDB->fetchAssoc($result)) {
683 $found[$row['settings_id']] = $row;
684 }
685 }
686 return $found;
687 }
688
689 // Saves a survey object to a database
690 public function saveToDb(): void
691 {
692 $ilDB = $this->db;
693
694 // date handling
695 $rmd_start = $this->getReminderStart();
696 if (is_object($rmd_start)) {
697 $rmd_start = $rmd_start->get(IL_CAL_DATE);
698 }
699 $rmd_end = $this->getReminderEnd();
700 if (is_object($rmd_end)) {
701 $rmd_end = $rmd_end->get(IL_CAL_DATE);
702 }
703 if ($this->getSurveyId() < 1) {
704 $next_id = $ilDB->nextId('svy_svy');
705 $affectedRows = $ilDB->insert("svy_svy", array(
706 "survey_id" => array("integer", $next_id),
707 "obj_fi" => array("integer", $this->getId()),
708 "author" => array("text", $this->getAuthor()),
709 "introduction" => array("clob", ilRTE::_replaceMediaObjectImageSrc($this->getIntroduction(), 0)),
710 "outro" => array("clob", ilRTE::_replaceMediaObjectImageSrc($this->getOutro(), 0)),
711 "startdate" => array("text", $this->getStartDate()),
712 "enddate" => array("text", $this->getEndDate()),
713 "evaluation_access" => array("text", $this->getEvaluationAccess()),
714 "complete" => array("text", $this->isComplete()),
715 "created" => array("integer", time()),
716 "anonymize" => array("text", $this->getAnonymize()),
717 "show_question_titles" => array("text", $this->getShowQuestionTitles()),
718 "mailnotification" => array('integer', ($this->getMailNotification()) ? 1 : 0),
719 "mailaddresses" => array('text', $this->getMailAddresses()),
720 "mailparticipantdata" => array('text', $this->getMailParticipantData()),
721 "tstamp" => array("integer", time()),
722 "pool_usage" => array("integer", $this->getPoolUsage()),
723 // Mode type
724 "mode" => array("integer", $this->getMode()),
725 // 360°
726 "mode_360_self_eval" => array("integer", $this->get360SelfEvaluation()),
727 "mode_360_self_rate" => array("integer", $this->get360SelfRaters()),
728 "mode_360_self_appr" => array("integer", $this->get360SelfAppraisee()),
729 "mode_360_results" => array("integer", $this->get360Results()),
730 // competences
731 "mode_skill_service" => array("integer", (int) $this->getSkillService()),
732 // Self Evaluation Only
733 "mode_self_eval_results" => array("integer", self::RESULTS_SELF_EVAL_OWN),
734 // reminder/notification
735 "reminder_status" => array("integer", (int) $this->getReminderStatus()),
736 "reminder_start" => array("datetime", $rmd_start),
737 "reminder_end" => array("datetime", $rmd_end),
738 "reminder_frequency" => array("integer", $this->getReminderFrequency()),
739 "reminder_target" => array("integer", $this->getReminderTarget()),
740 "reminder_last_sent" => array("datetime", $this->getReminderLastSent()),
741 "reminder_tmpl" => array("text", $this->getReminderTemplate(true)),
742 "tutor_ntf_status" => array("integer", (int) $this->getTutorNotificationStatus()),
743 "tutor_ntf_reci" => array("text", implode(";", $this->getTutorNotificationRecipients())),
744 "tutor_ntf_target" => array("integer", $this->getTutorNotificationTarget()),
745 "own_results_view" => array("integer", $this->hasViewOwnResults()),
746 "own_results_mail" => array("integer", $this->hasMailOwnResults()),
747 "tutor_res_status" => array("integer", (int) $this->getTutorResultsStatus()),
748 "tutor_res_reci" => array("text", implode(";", $this->getTutorResultsRecipients())),
749 "confirmation_mail" => array("integer", $this->hasMailConfirmation()),
750 "anon_user_list" => array("integer", $this->hasAnonymousUserList()),
751 "calculate_sum_score" => array("integer", $this->getCalculateSumScore())
752 ));
753 $this->setSurveyId($next_id);
754 } else {
755 $affectedRows = $ilDB->update("svy_svy", array(
756 "author" => array("text", $this->getAuthor()),
757 "introduction" => array("clob", ilRTE::_replaceMediaObjectImageSrc($this->getIntroduction(), 0)),
758 "outro" => array("clob", ilRTE::_replaceMediaObjectImageSrc($this->getOutro(), 0)),
759 "startdate" => array("text", $this->getStartDate()),
760 "enddate" => array("text", $this->getEndDate()),
761 "evaluation_access" => array("text", $this->getEvaluationAccess()),
762 "complete" => array("text", $this->isComplete()),
763 "anonymize" => array("text", $this->getAnonymize()),
764 "show_question_titles" => array("text", $this->getShowQuestionTitles()),
765 "mailnotification" => array('integer', ($this->getMailNotification()) ? 1 : 0),
766 "mailaddresses" => array('text', $this->getMailAddresses()),
767 "mailparticipantdata" => array('text', $this->getMailParticipantData()),
768 "tstamp" => array("integer", time()),
769 "pool_usage" => array("integer", $this->getPoolUsage()),
770 //MODE TYPE
771 "mode" => array("integer", $this->getMode()),
772 // 360°
773 "mode_360_self_eval" => array("integer", $this->get360SelfEvaluation()),
774 "mode_360_self_rate" => array("integer", $this->get360SelfRaters()),
775 "mode_360_self_appr" => array("integer", $this->get360SelfAppraisee()),
776 "mode_360_results" => array("integer", $this->get360Results()),
777 // Competences
778 "mode_skill_service" => array("integer", (int) $this->getSkillService()),
779 // Self Evaluation Only
780 "mode_self_eval_results" => array("integer", $this->getSelfEvaluationResults()),
781 // reminder/notification
782 "reminder_status" => array("integer", $this->getReminderStatus()),
783 "reminder_start" => array("datetime", $rmd_start),
784 "reminder_end" => array("datetime", $rmd_end),
785 "reminder_frequency" => array("integer", $this->getReminderFrequency()),
786 "reminder_target" => array("integer", $this->getReminderTarget()),
787 "reminder_last_sent" => array("datetime", $this->getReminderLastSent()),
788 "reminder_tmpl" => array("text", $this->getReminderTemplate()),
789 "tutor_ntf_status" => array("integer", $this->getTutorNotificationStatus()),
790 "tutor_ntf_reci" => array("text", implode(";", $this->getTutorNotificationRecipients())),
791 "tutor_ntf_target" => array("integer", $this->getTutorNotificationTarget()),
792 "own_results_view" => array("integer", $this->hasViewOwnResults()),
793 "own_results_mail" => array("integer", $this->hasMailOwnResults()),
794 "tutor_res_status" => array("integer", (int) $this->getTutorResultsStatus()),
795 "tutor_res_reci" => array("text", implode(";", $this->getTutorResultsRecipients())),
796 "confirmation_mail" => array("integer", $this->hasMailConfirmation()),
797 "anon_user_list" => array("integer", $this->hasAnonymousUserList()),
798 "calculate_sum_score" => array("integer", $this->getCalculateSumScore())
799 ), array(
800 "survey_id" => array("integer", $this->getSurveyId())
801 ));
802 }
803 if ($affectedRows) {
804 // save questions to db
805 $this->saveQuestionsToDb();
806 }
807
808 // moved activation to ilObjectActivation
809 if ($this->ref_id) {
810 ilObjectActivation::getItem($this->ref_id);
811
812 $item = new ilObjectActivation();
813 if (!$this->isActivationLimited()) {
814 $item->setTimingType(ilObjectActivation::TIMINGS_DEACTIVATED);
815 } else {
816 $item->setTimingType(ilObjectActivation::TIMINGS_ACTIVATION);
817 $item->setTimingStart($this->getActivationStartDate());
818 $item->setTimingEnd($this->getActivationEndDate());
819 $item->toggleVisible($this->getActivationVisibility());
820 }
821
822 $item->update($this->ref_id);
823 }
824 }
825
826 // Saves the survey questions to db
827 public function saveQuestionsToDb(): void
828 {
829 $ilDB = $this->db;
830
831 $this->svy_log->debug("save questions");
832
833 // gather old questions state
834 $old_questions = array();
835 $result = $ilDB->queryF(
836 "SELECT survey_question_id,question_fi,sequence" .
837 " FROM svy_svy_qst WHERE survey_fi = %s",
838 array('integer'),
839 array($this->getSurveyId())
840 );
841 while ($row = $ilDB->fetchAssoc($result)) {
842 $old_questions[$row["question_fi"]] = $row; // problem, as soon as duplicates exist, they will be hidden here
843 }
844
845 // #15231 - diff with current questions state
846 $insert = $update = $delete = array();
847 foreach ($this->questions as $seq => $fi) {
848 if (!array_key_exists($fi, $old_questions)) { // really new fi IDs
849 $insert[] = $fi; // this should be ok, should not create duplicates here
850 } elseif ($old_questions[$fi]["sequence"] != $seq) { // we are updating one of the duplicates (if any)
851 $update[$fi] = $old_questions[$fi]["survey_question_id"];
852 }
853 // keep track of still relevant questions
854 unset($old_questions[$fi]); // deleting old question, if they are not in current array
855 }
856
857 // delete obsolete question relations
858 if (count($old_questions)) {
859 $del_ids = array();
860 foreach ($old_questions as $fi => $old) {
861 $del_ids[] = $old["survey_question_id"];
862 }
863 $ilDB->manipulate($q = "DELETE FROM svy_svy_qst" .
864 " WHERE " . $ilDB->in("survey_question_id", $del_ids, "", "integer"));
865 $this->svy_log->debug("delete: " . $q);
866 }
867 unset($old_questions);
868
869 // create/update question relations
870 foreach ($this->questions as $seq => $fi) {
871 if (in_array($fi, $insert)) {
872 // check if question is not already in the survey, see #22018
873 if (!$this->isQuestionInSurvey($fi)) {
874 $next_id = $ilDB->nextId('svy_svy_qst');
875 $ilDB->manipulateF(
876 "INSERT INTO svy_svy_qst" .
877 " (survey_question_id, survey_fi, question_fi, heading, sequence, tstamp)" .
878 " VALUES (%s, %s, %s, %s, %s, %s)",
879 array('integer', 'integer', 'integer', 'text', 'integer', 'integer'),
880 array($next_id, $this->getSurveyId(), $fi, null, $seq, time())
881 );
882 $this->svy_log->debug("insert svy_svy_qst, id:" . $next_id . ", fi: " . $fi . ", seq:" . $seq);
883 }
884 } elseif (array_key_exists($fi, $update)) {
885 $ilDB->manipulate("UPDATE svy_svy_qst" .
886 " SET sequence = " . $ilDB->quote($seq, "integer") .
887 ", tstamp = " . $ilDB->quote(time(), "integer") .
888 " WHERE survey_question_id = " . $ilDB->quote($update[$fi], "integer"));
889 $this->svy_log->debug("update svy_svy_qst, id:" . $update[$fi] . ", fi: " . $fi . ", seq:" . $seq);
890 }
891 }
892 }
893
894 // Returns a question gui object to a given questiontype and question id
895 public function getQuestionGUI(
896 string $questiontype,
897 int $question_id
899 return SurveyQuestionGUI::_getQuestionGUI($questiontype, $question_id);
900 }
901
902 // Returns the question type of a question with a given id
903 public function getQuestionType(
904 int $question_id
905 ): string {
906 $ilDB = $this->db;
907 if ($question_id < 1) {
908 return -1;
909 }
910 $result = $ilDB->queryF(
911 "SELECT type_tag FROM svy_question, svy_qtype WHERE svy_question.question_id = %s AND " .
912 "svy_question.questiontype_fi = svy_qtype.questiontype_id",
913 array('integer'),
914 array($question_id)
915 );
916 if ($result->numRows() === 1) {
917 $data = $ilDB->fetchAssoc($result);
918 return $data["type_tag"];
919 } else {
920 return "";
921 }
922 }
923
924 public function getSurveyId(): int
925 {
926 return $this->survey_id;
927 }
928
932 public function setAnonymize(int $a_anonymize): void
933 {
934 switch ($a_anonymize) {
935 case self::ANONYMIZE_OFF:
936 case self::ANONYMIZE_ON:
937 case self::ANONYMIZE_FREEACCESS:
938 case self::ANONYMIZE_CODE_ALL:
939 $this->anonymize = $a_anonymize;
940 break;
941 default:
942 $this->anonymize = self::ANONYMIZE_OFF;
943 break;
944 }
945 }
946
947 public function getAnonymize(): int
948 {
949 return $this->anonymize;
950 }
951
952 public function setCalculateSumScore(
953 bool $a_val
954 ): void {
955 $this->calculate_sum_score = $a_val;
956 }
957
958 public function getCalculateSumScore(): bool
959 {
960 return $this->calculate_sum_score;
961 }
962
963 // Checks if the survey is accessible without a survey code
964 public function isAccessibleWithoutCode(): bool
965 {
966 return ($this->getAnonymize() === self::ANONYMIZE_OFF ||
967 $this->getAnonymize() === self::ANONYMIZE_FREEACCESS);
968 }
969
970 // Checks if the survey results are to be anonymized
971 public function hasAnonymizedResults(): bool
972 {
973 return ($this->getAnonymize() === self::ANONYMIZE_ON ||
974 $this->getAnonymize() === self::ANONYMIZE_FREEACCESS);
975 }
976
977 public function loadFromDb(): void
978 {
979 $ilDB = $this->db;
980 $result = $ilDB->queryF(
981 "SELECT * FROM svy_svy WHERE obj_fi = %s",
982 array('integer'),
983 array($this->getId())
984 );
985 if ($result->numRows() === 1) {
986 $data = $ilDB->fetchAssoc($result);
987 $this->setSurveyId($data["survey_id"]);
988 $this->setAuthor($data["author"] ?? "");
989 $this->setIntroduction(ilRTE::_replaceMediaObjectImageSrc((string) $data["introduction"], 1));
990 if (strcmp($data["outro"], "survey_finished") === 0) {
991 $this->setOutro($this->lng->txt("survey_finished"));
992 } else {
993 $this->setOutro(ilRTE::_replaceMediaObjectImageSrc((string) $data["outro"], 1));
994 }
995 $this->setShowQuestionTitles((bool) $data["show_question_titles"]);
996 $this->setStartDate((string) ($data["startdate"] ?? ""));
997 $this->setEndDate((string) ($data["enddate"] ?? ""));
998 $this->setAnonymize((int) $data["anonymize"]);
999 $this->setEvaluationAccess($data["evaluation_access"] ?? "");
1000 $this->loadQuestionsFromDb();
1001 $this->setMailNotification((bool) $data['mailnotification']);
1002 $this->setMailAddresses((string) $data['mailaddresses']);
1003 $this->setMailParticipantData((string) $data['mailparticipantdata']);
1004 $this->setPoolUsage((bool) $data['pool_usage']);
1005 // Mode
1006 $this->setMode($data['mode']);
1007 // 360°
1008 $this->set360SelfEvaluation((bool) $data['mode_360_self_eval']);
1009 $this->set360SelfRaters((bool) $data['mode_360_self_rate']);
1010 $this->set360SelfAppraisee((bool) $data['mode_360_self_appr']);
1011 $this->set360Results((int) $data['mode_360_results']);
1012 // Mode self evaluated
1013 $this->setSelfEvaluationResults((int) $data['mode_self_eval_results']);
1014 // Competences
1015 $this->setSkillService((bool) $data['mode_skill_service']);
1016 // reminder/notification
1017 $this->setReminderStatus((bool) $data["reminder_status"]);
1018 $this->setReminderStart($data["reminder_start"] ? new ilDate($data["reminder_start"], IL_CAL_DATE) : null);
1019 $this->setReminderEnd($data["reminder_end"] ? new ilDate($data["reminder_end"], IL_CAL_DATE) : null);
1020 $this->setReminderFrequency((int) $data["reminder_frequency"]);
1021 $this->setReminderTarget((int) $data["reminder_target"]);
1022 $this->setReminderLastSent((string) $data["reminder_last_sent"]);
1023 $this->setReminderTemplate((int) $data["reminder_tmpl"]);
1024 $this->setTutorNotificationStatus($data["tutor_ntf_status"]);
1025 $this->setTutorNotificationRecipients(explode(";", $data["tutor_ntf_reci"]));
1026 $this->setTutorNotificationTarget($data["tutor_ntf_target"]);
1027 $this->setTutorResultsStatus((bool) $data["tutor_res_status"]);
1028 $this->setTutorResultsRecipients(explode(";", $data["tutor_res_reci"]));
1029
1030 $this->setViewOwnResults((bool) $data["own_results_view"]);
1031 $this->setMailOwnResults((bool) $data["own_results_mail"]);
1032 $this->setMailConfirmation((bool) $data["confirmation_mail"]);
1033 $this->setCalculateSumScore((bool) $data["calculate_sum_score"]);
1034
1035 $this->setAnonymousUserList((bool) $data["anon_user_list"]);
1036 }
1037
1038 // moved activation to ilObjectActivation
1039 if (isset($this->ref_id) && $this->ref_id !== 0) {
1040 $activation = ilObjectActivation::getItem($this->ref_id);
1041 switch ($activation["timing_type"]) {
1043 $this->setActivationLimited(true);
1044 $this->setActivationStartDate($activation["timing_start"]);
1045 $this->setActivationEndDate($activation["timing_end"]);
1046 $this->setActivationVisibility($activation["visible"]);
1047 break;
1048
1049 default:
1050 $this->setActivationLimited(false);
1051 break;
1052 }
1053 }
1054 }
1055
1056 public function loadQuestionsFromDb(): void
1057 {
1058 $ilDB = $this->db;
1059 $this->questions = array();
1060 $result = $ilDB->queryF(
1061 "SELECT * FROM svy_svy_qst WHERE survey_fi = %s ORDER BY sequence",
1062 array('integer'),
1063 array($this->getSurveyId())
1064 );
1065 while ($data = $ilDB->fetchAssoc($result)) {
1066 $this->questions[$data["sequence"]] = $data["question_fi"];
1067 }
1068 }
1069
1070 // Remove duplicate sequence entries, see #22018
1071 public function fixSequenceStructure(): void
1072 {
1073 global $DIC;
1074
1075 $ilDB = $DIC->database();
1076 //return;
1077 // we keep all survey question ids with their lowest sequence
1078 $result = $ilDB->queryF(
1079 "SELECT * FROM svy_svy_qst WHERE survey_fi = %s ORDER BY sequence",
1080 array('integer'),
1081 array($this->getSurveyId())
1082 );
1083
1084 // step 1: find duplicates -> $to_delete_ids
1085 $fis = array();
1086 $to_delete_ids = array();
1087 while ($data = $ilDB->fetchAssoc($result)) {
1088 if (in_array($data["question_fi"], $fis)) { // found a duplicate
1089 $to_delete_ids[] = $data["survey_question_id"];
1090 } else {
1091 $fis[] = $data["question_fi"];
1092 }
1093 }
1094
1095 // step 2: we delete the duplicates
1096 if (count($to_delete_ids) > 0) {
1097 $ilDB->manipulate($q = "DELETE FROM svy_svy_qst" .
1098 " WHERE " . $ilDB->in("survey_question_id", $to_delete_ids, false, "integer") .
1099 " AND survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer"));
1100 $this->svy_log->debug("delete: " . $q);
1101
1102 $ilDB->manipulate($q = "DELETE FROM svy_qblk_qst " .
1103 " WHERE " . $ilDB->in("question_fi", $fis, true, "integer") .
1104 " AND survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer"));
1105 $this->svy_log->debug("delete: " . $q);
1106 }
1107
1108 // step 3: we fix the sequence
1109 $set = $ilDB->query("SELECT * FROM svy_svy_qst " .
1110 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") . " ORDER BY sequence");
1111 $seq = 0;
1112 while ($rec = $ilDB->fetchAssoc($set)) {
1113 $ilDB->manipulate(
1114 $q = "UPDATE svy_svy_qst SET " .
1115 " sequence = " . $ilDB->quote($seq++, "integer") .
1116 " WHERE survey_question_id = " . $ilDB->quote($rec["survey_question_id"], "integer")
1117 );
1118 $this->svy_log->debug("update: " . $q);
1119 }
1120 }
1121
1122 public function setAuthor(
1123 string $author = ""
1124 ): void {
1125 $this->author = $author;
1126 }
1127
1133 public function saveAuthorToMetadata(
1134 string $a_author = ""
1135 ): void {
1136 $md = new ilMD($this->getId(), 0, $this->getType());
1137 $md_life = $md->getLifecycle();
1138 if (!$md_life) {
1139 if ($a_author === '') {
1140 $ilUser = $this->user;
1141 $a_author = $ilUser->getFullname();
1142 }
1143
1144 $md_life = $md->addLifecycle();
1145 $md_life->save();
1146 $con = $md_life->addContribute();
1147 $con->setRole("Author");
1148 $con->save();
1149 $ent = $con->addEntity();
1150 $ent->setEntity($a_author);
1151 $ent->save();
1152 }
1153 }
1154
1155 // Gets the authors name from metadata
1156 public function getAuthor(): string
1157 {
1158 $author = array();
1159 $md = new ilMD($this->getId(), 0, $this->getType());
1160 $md_life = $md->getLifecycle();
1161 if ($md_life) {
1162 $ids = $md_life->getContributeIds();
1163 foreach ($ids as $id) {
1164 $md_cont = $md_life->getContribute($id);
1165 if (strcmp($md_cont->getRole(), "Author") === 0) {
1166 $entids = $md_cont->getEntityIds();
1167 foreach ($entids as $entid) {
1168 $md_ent = $md_cont->getEntity($entid);
1169 $author[] = $md_ent->getEntity();
1170 }
1171 }
1172 }
1173 }
1174 return implode(",", $author);
1175 }
1176
1177 public function getShowQuestionTitles(): bool
1178 {
1179 return (bool) $this->display_question_titles;
1180 }
1181
1182 public function setShowQuestionTitles(bool $a_show): void
1183 {
1184 $this->display_question_titles = $a_show;
1185 }
1186
1187 public function setIntroduction(
1188 string $introduction = ""
1189 ): void {
1190 $this->introduction = $introduction;
1191 }
1192
1193 public function setOutro(
1194 string $outro = ""
1195 ): void {
1196 $this->outro = $outro;
1197 }
1198
1199 public function getStartDate(): string
1200 {
1201 return $this->start_date;
1202 }
1203
1207 public function setStartDate(
1208 string $start_date = ""
1209 ): void {
1210 $this->start_date = $start_date;
1211 }
1212
1217 public function setStartDateAndTime(
1218 string $start_date,
1219 string $start_time
1220 ): void {
1221 $y = '';
1222 $m = '';
1223 $d = '';
1224 $h = '';
1225 $i = '';
1226 $s = '';
1227 if (preg_match("/(\d{4})-(\d{2})-(\d{2})/", $start_date, $matches)) {
1228 $y = $matches[1];
1229 $m = $matches[2];
1230 $d = $matches[3];
1231 }
1232 if (preg_match("/(\d{2}):(\d{2}):(\d{2})/", $start_time, $matches)) {
1233 $h = $matches[1];
1234 $i = $matches[2];
1235 $s = $matches[3];
1236 }
1237 $this->start_date = sprintf('%04d%02d%02d%02d%02d%02d', $y, $m, $d, $h, $i, $s);
1238 }
1239
1240 public function getEndDate(): string
1241 {
1242 return $this->end_date;
1243 }
1244
1248 public function setEndDate(
1249 string $end_date = ""
1250 ): void {
1251 $this->end_date = $end_date;
1252 }
1253
1254 public function hasStarted(): bool
1255 {
1256 $start = $this->getStartDate();
1257 if ($start) {
1258 $start_date = new ilDateTime($start, IL_CAL_TIMESTAMP);
1259 return ($start_date->get(IL_CAL_UNIX) < time());
1260 }
1261 return true;
1262 }
1263
1264 public function hasEnded(): bool
1265 {
1266 $end = $this->getEndDate();
1267 if ($end) {
1268 $end_date = new ilDateTime($end, IL_CAL_TIMESTAMP);
1269 return ($end_date->get(IL_CAL_UNIX) < time());
1270 }
1271 return false;
1272 }
1273
1278 public function setEndDateAndTime(
1279 string $end_date,
1280 string $end_time
1281 ): void {
1282 $y = '';
1283 $m = '';
1284 $d = '';
1285 $h = '';
1286 $i = '';
1287 $s = '';
1288 if (preg_match("/(\d{4})-(\d{2})-(\d{2})/", $end_date, $matches)) {
1289 $y = $matches[1];
1290 $m = $matches[2];
1291 $d = $matches[3];
1292 }
1293 if (preg_match("/(\d{2}):(\d{2}):(\d{2})/", $end_time, $matches)) {
1294 $h = $matches[1];
1295 $i = $matches[2];
1296 $s = $matches[3];
1297 }
1298 $this->end_date = sprintf('%04d%02d%02d%02d%02d%02d', $y, $m, $d, $h, $i, $s);
1299 }
1300
1301 // Gets the learners evaluation access
1302 public function getEvaluationAccess(): string
1303 {
1304 return $this->evaluation_access;
1305 }
1306
1307 public function setEvaluationAccess(
1308 string $evaluation_access = self::EVALUATION_ACCESS_OFF
1309 ): void {
1310 $this->evaluation_access = $evaluation_access;
1311 }
1312
1314 bool $a_value
1315 ): void {
1316 $this->activation_visibility = $a_value;
1317 }
1318
1319 public function getActivationVisibility(): bool
1320 {
1321 return $this->activation_visibility;
1322 }
1323
1324 public function isActivationLimited(): bool
1325 {
1326 return $this->activation_limited;
1327 }
1328
1329 public function setActivationLimited(bool $a_value): void
1330 {
1331 $this->activation_limited = $a_value;
1332 }
1333
1334 public function getIntroduction(): string
1335 {
1336 return $this->introduction;
1337 }
1338
1339 public function getOutro(): string
1340 {
1341 return $this->outro;
1342 }
1343
1348 public function getExistingQuestions(): array
1349 {
1350 $ilDB = $this->db;
1351 $existing_questions = array();
1352 $result = $ilDB->queryF(
1353 "SELECT svy_question.original_id FROM svy_question, svy_svy_qst WHERE " .
1354 "svy_svy_qst.survey_fi = %s AND svy_svy_qst.question_fi = svy_question.question_id",
1355 array('integer'),
1356 array($this->getSurveyId())
1357 );
1358 while ($data = $ilDB->fetchAssoc($result)) {
1359 if ($data["original_id"]) {
1360 $existing_questions[] = (int) $data["original_id"];
1361 }
1362 }
1363 return $existing_questions;
1364 }
1365
1370 public function getQuestionpoolTitles(
1371 bool $could_be_offline = false,
1372 bool $showPath = false
1373 ): array {
1374 return ilObjSurveyQuestionPool::_getAvailableQuestionpools(true, $could_be_offline, $showPath);
1375 }
1376
1384 public function moveQuestions(
1385 array $move_questions,
1386 int $target_index,
1387 int $insert_mode
1388 ): void {
1389 $array_pos = array_search($target_index, $this->questions);
1390 $part1 = $part2 = [];
1391 if ($insert_mode === 0) {
1392 $part1 = array_slice($this->questions, 0, $array_pos);
1393 $part2 = array_slice($this->questions, $array_pos);
1394 } elseif ($insert_mode === 1) {
1395 $part1 = array_slice($this->questions, 0, $array_pos + 1);
1396 $part2 = array_slice($this->questions, $array_pos + 1);
1397 }
1398 $found = 0;
1399 foreach ($move_questions as $question_id) {
1400 if (!(!in_array($question_id, $part1))) {
1401 unset($part1[array_search($question_id, $part1)]);
1402 $found++;
1403 }
1404 if (!(!in_array($question_id, $part2))) {
1405 unset($part2[array_search($question_id, $part2)]);
1406 $found++;
1407 }
1408 }
1409 // sanity check: do not move questions if they have not be found in the array
1410 if ($found !== count($move_questions)) {
1411 return;
1412 }
1413 $part1 = array_values($part1);
1414 $part2 = array_values($part2);
1415 $this->questions = array_values(array_merge($part1, $move_questions, $part2));
1416 foreach ($move_questions as $question_id) {
1417 $constraints = $this->getConstraints($question_id);
1418 foreach ($constraints as $idx => $constraint) {
1419 foreach ($part2 as $next_question_id) {
1420 if ($constraint["question"] == $next_question_id) {
1421 // constraint concerning a question that follows -> delete constraint
1422 $this->deleteConstraint($constraint["id"]);
1423 }
1424 }
1425 }
1426 }
1427 $this->saveQuestionsToDb();
1428 }
1429
1433 public function removeQuestion(
1434 int $question_id
1435 ): void {
1436 $question = self::_instanciateQuestion($question_id);
1437 #20610 if no question found, do nothing.
1438 if ($question) {
1439 $question->delete($question_id);
1440 $this->removeConstraintsConcerningQuestion($question_id);
1441 }
1442 }
1443
1449 int $question_id
1450 ): void {
1451 $ilDB = $this->db;
1452 $result = $ilDB->queryF(
1453 "SELECT constraint_fi FROM svy_qst_constraint WHERE question_fi = %s AND survey_fi = %s",
1454 array('integer','integer'),
1455 array($question_id, $this->getSurveyId())
1456 );
1457 if ($result->numRows() > 0) {
1458 $remove_constraints = array();
1459 while ($row = $ilDB->fetchAssoc($result)) {
1460 $remove_constraints[] = $row["constraint_fi"];
1461 }
1462 $affectedRows = $ilDB->manipulateF(
1463 "DELETE FROM svy_qst_constraint WHERE question_fi = %s AND survey_fi = %s",
1464 array('integer','integer'),
1465 array($question_id, $this->getSurveyId())
1466 );
1467 foreach ($remove_constraints as $key => $constraint_id) {
1468 $affectedRows = $ilDB->manipulateF(
1469 "DELETE FROM svy_constraint WHERE constraint_id = %s",
1470 array('integer'),
1471 array($constraint_id)
1472 );
1473 }
1474 }
1475 }
1476
1482 public function removeQuestions(
1483 array $remove_questions,
1484 array $remove_questionblocks
1485 ): void {
1486 $ilDB = $this->db;
1487
1488 $block_sizes = array();
1489 foreach ($this->getSurveyQuestions() as $question_id => $data) {
1490 if (in_array($question_id, $remove_questions) or in_array($data["questionblock_id"], $remove_questionblocks)) {
1491 unset($this->questions[array_search($question_id, $this->questions)]);
1492 $this->removeQuestion($question_id);
1493 } elseif ($data["questionblock_id"]) {
1494 $block_sizes[$data["questionblock_id"]] = ($block_sizes[$data["questionblock_id"]] ?? 0) + 1;
1495 }
1496 }
1497
1498 // blocks with just 1 question need to be deleted
1499 foreach ($block_sizes as $block_id => $size) {
1500 if ($size < 2) {
1501 $remove_questionblocks[] = $block_id;
1502 }
1503 }
1504
1505 foreach (array_unique($remove_questionblocks) as $questionblock_id) {
1506 $affectedRows = $ilDB->manipulateF(
1507 "DELETE FROM svy_qblk WHERE questionblock_id = %s",
1508 array('integer'),
1509 array($questionblock_id)
1510 );
1511 $affectedRows = $ilDB->manipulateF(
1512 "DELETE FROM svy_qblk_qst WHERE questionblock_fi = %s AND survey_fi = %s",
1513 array('integer','integer'),
1514 array($questionblock_id, $this->getSurveyId())
1515 );
1516 }
1517
1518 $this->questions = array_values($this->questions);
1519 $this->saveQuestionsToDb();
1520 }
1521
1526 public function unfoldQuestionblocks(
1527 array $questionblocks
1528 ): void {
1529 $ilDB = $this->db;
1530 foreach ($questionblocks as $index) {
1531 $affectedRows = $ilDB->manipulateF(
1532 "DELETE FROM svy_qblk WHERE questionblock_id = %s",
1533 array('integer'),
1534 array($index)
1535 );
1536 $affectedRows = $ilDB->manipulateF(
1537 "DELETE FROM svy_qblk_qst WHERE questionblock_fi = %s AND survey_fi = %s",
1538 array('integer','integer'),
1539 array($index, $this->getSurveyId())
1540 );
1541 }
1542 }
1543
1548 int $question_id,
1549 int $questionblock_id
1550 ): void {
1551 $ilDB = $this->db;
1552
1553 $ilDB->manipulateF(
1554 "DELETE FROM svy_qblk_qst WHERE questionblock_fi = %s AND survey_fi = %s AND question_fi = %s",
1555 array('integer','integer','integer'),
1556 array($questionblock_id, $this->getSurveyId(), $question_id)
1557 );
1558 }
1559
1563 public function addQuestionToBlock(
1564 int $question_id,
1565 int $questionblock_id
1566 ): void {
1567 $ilDB = $this->db;
1568
1569 // see #22018
1570 if (!$this->isQuestionInAnyBlock($question_id)) {
1571 $next_id = $ilDB->nextId('svy_qblk_qst');
1572 $affectedRows = $ilDB->manipulateF(
1573 "INSERT INTO svy_qblk_qst (qblk_qst_id, survey_fi, questionblock_fi, " .
1574 "question_fi) VALUES (%s, %s, %s, %s)",
1575 array('integer', 'integer', 'integer', 'integer'),
1576 array($next_id, $this->getSurveyId(), $questionblock_id, $question_id)
1577 );
1578
1579 $this->deleteConstraints($question_id); // #13713
1580 }
1581 }
1582
1586 public function isQuestionInAnyBlock(
1587 int $a_question_fi
1588 ): bool {
1589 global $DIC;
1590
1591 $ilDB = $DIC->database();
1592
1593 $set = $ilDB->query("SELECT * FROM svy_qblk_qst " .
1594 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") .
1595 " AND question_fi = " . $ilDB->quote($a_question_fi, "integer"));
1596 if ($rec = $ilDB->fetchAssoc($set)) {
1597 return true;
1598 }
1599 return false;
1600 }
1601
1602
1609 int $questionblock_id
1610 ): array {
1611 $ilDB = $this->db;
1612 $titles = array();
1613 $result = $ilDB->queryF(
1614 "SELECT svy_question.title, svy_qblk_qst.question_fi, svy_qblk_qst.survey_fi FROM " .
1615 "svy_qblk, svy_qblk_qst, svy_question WHERE svy_qblk.questionblock_id = svy_qblk_qst.questionblock_fi AND " .
1616 "svy_question.question_id = svy_qblk_qst.question_fi AND svy_qblk.questionblock_id = %s",
1617 array('integer'),
1618 array($questionblock_id)
1619 );
1620 $survey_id = "";
1621 while ($row = $ilDB->fetchAssoc($result)) {
1622 $titles[$row["question_fi"]] = $row["title"];
1623 $survey_id = $row["survey_fi"];
1624 }
1625 $result = $ilDB->queryF(
1626 "SELECT question_fi, sequence FROM svy_svy_qst WHERE survey_fi = %s ORDER BY sequence",
1627 array('integer'),
1628 array($survey_id)
1629 );
1630 $resultarray = array();
1631 $counter = 1;
1632 while ($row = $ilDB->fetchAssoc($result)) {
1633 if (array_key_exists($row["question_fi"], $titles)) {
1634 $resultarray[$counter++] = $titles[$row["question_fi"]];
1635 }
1636 }
1637 return $resultarray;
1638 }
1639
1646 int $questionblock_id
1647 ): array {
1648 $ilDB = $this->db;
1649
1650 // we need a correct order here, see #22011
1651 $result = $ilDB->queryF(
1652 "SELECT a.question_fi FROM svy_qblk_qst a JOIN svy_svy_qst b ON (a.question_fi = b.question_fi) " .
1653 " WHERE a.questionblock_fi = %s ORDER BY b.sequence",
1654 array("integer"),
1655 array($questionblock_id)
1656 );
1657 $ids = array();
1658 if ($result->numRows()) {
1659 while ($data = $ilDB->fetchAssoc($result)) {
1660 if (!in_array($data['question_fi'], $ids)) { // no duplicates, see #22018
1661 $ids[] = (int) $data['question_fi'];
1662 }
1663 }
1664 }
1665 return $ids;
1666 }
1667
1672 public static function _getQuestionblock(
1673 int $questionblock_id
1674 ): array {
1675 global $DIC;
1676
1677 $ilDB = $DIC->database();
1678 $result = $ilDB->queryF(
1679 "SELECT * FROM svy_qblk WHERE questionblock_id = %s",
1680 array('integer'),
1681 array($questionblock_id)
1682 );
1683 $row = $ilDB->fetchAssoc($result);
1684 return $row;
1685 }
1686
1690 public static function _addQuestionblock(
1691 string $title = "",
1692 int $owner = 0,
1693 bool $show_questiontext = true,
1694 bool $show_blocktitle = false,
1695 bool $compress_view = false
1696 ): int {
1697 global $DIC;
1698
1699 $ilDB = $DIC->database();
1700 $next_id = $ilDB->nextId('svy_qblk');
1701 $ilDB->manipulateF(
1702 "INSERT INTO svy_qblk (questionblock_id, title, show_questiontext," .
1703 " show_blocktitle, owner_fi, tstamp, compress_view) " .
1704 "VALUES (%s, %s, %s, %s, %s, %s, %s)",
1705 array('integer','text','integer','integer','integer','integer','integer'),
1706 array($next_id, $title, $show_questiontext, $show_blocktitle, $owner, time(),$compress_view)
1707 );
1708 return $next_id;
1709 }
1710
1714 public function createQuestionblock(
1715 string $title,
1716 bool $show_questiontext,
1717 bool $show_blocktitle,
1718 array $questions,
1719 bool $compress_view = false
1720 ): void {
1721 $ilDB = $this->db;
1722
1723 // if the selected questions are not in a continous selection, move all questions of the
1724 // questionblock at the position of the first selected question
1725 $this->moveQuestions($questions, $questions[0], 0);
1726
1727 // now save the question block
1728 $ilUser = $this->user;
1729 $next_id = $ilDB->nextId('svy_qblk');
1730 $affectedRows = $ilDB->manipulateF(
1731 "INSERT INTO svy_qblk (questionblock_id, title, show_questiontext," .
1732 " show_blocktitle, owner_fi, tstamp, compress_view) VALUES (%s, %s, %s, %s, %s, %s, %s)",
1733 array('integer','text','text','text','integer','integer','integer'),
1734 array($next_id, $title, $show_questiontext, $show_blocktitle, $ilUser->getId(), time(), $compress_view)
1735 );
1736 if ($affectedRows) {
1737 $questionblock_id = $next_id;
1738 foreach ($questions as $index) {
1739 if (!$this->isQuestionInAnyBlock($index)) {
1740 $next_id = $ilDB->nextId('svy_qblk_qst'); // #22018
1741 $affectedRows = $ilDB->manipulateF(
1742 "INSERT INTO svy_qblk_qst (qblk_qst_id, survey_fi, questionblock_fi, " .
1743 "question_fi) VALUES (%s, %s, %s, %s)",
1744 array('integer', 'integer', 'integer', 'integer'),
1745 array($next_id, $this->getSurveyId(), $questionblock_id, $index)
1746 );
1747 $this->deleteConstraints($index);
1748 }
1749 }
1750 }
1751 }
1752
1756 public function modifyQuestionblock(
1757 int $questionblock_id,
1758 string $title,
1759 bool $show_questiontext,
1760 bool $show_blocktitle,
1761 bool $compress_view = false
1762 ): void {
1763 $ilDB = $this->db;
1764 $ilDB->manipulateF(
1765 "UPDATE svy_qblk SET title = %s, show_questiontext = %s," .
1766 " show_blocktitle = %s, compress_view = %s WHERE questionblock_id = %s",
1767 array('text','text','text','integer', 'integer'),
1768 array($title, $show_questiontext, $show_blocktitle, $compress_view, $questionblock_id)
1769 );
1770 }
1771
1776 public function deleteConstraints(
1777 int $question_id
1778 ): void {
1779 $ilDB = $this->db;
1780 $result = $ilDB->queryF(
1781 "SELECT constraint_fi FROM svy_qst_constraint WHERE question_fi = %s AND survey_fi = %s",
1782 array('integer','integer'),
1783 array($question_id, $this->getSurveyId())
1784 );
1785 $constraints = array();
1786 while ($row = $ilDB->fetchAssoc($result)) {
1787 $constraints[] = $row["constraint_fi"];
1788 }
1789 foreach ($constraints as $constraint_id) {
1790 $this->deleteConstraint($constraint_id);
1791 }
1792 }
1793
1798 public function deleteConstraint(
1799 int $constraint_id
1800 ): void {
1801 $ilDB = $this->db;
1802 $affectedRows = $ilDB->manipulateF(
1803 "DELETE FROM svy_constraint WHERE constraint_id = %s",
1804 array('integer'),
1805 array($constraint_id)
1806 );
1807 $affectedRows = $ilDB->manipulateF(
1808 "DELETE FROM svy_qst_constraint WHERE constraint_fi = %s",
1809 array('integer'),
1810 array($constraint_id)
1811 );
1812 }
1813
1818 public function getSurveyQuestions(
1819 bool $with_answers = false
1820 ): array {
1821 $ilDB = $this->db;
1822 // get questionblocks
1823 $all_questions = array();
1824 $result = $ilDB->queryF(
1825 "SELECT svy_qtype.type_tag, svy_qtype.plugin, svy_question.question_id, " .
1826 "svy_svy_qst.heading FROM svy_qtype, svy_question, svy_svy_qst WHERE svy_svy_qst.survey_fi = %s AND " .
1827 "svy_svy_qst.question_fi = svy_question.question_id AND svy_question.questiontype_fi = svy_qtype.questiontype_id " .
1828 "ORDER BY svy_svy_qst.sequence",
1829 array('integer'),
1830 array($this->getSurveyId())
1831 );
1832 while ($row = $ilDB->fetchAssoc($result)) {
1833 $add = true;
1834 if ($row["plugin"]) {
1835 if (!$this->isPluginActive($row["type_tag"])) {
1836 $add = false;
1837 }
1838 }
1839 if ($add) {
1840 $question = self::_instanciateQuestion($row["question_id"]);
1841 $questionrow = $question->getQuestionDataArray($row["question_id"]);
1842 foreach ($row as $key => $value) {
1843 $questionrow[$key] = $value;
1844 }
1845 $all_questions[$row["question_id"]] = $questionrow;
1846 $all_questions[$row["question_id"]]["usableForPrecondition"] = $question->usableForPrecondition();
1847 $all_questions[$row["question_id"]]["availableRelations"] = $question->getAvailableRelations();
1848 }
1849 }
1850 // get all questionblocks
1851 $questionblocks = array();
1852 if (count($all_questions)) {
1853 $result = $ilDB->queryF(
1854 "SELECT svy_qblk.*, svy_qblk_qst.question_fi FROM svy_qblk, svy_qblk_qst WHERE " .
1855 "svy_qblk.questionblock_id = svy_qblk_qst.questionblock_fi AND svy_qblk_qst.survey_fi = %s " .
1856 "AND " . $ilDB->in('svy_qblk_qst.question_fi', array_keys($all_questions), false, 'integer'),
1857 array('integer'),
1858 array($this->getSurveyId())
1859 );
1860 while ($row = $ilDB->fetchAssoc($result)) {
1861 $questionblocks[$row['question_fi']] = $row;
1862 }
1863 }
1864
1865 foreach ($all_questions as $question_id => $row) {
1866 $constraints = $this->getConstraints($question_id);
1867 if (isset($questionblocks[$question_id])) {
1868 $all_questions[$question_id]["questionblock_title"] = $questionblocks[$question_id]['title'];
1869 $all_questions[$question_id]["questionblock_id"] = $questionblocks[$question_id]['questionblock_id'];
1870 } else {
1871 $all_questions[$question_id]["questionblock_title"] = "";
1872 $all_questions[$question_id]["questionblock_id"] = "";
1873 }
1874 $all_questions[$question_id]["constraints"] = $constraints;
1875 if ($with_answers) {
1876 $answers = array();
1877 $result = $ilDB->queryF(
1878 "SELECT svy_variable.*, svy_category.title FROM svy_variable, svy_category " .
1879 "WHERE svy_variable.question_fi = %s AND svy_variable.category_fi = svy_category.category_id " .
1880 "ORDER BY sequence ASC",
1881 array('integer'),
1882 array($question_id)
1883 );
1884 if ($result->numRows() > 0) {
1885 while ($data = $ilDB->fetchAssoc($result)) {
1886 $answers[] = $data["title"];
1887 }
1888 }
1889 $all_questions[$question_id]["answers"] = $answers;
1890 }
1891 }
1892 return $all_questions;
1893 }
1894
1900 public function setObligatoryStates(
1901 array $obligatory_questions
1902 ): void {
1903 $ilDB = $this->db;
1904 $result = $ilDB->queryF(
1905 "SELECT * FROM svy_svy_qst WHERE survey_fi = %s",
1906 array('integer'),
1907 array($this->getSurveyId())
1908 );
1909 if ($result->numRows()) {
1910 while ($row = $ilDB->fetchAssoc($result)) {
1911 if (!array_key_exists($row["question_fi"], $obligatory_questions)) {
1912 $obligatory_questions[$row["question_fi"]] = 0;
1913 }
1914 }
1915 }
1916 // set the obligatory states in the database
1917 foreach ($obligatory_questions as $question_fi => $obligatory) {
1918 // #12420
1919 $ilDB->manipulate("UPDATE svy_question" .
1920 " SET obligatory = " . $ilDB->quote($obligatory, "integer") .
1921 " WHERE question_id = " . $ilDB->quote($question_fi, "integer"));
1922 }
1923 }
1924
1929 public function getSurveyPages(): array
1930 {
1931 $ilDB = $this->db;
1932 // get questionblocks
1933 $all_questions = array();
1934 $result = $ilDB->queryF(
1935 "SELECT svy_question.*, svy_qtype.type_tag, svy_svy_qst.heading FROM " .
1936 "svy_question, svy_qtype, svy_svy_qst WHERE svy_svy_qst.survey_fi = %s AND " .
1937 "svy_svy_qst.question_fi = svy_question.question_id AND svy_question.questiontype_fi = svy_qtype.questiontype_id " .
1938 "ORDER BY svy_svy_qst.sequence",
1939 array('integer'),
1940 array($this->getSurveyId())
1941 );
1942 while ($row = $ilDB->fetchAssoc($result)) {
1943 $all_questions[$row["question_id"]] = $row;
1944 }
1945 // get all questionblocks
1946 $questionblocks = array();
1947 if (count($all_questions)) {
1948 $result = $ilDB->queryF(
1949 "SELECT svy_qblk.*, svy_qblk_qst.question_fi FROM svy_qblk, svy_qblk_qst " .
1950 "WHERE svy_qblk.questionblock_id = svy_qblk_qst.questionblock_fi AND svy_qblk_qst.survey_fi = %s " .
1951 "AND " . $ilDB->in('svy_qblk_qst.question_fi', array_keys($all_questions), false, 'integer'),
1952 array('integer'),
1953 array($this->getSurveyId())
1954 );
1955 while ($row = $ilDB->fetchAssoc($result)) {
1956 $questionblocks[$row['question_fi']] = $row;
1957 }
1958 }
1959
1960 $all_pages = array();
1961 $pageindex = -1;
1962 $currentblock = "";
1963 foreach ($all_questions as $question_id => $row) {
1964 $constraints = array();
1965 if (isset($questionblocks[$question_id])) {
1966 if (!$currentblock or ($currentblock != $questionblocks[$question_id]['questionblock_id'])) {
1967 $pageindex++;
1968 }
1969 $all_questions[$question_id]['page'] = $pageindex;
1970 $all_questions[$question_id]["questionblock_title"] = $questionblocks[$question_id]['title'];
1971 $all_questions[$question_id]["questionblock_id"] = $questionblocks[$question_id]['questionblock_id'];
1972 $all_questions[$question_id]["questionblock_show_questiontext"] = $questionblocks[$question_id]['show_questiontext'];
1973 $all_questions[$question_id]["questionblock_show_blocktitle"] = $questionblocks[$question_id]['show_blocktitle'];
1974 $all_questions[$question_id]["questionblock_compress_view"] = $questionblocks[$question_id]['compress_view'];
1975 $currentblock = $questionblocks[$question_id]['questionblock_id'];
1976 } else {
1977 $pageindex++;
1978 $all_questions[$question_id]['page'] = $pageindex;
1979 $all_questions[$question_id]["questionblock_title"] = "";
1980 $all_questions[$question_id]["questionblock_id"] = "";
1981 $all_questions[$question_id]["questionblock_show_questiontext"] = 1;
1982 $all_questions[$question_id]["questionblock_show_blocktitle"] = 1;
1983 $all_questions[$question_id]["questionblock_compress_view"] = false;
1984 $currentblock = "";
1985 }
1986 $constraints = $this->getConstraints($question_id);
1987 $all_questions[$question_id]["constraints"] = $constraints;
1988 if (!isset($all_pages[$pageindex])) {
1989 $all_pages[$pageindex] = array();
1990 }
1991 $all_pages[$pageindex][] = $all_questions[$question_id];
1992 }
1993 // calculate position percentage for every page
1994 $max = count($all_pages);
1995 $counter = 1;
1996 foreach ($all_pages as $index => $block) {
1997 foreach ($block as $blockindex => $question) {
1998 $all_pages[$index][$blockindex]["position"] = $counter / $max;
1999 }
2000 $counter++;
2001 }
2002
2003 return $all_pages;
2004 }
2005
2014 public function getNextPage(
2015 int $active_page_question_id,
2016 int $direction
2017 ): ?array {
2018 $foundpage = -1;
2019 $pages = $this->getSurveyPages();
2020 if ($active_page_question_id === 0) {
2021 return $pages[0];
2022 }
2023 foreach ($pages as $key => $question_array) {
2024 foreach ($question_array as $question) {
2025 if ($active_page_question_id == $question["question_id"]) {
2026 $foundpage = $key;
2027 }
2028 }
2029 }
2030 if ($foundpage === -1) {
2031 throw new ilSurveyException("nextPage: Current page not found.");
2032 } else {
2033 $foundpage += $direction;
2034 if ($foundpage < 0) {
2035 return null;
2036 }
2037 if ($foundpage >= count($pages)) {
2038 return null;
2039 }
2040 return $pages[$foundpage];
2041 }
2042 }
2043
2048 bool $use_obj_id = false,
2049 bool $could_be_offline = false,
2050 bool $showPath = false,
2051 string $permission = "read"
2052 ): array {
2053 return ilObjSurveyQuestionPool::_getAvailableQuestionpools($use_obj_id, $could_be_offline, $showPath, $permission);
2054 }
2055
2060 public function getPrecondition(
2061 int $constraint_id
2062 ): array {
2063 $ilDB = $this->db;
2064
2065 $result = $ilDB->queryF(
2066 "SELECT svy_constraint.*, svy_relation.*, svy_qst_constraint.question_fi ref_question_fi FROM svy_qst_constraint, svy_constraint, " .
2067 "svy_relation WHERE svy_constraint.relation_fi = svy_relation.relation_id AND " .
2068 "svy_qst_constraint.constraint_fi = svy_constraint.constraint_id AND svy_constraint.constraint_id = %s",
2069 array('integer'),
2070 array($constraint_id)
2071 );
2072 $pc = array();
2073 if ($result->numRows()) {
2074 $pc = $ilDB->fetchAssoc($result);
2075 }
2076 return $pc;
2077 }
2078
2083 public function getConstraints(
2084 int $question_id
2085 ): array {
2086 $ilDB = $this->db;
2087
2088 $result_array = array();
2089 $result = $ilDB->queryF(
2090 "SELECT svy_constraint.*, svy_relation.* FROM svy_qst_constraint, svy_constraint, svy_relation " .
2091 "WHERE svy_constraint.relation_fi = svy_relation.relation_id AND " .
2092 "svy_qst_constraint.constraint_fi = svy_constraint.constraint_id AND svy_qst_constraint.question_fi = %s " .
2093 "AND svy_qst_constraint.survey_fi = %s",
2094 array('integer','integer'),
2095 array($question_id, $this->getSurveyId())
2096 );
2097 while ($row = $ilDB->fetchAssoc($result)) {
2098 $question_type = SurveyQuestion::_getQuestionType($row["question_fi"]);
2099 SurveyQuestion::_includeClass($question_type);
2100 $question = new $question_type();
2101 $question->loadFromDb($row["question_fi"]);
2102 $valueoutput = $question->getPreconditionValueOutput($row["value"]);
2103 $result_array[] = array("id" => $row["constraint_id"],
2104 "question" => $row["question_fi"],
2105 "short" => $row["shortname"],
2106 "long" => $row["longname"],
2107 "value" => $row["value"],
2108 "conjunction" => $row["conjunction"],
2109 "valueoutput" => $valueoutput
2110 );
2111 }
2112 return $result_array;
2113 }
2114
2118 public static function _getConstraints(
2119 int $survey_id
2120 ): array {
2121 global $DIC;
2122
2123 $ilDB = $DIC->database();
2124 $result_array = array();
2125 $result = $ilDB->queryF(
2126 "SELECT svy_qst_constraint.question_fi as for_question, svy_constraint.*, svy_relation.* " .
2127 "FROM svy_qst_constraint, svy_constraint, svy_relation WHERE svy_constraint.relation_fi = svy_relation.relation_id " .
2128 "AND svy_qst_constraint.constraint_fi = svy_constraint.constraint_id AND svy_qst_constraint.survey_fi = %s",
2129 array('integer'),
2130 array($survey_id)
2131 );
2132 while ($row = $ilDB->fetchAssoc($result)) {
2133 $result_array[] = array("id" => $row["constraint_id"],
2134 "for_question" => $row["for_question"],
2135 "question" => $row["question_fi"],
2136 "short" => $row["shortname"],
2137 "long" => $row["longname"],
2138 "relation_id" => $row["relation_id"],
2139 "value" => $row["value"],
2140 'conjunction' => $row['conjunction']
2141 );
2142 }
2143 return $result_array;
2144 }
2145
2146
2151 public function getVariables(
2152 int $question_id
2153 ): array {
2154 $ilDB = $this->db;
2155
2156 $result_array = array();
2157 $result = $ilDB->queryF(
2158 "SELECT svy_variable.*, svy_category.title FROM svy_variable LEFT JOIN " .
2159 "svy_category ON svy_variable.category_fi = svy_category.category_id WHERE svy_variable.question_fi = %s " .
2160 "ORDER BY svy_variable.sequence",
2161 array('integer'),
2162 array($question_id)
2163 );
2164 while ($row = $ilDB->fetchObject($result)) {
2165 $result_array[$row->sequence] = $row;
2166 }
2167 return $result_array;
2168 }
2169
2179 public function addConstraint(
2180 int $if_question_id,
2181 int $relation,
2182 float $value,
2183 int $conjunction
2184 ): ?int {
2185 $ilDB = $this->db;
2186
2187 $next_id = $ilDB->nextId('svy_constraint');
2188 $affectedRows = $ilDB->manipulateF(
2189 "INSERT INTO svy_constraint (constraint_id, question_fi, relation_fi, value, conjunction) VALUES " .
2190 "(%s, %s, %s, %s, %s)",
2191 array('integer','integer','integer','float', 'integer'),
2192 array($next_id, $if_question_id, $relation, $value, $conjunction)
2193 );
2194 if ($affectedRows) {
2195 return $next_id;
2196 } else {
2197 return null;
2198 }
2199 }
2200
2201
2206 int $to_question_id,
2207 int $constraint_id
2208 ): void {
2209 $ilDB = $this->db;
2210
2211 $next_id = $ilDB->nextId('svy_qst_constraint');
2212 $ilDB->manipulateF(
2213 "INSERT INTO svy_qst_constraint (question_constraint_id, survey_fi, question_fi, " .
2214 "constraint_fi) VALUES (%s, %s, %s, %s)",
2215 array('integer','integer','integer','integer'),
2216 array($next_id, $this->getSurveyId(), $to_question_id, $constraint_id)
2217 );
2218 }
2219
2223 public function updateConstraint(
2224 int $precondition_id,
2225 int $if_question_id,
2226 int $relation,
2227 float $value,
2228 int $conjunction
2229 ): void {
2230 $ilDB = $this->db;
2231 $ilDB->manipulateF(
2232 "UPDATE svy_constraint SET question_fi = %s, relation_fi = %s, value = %s, conjunction = %s " .
2233 "WHERE constraint_id = %s",
2234 array('integer','integer','float','integer','integer'),
2235 array($if_question_id, $relation, $value, $conjunction, $precondition_id)
2236 );
2237 }
2238
2243 array $questions,
2244 int $conjunction
2245 ): void {
2246 $ilDB = $this->db;
2247 foreach ($questions as $question_id) {
2248 $ilDB->manipulateF(
2249 "UPDATE svy_constraint SET conjunction = %s " .
2250 "WHERE constraint_id IN (SELECT constraint_fi FROM svy_qst_constraint WHERE svy_qst_constraint.question_fi = %s)",
2251 array('integer','integer'),
2252 array($conjunction, $question_id)
2253 );
2254 }
2255 }
2256
2260 public function getAllRelations(
2261 bool $short_as_key = false
2262 ): array {
2263 $ilDB = $this->db;
2264
2265 // #7987
2266 $custom_order = array("equal", "not_equal", "less", "less_or_equal", "more", "more_or_equal");
2267 $custom_order = array_flip($custom_order);
2268
2269 $result_array = array();
2270 $result = $ilDB->query("SELECT * FROM svy_relation");
2271 while ($row = $ilDB->fetchAssoc($result)) {
2272 if ($short_as_key) {
2273 $result_array[$row["shortname"]] = array("short" => $row["shortname"], "long" => $row["longname"], "id" => $row["relation_id"], "order" => $custom_order[$row["longname"]]);
2274 } else {
2275 $result_array[$row["relation_id"]] = array("short" => $row["shortname"], "long" => $row["longname"], "order" => $custom_order[$row["longname"]]);
2276 }
2277 }
2278
2279 $result_array = ilArrayUtil::sortArray($result_array, "order", "ASC", true, true);
2280 foreach ($result_array as $idx => $item) {
2281 unset($result_array[$idx]["order"]);
2282 }
2283
2284 return $result_array;
2285 }
2286
2287
2288
2289
2294 public function deleteWorkingData(
2295 int $question_id,
2296 int $active_id
2297 ): void {
2298 $ilDB = $this->db;
2299
2300 $affectedRows = $ilDB->manipulateF(
2301 "DELETE FROM svy_answer WHERE question_fi = %s AND active_fi = %s",
2302 array('integer','integer'),
2303 array($question_id, $active_id)
2304 );
2305 }
2306
2311 public function loadWorkingData(
2312 int $question_id,
2313 int $active_id
2314 ): array {
2315 $ilDB = $this->db;
2316 $result_array = array();
2317 $result = $ilDB->queryF(
2318 "SELECT * FROM svy_answer WHERE question_fi = %s AND active_fi = %s",
2319 array('integer','integer'),
2320 array($question_id, $active_id)
2321 );
2322 if ($result->numRows() >= 1) {
2323 while ($row = $ilDB->fetchAssoc($result)) {
2324 $result_array[] = $row;
2325 }
2326 }
2327 return $result_array;
2328 }
2329
2334 public function finishSurvey(
2335 int $finished_id,
2336 int $appr_id = 0
2337 ): void {
2338 $ilDB = $this->db;
2339
2340 $ilDB->manipulateF(
2341 "UPDATE svy_finished SET state = %s, tstamp = %s" .
2342 " WHERE survey_fi = %s AND finished_id = %s",
2343 array('text','integer','integer','integer'),
2344 array(1, time(), $this->getSurveyId(), $finished_id)
2345 );
2346
2347 // self eval writes skills on finishing
2348 if ($this->getMode() === self::MODE_SELF_EVAL) {
2349 $user = $this->getUserDataFromActiveId($finished_id);
2350 $sskill = new ilSurveySkill($this);
2351 $sskill->writeAndAddSelfEvalSkills($user['usr_id']);
2352 }
2353
2354 // self eval writes skills on finishing
2355 if ($this->getMode() === self::MODE_IND_FEEDB) {
2356
2357 // we use a rater id like "a27" for anonymous or
2358 // "123" for non anonymous user
2359 // @todo: move this e.g. to participant manager
2360 $raters = $this->getRatersData($appr_id);
2361 $run_manager = $this->survey_service
2362 ->domain()
2363 ->execution()
2364 ->run($this, $this->user->getId(), $appr_id);
2365 $run = $run_manager->getById($finished_id);
2366 $rater_id = "";
2367 if ($run->getUserId() !== 0 && $run->getUserId() !== ANONYMOUS_USER_ID) {
2368 $rater_id = $run->getUserId();
2369 } else {
2370 foreach ($raters as $id => $rater) {
2371 if (($rater["code"] ?? "") === $run->getCode()) {
2372 $rater_id = $id;
2373 }
2374 }
2375 }
2376 $sskill = new ilSurveySkill($this);
2377 //$sskill->writeAndAddSelfEvalSkills($user['usr_id']);
2378 $sskill->writeAndAddIndFeedbackSkills($finished_id, $appr_id, $rater_id);
2379 }
2380
2381 $this->checkTutorNotification();
2382 }
2383
2390 public function setPage(
2391 int $finished_id,
2392 int $page_id
2393 ): void {
2394 $ilDB = $this->db;
2395
2396 $ilDB->manipulateF(
2397 "UPDATE svy_finished SET lastpage = %s WHERE finished_id = %s",
2398 array('integer','integer'),
2399 array(($page_id) ?: 0, $finished_id)
2400 );
2401 }
2402
2409 public function sendNotificationMail(
2410 int $a_user_id,
2411 string $a_anonymize_id,
2412 int $a_appr_id = 0
2413 ): void {
2414 // #12755
2415 $placeholders = array(
2416 "FIRST_NAME" => "firstname",
2417 "LAST_NAME" => "lastname",
2418 "LOGIN" => "login",
2419 // old style
2420 "firstname" => "firstname"
2421 );
2422
2423 //mailaddresses is just text split by commas.
2424 //sendMail can send emails if it gets an user id or an email as first parameter.
2425 $recipients = explode(",", $this->mailaddresses);
2426 foreach ($recipients as $recipient) {
2427 // #11298
2428 $ntf = new ilSystemNotification();
2429 $ntf->setLangModules(array("survey"));
2430 $ntf->setRefId($this->getRefId());
2431 $ntf->setSubjectLangId('finished_mail_subject');
2432
2433 $messagetext = $this->mailparticipantdata;
2434 $data = [];
2435 if (trim($messagetext)) {
2436 if (!$this->hasAnonymizedResults()) {
2437 $data = ilObjUser::_getUserData(array($a_user_id));
2438 $data = $data[0];
2439 }
2440 foreach ($placeholders as $key => $mapping) {
2441 if ($this->hasAnonymizedResults()) { // #16480
2442 $messagetext = str_replace('[' . $key . ']', '', $messagetext);
2443 } else {
2444 $messagetext = str_replace('[' . $key . ']', trim($data[$mapping]), $messagetext);
2445 }
2446 }
2447 $ntf->setIntroductionDirect($messagetext);
2448 } else {
2449 $ntf->setIntroductionLangId('survey_notification_finished_introduction');
2450 }
2451
2452 // 360°? add appraisee data
2453 if ($a_appr_id) {
2454 $ntf->addAdditionalInfo(
2455 'survey_360_appraisee',
2457 );
2458 }
2459
2460 $active_id = $this->getActiveID($a_user_id, $a_anonymize_id, $a_appr_id);
2461 $ntf->addAdditionalInfo(
2462 'results',
2463 $this->getParticipantTextResults($active_id),
2464 true
2465 );
2466
2467 $ntf->setGotoLangId('survey_notification_tutor_link');
2468 $ntf->setReasonLangId('survey_notification_finished_reason');
2469
2470 $recipient = trim($recipient);
2471 $user_id = (int) ilObjUser::_lookupId($recipient);
2472 if ($user_id > 0) {
2473 $ntf->sendMailAndReturnRecipients([$user_id]);
2474 } else {
2475 $user_ids = ilObjUser::getUserIdsByEmail($recipient);
2476 if (count($user_ids) > 0) {
2477 $ntf->sendMailAndReturnRecipients([current($user_ids)]);
2478 }
2479 }
2480 /* note: this block is replace by the single line above
2481 since the UI asks for account names and the "e-mail" fallback leads
2482 to strange issues like multiple mails. Also the test case has been
2483 adopted, see https://mantis.ilias.de/view.php?id=36327
2484 if (is_numeric($recipient)) {
2485 $lng = $ntf->getUserLanguage((int) $recipient);
2486 $ntf->sendMailAndReturnRecipients([(int) $recipient]);
2487 } else {
2488 $recipient = trim($recipient);
2489 $user_ids = ilObjUser::getUserIdsByEmail($recipient);
2490 if (empty($user_ids)) {
2491 $ntf->sendMailAndReturnRecipients([ilObjUser::_lookupId($recipient)]);
2492 } else {
2493 foreach ($user_ids as $user_id) {
2494 $lng = $ntf->getUserLanguage($user_id);
2495 $ntf->sendMailAndReturnRecipients(array($user_id));
2496 }
2497 }
2498 }*/
2499 }
2500 }
2501
2502 protected function getParticipantTextResults(
2503 int $active_id
2504 ): string {
2505 $textresult = "";
2506 $userResults = $this->getUserSpecificResults(array($active_id));
2507 $questions = $this->getSurveyQuestions(true);
2508 $questioncounter = 1;
2509 foreach ($questions as $question_id => $question_data) {
2510 $textresult .= $questioncounter++ . ". " . $question_data["title"] . "\n";
2511 $found = $userResults[$question_id][$active_id];
2512 $text = "";
2513 if (is_array($found)) {
2514 $text = implode("\n", $found);
2515 } else {
2516 $text = $found;
2517 }
2518 if ($text === '') {
2519 $text = self::getSurveySkippedValue();
2520 }
2521 $text = str_replace("<br />", "\n", $text);
2522 $textresult .= $text . "\n\n";
2523 }
2524 return $textresult;
2525 }
2526
2531 public function getActiveID(
2532 int $user_id,
2533 string $anonymize_id,
2534 int $appr_id
2535 ): ?int {
2536 $ilDB = $this->db;
2537
2538 // #15031 - should not matter if code was used by registered or anonymous (each code must be unique)
2539 if ($anonymize_id) {
2540 $result = $ilDB->queryF(
2541 "SELECT finished_id FROM svy_finished" .
2542 " WHERE survey_fi = %s AND anonymous_id = %s AND appr_id = %s",
2543 array('integer','text','integer'),
2544 array($this->getSurveyId(), $anonymize_id, $appr_id)
2545 );
2546 } else {
2547 $result = $ilDB->queryF(
2548 "SELECT finished_id FROM svy_finished" .
2549 " WHERE survey_fi = %s AND user_fi = %s AND appr_id = %s",
2550 array('integer','integer','integer'),
2551 array($this->getSurveyId(), $user_id, $appr_id)
2552 );
2553 }
2554 if ($result->numRows() === 0) {
2555 return null;
2556 } else {
2557 $row = $ilDB->fetchAssoc($result);
2558 return (int) $row["finished_id"];
2559 }
2560 }
2561
2566 public function getLastActivePage(
2567 int $active_id
2568 ): ?int {
2569 $ilDB = $this->db;
2570 $result = $ilDB->queryF(
2571 "SELECT lastpage FROM svy_finished WHERE finished_id = %s",
2572 array('integer'),
2573 array($active_id)
2574 );
2575 if ($row = $ilDB->fetchAssoc($result)) {
2576 return (int) $row["lastpage"];
2577 }
2578 return null;
2579 }
2580
2589 public function checkConstraint(
2590 array $constraint_data,
2591 ?array $working_data
2592 ): bool {
2593 if (!is_array($working_data) || count($working_data) === 0) {
2594 return 0;
2595 }
2596
2597 if ((count($working_data) === 1) and (strcmp($working_data[0]["value"], "") === 0)) {
2598 return 0;
2599 }
2600
2601 $found = false;
2602 foreach ($working_data as $data) {
2603 switch ($constraint_data["short"]) {
2604 case "<":
2605 if ($data["value"] < $constraint_data["value"]) {
2606 $found = true;
2607 }
2608 break;
2609
2610 case "<=":
2611 if ($data["value"] <= $constraint_data["value"]) {
2612 $found = true;
2613 }
2614 break;
2615
2616 case "=":
2617 if ($data["value"] == $constraint_data["value"]) {
2618 $found = true;
2619 }
2620 break;
2621
2622 case "<>":
2623 if ($data["value"] <> $constraint_data["value"]) {
2624 $found = true;
2625 }
2626 break;
2627
2628 case ">=":
2629 if ($data["value"] >= $constraint_data["value"]) {
2630 $found = true;
2631 }
2632 break;
2633
2634 case ">":
2635 if ($data["value"] > $constraint_data["value"]) {
2636 $found = true;
2637 }
2638 break;
2639 }
2640 if ($found) {
2641 break;
2642 }
2643 }
2644
2645 return (int) $found;
2646 }
2647
2651 public static function _hasDatasets(
2652 int $survey_id
2653 ): bool {
2654 global $DIC;
2655
2656 $ilDB = $DIC->database();
2657
2658 $result = $ilDB->queryF(
2659 "SELECT finished_id FROM svy_finished WHERE survey_fi = %s",
2660 array('integer'),
2661 array($survey_id)
2662 );
2663 return (bool) $result->numRows();
2664 }
2665
2671 public function getSurveyFinishedIds(): array
2672 {
2673 $ilDB = $this->db;
2674
2675 $users = array();
2676 $result = $ilDB->queryF(
2677 "SELECT * FROM svy_finished WHERE survey_fi = %s",
2678 array('integer'),
2679 array($this->getSurveyId())
2680 );
2681 if ($result->numRows()) {
2682 while ($row = $ilDB->fetchAssoc($result)) {
2683 $users[] = (int) $row["finished_id"];
2684 }
2685 }
2686 return $users;
2687 }
2688
2694 public function getUserSpecificResults(
2695 array $finished_ids
2696 ): array {
2697 $evaluation = array();
2698
2699 foreach (array_keys($this->getSurveyQuestions()) as $question_id) {
2700 // get question instance
2701 $question_type = SurveyQuestion::_getQuestionType($question_id);
2702 SurveyQuestion::_includeClass($question_type);
2703 $question = new $question_type();
2704 $question->loadFromDb($question_id);
2705
2706 $q_eval = SurveyQuestion::_instanciateQuestionEvaluation($question_id, $finished_ids);
2707 $q_res = $q_eval->getResults();
2708
2709 $data = array();
2710 foreach ($finished_ids as $user_id) {
2711 $data[$user_id] = $q_eval->parseUserSpecificResults($q_res, $user_id);
2712 }
2713
2714 $evaluation[$question_id] = $data;
2715 }
2716
2717 return $evaluation;
2718 }
2719
2725 int $active_id,
2726 bool $force_non_anonymous = false
2727 ): array {
2728 $ilDB = $this->db;
2729
2730 $surveySetting = new ilSetting("survey");
2731 $use_anonymous_id = $surveySetting->get("use_anonymous_id");
2732 $result = $ilDB->queryF(
2733 "SELECT * FROM svy_finished WHERE finished_id = %s",
2734 array('integer'),
2735 array($active_id)
2736 );
2737 $row = array();
2738 $foundrows = $result->numRows();
2739 if ($foundrows) {
2740 $row = $ilDB->fetchAssoc($result);
2741 }
2742 $name = ($use_anonymous_id) ? $row["anonymous_id"] : $this->lng->txt("anonymous");
2743 $userdata = array(
2744 "fullname" => $name,
2745 "sortname" => $name,
2746 "firstname" => "",
2747 "lastname" => "",
2748 "login" => "",
2749 "gender" => "",
2750 "active_id" => (string) $active_id
2751 );
2752 if ($foundrows) {
2753 if (($row["user_fi"] > 0) &&
2754 (($row["user_fi"] != ANONYMOUS_USER_ID &&
2755 !$this->hasAnonymizedResults() &&
2756 !$this->get360Mode()) || // 360° uses ANONYMIZE_CODE_ALL which is wrong - see ilObjSurveyGUI::afterSave()
2757 $force_non_anonymous)) {
2758 if (ilObjUser::_lookupLogin($row["user_fi"]) === '') {
2759 $userdata["fullname"] = $userdata["sortname"] = $this->lng->txt("deleted_user");
2760 } else {
2761 $user = new ilObjUser($row["user_fi"]);
2762 $userdata['usr_id'] = $row['user_fi'];
2763 $userdata["fullname"] = $user->getFullname();
2764 $gender = $user->getGender();
2765 if (strlen($gender) === 1) {
2766 $gender = $this->lng->txt("gender_$gender");
2767 }
2768 $userdata["gender"] = $gender;
2769 $userdata["firstname"] = $user->getFirstname();
2770 $userdata["lastname"] = $user->getLastname();
2771 $userdata["sortname"] = $user->getLastname() . ", " . $user->getFirstname();
2772 $userdata["login"] = $user->getLogin();
2773 }
2774 }
2775 if ($row["user_fi"] == 0 || $row["user_fi"] == ANONYMOUS_USER_ID) {
2776 $code = $this->code_manager->getByUserKey((string) $row["anonymous_id"]);
2777 if (!is_null($code) && $this->feature_config->usesAppraisees()) {
2778 $userdata["firstname"] = $code->getFirstName();
2779 $userdata["lastname"] = $code->getLastName();
2780 $userdata["sortname"] = $code->getLastName() . ", " . $code->getFirstName();
2781 }
2782 }
2783 }
2784 return $userdata;
2785 }
2786
2791 public function getEvaluationByUser(
2792 array $questions,
2793 int $active_id
2794 ): array {
2795 $ilDB = $this->db;
2796
2797 // collect all answers
2798 $answers = array();
2799 $result = $ilDB->queryF(
2800 "SELECT * FROM svy_answer WHERE active_fi = %s",
2801 array('integer'),
2802 array($active_id)
2803 );
2804 while ($row = $ilDB->fetchAssoc($result)) {
2805 if (!is_array($answers[$row["question_fi"]])) {
2806 $answers[$row["question_fi"]] = array();
2807 }
2808 $answers[$row["question_fi"]][] = $row;
2809 }
2810 $userdata = $this->getUserDataFromActiveId($active_id);
2811 $resultset = array(
2812 "name" => $userdata["fullname"],
2813 "firstname" => $userdata["firstname"],
2814 "lastname" => $userdata["lastname"],
2815 "login" => $userdata["login"],
2816 "gender" => $userdata["gender"],
2817 "answers" => array()
2818 );
2819 foreach ($questions as $key => $question) {
2820 if (array_key_exists($key, $answers)) {
2821 $resultset["answers"][$key] = $answers[$key];
2822 } else {
2823 $resultset["answers"][$key] = array();
2824 }
2825 sort($resultset["answers"][$key]);
2826 }
2827 return $resultset;
2828 }
2829
2834 public function getQuestionsTable(
2835 array $arrFilter
2836 ): array {
2837 $ilDB = $this->db;
2838 $where = "";
2839 if (array_key_exists('title', $arrFilter) && strlen($arrFilter['title'])) {
2840 $where .= " AND " . $ilDB->like('svy_question.title', 'text', "%%" . $arrFilter['title'] . "%%");
2841 }
2842 if (array_key_exists('description', $arrFilter) && strlen($arrFilter['description'])) {
2843 $where .= " AND " . $ilDB->like('svy_question.description', 'text', "%%" . $arrFilter['description'] . "%%");
2844 }
2845 if (array_key_exists('author', $arrFilter) && strlen($arrFilter['author'])) {
2846 $where .= " AND " . $ilDB->like('svy_question.author', 'text', "%%" . $arrFilter['author'] . "%%");
2847 }
2848 if (array_key_exists('type', $arrFilter) && strlen($arrFilter['type'])) {
2849 $where .= " AND svy_qtype.type_tag = " . $ilDB->quote($arrFilter['type'], 'text');
2850 }
2851 if (array_key_exists('spl', $arrFilter) && strlen($arrFilter['spl'])) {
2852 $where .= " AND svy_question.obj_fi = " . $ilDB->quote($arrFilter['spl'], 'integer');
2853 }
2854
2855 $spls = $this->getAvailableQuestionpools(true, false, false);
2856 $forbidden = " AND " . $ilDB->in('svy_question.obj_fi', array_keys($spls), false, 'integer');
2857 $forbidden .= " AND svy_question.complete = " . $ilDB->quote("1", 'text');
2858 $existing = "";
2859 $existing_questions = $this->getExistingQuestions();
2860 if (count($existing_questions)) {
2861 $existing = " AND " . $ilDB->in('svy_question.question_id', $existing_questions, true, 'integer');
2862 }
2863
2865
2866 $query_result = $ilDB->query("SELECT svy_question.*, svy_qtype.type_tag, svy_qtype.plugin, object_reference.ref_id" .
2867 " FROM svy_question, svy_qtype, object_reference" .
2868 " WHERE svy_question.original_id IS NULL" . $forbidden . $existing .
2869 " AND svy_question.obj_fi = object_reference.obj_id AND svy_question.tstamp > 0" .
2870 " AND svy_question.questiontype_fi = svy_qtype.questiontype_id " . $where);
2871
2872 $rows = array();
2873 if ($query_result->numRows()) {
2874 while ($row = $ilDB->fetchAssoc($query_result)) {
2875 if (array_key_exists('spl_txt', $arrFilter) && strlen($arrFilter['spl_txt'])) {
2876 if (stripos($spls[$row["obj_fi"]], $arrFilter['spl_txt']) === false) {
2877 continue;
2878 }
2879 }
2880
2881 $row['ttype'] = $trans[$row['type_tag']];
2882 if ($row["plugin"]) {
2883 if ($this->isPluginActive($row["type_tag"])) {
2884 $rows[] = $row;
2885 }
2886 } else {
2887 $rows[] = $row;
2888 }
2889 }
2890 }
2891 return $rows;
2892 }
2893
2898 public function getQuestionblocksTable(
2899 array $arrFilter
2900 ): array {
2901 $ilDB = $this->db;
2902
2903 $where = "";
2904 if (array_key_exists('title', $arrFilter) && strlen($arrFilter['title'])) {
2905 $where .= " AND " . $ilDB->like('svy_qblk.title', 'text', "%%" . $arrFilter['title'] . "%%");
2906 }
2907
2908 $query_result = $ilDB->query("SELECT svy_qblk.*, svy_svy.obj_fi FROM svy_qblk , svy_qblk_qst, svy_svy WHERE " .
2909 "svy_qblk.questionblock_id = svy_qblk_qst.questionblock_fi AND svy_svy.survey_id = svy_qblk_qst.survey_fi " .
2910 "$where GROUP BY svy_qblk.questionblock_id, svy_qblk.title, svy_qblk.show_questiontext, svy_qblk.show_blocktitle, " .
2911 "svy_qblk.owner_fi, svy_qblk.tstamp, svy_svy.obj_fi");
2912 $rows = array();
2913 if ($query_result->numRows()) {
2914 $survey_ref_ids = ilUtil::_getObjectsByOperations("svy", "write");
2915 $surveytitles = array();
2916 foreach ($survey_ref_ids as $survey_ref_id) {
2917 $survey_id = ilObject::_lookupObjId($survey_ref_id);
2918 $surveytitles[$survey_id] = ilObject::_lookupTitle($survey_id);
2919 }
2920 while ($row = $ilDB->fetchAssoc($query_result)) {
2921 $questions_array = $this->getQuestionblockQuestions($row["questionblock_id"]);
2922 $counter = 1;
2923 foreach ($questions_array as $key => $value) {
2924 $questions_array[$key] = "$counter. $value";
2925 $counter++;
2926 }
2927 if (strlen($surveytitles[$row["obj_fi"]] ?? "")) { // only questionpools which are not in trash
2928 $rows[$row["questionblock_id"]] = array(
2929 "questionblock_id" => $row["questionblock_id"],
2930 "title" => $row["title"],
2931 "svy" => $surveytitles[$row["obj_fi"]],
2932 "contains" => implode(", ", $questions_array),
2933 "owner" => $row["owner_fi"]
2934 );
2935 }
2936 }
2937 }
2938 return $rows;
2939 }
2940
2945 public function toXML(): string
2946 {
2947 $a_xml_writer = new ilXmlWriter();
2948 // set xml header
2949 $a_xml_writer->xmlHeader();
2950 $attrs = array(
2951 "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
2952 "xsi:noNamespaceSchemaLocation" => "https://www.ilias.de/download/xsd/ilias_survey_4_2.xsd"
2953 );
2954 $a_xml_writer->xmlStartTag("surveyobject", $attrs);
2955 $attrs = array(
2956 "id" => $this->getSurveyId(),
2957 "title" => $this->getTitle()
2958 );
2959 $a_xml_writer->xmlStartTag("survey", $attrs);
2960
2961 $a_xml_writer->xmlElement("description", null, $this->getDescription());
2962 $a_xml_writer->xmlElement("author", null, $this->getAuthor());
2963 $a_xml_writer->xmlStartTag("objectives");
2964 $attrs = array(
2965 "label" => "introduction"
2966 );
2967 $this->addMaterialTag($a_xml_writer, $this->getIntroduction(), true, true, $attrs);
2968 $attrs = array(
2969 "label" => "outro"
2970 );
2971 $this->addMaterialTag($a_xml_writer, $this->getOutro(), true, true, $attrs);
2972 $a_xml_writer->xmlEndTag("objectives");
2973
2974 if ($this->getAnonymize()) {
2975 $attribs = array("enabled" => "1");
2976 } else {
2977 $attribs = array("enabled" => "0");
2978 }
2979 $a_xml_writer->xmlElement("anonymisation", $attribs);
2980 $a_xml_writer->xmlStartTag("restrictions");
2981 if ($this->getAnonymize() === 2) {
2982 $attribs = array("type" => "free");
2983 } else {
2984 $attribs = array("type" => "restricted");
2985 }
2986 $a_xml_writer->xmlElement("access", $attribs);
2987 if ($this->getStartDate()) {
2988 $attrs = array("type" => "date");
2989 preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getStartDate(), $matches);
2990 $a_xml_writer->xmlElement("startingtime", $attrs, sprintf("%04d-%02d-%02dT%02d:%02d:00", $matches[1], $matches[2], $matches[3], $matches[4], $matches[5]));
2991 }
2992 if ($this->getEndDate()) {
2993 $attrs = array("type" => "date");
2994 preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getEndDate(), $matches);
2995 $a_xml_writer->xmlElement("endingtime", $attrs, sprintf("%04d-%02d-%02dT%02d:%02d:00", $matches[1], $matches[2], $matches[3], $matches[4], $matches[5]));
2996 }
2997 $a_xml_writer->xmlEndTag("restrictions");
2998
2999 // constraints
3000 $pages = $this->getSurveyPages();
3001 $hasconstraints = false;
3002 foreach ($pages as $question_array) {
3003 foreach ($question_array as $question) {
3004 if (count($question["constraints"])) {
3005 $hasconstraints = true;
3006 }
3007 }
3008 }
3009
3010 if ($hasconstraints) {
3011 $a_xml_writer->xmlStartTag("constraints");
3012 foreach ($pages as $question_array) {
3013 foreach ($question_array as $question) {
3014 if (count($question["constraints"])) {
3015 // found constraints
3016 foreach ($question["constraints"] as $constraint) {
3017 $attribs = array(
3018 "sourceref" => $question["question_id"],
3019 "destref" => $constraint["question"],
3020 "relation" => $constraint["short"],
3021 "value" => $constraint["value"],
3022 "conjunction" => $constraint["conjunction"]
3023 );
3024 $a_xml_writer->xmlElement("constraint", $attribs);
3025 }
3026 }
3027 }
3028 }
3029 $a_xml_writer->xmlEndTag("constraints");
3030 }
3031
3032 // add the rest of the preferences in qtimetadata tags, because there is no correspondent definition in QTI
3033 $a_xml_writer->xmlStartTag("metadata");
3034
3035 $custom_properties = array();
3036 $custom_properties["evaluation_access"] = $this->getEvaluationAccess();
3037 $custom_properties["status"] = !$this->getOfflineStatus();
3038 $custom_properties["display_question_titles"] = $this->getShowQuestionTitles();
3039 $custom_properties["pool_usage"] = (int) $this->getPoolUsage();
3040
3041 $custom_properties["own_results_view"] = (int) $this->hasViewOwnResults();
3042 $custom_properties["own_results_mail"] = (int) $this->hasMailOwnResults();
3043 $custom_properties["confirmation_mail"] = (int) $this->hasMailConfirmation();
3044
3045 $custom_properties["anon_user_list"] = (int) $this->hasAnonymousUserList();
3046 $custom_properties["mode"] = $this->getMode();
3047 $custom_properties["mode_360_self_eval"] = (int) $this->get360SelfEvaluation();
3048 $custom_properties["mode_360_self_rate"] = (int) $this->get360SelfRaters();
3049 $custom_properties["mode_360_self_appr"] = (int) $this->get360SelfAppraisee();
3050 $custom_properties["mode_360_results"] = $this->get360Results();
3051 $custom_properties["mode_skill_service"] = (int) $this->getSkillService();
3052 $custom_properties["mode_self_eval_results"] = $this->getSelfEvaluationResults();
3053
3054
3055 // :TODO: skills?
3056
3057 // reminder/tutor notification are (currently?) not exportable
3058
3059 foreach ($custom_properties as $label => $value) {
3060 $a_xml_writer->xmlStartTag("metadatafield");
3061 $a_xml_writer->xmlElement("fieldlabel", null, $label);
3062 $a_xml_writer->xmlElement("fieldentry", null, $value);
3063 $a_xml_writer->xmlEndTag("metadatafield");
3064 }
3065
3066 $a_xml_writer->xmlStartTag("metadatafield");
3067 $a_xml_writer->xmlElement("fieldlabel", null, "SCORM");
3068 $md = new ilMD($this->getId(), 0, $this->getType());
3069 $writer = new ilXmlWriter();
3070 $md->toXML($writer);
3071 $metadata = $writer->xmlDumpMem();
3072 $a_xml_writer->xmlElement("fieldentry", null, $metadata);
3073 $a_xml_writer->xmlEndTag("metadatafield");
3074
3075 $a_xml_writer->xmlEndTag("metadata");
3076 $a_xml_writer->xmlEndTag("survey");
3077
3078 $attribs = array("id" => $this->getId());
3079 $a_xml_writer->xmlStartTag("surveyquestions", $attribs);
3080 // add questionblock descriptions
3081 foreach ($pages as $question_array) {
3082 if (count($question_array) > 1) {
3083 $attribs = array("id" => $question_array[0]["question_id"]);
3084 $attribs = array(
3085 "showQuestiontext" => $question_array[0]["questionblock_show_questiontext"],
3086 "showBlocktitle" => $question_array[0]["questionblock_show_blocktitle"],
3087 "compressView" => $question_array[0]["questionblock_compress_view"]
3088 );
3089 $a_xml_writer->xmlStartTag("questionblock", $attribs);
3090 if (strlen($question_array[0]["questionblock_title"])) {
3091 $a_xml_writer->xmlElement("questionblocktitle", null, $question_array[0]["questionblock_title"]);
3092 }
3093 }
3094 foreach ($question_array as $question) {
3095 if (strlen($question["heading"])) {
3096 $a_xml_writer->xmlElement("textblock", null, $question["heading"]);
3097 }
3098 $questionObject = self::_instanciateQuestion($question["question_id"]);
3099 //questionObject contains all the fields from the database. (loadFromDb)
3100 //we don't need the value from svy_qst_oblig table, we already have the values from svy_question table.
3101 //if ($questionObject !== FALSE) $questionObject->insertXML($a_xml_writer, FALSE, $obligatory_states[$question["question_id"]]);
3102 if ($questionObject !== false) {
3103 $questionObject->insertXML($a_xml_writer, false);
3104 }
3105 }
3106 if (count($question_array) > 1) {
3107 $a_xml_writer->xmlEndTag("questionblock");
3108 }
3109 }
3110
3111 $a_xml_writer->xmlEndTag("surveyquestions");
3112 $a_xml_writer->xmlEndTag("surveyobject");
3113 $xml = $a_xml_writer->xmlDumpMem(false);
3114 return $xml;
3115 }
3116
3121 public static function _instanciateQuestion(
3122 int $question_id
3123 ): ?SurveyQuestion {
3124 if ($question_id < 1) {
3125 return null;
3126 }
3127 $question_type = SurveyQuestion::_getQuestionType($question_id);
3128 if ($question_type === '') {
3129 return null;
3130 }
3131 SurveyQuestion::_includeClass($question_type);
3132 $question = new $question_type();
3133 $question->loadFromDb($question_id);
3134 return $question;
3135 }
3136
3142 public function locateImportFiles(
3143 string $a_dir
3144 ): ?array {
3145 if (!is_dir($a_dir) || is_int(strpos($a_dir, ".."))) {
3146 return null;
3147 }
3148 $importDirectory = "";
3149 $xmlFile = "";
3150
3151 $current_dir = opendir($a_dir);
3152 $files = array();
3153 while ($entryname = readdir($current_dir)) {
3154 $files[] = $entryname;
3155 }
3156
3157 foreach ($files as $file) {
3158 if (is_dir($a_dir . "/" . $file) and ($file !== "." and $file !== "..")) {
3159 // found directory created by zip
3160 $importDirectory = $a_dir . "/" . $file;
3161 }
3162 }
3163 closedir($current_dir);
3164 if ($importDirectory !== '') {
3165 // find the xml file
3166 $current_dir = opendir($importDirectory);
3167 $files = array();
3168 while ($entryname = readdir($current_dir)) {
3169 $files[] = $entryname;
3170 }
3171 foreach ($files as $file) {
3172 if (is_file($importDirectory . "/" . $file) &&
3173 ($file !== "." && $file !== "..") &&
3174 (preg_match("/^[0-9]{10}__[0-9]+__(svy_)*[0-9]+\.[A-Za-z]{1,3}$/", $file) ||
3175 preg_match("/^[0-9]{10}__[0-9]+__(survey__)*[0-9]+\.[A-Za-z]{1,3}$/", $file))) {
3176 // found xml file
3177 $xmlFile = $importDirectory . "/" . $file;
3178 }
3179 }
3180 }
3181 return array("dir" => $importDirectory, "xml" => $xmlFile);
3182 }
3183
3191 public function importObject(
3192 array $file_info,
3193 int $svy_qpl_id
3194 ): string {
3195 if ($svy_qpl_id < 1) {
3196 $svy_qpl_id = -1;
3197 }
3198 // check if file was uploaded
3199 $source = $file_info["tmp_name"];
3200 $error = "";
3201 if (($source === 'none') || (!$source) || $file_info["error"] > UPLOAD_ERR_OK) {
3202 $error = $this->lng->txt("import_no_file_selected");
3203 }
3204 // check correct file type
3205 $isXml = false;
3206 $isZip = false;
3207 if ((strcmp($file_info["type"], "text/xml") === 0) || (strcmp($file_info["type"], "application/xml") === 0)) {
3208 $this->svy_log->debug("isXML");
3209 $isXml = true;
3210 }
3211 // too many different mime-types, so we use the suffix
3212 $suffix = pathinfo($file_info["name"]);
3213 if (strcmp(strtolower($suffix["extension"]), "zip") === 0) {
3214 $this->svy_log->debug("isZip");
3215 $isZip = true;
3216 }
3217 if (!$isXml && !$isZip) {
3218 $error = $this->lng->txt("import_wrong_file_type");
3219 $this->svy_log->debug("Survey: Import error. Filetype was \"" . $file_info["type"] . "\"");
3220 }
3221 if ($error === '') {
3222 // import file as a survey
3223 $import_dir = $this->getImportDirectory();
3224 $import_subdir = "";
3225 $importfile = "";
3226 if ($isZip) {
3227 $importfile = $import_dir . "/" . $file_info["name"];
3228 ilFileUtils::moveUploadedFile($source, $file_info["name"], $importfile);
3229 ilFileUtils::unzip($importfile);
3230 $found = $this->locateImportFiles($import_dir);
3231 if (!((strlen($found["dir"]) > 0) && (strlen($found["xml"]) > 0))) {
3232 $error = $this->lng->txt("wrong_import_file_structure");
3233 return $error;
3234 }
3235 $importfile = $found["xml"];
3236 $import_subdir = $found["dir"];
3237 } else {
3238 $importfile = tempnam($import_dir, "survey_import");
3239 ilFileUtils::moveUploadedFile($source, $file_info["name"], $importfile);
3240 }
3241
3242 $this->svy_log->debug("Import file = $importfile");
3243 $this->svy_log->debug("Import subdir = $import_subdir");
3244
3245 $fh = fopen($importfile, 'rb');
3246 if (!$fh) {
3247 $error = $this->lng->txt("import_error_opening_file");
3248 return $error;
3249 }
3250 $xml = fread($fh, filesize($importfile));
3251 $result = fclose($fh);
3252 if (!$result) {
3253 $error = $this->lng->txt("import_error_closing_file");
3254 return $error;
3255 }
3256
3257 $this->import_manager->clearMobs();
3258 if (strpos($xml, "questestinterop")) {
3259 throw new ilInvalidSurveyImportFileException("Unsupported survey version (< 3.8) found.");
3260 } else {
3261 $this->svy_log->debug("survey id = " . $this->getId());
3262 $this->svy_log->debug("question pool id = " . $svy_qpl_id);
3263
3264 $imp = new ilImport();
3265 $config = $imp->getConfig("Modules/Survey");
3266 $config->setQuestionPoolID($svy_qpl_id);
3267 $imp->getMapping()->addMapping("Modules/Survey", "svy", 0, $this->getId());
3268 $imp->importFromDirectory($import_subdir, "svy", "Modules/Survey");
3269 $this->svy_log->debug("config(Modules/survey)->getQuestionPoolId =" . $config->getQuestionPoolID());
3270 }
3271 }
3272 return $error;
3273 }
3274
3275 public function cloneObject(int $target_id, int $copy_id = 0, bool $omit_tree = false): ?ilObject
3276 {
3277 $ilDB = $this->db;
3278
3279 $this->loadFromDb();
3280
3281 //survey mode
3282 $svy_type = $this->getMode();
3283
3285 $newObj = parent::cloneObject($target_id, $copy_id, $omit_tree);
3286 $this->cloneMetaData($newObj);
3287 $newObj->updateMetaData();
3288
3289 $newObj->setAuthor($this->getAuthor());
3290 $newObj->setIntroduction($this->getIntroduction());
3291 $newObj->setOutro($this->getOutro());
3292 $newObj->setEvaluationAccess($this->getEvaluationAccess());
3293 $newObj->setStartDate($this->getStartDate());
3294 $newObj->setEndDate($this->getEndDate());
3295 $newObj->setAnonymize($this->getAnonymize());
3296 $newObj->setShowQuestionTitles($this->getShowQuestionTitles());
3297 $newObj->setPoolUsage($this->getPoolUsage());
3298 $newObj->setViewOwnResults($this->hasViewOwnResults());
3299 $newObj->setMailOwnResults($this->hasMailOwnResults());
3300 $newObj->setMailConfirmation($this->hasMailConfirmation());
3301 $newObj->setAnonymousUserList($this->hasAnonymousUserList());
3302
3303 $newObj->setMode($this->getMode());
3304 $newObj->set360SelfEvaluation($this->get360SelfEvaluation());
3305 $newObj->set360SelfAppraisee($this->get360SelfAppraisee());
3306 $newObj->set360SelfRaters($this->get360SelfRaters());
3307 $newObj->set360Results($this->get360Results());
3308 $newObj->setSkillService($this->getSkillService());
3309
3310 // reminder/notification
3311 $newObj->setReminderStatus($this->getReminderStatus());
3312 $newObj->setReminderStart($this->getReminderStart());
3313 $newObj->setReminderEnd($this->getReminderEnd());
3314 $newObj->setReminderFrequency($this->getReminderFrequency());
3315 $newObj->setReminderTarget($this->getReminderTarget());
3316 $newObj->setReminderTemplate($this->getReminderTemplate());
3317 // reminder_last_sent must not be copied!
3318 $newObj->setTutorNotificationStatus($this->getTutorNotificationStatus());
3319 $newObj->setTutorNotificationRecipients($this->getTutorNotificationRecipients());
3320 $newObj->setTutorNotificationTarget($this->getTutorNotificationTarget());
3321 $newObj->setTutorResultsStatus($this->getTutorResultsStatus());
3322 $newObj->setTutorResultsRecipients($this->getTutorResultsRecipients());
3323
3324 $newObj->setMailNotification($this->getMailNotification());
3325 $newObj->setMailAddresses($this->getMailAddresses());
3326 $newObj->setMailParticipantData($this->getMailParticipantData());
3327
3328 $question_pointer = array();
3329 // clone the questions
3330 $mapping = array();
3331
3332 foreach ($this->questions as $key => $question_id) {
3334 $question = self::_instanciateQuestion($question_id);
3335 if ($question) { // #10824
3336 $question->id = -1;
3337 $original_id = SurveyQuestion::_getOriginalId($question_id, false);
3338 $question->setObjId($newObj->getId());
3339 $question->saveToDb($original_id);
3340 $newObj->questions[$key] = $question->getId();
3341 $question_pointer[$question_id] = $question->getId();
3342 $mapping[$question_id] = $question->getId();
3343 }
3344 }
3345
3346 //copy online status if object is not the root copy object
3347 $cp_options = ilCopyWizardOptions::_getInstance($copy_id);
3348
3349 if (!$cp_options->isRootNode($this->getRefId())) {
3350 $newObj->setOfflineStatus($this->getOfflineStatus());
3351 }
3352
3353 $newObj->saveToDb();
3354 $newObj->cloneTextblocks($mapping);
3355
3356 // #14929
3357 if (($svy_type === self::MODE_360 || $svy_type === self::MODE_SELF_EVAL) &&
3358 $this->getSkillService()) {
3359 $src_skills = new ilSurveySkill($this);
3360 $tgt_skills = new ilSurveySkill($newObj);
3361
3362 foreach ($mapping as $src_qst_id => $tgt_qst_id) {
3363 $qst_skill = $src_skills->getSkillForQuestion($src_qst_id);
3364 if ($qst_skill) {
3365 $tgt_skills->addQuestionSkillAssignment($tgt_qst_id, $qst_skill["base_skill_id"], $qst_skill["tref_id"]);
3366 }
3367 }
3368
3369 $thresholds = new ilSurveySkillThresholds($this);
3370 $thresholds->cloneTo($newObj, $mapping);
3371 }
3372
3373 // clone the questionblocks
3374 $questionblocks = array();
3375 $questionblock_questions = array();
3376 $result = $ilDB->queryF(
3377 "SELECT * FROM svy_qblk_qst WHERE survey_fi = %s",
3378 array('integer'),
3379 array($this->getSurveyId())
3380 );
3381 if ($result->numRows() > 0) {
3382 while ($row = $ilDB->fetchAssoc($result)) {
3383 $questionblock_questions[] = $row;
3384 $questionblocks[$row["questionblock_fi"]] = $row["questionblock_fi"];
3385 }
3386 }
3387 // create new questionblocks
3388 foreach ($questionblocks as $key => $value) {
3389 $questionblock = self::_getQuestionblock($key);
3390 $questionblock_id = self::_addQuestionblock(
3391 (string) $questionblock["title"],
3392 (int) $questionblock["owner_fi"],
3393 (bool) $questionblock["show_questiontext"],
3394 (bool) $questionblock["show_blocktitle"],
3395 (bool) $questionblock["compress_view"]
3396 );
3397 $questionblocks[$key] = $questionblock_id;
3398 }
3399 // create new questionblock questions
3400 foreach ($questionblock_questions as $key => $value) {
3401 if ($questionblocks[$value["questionblock_fi"]] &&
3402 $question_pointer[$value["question_fi"]]) {
3403 $next_id = $ilDB->nextId('svy_qblk_qst');
3404 $affectedRows = $ilDB->manipulateF(
3405 "INSERT INTO svy_qblk_qst (qblk_qst_id, survey_fi, questionblock_fi, question_fi) " .
3406 "VALUES (%s, %s, %s, %s)",
3407 array('integer','integer','integer','integer'),
3408 array($next_id, $newObj->getSurveyId(), $questionblocks[$value["questionblock_fi"]], $question_pointer[$value["question_fi"]])
3409 );
3410 }
3411 }
3412
3413 // clone the constraints
3414 $constraints = self::_getConstraints($this->getSurveyId());
3415 $newConstraints = array();
3416 foreach ($constraints as $key => $constraint) {
3417 if ($question_pointer[$constraint["for_question"]] &&
3418 $question_pointer[$constraint["question"]]) {
3419 if (!array_key_exists($constraint['id'], $newConstraints)) {
3420 $constraint_id = $newObj->addConstraint($question_pointer[$constraint["question"]], $constraint["relation_id"], $constraint["value"], $constraint['conjunction']);
3421 $newConstraints[$constraint['id']] = $constraint_id;
3422 }
3423 $newObj->addConstraintToQuestion($question_pointer[$constraint["for_question"]], $newConstraints[$constraint['id']]);
3424 }
3425 }
3426
3427 // #16210 - clone LP settings
3428 $obj_settings = new ilLPObjSettings($this->getId());
3429 $obj_settings->cloneSettings($newObj->getId());
3430 unset($obj_settings);
3431
3432 return $newObj;
3433 }
3434
3438 public function getTextblock(
3439 int $question_id
3440 ): string {
3441 $ilDB = $this->db;
3442 $result = $ilDB->queryF(
3443 "SELECT * FROM svy_svy_qst WHERE question_fi = %s",
3444 array('integer'),
3445 array($question_id)
3446 );
3447 if ($row = $ilDB->fetchAssoc($result)) {
3448 return $row["heading"] ?? "";
3449 } else {
3450 return "";
3451 }
3452 }
3453
3459 public function cloneTextblocks(
3460 array $mapping
3461 ): void {
3462 foreach ($mapping as $original_id => $new_id) {
3463 $textblock = $this->getTextblock($original_id);
3464 $this->saveHeading(ilUtil::stripSlashes($textblock, true, ilObjAdvancedEditing::_getUsedHTMLTagsAsString("survey")), $new_id);
3465 }
3466 }
3467
3474 public function createExportDirectory(): void
3475 {
3476 $svy_data_dir = ilFileUtils::getDataDir() . "/svy_data";
3477 ilFileUtils::makeDir($svy_data_dir);
3478 if (!is_writable($svy_data_dir)) {
3479 throw new ilSurveyException("Survey Data Directory (" . $svy_data_dir . ") not writeable.");
3480 }
3481
3482 // create learning module directory (data_dir/lm_data/lm_<id>)
3483 $svy_dir = $svy_data_dir . "/svy_" . $this->getId();
3484 ilFileUtils::makeDir($svy_dir);
3485 if (!is_dir($svy_dir)) {
3486 throw new ilSurveyException("Creation of Survey Directory failed.");
3487 }
3488 // create Export subdirectory (data_dir/lm_data/lm_<id>/Export)
3489 $export_dir = $svy_dir . "/export";
3490 ilFileUtils::makeDir($export_dir);
3491 if (!is_dir($export_dir)) {
3492 throw new ilSurveyException("Creation of Export Directory failed.");
3493 }
3494 }
3495
3499 public function getExportDirectory(): string
3500 {
3501 $export_dir = ilFileUtils::getDataDir() . "/svy_data" . "/svy_" . $this->getId() . "/export";
3502
3503 return $export_dir;
3504 }
3505
3512 public function createImportDirectory(): void
3513 {
3514 $svy_data_dir = ilFileUtils::getDataDir() . "/svy_data";
3515 ilFileUtils::makeDir($svy_data_dir);
3516
3517 if (!is_writable($svy_data_dir)) {
3518 throw new ilSurveyException("Survey Data Directory (" . $svy_data_dir . ") not writeable.");
3519 }
3520
3521 // create test directory (data_dir/svy_data/svy_<id>)
3522 $svy_dir = $svy_data_dir . "/svy_" . $this->getId();
3523 ilFileUtils::makeDir($svy_dir);
3524 if (!is_dir($svy_dir)) {
3525 throw new ilSurveyException("Creation of Survey Directory failed.");
3526 }
3527
3528 // create import subdirectory (data_dir/svy_data/svy_<id>/import)
3529 $import_dir = $svy_dir . "/import";
3530 ilFileUtils::makeDir($import_dir);
3531 if (!is_dir($import_dir)) {
3532 throw new ilSurveyException("Creation of Import Directory failed.");
3533 }
3534 }
3535
3540 public function getImportDirectory(): string
3541 {
3542 $import_dir = ilFileUtils::getDataDir() . "/svy_data" .
3543 "/svy_" . $this->getId() . "/import";
3544 if (!is_dir($import_dir)) {
3545 ilFileUtils::makeDirParents($import_dir);
3546 }
3547 if (is_dir($import_dir)) {
3548 return $import_dir;
3549 } else {
3550 return "";
3551 }
3552 }
3553
3559 public function saveHeading(
3560 string $heading,
3561 int $insertbefore
3562 ): void {
3563 $ilDB = $this->db;
3564 if ($heading) {
3565 $ilDB->manipulateF(
3566 "UPDATE svy_svy_qst SET heading=%s WHERE survey_fi=%s AND question_fi=%s",
3567 array('text','integer','integer'),
3568 array($heading, $this->getSurveyId(), $insertbefore)
3569 );
3570 } else {
3571 $ilDB->manipulateF(
3572 "UPDATE svy_svy_qst SET heading=%s WHERE survey_fi=%s AND question_fi=%s",
3573 array('text','integer','integer'),
3574 array(null, $this->getSurveyId(), $insertbefore)
3575 );
3576 }
3577 }
3578
3583 public function isAnonymizedParticipant(string $key): bool
3584 {
3585 $ilDB = $this->db;
3586
3587 $result = $ilDB->queryF(
3588 "SELECT finished_id FROM svy_finished WHERE anonymous_id = %s AND survey_fi = %s",
3589 array('text','integer'),
3590 array($key, $this->getSurveyId())
3591 );
3592 return $result->numRows() === 1;
3593 }
3594
3605 array $a_codes = null,
3606 array $a_ids = null
3607 ): string {
3608 $ilDB = $this->db;
3609 $ilUser = $this->user;
3610 $lng = $this->lng;
3611
3612 $sql = "SELECT svy_anonymous.*, svy_finished.state" .
3613 " FROM svy_anonymous" .
3614 " LEFT JOIN svy_finished ON (svy_anonymous.survey_key = svy_finished.anonymous_id)" .
3615 " WHERE svy_anonymous.survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") .
3616 " AND svy_anonymous.user_key IS NULL";
3617
3618 if ($a_codes) {
3619 $sql .= " AND " . $ilDB->in("svy_anonymous.survey_key", $a_codes, "", "text");
3620 } elseif ($a_ids) {
3621 $sql .= " AND " . $ilDB->in("svy_anonymous.anonymous_id", $a_ids, "", "text");
3622 }
3623
3624 $export = array();
3625
3626 // #14905
3627 $titles = array();
3628 $titles[] = '"' . $lng->txt("survey_code") . '"';
3629 $titles[] = '"' . $lng->txt("email") . '"';
3630 $titles[] = '"' . $lng->txt("lastname") . '"';
3631 $titles[] = '"' . $lng->txt("firstname") . '"';
3632 $titles[] = '"' . $lng->txt("create_date") . '"';
3633 $titles[] = '"' . $lng->txt("used") . '"';
3634 $titles[] = '"' . $lng->txt("mail_sent_short") . '"';
3635 $titles[] = '"' . $lng->txt("survey_code_url") . '"';
3636 $export[] = implode(";", $titles);
3637
3638 $result = $ilDB->query($sql);
3639 $default_lang = $ilUser->getPref("survey_code_language");
3640 while ($row = $ilDB->fetchAssoc($result)) {
3641 $item = array();
3642 $item[] = $row["survey_key"];
3643
3644 if ($row["externaldata"]) {
3645 $ext = unserialize($row["externaldata"], ['allowed_classes' => false]);
3646 $item[] = $ext["email"];
3647 $item[] = $ext["lastname"];
3648 $item[] = $ext["firstname"];
3649 } else {
3650 $item[] = "";
3651 $item[] = "";
3652 $item[] = "";
3653 }
3654
3655 // No relative (today, tomorrow...) dates in export.
3656 $date = new ilDateTime($row['tstamp'], IL_CAL_UNIX);
3657 $item[] = $date->get(IL_CAL_DATETIME);
3658
3659 $item[] = ($this->isSurveyCodeUsed($row["survey_key"])) ? 1 : 0;
3660 $item[] = ($row["sent"]) ? 1 : 0;
3661
3662 $params = array("accesscode" => $row["survey_key"]);
3663 if ($default_lang) {
3664 $params["lang"] = $default_lang;
3665 }
3666 $item[] = ilLink::_getLink($this->getRefId(), "svy", $params);
3667
3668 $export[] = '"' . implode('";"', $item) . '"';
3669 }
3670 return implode("\n", $export);
3671 }
3672
3679 array $ids = null,
3680 string $lang = null
3681 ): array {
3682 $ilDB = $this->db;
3683
3684 $codes = array();
3685
3686 $sql = "SELECT svy_anonymous.*, svy_finished.state" .
3687 " FROM svy_anonymous" .
3688 " LEFT JOIN svy_finished ON (svy_anonymous.survey_key = svy_finished.anonymous_id)" .
3689 " WHERE svy_anonymous.survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") /*.
3690 " AND svy_anonymous.user_key IS NULL" */; // #15860
3691
3692 if ($ids) {
3693 $sql .= " AND " . $ilDB->in("svy_anonymous.anonymous_id", $ids, "", "integer");
3694 }
3695
3696 $sql .= " ORDER BY tstamp, survey_key ASC";
3697 $result = $ilDB->query($sql);
3698 if ($result->numRows() > 0) {
3699 while ($row = $ilDB->fetchAssoc($result)) {
3700 $href = "";
3701 $used = false;
3702 if ($this->isSurveyCodeUsed($row["survey_key"])) {
3703 $used = true;
3704 } else {
3705 $params = array("accesscode" => $row["survey_key"]);
3706 if ($lang) {
3707 $params["lang"] = $lang;
3708 }
3709 $href = ilLink::_getLink($this->getRefId(), "svy", $params);
3710 }
3711
3712
3713 $item = array(
3714 'id' => $row["anonymous_id"],
3715 'code' => $row["survey_key"],
3716 'date' => $row["tstamp"],
3717 'used' => $used,
3718 'sent' => $row['sent'],
3719 'href' => $href,
3720 'email' => '',
3721 'last_name' => '',
3722 'first_name' => ''
3723 );
3724
3725 if ($row["externaldata"]) {
3726 $ext = unserialize($row["externaldata"], ['allowed_classes' => false]);
3727 $item['email'] = $ext['email'] ?? "";
3728 $item['last_name'] = $ext['lastname'] ?? "";
3729 $item['first_name'] = $ext['firstname'] ?? "";
3730 }
3731
3732 $codes[] = $item;
3733 }
3734 }
3735 return $codes;
3736 }
3737
3741 public function isSurveyCodeUsed(
3742 string $code
3743 ): bool {
3744 $ilDB = $this->db;
3745 $result = $ilDB->queryF(
3746 "SELECT finished_id FROM svy_finished WHERE survey_fi = %s AND anonymous_id = %s",
3747 array('integer','text'),
3748 array($this->getSurveyId(), $code)
3749 );
3750 return $result->numRows() > 0;
3751 }
3752
3756 public function isSurveyCodeUnique(
3757 string $code
3758 ): bool {
3759 $ilDB = $this->db;
3760 $result = $ilDB->queryF(
3761 "SELECT anonymous_id FROM svy_anonymous WHERE survey_fi = %s AND survey_key = %s",
3762 array('integer','text'),
3763 array($this->getSurveyId(), $code)
3764 );
3765 return !(($result->numRows() > 0));
3766 }
3767
3771 public function importSurveyCode(
3772 string $a_anonymize_key,
3773 int $a_created,
3774 array $a_data
3775 ): void {
3776 $ilDB = $this->db;
3777
3778 $next_id = $ilDB->nextId('svy_anonymous');
3779 $ilDB->manipulateF(
3780 "INSERT INTO svy_anonymous (anonymous_id, survey_key, survey_fi, externaldata, tstamp) " .
3781 "VALUES (%s, %s, %s, %s, %s)",
3782 array('integer','text','integer','text','integer'),
3783 array($next_id, $a_anonymize_key, $this->getSurveyId(), serialize($a_data), $a_created)
3784 );
3785 }
3786
3790 public function sendCodes(
3791 int $not_sent,
3792 string $subject,
3793 string $message,
3794 string $lang
3795 ): void {
3796 /*
3797 * 0 = all
3798 * 1 = not sent
3799 * 2 = finished
3800 * 3 = not finished
3801 */
3802 $check_finished = ($not_sent > 1);
3803
3804
3805 $mail = new ilMail(ANONYMOUS_USER_ID);
3806 $recipients = $this->getExternalCodeRecipients($check_finished);
3807 foreach ($recipients as $data) {
3808 if ($data['email'] && $data['code']) {
3809 $do_send = false;
3810 switch ($not_sent) {
3811 case 1:
3812 $do_send = !(bool) $data['sent'];
3813 break;
3814
3815 case 2:
3816 $do_send = $data['finished'];
3817 break;
3818
3819 case 3:
3820 $do_send = !$data['finished'];
3821 break;
3822
3823 default:
3824 $do_send = true;
3825 break;
3826 }
3827 if ($do_send) {
3828 // build text
3829 $messagetext = $message;
3831 $this->getRefId(),
3832 "svy",
3833 array(
3834 "accesscode" => $data["code"],
3835 "lang" => $lang
3836 )
3837 );
3838 $messagetext = str_replace('[url]', $url, $messagetext);
3839 foreach ($data as $key => $value) {
3840 $messagetext = str_replace('[' . $key . ']', $value, $messagetext);
3841 }
3842
3843 // send mail
3844 $mail->enqueue(
3845 $data['email'], // to
3846 "", // cc
3847 "", // bcc
3848 $subject, // subject
3849 $messagetext, // message
3850 array() // attachments
3851 );
3852 }
3853 }
3854 }
3855
3856 $ilDB = $this->db;
3857 $ilDB->manipulateF(
3858 "UPDATE svy_anonymous SET sent = %s WHERE survey_fi = %s AND externaldata IS NOT NULL",
3859 array('integer','integer'),
3860 array(1, $this->getSurveyId())
3861 );
3862 }
3863
3868 bool $a_check_finished = false
3869 ): array {
3870 $ilDB = $this->db;
3871 $result = $ilDB->queryF(
3872 "SELECT survey_key code, externaldata, sent FROM svy_anonymous WHERE survey_fi = %s",
3873 array('integer'),
3874 array($this->getSurveyId())
3875 );
3876 $res = array();
3877 while ($row = $ilDB->fetchAssoc($result)) {
3878 if (!$row['externaldata']) {
3879 continue;
3880 }
3881
3882 $externaldata = unserialize($row['externaldata'], ['allowed_classes' => false]);
3883 if (!$externaldata['email']) {
3884 continue;
3885 }
3886
3887 $externaldata['code'] = $row['code'];
3888 $externaldata['sent'] = $row['sent'];
3889
3890 if ($a_check_finished) {
3891 #23294
3892 //$externaldata['finished'] = $this->isSurveyCodeUsed($row['code']);
3893 $externaldata['finished'] = $this->isSurveyFinishedByCode($row['code']);
3894 }
3895
3896 $res[] = $externaldata;
3897 }
3898 return $res;
3899 }
3900
3907 public function isSurveyFinishedByCode(string $a_code): bool
3908 {
3909 $result = $this->db->queryF(
3910 "SELECT state FROM svy_finished WHERE survey_fi = %s AND anonymous_id = %s",
3911 array('integer','text'),
3912 array($this->getSurveyId(), $a_code)
3913 );
3914
3915 $row = $this->db->fetchAssoc($result);
3916
3917 return $row['state'] ?? false;
3918 }
3919
3923 public function deleteSurveyCode(
3924 string $survey_code
3925 ): void {
3926 $ilDB = $this->db;
3927
3928 if ($survey_code !== '') {
3929 $affectedRows = $ilDB->manipulateF(
3930 "DELETE FROM svy_anonymous WHERE survey_fi = %s AND survey_key = %s",
3931 array('integer', 'text'),
3932 array($this->getSurveyId(), $survey_code)
3933 );
3934 }
3935 }
3936
3943 public function getUserAccessCode(int $user_id): string
3944 {
3945 $ilDB = $this->db;
3946 $access_code = "";
3947 $result = $ilDB->queryF(
3948 "SELECT survey_key FROM svy_anonymous WHERE survey_fi = %s AND user_key = %s",
3949 array('integer','text'),
3950 array($this->getSurveyId(), md5($user_id))
3951 );
3952 if ($result->numRows()) {
3953 $row = $ilDB->fetchAssoc($result);
3954 $access_code = $row["survey_key"];
3955 }
3956 return $access_code;
3957 }
3958
3965 public function saveUserAccessCode(
3966 int $user_id,
3967 string $access_code
3968 ): void {
3969 $ilDB = $this->db;
3970
3971 // not really sure what to do about ANONYMOUS_USER_ID
3972
3973 $next_id = $ilDB->nextId('svy_anonymous');
3974 $affectedRows = $ilDB->manipulateF(
3975 "INSERT INTO svy_anonymous (anonymous_id, survey_key, survey_fi, user_key, tstamp) " .
3976 "VALUES (%s, %s, %s, %s, %s)",
3977 array('integer','text', 'integer', 'text', 'integer'),
3978 array($next_id, $access_code, $this->getSurveyId(), md5($user_id), time())
3979 );
3980 }
3981
3982
3986 public function getLastAccess(
3987 int $finished_id
3988 ): ?int {
3989 $ilDB = $this->db;
3990
3991 $result = $ilDB->queryF(
3992 "SELECT tstamp FROM svy_answer WHERE active_fi = %s ORDER BY tstamp DESC",
3993 array('integer'),
3994 array($finished_id)
3995 );
3996 if ($result->numRows()) {
3997 $row = $ilDB->fetchAssoc($result);
3998 return (int) $row["tstamp"];
3999 } else {
4000 $result = $ilDB->queryF(
4001 "SELECT tstamp FROM svy_finished WHERE finished_id = %s",
4002 array('integer'),
4003 array($finished_id)
4004 );
4005 if ($result->numRows()) {
4006 $row = $ilDB->fetchAssoc($result);
4007 return (int) $row["tstamp"];
4008 }
4009 }
4010 return null;
4011 }
4012
4016 public function prepareTextareaOutput(
4017 string $txt_output
4018 ): string {
4019 return ilLegacyFormElementsUtil::prepareTextareaOutput($txt_output);
4020 }
4021
4026 public function isHTML(
4027 string $a_text
4028 ): bool {
4029 if (preg_match("/<[^>]*?>/", $a_text)) {
4030 return true;
4031 } else {
4032 return false;
4033 }
4034 }
4035
4040 public function addMaterialTag(
4041 ilXmlWriter $a_xml_writer,
4042 string $a_material,
4043 bool $close_material_tag = true,
4044 bool $add_mobs = true,
4045 array $attribs = null
4046 ): void {
4047 $a_xml_writer->xmlStartTag("material", $attribs);
4048 $attrs = array(
4049 "type" => "text/plain"
4050 );
4051 if ($this->isHTML($a_material)) {
4052 $attrs["type"] = "text/xhtml";
4053 }
4054 $mattext = ilRTE::_replaceMediaObjectImageSrc($a_material, 0);
4055 $a_xml_writer->xmlElement("mattext", $attrs, $mattext);
4056
4057 if ($add_mobs) {
4058 $mobs = ilObjMediaObject::_getMobsOfObject("svy:html", $this->getId());
4059 foreach ($mobs as $mob) {
4060 $mob_id = "il_" . IL_INST_ID . "_mob_" . $mob;
4061 if (strpos($mattext, $mob_id) !== false) {
4062 $mob_obj = new ilObjMediaObject($mob);
4063 $imgattrs = array(
4064 "label" => $mob_id,
4065 "uri" => "objects/" . "il_" . IL_INST_ID . "_mob_" . $mob . "/" . $mob_obj->getTitle(),
4066 "type" => "svy:html",
4067 "id" => $this->getId()
4068 );
4069 $a_xml_writer->xmlElement("matimage", $imgattrs, null);
4070 }
4071 }
4072 }
4073 if ($close_material_tag) {
4074 $a_xml_writer->xmlEndTag("material");
4075 }
4076 }
4077
4085 public function canExportSurveyCode(): bool
4086 {
4087 if ($this->getAnonymize() !== self::ANONYMIZE_OFF) {
4088 if ($this->surveyCodeSecurity === false) {
4089 return true;
4090 }
4091 }
4092 return false;
4093 }
4094
4098 public function isPluginActive(string $a_pname): bool
4099 {
4100 $ilPluginAdmin = $this->plugin_admin;
4101 if ($ilPluginAdmin->isActive(ilComponentInfo::TYPE_MODULES, "SurveyQuestionPool", "svyq", $a_pname)) {
4102 return true;
4103 } else {
4104 return false;
4105 }
4106 }
4107
4108 public function setSurveyId(int $survey_id): void
4109 {
4110 $this->survey_id = $survey_id;
4111 }
4112
4120 public function getUserData(
4121 array $ids
4122 ): array {
4123 $ilDB = $this->db;
4124
4125 if (count($ids) === 0) {
4126 return array();
4127 }
4128
4129 $result = $ilDB->query("SELECT usr_id, login, lastname, firstname FROM usr_data WHERE " . $ilDB->in('usr_id', $ids, false, 'integer') . " ORDER BY login");
4130 $result_array = array();
4131 while ($row = $ilDB->fetchAssoc($result)) {
4132 $result_array[$row["usr_id"]] = $row;
4133 }
4134 return $result_array;
4135 }
4136
4140 public function getMailNotification(): bool
4141 {
4142 return $this->mailnotification;
4143 }
4144
4148 public function setMailNotification(bool $a_notification): void
4149 {
4150 $this->mailnotification = $a_notification;
4151 }
4152
4156 public function getMailAddresses(): string
4157 {
4158 return $this->mailaddresses;
4159 }
4160
4164 public function setMailAddresses(string $a_addresses): void
4165 {
4166 $this->mailaddresses = $a_addresses;
4167 }
4168
4172 public function getMailParticipantData(): string
4173 {
4174 return $this->mailparticipantdata;
4175 }
4176
4180 public function setMailParticipantData(string $a_data): void
4181 {
4182 $this->mailparticipantdata = $a_data;
4183 }
4184
4189 int $finished_id
4190 ): int {
4191 $ilDB = $this->db;
4192
4193 $result = $ilDB->queryF(
4194 "SELECT * FROM svy_times WHERE finished_fi = %s",
4195 array('integer'),
4196 array($finished_id)
4197 );
4198 $total = 0;
4199 while ($row = $ilDB->fetchAssoc($result)) {
4200 if ($row['left_page'] > 0 && $row['entered_page'] > 0) {
4201 $total += $row['left_page'] - $row['entered_page'];
4202 }
4203 }
4204 return $total;
4205 }
4206
4207 public function updateOrder(
4208 array $a_order
4209 ): void {
4210 if (count($this->questions) === count($a_order)) {
4211 $this->questions = array_flip($a_order);
4212 $this->saveQuestionsToDb();
4213 }
4214 }
4215
4216 public function getPoolUsage(): bool
4217 {
4218 return $this->pool_usage;
4219 }
4220
4221 public function setPoolUsage(bool $a_value): void
4222 {
4223 $this->pool_usage = $a_value;
4224 }
4225
4229 public function updateCode(
4230 int $a_id,
4231 string $a_email,
4232 string $a_last_name,
4233 string $a_first_name,
4234 int $a_sent
4235 ): bool {
4236 $ilDB = $this->db;
4237
4238 $a_email = trim($a_email);
4239
4240 // :TODO:
4241 if (($a_email && !ilUtil::is_email($a_email)) || $a_email === "") {
4242 return false;
4243 }
4244
4245 $data = array("email" => $a_email,
4246 "lastname" => trim($a_last_name),
4247 "firstname" => trim($a_first_name));
4248
4249 $fields = array(
4250 "externaldata" => array("text", serialize($data)),
4251 "sent" => array("integer", $a_sent)
4252 );
4253
4254 $ilDB->update(
4255 "svy_anonymous",
4256 $fields,
4257 array("anonymous_id" => array("integer", $a_id))
4258 );
4259
4260 return true;
4261 }
4262
4263
4264 //
4265 // 360°
4266 //
4267
4268 public function get360Mode(): bool
4269 {
4270 if ($this->getMode() === self::MODE_360) {
4271 return true;
4272 }
4273 return false;
4274 }
4275
4276 public function set360SelfEvaluation(bool $a_value): void
4277 {
4278 $this->mode_360_self_eval = $a_value;
4279 }
4280
4281 public function get360SelfEvaluation(): bool
4282 {
4283 return $this->mode_360_self_eval;
4284 }
4285
4286 public function set360SelfAppraisee(bool $a_value): void
4287 {
4288 $this->mode_360_self_appr = $a_value;
4289 }
4290
4291 public function get360SelfAppraisee(): bool
4292 {
4293 return $this->mode_360_self_appr;
4294 }
4295
4296 public function set360SelfRaters(bool $a_value): void
4297 {
4298 $this->mode_360_self_rate = $a_value;
4299 }
4300
4301 public function get360SelfRaters(): bool
4302 {
4303 return $this->mode_360_self_rate;
4304 }
4305
4306 public function set360Results(int $a_value): void
4307 {
4308 $this->mode_360_results = $a_value;
4309 }
4310
4311 public function get360Results(): int
4312 {
4313 return $this->mode_360_results;
4314 }
4315
4319 public function addAppraisee(
4320 int $a_user_id
4321 ): void {
4322 global $DIC;
4323
4324 $ilDB = $DIC->database();
4325 $access = $DIC->access();
4326
4327 if (!$this->isAppraisee($a_user_id) &&
4328 $a_user_id !== ANONYMOUS_USER_ID) {
4329 $fields = array(
4330 "obj_id" => array("integer", $this->getSurveyId()),
4331 "user_id" => array("integer", $a_user_id)
4332 );
4333 $ilDB->insert("svy_360_appr", $fields);
4334
4335 // send notification and add to desktop
4336 if ($access->checkAccessOfUser($a_user_id, "read", "", $this->getRefId())) {
4337 $this->sendAppraiseeNotification($a_user_id);
4338 }
4339 }
4340 }
4341
4346 int $a_user_id
4347 ): void {
4348 $ntf = new ilSystemNotification();
4349 $ntf->setLangModules(array("svy", "survey"));
4350 $ntf->setRefId($this->getRefId());
4351 $ntf->setGotoLangId('url');
4352
4353 // user specific language
4354 $lng = $ntf->getUserLanguage($a_user_id);
4355
4356 $ntf->setIntroductionLangId("svy_user_added_appraisee_mail");
4357 $subject = str_replace("%1", $this->getTitle(), $lng->txt("svy_user_added_appraisee"));
4358
4359 // #10044
4360 $mail = new ilMail(ANONYMOUS_USER_ID);
4361 $mail->enqueue(
4362 ilObjUser::_lookupLogin($a_user_id),
4363 "",
4364 "",
4365 $subject,
4366 $ntf->composeAndGetMessage($a_user_id, null, "read", true),
4367 []
4368 );
4369 }
4370
4375 int $a_user_id
4376 ): void {
4377 $ntf = new ilSystemNotification();
4378 $ntf->setLangModules(array("svy", "survey"));
4379 $ntf->setRefId($this->getRefId());
4380 $ntf->setGotoLangId('url');
4381
4382 // user specific language
4383 $lng = $ntf->getUserLanguage($a_user_id);
4384
4385 $ntf->setIntroductionLangId("svy_user_added_appraisee_close_mail");
4386 $subject = str_replace("%1", $this->getTitle(), $lng->txt("svy_user_added_appraisee"));
4387
4388 // #10044
4389 $mail = new ilMail(ANONYMOUS_USER_ID);
4390 $mail->enqueue(
4391 ilObjUser::_lookupLogin($a_user_id),
4392 "",
4393 "",
4394 $subject,
4395 $ntf->composeAndGetMessage($a_user_id, null, "read", true),
4396 []
4397 );
4398 }
4399
4403 public function sendRaterNotification(
4404 int $a_user_id,
4405 int $a_appraisee_id
4406 ): void {
4407 $ntf = new ilSystemNotification();
4408 $ntf->setLangModules(array("svy", "survey"));
4409 $ntf->setRefId($this->getRefId());
4410 $ntf->setGotoLangId('url');
4411
4412 // user specific language
4413 $lng = $ntf->getUserLanguage($a_user_id);
4414
4415 $ntf->setIntroductionLangId("svy_user_added_rater_mail");
4416 $subject = str_replace("%1", $this->getTitle(), $lng->txt("svy_user_added_rater"));
4417 $ntf->addAdditionalInfo("survey_360_appraisee", ilUserUtil::getNamePresentation($a_appraisee_id, false, false, "", true));
4418
4419 // #10044
4420 $mail = new ilMail(ANONYMOUS_USER_ID);
4421 $mail->enqueue(
4422 ilObjUser::_lookupLogin($a_user_id),
4423 "",
4424 "",
4425 $subject,
4426 $ntf->composeAndGetMessage($a_user_id, null, "read", true),
4427 []
4428 );
4429 }
4430
4434 public function isAppraisee(
4435 int $a_user_id
4436 ): bool {
4437 $ilDB = $this->db;
4438 $set = $ilDB->query("SELECT user_id" .
4439 " FROM svy_360_appr" .
4440 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer") .
4441 " AND user_id = " . $ilDB->quote($a_user_id, "integer"));
4442 return (bool) $ilDB->numRows($set);
4443 }
4444
4448 public function isAppraiseeClosed(
4449 int $a_user_id
4450 ): bool {
4451 $ilDB = $this->db;
4452
4453 $set = $ilDB->query("SELECT has_closed" .
4454 " FROM svy_360_appr" .
4455 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer") .
4456 " AND user_id = " . $ilDB->quote($a_user_id, "integer"));
4457 $row = $ilDB->fetchAssoc($set);
4458 return (bool) ($row["has_closed"] ?? false);
4459 }
4460
4464 public function deleteAppraisee(
4465 int $a_user_id
4466 ): void {
4467 $ilDB = $this->db;
4468
4469 $ilDB->manipulate("DELETE FROM svy_360_appr" .
4470 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer") .
4471 " AND user_id = " . $ilDB->quote($a_user_id, "integer"));
4472
4473 $set = $ilDB->query("SELECT user_id" .
4474 " FROM svy_360_rater" .
4475 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer") .
4476 " AND appr_id = " . $ilDB->quote($a_user_id, "integer"));
4477 while ($row = $ilDB->fetchAssoc($set)) {
4478 $this->deleteRater($a_user_id, $row["user_id"]);
4479 }
4480 // appraisee will not be part of raters table
4481 if ($this->get360SelfEvaluation()) {
4482 $this->deleteRater($a_user_id, $a_user_id);
4483 }
4484 }
4485
4489 public function getAppraiseesData(): array
4490 {
4491 $ilDB = $this->db;
4492
4493 $res = array();
4494
4495 $set = $ilDB->query("SELECT * FROM svy_360_appr" .
4496 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer"));
4497 while ($row = $ilDB->fetchAssoc($set)) {
4498 $name = ilObjUser::_lookupName($row["user_id"]);
4499 $name["email"] = ilObjUser::_lookupEmail($row["user_id"]);
4500 $name["name"] = $name["lastname"] . ", " . $name["firstname"];
4501 $res[$row["user_id"]] = $name;
4502
4503 $finished = 0;
4504 $raters = $this->getRatersData($row["user_id"]);
4505 foreach ($raters as $rater) {
4506 if ($rater["finished"]) {
4507 $finished++;
4508 }
4509 }
4510 $res[$row["user_id"]]["finished"] = $finished . "/" . count($raters);
4511 $res[$row["user_id"]]["closed"] = $row["has_closed"];
4512 }
4513
4514 return $res;
4515 }
4516
4520 public function addRater(
4521 int $a_appraisee_id,
4522 int $a_user_id,
4523 int $a_anonymous_id = 0
4524 ): void {
4525 global $DIC;
4526
4527 $ilDB = $DIC->database();
4528 $access = $DIC->access();
4529
4530 if ($this->isAppraisee($a_appraisee_id) &&
4531 !$this->isRater($a_appraisee_id, $a_user_id, $a_anonymous_id)) {
4532 $fields = array(
4533 "obj_id" => array("integer", $this->getSurveyId()),
4534 "appr_id" => array("integer", $a_appraisee_id),
4535 "user_id" => array("integer", $a_user_id),
4536 "anonymous_id" => array("integer", $a_anonymous_id)
4537 );
4538 $ilDB->insert("svy_360_rater", $fields);
4539
4540 // send notification and add to desktop
4541 if ($access->checkAccessOfUser($a_user_id, "read", "", $this->getRefId())) {
4542 $this->sendRaterNotification($a_user_id, $a_appraisee_id);
4543 }
4544 }
4545 }
4546
4550 public function isRater(
4551 int $a_appraisee_id,
4552 int $a_user_id,
4553 int $a_anonymous_id = 0
4554 ): bool {
4555 $ilDB = $this->db;
4556
4557 // user is rater if already appraisee and active self-evaluation
4558 if ($this->isAppraisee($a_user_id) &&
4559 $this->get360SelfEvaluation() &&
4560 (!$a_appraisee_id || $a_appraisee_id === $a_user_id)) {
4561 return true;
4562 }
4563
4564 // :TODO: should we get rid of code as well?
4565
4566 $sql = "SELECT user_id" .
4567 " FROM svy_360_rater" .
4568 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer") .
4569 " AND user_id = " . $ilDB->quote($a_user_id, "integer") .
4570 " AND anonymous_id = " . $ilDB->quote($a_anonymous_id, "integer");
4571 if ($a_appraisee_id) {
4572 $sql .= " AND appr_id = " . $ilDB->quote($a_appraisee_id, "integer");
4573 }
4574 $set = $ilDB->query($sql);
4575 return (bool) $ilDB->numRows($set);
4576 }
4577
4581 public function deleteRater(
4582 int $a_appraisee_id,
4583 int $a_user_id,
4584 int $a_anonymous_id = 0
4585 ): void {
4586 $ilDB = $this->db;
4587
4588 $finished_id = $this->getFinishedIdForAppraiseeIdAndRaterId($a_appraisee_id, $a_user_id);
4589 if ($finished_id) {
4590 $this->removeSelectedSurveyResults(array($finished_id));
4591 }
4592
4593 $ilDB->manipulate("DELETE FROM svy_360_rater" .
4594 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer") .
4595 " AND appr_id = " . $ilDB->quote($a_appraisee_id, "integer") .
4596 " AND user_id = " . $ilDB->quote($a_user_id, "integer") .
4597 " AND anonymous_id = " . $ilDB->quote($a_anonymous_id, "integer"));
4598 }
4599
4603 public function getRatersData(
4604 int $a_appraisee_id
4605 ): array {
4606 $ilDB = $this->db;
4607
4608 $res = $anonymous_ids = array();
4609
4610 $set = $ilDB->query("SELECT * FROM svy_360_rater" .
4611 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer") .
4612 " AND appr_id = " . $ilDB->quote($a_appraisee_id, "integer"));
4613 while ($row = $ilDB->fetchAssoc($set)) {
4614 if ($row["anonymous_id"]) {
4615 $res["a" . $row["anonymous_id"]] = array(
4616 "lastname" => "unknown code " . $row["anonymous_id"],
4617 "sent" => $row["mail_sent"],
4618 "finished" => null
4619 );
4620 $anonymous_ids[] = $row["anonymous_id"];
4621 } else {
4622 $name = ilObjUser::_lookupName($row["user_id"]);
4623 $name["name"] = $name["lastname"] . ", " . $name["firstname"];
4624 $name["user_id"] = "u" . $name["user_id"];
4625 $name["email"] = ilObjUser::_lookupEmail($row["user_id"]);
4626 $name["sent"] = $row["mail_sent"];
4627 $name["finished"] = (bool) $this->is360SurveyStarted($a_appraisee_id, (int) $row["user_id"]);
4628 $res["u" . $row["user_id"]] = $name;
4629 }
4630 }
4631
4632 if (count($anonymous_ids)) {
4633 $data = $this->getSurveyCodesTableData($anonymous_ids);
4634 foreach ($data as $item) {
4635 if (isset($res["a" . $item["id"]])) {
4636 $res["a" . $item["id"]] = array(
4637 "user_id" => "a" . $item["id"],
4638 "lastname" => $item["last_name"],
4639 "firstname" => $item["first_name"],
4640 "name" => $item["last_name"] . ", " . $item["first_name"],
4641 "login" => "",
4642 "email" => $item["email"],
4643 "code" => $item["code"],
4644 "href" => $item["href"],
4645 "sent" => $res["a" . $item["id"]]["sent"],
4646 "finished" => (bool) $this->is360SurveyStarted($a_appraisee_id, 0, $item["code"])
4647 );
4648 }
4649 }
4650 }
4651
4652 return $res;
4653 }
4654
4659 public function getAppraiseesToRate(
4660 ?int $a_user_id,
4661 int $a_anonymous_id = null
4662 ): array {
4663 $ilDB = $this->db;
4664
4665 $res = array();
4666
4667 $sql = "SELECT appr_id FROM svy_360_rater" .
4668 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer");
4669
4670 if ($a_user_id) {
4671 $sql .= " AND user_id = " . $ilDB->quote($a_user_id, "integer");
4672 } else {
4673 $sql .= " AND anonymous_id = " . $ilDB->quote($a_anonymous_id, "integer");
4674 }
4675
4676 $set = $ilDB->query($sql);
4677 while ($row = $ilDB->fetchAssoc($set)) {
4678 $res[] = (int) $row["appr_id"];
4679 }
4680
4681 // user may evaluate himself if already appraisee
4682 if ($this->get360SelfEvaluation() &&
4683 $this->isAppraisee((int) $a_user_id) &&
4684 !in_array($a_user_id, $res)) {
4685 $res[] = $a_user_id;
4686 }
4687
4688 return $res;
4689 }
4690
4694 public function getAnonymousIdByCode(
4695 string $a_code
4696 ): ?int {
4697 $ilDB = $this->db;
4698 $set = $ilDB->query("SELECT anonymous_id FROM svy_anonymous" .
4699 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") .
4700 " AND survey_key = " . $ilDB->quote($a_code, "text"));
4701 $res = $ilDB->fetchAssoc($set);
4702 return $res["anonymous_id"] ?? null;
4703 }
4704
4708 public function is360SurveyStarted(
4709 int $appr_id,
4710 int $user_id,
4711 string $anonymous_code = null
4712 ): ?int {
4713 $ilDB = $this->db;
4714
4715 $sql = "SELECT * FROM svy_finished" .
4716 " WHERE survey_fi =" . $ilDB->quote($this->getSurveyId(), "integer") .
4717 " AND appr_id = " . $ilDB->quote($appr_id, "integer");
4718 if ($user_id) {
4719 $sql .= " AND user_fi = " . $ilDB->quote($user_id, "integer");
4720 } else {
4721 $sql .= " AND anonymous_id = " . $ilDB->quote($anonymous_code, "text");
4722 }
4723 $result = $ilDB->query($sql);
4724 if ($result->numRows() === 0) {
4725 return null;
4726 } else {
4727 $row = $ilDB->fetchAssoc($result);
4728 return (int) $row["state"];
4729 }
4730 }
4731
4741 string $a_code = null
4742 ): ?array {
4743 $ilUser = $this->user;
4744 $ilDB = $this->db;
4745 $user_id = $ilUser->getId();
4746 // code is obligatory?
4747 if (!$this->isAccessibleWithoutCode()) {
4748 if (!$a_code) {
4749 // registered raters do not need code
4750 if ($this->feature_config->usesAppraisees() &&
4751 $user_id !== ANONYMOUS_USER_ID &&
4752 $this->isRater(0, $user_id)) {
4753 // auto-generate code
4754 $code = $this->data_manager->code("")
4755 ->withUserId($user_id);
4756 $this->code_manager->add($code);
4757 $a_code = $this->code_manager->getByUserId($user_id);
4758 } else {
4759 return null;
4760 }
4761 }
4762 } elseif ($user_id === ANONYMOUS_USER_ID ||
4763 $this->getAnonymize() === self::ANONYMIZE_FREEACCESS) {
4764 // self::ANONYMIZE_FREEACCESS: anonymized, no codes
4765 // or anonymous user when no codes are used
4766 if (!$a_code) {
4767 // auto-generate code
4768 $code = $this->data_manager->code("")
4769 ->withUserId($user_id);
4770 $code_id = $this->code_manager->add($code);
4771 $a_code = $this->code_manager->getByCodeId($code_id);
4772 }
4773 } else {
4774 $a_code = null;
4775 }
4776
4777 $res = array();
4778
4779 // get runs for user id / code
4780 $sql = "SELECT * FROM svy_finished" .
4781 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer");
4782 // if proper user id is given, use it or current code
4783 if ($user_id !== ANONYMOUS_USER_ID) {
4784 $sql .= " AND (user_fi = " . $ilDB->quote($user_id, "integer") .
4785 " OR anonymous_id = " . $ilDB->quote($a_code, "text") . ")";
4786 }
4787 // use anonymous code to find finished id(s)
4788 else {
4789 $sql .= " AND anonymous_id = " . $ilDB->quote($a_code, "text");
4790 }
4791 $set = $ilDB->query($sql);
4792 while ($row = $ilDB->fetchAssoc($set)) {
4793 $res[$row["finished_id"]] = array("appr_id" => $row["appr_id"],
4794 "user_id" => $row["user_fi"],
4795 "code" => $row["anonymous_id"],
4796 "finished" => (bool) $row["state"]);
4797 }
4798 return array("code" => $a_code, "runs" => $res);
4799 }
4800
4804 public function findCodeForUser(
4805 int $a_user_id
4806 ): string {
4807 $ilDB = $this->db;
4808
4809 if ($a_user_id !== ANONYMOUS_USER_ID) {
4810 $set = $ilDB->query("SELECT sf.anonymous_id FROM svy_finished sf" .
4811 " WHERE sf.survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") .
4812 " AND sf.user_fi = " . $ilDB->quote($a_user_id, "integer"));
4813 $a_code = $ilDB->fetchAssoc($set);
4814 return (string) ($a_code["anonymous_id"] ?? "");
4815 }
4816 return "";
4817 }
4818
4822 public function isUnusedCode(
4823 string $a_code,
4824 int $a_user_id
4825 ): bool {
4826 $ilDB = $this->db;
4827
4828 $set = $ilDB->query("SELECT user_fi FROM svy_finished" .
4829 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") .
4830 " AND anonymous_id = " . $ilDB->quote($a_code, "text"));
4831 $user_id = $ilDB->fetchAssoc($set);
4832 $user_id = (int) $user_id["user_fi"];
4833
4834 if ($user_id && ($user_id !== $a_user_id || $user_id === ANONYMOUS_USER_ID)) {
4835 return false;
4836 }
4837 return true;
4838 }
4839
4845 int $a_appr_id,
4846 bool $a_exclude_appraisee = false
4847 ): array {
4848 $ilDB = $this->db;
4849
4850 $res = array();
4851
4852 $set = $ilDB->query("SELECT finished_id, user_fi FROM svy_finished" .
4853 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") .
4854 " AND appr_id = " . $ilDB->quote($a_appr_id, "integer"));
4855 while ($row = $ilDB->fetchAssoc($set)) {
4856 if ($a_exclude_appraisee && $row["user_fi"] == $a_appr_id) {
4857 continue;
4858 }
4859 $res[] = (int) $row["finished_id"];
4860 }
4861
4862 return $res;
4863 }
4864
4870 int $a_appr_id,
4871 int $a_rat_id
4872 ): ?int {
4873 $ilDB = $this->db;
4874
4875 $set = $ilDB->query("SELECT finished_id, user_fi FROM svy_finished" .
4876 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") .
4877 " AND appr_id = " . $ilDB->quote($a_appr_id, "integer") .
4878 " AND user_fi = " . $ilDB->quote($a_rat_id, "integer"));
4879 if ($row = $ilDB->fetchAssoc($set)) {
4880 return (int) $row["finished_id"];
4881 }
4882 return null;
4883 }
4884
4885
4886 // 360° using competence/skill service
4887
4888 public function setSkillService(bool $a_val): void
4889 {
4890 $this->mode_skill_service = $a_val;
4891 }
4892
4893 public function getSkillService(): bool
4894 {
4895 return $this->mode_skill_service;
4896 }
4897
4901 public function set360RaterSent(
4902 int $a_appraisee_id,
4903 int $a_user_id,
4904 int $a_anonymous_id,
4905 int $a_tstamp = null
4906 ): void {
4907 $ilDB = $this->db;
4908
4909 if (!$a_tstamp) {
4910 $a_tstamp = time();
4911 }
4912
4913 $ilDB->manipulate("UPDATE svy_360_rater" .
4914 " SET mail_sent = " . $ilDB->quote($a_tstamp, "integer") .
4915 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer") .
4916 " AND appr_id = " . $ilDB->quote($a_appraisee_id, "integer") .
4917 " AND user_id = " . $ilDB->quote($a_user_id, "integer") .
4918 " AND anonymous_id = " . $ilDB->quote($a_anonymous_id, "integer"));
4919 }
4920
4924 public function closeAppraisee(
4925 int $a_user_id
4926 ): void {
4927 global $DIC;
4928
4929 $ilDB = $DIC->database();
4930 $user = $DIC->user();
4931
4932 // close the appraisee
4933 $ilDB->manipulate("UPDATE svy_360_appr" .
4934 " SET has_closed = " . $ilDB->quote(time(), "integer") .
4935 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer") .
4936 " AND user_id = " . $ilDB->quote($a_user_id, "integer"));
4937
4938 // write competences
4939 $skmg_set = new ilSkillManagementSettings();
4940 if ($this->getSkillService() && $skmg_set->isActivated()) {
4941 $sskill = new ilSurveySkill($this);
4942 $sskill->writeAndAddAppraiseeSkills($a_user_id);
4943 }
4944
4945 // send notification
4946 if ($user->getId() !== $a_user_id) {
4947 $this->sendAppraiseeCloseNotification($a_user_id);
4948 }
4949 }
4950
4954 public function openAllAppraisees(): void
4955 {
4956 $ilDB = $this->db;
4957
4958 $ilDB->manipulate("UPDATE svy_360_appr" .
4959 " SET has_closed = " . $ilDB->quote(null, "integer") .
4960 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer"));
4961 }
4962
4966 public static function validateExternalRaterCode(
4967 int $a_ref_id,
4968 string $a_code
4969 ): bool {
4971 global $DIC;
4972
4973 $anonym_repo = $DIC->survey()
4974 ->internal()
4975 ->repo()
4976 ->execution()
4977 ->runSession();
4978
4979 if (!$anonym_repo->isExternalRaterValidated($a_ref_id)) {
4980 $svy = new self($a_ref_id);
4981 $svy->loadFromDb();
4982
4983 $domain_service = $DIC->survey()->internal()->domain();
4984 $code_manager = $domain_service->code($svy, 0);
4985 $feature_config = $domain_service->modeFeatureConfig($svy->getMode());
4986 $access_manager = $domain_service->access($a_ref_id, 0);
4987
4988 if ($access_manager->canStartSurvey() &&
4990 $code_manager->exists($a_code)) {
4991 $anonymous_id = $svy->getAnonymousIdByCode($a_code);
4992 if ($anonymous_id) {
4993 if (count($svy->getAppraiseesToRate(null, $anonymous_id))) {
4994 $anonym_repo->setExternalRaterValidation($a_ref_id, true);
4995 return true;
4996 }
4997 }
4998 }
4999 $anonym_repo->setExternalRaterValidation($a_ref_id, false);
5000 return false;
5001 }
5002
5003 return $anonym_repo->isExternalRaterValidated($a_ref_id);
5004 }
5005
5006
5007 //
5008 // reminder/notification
5009 //
5010
5011 public function getReminderStatus(): bool
5012 {
5013 return $this->reminder_status;
5014 }
5015
5016 public function setReminderStatus(bool $a_value): void
5017 {
5018 $this->reminder_status = $a_value;
5019 }
5020
5021 public function getReminderStart(): ?ilDate
5022 {
5023 return $this->reminder_start;
5024 }
5025
5026 public function setReminderStart(?ilDate $a_value = null): void
5027 {
5028 $this->reminder_start = $a_value;
5029 }
5030
5031 public function getReminderEnd(): ?ilDate
5032 {
5033 return $this->reminder_end;
5034 }
5035
5036 public function setReminderEnd(?ilDate $a_value = null): void
5037 {
5038 $this->reminder_end = $a_value;
5039 }
5040
5041 public function getReminderFrequency(): int
5042 {
5043 return $this->reminder_frequency;
5044 }
5045
5046 public function setReminderFrequency(int $a_value): void
5047 {
5048 $this->reminder_frequency = $a_value;
5049 }
5050
5051 public function getReminderTarget(): int
5052 {
5053 return $this->reminder_target;
5054 }
5055
5056 public function setReminderTarget(int $a_value): void
5057 {
5058 $this->reminder_target = $a_value;
5059 }
5060
5061 public function getReminderLastSent(): ?string
5062 {
5063 return $this->reminder_last_sent;
5064 }
5065
5066 public function setReminderLastSent(?string $a_value): void
5067 {
5068 if ($a_value == "") {
5069 $a_value = null;
5070 }
5071 $this->reminder_last_sent = $a_value;
5072 }
5073
5074 public function getReminderTemplate(
5075 bool $selectDefault = false
5076 ): ?int {
5077 if ($selectDefault) {
5078 $defaultTemplateId = 0;
5079 $this->getReminderMailTemplates($defaultTemplateId);
5080
5081 if ($defaultTemplateId > 0) {
5082 return $defaultTemplateId;
5083 }
5084 }
5085
5086 return $this->reminder_tmpl;
5087 }
5088
5089 public function setReminderTemplate(?int $a_value): void
5090 {
5091 $this->reminder_tmpl = $a_value;
5092 }
5093
5094 public function getTutorNotificationStatus(): bool
5095 {
5096 return $this->tutor_ntf_status;
5097 }
5098
5102 public function setTutorNotificationStatus(bool $a_value): void
5103 {
5104 $this->tutor_ntf_status = $a_value;
5105 }
5106
5110 public function getTutorNotificationRecipients(): array
5111 {
5112 return $this->tutor_ntf_recipients;
5113 }
5114
5118 public function setTutorNotificationRecipients(array $a_value): void
5119 {
5120 $this->tutor_ntf_recipients = $a_value;
5121 }
5122
5130 {
5131 return $this->tutor_ntf_target;
5132 }
5133
5134 public function setTutorNotificationTarget(int $a_value): void
5135 {
5136 $this->tutor_ntf_target = $a_value;
5137 }
5138
5139 public function getTutorResultsStatus(): bool
5140 {
5141 return $this->tutor_res_status;
5142 }
5143
5144 public function setTutorResultsStatus(bool $a_value): void
5145 {
5146 $this->tutor_res_status = $a_value;
5147 }
5148
5149 public function getTutorResultsRecipients(): array
5150 {
5151 return $this->tutor_res_recipients;
5152 }
5153
5154 public function setTutorResultsRecipients(array $a_value): void
5155 {
5156 $this->tutor_res_recipients = $a_value;
5157 }
5158
5163 protected function checkTutorNotification(): void
5164 {
5165 $ilDB = $this->db;
5166
5167 if ($this->getTutorNotificationStatus()) {
5168
5169 // get target users, either parent course/group members or
5170 // user with the survey on the dashboard
5171 $user_ids = $this->getNotificationTargetUserIds(($this->getTutorNotificationTarget() === self::NOTIFICATION_INVITED_USERS));
5172 if ($user_ids) {
5173 $set = $ilDB->query("SELECT COUNT(*) numall FROM svy_finished" .
5174 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") .
5175 " AND state = " . $ilDB->quote(1, "integer") .
5176 " AND " . $ilDB->in("user_fi", $user_ids, "", "integer"));
5177 $row = $ilDB->fetchAssoc($set);
5178
5179 // all users finished the survey -> send notifications
5180 if ((int) $row["numall"] === count($user_ids)) {
5181 $this->sendTutorNotification();
5182 }
5183 }
5184 }
5185 }
5186
5190 public function sent360Reminders(): void
5191 {
5192 global $DIC;
5193
5194 $access = $DIC->access();
5195 // collect all open ratings
5196 $rater_ids = array();
5197 foreach ($this->getAppraiseesData() as $app) {
5198 $this->svy_log->debug("Handle appraisee " . $app['user_id']);
5199
5200 if (!$this->isAppraiseeClosed($app['user_id'])) {
5201 $this->svy_log->debug("Check self evaluation, self: " . $this->get360SelfAppraisee() . ", target: " . $this->getReminderTarget());
5202
5203 // self evaluation?
5204 if ($this->get360SelfEvaluation() &&
5205 in_array($this->getReminderTarget(), array(self::NOTIFICATION_APPRAISEES, self::NOTIFICATION_APPRAISEES_AND_RATERS))) {
5206 $this->svy_log->debug("...1");
5207 // did user already finished self evaluation?
5208 if (!$this->is360SurveyStarted((int) $app['user_id'], (int) $app['user_id'])) {
5209 $this->svy_log->debug("...2");
5210 if (!isset($rater_ids[$app['user_id']])) {
5211 $rater_ids[$app['user_id']] = array();
5212 }
5213 if (!isset($app["user_id"], $rater_ids[$app['user_id']])) {
5214 $rater_ids[$app['user_id']][] = $app["user_id"];
5215 }
5216 }
5217 }
5218
5219 $this->svy_log->debug("Check raters.");
5220
5221 // should raters be notified?
5222 if (
5223 in_array(
5224 $this->getReminderTarget(),
5225 array(self::NOTIFICATION_RATERS, self::NOTIFICATION_APPRAISEES_AND_RATERS),
5226 true
5227 )
5228 ) {
5229 foreach ($this->getRatersData($app['user_id']) as $rater) {
5230 $rater_id = 0;
5231 if ($rater["login"] !== "") {
5232 $rater_id = ilObjUser::_lookupId($rater["login"]);
5233 }
5234 if ($rater_id > 0) {
5235 // is rater not anonymous and did not rate yet?
5236 if (!($rater["anonymous_id"] ?? false) && !($rater["finished"] ?? false)) {
5237 if (!isset($rater_ids[$rater_id])) {
5238 $rater_ids[$rater_id] = array();
5239 }
5240 if (!in_array($app["user_id"], $rater_ids[$rater_id])) {
5241 $rater_ids[$rater_id][] = $app["user_id"];
5242 }
5243 }
5244 }
5245 }
5246 }
5247 }
5248 }
5249
5250 $this->svy_log->debug("Found raters:" . count($rater_ids));
5251
5252 foreach ($rater_ids as $id => $app) {
5253 if ($access->checkAccessOfUser((int) $id, "read", "", $this->getRefId())) {
5254 $this->send360ReminderToUser((int) $id, $app);
5255 }
5256 }
5257 }
5258
5259 public function send360ReminderToUser(
5260 int $a_user_id,
5261 array $a_appraisee_ids
5262 ): void {
5263 $this->svy_log->debug("Send mail to:" . $a_user_id);
5264
5265 $ntf = new ilSystemNotification();
5266 $ntf->setLangModules(array("svy", "survey"));
5267 $ntf->setRefId($this->getRefId());
5268 $ntf->setGotoLangId('url');
5269
5270 // user specific language
5271 $lng = $ntf->getUserLanguage($a_user_id);
5272
5273 $ntf->setIntroductionLangId("svy_user_added_rater_reminder_mail");
5274 $subject = str_replace("%1", $this->getTitle(), $lng->txt("svy_user_added_rater"));
5275
5276 foreach ($a_appraisee_ids as $appraisee_id) {
5277 $ntf->addAdditionalInfo("survey_360_appraisee", ilUserUtil::getNamePresentation($appraisee_id, false, false, "", true));
5278 }
5279
5280 // #10044
5281 $mail = new ilMail(ANONYMOUS_USER_ID);
5282 $mail->enqueue(
5283 ilObjUser::_lookupLogin($a_user_id),
5284 "",
5285 "",
5286 $subject,
5287 $ntf->composeAndGetMessage($a_user_id, null, "read", true),
5288 []
5289 );
5290 }
5291
5300 bool $a_use_invited
5301 ): array {
5302 $tree = $this->tree;
5303
5304 $user_ids = [];
5305 if ($a_use_invited) {
5306 $user_ids = $this->invitation_manager->getAllForSurvey($this->getSurveyId());
5307 } else {
5308 $parent_grp_ref_id = $tree->checkForParentType($this->getRefId(), "grp");
5309 if ($parent_grp_ref_id) {
5310 $part = new ilGroupParticipants(ilObject::_lookupObjId($parent_grp_ref_id));
5311 $user_ids = $part->getMembers();
5312 } else {
5313 $parent_crs_ref_id = $tree->checkForParentType($this->getRefId(), "crs");
5314 if ($parent_crs_ref_id) {
5315 $part = new ilCourseParticipants(ilObject::_lookupObjId($parent_crs_ref_id));
5316 $user_ids = $part->getMembers();
5317 }
5318 }
5319 }
5320 return $user_ids;
5321 }
5322
5326 protected function sendTutorNotification(): void
5327 {
5328 $link = ilLink::_getStaticLink($this->getRefId(), "svy");
5329
5330 // get tutors being set in the setting
5331 foreach ($this->getTutorNotificationRecipients() as $user_id) {
5332 // use language of recipient to compose message
5333 $ulng = ilLanguageFactory::_getLanguageOfUser($user_id);
5334 $ulng->loadLanguageModule('survey');
5335
5336 $subject = sprintf($ulng->txt('survey_notification_tutor_subject'), $this->getTitle());
5337 $message = sprintf($ulng->txt('survey_notification_tutor_salutation'), ilObjUser::_lookupFullname($user_id)) . "\n\n";
5338
5339 $message .= $ulng->txt('survey_notification_tutor_body') . ":\n\n";
5340 $message .= $ulng->txt('obj_svy') . ": " . $this->getTitle() . "\n";
5341 $message .= "\n" . $ulng->txt('survey_notification_tutor_link') . ": " . $link;
5342
5343 $mail_obj = new ilMail(ANONYMOUS_USER_ID);
5344 $mail_obj->appendInstallationSignature(true);
5345 $mail_obj->enqueue(
5346 ilObjUser::_lookupLogin($user_id),
5347 "",
5348 "",
5349 $subject,
5350 $message,
5351 array()
5352 );
5353 }
5354 }
5355
5356 public function checkReminder(): ?int
5357 {
5358 $ilDB = $this->db;
5359 $ilAccess = $this->access;
5360
5361 $now = time();
5362 $now_with_format = date("YmdHis", $now);
5363 $today = date("Y-m-d");
5364
5365 $this->svy_log->debug("Check status and dates.");
5366
5367 // object settings / participation period
5368 if (
5369 $this->getOfflineStatus() ||
5370 !$this->getReminderStatus() ||
5371 ($this->getStartDate() && $now_with_format < $this->getStartDate()) ||
5372 ($this->getEndDate() && $now_with_format > $this->getEndDate())) {
5373 return null;
5374 }
5375
5376 // reminder period
5377 $start = $this->getReminderStart();
5378 if ($start) {
5379 $start = $start->get(IL_CAL_DATE);
5380 }
5381 $end = $this->getReminderEnd();
5382 if ($end) {
5383 $end = $end->get(IL_CAL_DATE);
5384 }
5385 if ($today < $start ||
5386 ($end && $today > $end)) {
5387 return null;
5388 }
5389
5390 $this->svy_log->debug("Check access period.");
5391
5392 // object access period
5393 $item_data = ilObjectActivation::getItem($this->getRefId());
5394 if ((int) $item_data["timing_type"] === ilObjectActivation::TIMINGS_ACTIVATION &&
5395 ($now < $item_data["timing_start"] ||
5396 $now > $item_data["timing_end"])) {
5397 return null;
5398 }
5399
5400 $this->svy_log->debug("Check frequency.");
5401
5402 // check frequency
5403 $cut = new ilDate($today, IL_CAL_DATE);
5404 $cut->increment(IL_CAL_DAY, $this->getReminderFrequency() * -1);
5405 if (!$this->getReminderLastSent() ||
5406 $cut->get(IL_CAL_DATE) >= substr($this->getReminderLastSent(), 0, 10)) {
5407 $missing_ids = array();
5408 if (!$this->feature_config->usesAppraisees()) {
5409 $this->svy_log->debug("Entering survey mode.");
5410
5411 // #16871
5412 $user_ids = $this->getNotificationTargetUserIds(($this->getReminderTarget() === self::NOTIFICATION_INVITED_USERS));
5413 if ($user_ids) {
5414 // gather participants who already finished
5415 $finished_ids = array();
5416 $set = $ilDB->query("SELECT user_fi FROM svy_finished" .
5417 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") .
5418 " AND state = " . $ilDB->quote(1, "text") .
5419 " AND " . $ilDB->in("user_fi", $user_ids, "", "integer"));
5420 while ($row = $ilDB->fetchAssoc($set)) {
5421 $finished_ids[] = $row["user_fi"];
5422 }
5423
5424 // some users missing out?
5425 $missing_ids = array_diff($user_ids, $finished_ids);
5426 if ($missing_ids) {
5427 foreach ($missing_ids as $idx => $user_id) {
5428 // should be able to participate
5429 if (!$ilAccess->checkAccessOfUser($user_id, "read", "", $this->getRefId(), "svy", $this->getId())) {
5430 unset($missing_ids[$idx]);
5431 }
5432 }
5433 }
5434 if ($missing_ids) {
5435 $this->sentReminder($missing_ids);
5436 }
5437 }
5438 } else {
5439 $this->svy_log->debug("Entering 360 mode.");
5440
5441 $this->sent360Reminders();
5442 }
5443
5444
5445 $this->setReminderLastSent($today);
5446 $this->saveToDb();
5447
5448 return count($missing_ids);
5449 }
5450
5451 return null;
5452 }
5453
5454 protected function sentReminder(
5455 array $a_recipient_ids
5456 ): void {
5457 global $DIC;
5458
5459 $link = "";
5460
5461 // use mail template
5462 if ($this->getReminderTemplate() &&
5463 array_key_exists($this->getReminderTemplate(), $this->getReminderMailTemplates())) {
5465 $templateService = $DIC['mail.texttemplates.service'];
5466 $tmpl = $templateService->loadTemplateForId($this->getReminderTemplate());
5467
5468 $tmpl_params = array(
5469 "ref_id" => $this->getRefId(),
5470 "ts" => time()
5471 );
5472 } else {
5473 $tmpl = null;
5474 $tmpl_params = null;
5475 $link = ilLink::_getStaticLink($this->getRefId(), "svy");
5476 }
5477
5478 foreach ($a_recipient_ids as $user_id) {
5479 if ($tmpl) {
5480 $subject = $tmpl->getSubject();
5481 $message = $this->sentReminderPlaceholders($tmpl->getMessage(), $user_id, $tmpl_params);
5482 }
5483 // use lng
5484 else {
5485 // use language of recipient to compose message
5486 $ulng = ilLanguageFactory::_getLanguageOfUser($user_id);
5487 $ulng->loadLanguageModule('survey');
5488
5489 $subject = sprintf($ulng->txt('survey_reminder_subject'), $this->getTitle());
5490 $message = sprintf($ulng->txt('survey_reminder_salutation'), ilObjUser::_lookupFullname($user_id)) . "\n\n";
5491
5492 $message .= $ulng->txt('survey_reminder_body') . ":\n\n";
5493 $message .= $ulng->txt('obj_svy') . ": " . $this->getTitle() . "\n";
5494 $message .= "\n" . $ulng->txt('survey_reminder_link') . ": " . $link;
5495 }
5496
5497 $mail_obj = new ilMail(ANONYMOUS_USER_ID);
5498 $mail_obj->appendInstallationSignature(true);
5499 $mail_obj->enqueue(
5500 ilObjUser::_lookupLogin($user_id),
5501 "",
5502 "",
5503 $subject,
5504 $message,
5505 array()
5506 );
5507 }
5508 }
5509
5510 public function setActivationStartDate(
5511 int $starting_time = null
5512 ): void {
5513 $this->activation_starting_time = $starting_time;
5514 }
5515
5516 public function setActivationEndDate(
5517 int $ending_time = null
5518 ): void {
5519 $this->activation_ending_time = $ending_time;
5520 }
5521
5522 public function getActivationStartDate(): ?int
5523 {
5524 return $this->activation_starting_time;
5525 }
5526
5527 public function getActivationEndDate(): ?int
5528 {
5529 return $this->activation_ending_time;
5530 }
5531
5532 public function setViewOwnResults(bool $a_value): void
5533 {
5534 $this->view_own_results = $a_value;
5535 }
5536
5537 public function hasViewOwnResults(): bool
5538 {
5539 return $this->view_own_results;
5540 }
5541
5542 public function setMailOwnResults(bool $a_value): void
5543 {
5544 $this->mail_own_results = $a_value;
5545 }
5546
5547 public function hasMailOwnResults(): bool
5548 {
5549 return $this->mail_own_results;
5550 }
5551
5552 public function setMailConfirmation(bool $a_value): void
5553 {
5554 $this->mail_confirmation = $a_value;
5555 }
5556
5557 public function hasMailConfirmation(): bool
5558 {
5559 return $this->mail_confirmation;
5560 }
5561
5562 public function setAnonymousUserList(bool $a_value): void
5563 {
5564 $this->anon_user_list = $a_value;
5565 }
5566
5567 public function hasAnonymousUserList(): bool
5568 {
5569 return $this->anon_user_list;
5570 }
5571
5572 public static function getSurveySkippedValue(): string
5573 {
5574 global $DIC;
5575
5576 $lng = $DIC->language();
5577
5578 // #13541
5579
5580 $surveySetting = new ilSetting("survey");
5581 if (!$surveySetting->get("skipped_is_custom", false)) {
5582 return $lng->txt("skipped");
5583 } else {
5584 return $surveySetting->get("skipped_custom_value", "");
5585 }
5586 }
5587
5588 public function getReminderMailTemplates(
5589 int &$defaultTemplateId = null
5590 ): array {
5591 global $DIC;
5592
5593 $res = array();
5594
5596 $templateService = $DIC['mail.texttemplates.service'];
5597 foreach ($templateService->loadTemplatesForContextId(ilSurveyMailTemplateReminderContext::ID) as $tmpl) {
5598 $res[$tmpl->getTplId()] = $tmpl->getTitle();
5599 if (null !== $defaultTemplateId && $tmpl->isDefault()) {
5600 $defaultTemplateId = $tmpl->getTplId();
5601 }
5602 }
5603
5604 return $res;
5605 }
5606
5607 protected function sentReminderPlaceholders(
5608 string $a_message,
5609 int $a_user_id,
5610 array $a_context_params
5611 ): string {
5612 // see ilMail::replacePlaceholders()
5613 try {
5615
5616 $user = new \ilObjUser($a_user_id);
5617
5618 $processor = new \ilMailTemplatePlaceholderResolver($context, $a_message);
5619 $a_message = $processor->resolve($user, $a_context_params);
5620 } catch (\Exception $e) {
5621 ilLoggerFactory::getLogger('mail')->error(__METHOD__ . ' has been called with invalid context.');
5622 }
5623
5624 return $a_message;
5625 }
5626
5627 public function setMode(int $a_value): void
5628 {
5629 $this->mode = $a_value;
5630 }
5631
5632 public function getMode(): int
5633 {
5634 return $this->mode;
5635 }
5636
5637 public function setSelfEvaluationResults(int $a_value): void
5638 {
5639 $this->mode_self_eval_results = $a_value;
5640 }
5641
5643 {
5644 return $this->mode_self_eval_results;
5645 }
5646
5650 public static function getSurveysWithTutorResults(): array
5651 {
5652 global $ilDB;
5653
5654 $res = array();
5655
5657
5658
5659 $q = "SELECT obj_fi FROM svy_svy" .
5660 " WHERE tutor_res_cron IS NULL" .
5661 " AND tutor_res_status = " . $ilDB->quote(1, "integer") .
5662 " AND enddate < " . $ilDB->quote(date("Ymd000000"), "text");
5663
5664 if (DEVMODE) {
5665 $q = "SELECT obj_fi FROM svy_svy" .
5666 " WHERE tutor_res_status = " . $ilDB->quote(1, "integer") .
5667 " AND enddate < " . $ilDB->quote(date("Ymd000000"), "text");
5668 }
5669
5670 $set = $ilDB->query($q);
5671
5672 $log->debug($q);
5673
5674 while ($row = $ilDB->fetchAssoc($set)) {
5675 $res[] = (int) $row["obj_fi"];
5676 }
5677
5678 return $res;
5679 }
5680
5681 public function getMaxSumScore(): int
5682 {
5683 $sum_score = 0;
5685 $sum_score += call_user_func([$c, "getMaxSumScore"], $this->getSurveyId());
5686 }
5687 return $sum_score;
5688 }
5689}
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
Basic class for all survey question types The SurveyQuestionGUI class defines and encapsulates basic ...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static _getOriginalId(int $question_id, bool $a_return_question_id_if_no_original=true)
Returns the original id of a question.
static _instanciateQuestionEvaluation(int $question_id, array $a_finished_ids=null)
static _includeClass(string $question_type, int $gui=0)
Include the php class file for a given question type.
static _getQuestionType(int $question_id)
Returns the question type of a question with a given id.
static _isComplete(int $question_id)
Checks whether the question is complete or not.
const IL_CAL_DATE
const IL_CAL_TIMESTAMP
const IL_CAL_UNIX
const IL_CAL_DATETIME
const IL_CAL_DAY
return true
static sortArray(array $array, string $a_array_sortby_key, string $a_array_sortorder="asc", bool $a_numeric=false, bool $a_keep_keys=false)
static _getInstance(int $a_copy_id)
@classDescription Date and time handling
Class for single dates.
static makeDirParents(string $a_dir)
Create a new directory and all parent directories.
static unzip(string $path_to_zip_file, bool $overwrite_existing=false, bool $unpack_flat=false)
static makeDir(string $a_dir)
creates a new directory and inherits all filesystem permissions of the parent directory You may pass ...
static delDir(string $a_dir, bool $a_clean_only=false)
removes a dir and all its content (subdirs and files) recursively
static getDataDir()
get data directory (outside webspace)
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...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static _getLanguageOfUser(int $a_usr_id)
Get language object of user.
txt(string $a_topic, string $a_default_lang_fallback_mod="")
gets the text for a given topic if the topic is not in the list, the topic itself with "-" will be re...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static getLogger(string $a_component_id)
Get component logger.
Component logger with individual log levels by component id.
getLifecycle()
Definition: class.ilMD.php:51
setLangModules(array $a_modules)
Class ilMailTemplateContextService.
static _getUsedHTMLTagsAsString(string $a_module="")
Returns a string of all allowed HTML tags for text editing.
static _getMobsOfObject(string $a_type, int $a_id, int $a_usage_hist_nr=0, string $a_lang="-")
static _removeUsage(int $a_mob_id, string $a_type, int $a_id, int $a_usage_hist_nr=0, string $a_lang="-")
Remove usage of mob in another container.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
set360SelfAppraisee(bool $a_value)
sendRaterNotification(int $a_user_id, int $a_appraisee_id)
string $author
A text representation of the authors name.
isUnusedCode(string $a_code, int $a_user_id)
Participants InvitationsManager $invitation_manager
getSurveyPages()
Returns the survey pages in an array (a page contains one or more questions)
isPluginActive(string $a_pname)
getNextPage(int $active_page_question_id, int $direction)
Get current, previous or next page.
getAppraiseesToRate(?int $a_user_id, int $a_anonymous_id=null)
removeQuestionFromBlock(int $question_id, int $questionblock_id)
addRater(int $a_appraisee_id, int $a_user_id, int $a_anonymous_id=0)
isSurveyCodeUsed(string $code)
setEndDateAndTime(string $end_date, string $end_time)
importSurveyCode(string $a_anonymize_key, int $a_created, array $a_data)
const EVALUATION_ACCESS_PARTICIPANTS
isSurveyFinishedByCode(string $a_code)
Get if survey is finished for a specific anonymous user code.
const RESULTS_SELF_EVAL_NONE
setStartDateAndTime(string $start_date, string $start_time)
getExternalCodeRecipients(bool $a_check_finished=false)
Mode FeatureConfig $feature_config
setMode(int $a_value)
removeQuestion(int $question_id)
setAnonymousUserList(bool $a_value)
closeAppraisee(int $a_user_id)
moveQuestions(array $move_questions, int $target_index, int $insert_mode)
Move questions and/or questionblocks to another position.
getFinishedIdForAppraiseeIdAndRaterId(int $a_appr_id, int $a_rat_id)
Get finished id for an appraisee and a rater.
addAppraisee(int $a_user_id)
getUserSpecificResults(array $finished_ids)
Calculates the evaluation data for the user specific results.
getQuestionblockQuestions(int $questionblock_id)
getSurveyFinishedIds()
Get run ids.
setReminderStatus(bool $a_value)
sendNotificationMail(int $a_user_id, string $a_anonymize_id, int $a_appr_id=0)
These mails are sent to tutors for each single participant that finishes a survey.
string $mailparticipantdata
getTextblock(int $question_id)
update($a_upload=false)
setSelfEvaluationResults(int $a_value)
setTutorNotificationStatus(bool $a_value)
Activates mail being sent to tutors after all participants have finished the survey.
prepareTextareaOutput(string $txt_output)
Prepares a string for a text area output in surveys.
createImportDirectory()
creates data directory for import files (data_dir/svy_data/svy_<id>/import)
static _addQuestionblock(string $title="", int $owner=0, bool $show_questiontext=true, bool $show_blocktitle=false, bool $compress_view=false)
getAvailableQuestionpools(bool $use_obj_id=false, bool $could_be_offline=false, bool $showPath=false, string $permission="read")
Returns the available question pools for the active user.
ilAccessHandler $access
__construct(int $a_id=0, bool $a_call_by_reference=true)
getUserSettings(int $usr_id, string $key)
setReminderLastSent(?string $a_value)
array $tutor_res_recipients
isQuestionInSurvey(int $a_question_fi)
set360Results(int $a_value)
getEvaluationByUser(array $questions, int $active_id)
Calculates the evaluation data for a given run.
setSurveyId(int $survey_id)
isQuestionInAnyBlock(int $a_question_fi)
create($a_upload=false)
sendAppraiseeNotification(int $a_user_id)
static _getQuestionblock(int $questionblock_id)
get question block properties
locateImportFiles(string $a_dir)
Locates the import directory and the xml file in a directory with an unzipped import file.
getTutorNotificationRecipients()
Mail being sent to tutors after all participants have finished the survey?
getSurveyQuestions(bool $with_answers=false)
Returns the survey questions and questionblocks in an array.
setOutro(string $outro="")
addMaterialTag(ilXmlWriter $a_xml_writer, string $a_material, bool $close_material_tag=true, bool $add_mobs=true, array $attribs=null)
Creates an XML material tag from a plain text or xhtml text.
addQuestion(int $question_id)
Adds a question to the survey (used in importer!)
setTutorResultsRecipients(array $a_value)
const NOTIFICATION_INVITED_USERS
const RESULTS_SELF_EVAL_ALL
updateConstraint(int $precondition_id, int $if_question_id, int $relation, float $value, int $conjunction)
getExistingQuestions()
Gets the question id's of the questions which are already in the survey.
deleteConstraint(int $constraint_id)
Deletes a single constraint.
setReminderTemplate(?int $a_value)
setSkillService(bool $a_val)
removeSelectedSurveyResults(array $finished_ids)
Deletes the user data of a given array of survey participants.
saveUserAccessCode(int $user_id, string $access_code)
Saves a survey access code for a registered user to the database.
setObligatoryStates(array $obligatory_questions)
Sets the obligatory states for questions in a survey from the questions form.
setIntroduction(string $introduction="")
setEndDate(string $end_date="")
ilPluginAdmin $plugin_admin
sendCodes(int $not_sent, string $subject, string $message, string $lang)
setStartDate(string $start_date="")
getQuestionblocksTable(array $arrFilter)
Retrieve data for question block browser.
getUserAccessCode(int $user_id)
Returns a survey access code that was saved for a registered user.
const NOTIFICATION_PARENT_COURSE
const EVALUATION_ACCESS_ALL
isRater(int $a_appraisee_id, int $a_user_id, int $a_anonymous_id=0)
deleteWorkingData(int $question_id, int $active_id)
Deletes the working data of a question in the database.
getQuestionsTable(array $arrFilter)
Retrieve data for question browser.
setActivationEndDate(int $ending_time=null)
insertQuestionblock(int $questionblock_id)
getUserDataFromActiveId(int $active_id, bool $force_non_anonymous=false)
Returns run information.
getUserData(array $ids)
Returns a data of all users specified by id list.
getTutorNotificationTarget()
Group that is checked for the "all participants have finished" mail Either a) all members of a parent...
getNotificationTargetUserIds(bool $a_use_invited)
These users must finish the survey to trigger the tutor notification "all users finished the survey" ...
ILIAS SurveyQuestionPool Export ImportManager $import_manager
getFinishedIdsForAppraiseeId(int $a_appr_id, bool $a_exclude_appraisee=false)
const QUESTIONTITLES_VISIBLE
getLastActivePage(int $active_id)
Returns the question id of the last active page a user visited in a survey.
deleteSurveyCode(string $survey_code)
setReminderFrequency(int $a_value)
setShowQuestionTitles(bool $a_show)
toXML()
Returns a QTI xml representation of the survey.
getAnonymousIdByCode(string $a_code)
getLastAccess(int $finished_id)
sendTutorNotification()
Send mail to tutors after all participants have finished the survey.
getReminderTemplate(bool $selectDefault=false)
loadWorkingData(int $question_id, int $active_id)
Gets the given answer data of a question in a run.
deleteConstraints(int $question_id)
Deletes the constraints for a question.
getQuestionpoolTitles(bool $could_be_offline=false, bool $showPath=false)
Returns the available question pools for the active user.
const NOTIFICATION_APPRAISEES_AND_RATERS
setPage(int $finished_id, int $page_id)
Sets the number of the active survey page.
static getSurveysWithTutorResults()
static _getConstraints(int $survey_id)
saveUserSettings(int $usr_id, string $key, string $title, string $value)
const ANONYMIZE_FREEACCESS
int $survey_id
A unique positive numerical ID which identifies the survey.
saveHeading(string $heading, int $insertbefore)
updateOrder(array $a_order)
getQuestionType(int $question_id)
deleteUserSettings(int $id)
addConstraintToQuestion(int $to_question_id, int $constraint_id)
setTutorResultsStatus(bool $a_value)
updateConjunctionForQuestions(array $questions, int $conjunction)
setActivationVisibility(bool $a_value)
static _hasDatasets(int $survey_id)
deleteAllUserData(bool $reset_LP=true)
Deletes all user data of a survey.
getMailParticipantData()
Preceding text (incl.
setMailNotification(bool $a_notification)
Activate mail to tutors each time a participant finishes the survey.
getAllRelations(bool $short_as_key=false)
is360SurveyStarted(int $appr_id, int $user_id, string $anonymous_code=null)
sendAppraiseeCloseNotification(int $a_user_id)
int $display_question_titles
deleteAppraisee(int $a_user_id)
getMailAddresses()
(Tutor) Recipients of "single participant has finished" mails
setTutorNotificationRecipients(array $a_value)
Set tutor recipients for "all participants have finished" mail.
isComplete()
Check if survey is complete for use.
getSurveyParticipants(?array $finished_ids=null, bool $force_non_anonymous=false, bool $include_invites=false)
setReminderTarget(int $a_value)
getQuestionblockQuestionIds(int $questionblock_id)
addConstraint(int $if_question_id, int $relation, float $value, int $conjunction)
Adds a constraint.
getQuestionGUI(string $questiontype, int $question_id)
getSurveyCodesForExport(array $a_codes=null, array $a_ids=null)
Return a list of survey codes for file export, note: user_key needs to be null to export a record.
getActiveID(int $user_id, string $anonymize_id, int $appr_id)
Get run id.
getConstraints(int $question_id)
Returns the constraints to a given question or questionblock.
saveAuthorToMetadata(string $a_author="")
Saves an authors name into the lifecycle metadata if no lifecycle metadata exists This will only be c...
checkTutorNotification()
Check, if mail to tutors after all participants have finished the survey should be sent.
getWorkingtimeForParticipant(int $finished_id)
findCodeForUser(int $a_user_id)
deleteRater(int $a_appraisee_id, int $a_user_id, int $a_anonymous_id=0)
saveCompletionStatus()
Saves the completion status of the survey.
static getSurveySkippedValue()
const EVALUATION_ACCESS_OFF
static _instanciateQuestion(int $question_id)
Creates an instance of a question with a given question id.
createReference()
creates reference for object
updateCode(int $a_id, string $a_email, string $a_last_name, string $a_first_name, int $a_sent)
setMailParticipantData(string $a_data)
Set preceding text (incl.
duplicateQuestionForSurvey(int $question_id, bool $a_force=false)
Takes a question and creates a copy of the question for use in the survey.
isHTML(string $a_text)
Checks if a given string contains HTML or not.
setCalculateSumScore(bool $a_val)
string $reminder_last_sent
modifyQuestionblock(int $questionblock_id, string $title, bool $show_questiontext, bool $show_blocktitle, bool $compress_view=false)
setTutorNotificationTarget(int $a_value)
setEvaluationAccess(string $evaluation_access=self::EVALUATION_ACCESS_OFF)
getImportDirectory()
get import directory of survey
deleteSurveyRecord()
Deletes the survey from the database.
getUserSurveyExecutionStatus(string $a_code=null)
Get current user execution status.
createQuestionblock(string $title, bool $show_questiontext, bool $show_blocktitle, array $questions, bool $compress_view=false)
cloneTextblocks(array $mapping)
Clones the text blocks of survey questions.
isAppraiseeClosed(int $a_user_id)
const RESULTS_SELF_EVAL_OWN
sent360Reminders()
Send 360 reminders.
set360RaterSent(int $a_appraisee_id, int $a_user_id, int $a_anonymous_id, int $a_tstamp=null)
finishSurvey(int $finished_id, int $appr_id=0)
Finishes the survey creating an entry in the database.
isAppraisee(int $a_user_id)
const NOTIFICATION_APPRAISEES
unfoldQuestionblocks(array $questionblocks)
const ANONYMIZE_CODE_ALL
setMailConfirmation(bool $a_value)
getParticipantTextResults(int $active_id)
isSurveyCodeUnique(string $code)
string $evaluation_access
setReminderStart(?ilDate $a_value=null)
setReminderEnd(?ilDate $a_value=null)
setAnonymize(int $a_anonymize)
set anonymize status
removeConstraintsConcerningQuestion(int $question_id)
Remove constraints concerning a question with a given question_id.
importObject(array $file_info, int $svy_qpl_id)
addQuestionToBlock(int $question_id, int $questionblock_id)
removeQuestions(array $remove_questions, array $remove_questionblocks)
getSurveyCodesTableData(array $ids=null, string $lang=null)
Fetches the data for the survey codes table.
set360SelfRaters(bool $a_value)
getRatersData(int $a_appraisee_id)
getMailNotification()
Send mail to tutors each time a participant finishes the survey?
setMailAddresses(string $a_addresses)
Set (Tutor) Recipients of "single participant has finished" mails.
getPrecondition(int $constraint_id)
Returns a precondition with a given id.
checkConstraint(array $constraint_data, ?array $working_data)
Checks if a constraint is valid.
setActivationLimited(bool $a_value)
setPoolUsage(bool $a_value)
insertQuestion(int $question_id)
Inserts a question in the survey and saves the relation to the database.
setMailOwnResults(bool $a_value)
send360ReminderToUser(int $a_user_id, array $a_appraisee_ids)
setActivationStartDate(int $starting_time=null)
array $tutor_ntf_recipients
set360SelfEvaluation(bool $a_value)
setViewOwnResults(bool $a_value)
createExportDirectory()
creates data directory for export files (data_dir/svy_data/svy_<id>/export)
const NOTIFICATION_RATERS
setAuthor(string $author="")
canExportSurveyCode()
Checks if the survey code can be exported with the survey evaluation.
ILIAS Survey InternalDataService $data_manager
getVariables(int $question_id)
Returns all variables (answer options/scale values) of a question.
isAnonymizedParticipant(string $key)
sentReminderPlaceholders(string $a_message, int $a_user_id, array $a_context_params)
ILIAS Survey Code CodeManager $code_manager
ILIAS Survey InternalService $survey_service
const QUESTIONTITLES_HIDDEN
User class.
getFullname(int $a_max_strlen=0)
static _lookupFullname(int $a_user_id)
static _lookupId($a_user_str)
static _getUserData(array $a_internalids)
return user data for given user ids
static getUserIdsByEmail(string $a_email)
static _lookupName(int $a_user_id)
lookup user name
static _lookupLogin(int $a_user_id)
static _lookupEmail(int $a_user_id)
Class ilObjectActivation.
static getItem(int $ref_id)
static getInstance(int $obj_id)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
ilLanguage $lng
setOfflineStatus(bool $status)
static _lookupObjId(int $ref_id)
static _lookupTitle(int $obj_id)
ilDBInterface $db
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static _replaceMediaObjectImageSrc(string $a_text, int $a_direction=0, string $nic='')
Replaces image source from mob image urls with the mob id or replaces mob id with the correct image s...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static getNamePresentation( $a_user_id, bool $a_user_image=false, bool $a_profile_link=false, string $a_profile_back_link="", bool $a_force_first_lastname=false, bool $a_omit_login=false, bool $a_sortable=true, bool $a_return_data_array=false, $a_ctrl_path="ilpublicuserprofilegui")
Default behaviour is:
static stripSlashes(string $a_str, bool $a_strip_html=true, string $a_allow="")
static is_email(string $a_email, ilMailRfc822AddressParserFactory $mailAddressParserFactory=null)
This preg-based function checks whether an e-mail address is formally valid.
static _getObjectsByOperations( $a_obj_type, string $a_operation, int $a_usr_id=0, int $limit=0)
Get all objects of a specific type and check access This function is not recursive,...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
xmlElement(string $tag, $attrs=null, $data=null, $encode=true, $escape=true)
Writes a basic element (no children, just textual content)
xmlEndTag(string $tag)
Writes an endtag.
$c
Definition: cli.php:38
$app
Definition: cli.php:39
if(!file_exists(getcwd() . '/ilias.ini.php'))
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Definition: confirmReg.php:20
const IL_INST_ID
Definition: constants.php:40
const ANONYMOUS_USER_ID
Definition: constants.php:27
try
Definition: cron.php:23
for( $i=6;$i< 13;$i++) for($i=1; $i< 13; $i++) $d
Definition: date.php:296
return['3gp', '7z', 'ai', 'aif', 'aifc', 'aiff', 'au', 'arw', 'avi', 'backup', 'bak', 'bas', 'bpmn', 'bpmn2', 'bmp', 'bib', 'bibtex', 'bz', 'bz2', 'c', 'c++', 'cc', 'cct', 'cdf', 'cer', 'class', 'cls', 'conf', 'cpp', 'crt', 'crs', 'crw', 'cr2', 'css', 'cst', 'csv', 'cur', 'db', 'dcr', 'des', 'dng', 'doc', 'docx', 'dot', 'dotx', 'dtd', 'dvi', 'el', 'eps', 'epub', 'f', 'f77', 'f90', 'flv', 'for', 'g3', 'gif', 'gl', 'gan', 'ggb', 'gsd', 'gsm', 'gtar', 'gz', 'gzip', 'h', 'hpp', 'htm', 'html', 'htmls', 'ibooks', 'ico', 'ics', 'ini', 'ipynb', 'java', 'jbf', 'jpeg', 'jpg', 'js', 'jsf', 'jso', 'json', 'latex', 'lang', 'less', 'log', 'lsp', 'ltx', 'm1v', 'm2a', 'm2v', 'm3u', 'm4a', 'm4v', 'markdown', 'm', 'mat', 'md', 'mdl', 'mdown', 'mid', 'min', 'midi', 'mobi', 'mod', 'mov', 'movie', 'mp2', 'mp3', 'mp4', 'mpa', 'mpeg', 'mpg', 'mph', 'mpga', 'mpp', 'mpt', 'mpv', 'mpx', 'mv', 'mw', 'mv4', 'nb', 'nbp', 'nef', 'nif', 'niff', 'obj', 'obm', 'odt', 'ods', 'odp', 'odg', 'odf', 'oga', 'ogg', 'ogv', 'old', 'p', 'pas', 'pbm', 'pcl', 'pct', 'pcx', 'pdf', 'pgm', 'pic', 'pict', 'png', 'por', 'pov', 'project', 'properties', 'ppa', 'ppm', 'pps', 'ppsx', 'ppt', 'pptx', 'ppz', 'ps', 'psd', 'pwz', 'qt', 'qtc', 'qti', 'qtif', 'r', 'ra', 'ram', 'rar', 'rast', 'rda', 'rev', 'rexx', 'ris', 'rf', 'rgb', 'rm', 'rmd', 'rmi', 'rmm', 'rmp', 'rt', 'rtf', 'rtx', 'rv', 's', 's3m', 'sav', 'sbs', 'sec', 'sdml', 'sgm', 'sgml', 'smi', 'smil', 'srt', 'sps', 'spv', 'stl', 'svg', 'swa', 'swf', 'swz', 'tar', 'tex', 'texi', 'texinfo', 'text', 'tgz', 'tif', 'tiff', 'ttf', 'txt', 'tmp', 'uvproj', 'vdf', 'vimeo', 'viv', 'vivo', 'vrml', 'vsdx', 'wav', 'webm', 'wmv', 'wmx', 'wmz', 'woff', 'wwd', 'xhtml', 'xif', 'xls', 'xlsx', 'xmind', 'xml', 'xsl', 'xsd', 'zip']
global $DIC
Definition: feed.php:28
$target_id
Definition: goto.php:52
global $ilPluginAdmin
Definition: goto.php:23
$update
Definition: imgupload.php:92
$mobs
Definition: imgupload.php:70
$ilUser
Definition: imgupload.php:34
usesAppraisees()
If raters rate single persons (appraisees) this mode is activated.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
checkAccessOfUser(int $a_user_id, string $a_permission, string $a_cmd, int $a_ref_id, string $a_type="", ?int $a_obj_id=null, ?int $a_tree_id=null)
check access for an object (provide $a_type and $a_obj_id if available for better performance)
if(! $DIC->user() ->getId()||!ilLTIConsumerAccess::hasCustomProviderCreationAccess()) $params
Definition: ltiregstart.php:33
$res
Definition: ltiservices.php:69
if($format !==null) $name
Definition: metadata.php:247
$index
Definition: metadata.php:145
if(!array_key_exists('PATH_INFO', $_SERVER)) $config
Definition: metadata.php:85
$i
Definition: metadata.php:41
$source
Definition: metadata.php:93
$xml
Definition: metadata.php:351
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc
string $key
Consumer key/client ID value.
Definition: System.php:193
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
$url
$log
Definition: result.php:33
$lng
$context
Definition: webdav.php:29
$lang
Definition: xapiexit.php:26
$message
Definition: xapiexit.php:32
$rows
Definition: xhr_table.php:10