ILIAS  trunk Revision v12.0_alpha-1227-g7ff6d300864
class.ilObjSurvey.php
Go to the documentation of this file.
1<?php
2
22
26class ilObjSurvey extends ilObject
27{
28 public const EVALUATION_ACCESS_OFF = "0";
29 public const EVALUATION_ACCESS_ALL = "1";
31
32 public const ANONYMIZE_OFF = 0; // personalized, no codes
33 public const ANONYMIZE_ON = 1; // anonymized, codes
34 public const ANONYMIZE_FREEACCESS = 2; // anonymized, no codes
35 public const ANONYMIZE_CODE_ALL = 3; // personalized, codes
36
37 public const QUESTIONTITLES_HIDDEN = 0;
38 public const QUESTIONTITLES_VISIBLE = 1;
39
40 // constants to define the print view values.
41 public const PRINT_HIDE_LABELS = 1; // Show only the titles in print view
42 public const PRINT_SHOW_LABELS = 3; // Show titles and labels in print view
43
44 //MODE TYPES
45 public const MODE_STANDARD = 0;
46 public const MODE_360 = 1;
47 public const MODE_SELF_EVAL = 2;
48 public const MODE_IND_FEEDB = 3;
49
50 //self evaluation only access to results
51 public const RESULTS_SELF_EVAL_NONE = 0;
52 public const RESULTS_SELF_EVAL_OWN = 1;
53 public const RESULTS_SELF_EVAL_ALL = 2;
54
55 public const RESULTS_360_NONE = 0;
56 public const RESULTS_360_OWN = 1;
57 public const RESULTS_360_ALL = 2;
58
61 public const NOTIFICATION_APPRAISEES = 3;
62 public const NOTIFICATION_RATERS = 4;
65
66 protected ilLogger $svy_log;
67 protected bool $activation_limited = false;
68 protected ilObjUser $user;
71 protected bool $calculate_sum_score = false;
76 public int $survey_id = 0;
81 public string $author = "";
82 public string $introduction = "";
83 public string $outro = "";
84 // Indicates the evaluation access for learners
86 // The start date of the survey
87 public string $start_date = "";
88 // The end date of the survey
89 public string $end_date = "";
90 // The questions contained in this survey
91 public array $questions = [];
92 // Indicates the anonymization of the survey
93 public int $anonymize = 0;
94 // Indicates if the question titles are shown during a query
96 // Indicates if a survey code may be exported with the survey statistics
97 public bool $surveyCodeSecurity = false;
98
99 public bool $mailnotification = false;
100 public string $mailaddresses = "";
101 public string $mailparticipantdata = "";
102 public bool $pool_usage = false;
103
104 protected bool $activation_visibility = false;
105 protected ?int $activation_starting_time = null;
106 protected ?int $activation_ending_time = null;
107
108 // 360°
109 protected bool $mode_360_self_eval = false;
110 protected bool $mode_360_self_appr = false;
111 protected bool $mode_360_self_rate = false;
112 protected int $mode_360_results = 0;
113 protected bool $mode_skill_service = false;
114
115 // reminder/notification
116 protected bool $reminder_status = false;
117 protected ?ilDate $reminder_start = null;
118 protected ?ilDate $reminder_end = null;
119 protected int $reminder_frequency = 0;
120 protected int $reminder_target = 0;
121 protected ?string $reminder_last_sent = null;
122 protected ?int $reminder_tmpl = null;
123 protected bool $tutor_ntf_status = false;
124 protected array $tutor_ntf_recipients = [];
125 protected int $tutor_ntf_target = 0;
126 protected bool $tutor_res_status = false;
127 protected array $tutor_res_recipients = [];
128
129 protected bool $view_own_results = false;
130 protected bool $mail_own_results = false;
131 protected bool $mail_confirmation = false;
132
133 protected bool $anon_user_list = false;
134 protected int $mode = 0;
135 protected int $mode_self_eval_results = 0;
136
138 protected \ILIAS\Survey\InternalService $survey_service;
139 protected \ILIAS\Survey\Code\CodeManager $code_manager;
140 protected \ILIAS\Survey\InternalDataService $data_manager;
142 protected \ILIAS\SurveyQuestionPool\Export\ImportManager $import_manager;
144
145 public function __construct(
146 int $a_id = 0,
147 bool $a_call_by_reference = true
148 ) {
149 global $DIC;
150
151 $this->survey_service = $DIC->survey()->internal();
152
153 $this->user = $DIC->user();
154 $this->lng = $DIC->language();
155 $this->db = $DIC->database();
156 $this->access = $DIC->access();
157 $this->plugin_admin = $DIC["ilPluginAdmin"];
158 $this->tree = $DIC->repositoryTree();
159 $ilUser = $DIC->user();
160 $lng = $DIC->language();
161
162 $this->type = "svy";
163 $this->survey_id = -1;
164 $this->introduction = "";
165 $this->outro = $lng->txt("survey_finished");
166 $this->author = $ilUser->getFullname();
167 $this->evaluation_access = self::EVALUATION_ACCESS_OFF;
168 $this->questions = array();
169 $this->anonymize = self::ANONYMIZE_OFF;
170 $this->display_question_titles = self::QUESTIONTITLES_VISIBLE;
171 $this->surveyCodeSecurity = true;
172 $this->pool_usage = true;
173 $this->mode = self::MODE_STANDARD;
174 $this->mode_self_eval_results = self::RESULTS_SELF_EVAL_OWN;
175
176 $this->invitation_manager = $this
177 ->survey_service
178 ->domain()
179 ->participants()
180 ->invitations();
181
182 $this->import_manager = $DIC->surveyQuestionPool()
183 ->internal()
184 ->domain()
185 ->import();
186 $this->placeholder_resolver = $DIC->mail()->placeholderResolver();
187
188 parent::__construct($a_id, $a_call_by_reference);
189 $this->svy_log = ilLoggerFactory::getLogger("svy");
190 $this->initServices();
191 $this->domain = $DIC->survey()->internal()->domain();
192 }
193
194 protected function initServices(): void
195 {
196 if ($this->getRefId() > 0) {
197 $this->code_manager = $this
198 ->survey_service
199 ->domain()
200 ->code($this, $this->user->getId());
201 }
202 $this->feature_config = $this
203 ->survey_service
204 ->domain()
205 ->modeFeatureConfig($this->getMode());
206
207 $this->data_manager = $this
208 ->survey_service
209 ->data();
210 }
211
212 public function create($a_upload = false): int
213 {
214 $id = parent::create();
215 if (!$a_upload) {
216 $this->createMetaData();
217 }
218 $this->setOfflineStatus(true);
219 $this->update($a_upload);
220 return $id;
221 }
222
223 protected function doCreateMetaData(): void
224 {
225 $this->saveAuthorToMetadata();
226 }
227
228 public function update($a_upload = false): bool
229 {
230 if (!$a_upload) {
231 $this->updateMetaData();
232 }
233
234 if (!parent::update()) {
235 return false;
236 }
237
238 // put here object specific stuff
239
240 return true;
241 }
242
243 public function createReference(): int
244 {
245 $result = parent::createReference();
246 $this->saveToDb();
247 return $result;
248 }
249
250 public function read(): void
251 {
252 parent::read();
253 $this->loadFromDb();
254 $this->initServices();
255 }
256
260 public function addQuestion(int $question_id): void
261 {
262 $this->questions[] = $question_id;
263 }
264
265 public function delete(): bool
266 {
267 $this->svy_log->debug("Deleting Survey, ref id: " . $this->getRefId() . ", obj id: " .
268 $this->getId() . ", title: " . $this->getTitle());
269 $this->svy_log->debug("References: " . $this->countReferences());
270 if ($this->countReferences() === 1) {
271 $this->deleteMetaData();
272
273 // Delete all survey questions, constraints and materials
274 foreach ($this->questions as $question_id) {
275 $this->svy_log->debug("Remove question " . $question_id);
276 $this->removeQuestion($question_id);
277 }
278 $this->deleteSurveyRecord();
279
281 }
282
283 $this->svy_log->debug("Call parent delete.");
284 $remove = parent::delete();
285
286 // always call parent delete function first!!
287 if (!$remove) {
288 return false;
289 }
290 return true;
291 }
292
297 public function deleteSurveyRecord(): void
298 {
300
301 $ilDB->manipulateF(
302 "DELETE FROM svy_svy WHERE survey_id = %s",
303 array('integer'),
304 array($this->getSurveyId())
305 );
306
307 $result = $ilDB->queryF(
308 "SELECT questionblock_fi FROM svy_qblk_qst WHERE survey_fi = %s",
309 array('integer'),
310 array($this->getSurveyId())
311 );
312 $questionblocks = array();
313 while ($row = $ilDB->fetchAssoc($result)) {
314 $questionblocks[] = $row["questionblock_fi"];
315 }
316 if (count($questionblocks)) {
317 $affectedRows = $ilDB->manipulate("DELETE FROM svy_qblk WHERE " . $ilDB->in('questionblock_id', $questionblocks, false, 'integer'));
318 }
319 $ilDB->manipulateF(
320 "DELETE FROM svy_qblk_qst WHERE survey_fi = %s",
321 array('integer'),
322 array($this->getSurveyId())
323 );
324 $this->deleteAllUserData(false);
325
326 if (isset($this->code_manager)) {
327 $this->code_manager->deleteAll(true);
328 }
329
330 // delete export files
331 $svy_data_dir = ilFileUtils::getDataDir() . "/svy_data";
332 $directory = $svy_data_dir . "/svy_" . $this->getId();
333 if (is_dir($directory)) {
334 ilFileUtils::delDir($directory);
335 }
336
337 $mobs = ilObjMediaObject::_getMobsOfObject("svy:html", $this->getId());
338 // remaining usages are not in text anymore -> delete them
339 // and media objects (note: delete method of ilObjMediaObject
340 // checks whether object is used in another context; if yes,
341 // the object is not deleted!)
342 foreach ($mobs as $mob) {
343 ilObjMediaObject::_removeUsage($mob, "svy:html", $this->getId());
344 $mob_obj = new ilObjMediaObject($mob);
345 $mob_obj->delete();
346 }
347 }
348
354 public function deleteAllUserData(
355 bool $reset_LP = true
356 ): void {
357 $ilDB = $this->db;
358
359 $result = $ilDB->queryF(
360 "SELECT finished_id FROM svy_finished WHERE survey_fi = %s",
361 array('integer'),
362 array($this->getSurveyId())
363 );
364 $active_array = array();
365 while ($row = $ilDB->fetchAssoc($result)) {
366 $active_array[] = $row["finished_id"];
367 }
368
369 $affectedRows = $ilDB->manipulateF(
370 "DELETE FROM svy_finished WHERE survey_fi = %s",
371 array('integer'),
372 array($this->getSurveyId())
373 );
374
375 foreach ($active_array as $active_fi) {
376 $affectedRows = $ilDB->manipulateF(
377 "DELETE FROM svy_answer WHERE active_fi = %s",
378 array('integer'),
379 array($active_fi)
380 );
381 $affectedRows = $ilDB->manipulateF(
382 "DELETE FROM svy_times WHERE finished_fi = %s",
383 array('integer'),
384 array($active_fi)
385 );
386 }
387
388 if ($reset_LP) {
389 $lp_obj = ilObjectLP::getInstance($this->getId());
390 $lp_obj->resetLPDataForCompleteObject();
391 }
392
393 $this->invitation_manager->removeAll($this->getSurveyId());
394 }
395
401 array $finished_ids
402 ): void {
403 $ilDB = $this->db;
404
405 $user_ids = [];
406
407 foreach ($finished_ids as $finished_id) {
408 $result = $ilDB->queryF(
409 "SELECT finished_id, user_fi FROM svy_finished WHERE finished_id = %s",
410 array('integer'),
411 array($finished_id)
412 );
413 $row = $ilDB->fetchAssoc($result);
414 if ($row["user_fi"]) {
415 $user_ids[] = (int) $row["user_fi"];
416 }
417
418 $affectedRows = $ilDB->manipulateF(
419 "DELETE FROM svy_answer WHERE active_fi = %s",
420 array('integer'),
421 array($row["finished_id"])
422 );
423
424 $affectedRows = $ilDB->manipulateF(
425 "DELETE FROM svy_finished WHERE finished_id = %s",
426 array('integer'),
427 array($finished_id)
428 );
429
430 $affectedRows = $ilDB->manipulateF(
431 "DELETE FROM svy_times WHERE finished_fi = %s",
432 array('integer'),
433 array($row["finished_id"])
434 );
435 }
436
437 if (count($user_ids)) {
438 $lp_obj = ilObjectLP::getInstance($this->getId());
439 $lp_obj->resetLPDataForUserIds($user_ids);
440
441 // remove invitations, if exist
442 foreach ($user_ids as $user_id) {
443 $this->invitation_manager->remove($this->getSurveyId(), $user_id);
444 }
445 }
446 }
447
451 public function getSurveyParticipants(
452 ?array $finished_ids = null,
453 bool $force_non_anonymous = false,
454 bool $include_invites = false
455 ): array {
456 $ilDB = $this->db;
457 $sql = "SELECT * FROM svy_finished" .
458 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer");
459 if ($finished_ids) {
460 $sql .= " AND " . $ilDB->in("finished_id", $finished_ids, "", "integer");
461 }
462
463 $result = $ilDB->query($sql);
464 $participants = array();
465 if ($result->numRows() > 0) {
466 while ($row = $ilDB->fetchAssoc($result)) {
467 $userdata = $this->getUserDataFromActiveId($row["finished_id"], $force_non_anonymous);
468 $userdata["finished"] = (bool) $row["state"];
469 $userdata["finished_tstamp"] = $row["tstamp"];
470 $participants[$userdata["sortname"] . $userdata["active_id"]] = $userdata;
471 }
472 }
473 $participant_ids = array_column($participants, "usr_id");
474 if ($include_invites) {
475 foreach ($this->invitation_manager->getAllForSurvey($this->getSurveyId()) as $usr_id) {
476 if (!in_array($usr_id, $participant_ids)) {
477 $name = ilObjUser::_lookupName($usr_id);
478 $participants[$name["lastname"] . "," . $name["firstname"] . $usr_id] = [
479 "fullname" => ilObjUser::_lookupFullname($usr_id),
480 "sortname" => $name["lastname"] . "," . $name["firstname"],
481 "fistname" => $name["firstname"],
482 "lastname" => $name["lastname"],
483 "login" => $name["login"],
484 "gender" => "",
485 "usr_id" => $usr_id,
486 "finished" => false,
487 "finished_tstamp" => 0,
488 "invited" => true
489 ];
490 }
491 }
492 }
493 return $participants;
494 }
495
500 public function isComplete(): bool
501 {
502 return ($this->getTitle() && count($this->questions));
503 }
504
505 public function hasQuestions(): bool
506 {
507 return count($this->questions);
508 }
509
514 public function saveCompletionStatus(): void
515 {
516 $db = $this->db;
517 if ($this->getSurveyId() > 0) {
518 $db->manipulateF(
519 "UPDATE svy_svy SET complete = %s, tstamp = %s WHERE survey_id = %s",
520 array('text','integer','integer'),
521 array($this->isComplete(), time(), $this->getSurveyId())
522 );
523 }
524 }
525
531 int $question_id,
532 bool $a_force = false
533 ): int {
534 $questiontype = $this->getQuestionType($question_id);
535 $question_gui = $this->getQuestionGUI($questiontype, $question_id);
536
537 // check if question is a pool question at all, if not do nothing
538 if ($this->getId() === $question_gui->object->getObjId() && !$a_force) {
539 return $question_id;
540 }
541
542 $duplicate_id = $question_gui->object->duplicate(true, "", "", 0, $this->getId());
543 return $duplicate_id;
544 }
545
550 public function insertQuestion(
551 int $question_id
552 ): bool {
553 $ilDB = $this->db;
554
555 $this->svy_log->debug("insert question, id:" . $question_id);
556 if (!SurveyQuestion::_isComplete($question_id)) {
557 $this->svy_log->debug("question is not complete");
558 return false;
559 } else {
560 // get maximum sequence index in test
561 $result = $ilDB->queryF(
562 "SELECT survey_question_id FROM svy_svy_qst WHERE survey_fi = %s",
563 array('integer'),
564 array($this->getSurveyId())
565 );
566 $sequence = $result->numRows();
567 $duplicate_id = $this->duplicateQuestionForSurvey($question_id);
568 $this->svy_log->debug("duplicate, id: " . $question_id . ", duplicate id: " . $duplicate_id);
569
570 // check if question is not already in the survey, see #22018
571 if ($this->isQuestionInSurvey($duplicate_id)) {
572 return false;
573 }
574
575 $next_id = $ilDB->nextId('svy_svy_qst');
576 $affectedRows = $ilDB->manipulateF(
577 "INSERT INTO svy_svy_qst (survey_question_id, survey_fi, question_fi, sequence, tstamp) VALUES (%s, %s, %s, %s, %s)",
578 array('integer', 'integer', 'integer', 'integer', 'integer'),
579 array($next_id, $this->getSurveyId(), $duplicate_id, $sequence, time())
580 );
581
582 $this->svy_log->debug("added entry to svy_svy_qst, id: " . $next_id . ", question id: " . $duplicate_id . ", sequence: " . $sequence);
583
584 $this->loadQuestionsFromDb();
585 $this->saveCompletionStatus();
586 return true;
587 }
588 }
589
590 // Check if a question is already in the survey
591 public function isQuestionInSurvey(
592 int $a_question_fi
593 ): bool {
594 global $DIC;
595 //return false;
596 $ilDB = $DIC->database();
597
598 $set = $ilDB->query("SELECT * FROM svy_svy_qst " .
599 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") .
600 " AND question_fi = " . $ilDB->quote($a_question_fi, "integer"));
601 if ($rec = $ilDB->fetchAssoc($set)) {
602 return true;
603 }
604 return false;
605 }
606
607 // Inserts a questionblock in the survey
608 public function insertQuestionblock(
609 int $questionblock_id
610 ): void {
611
612 $sequence_manager = $this->survey_service->domain()->sequence(
613 $this->getSurveyId(),
614 $this
615 );
616
617 $ilDB = $this->db;
618 $result = $ilDB->queryF(
619 "SELECT svy_qblk.title, svy_qblk.show_questiontext, svy_qblk.show_blocktitle," .
620 " svy_qblk_qst.question_fi FROM svy_qblk, svy_qblk_qst, svy_svy_qst" .
621 " WHERE svy_qblk.questionblock_id = svy_qblk_qst.questionblock_fi" .
622 " AND svy_svy_qst.question_fi = svy_qblk_qst.question_fi" .
623 " AND svy_qblk.questionblock_id = %s" .
624 " ORDER BY svy_svy_qst.sequence",
625 array('integer'),
626 array($questionblock_id)
627 );
628 $questions = array();
629 $show_questiontext = false;
630 $show_blocktitle = false;
631 $title = "";
632 while ($row = $ilDB->fetchAssoc($result)) {
633 //$duplicate_id = $this->duplicateQuestionForSurvey($row["question_fi"]);
634 $duplicate_id = $sequence_manager->appendQuestion($row["question_fi"], true);
635 $questions[] = $duplicate_id;
636 $title = (string) $row["title"];
637 $show_questiontext = (bool) $row["show_questiontext"];
638 $show_blocktitle = (bool) $row["show_blocktitle"];
639 }
640 $this->createQuestionblock($title, $show_questiontext, $show_blocktitle, $questions);
641 }
642
643 // seems to be only be used for code mails
644 public function saveUserSettings(
645 int $usr_id,
646 string $key,
647 string $title,
648 string $value
649 ): void {
650 $ilDB = $this->db;
651
652 $next_id = $ilDB->nextId('svy_settings');
653 $affectedRows = $ilDB->insert("svy_settings", array(
654 "settings_id" => array("integer", $next_id),
655 "usr_id" => array("integer", $usr_id),
656 "keyword" => array("text", $key),
657 "title" => array("text", $title),
658 "value" => array("clob", $value)
659 ));
660 }
661
662 public function deleteUserSettings(
663 int $id
664 ): void {
665 $ilDB = $this->db;
666
667 $ilDB->manipulateF(
668 "DELETE FROM svy_settings WHERE settings_id = %s",
669 array('integer'),
670 array($id)
671 );
672 }
673
674 public function getUserSettings(
675 int $usr_id,
676 string $key
677 ): array {
678 $ilDB = $this->db;
679
680 $result = $ilDB->queryF(
681 "SELECT * FROM svy_settings WHERE usr_id = %s AND keyword = %s",
682 array('integer', 'text'),
683 array($usr_id, $key)
684 );
685 $found = array();
686 if ($result->numRows()) {
687 while ($row = $ilDB->fetchAssoc($result)) {
688 $found[$row['settings_id']] = $row;
689 }
690 }
691 return $found;
692 }
693
694 // Saves a survey object to a database
695 public function saveToDb(): void
696 {
697 $ilDB = $this->db;
698
699 // date handling
700 $rmd_start = $this->getReminderStart();
701 if (is_object($rmd_start)) {
702 $rmd_start = $rmd_start->get(IL_CAL_DATE);
703 }
704 $rmd_end = $this->getReminderEnd();
705 if (is_object($rmd_end)) {
706 $rmd_end = $rmd_end->get(IL_CAL_DATE);
707 }
708 if ($this->getSurveyId() < 1) {
709 $next_id = $ilDB->nextId('svy_svy');
710 $affectedRows = $ilDB->insert("svy_svy", array(
711 "survey_id" => array("integer", $next_id),
712 "obj_fi" => array("integer", $this->getId()),
713 "author" => array("text", $this->getAuthor()),
714 "introduction" => array("clob", ilRTE::_replaceMediaObjectImageSrc($this->getIntroduction(), 0)),
715 "outro" => array("clob", ilRTE::_replaceMediaObjectImageSrc($this->getOutro(), 0)),
716 "startdate" => array("text", $this->getStartDate()),
717 "enddate" => array("text", $this->getEndDate()),
718 "evaluation_access" => array("text", $this->getEvaluationAccess()),
719 "complete" => array("text", $this->isComplete()),
720 "created" => array("integer", time()),
721 "anonymize" => array("text", $this->getAnonymize()),
722 "show_question_titles" => array("text", $this->getShowQuestionTitles()),
723 "mailnotification" => array('integer', ($this->getMailNotification()) ? 1 : 0),
724 "mailaddresses" => array('text', $this->getMailAddresses()),
725 "mailparticipantdata" => array('text', $this->getMailParticipantData()),
726 "tstamp" => array("integer", time()),
727 // Mode type
728 "mode" => array("integer", $this->getMode()),
729 // 360°
730 "mode_360_self_eval" => array("integer", $this->get360SelfEvaluation()),
731 "mode_360_self_rate" => array("integer", $this->get360SelfRaters()),
732 "mode_360_self_appr" => array("integer", $this->get360SelfAppraisee()),
733 "mode_360_results" => array("integer", $this->get360Results()),
734 // competences
735 "mode_skill_service" => array("integer", (int) $this->getSkillService()),
736 // Self Evaluation Only
737 "mode_self_eval_results" => array("integer", self::RESULTS_SELF_EVAL_OWN),
738 // reminder/notification
739 "reminder_status" => array("integer", (int) $this->getReminderStatus()),
740 "reminder_start" => array("datetime", $rmd_start),
741 "reminder_end" => array("datetime", $rmd_end),
742 "reminder_frequency" => array("integer", $this->getReminderFrequency()),
743 "reminder_target" => array("integer", $this->getReminderTarget()),
744 "reminder_last_sent" => array("datetime", $this->getReminderLastSent()),
745 "reminder_tmpl" => array("text", $this->getReminderTemplate(true)),
746 "tutor_ntf_status" => array("integer", (int) $this->getTutorNotificationStatus()),
747 "tutor_ntf_reci" => array("text", implode(";", $this->getTutorNotificationRecipients())),
748 "tutor_ntf_target" => array("integer", $this->getTutorNotificationTarget()),
749 "own_results_view" => array("integer", $this->hasViewOwnResults()),
750 "own_results_mail" => array("integer", $this->hasMailOwnResults()),
751 "tutor_res_status" => array("integer", (int) $this->getTutorResultsStatus()),
752 "tutor_res_reci" => array("text", implode(";", $this->getTutorResultsRecipients())),
753 "confirmation_mail" => array("integer", $this->hasMailConfirmation()),
754 "anon_user_list" => array("integer", $this->hasAnonymousUserList()),
755 "calculate_sum_score" => array("integer", $this->getCalculateSumScore())
756 ));
757 $this->setSurveyId($next_id);
758 } else {
759 $affectedRows = $ilDB->update("svy_svy", array(
760 "author" => array("text", ilStr::subStr($this->getAuthor(), 0, 50)),
761 "introduction" => array("clob", ilRTE::_replaceMediaObjectImageSrc($this->getIntroduction(), 0)),
762 "outro" => array("clob", ilRTE::_replaceMediaObjectImageSrc($this->getOutro(), 0)),
763 "startdate" => array("text", $this->getStartDate()),
764 "enddate" => array("text", $this->getEndDate()),
765 "evaluation_access" => array("text", $this->getEvaluationAccess()),
766 "complete" => array("text", $this->isComplete()),
767 "anonymize" => array("text", $this->getAnonymize()),
768 "show_question_titles" => array("text", $this->getShowQuestionTitles()),
769 "mailnotification" => array('integer', ($this->getMailNotification()) ? 1 : 0),
770 "mailaddresses" => array('text', $this->getMailAddresses()),
771 "mailparticipantdata" => array('text', $this->getMailParticipantData()),
772 "tstamp" => array("integer", time()),
773 //MODE TYPE
774 "mode" => array("integer", $this->getMode()),
775 // 360°
776 "mode_360_self_eval" => array("integer", $this->get360SelfEvaluation()),
777 "mode_360_self_rate" => array("integer", $this->get360SelfRaters()),
778 "mode_360_self_appr" => array("integer", $this->get360SelfAppraisee()),
779 "mode_360_results" => array("integer", $this->get360Results()),
780 // Competences
781 "mode_skill_service" => array("integer", (int) $this->getSkillService()),
782 // Self Evaluation Only
783 "mode_self_eval_results" => array("integer", $this->getSelfEvaluationResults()),
784 // reminder/notification
785 "reminder_status" => array("integer", $this->getReminderStatus()),
786 "reminder_start" => array("datetime", $rmd_start),
787 "reminder_end" => array("datetime", $rmd_end),
788 "reminder_frequency" => array("integer", $this->getReminderFrequency()),
789 "reminder_target" => array("integer", $this->getReminderTarget()),
790 "reminder_last_sent" => array("datetime", $this->getReminderLastSent()),
791 "reminder_tmpl" => array("text", $this->getReminderTemplate()),
792 "tutor_ntf_status" => array("integer", $this->getTutorNotificationStatus()),
793 "tutor_ntf_reci" => array("text", implode(";", $this->getTutorNotificationRecipients())),
794 "tutor_ntf_target" => array("integer", $this->getTutorNotificationTarget()),
795 "own_results_view" => array("integer", $this->hasViewOwnResults()),
796 "own_results_mail" => array("integer", $this->hasMailOwnResults()),
797 "tutor_res_status" => array("integer", (int) $this->getTutorResultsStatus()),
798 "tutor_res_reci" => array("text", implode(";", $this->getTutorResultsRecipients())),
799 "confirmation_mail" => array("integer", $this->hasMailConfirmation()),
800 "anon_user_list" => array("integer", $this->hasAnonymousUserList()),
801 "calculate_sum_score" => array("integer", $this->getCalculateSumScore())
802 ), array(
803 "survey_id" => array("integer", $this->getSurveyId())
804 ));
805 }
806 if ($affectedRows) {
807 // save questions to db
808 $this->saveQuestionsToDb();
809 }
810
811 // moved activation to ilObjectActivation
812 if ($this->ref_id) {
813 ilObjectActivation::getItem($this->ref_id);
814
815 $item = new ilObjectActivation();
816 if (!$this->isActivationLimited()) {
817 $item->setTimingType(ilObjectActivation::TIMINGS_DEACTIVATED);
818 } else {
819 $item->setTimingType(ilObjectActivation::TIMINGS_ACTIVATION);
820 $item->setTimingStart($this->getActivationStartDate());
821 $item->setTimingEnd($this->getActivationEndDate());
822 $item->toggleVisible($this->getActivationVisibility());
823 }
824
825 $item->update($this->ref_id);
826 }
827 }
828
829 // Saves the survey questions to db
830 public function saveQuestionsToDb(): void
831 {
832 $ilDB = $this->db;
833
834 $this->svy_log->debug("save questions");
835
836 // gather old questions state
837 $old_questions = array();
838 $result = $ilDB->queryF(
839 "SELECT survey_question_id,question_fi,sequence" .
840 " FROM svy_svy_qst WHERE survey_fi = %s",
841 array('integer'),
842 array($this->getSurveyId())
843 );
844 while ($row = $ilDB->fetchAssoc($result)) {
845 $old_questions[$row["question_fi"]] = $row; // problem, as soon as duplicates exist, they will be hidden here
846 }
847
848 // #15231 - diff with current questions state
849 $insert = $update = $delete = array();
850 foreach ($this->questions as $seq => $fi) {
851 if (!array_key_exists($fi, $old_questions)) { // really new fi IDs
852 $insert[] = $fi; // this should be ok, should not create duplicates here
853 } elseif ($old_questions[$fi]["sequence"] != $seq) { // we are updating one of the duplicates (if any)
854 $update[$fi] = $old_questions[$fi]["survey_question_id"];
855 }
856 // keep track of still relevant questions
857 unset($old_questions[$fi]); // deleting old question, if they are not in current array
858 }
859
860 // delete obsolete question relations
861 if (count($old_questions)) {
862 $del_ids = array();
863 foreach ($old_questions as $fi => $old) {
864 $del_ids[] = $old["survey_question_id"];
865 }
866 $ilDB->manipulate($q = "DELETE FROM svy_svy_qst" .
867 " WHERE " . $ilDB->in("survey_question_id", $del_ids, "", "integer"));
868 $this->svy_log->debug("delete: " . $q);
869 }
870 unset($old_questions);
871
872 // create/update question relations
873 foreach ($this->questions as $seq => $fi) {
874 if (in_array($fi, $insert)) {
875 // check if question is not already in the survey, see #22018
876 if (!$this->isQuestionInSurvey($fi)) {
877 $next_id = $ilDB->nextId('svy_svy_qst');
878 $ilDB->manipulateF(
879 "INSERT INTO svy_svy_qst" .
880 " (survey_question_id, survey_fi, question_fi, heading, sequence, tstamp)" .
881 " VALUES (%s, %s, %s, %s, %s, %s)",
882 array('integer', 'integer', 'integer', 'text', 'integer', 'integer'),
883 array($next_id, $this->getSurveyId(), $fi, null, $seq, time())
884 );
885 $this->svy_log->debug("insert svy_svy_qst, id:" . $next_id . ", fi: " . $fi . ", seq:" . $seq);
886 }
887 } elseif (array_key_exists($fi, $update)) {
888 $ilDB->manipulate("UPDATE svy_svy_qst" .
889 " SET sequence = " . $ilDB->quote($seq, "integer") .
890 ", tstamp = " . $ilDB->quote(time(), "integer") .
891 " WHERE survey_question_id = " . $ilDB->quote($update[$fi], "integer"));
892 $this->svy_log->debug("update svy_svy_qst, id:" . $update[$fi] . ", fi: " . $fi . ", seq:" . $seq);
893 }
894 }
895 }
896
897 // Returns a question gui object to a given questiontype and question id
898 public function getQuestionGUI(
899 string $questiontype,
900 int $question_id
902 return SurveyQuestionGUI::_getQuestionGUI($questiontype, $question_id);
903 }
904
905 // Returns the question type of a question with a given id
906 public function getQuestionType(
907 int $question_id
908 ): string {
909 $ilDB = $this->db;
910 if ($question_id < 1) {
911 return -1;
912 }
913 $result = $ilDB->queryF(
914 "SELECT type_tag FROM svy_question, svy_qtype WHERE svy_question.question_id = %s AND " .
915 "svy_question.questiontype_fi = svy_qtype.questiontype_id",
916 array('integer'),
917 array($question_id)
918 );
919 if ($result->numRows() === 1) {
920 $data = $ilDB->fetchAssoc($result);
921 return $data["type_tag"];
922 } else {
923 return "";
924 }
925 }
926
927 public function getSurveyId(): int
928 {
929 return $this->survey_id;
930 }
931
935 public function setAnonymize(int $a_anonymize): void
936 {
937 switch ($a_anonymize) {
938 case self::ANONYMIZE_OFF:
939 case self::ANONYMIZE_ON:
940 case self::ANONYMIZE_FREEACCESS:
941 case self::ANONYMIZE_CODE_ALL:
942 $this->anonymize = $a_anonymize;
943 break;
944 default:
945 $this->anonymize = self::ANONYMIZE_OFF;
946 break;
947 }
948 }
949
950 public function getAnonymize(): int
951 {
952 return $this->anonymize;
953 }
954
955 public function setCalculateSumScore(
956 bool $a_val
957 ): void {
958 $this->calculate_sum_score = $a_val;
959 }
960
961 public function getCalculateSumScore(): bool
962 {
963 return $this->calculate_sum_score;
964 }
965
966 // Checks if the survey is accessible without a survey code
967 public function isAccessibleWithoutCode(): bool
968 {
969 return ($this->getAnonymize() === self::ANONYMIZE_OFF ||
970 $this->getAnonymize() === self::ANONYMIZE_FREEACCESS);
971 }
972
973 // Checks if the survey results are to be anonymized
974 public function hasAnonymizedResults(): bool
975 {
976 return ($this->getAnonymize() === self::ANONYMIZE_ON ||
977 $this->getAnonymize() === self::ANONYMIZE_FREEACCESS);
978 }
979
980 public function loadFromDb(): void
981 {
982 $ilDB = $this->db;
983 $result = $ilDB->queryF(
984 "SELECT * FROM svy_svy WHERE obj_fi = %s",
985 array('integer'),
986 array($this->getId())
987 );
988 if ($result->numRows() === 1) {
989 $data = $ilDB->fetchAssoc($result);
990 $this->setSurveyId($data["survey_id"]);
991 $this->setAuthor($data["author"] ?? "");
992 $this->setIntroduction(ilRTE::_replaceMediaObjectImageSrc((string) $data["introduction"], 1));
993 if ($data["outro"] === "survey_finished") {
994 $this->setOutro($this->lng->txt("survey_finished"));
995 } else {
996 $this->setOutro(ilRTE::_replaceMediaObjectImageSrc((string) $data["outro"], 1));
997 }
998 $this->setShowQuestionTitles((bool) $data["show_question_titles"]);
999 $this->setStartDate((string) ($data["startdate"] ?? ""));
1000 $this->setEndDate((string) ($data["enddate"] ?? ""));
1001 $this->setAnonymize((int) $data["anonymize"]);
1002 $this->setEvaluationAccess($data["evaluation_access"] ?? "");
1003 $this->loadQuestionsFromDb();
1004 $this->setMailNotification((bool) $data['mailnotification']);
1005 $this->setMailAddresses((string) $data['mailaddresses']);
1006 $this->setMailParticipantData((string) $data['mailparticipantdata']);
1007 $this->setPoolUsage((bool) $data['pool_usage']);
1008 // Mode
1009 $this->setMode($data['mode']);
1010 // 360°
1011 $this->set360SelfEvaluation((bool) $data['mode_360_self_eval']);
1012 $this->set360SelfRaters((bool) $data['mode_360_self_rate']);
1013 $this->set360SelfAppraisee((bool) $data['mode_360_self_appr']);
1014 $this->set360Results((int) $data['mode_360_results']);
1015 // Mode self evaluated
1016 $this->setSelfEvaluationResults((int) $data['mode_self_eval_results']);
1017 // Competences
1018 $this->setSkillService((bool) $data['mode_skill_service']);
1019 // reminder/notification
1020 $this->setReminderStatus((bool) $data["reminder_status"]);
1021 $this->setReminderStart($data["reminder_start"] ? new ilDate($data["reminder_start"], IL_CAL_DATE) : null);
1022 $this->setReminderEnd($data["reminder_end"] ? new ilDate($data["reminder_end"], IL_CAL_DATE) : null);
1023 $this->setReminderFrequency((int) $data["reminder_frequency"]);
1024 $this->setReminderTarget((int) $data["reminder_target"]);
1025 $this->setReminderLastSent((string) $data["reminder_last_sent"]);
1026 $this->setReminderTemplate((int) $data["reminder_tmpl"]);
1027 $this->setTutorNotificationStatus($data["tutor_ntf_status"]);
1028 $this->setTutorNotificationRecipients(explode(";", $data["tutor_ntf_reci"] ?? ""));
1029 $this->setTutorNotificationTarget($data["tutor_ntf_target"]);
1030 $this->setTutorResultsStatus((bool) $data["tutor_res_status"]);
1031 $this->setTutorResultsRecipients(explode(";", $data["tutor_res_reci"] ?? ""));
1032
1033 $this->setMailOwnResults((bool) $data["own_results_mail"]);
1034 $this->setMailConfirmation((bool) $data["confirmation_mail"]);
1035 $this->setCalculateSumScore((bool) $data["calculate_sum_score"]);
1036
1037 $this->setAnonymousUserList((bool) $data["anon_user_list"]);
1038 }
1039
1040 // moved activation to ilObjectActivation
1041 if (isset($this->ref_id) && $this->ref_id !== 0) {
1042 $activation = ilObjectActivation::getItem($this->ref_id);
1043 switch ($activation["timing_type"]) {
1045 $this->setActivationLimited(true);
1046 $this->setActivationStartDate($activation["timing_start"]);
1047 $this->setActivationEndDate($activation["timing_end"]);
1048 $this->setActivationVisibility($activation["visible"]);
1049 break;
1050
1051 default:
1052 $this->setActivationLimited(false);
1053 break;
1054 }
1055 }
1056 }
1057
1058 public function loadQuestionsFromDb(): void
1059 {
1060 $ilDB = $this->db;
1061 $this->questions = array();
1062 $result = $ilDB->queryF(
1063 "SELECT * FROM svy_svy_qst WHERE survey_fi = %s ORDER BY sequence",
1064 array('integer'),
1065 array($this->getSurveyId())
1066 );
1067 while ($data = $ilDB->fetchAssoc($result)) {
1068 $this->questions[$data["sequence"]] = $data["question_fi"];
1069 }
1070 }
1071
1072 // Remove duplicate sequence entries, see #22018
1073 public function fixSequenceStructure(): void
1074 {
1075 global $DIC;
1076
1077 $ilDB = $DIC->database();
1078 //return;
1079 // we keep all survey question ids with their lowest sequence
1080 $result = $ilDB->queryF(
1081 "SELECT * FROM svy_svy_qst WHERE survey_fi = %s ORDER BY sequence",
1082 array('integer'),
1083 array($this->getSurveyId())
1084 );
1085
1086 // step 1: find duplicates -> $to_delete_ids
1087 $fis = array();
1088 $to_delete_ids = array();
1089 while ($data = $ilDB->fetchAssoc($result)) {
1090 if (in_array($data["question_fi"], $fis)) { // found a duplicate
1091 $to_delete_ids[] = $data["survey_question_id"];
1092 } else {
1093 $fis[] = $data["question_fi"];
1094 }
1095 }
1096
1097 // step 2: we delete the duplicates
1098 if (count($to_delete_ids) > 0) {
1099 $ilDB->manipulate($q = "DELETE FROM svy_svy_qst" .
1100 " WHERE " . $ilDB->in("survey_question_id", $to_delete_ids, false, "integer") .
1101 " AND survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer"));
1102 $this->svy_log->debug("delete: " . $q);
1103
1104 $ilDB->manipulate($q = "DELETE FROM svy_qblk_qst " .
1105 " WHERE " . $ilDB->in("question_fi", $fis, true, "integer") .
1106 " AND survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer"));
1107 $this->svy_log->debug("delete: " . $q);
1108 }
1109
1110 // step 3: we fix the sequence
1111 $set = $ilDB->query("SELECT * FROM svy_svy_qst " .
1112 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") . " ORDER BY sequence");
1113 $seq = 0;
1114 while ($rec = $ilDB->fetchAssoc($set)) {
1115 $ilDB->manipulate(
1116 $q = "UPDATE svy_svy_qst SET " .
1117 " sequence = " . $ilDB->quote($seq++, "integer") .
1118 " WHERE survey_question_id = " . $ilDB->quote($rec["survey_question_id"], "integer")
1119 );
1120 $this->svy_log->debug("update: " . $q);
1121 }
1122 }
1123
1124 public function setAuthor(
1125 string $author = ""
1126 ): void {
1127 $this->author = $author;
1128 }
1129
1135 public function saveAuthorToMetadata(
1136 string $a_author = ""
1137 ): void {
1138 if ($a_author === '') {
1139 $ilUser = $this->user;
1140 $a_author = $ilUser->getFullname();
1141 }
1142 $this->domain->metadata()->saveAuthorsInLOMIfNoLifecycleSet(
1143 $this->getId(),
1144 0,
1145 $this->getType(),
1146 $a_author,
1147 );
1148 }
1149
1150 // Gets the authors name from metadata
1151 public function getAuthor(): string
1152 {
1153 return $this->domain->metadata()->getAuthorsFromLOM(
1154 $this->getId(),
1155 0,
1156 $this->getType()
1157 );
1158 }
1159
1160 public function getShowQuestionTitles(): bool
1161 {
1162 return (bool) $this->display_question_titles;
1163 }
1164
1165 public function setShowQuestionTitles(bool $a_show): void
1166 {
1167 $this->display_question_titles = $a_show;
1168 }
1169
1170 public function setIntroduction(
1171 string $introduction = ""
1172 ): void {
1173 $this->introduction = $introduction;
1174 }
1175
1176 public function setOutro(
1177 string $outro = ""
1178 ): void {
1179 $this->outro = $outro;
1180 }
1181
1182 public function getStartDate(): string
1183 {
1184 return $this->start_date;
1185 }
1186
1190 public function setStartDate(
1191 string $start_date = ""
1192 ): void {
1193 $this->start_date = $start_date;
1194 }
1195
1200 public function setStartDateAndTime(
1201 string $start_date,
1202 string $start_time
1203 ): void {
1204 $y = '';
1205 $m = '';
1206 $d = '';
1207 $h = '';
1208 $i = '';
1209 $s = '';
1210 if (preg_match("/(\d{4})-(\d{2})-(\d{2})/", $start_date, $matches)) {
1211 $y = $matches[1];
1212 $m = $matches[2];
1213 $d = $matches[3];
1214 }
1215 if (preg_match("/(\d{2}):(\d{2}):(\d{2})/", $start_time, $matches)) {
1216 $h = $matches[1];
1217 $i = $matches[2];
1218 $s = $matches[3];
1219 }
1220 $this->start_date = sprintf('%04d%02d%02d%02d%02d%02d', $y, $m, $d, $h, $i, $s);
1221 }
1222
1223 public function getEndDate(): string
1224 {
1225 return $this->end_date;
1226 }
1227
1231 public function setEndDate(
1232 string $end_date = ""
1233 ): void {
1234 $this->end_date = $end_date;
1235 }
1236
1237 public function hasStarted(): bool
1238 {
1239 $start = $this->getStartDate();
1240 if ($start) {
1241 $start_date = new ilDateTime($start, IL_CAL_TIMESTAMP);
1242 return ($start_date->get(IL_CAL_UNIX) < time());
1243 }
1244 return true;
1245 }
1246
1247 public function hasEnded(): bool
1248 {
1249 $end = $this->getEndDate();
1250 if ($end) {
1251 $end_date = new ilDateTime($end, IL_CAL_TIMESTAMP);
1252 return ($end_date->get(IL_CAL_UNIX) < time());
1253 }
1254 return false;
1255 }
1256
1261 public function setEndDateAndTime(
1262 string $end_date,
1263 string $end_time
1264 ): void {
1265 $y = '';
1266 $m = '';
1267 $d = '';
1268 $h = '';
1269 $i = '';
1270 $s = '';
1271 if (preg_match("/(\d{4})-(\d{2})-(\d{2})/", $end_date, $matches)) {
1272 $y = $matches[1];
1273 $m = $matches[2];
1274 $d = $matches[3];
1275 }
1276 if (preg_match("/(\d{2}):(\d{2}):(\d{2})/", $end_time, $matches)) {
1277 $h = $matches[1];
1278 $i = $matches[2];
1279 $s = $matches[3];
1280 }
1281 $this->end_date = sprintf('%04d%02d%02d%02d%02d%02d', $y, $m, $d, $h, $i, $s);
1282 }
1283
1284 // Gets the learners evaluation access
1285 public function getEvaluationAccess(): string
1286 {
1287 return $this->evaluation_access;
1288 }
1289
1290 public function setEvaluationAccess(
1291 string $evaluation_access = self::EVALUATION_ACCESS_OFF
1292 ): void {
1293 $this->evaluation_access = $evaluation_access;
1294 }
1295
1297 bool $a_value
1298 ): void {
1299 $this->activation_visibility = $a_value;
1300 }
1301
1302 public function getActivationVisibility(): bool
1303 {
1304 return $this->activation_visibility;
1305 }
1306
1307 public function isActivationLimited(): bool
1308 {
1309 return $this->activation_limited;
1310 }
1311
1312 public function setActivationLimited(bool $a_value): void
1313 {
1314 $this->activation_limited = $a_value;
1315 }
1316
1317 public function getIntroduction(): string
1318 {
1319 return $this->introduction;
1320 }
1321
1322 public function getOutro(): string
1323 {
1324 return $this->outro;
1325 }
1326
1331 public function getExistingQuestions(): array
1332 {
1333 $ilDB = $this->db;
1334 $existing_questions = array();
1335 $result = $ilDB->queryF(
1336 "SELECT svy_question.original_id FROM svy_question, svy_svy_qst WHERE " .
1337 "svy_svy_qst.survey_fi = %s AND svy_svy_qst.question_fi = svy_question.question_id",
1338 array('integer'),
1339 array($this->getSurveyId())
1340 );
1341 while ($data = $ilDB->fetchAssoc($result)) {
1342 if ($data["original_id"]) {
1343 $existing_questions[] = (int) $data["original_id"];
1344 }
1345 }
1346 return $existing_questions;
1347 }
1348
1353 public function getQuestionpoolTitles(
1354 bool $could_be_offline = false,
1355 bool $showPath = false
1356 ): array {
1357 return ilObjSurveyQuestionPool::_getAvailableQuestionpools(true, $could_be_offline, $showPath);
1358 }
1359
1367 public function moveQuestions(
1368 array $move_questions,
1369 int $target_index,
1370 int $insert_mode
1371 ): void {
1372 $array_pos = array_search($target_index, $this->questions);
1373 $part1 = $part2 = [];
1374 if ($insert_mode === 0) {
1375 $part1 = array_slice($this->questions, 0, $array_pos);
1376 $part2 = array_slice($this->questions, $array_pos);
1377 } elseif ($insert_mode === 1) {
1378 $part1 = array_slice($this->questions, 0, $array_pos + 1);
1379 $part2 = array_slice($this->questions, $array_pos + 1);
1380 }
1381 $found = 0;
1382 foreach ($move_questions as $question_id) {
1383 if (!(!in_array($question_id, $part1))) {
1384 unset($part1[array_search($question_id, $part1)]);
1385 $found++;
1386 }
1387 if (!(!in_array($question_id, $part2))) {
1388 unset($part2[array_search($question_id, $part2)]);
1389 $found++;
1390 }
1391 }
1392 // sanity check: do not move questions if they have not be found in the array
1393 if ($found !== count($move_questions)) {
1394 return;
1395 }
1396 $part1 = array_values($part1);
1397 $part2 = array_values($part2);
1398 $this->questions = array_values(array_merge($part1, $move_questions, $part2));
1399 foreach ($move_questions as $question_id) {
1400 $constraints = $this->getConstraints($question_id);
1401 foreach ($constraints as $idx => $constraint) {
1402 foreach ($part2 as $next_question_id) {
1403 if ($constraint["question"] == $next_question_id) {
1404 // constraint concerning a question that follows -> delete constraint
1405 $this->deleteConstraint($constraint["id"]);
1406 }
1407 }
1408 }
1409 }
1410 $this->saveQuestionsToDb();
1411 }
1412
1416 public function removeQuestion(
1417 int $question_id
1418 ): void {
1419 $question = self::_instanciateQuestion($question_id);
1420 #20610 if no question found, do nothing.
1421 if ($question) {
1422 $question->delete($question_id);
1423 $this->removeConstraintsConcerningQuestion($question_id);
1424 }
1425 }
1426
1432 int $question_id
1433 ): void {
1434 $ilDB = $this->db;
1435 $result = $ilDB->queryF(
1436 "SELECT constraint_fi FROM svy_qst_constraint WHERE question_fi = %s AND survey_fi = %s",
1437 array('integer','integer'),
1438 array($question_id, $this->getSurveyId())
1439 );
1440 if ($result->numRows() > 0) {
1441 $remove_constraints = array();
1442 while ($row = $ilDB->fetchAssoc($result)) {
1443 $remove_constraints[] = $row["constraint_fi"];
1444 }
1445 $affectedRows = $ilDB->manipulateF(
1446 "DELETE FROM svy_qst_constraint WHERE question_fi = %s AND survey_fi = %s",
1447 array('integer','integer'),
1448 array($question_id, $this->getSurveyId())
1449 );
1450 foreach ($remove_constraints as $key => $constraint_id) {
1451 $affectedRows = $ilDB->manipulateF(
1452 "DELETE FROM svy_constraint WHERE constraint_id = %s",
1453 array('integer'),
1454 array($constraint_id)
1455 );
1456 }
1457 }
1458 }
1459
1465 public function removeQuestions(
1466 array $remove_questions,
1467 array $remove_questionblocks
1468 ): void {
1469 $ilDB = $this->db;
1470
1471 $block_sizes = array();
1472 foreach ($this->getSurveyQuestions() as $question_id => $data) {
1473 if (in_array($question_id, $remove_questions) or in_array($data["questionblock_id"], $remove_questionblocks)) {
1474 unset($this->questions[array_search($question_id, $this->questions)]);
1475 $this->removeQuestion($question_id);
1476 } elseif ($data["questionblock_id"]) {
1477 $block_sizes[$data["questionblock_id"]] = ($block_sizes[$data["questionblock_id"]] ?? 0) + 1;
1478 }
1479 }
1480
1481 // blocks with just 1 question need to be deleted
1482 foreach ($block_sizes as $block_id => $size) {
1483 if ($size < 2) {
1484 $remove_questionblocks[] = $block_id;
1485 }
1486 }
1487
1488 foreach (array_unique($remove_questionblocks) as $questionblock_id) {
1489 $affectedRows = $ilDB->manipulateF(
1490 "DELETE FROM svy_qblk WHERE questionblock_id = %s",
1491 array('integer'),
1492 array($questionblock_id)
1493 );
1494 $affectedRows = $ilDB->manipulateF(
1495 "DELETE FROM svy_qblk_qst WHERE questionblock_fi = %s AND survey_fi = %s",
1496 array('integer','integer'),
1497 array($questionblock_id, $this->getSurveyId())
1498 );
1499 }
1500
1501 $this->questions = array_values($this->questions);
1502 $this->saveQuestionsToDb();
1503 }
1504
1509 public function unfoldQuestionblocks(
1510 array $questionblocks
1511 ): void {
1512 $ilDB = $this->db;
1513 foreach ($questionblocks as $index) {
1514 $affectedRows = $ilDB->manipulateF(
1515 "DELETE FROM svy_qblk WHERE questionblock_id = %s",
1516 array('integer'),
1517 array($index)
1518 );
1519 $affectedRows = $ilDB->manipulateF(
1520 "DELETE FROM svy_qblk_qst WHERE questionblock_fi = %s AND survey_fi = %s",
1521 array('integer','integer'),
1522 array($index, $this->getSurveyId())
1523 );
1524 }
1525 }
1526
1531 int $question_id,
1532 int $questionblock_id
1533 ): void {
1534 $ilDB = $this->db;
1535
1536 $ilDB->manipulateF(
1537 "DELETE FROM svy_qblk_qst WHERE questionblock_fi = %s AND survey_fi = %s AND question_fi = %s",
1538 array('integer','integer','integer'),
1539 array($questionblock_id, $this->getSurveyId(), $question_id)
1540 );
1541 }
1542
1546 public function addQuestionToBlock(
1547 int $question_id,
1548 int $questionblock_id
1549 ): void {
1550 $ilDB = $this->db;
1551
1552 // see #22018
1553 if (!$this->isQuestionInAnyBlock($question_id)) {
1554 $next_id = $ilDB->nextId('svy_qblk_qst');
1555 $affectedRows = $ilDB->manipulateF(
1556 "INSERT INTO svy_qblk_qst (qblk_qst_id, survey_fi, questionblock_fi, " .
1557 "question_fi) VALUES (%s, %s, %s, %s)",
1558 array('integer', 'integer', 'integer', 'integer'),
1559 array($next_id, $this->getSurveyId(), $questionblock_id, $question_id)
1560 );
1561
1562 $this->deleteConstraints($question_id); // #13713
1563 }
1564 }
1565
1569 public function isQuestionInAnyBlock(
1570 int $a_question_fi
1571 ): bool {
1572 global $DIC;
1573
1574 $ilDB = $DIC->database();
1575
1576 $set = $ilDB->query("SELECT * FROM svy_qblk_qst " .
1577 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") .
1578 " AND question_fi = " . $ilDB->quote($a_question_fi, "integer"));
1579 if ($rec = $ilDB->fetchAssoc($set)) {
1580 return true;
1581 }
1582 return false;
1583 }
1584
1585
1592 int $questionblock_id
1593 ): array {
1594 $ilDB = $this->db;
1595 $titles = array();
1596 $result = $ilDB->queryF(
1597 "SELECT svy_question.title, svy_qblk_qst.question_fi, svy_qblk_qst.survey_fi FROM " .
1598 "svy_qblk, svy_qblk_qst, svy_question WHERE svy_qblk.questionblock_id = svy_qblk_qst.questionblock_fi AND " .
1599 "svy_question.question_id = svy_qblk_qst.question_fi AND svy_qblk.questionblock_id = %s",
1600 array('integer'),
1601 array($questionblock_id)
1602 );
1603 $survey_id = "";
1604 while ($row = $ilDB->fetchAssoc($result)) {
1605 $titles[$row["question_fi"]] = $row["title"];
1606 $survey_id = $row["survey_fi"];
1607 }
1608 $result = $ilDB->queryF(
1609 "SELECT question_fi, sequence FROM svy_svy_qst WHERE survey_fi = %s ORDER BY sequence",
1610 array('integer'),
1611 array($survey_id)
1612 );
1613 $resultarray = array();
1614 $counter = 1;
1615 while ($row = $ilDB->fetchAssoc($result)) {
1616 if (array_key_exists($row["question_fi"], $titles)) {
1617 $resultarray[$counter++] = $titles[$row["question_fi"]];
1618 }
1619 }
1620 return $resultarray;
1621 }
1622
1629 int $questionblock_id
1630 ): array {
1631 $ilDB = $this->db;
1632
1633 // we need a correct order here, see #22011
1634 $result = $ilDB->queryF(
1635 "SELECT a.question_fi FROM svy_qblk_qst a JOIN svy_svy_qst b ON (a.question_fi = b.question_fi) " .
1636 " WHERE a.questionblock_fi = %s ORDER BY b.sequence",
1637 array("integer"),
1638 array($questionblock_id)
1639 );
1640 $ids = array();
1641 if ($result->numRows()) {
1642 while ($data = $ilDB->fetchAssoc($result)) {
1643 if (!in_array($data['question_fi'], $ids)) { // no duplicates, see #22018
1644 $ids[] = (int) $data['question_fi'];
1645 }
1646 }
1647 }
1648 return $ids;
1649 }
1650
1655 public static function _getQuestionblock(
1656 int $questionblock_id
1657 ): array {
1658 global $DIC;
1659
1660 $ilDB = $DIC->database();
1661 $result = $ilDB->queryF(
1662 "SELECT * FROM svy_qblk WHERE questionblock_id = %s",
1663 array('integer'),
1664 array($questionblock_id)
1665 );
1666 $row = $ilDB->fetchAssoc($result);
1667 return $row;
1668 }
1669
1673 public static function _addQuestionblock(
1674 string $title = "",
1675 int $owner = 0,
1676 bool $show_questiontext = true,
1677 bool $show_blocktitle = false,
1678 bool $compress_view = false
1679 ): int {
1680 global $DIC;
1681
1682 $ilDB = $DIC->database();
1683 $next_id = $ilDB->nextId('svy_qblk');
1684 $ilDB->manipulateF(
1685 "INSERT INTO svy_qblk (questionblock_id, title, show_questiontext," .
1686 " show_blocktitle, owner_fi, tstamp, compress_view) " .
1687 "VALUES (%s, %s, %s, %s, %s, %s, %s)",
1688 array('integer','text','integer','integer','integer','integer','integer'),
1689 array($next_id, $title, $show_questiontext, $show_blocktitle, $owner, time(),$compress_view)
1690 );
1691 return $next_id;
1692 }
1693
1697 public function createQuestionblock(
1698 string $title,
1699 bool $show_questiontext,
1700 bool $show_blocktitle,
1701 array $questions,
1702 bool $compress_view = false
1703 ): void {
1704 $ilDB = $this->db;
1705
1706 // if the selected questions are not in a continous selection, move all questions of the
1707 // questionblock at the position of the first selected question
1708 $this->moveQuestions($questions, $questions[0], 0);
1709
1710 // now save the question block
1711 $ilUser = $this->user;
1712 $next_id = $ilDB->nextId('svy_qblk');
1713 $affectedRows = $ilDB->manipulateF(
1714 "INSERT INTO svy_qblk (questionblock_id, title, show_questiontext," .
1715 " show_blocktitle, owner_fi, tstamp, compress_view) VALUES (%s, %s, %s, %s, %s, %s, %s)",
1716 array('integer','text','text','text','integer','integer','integer'),
1717 array($next_id, $title, $show_questiontext, $show_blocktitle, $ilUser->getId(), time(), $compress_view)
1718 );
1719 if ($affectedRows) {
1720 $questionblock_id = $next_id;
1721 foreach ($questions as $index) {
1722 if (!$this->isQuestionInAnyBlock($index)) {
1723 $next_id = $ilDB->nextId('svy_qblk_qst'); // #22018
1724 $affectedRows = $ilDB->manipulateF(
1725 "INSERT INTO svy_qblk_qst (qblk_qst_id, survey_fi, questionblock_fi, " .
1726 "question_fi) VALUES (%s, %s, %s, %s)",
1727 array('integer', 'integer', 'integer', 'integer'),
1728 array($next_id, $this->getSurveyId(), $questionblock_id, $index)
1729 );
1730 $this->deleteConstraints($index);
1731 }
1732 }
1733 }
1734 }
1735
1739 public function modifyQuestionblock(
1740 int $questionblock_id,
1741 string $title,
1742 bool $show_questiontext,
1743 bool $show_blocktitle,
1744 bool $compress_view = false
1745 ): void {
1746 $ilDB = $this->db;
1747 $ilDB->manipulateF(
1748 "UPDATE svy_qblk SET title = %s, show_questiontext = %s," .
1749 " show_blocktitle = %s, compress_view = %s WHERE questionblock_id = %s",
1750 array('text','text','text','integer', 'integer'),
1751 array($title, $show_questiontext, $show_blocktitle, $compress_view, $questionblock_id)
1752 );
1753 }
1754
1759 public function deleteConstraints(
1760 int $question_id
1761 ): void {
1762 $ilDB = $this->db;
1763 $result = $ilDB->queryF(
1764 "SELECT constraint_fi FROM svy_qst_constraint WHERE question_fi = %s AND survey_fi = %s",
1765 array('integer','integer'),
1766 array($question_id, $this->getSurveyId())
1767 );
1768 $constraints = array();
1769 while ($row = $ilDB->fetchAssoc($result)) {
1770 $constraints[] = $row["constraint_fi"];
1771 }
1772 foreach ($constraints as $constraint_id) {
1773 $this->deleteConstraint($constraint_id);
1774 }
1775 }
1776
1781 public function deleteConstraint(
1782 int $constraint_id
1783 ): void {
1784 $ilDB = $this->db;
1785 $affectedRows = $ilDB->manipulateF(
1786 "DELETE FROM svy_constraint WHERE constraint_id = %s",
1787 array('integer'),
1788 array($constraint_id)
1789 );
1790 $affectedRows = $ilDB->manipulateF(
1791 "DELETE FROM svy_qst_constraint WHERE constraint_fi = %s",
1792 array('integer'),
1793 array($constraint_id)
1794 );
1795 }
1796
1801 public function getSurveyQuestions(
1802 bool $with_answers = false
1803 ): array {
1804 $ilDB = $this->db;
1805 // get questionblocks
1806 $all_questions = array();
1807 $result = $ilDB->queryF(
1808 "SELECT svy_qtype.type_tag, svy_qtype.plugin, svy_question.question_id, " .
1809 "svy_svy_qst.heading FROM svy_qtype, svy_question, svy_svy_qst WHERE svy_svy_qst.survey_fi = %s AND " .
1810 "svy_svy_qst.question_fi = svy_question.question_id AND svy_question.questiontype_fi = svy_qtype.questiontype_id " .
1811 "ORDER BY svy_svy_qst.sequence",
1812 array('integer'),
1813 array($this->getSurveyId())
1814 );
1815 while ($row = $ilDB->fetchAssoc($result)) {
1816 $add = true;
1817 if ($row["plugin"]) {
1818 $add = false;
1819 }
1820 if ($add) {
1821 $question = self::_instanciateQuestion($row["question_id"]);
1822 $questionrow = $question->getQuestionDataArray($row["question_id"]);
1823 foreach ($row as $key => $value) {
1824 $questionrow[$key] = $value;
1825 }
1826 $all_questions[$row["question_id"]] = $questionrow;
1827 $all_questions[$row["question_id"]]["usableForPrecondition"] = $question->usableForPrecondition();
1828 $all_questions[$row["question_id"]]["availableRelations"] = $question->getAvailableRelations();
1829 }
1830 }
1831 // get all questionblocks
1832 $questionblocks = array();
1833 if (count($all_questions)) {
1834 $result = $ilDB->queryF(
1835 "SELECT svy_qblk.*, svy_qblk_qst.question_fi FROM svy_qblk, svy_qblk_qst WHERE " .
1836 "svy_qblk.questionblock_id = svy_qblk_qst.questionblock_fi AND svy_qblk_qst.survey_fi = %s " .
1837 "AND " . $ilDB->in('svy_qblk_qst.question_fi', array_keys($all_questions), false, 'integer'),
1838 array('integer'),
1839 array($this->getSurveyId())
1840 );
1841 while ($row = $ilDB->fetchAssoc($result)) {
1842 $questionblocks[$row['question_fi']] = $row;
1843 }
1844 }
1845
1846 foreach ($all_questions as $question_id => $row) {
1847 $constraints = $this->getConstraints($question_id);
1848 if (isset($questionblocks[$question_id])) {
1849 $all_questions[$question_id]["questionblock_title"] = $questionblocks[$question_id]['title'];
1850 $all_questions[$question_id]["questionblock_id"] = $questionblocks[$question_id]['questionblock_id'];
1851 } else {
1852 $all_questions[$question_id]["questionblock_title"] = "";
1853 $all_questions[$question_id]["questionblock_id"] = "";
1854 }
1855 $all_questions[$question_id]["constraints"] = $constraints;
1856 if ($with_answers) {
1857 $answers = array();
1858 $result = $ilDB->queryF(
1859 "SELECT svy_variable.*, svy_category.title FROM svy_variable, svy_category " .
1860 "WHERE svy_variable.question_fi = %s AND svy_variable.category_fi = svy_category.category_id " .
1861 "ORDER BY sequence ASC",
1862 array('integer'),
1863 array($question_id)
1864 );
1865 if ($result->numRows() > 0) {
1866 while ($data = $ilDB->fetchAssoc($result)) {
1867 $answers[] = $data["title"];
1868 }
1869 }
1870 $all_questions[$question_id]["answers"] = $answers;
1871 }
1872 }
1873 return $all_questions;
1874 }
1875
1881 public function setObligatoryStates(
1882 array $obligatory_questions
1883 ): void {
1884 $ilDB = $this->db;
1885 $result = $ilDB->queryF(
1886 "SELECT * FROM svy_svy_qst WHERE survey_fi = %s",
1887 array('integer'),
1888 array($this->getSurveyId())
1889 );
1890 if ($result->numRows()) {
1891 while ($row = $ilDB->fetchAssoc($result)) {
1892 if (!array_key_exists($row["question_fi"], $obligatory_questions)) {
1893 $obligatory_questions[$row["question_fi"]] = 0;
1894 }
1895 }
1896 }
1897 // set the obligatory states in the database
1898 foreach ($obligatory_questions as $question_fi => $obligatory) {
1899 // #12420
1900 $ilDB->manipulate("UPDATE svy_question" .
1901 " SET obligatory = " . $ilDB->quote($obligatory, "integer") .
1902 " WHERE question_id = " . $ilDB->quote($question_fi, "integer"));
1903 }
1904 }
1905
1910 public function getSurveyPages(): array
1911 {
1912 $ilDB = $this->db;
1913 // get questionblocks
1914 $all_questions = array();
1915 $result = $ilDB->queryF(
1916 "SELECT svy_question.*, svy_qtype.type_tag, svy_svy_qst.heading FROM " .
1917 "svy_question, svy_qtype, svy_svy_qst WHERE svy_svy_qst.survey_fi = %s AND " .
1918 "svy_svy_qst.question_fi = svy_question.question_id AND svy_question.questiontype_fi = svy_qtype.questiontype_id " .
1919 "ORDER BY svy_svy_qst.sequence",
1920 array('integer'),
1921 array($this->getSurveyId())
1922 );
1923 while ($row = $ilDB->fetchAssoc($result)) {
1924 $all_questions[$row["question_id"]] = $row;
1925 }
1926 // get all questionblocks
1927 $questionblocks = array();
1928 if (count($all_questions)) {
1929 $result = $ilDB->queryF(
1930 "SELECT svy_qblk.*, svy_qblk_qst.question_fi FROM svy_qblk, svy_qblk_qst " .
1931 "WHERE svy_qblk.questionblock_id = svy_qblk_qst.questionblock_fi AND svy_qblk_qst.survey_fi = %s " .
1932 "AND " . $ilDB->in('svy_qblk_qst.question_fi', array_keys($all_questions), false, 'integer'),
1933 array('integer'),
1934 array($this->getSurveyId())
1935 );
1936 while ($row = $ilDB->fetchAssoc($result)) {
1937 $questionblocks[$row['question_fi']] = $row;
1938 }
1939 }
1940
1941 $all_pages = array();
1942 $pageindex = -1;
1943 $currentblock = "";
1944 foreach ($all_questions as $question_id => $row) {
1945 $constraints = array();
1946 if (isset($questionblocks[$question_id])) {
1947 if (!$currentblock or ($currentblock != $questionblocks[$question_id]['questionblock_id'])) {
1948 $pageindex++;
1949 }
1950 $all_questions[$question_id]['page'] = $pageindex;
1951 $all_questions[$question_id]["questionblock_title"] = $questionblocks[$question_id]['title'];
1952 $all_questions[$question_id]["questionblock_id"] = $questionblocks[$question_id]['questionblock_id'];
1953 $all_questions[$question_id]["questionblock_show_questiontext"] = $questionblocks[$question_id]['show_questiontext'];
1954 $all_questions[$question_id]["questionblock_show_blocktitle"] = $questionblocks[$question_id]['show_blocktitle'];
1955 $all_questions[$question_id]["questionblock_compress_view"] = $questionblocks[$question_id]['compress_view'];
1956 $currentblock = $questionblocks[$question_id]['questionblock_id'];
1957 } else {
1958 $pageindex++;
1959 $all_questions[$question_id]['page'] = $pageindex;
1960 $all_questions[$question_id]["questionblock_title"] = "";
1961 $all_questions[$question_id]["questionblock_id"] = "";
1962 $all_questions[$question_id]["questionblock_show_questiontext"] = 1;
1963 $all_questions[$question_id]["questionblock_show_blocktitle"] = 1;
1964 $all_questions[$question_id]["questionblock_compress_view"] = false;
1965 $currentblock = "";
1966 }
1967 $constraints = $this->getConstraints($question_id);
1968 $all_questions[$question_id]["constraints"] = $constraints;
1969 if (!isset($all_pages[$pageindex])) {
1970 $all_pages[$pageindex] = array();
1971 }
1972 $all_pages[$pageindex][] = $all_questions[$question_id];
1973 }
1974 // calculate position percentage for every page
1975 $max = count($all_pages);
1976 $counter = 1;
1977 foreach ($all_pages as $index => $block) {
1978 foreach ($block as $blockindex => $question) {
1979 $all_pages[$index][$blockindex]["position"] = $counter / $max;
1980 }
1981 $counter++;
1982 }
1983
1984 return $all_pages;
1985 }
1986
1995 public function getNextPage(
1996 int $active_page_question_id,
1997 int $direction
1998 ): ?array {
1999 $foundpage = -1;
2000 $pages = $this->getSurveyPages();
2001 if ($active_page_question_id === 0) {
2002 return $pages[0];
2003 }
2004 foreach ($pages as $key => $question_array) {
2005 foreach ($question_array as $question) {
2006 if ($active_page_question_id == $question["question_id"]) {
2007 $foundpage = $key;
2008 }
2009 }
2010 }
2011 if ($foundpage === -1) {
2012 throw new ilSurveyException("nextPage: Current page not found.");
2013 } else {
2014 $foundpage += $direction;
2015 if ($foundpage < 0) {
2016 return null;
2017 }
2018 if ($foundpage >= count($pages)) {
2019 return null;
2020 }
2021 return $pages[$foundpage];
2022 }
2023 }
2024
2029 bool $use_obj_id = false,
2030 bool $could_be_offline = false,
2031 bool $showPath = false,
2032 string $permission = "read"
2033 ): array {
2034 return ilObjSurveyQuestionPool::_getAvailableQuestionpools($use_obj_id, $could_be_offline, $showPath, $permission);
2035 }
2036
2041 public function getPrecondition(
2042 int $constraint_id
2043 ): array {
2044 $ilDB = $this->db;
2045
2046 $result = $ilDB->queryF(
2047 "SELECT svy_constraint.*, svy_relation.*, svy_qst_constraint.question_fi ref_question_fi FROM svy_qst_constraint, svy_constraint, " .
2048 "svy_relation WHERE svy_constraint.relation_fi = svy_relation.relation_id AND " .
2049 "svy_qst_constraint.constraint_fi = svy_constraint.constraint_id AND svy_constraint.constraint_id = %s",
2050 array('integer'),
2051 array($constraint_id)
2052 );
2053 $pc = array();
2054 if ($result->numRows()) {
2055 $pc = $ilDB->fetchAssoc($result);
2056 }
2057 return $pc;
2058 }
2059
2064 public function getConstraints(
2065 int $question_id
2066 ): array {
2067 $ilDB = $this->db;
2068
2069 $result_array = array();
2070 $result = $ilDB->queryF(
2071 "SELECT svy_constraint.*, svy_relation.* FROM svy_qst_constraint, svy_constraint, svy_relation " .
2072 "WHERE svy_constraint.relation_fi = svy_relation.relation_id AND " .
2073 "svy_qst_constraint.constraint_fi = svy_constraint.constraint_id AND svy_qst_constraint.question_fi = %s " .
2074 "AND svy_qst_constraint.survey_fi = %s",
2075 array('integer','integer'),
2076 array($question_id, $this->getSurveyId())
2077 );
2078 while ($row = $ilDB->fetchAssoc($result)) {
2079 $question_type = SurveyQuestion::_getQuestionType($row["question_fi"]);
2080 SurveyQuestion::_includeClass($question_type);
2081 $question = new $question_type();
2082 $question->loadFromDb($row["question_fi"]);
2083 $valueoutput = $question->getPreconditionValueOutput($row["value"]);
2084 $result_array[] = array("id" => $row["constraint_id"],
2085 "question" => $row["question_fi"],
2086 "short" => $row["shortname"],
2087 "long" => $row["longname"],
2088 "value" => $row["value"],
2089 "conjunction" => $row["conjunction"],
2090 "valueoutput" => $valueoutput
2091 );
2092 }
2093 return $result_array;
2094 }
2095
2099 public static function _getConstraints(
2100 int $survey_id
2101 ): array {
2102 global $DIC;
2103
2104 $ilDB = $DIC->database();
2105 $result_array = array();
2106 $result = $ilDB->queryF(
2107 "SELECT svy_qst_constraint.question_fi as for_question, svy_constraint.*, svy_relation.* " .
2108 "FROM svy_qst_constraint, svy_constraint, svy_relation WHERE svy_constraint.relation_fi = svy_relation.relation_id " .
2109 "AND svy_qst_constraint.constraint_fi = svy_constraint.constraint_id AND svy_qst_constraint.survey_fi = %s",
2110 array('integer'),
2111 array($survey_id)
2112 );
2113 while ($row = $ilDB->fetchAssoc($result)) {
2114 $result_array[] = array("id" => $row["constraint_id"],
2115 "for_question" => $row["for_question"],
2116 "question" => $row["question_fi"],
2117 "short" => $row["shortname"],
2118 "long" => $row["longname"],
2119 "relation_id" => $row["relation_id"],
2120 "value" => $row["value"],
2121 'conjunction' => $row['conjunction']
2122 );
2123 }
2124 return $result_array;
2125 }
2126
2127
2132 public function getVariables(
2133 int $question_id
2134 ): array {
2135 $ilDB = $this->db;
2136
2137 $result_array = array();
2138 $result = $ilDB->queryF(
2139 "SELECT svy_variable.*, svy_category.title FROM svy_variable LEFT JOIN " .
2140 "svy_category ON svy_variable.category_fi = svy_category.category_id WHERE svy_variable.question_fi = %s " .
2141 "ORDER BY svy_variable.sequence",
2142 array('integer'),
2143 array($question_id)
2144 );
2145 while ($row = $ilDB->fetchObject($result)) {
2146 $result_array[$row->sequence] = $row;
2147 }
2148 return $result_array;
2149 }
2150
2160 public function addConstraint(
2161 int $if_question_id,
2162 int $relation,
2163 float $value,
2164 int $conjunction
2165 ): ?int {
2166 $ilDB = $this->db;
2167
2168 $next_id = $ilDB->nextId('svy_constraint');
2169 $affectedRows = $ilDB->manipulateF(
2170 "INSERT INTO svy_constraint (constraint_id, question_fi, relation_fi, value, conjunction) VALUES " .
2171 "(%s, %s, %s, %s, %s)",
2172 array('integer','integer','integer','float', 'integer'),
2173 array($next_id, $if_question_id, $relation, $value, $conjunction)
2174 );
2175 if ($affectedRows) {
2176 return $next_id;
2177 } else {
2178 return null;
2179 }
2180 }
2181
2182
2187 int $to_question_id,
2188 int $constraint_id
2189 ): void {
2190 $ilDB = $this->db;
2191
2192 $next_id = $ilDB->nextId('svy_qst_constraint');
2193 $ilDB->manipulateF(
2194 "INSERT INTO svy_qst_constraint (question_constraint_id, survey_fi, question_fi, " .
2195 "constraint_fi) VALUES (%s, %s, %s, %s)",
2196 array('integer','integer','integer','integer'),
2197 array($next_id, $this->getSurveyId(), $to_question_id, $constraint_id)
2198 );
2199 }
2200
2204 public function updateConstraint(
2205 int $precondition_id,
2206 int $if_question_id,
2207 int $relation,
2208 float $value,
2209 int $conjunction
2210 ): void {
2211 $ilDB = $this->db;
2212 $ilDB->manipulateF(
2213 "UPDATE svy_constraint SET question_fi = %s, relation_fi = %s, value = %s, conjunction = %s " .
2214 "WHERE constraint_id = %s",
2215 array('integer','integer','float','integer','integer'),
2216 array($if_question_id, $relation, $value, $conjunction, $precondition_id)
2217 );
2218 }
2219
2224 array $questions,
2225 int $conjunction
2226 ): void {
2227 $ilDB = $this->db;
2228 foreach ($questions as $question_id) {
2229 $ilDB->manipulateF(
2230 "UPDATE svy_constraint SET conjunction = %s " .
2231 "WHERE constraint_id IN (SELECT constraint_fi FROM svy_qst_constraint WHERE svy_qst_constraint.question_fi = %s)",
2232 array('integer','integer'),
2233 array($conjunction, $question_id)
2234 );
2235 }
2236 }
2237
2241 public function getAllRelations(
2242 bool $short_as_key = false
2243 ): array {
2244 $ilDB = $this->db;
2245
2246 // #7987
2247 $custom_order = array("equal", "not_equal", "less", "less_or_equal", "more", "more_or_equal");
2248 $custom_order = array_flip($custom_order);
2249
2250 $result_array = array();
2251 $result = $ilDB->query("SELECT * FROM svy_relation");
2252 while ($row = $ilDB->fetchAssoc($result)) {
2253 if ($short_as_key) {
2254 $result_array[$row["shortname"]] = array("short" => $row["shortname"], "long" => $row["longname"], "id" => $row["relation_id"], "order" => $custom_order[$row["longname"]]);
2255 } else {
2256 $result_array[$row["relation_id"]] = array("short" => $row["shortname"], "long" => $row["longname"], "order" => $custom_order[$row["longname"]]);
2257 }
2258 }
2259
2260 $result_array = ilArrayUtil::sortArray($result_array, "order", "ASC", true, true);
2261 foreach ($result_array as $idx => $item) {
2262 unset($result_array[$idx]["order"]);
2263 }
2264
2265 return $result_array;
2266 }
2267
2268
2269
2270
2275 public function deleteWorkingData(
2276 int $question_id,
2277 int $active_id
2278 ): void {
2279 $ilDB = $this->db;
2280
2281 $affectedRows = $ilDB->manipulateF(
2282 "DELETE FROM svy_answer WHERE question_fi = %s AND active_fi = %s",
2283 array('integer','integer'),
2284 array($question_id, $active_id)
2285 );
2286 }
2287
2292 public function loadWorkingData(
2293 int $question_id,
2294 int $active_id
2295 ): array {
2296 $ilDB = $this->db;
2297 $result_array = array();
2298 $result = $ilDB->queryF(
2299 "SELECT * FROM svy_answer WHERE question_fi = %s AND active_fi = %s",
2300 array('integer','integer'),
2301 array($question_id, $active_id)
2302 );
2303 if ($result->numRows() >= 1) {
2304 while ($row = $ilDB->fetchAssoc($result)) {
2305 $result_array[] = $row;
2306 }
2307 }
2308 return $result_array;
2309 }
2310
2315 public function finishSurvey(
2316 int $finished_id,
2317 int $appr_id = 0
2318 ): void {
2319 $ilDB = $this->db;
2320
2321 $ilDB->manipulateF(
2322 "UPDATE svy_finished SET state = %s, tstamp = %s" .
2323 " WHERE survey_fi = %s AND finished_id = %s",
2324 array('text','integer','integer','integer'),
2325 array(1, time(), $this->getSurveyId(), $finished_id)
2326 );
2327
2328 // self eval writes skills on finishing
2329 if ($this->getMode() === self::MODE_SELF_EVAL) {
2330 $user = $this->getUserDataFromActiveId($finished_id);
2331 $sskill = new ilSurveySkill($this);
2332 $sskill->writeAndAddSelfEvalSkills($user['usr_id']);
2333 }
2334
2335 // self eval writes skills on finishing
2336 if ($this->getMode() === self::MODE_IND_FEEDB) {
2337 // we use a rater id like "a27" for anonymous or
2338 // "123" for non anonymous user
2339 // @todo: move this e.g. to participant manager
2340 $raters = $this->getRatersData($appr_id);
2341 $run_manager = $this->survey_service
2342 ->domain()
2343 ->execution()
2344 ->run($this, $this->user->getId(), $appr_id);
2345 $run = $run_manager->getById($finished_id);
2346 $rater_id = "";
2347 if ($run->getUserId() !== 0 && $run->getUserId() !== ANONYMOUS_USER_ID) {
2348 $rater_id = $run->getUserId();
2349 } else {
2350 foreach ($raters as $id => $rater) {
2351 if (($rater["code"] ?? "") === $run->getCode()) {
2352 $rater_id = $id;
2353 }
2354 }
2355 }
2356 $sskill = new ilSurveySkill($this);
2357 //$sskill->writeAndAddSelfEvalSkills($user['usr_id']);
2358 $sskill->writeAndAddIndFeedbackSkills($finished_id, $appr_id, $rater_id);
2359 }
2360
2361 $this->checkTutorNotification();
2362 }
2363
2370 public function setPage(
2371 int $finished_id,
2372 int $page_id
2373 ): void {
2374 $ilDB = $this->db;
2375
2376 $ilDB->manipulateF(
2377 "UPDATE svy_finished SET lastpage = %s WHERE finished_id = %s",
2378 array('integer','integer'),
2379 array(($page_id) ?: 0, $finished_id)
2380 );
2381 }
2382
2389 public function sendNotificationMail(
2390 int $a_user_id,
2391 string $a_anonymize_id,
2392 int $a_appr_id = 0
2393 ): void {
2394 // #12755
2395 $placeholders = array(
2396 "FIRST_NAME" => "firstname",
2397 "LAST_NAME" => "lastname",
2398 "LOGIN" => "login",
2399 // old style
2400 "firstname" => "firstname"
2401 );
2402
2403 //mailaddresses is just text split by commas.
2404 //sendMail can send emails if it gets an user id or an email as first parameter.
2405 $recipients = explode(",", $this->mailaddresses ?? "");
2406 foreach ($recipients as $recipient) {
2407 // #11298
2408 $ntf = new ilSystemNotification();
2409 $ntf->setLangModules(array("survey"));
2410 $ntf->setRefId($this->getRefId());
2411 $ntf->setSubjectLangId('finished_mail_subject');
2412
2413 $messagetext = $this->mailparticipantdata;
2414 $data = [];
2415 if (trim($messagetext ?? "")) {
2416 if (!$this->hasAnonymizedResults()) {
2417 $data = ilObjUser::_getUserData(array($a_user_id));
2418 $data = $data[0];
2419 }
2420 foreach ($placeholders as $key => $mapping) {
2421 if ($this->hasAnonymizedResults()) { // #16480
2422 $messagetext = str_replace('[' . $key . ']', '', $messagetext);
2423 } else {
2424 $messagetext = str_replace('[' . $key . ']', trim($data[$mapping] ?? ""), $messagetext);
2425 }
2426 }
2427 $ntf->setIntroductionDirect($messagetext);
2428 } else {
2429 $ntf->setIntroductionLangId('survey_notification_finished_introduction');
2430 }
2431
2432 // 360°? add appraisee data
2433 if ($a_appr_id) {
2434 $ntf->addAdditionalInfo(
2435 'survey_360_appraisee',
2437 );
2438 }
2439
2440 $active_id = $this->getActiveID($a_user_id, $a_anonymize_id, $a_appr_id);
2441 if ($active_id) { // 43908
2442 $ntf->addAdditionalInfo(
2443 'results',
2444 $this->getParticipantTextResults($active_id),
2445 true
2446 );
2447 }
2448
2449 $ntf->setGotoLangId('survey_notification_tutor_link');
2450 $ntf->setReasonLangId('survey_notification_finished_reason');
2451
2452 $recipient = trim($recipient ?? "");
2453 $user_id = (int) ilObjUser::_lookupId($recipient);
2454 if ($user_id > 0) {
2455 $ntf->sendMailAndReturnRecipients([$user_id]);
2456 } else {
2457 $user_ids = ilObjUser::getUserIdsByEmail($recipient);
2458 if (count($user_ids) > 0) {
2459 $ntf->sendMailAndReturnRecipients([current($user_ids)]);
2460 }
2461 }
2462 /* note: this block is replace by the single line above
2463 since the UI asks for account names and the "e-mail" fallback leads
2464 to strange issues like multiple mails. Also the test case has been
2465 adopted, see https://mantis.ilias.de/view.php?id=36327
2466 if (is_numeric($recipient)) {
2467 $lng = $ntf->getUserLanguage((int) $recipient);
2468 $ntf->sendMailAndReturnRecipients([(int) $recipient]);
2469 } else {
2470 $recipient = trim($recipient);
2471 $user_ids = ilObjUser::getUserIdsByEmail($recipient);
2472 if (empty($user_ids)) {
2473 $ntf->sendMailAndReturnRecipients([ilObjUser::_lookupId($recipient)]);
2474 } else {
2475 foreach ($user_ids as $user_id) {
2476 $lng = $ntf->getUserLanguage($user_id);
2477 $ntf->sendMailAndReturnRecipients(array($user_id));
2478 }
2479 }
2480 }*/
2481 }
2482 }
2483
2484 protected function getParticipantTextResults(
2485 int $active_id
2486 ): string {
2487 $textresult = "";
2488 $userResults = $this->getUserSpecificResults(array($active_id));
2489 $questions = $this->getSurveyQuestions(true);
2490 $questioncounter = 1;
2491 foreach ($questions as $question_id => $question_data) {
2492 $textresult .= $questioncounter++ . ". " . $question_data["title"] . "\n";
2493 $found = $userResults[$question_id][$active_id];
2494 $text = "";
2495 if (is_array($found)) {
2496 $text = implode("\n", $found);
2497 } else {
2498 $text = $found;
2499 }
2500 if ($text === '') {
2501 $text = self::getSurveySkippedValue();
2502 }
2503 $text = str_replace("<br />", "\n", $text);
2504 $textresult .= $text . "\n\n";
2505 }
2506 return $textresult;
2507 }
2508
2513 public function getActiveID(
2514 int $user_id,
2515 string $anonymize_id,
2516 int $appr_id
2517 ): ?int {
2518 $ilDB = $this->db;
2519 // #15031 - should not matter if code was used by registered or anonymous (each code must be unique)
2520 if ($anonymize_id) {
2521 $result = $ilDB->queryF(
2522 "SELECT finished_id FROM svy_finished" .
2523 " WHERE survey_fi = %s AND anonymous_id = %s AND appr_id = %s",
2524 array('integer','text','integer'),
2525 array($this->getSurveyId(), $anonymize_id, $appr_id)
2526 );
2527 } else {
2528 if ($user_id == ANONYMOUS_USER_ID) {
2529 return null;
2530 }
2531 $result = $ilDB->queryF(
2532 "SELECT finished_id FROM svy_finished" .
2533 " WHERE survey_fi = %s AND user_fi = %s AND appr_id = %s",
2534 array('integer','integer','integer'),
2535 array($this->getSurveyId(), $user_id, $appr_id)
2536 );
2537 }
2538 if ($result->numRows() === 0) {
2539 return null;
2540 } else {
2541 $row = $ilDB->fetchAssoc($result);
2542 return (int) $row["finished_id"];
2543 }
2544 }
2545
2550 public function getLastActivePage(
2551 int $active_id
2552 ): ?int {
2553 $ilDB = $this->db;
2554 $result = $ilDB->queryF(
2555 "SELECT lastpage FROM svy_finished WHERE finished_id = %s",
2556 array('integer'),
2557 array($active_id)
2558 );
2559 if ($row = $ilDB->fetchAssoc($result)) {
2560 return (int) $row["lastpage"];
2561 }
2562 return null;
2563 }
2564
2573 public function checkConstraint(
2574 array $constraint_data,
2575 ?array $working_data
2576 ): bool {
2577 if (!is_array($working_data) || count($working_data) === 0) {
2578 return 0;
2579 }
2580
2581 if ((count($working_data) === 1) and (strcmp($working_data[0]["value"], "") === 0)) {
2582 return 0;
2583 }
2584
2585 $found = false;
2586 foreach ($working_data as $data) {
2587 switch ($constraint_data["short"]) {
2588 case "<":
2589 if ($data["value"] < $constraint_data["value"]) {
2590 $found = true;
2591 }
2592 break;
2593
2594 case "<=":
2595 if ($data["value"] <= $constraint_data["value"]) {
2596 $found = true;
2597 }
2598 break;
2599
2600 case "=":
2601 if ($data["value"] == $constraint_data["value"]) {
2602 $found = true;
2603 }
2604 break;
2605
2606 case "<>":
2607 if ($data["value"] <> $constraint_data["value"]) {
2608 $found = true;
2609 }
2610 break;
2611
2612 case ">=":
2613 if ($data["value"] >= $constraint_data["value"]) {
2614 $found = true;
2615 }
2616 break;
2617
2618 case ">":
2619 if ($data["value"] > $constraint_data["value"]) {
2620 $found = true;
2621 }
2622 break;
2623 }
2624 if ($found) {
2625 break;
2626 }
2627 }
2628
2629 return (int) $found;
2630 }
2631
2635 public static function _hasDatasets(
2636 int $survey_id
2637 ): bool {
2638 global $DIC;
2639
2640 $ilDB = $DIC->database();
2641
2642 $result = $ilDB->queryF(
2643 "SELECT finished_id FROM svy_finished WHERE survey_fi = %s",
2644 array('integer'),
2645 array($survey_id)
2646 );
2647 return (bool) $result->numRows();
2648 }
2649
2655 public function getSurveyFinishedIds(): array
2656 {
2657 $ilDB = $this->db;
2658
2659 $users = array();
2660 $result = $ilDB->queryF(
2661 "SELECT * FROM svy_finished WHERE survey_fi = %s",
2662 array('integer'),
2663 array($this->getSurveyId())
2664 );
2665 if ($result->numRows()) {
2666 while ($row = $ilDB->fetchAssoc($result)) {
2667 $users[] = (int) $row["finished_id"];
2668 }
2669 }
2670 return $users;
2671 }
2672
2678 public function getUserSpecificResults(
2679 array $finished_ids
2680 ): array {
2681 $evaluation = array();
2682
2683 foreach (array_keys($this->getSurveyQuestions()) as $question_id) {
2684 // get question instance
2685 $question_type = SurveyQuestion::_getQuestionType($question_id);
2686 SurveyQuestion::_includeClass($question_type);
2687 $question = new $question_type();
2688 $question->loadFromDb($question_id);
2689
2690 $q_eval = SurveyQuestion::_instanciateQuestionEvaluation($question_id, $finished_ids);
2691 $q_res = $q_eval->getResults();
2692
2693 $data = array();
2694 foreach ($finished_ids as $user_id) {
2695 $data[$user_id] = $q_eval->parseUserSpecificResults($q_res, $user_id);
2696 }
2697
2698 $evaluation[$question_id] = $data;
2699 }
2700
2701 return $evaluation;
2702 }
2703
2709 int $active_id,
2710 bool $force_non_anonymous = false
2711 ): array {
2712 $ilDB = $this->db;
2713
2714 $surveySetting = new ilSetting("survey");
2715 $use_anonymous_id = $surveySetting->get("use_anonymous_id");
2716 $result = $ilDB->queryF(
2717 "SELECT * FROM svy_finished WHERE finished_id = %s",
2718 array('integer'),
2719 array($active_id)
2720 );
2721 $row = array();
2722 $foundrows = $result->numRows();
2723 if ($foundrows) {
2724 $row = $ilDB->fetchAssoc($result);
2725 }
2726 $name = ($use_anonymous_id) ? $row["anonymous_id"] : $this->lng->txt("anonymous");
2727 $userdata = array(
2728 "fullname" => $name,
2729 "sortname" => $name,
2730 "firstname" => "",
2731 "lastname" => "",
2732 "login" => "",
2733 "gender" => "",
2734 "active_id" => (string) $active_id
2735 );
2736 if ($foundrows) {
2737 if (($row["user_fi"] > 0) &&
2738 (($row["user_fi"] != ANONYMOUS_USER_ID &&
2739 !$this->hasAnonymizedResults() &&
2740 !$this->get360Mode()) || // 360° uses ANONYMIZE_CODE_ALL which is wrong - see ilObjSurveyGUI::afterSave()
2741 $force_non_anonymous)) {
2742 if (ilObjUser::_lookupLogin($row["user_fi"]) === '') {
2743 $userdata["fullname"] = $userdata["sortname"] = $this->lng->txt("deleted_user");
2744 } else {
2745 $user = new ilObjUser($row["user_fi"]);
2746 $userdata['usr_id'] = $row['user_fi'];
2747 $userdata["fullname"] = $user->getFullname();
2748 $gender = $user->getGender();
2749 if (strlen($gender) === 1) {
2750 $gender = $this->lng->txt("gender_$gender");
2751 }
2752 $userdata["gender"] = $gender;
2753 $userdata["firstname"] = $user->getFirstname();
2754 $userdata["lastname"] = $user->getLastname();
2755 $userdata["sortname"] = $user->getLastname() . ", " . $user->getFirstname();
2756 $userdata["login"] = $user->getLogin();
2757 }
2758 }
2759 if ($row["user_fi"] == 0 || $row["user_fi"] == ANONYMOUS_USER_ID) {
2760 $code = $this->code_manager->getByUserKey((string) $row["anonymous_id"]);
2761 if (!is_null($code) && $this->feature_config->usesAppraisees()) {
2762 $userdata["firstname"] = $code->getFirstName();
2763 $userdata["lastname"] = $code->getLastName();
2764 $userdata["sortname"] = $code->getLastName() . ", " . $code->getFirstName();
2765 }
2766 }
2767 }
2768 return $userdata;
2769 }
2770
2775 public function getEvaluationByUser(
2776 array $questions,
2777 int $active_id
2778 ): array {
2779 $ilDB = $this->db;
2780
2781 // collect all answers
2782 $answers = array();
2783 $result = $ilDB->queryF(
2784 "SELECT * FROM svy_answer WHERE active_fi = %s",
2785 array('integer'),
2786 array($active_id)
2787 );
2788 while ($row = $ilDB->fetchAssoc($result)) {
2789 if (!is_array($answers[$row["question_fi"]])) {
2790 $answers[$row["question_fi"]] = array();
2791 }
2792 $answers[$row["question_fi"]][] = $row;
2793 }
2794 $userdata = $this->getUserDataFromActiveId($active_id);
2795 $resultset = array(
2796 "name" => $userdata["fullname"],
2797 "firstname" => $userdata["firstname"],
2798 "lastname" => $userdata["lastname"],
2799 "login" => $userdata["login"],
2800 "gender" => $userdata["gender"],
2801 "answers" => array()
2802 );
2803 foreach ($questions as $key => $question) {
2804 if (array_key_exists($key, $answers)) {
2805 $resultset["answers"][$key] = $answers[$key];
2806 } else {
2807 $resultset["answers"][$key] = array();
2808 }
2809 sort($resultset["answers"][$key]);
2810 }
2811 return $resultset;
2812 }
2813
2818 public function getQuestionsTable(
2819 array $arrFilter
2820 ): array {
2821 $ilDB = $this->db;
2822 $where = "";
2823 if (array_key_exists('title', $arrFilter) && strlen($arrFilter['title'])) {
2824 $where .= " AND " . $ilDB->like('svy_question.title', 'text', "%%" . $arrFilter['title'] . "%%");
2825 }
2826 if (array_key_exists('description', $arrFilter) && strlen($arrFilter['description'])) {
2827 $where .= " AND " . $ilDB->like('svy_question.description', 'text', "%%" . $arrFilter['description'] . "%%");
2828 }
2829 if (array_key_exists('author', $arrFilter) && strlen($arrFilter['author'])) {
2830 $where .= " AND " . $ilDB->like('svy_question.author', 'text', "%%" . $arrFilter['author'] . "%%");
2831 }
2832 if (array_key_exists('type', $arrFilter) && strlen($arrFilter['type'])) {
2833 $where .= " AND svy_qtype.type_tag = " . $ilDB->quote($arrFilter['type'], 'text');
2834 }
2835 if (array_key_exists('spl', $arrFilter) && strlen($arrFilter['spl'])) {
2836 $where .= " AND svy_question.obj_fi = " . $ilDB->quote($arrFilter['spl'], 'integer');
2837 }
2838
2839 $spls = $this->getAvailableQuestionpools(true, false, false);
2840 $forbidden = " AND " . $ilDB->in('svy_question.obj_fi', array_keys($spls), false, 'integer');
2841 $forbidden .= " AND svy_question.complete = " . $ilDB->quote("1", 'text');
2842 $existing = "";
2843 $existing_questions = $this->getExistingQuestions();
2844 if (count($existing_questions)) {
2845 $existing = " AND " . $ilDB->in('svy_question.question_id', $existing_questions, true, 'integer');
2846 }
2847
2849
2850 $query_result = $ilDB->query("SELECT svy_question.*, svy_qtype.type_tag, svy_qtype.plugin, object_reference.ref_id" .
2851 " FROM svy_question, svy_qtype, object_reference" .
2852 " WHERE svy_question.original_id IS NULL" . $forbidden . $existing .
2853 " AND svy_question.obj_fi = object_reference.obj_id AND svy_question.tstamp > 0" .
2854 " AND svy_question.questiontype_fi = svy_qtype.questiontype_id " . $where);
2855
2856 $rows = array();
2857 if ($query_result->numRows()) {
2858 while ($row = $ilDB->fetchAssoc($query_result)) {
2859 if (array_key_exists('spl_txt', $arrFilter) && strlen($arrFilter['spl_txt'])) {
2860 if (stripos($spls[$row["obj_fi"]], $arrFilter['spl_txt']) === false) {
2861 continue;
2862 }
2863 }
2864
2865 $row['ttype'] = $trans[$row['type_tag']];
2866 if ($row["plugin"]) {
2867 continue;
2868 } else {
2869 $rows[] = $row;
2870 }
2871 }
2872 }
2873 return $rows;
2874 }
2875
2880 public function getQuestionblocksTable(
2881 array $arrFilter
2882 ): array {
2883 $ilDB = $this->db;
2884
2885 $where = "";
2886 if (array_key_exists('title', $arrFilter) && strlen($arrFilter['title'])) {
2887 $where .= " AND " . $ilDB->like('svy_qblk.title', 'text', "%%" . $arrFilter['title'] . "%%");
2888 }
2889
2890 $query_result = $ilDB->query("SELECT svy_qblk.*, svy_svy.obj_fi FROM svy_qblk , svy_qblk_qst, svy_svy WHERE " .
2891 "svy_qblk.questionblock_id = svy_qblk_qst.questionblock_fi AND svy_svy.survey_id = svy_qblk_qst.survey_fi " .
2892 "$where GROUP BY svy_qblk.questionblock_id, svy_qblk.title, svy_qblk.show_questiontext, svy_qblk.show_blocktitle, " .
2893 "svy_qblk.owner_fi, svy_qblk.tstamp, svy_svy.obj_fi");
2894 $rows = array();
2895 if ($query_result->numRows()) {
2896 $survey_ref_ids = ilUtil::_getObjectsByOperations("svy", "write");
2897 $surveytitles = array();
2898 foreach ($survey_ref_ids as $survey_ref_id) {
2899 $survey_id = ilObject::_lookupObjId($survey_ref_id);
2900 $surveytitles[$survey_id] = ilObject::_lookupTitle($survey_id);
2901 }
2902 while ($row = $ilDB->fetchAssoc($query_result)) {
2903 $questions_array = $this->getQuestionblockQuestions($row["questionblock_id"]);
2904 $counter = 1;
2905 foreach ($questions_array as $key => $value) {
2906 $questions_array[$key] = "$counter. $value";
2907 $counter++;
2908 }
2909 if (strlen($surveytitles[$row["obj_fi"]] ?? "")) { // only questionpools which are not in trash
2910 $rows[$row["questionblock_id"]] = array(
2911 "questionblock_id" => $row["questionblock_id"],
2912 "title" => $row["title"],
2913 "svy" => $surveytitles[$row["obj_fi"]],
2914 "contains" => implode(", ", $questions_array),
2915 "owner" => $row["owner_fi"]
2916 );
2917 }
2918 }
2919 }
2920 return $rows;
2921 }
2922
2927 public function toXML(): string
2928 {
2929 $a_xml_writer = new ilXmlWriter();
2930 // set xml header
2931 $a_xml_writer->xmlHeader();
2932 $attrs = array(
2933 "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
2934 "xsi:noNamespaceSchemaLocation" => "https://www.ilias.de/download/xsd/ilias_survey_4_2.xsd"
2935 );
2936 $a_xml_writer->xmlStartTag("surveyobject", $attrs);
2937 $attrs = array(
2938 "id" => $this->getSurveyId(),
2939 "title" => $this->getTitle()
2940 );
2941 $a_xml_writer->xmlStartTag("survey", $attrs);
2942
2943 $a_xml_writer->xmlElement("description", null, $this->getDescription());
2944 $a_xml_writer->xmlElement("author", null, $this->getAuthor());
2945 $a_xml_writer->xmlStartTag("objectives");
2946 $attrs = array(
2947 "label" => "introduction"
2948 );
2949 $this->addMaterialTag($a_xml_writer, $this->getIntroduction(), true, true, $attrs);
2950 $attrs = array(
2951 "label" => "outro"
2952 );
2953 $this->addMaterialTag($a_xml_writer, $this->getOutro(), true, true, $attrs);
2954 $a_xml_writer->xmlEndTag("objectives");
2955
2956 if ($this->getAnonymize()) {
2957 $attribs = array("enabled" => "1");
2958 } else {
2959 $attribs = array("enabled" => "0");
2960 }
2961 $a_xml_writer->xmlElement("anonymisation", $attribs);
2962 $a_xml_writer->xmlStartTag("restrictions");
2963 if ($this->getAnonymize() === 2) {
2964 $attribs = array("type" => "free");
2965 } else {
2966 $attribs = array("type" => "restricted");
2967 }
2968 $a_xml_writer->xmlElement("access", $attribs);
2969 if ($this->getStartDate()) {
2970 $attrs = array("type" => "date");
2971 preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getStartDate(), $matches);
2972 $a_xml_writer->xmlElement("startingtime", $attrs, sprintf("%04d-%02d-%02dT%02d:%02d:00", $matches[1], $matches[2], $matches[3], $matches[4], $matches[5]));
2973 }
2974 if ($this->getEndDate()) {
2975 $attrs = array("type" => "date");
2976 preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getEndDate(), $matches);
2977 $a_xml_writer->xmlElement("endingtime", $attrs, sprintf("%04d-%02d-%02dT%02d:%02d:00", $matches[1], $matches[2], $matches[3], $matches[4], $matches[5]));
2978 }
2979 $a_xml_writer->xmlEndTag("restrictions");
2980
2981 // constraints
2982 $pages = $this->getSurveyPages();
2983 $hasconstraints = false;
2984 foreach ($pages as $question_array) {
2985 foreach ($question_array as $question) {
2986 if (count($question["constraints"])) {
2987 $hasconstraints = true;
2988 }
2989 }
2990 }
2991
2992 if ($hasconstraints) {
2993 $a_xml_writer->xmlStartTag("constraints");
2994 foreach ($pages as $question_array) {
2995 foreach ($question_array as $question) {
2996 if (count($question["constraints"])) {
2997 // found constraints
2998 foreach ($question["constraints"] as $constraint) {
2999 $attribs = array(
3000 "sourceref" => $question["question_id"],
3001 "destref" => $constraint["question"],
3002 "relation" => $constraint["short"],
3003 "value" => $constraint["value"],
3004 "conjunction" => $constraint["conjunction"]
3005 );
3006 $a_xml_writer->xmlElement("constraint", $attribs);
3007 }
3008 }
3009 }
3010 }
3011 $a_xml_writer->xmlEndTag("constraints");
3012 }
3013
3014 // add the rest of the preferences in qtimetadata tags, because there is no correspondent definition in QTI
3015 $a_xml_writer->xmlStartTag("metadata");
3016
3017 $custom_properties = array();
3018 $custom_properties["evaluation_access"] = $this->getEvaluationAccess();
3019 $custom_properties["status"] = !$this->getOfflineStatus();
3020 $custom_properties["display_question_titles"] = $this->getShowQuestionTitles();
3021
3022 $custom_properties["own_results_view"] = (int) $this->hasViewOwnResults();
3023 $custom_properties["own_results_mail"] = (int) $this->hasMailOwnResults();
3024 $custom_properties["confirmation_mail"] = (int) $this->hasMailConfirmation();
3025
3026 $custom_properties["anon_user_list"] = (int) $this->hasAnonymousUserList();
3027 $custom_properties["mode"] = $this->getMode();
3028 $custom_properties["mode_360_self_eval"] = (int) $this->get360SelfEvaluation();
3029 $custom_properties["mode_360_self_rate"] = (int) $this->get360SelfRaters();
3030 $custom_properties["mode_360_self_appr"] = (int) $this->get360SelfAppraisee();
3031 $custom_properties["mode_360_results"] = $this->get360Results();
3032 $custom_properties["mode_skill_service"] = (int) $this->getSkillService();
3033 $custom_properties["mode_self_eval_results"] = $this->getSelfEvaluationResults();
3034
3035
3036 // :TODO: skills?
3037
3038 // reminder/tutor notification are (currently?) not exportable
3039
3040 foreach ($custom_properties as $label => $value) {
3041 $a_xml_writer->xmlStartTag("metadatafield");
3042 $a_xml_writer->xmlElement("fieldlabel", null, $label);
3043 $a_xml_writer->xmlElement("fieldentry", null, $value);
3044 $a_xml_writer->xmlEndTag("metadatafield");
3045 }
3046
3047 $a_xml_writer->xmlEndTag("metadata");
3048 $a_xml_writer->xmlEndTag("survey");
3049
3050 $attribs = array("id" => $this->getId());
3051 $a_xml_writer->xmlStartTag("surveyquestions", $attribs);
3052 // add questionblock descriptions
3053 foreach ($pages as $question_array) {
3054 if (count($question_array) > 1) {
3055 $attribs = array("id" => $question_array[0]["question_id"]);
3056 $attribs = array(
3057 "showQuestiontext" => $question_array[0]["questionblock_show_questiontext"],
3058 "showBlocktitle" => $question_array[0]["questionblock_show_blocktitle"],
3059 "compressView" => $question_array[0]["questionblock_compress_view"]
3060 );
3061 $a_xml_writer->xmlStartTag("questionblock", $attribs);
3062 if (strlen($question_array[0]["questionblock_title"])) {
3063 $a_xml_writer->xmlElement("questionblocktitle", null, $question_array[0]["questionblock_title"]);
3064 }
3065 }
3066 foreach ($question_array as $question) {
3067 if (strlen($question["heading"] ?? "")) {
3068 $a_xml_writer->xmlElement("textblock", null, $question["heading"]);
3069 }
3070 $questionObject = self::_instanciateQuestion($question["question_id"]);
3071 //questionObject contains all the fields from the database. (loadFromDb)
3072 //we don't need the value from svy_qst_oblig table, we already have the values from svy_question table.
3073 //if ($questionObject !== FALSE) $questionObject->insertXML($a_xml_writer, FALSE, $obligatory_states[$question["question_id"]]);
3074 if ($questionObject !== false) {
3075 $questionObject->insertXML($a_xml_writer, false);
3076 }
3077 }
3078 if (count($question_array) > 1) {
3079 $a_xml_writer->xmlEndTag("questionblock");
3080 }
3081 }
3082
3083 $a_xml_writer->xmlEndTag("surveyquestions");
3084 $a_xml_writer->xmlEndTag("surveyobject");
3085 $xml = $a_xml_writer->xmlDumpMem(false);
3086 return $xml;
3087 }
3088
3093 public static function _instanciateQuestion(
3094 int $question_id
3095 ): ?SurveyQuestion {
3096 if ($question_id < 1) {
3097 return null;
3098 }
3099 $question_type = SurveyQuestion::_getQuestionType($question_id);
3100 if ($question_type === '') {
3101 return null;
3102 }
3103 SurveyQuestion::_includeClass($question_type);
3104 $question = new $question_type();
3105 $question->loadFromDb($question_id);
3106 return $question;
3107 }
3108
3114 public function locateImportFiles(
3115 string $a_dir
3116 ): ?array {
3117 if (!is_dir($a_dir) || is_int(strpos($a_dir, ".."))) {
3118 return null;
3119 }
3120 $importDirectory = "";
3121 $xmlFile = "";
3122
3123 $current_dir = opendir($a_dir);
3124 $files = array();
3125 while ($entryname = readdir($current_dir)) {
3126 $files[] = $entryname;
3127 }
3128
3129 foreach ($files as $file) {
3130 if (is_dir($a_dir . "/" . $file) and ($file !== "." and $file !== "..")) {
3131 // found directory created by zip
3132 $importDirectory = $a_dir . "/" . $file;
3133 }
3134 }
3135 closedir($current_dir);
3136 if ($importDirectory !== '') {
3137 // find the xml file
3138 $current_dir = opendir($importDirectory);
3139 $files = array();
3140 while ($entryname = readdir($current_dir)) {
3141 $files[] = $entryname;
3142 }
3143 foreach ($files as $file) {
3144 if (is_file($importDirectory . "/" . $file) &&
3145 ($file !== "." && $file !== "..") &&
3146 (preg_match("/^[0-9]{10}__[0-9]+__(svy_)*[0-9]+\.[A-Za-z]{1,3}$/", $file) ||
3147 preg_match("/^[0-9]{10}__[0-9]+__(survey__)*[0-9]+\.[A-Za-z]{1,3}$/", $file))) {
3148 // found xml file
3149 $xmlFile = $importDirectory . "/" . $file;
3150 }
3151 }
3152 }
3153 return array("dir" => $importDirectory, "xml" => $xmlFile);
3154 }
3155
3163 public function importObject(
3164 array $file_info,
3165 int $svy_qpl_id
3166 ): string {
3167 if ($svy_qpl_id < 1) {
3168 $svy_qpl_id = -1;
3169 }
3170 // check if file was uploaded
3171 $source = $file_info["tmp_name"];
3172 $error = "";
3173 if (($source === 'none') || (!$source) || $file_info["error"] > UPLOAD_ERR_OK) {
3174 $error = $this->lng->txt("import_no_file_selected");
3175 }
3176 // check correct file type
3177 $isXml = false;
3178 $isZip = false;
3179 if ((strcmp($file_info["type"], "text/xml") === 0) || (strcmp($file_info["type"], "application/xml") === 0)) {
3180 $this->svy_log->debug("isXML");
3181 $isXml = true;
3182 }
3183 // too many different mime-types, so we use the suffix
3184 $suffix = pathinfo($file_info["name"]);
3185 if (strcmp(strtolower($suffix["extension"]), "zip") === 0) {
3186 $this->svy_log->debug("isZip");
3187 $isZip = true;
3188 }
3189 if (!$isXml && !$isZip) {
3190 $error = $this->lng->txt("import_wrong_file_type");
3191 $this->svy_log->debug("Survey: Import error. Filetype was \"" . $file_info["type"] . "\"");
3192 }
3193 if ($error === '') {
3194 // import file as a survey
3195 $import_dir = $this->getImportDirectory();
3196 $import_subdir = "";
3197 $importfile = "";
3198 if ($isZip) {
3199 $importfile = $import_dir . "/" . $file_info["name"];
3200 ilFileUtils::moveUploadedFile($source, $file_info["name"], $importfile);
3201 $this->domain->resources()->zip()->unzipFile($importfile);
3202 $found = $this->locateImportFiles($import_dir);
3203 if (!((strlen($found["dir"]) > 0) && (strlen($found["xml"]) > 0))) {
3204 $error = $this->lng->txt("wrong_import_file_structure");
3205 return $error;
3206 }
3207 $importfile = $found["xml"];
3208 $import_subdir = $found["dir"];
3209 } else {
3210 $importfile = tempnam($import_dir, "survey_import");
3211 ilFileUtils::moveUploadedFile($source, $file_info["name"], $importfile);
3212 }
3213
3214 $this->svy_log->debug("Import file = $importfile");
3215 $this->svy_log->debug("Import subdir = $import_subdir");
3216
3217 $fh = fopen($importfile, 'rb');
3218 if (!$fh) {
3219 $error = $this->lng->txt("import_error_opening_file");
3220 return $error;
3221 }
3222 $xml = fread($fh, filesize($importfile));
3223 $result = fclose($fh);
3224 if (!$result) {
3225 $error = $this->lng->txt("import_error_closing_file");
3226 return $error;
3227 }
3228
3229 $this->import_manager->clearMobs();
3230 if (strpos($xml, "questestinterop")) {
3231 throw new ilInvalidSurveyImportFileException("Unsupported survey version (< 3.8) found.");
3232 } else {
3233 $this->svy_log->debug("survey id = " . $this->getId());
3234 $this->svy_log->debug("question pool id = " . $svy_qpl_id);
3235
3236 $imp = new ilImport();
3237 $config = $imp->getConfig("components/ILIAS/Survey");
3238 $config->setQuestionPoolID($svy_qpl_id);
3239 $imp->getMapping()->addMapping("components/ILIAS/Survey", "svy", 0, $this->getId());
3240 $imp->importFromDirectory($import_subdir, "svy", "components/ILIAS/Survey");
3241 $this->svy_log->debug("config(Modules/survey)->getQuestionPoolId =" . $config->getQuestionPoolID());
3242 }
3243 }
3244 return $error;
3245 }
3246
3247 public function cloneObject(int $target_id, int $copy_id = 0, bool $omit_tree = false): ?ilObject
3248 {
3249 $ilDB = $this->db;
3250
3251 $this->loadFromDb();
3252
3253 //survey mode
3254 $svy_type = $this->getMode();
3255
3257 $newObj = parent::cloneObject($target_id, $copy_id, $omit_tree);
3258 $this->cloneMetaData($newObj);
3259 $newObj->updateMetaData();
3260
3261 $newObj->setAuthor($this->getAuthor());
3262 $newObj->setIntroduction($this->getIntroduction());
3263 $newObj->setOutro($this->getOutro());
3264 $newObj->setEvaluationAccess($this->getEvaluationAccess());
3265 $newObj->setStartDate($this->getStartDate());
3266 $newObj->setEndDate($this->getEndDate());
3267 $newObj->setAnonymize($this->getAnonymize());
3268 $newObj->setShowQuestionTitles($this->getShowQuestionTitles());
3269 $newObj->setMailOwnResults($this->hasMailOwnResults());
3270 $newObj->setMailConfirmation($this->hasMailConfirmation());
3271 $newObj->setAnonymousUserList($this->hasAnonymousUserList());
3272
3273 $newObj->setMode($this->getMode());
3274 $newObj->set360SelfEvaluation($this->get360SelfEvaluation());
3275 $newObj->set360SelfAppraisee($this->get360SelfAppraisee());
3276 $newObj->set360SelfRaters($this->get360SelfRaters());
3277 $newObj->set360Results($this->get360Results());
3278 $newObj->setSkillService($this->getSkillService());
3279
3280 // reminder/notification
3281 $newObj->setReminderStatus($this->getReminderStatus());
3282 $newObj->setReminderStart($this->getReminderStart());
3283 $newObj->setReminderEnd($this->getReminderEnd());
3284 $newObj->setReminderFrequency($this->getReminderFrequency());
3285 $newObj->setReminderTarget($this->getReminderTarget());
3286 $newObj->setReminderTemplate($this->getReminderTemplate());
3287 // reminder_last_sent must not be copied!
3288 $newObj->setTutorNotificationStatus($this->getTutorNotificationStatus());
3289 $newObj->setTutorNotificationRecipients($this->getTutorNotificationRecipients());
3290 $newObj->setTutorNotificationTarget($this->getTutorNotificationTarget());
3291 $newObj->setTutorResultsStatus($this->getTutorResultsStatus());
3292 $newObj->setTutorResultsRecipients($this->getTutorResultsRecipients());
3293
3294 $newObj->setMailNotification($this->getMailNotification());
3295 $newObj->setMailAddresses($this->getMailAddresses());
3296 $newObj->setMailParticipantData($this->getMailParticipantData());
3297
3298 $question_pointer = array();
3299 // clone the questions
3300 $mapping = array();
3301
3302 foreach ($this->questions as $key => $question_id) {
3304 $question = self::_instanciateQuestion($question_id);
3305 if ($question) { // #10824
3306 $question->id = -1;
3307 $original_id = SurveyQuestion::_getOriginalId($question_id, false);
3308 $question->setObjId($newObj->getId());
3309 $question->saveToDb($original_id);
3310 $newObj->questions[$key] = $question->getId();
3311 $question_pointer[$question_id] = $question->getId();
3312 $mapping[$question_id] = $question->getId();
3313 }
3314 }
3315
3316 //copy online status if object is not the root copy object
3317 $cp_options = ilCopyWizardOptions::_getInstance($copy_id);
3318
3319 if (!$cp_options->isRootNode($this->getRefId())) {
3320 $newObj->setOfflineStatus($this->getOfflineStatus());
3321 }
3322
3323 $newObj->saveToDb();
3324 $newObj->cloneTextblocks($mapping);
3325
3326 // #14929
3327 if (($svy_type === self::MODE_360 || $svy_type === self::MODE_SELF_EVAL) &&
3328 $this->getSkillService()) {
3329 $src_skills = new ilSurveySkill($this);
3330 $tgt_skills = new ilSurveySkill($newObj);
3331
3332 foreach ($mapping as $src_qst_id => $tgt_qst_id) {
3333 $qst_skill = $src_skills->getSkillForQuestion($src_qst_id);
3334 if ($qst_skill) {
3335 $tgt_skills->addQuestionSkillAssignment($tgt_qst_id, $qst_skill["base_skill_id"], $qst_skill["tref_id"]);
3336 }
3337 }
3338
3339 $thresholds = new ilSurveySkillThresholds($this);
3340 $thresholds->cloneTo($newObj, $mapping);
3341 }
3342
3343 // clone the questionblocks
3344 $questionblocks = array();
3345 $questionblock_questions = array();
3346 $result = $ilDB->queryF(
3347 "SELECT * FROM svy_qblk_qst WHERE survey_fi = %s",
3348 array('integer'),
3349 array($this->getSurveyId())
3350 );
3351 if ($result->numRows() > 0) {
3352 while ($row = $ilDB->fetchAssoc($result)) {
3353 $questionblock_questions[] = $row;
3354 $questionblocks[$row["questionblock_fi"]] = $row["questionblock_fi"];
3355 }
3356 }
3357 // create new questionblocks
3358 foreach ($questionblocks as $key => $value) {
3359 $questionblock = self::_getQuestionblock($key);
3360 $questionblock_id = self::_addQuestionblock(
3361 (string) $questionblock["title"],
3362 (int) $questionblock["owner_fi"],
3363 (bool) $questionblock["show_questiontext"],
3364 (bool) $questionblock["show_blocktitle"],
3365 (bool) $questionblock["compress_view"]
3366 );
3367 $questionblocks[$key] = $questionblock_id;
3368 }
3369 // create new questionblock questions
3370 foreach ($questionblock_questions as $key => $value) {
3371 if ($questionblocks[$value["questionblock_fi"]] &&
3372 $question_pointer[$value["question_fi"]]) {
3373 $next_id = $ilDB->nextId('svy_qblk_qst');
3374 $affectedRows = $ilDB->manipulateF(
3375 "INSERT INTO svy_qblk_qst (qblk_qst_id, survey_fi, questionblock_fi, question_fi) " .
3376 "VALUES (%s, %s, %s, %s)",
3377 array('integer','integer','integer','integer'),
3378 array($next_id, $newObj->getSurveyId(), $questionblocks[$value["questionblock_fi"]], $question_pointer[$value["question_fi"]])
3379 );
3380 }
3381 }
3382
3383 // clone the constraints
3384 $constraints = self::_getConstraints($this->getSurveyId());
3385 $newConstraints = array();
3386 foreach ($constraints as $key => $constraint) {
3387 if ($question_pointer[$constraint["for_question"]] &&
3388 $question_pointer[$constraint["question"]]) {
3389 if (!array_key_exists($constraint['id'], $newConstraints)) {
3390 $constraint_id = $newObj->addConstraint($question_pointer[$constraint["question"]], $constraint["relation_id"], $constraint["value"], $constraint['conjunction']);
3391 $newConstraints[$constraint['id']] = $constraint_id;
3392 }
3393 $newObj->addConstraintToQuestion($question_pointer[$constraint["for_question"]], $newConstraints[$constraint['id']]);
3394 }
3395 }
3396
3397 // #16210 - clone LP settings
3398 $obj_settings = new ilLPObjSettings($this->getId());
3399 $obj_settings->cloneSettings($newObj->getId());
3400 unset($obj_settings);
3401
3402 return $newObj;
3403 }
3404
3408 public function getTextblock(
3409 int $question_id
3410 ): string {
3411 $ilDB = $this->db;
3412 $result = $ilDB->queryF(
3413 "SELECT * FROM svy_svy_qst WHERE question_fi = %s",
3414 array('integer'),
3415 array($question_id)
3416 );
3417 if ($row = $ilDB->fetchAssoc($result)) {
3418 return $row["heading"] ?? "";
3419 } else {
3420 return "";
3421 }
3422 }
3423
3429 public function cloneTextblocks(
3430 array $mapping
3431 ): void {
3432 foreach ($mapping as $original_id => $new_id) {
3433 $textblock = $this->getTextblock($original_id);
3434 $this->saveHeading(ilUtil::stripSlashes($textblock, true, ilRTESettings::_getUsedHTMLTagsAsString("survey")), $new_id);
3435 }
3436 }
3437
3444 public function createExportDirectory(): void
3445 {
3446 $svy_data_dir = ilFileUtils::getDataDir() . "/svy_data";
3447 ilFileUtils::makeDir($svy_data_dir);
3448 if (!is_writable($svy_data_dir)) {
3449 throw new ilSurveyException("Survey Data Directory (" . $svy_data_dir . ") not writeable.");
3450 }
3451
3452 // create learning module directory (data_dir/lm_data/lm_<id>)
3453 $svy_dir = $svy_data_dir . "/svy_" . $this->getId();
3454 ilFileUtils::makeDir($svy_dir);
3455 if (!is_dir($svy_dir)) {
3456 throw new ilSurveyException("Creation of Survey Directory failed.");
3457 }
3458 // create Export subdirectory (data_dir/lm_data/lm_<id>/Export)
3459 $export_dir = $svy_dir . "/export";
3460 ilFileUtils::makeDir($export_dir);
3461 if (!is_dir($export_dir)) {
3462 throw new ilSurveyException("Creation of Export Directory failed.");
3463 }
3464 }
3465
3469 public function getExportDirectory(): string
3470 {
3471 $export_dir = ilFileUtils::getDataDir() . "/svy_data" . "/svy_" . $this->getId() . "/export";
3472
3473 return $export_dir;
3474 }
3475
3482 public function createImportDirectory(): void
3483 {
3484 $svy_data_dir = ilFileUtils::getDataDir() . "/svy_data";
3485 ilFileUtils::makeDir($svy_data_dir);
3486
3487 if (!is_writable($svy_data_dir)) {
3488 throw new ilSurveyException("Survey Data Directory (" . $svy_data_dir . ") not writeable.");
3489 }
3490
3491 // create test directory (data_dir/svy_data/svy_<id>)
3492 $svy_dir = $svy_data_dir . "/svy_" . $this->getId();
3493 ilFileUtils::makeDir($svy_dir);
3494 if (!is_dir($svy_dir)) {
3495 throw new ilSurveyException("Creation of Survey Directory failed.");
3496 }
3497
3498 // create import subdirectory (data_dir/svy_data/svy_<id>/import)
3499 $import_dir = $svy_dir . "/import";
3500 ilFileUtils::makeDir($import_dir);
3501 if (!is_dir($import_dir)) {
3502 throw new ilSurveyException("Creation of Import Directory failed.");
3503 }
3504 }
3505
3510 public function getImportDirectory(): string
3511 {
3512 $import_dir = ilFileUtils::getDataDir() . "/svy_data" .
3513 "/svy_" . $this->getId() . "/import";
3514 if (!is_dir($import_dir)) {
3515 ilFileUtils::makeDirParents($import_dir);
3516 }
3517 if (is_dir($import_dir)) {
3518 return $import_dir;
3519 } else {
3520 return "";
3521 }
3522 }
3523
3529 public function saveHeading(
3530 string $heading,
3531 int $insertbefore
3532 ): void {
3533 $ilDB = $this->db;
3534 if ($heading) {
3535 $ilDB->manipulateF(
3536 "UPDATE svy_svy_qst SET heading=%s WHERE survey_fi=%s AND question_fi=%s",
3537 array('text','integer','integer'),
3538 array($heading, $this->getSurveyId(), $insertbefore)
3539 );
3540 } else {
3541 $ilDB->manipulateF(
3542 "UPDATE svy_svy_qst SET heading=%s WHERE survey_fi=%s AND question_fi=%s",
3543 array('text','integer','integer'),
3544 array(null, $this->getSurveyId(), $insertbefore)
3545 );
3546 }
3547 }
3548
3553 public function isAnonymizedParticipant(string $key): bool
3554 {
3555 $ilDB = $this->db;
3556
3557 $result = $ilDB->queryF(
3558 "SELECT finished_id FROM svy_finished WHERE anonymous_id = %s AND survey_fi = %s",
3559 array('text','integer'),
3560 array($key, $this->getSurveyId())
3561 );
3562 return $result->numRows() === 1;
3563 }
3564
3575 ?array $a_codes = null,
3576 ?array $a_ids = null
3577 ): string {
3578 $ilDB = $this->db;
3579 $ilUser = $this->user;
3580 $lng = $this->lng;
3581
3582 $sql = "SELECT svy_anonymous.*, svy_finished.state" .
3583 " FROM svy_anonymous" .
3584 " LEFT JOIN svy_finished ON (svy_anonymous.survey_key = svy_finished.anonymous_id)" .
3585 " WHERE svy_anonymous.survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") .
3586 " AND svy_anonymous.user_key IS NULL";
3587
3588 if ($a_codes) {
3589 $sql .= " AND " . $ilDB->in("svy_anonymous.survey_key", $a_codes, "", "text");
3590 } elseif ($a_ids) {
3591 $sql .= " AND " . $ilDB->in("svy_anonymous.anonymous_id", $a_ids, "", "text");
3592 }
3593
3594 $export = array();
3595
3596 // #14905
3597 $titles = array();
3598 $titles[] = '"' . $lng->txt("survey_code") . '"';
3599 $titles[] = '"' . $lng->txt("email") . '"';
3600 $titles[] = '"' . $lng->txt("lastname") . '"';
3601 $titles[] = '"' . $lng->txt("firstname") . '"';
3602 $titles[] = '"' . $lng->txt("create_date") . '"';
3603 $titles[] = '"' . $lng->txt("used") . '"';
3604 $titles[] = '"' . $lng->txt("mail_sent_short") . '"';
3605 $titles[] = '"' . $lng->txt("survey_code_url") . '"';
3606 $export[] = implode(";", $titles);
3607
3608 $result = $ilDB->query($sql);
3609 $default_lang = $ilUser->getPref("survey_code_language");
3610 while ($row = $ilDB->fetchAssoc($result)) {
3611 $item = array();
3612 $item[] = $row["survey_key"];
3613
3614 if ($row["externaldata"]) {
3615 $ext = unserialize((string) $row["externaldata"], ['allowed_classes' => false]);
3616 $item[] = $ext["email"];
3617 $item[] = $ext["lastname"];
3618 $item[] = $ext["firstname"];
3619 } else {
3620 $item[] = "";
3621 $item[] = "";
3622 $item[] = "";
3623 }
3624
3625 // No relative (today, tomorrow...) dates in export.
3626 $date = new ilDateTime($row['tstamp'], IL_CAL_UNIX);
3627 $item[] = $date->get(IL_CAL_DATETIME);
3628
3629 $item[] = ($this->isSurveyCodeUsed($row["survey_key"])) ? 1 : 0;
3630 $item[] = ($row["sent"]) ? 1 : 0;
3631
3632 $params = array("accesscode" => $row["survey_key"]);
3633 if ($default_lang) {
3634 $params["lang"] = $default_lang;
3635 }
3636 $item[] = ilLink::_getLink($this->getRefId(), "svy", $params);
3637
3638 $export[] = '"' . implode('";"', $item) . '"';
3639 }
3640 return implode("\n", $export);
3641 }
3642
3649 ?array $ids = null,
3650 ?string $lang = null
3651 ): array {
3652 $ilDB = $this->db;
3653
3654 $codes = array();
3655
3656 $sql = "SELECT svy_anonymous.*, svy_finished.state" .
3657 " FROM svy_anonymous" .
3658 " LEFT JOIN svy_finished ON (svy_anonymous.survey_key = svy_finished.anonymous_id)" .
3659 " WHERE svy_anonymous.survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") /*.
3660 " AND svy_anonymous.user_key IS NULL" */; // #15860
3661
3662 if ($ids) {
3663 $sql .= " AND " . $ilDB->in("svy_anonymous.anonymous_id", $ids, "", "integer");
3664 }
3665
3666 $sql .= " ORDER BY tstamp, survey_key ASC";
3667 $result = $ilDB->query($sql);
3668 if ($result->numRows() > 0) {
3669 while ($row = $ilDB->fetchAssoc($result)) {
3670 $href = "";
3671 $used = false;
3672 if ($this->isSurveyCodeUsed($row["survey_key"])) {
3673 $used = true;
3674 } else {
3675 $params = array("accesscode" => $row["survey_key"]);
3676 if ($lang) {
3677 $params["lang"] = $lang;
3678 }
3679 $href = ilLink::_getLink($this->getRefId(), "svy", $params);
3680 }
3681
3682
3683 $item = array(
3684 'id' => $row["anonymous_id"],
3685 'code' => $row["survey_key"],
3686 'date' => $row["tstamp"],
3687 'used' => $used,
3688 'sent' => $row['sent'],
3689 'href' => $href,
3690 'email' => '',
3691 'last_name' => '',
3692 'first_name' => ''
3693 );
3694
3695 if ($row["externaldata"]) {
3696 $ext = unserialize((string) $row["externaldata"], ['allowed_classes' => false]);
3697 $item['email'] = $ext['email'] ?? "";
3698 $item['last_name'] = $ext['lastname'] ?? "";
3699 $item['first_name'] = $ext['firstname'] ?? "";
3700 }
3701
3702 $codes[] = $item;
3703 }
3704 }
3705 return $codes;
3706 }
3707
3711 public function isSurveyCodeUsed(
3712 string $code
3713 ): bool {
3714 $ilDB = $this->db;
3715 $result = $ilDB->queryF(
3716 "SELECT finished_id FROM svy_finished WHERE survey_fi = %s AND anonymous_id = %s",
3717 array('integer','text'),
3718 array($this->getSurveyId(), $code)
3719 );
3720 return $result->numRows() > 0;
3721 }
3722
3726 public function isSurveyCodeUnique(
3727 string $code
3728 ): bool {
3729 $ilDB = $this->db;
3730 $result = $ilDB->queryF(
3731 "SELECT anonymous_id FROM svy_anonymous WHERE survey_fi = %s AND survey_key = %s",
3732 array('integer','text'),
3733 array($this->getSurveyId(), $code)
3734 );
3735 return !(($result->numRows() > 0));
3736 }
3737
3741 public function importSurveyCode(
3742 string $a_anonymize_key,
3743 int $a_created,
3744 array $a_data
3745 ): void {
3746 $ilDB = $this->db;
3747
3748 $next_id = $ilDB->nextId('svy_anonymous');
3749 $ilDB->manipulateF(
3750 "INSERT INTO svy_anonymous (anonymous_id, survey_key, survey_fi, externaldata, tstamp) " .
3751 "VALUES (%s, %s, %s, %s, %s)",
3752 array('integer','text','integer','text','integer'),
3753 array($next_id, $a_anonymize_key, $this->getSurveyId(), serialize($a_data), $a_created)
3754 );
3755 }
3756
3760 public function sendCodes(
3761 int $not_sent,
3762 string $subject,
3763 string $message,
3764 string $lang
3765 ): void {
3766 /*
3767 * 0 = all
3768 * 1 = not sent
3769 * 2 = finished
3770 * 3 = not finished
3771 */
3772 $check_finished = ($not_sent > 1);
3773
3774
3775 $mail = new ilMail(ANONYMOUS_USER_ID);
3776 $recipients = $this->getExternalCodeRecipients($check_finished);
3777 foreach ($recipients as $data) {
3778 if ($data['email'] && $data['code']) {
3779 $do_send = false;
3780 switch ($not_sent) {
3781 case 1:
3782 $do_send = !(bool) $data['sent'];
3783 break;
3784
3785 case 2:
3786 $do_send = $data['finished'];
3787 break;
3788
3789 case 3:
3790 $do_send = !$data['finished'];
3791 break;
3792
3793 default:
3794 $do_send = true;
3795 break;
3796 }
3797 if ($do_send) {
3798 // build text
3799 $messagetext = $message;
3800 $url = ilLink::_getLink(
3801 $this->getRefId(),
3802 "svy",
3803 array(
3804 "accesscode" => $data["code"],
3805 "lang" => $lang
3806 )
3807 );
3808 $messagetext = str_replace('[url]', $url, $messagetext);
3809 foreach ($data as $key => $value) {
3810 $messagetext = str_replace('[' . $key . ']', $value, $messagetext);
3811 }
3812
3813 // send mail
3814 $mail->enqueue(
3815 $data['email'], // to
3816 "", // cc
3817 "", // bcc
3818 $subject, // subject
3819 $messagetext, // message
3820 array() // attachments
3821 );
3822 }
3823 }
3824 }
3825
3826 $ilDB = $this->db;
3827 $ilDB->manipulateF(
3828 "UPDATE svy_anonymous SET sent = %s WHERE survey_fi = %s AND externaldata IS NOT NULL",
3829 array('integer','integer'),
3830 array(1, $this->getSurveyId())
3831 );
3832 }
3833
3838 bool $a_check_finished = false
3839 ): array {
3840 $ilDB = $this->db;
3841 $result = $ilDB->queryF(
3842 "SELECT survey_key code, externaldata, sent FROM svy_anonymous WHERE survey_fi = %s",
3843 array('integer'),
3844 array($this->getSurveyId())
3845 );
3846 $res = array();
3847 while ($row = $ilDB->fetchAssoc($result)) {
3848 if (!$row['externaldata']) {
3849 continue;
3850 }
3851
3852 $externaldata = unserialize((string) $row['externaldata'], ['allowed_classes' => false]);
3853 if (!$externaldata['email']) {
3854 continue;
3855 }
3856
3857 $externaldata['code'] = $row['code'];
3858 $externaldata['sent'] = $row['sent'];
3859
3860 if ($a_check_finished) {
3861 #23294
3862 //$externaldata['finished'] = $this->isSurveyCodeUsed($row['code']);
3863 $externaldata['finished'] = $this->isSurveyFinishedByCode($row['code']);
3864 }
3865
3866 $res[] = $externaldata;
3867 }
3868 return $res;
3869 }
3870
3877 public function isSurveyFinishedByCode(string $a_code): bool
3878 {
3879 $result = $this->db->queryF(
3880 "SELECT state FROM svy_finished WHERE survey_fi = %s AND anonymous_id = %s",
3881 array('integer','text'),
3882 array($this->getSurveyId(), $a_code)
3883 );
3884
3885 $row = $this->db->fetchAssoc($result);
3886
3887 return $row['state'] ?? false;
3888 }
3889
3893 public function deleteSurveyCode(
3894 string $survey_code
3895 ): void {
3896 $ilDB = $this->db;
3897
3898 if ($survey_code !== '') {
3899 $affectedRows = $ilDB->manipulateF(
3900 "DELETE FROM svy_anonymous WHERE survey_fi = %s AND survey_key = %s",
3901 array('integer', 'text'),
3902 array($this->getSurveyId(), $survey_code)
3903 );
3904 }
3905 }
3906
3913 public function getUserAccessCode(int $user_id): string
3914 {
3915 $ilDB = $this->db;
3916 $access_code = "";
3917 $result = $ilDB->queryF(
3918 "SELECT survey_key FROM svy_anonymous WHERE survey_fi = %s AND user_key = %s",
3919 array('integer','text'),
3920 array($this->getSurveyId(), md5($user_id))
3921 );
3922 if ($result->numRows()) {
3923 $row = $ilDB->fetchAssoc($result);
3924 $access_code = $row["survey_key"];
3925 }
3926 return $access_code;
3927 }
3928
3935 public function saveUserAccessCode(
3936 int $user_id,
3937 string $access_code
3938 ): void {
3939 $ilDB = $this->db;
3940
3941 // not really sure what to do about ANONYMOUS_USER_ID
3942
3943 $next_id = $ilDB->nextId('svy_anonymous');
3944 $affectedRows = $ilDB->manipulateF(
3945 "INSERT INTO svy_anonymous (anonymous_id, survey_key, survey_fi, user_key, tstamp) " .
3946 "VALUES (%s, %s, %s, %s, %s)",
3947 array('integer','text', 'integer', 'text', 'integer'),
3948 array($next_id, $access_code, $this->getSurveyId(), md5($user_id), time())
3949 );
3950 }
3951
3952
3956 public function getLastAccess(
3957 int $finished_id
3958 ): ?int {
3959 $ilDB = $this->db;
3960
3961 $result = $ilDB->queryF(
3962 "SELECT tstamp FROM svy_answer WHERE active_fi = %s ORDER BY tstamp DESC",
3963 array('integer'),
3964 array($finished_id)
3965 );
3966 if ($result->numRows()) {
3967 $row = $ilDB->fetchAssoc($result);
3968 return (int) $row["tstamp"];
3969 } else {
3970 $result = $ilDB->queryF(
3971 "SELECT tstamp FROM svy_finished WHERE finished_id = %s",
3972 array('integer'),
3973 array($finished_id)
3974 );
3975 if ($result->numRows()) {
3976 $row = $ilDB->fetchAssoc($result);
3977 return (int) $row["tstamp"];
3978 }
3979 }
3980 return null;
3981 }
3982
3986 public function prepareTextareaOutput(
3987 string $txt_output
3988 ): string {
3989 return ilLegacyFormElementsUtil::prepareTextareaOutput($txt_output);
3990 }
3991
3996 public function isHTML(
3997 string $a_text
3998 ): bool {
3999 if (preg_match("/<[^>]*?>/", $a_text)) {
4000 return true;
4001 } else {
4002 return false;
4003 }
4004 }
4005
4010 public function addMaterialTag(
4011 ilXmlWriter $a_xml_writer,
4012 string $a_material,
4013 bool $close_material_tag = true,
4014 bool $add_mobs = true,
4015 ?array $attribs = null
4016 ): void {
4017 $a_xml_writer->xmlStartTag("material", $attribs);
4018 $attrs = array(
4019 "type" => "text/plain"
4020 );
4021 if ($this->isHTML($a_material)) {
4022 $attrs["type"] = "text/xhtml";
4023 }
4024 $mattext = ilRTE::_replaceMediaObjectImageSrc($a_material, 0);
4025 $a_xml_writer->xmlElement("mattext", $attrs, $mattext);
4026
4027 if ($add_mobs) {
4028 $mobs = ilObjMediaObject::_getMobsOfObject("svy:html", $this->getId());
4029 foreach ($mobs as $mob) {
4030 $mob_id = "il_" . IL_INST_ID . "_mob_" . $mob;
4031 if (strpos($mattext, $mob_id) !== false) {
4032 $mob_obj = new ilObjMediaObject($mob);
4033 $imgattrs = array(
4034 "label" => $mob_id,
4035 "uri" => "objects/" . "il_" . IL_INST_ID . "_mob_" . $mob . "/" . $mob_obj->getTitle(),
4036 "type" => "svy:html",
4037 "id" => $this->getId()
4038 );
4039 $a_xml_writer->xmlElement("matimage", $imgattrs, null);
4040 }
4041 }
4042 }
4043 if ($close_material_tag) {
4044 $a_xml_writer->xmlEndTag("material");
4045 }
4046 }
4047
4055 public function canExportSurveyCode(): bool
4056 {
4057 if ($this->getAnonymize() !== self::ANONYMIZE_OFF) {
4058 if ($this->surveyCodeSecurity === false) {
4059 return true;
4060 }
4061 }
4062 return false;
4063 }
4064
4065 public function setSurveyId(int $survey_id): void
4066 {
4067 $this->survey_id = $survey_id;
4068 }
4069
4077 public function getUserData(
4078 array $ids
4079 ): array {
4080 $ilDB = $this->db;
4081
4082 if (count($ids) === 0) {
4083 return array();
4084 }
4085
4086 $result = $ilDB->query("SELECT usr_id, login, lastname, firstname FROM usr_data WHERE " . $ilDB->in('usr_id', $ids, false, 'integer') . " ORDER BY login");
4087 $result_array = array();
4088 while ($row = $ilDB->fetchAssoc($result)) {
4089 $result_array[$row["usr_id"]] = $row;
4090 }
4091 return $result_array;
4092 }
4093
4097 public function getMailNotification(): bool
4098 {
4099 return $this->mailnotification;
4100 }
4101
4105 public function setMailNotification(bool $a_notification): void
4106 {
4107 $this->mailnotification = $a_notification;
4108 }
4109
4113 public function getMailAddresses(): string
4114 {
4115 return $this->mailaddresses;
4116 }
4117
4121 public function setMailAddresses(string $a_addresses): void
4122 {
4123 $this->mailaddresses = $a_addresses;
4124 }
4125
4129 public function getMailParticipantData(): string
4130 {
4131 return $this->mailparticipantdata;
4132 }
4133
4137 public function setMailParticipantData(string $a_data): void
4138 {
4139 $this->mailparticipantdata = $a_data;
4140 }
4141
4146 int $finished_id
4147 ): int {
4148 $ilDB = $this->db;
4149
4150 $result = $ilDB->queryF(
4151 "SELECT * FROM svy_times WHERE finished_fi = %s",
4152 array('integer'),
4153 array($finished_id)
4154 );
4155 $total = 0;
4156 while ($row = $ilDB->fetchAssoc($result)) {
4157 if ($row['left_page'] > 0 && $row['entered_page'] > 0) {
4158 $total += $row['left_page'] - $row['entered_page'];
4159 }
4160 }
4161 return $total;
4162 }
4163
4164 public function updateOrder(
4165 array $a_order
4166 ): void {
4167 if (count($this->questions) === count($a_order)) {
4168 $this->questions = array_flip($a_order);
4169 $this->saveQuestionsToDb();
4170 }
4171 }
4172
4173 public function getPoolUsage(): bool
4174 {
4175 return $this->pool_usage;
4176 }
4177
4178 public function setPoolUsage(bool $a_value): void
4179 {
4180 $this->pool_usage = $a_value;
4181 }
4182
4186 public function updateCode(
4187 int $a_id,
4188 string $a_email,
4189 string $a_last_name,
4190 string $a_first_name,
4191 int $a_sent
4192 ): bool {
4193 $ilDB = $this->db;
4194
4195 $a_email = trim($a_email);
4196
4197 // :TODO:
4198 if (($a_email && !ilUtil::is_email($a_email)) || $a_email === "") {
4199 return false;
4200 }
4201
4202 $data = array("email" => $a_email,
4203 "lastname" => trim($a_last_name),
4204 "firstname" => trim($a_first_name));
4205
4206 $fields = array(
4207 "externaldata" => array("text", serialize($data)),
4208 "sent" => array("integer", $a_sent)
4209 );
4210
4211 $ilDB->update(
4212 "svy_anonymous",
4213 $fields,
4214 array("anonymous_id" => array("integer", $a_id))
4215 );
4216
4217 return true;
4218 }
4219
4220
4221 //
4222 // 360°
4223 //
4224
4225 public function get360Mode(): bool
4226 {
4227 if ($this->getMode() === self::MODE_360) {
4228 return true;
4229 }
4230 return false;
4231 }
4232
4233 public function set360SelfEvaluation(bool $a_value): void
4234 {
4235 $this->mode_360_self_eval = $a_value;
4236 }
4237
4238 public function get360SelfEvaluation(): bool
4239 {
4240 return $this->mode_360_self_eval;
4241 }
4242
4243 public function set360SelfAppraisee(bool $a_value): void
4244 {
4245 $this->mode_360_self_appr = $a_value;
4246 }
4247
4248 public function get360SelfAppraisee(): bool
4249 {
4250 return $this->mode_360_self_appr;
4251 }
4252
4253 public function set360SelfRaters(bool $a_value): void
4254 {
4255 $this->mode_360_self_rate = $a_value;
4256 }
4257
4258 public function get360SelfRaters(): bool
4259 {
4260 return $this->mode_360_self_rate;
4261 }
4262
4263 public function set360Results(int $a_value): void
4264 {
4265 $this->mode_360_results = $a_value;
4266 }
4267
4268 public function get360Results(): int
4269 {
4270 return $this->mode_360_results;
4271 }
4272
4276 public function addAppraisee(
4277 int $a_user_id
4278 ): void {
4279 global $DIC;
4280
4281 $ilDB = $DIC->database();
4282 $access = $DIC->access();
4283
4284 if (!$this->isAppraisee($a_user_id) &&
4285 $a_user_id !== ANONYMOUS_USER_ID) {
4286 $fields = array(
4287 "obj_id" => array("integer", $this->getSurveyId()),
4288 "user_id" => array("integer", $a_user_id)
4289 );
4290 $ilDB->insert("svy_360_appr", $fields);
4291
4292 // send notification and add to desktop
4293 if ($access->checkAccessOfUser($a_user_id, "read", "", $this->getRefId())) {
4294 $this->sendAppraiseeNotification($a_user_id);
4295 }
4296 }
4297 }
4298
4303 int $a_user_id
4304 ): void {
4305 $ntf = new ilSystemNotification();
4306 $ntf->setLangModules(array("svy", "survey"));
4307 $ntf->setRefId($this->getRefId());
4308 $ntf->setGotoLangId('url');
4309
4310 // user specific language
4311 $lng = $ntf->getUserLanguage($a_user_id);
4312
4313 $ntf->setIntroductionLangId("svy_user_added_appraisee_mail");
4314 $subject = str_replace("%1", $this->getTitle(), $lng->txt("svy_user_added_appraisee"));
4315
4316 // #10044
4317 $mail = new ilMail(ANONYMOUS_USER_ID);
4318 $mail->enqueue(
4319 ilObjUser::_lookupLogin($a_user_id),
4320 "",
4321 "",
4322 $subject,
4323 $ntf->composeAndGetMessage($a_user_id, null, "read", true),
4324 []
4325 );
4326 }
4327
4332 int $a_user_id
4333 ): void {
4334 $ntf = new ilSystemNotification();
4335 $ntf->setLangModules(array("svy", "survey"));
4336 $ntf->setRefId($this->getRefId());
4337 $ntf->setGotoLangId('url');
4338
4339 // user specific language
4340 $lng = $ntf->getUserLanguage($a_user_id);
4341
4342 $ntf->setIntroductionLangId("svy_user_added_appraisee_close_mail");
4343 $subject = str_replace("%1", $this->getTitle(), $lng->txt("svy_user_added_appraisee"));
4344
4345 // #10044
4346 $mail = new ilMail(ANONYMOUS_USER_ID);
4347 $mail->enqueue(
4348 ilObjUser::_lookupLogin($a_user_id),
4349 "",
4350 "",
4351 $subject,
4352 $ntf->composeAndGetMessage($a_user_id, null, "read", true),
4353 []
4354 );
4355 }
4356
4360 public function sendRaterNotification(
4361 int $a_user_id,
4362 int $a_appraisee_id
4363 ): void {
4364 $ntf = new ilSystemNotification();
4365 $ntf->setLangModules(array("svy", "survey"));
4366 $ntf->setRefId($this->getRefId());
4367 $ntf->setGotoLangId('url');
4368
4369 // user specific language
4370 $lng = $ntf->getUserLanguage($a_user_id);
4371
4372 $ntf->setIntroductionLangId("svy_user_added_rater_mail");
4373 $subject = str_replace("%1", $this->getTitle(), $lng->txt("svy_user_added_rater"));
4374 $ntf->addAdditionalInfo("survey_360_appraisee", ilUserUtil::getNamePresentation($a_appraisee_id, false, false, "", true));
4375
4376 // #10044
4377 $mail = new ilMail(ANONYMOUS_USER_ID);
4378 $mail->enqueue(
4379 ilObjUser::_lookupLogin($a_user_id),
4380 "",
4381 "",
4382 $subject,
4383 $ntf->composeAndGetMessage($a_user_id, null, "read", true),
4384 []
4385 );
4386 }
4387
4391 public function isAppraisee(
4392 int $a_user_id
4393 ): bool {
4394 $ilDB = $this->db;
4395 $set = $ilDB->query("SELECT user_id" .
4396 " FROM svy_360_appr" .
4397 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer") .
4398 " AND user_id = " . $ilDB->quote($a_user_id, "integer"));
4399 return (bool) $ilDB->numRows($set);
4400 }
4401
4405 public function isAppraiseeClosed(
4406 int $a_user_id
4407 ): bool {
4408 $ilDB = $this->db;
4409
4410 $set = $ilDB->query("SELECT has_closed" .
4411 " FROM svy_360_appr" .
4412 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer") .
4413 " AND user_id = " . $ilDB->quote($a_user_id, "integer"));
4414 $row = $ilDB->fetchAssoc($set);
4415 return (bool) ($row["has_closed"] ?? false);
4416 }
4417
4421 public function deleteAppraisee(
4422 int $a_user_id
4423 ): void {
4424 $ilDB = $this->db;
4425
4426 $ilDB->manipulate("DELETE FROM svy_360_appr" .
4427 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer") .
4428 " AND user_id = " . $ilDB->quote($a_user_id, "integer"));
4429
4430 $set = $ilDB->query("SELECT user_id" .
4431 " FROM svy_360_rater" .
4432 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer") .
4433 " AND appr_id = " . $ilDB->quote($a_user_id, "integer"));
4434 while ($row = $ilDB->fetchAssoc($set)) {
4435 $this->deleteRater($a_user_id, $row["user_id"]);
4436 }
4437 // appraisee will not be part of raters table
4438 if ($this->get360SelfEvaluation()) {
4439 $this->deleteRater($a_user_id, $a_user_id);
4440 }
4441 }
4442
4446 public function getAppraiseesData(): array
4447 {
4448 $ilDB = $this->db;
4449
4450 $res = array();
4451
4452 $set = $ilDB->query("SELECT * FROM svy_360_appr" .
4453 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer"));
4454 while ($row = $ilDB->fetchAssoc($set)) {
4455 $name = ilObjUser::_lookupName($row["user_id"]);
4456 $name["email"] = ilObjUser::_lookupEmail($row["user_id"]);
4457 $name["name"] = $name["lastname"] . ", " . $name["firstname"];
4458 $res[$row["user_id"]] = $name;
4459
4460 $finished = 0;
4461 $raters = $this->getRatersData($row["user_id"]);
4462 foreach ($raters as $rater) {
4463 if ($rater["finished"]) {
4464 $finished++;
4465 }
4466 }
4467 $res[$row["user_id"]]["finished"] = $finished . "/" . count($raters);
4468 $res[$row["user_id"]]["closed"] = $row["has_closed"];
4469 }
4470
4471 return $res;
4472 }
4473
4477 public function addRater(
4478 int $a_appraisee_id,
4479 int $a_user_id,
4480 int $a_anonymous_id = 0
4481 ): void {
4482 global $DIC;
4483
4484 $ilDB = $DIC->database();
4485 $access = $DIC->access();
4486
4487 if ($this->isAppraisee($a_appraisee_id) &&
4488 !$this->isRater($a_appraisee_id, $a_user_id, $a_anonymous_id)) {
4489 $fields = array(
4490 "obj_id" => array("integer", $this->getSurveyId()),
4491 "appr_id" => array("integer", $a_appraisee_id),
4492 "user_id" => array("integer", $a_user_id),
4493 "anonymous_id" => array("integer", $a_anonymous_id)
4494 );
4495 $ilDB->insert("svy_360_rater", $fields);
4496
4497 // send notification and add to desktop
4498 if ($access->checkAccessOfUser($a_user_id, "read", "", $this->getRefId())) {
4499 // out-commented, since adding raters will end in a mail
4500 // form to send the mail "manually"
4501 // otherwise two mails would be sent (tested in individual feedback)
4502 //$this->sendRaterNotification($a_user_id, $a_appraisee_id);
4503 }
4504 }
4505 }
4506
4510 public function isRater(
4511 int $a_appraisee_id,
4512 int $a_user_id,
4513 int $a_anonymous_id = 0
4514 ): bool {
4515 $ilDB = $this->db;
4516
4517 // user is rater if already appraisee and active self-evaluation
4518 if ($this->isAppraisee($a_user_id) &&
4519 $this->get360SelfEvaluation() &&
4520 (!$a_appraisee_id || $a_appraisee_id === $a_user_id)) {
4521 return true;
4522 }
4523
4524 // :TODO: should we get rid of code as well?
4525
4526 $sql = "SELECT user_id" .
4527 " FROM svy_360_rater" .
4528 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer") .
4529 " AND user_id = " . $ilDB->quote($a_user_id, "integer") .
4530 " AND anonymous_id = " . $ilDB->quote($a_anonymous_id, "integer");
4531 if ($a_appraisee_id) {
4532 $sql .= " AND appr_id = " . $ilDB->quote($a_appraisee_id, "integer");
4533 }
4534 $set = $ilDB->query($sql);
4535 return (bool) $ilDB->numRows($set);
4536 }
4537
4541 public function deleteRater(
4542 int $a_appraisee_id,
4543 int $a_user_id,
4544 int $a_anonymous_id = 0
4545 ): void {
4546 $ilDB = $this->db;
4547
4548 $finished_id = $this->getFinishedIdForAppraiseeIdAndRaterId($a_appraisee_id, $a_user_id);
4549 if ($finished_id) {
4550 $this->removeSelectedSurveyResults(array($finished_id));
4551 }
4552
4553 $ilDB->manipulate("DELETE FROM svy_360_rater" .
4554 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer") .
4555 " AND appr_id = " . $ilDB->quote($a_appraisee_id, "integer") .
4556 " AND user_id = " . $ilDB->quote($a_user_id, "integer") .
4557 " AND anonymous_id = " . $ilDB->quote($a_anonymous_id, "integer"));
4558 }
4559
4563 public function getRatersData(
4564 int $a_appraisee_id
4565 ): array {
4566 $ilDB = $this->db;
4567
4568 $res = $anonymous_ids = array();
4569
4570 $set = $ilDB->query("SELECT * FROM svy_360_rater" .
4571 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer") .
4572 " AND appr_id = " . $ilDB->quote($a_appraisee_id, "integer"));
4573 while ($row = $ilDB->fetchAssoc($set)) {
4574 if ($row["anonymous_id"]) {
4575 $res["a" . $row["anonymous_id"]] = array(
4576 "lastname" => "unknown code " . $row["anonymous_id"],
4577 "sent" => $row["mail_sent"],
4578 "finished" => null
4579 );
4580 $anonymous_ids[] = $row["anonymous_id"];
4581 } else {
4582 $name = ilObjUser::_lookupName($row["user_id"]);
4583 $name["name"] = $name["lastname"] . ", " . $name["firstname"];
4584 $name["user_id"] = "u" . $name["user_id"];
4585 $name["email"] = ilObjUser::_lookupEmail($row["user_id"]);
4586 $name["sent"] = $row["mail_sent"];
4587 $name["finished"] = (bool) $this->is360SurveyStarted($a_appraisee_id, (int) $row["user_id"]);
4588 $res["u" . $row["user_id"]] = $name;
4589 }
4590 }
4591
4592 if (count($anonymous_ids)) {
4593 $data = $this->getSurveyCodesTableData($anonymous_ids);
4594 foreach ($data as $item) {
4595 if (isset($res["a" . $item["id"]])) {
4596 $res["a" . $item["id"]] = array(
4597 "user_id" => "a" . $item["id"],
4598 "lastname" => $item["last_name"],
4599 "firstname" => $item["first_name"],
4600 "name" => $item["last_name"] . ", " . $item["first_name"],
4601 "login" => "",
4602 "email" => $item["email"],
4603 "code" => $item["code"],
4604 "href" => $item["href"],
4605 "sent" => $res["a" . $item["id"]]["sent"],
4606 "finished" => (bool) $this->is360SurveyStarted($a_appraisee_id, 0, $item["code"])
4607 );
4608 }
4609 }
4610 }
4611
4612 return $res;
4613 }
4614
4619 public function getAppraiseesToRate(
4620 ?int $a_user_id,
4621 ?int $a_anonymous_id = null
4622 ): array {
4623 $ilDB = $this->db;
4624
4625 $res = array();
4626
4627 $sql = "SELECT appr_id FROM svy_360_rater" .
4628 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer");
4629
4630 if ($a_user_id) {
4631 $sql .= " AND user_id = " . $ilDB->quote($a_user_id, "integer");
4632 } else {
4633 $sql .= " AND anonymous_id = " . $ilDB->quote($a_anonymous_id, "integer");
4634 }
4635
4636 $set = $ilDB->query($sql);
4637 while ($row = $ilDB->fetchAssoc($set)) {
4638 $res[] = (int) $row["appr_id"];
4639 }
4640
4641 // user may evaluate himself if already appraisee
4642 if ($this->get360SelfEvaluation() &&
4643 $this->isAppraisee((int) $a_user_id) &&
4644 !in_array($a_user_id, $res)) {
4645 $res[] = $a_user_id;
4646 }
4647
4648 return $res;
4649 }
4650
4654 public function getAnonymousIdByCode(
4655 string $a_code
4656 ): ?int {
4657 $ilDB = $this->db;
4658 $set = $ilDB->query("SELECT anonymous_id FROM svy_anonymous" .
4659 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") .
4660 " AND survey_key = " . $ilDB->quote($a_code, "text"));
4661 $res = $ilDB->fetchAssoc($set);
4662 return $res["anonymous_id"] ?? null;
4663 }
4664
4668 public function is360SurveyStarted(
4669 int $appr_id,
4670 int $user_id,
4671 ?string $anonymous_code = null
4672 ): ?int {
4673 $ilDB = $this->db;
4674
4675 $sql = "SELECT * FROM svy_finished" .
4676 " WHERE survey_fi =" . $ilDB->quote($this->getSurveyId(), "integer") .
4677 " AND appr_id = " . $ilDB->quote($appr_id, "integer");
4678 if ($user_id) {
4679 $sql .= " AND user_fi = " . $ilDB->quote($user_id, "integer");
4680 } else {
4681 $sql .= " AND anonymous_id = " . $ilDB->quote($anonymous_code, "text");
4682 }
4683 $result = $ilDB->query($sql);
4684 if ($result->numRows() === 0) {
4685 return null;
4686 } else {
4687 $row = $ilDB->fetchAssoc($result);
4688 return (int) $row["state"];
4689 }
4690 }
4691
4701 ?string $a_code = null
4702 ): ?array {
4703 $ilUser = $this->user;
4704 $ilDB = $this->db;
4705 $user_id = $ilUser->getId();
4706 // code is obligatory?
4707 if (!$this->isAccessibleWithoutCode()) {
4708 if (!$a_code) {
4709 // registered raters do not need code
4710 if ($this->feature_config->usesAppraisees() &&
4712 $this->isRater(0, $user_id)) {
4713 // auto-generate code
4714 $code = $this->data_manager->code("")
4715 ->withUserId($user_id);
4716 $this->code_manager->add($code);
4717 $a_code = $this->code_manager->getByUserId($user_id);
4718 } else {
4719 return null;
4720 }
4721 }
4722 } elseif ($user_id === ANONYMOUS_USER_ID ||
4723 $this->getAnonymize() === self::ANONYMIZE_FREEACCESS) {
4724 // self::ANONYMIZE_FREEACCESS: anonymized, no codes
4725 // or anonymous user when no codes are used
4726 if (!$a_code) {
4727 // auto-generate code
4728 $code = $this->data_manager->code("")
4729 ->withUserId($user_id);
4730 $code_id = $this->code_manager->add($code);
4731 $a_code = $this->code_manager->getByCodeId($code_id);
4732 }
4733 } else {
4734 $a_code = null;
4735 }
4736
4737 $res = array();
4738
4739 // get runs for user id / code
4740 $sql = "SELECT * FROM svy_finished" .
4741 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer");
4742 // if proper user id is given, use it or current code
4743 if ($user_id !== ANONYMOUS_USER_ID) {
4744 $sql .= " AND (user_fi = " . $ilDB->quote($user_id, "integer") .
4745 " OR anonymous_id = " . $ilDB->quote($a_code, "text") . ")";
4746 }
4747 // use anonymous code to find finished id(s)
4748 else {
4749 $sql .= " AND anonymous_id = " . $ilDB->quote($a_code, "text");
4750 }
4751 $set = $ilDB->query($sql);
4752 while ($row = $ilDB->fetchAssoc($set)) {
4753 $res[$row["finished_id"]] = array("appr_id" => $row["appr_id"],
4754 "user_id" => $row["user_fi"],
4755 "code" => $row["anonymous_id"],
4756 "finished" => (bool) $row["state"]);
4757 }
4758 return array("code" => $a_code, "runs" => $res);
4759 }
4760
4764 public function findCodeForUser(
4765 int $a_user_id
4766 ): string {
4767 $ilDB = $this->db;
4768
4769 if ($a_user_id !== ANONYMOUS_USER_ID) {
4770 $set = $ilDB->query("SELECT sf.anonymous_id FROM svy_finished sf" .
4771 " WHERE sf.survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") .
4772 " AND sf.user_fi = " . $ilDB->quote($a_user_id, "integer"));
4773 $a_code = $ilDB->fetchAssoc($set);
4774 return (string) ($a_code["anonymous_id"] ?? "");
4775 }
4776 return "";
4777 }
4778
4782 public function isUnusedCode(
4783 string $a_code,
4784 int $a_user_id
4785 ): bool {
4786 $ilDB = $this->db;
4787
4788 $set = $ilDB->query("SELECT user_fi FROM svy_finished" .
4789 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") .
4790 " AND anonymous_id = " . $ilDB->quote($a_code, "text"));
4791 $user_id = $ilDB->fetchAssoc($set);
4792 $user_id = (int) $user_id["user_fi"];
4793
4794 if ($user_id && ($user_id !== $a_user_id || $user_id === ANONYMOUS_USER_ID)) {
4795 return false;
4796 }
4797 return true;
4798 }
4799
4805 int $a_appr_id,
4806 bool $a_exclude_appraisee = false
4807 ): array {
4808 $ilDB = $this->db;
4809
4810 $res = array();
4811
4812 $set = $ilDB->query("SELECT finished_id, user_fi FROM svy_finished" .
4813 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") .
4814 " AND appr_id = " . $ilDB->quote($a_appr_id, "integer"));
4815 while ($row = $ilDB->fetchAssoc($set)) {
4816 if ($a_exclude_appraisee && $row["user_fi"] == $a_appr_id) {
4817 continue;
4818 }
4819 $res[] = (int) $row["finished_id"];
4820 }
4821
4822 return $res;
4823 }
4824
4830 int $a_appr_id,
4831 int $a_rat_id
4832 ): ?int {
4833 $ilDB = $this->db;
4834
4835 $set = $ilDB->query("SELECT finished_id, user_fi FROM svy_finished" .
4836 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") .
4837 " AND appr_id = " . $ilDB->quote($a_appr_id, "integer") .
4838 " AND user_fi = " . $ilDB->quote($a_rat_id, "integer"));
4839 if ($row = $ilDB->fetchAssoc($set)) {
4840 return (int) $row["finished_id"];
4841 }
4842 return null;
4843 }
4844
4845
4846 // 360° using competence/skill service
4847
4848 public function setSkillService(bool $a_val): void
4849 {
4850 $this->mode_skill_service = $a_val;
4851 }
4852
4853 public function getSkillService(): bool
4854 {
4855 return $this->mode_skill_service;
4856 }
4857
4861 public function set360RaterSent(
4862 int $a_appraisee_id,
4863 int $a_user_id,
4864 int $a_anonymous_id,
4865 ?int $a_tstamp = null
4866 ): void {
4867 $ilDB = $this->db;
4868
4869 if (!$a_tstamp) {
4870 $a_tstamp = time();
4871 }
4872
4873 $ilDB->manipulate("UPDATE svy_360_rater" .
4874 " SET mail_sent = " . $ilDB->quote($a_tstamp, "integer") .
4875 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer") .
4876 " AND appr_id = " . $ilDB->quote($a_appraisee_id, "integer") .
4877 " AND user_id = " . $ilDB->quote($a_user_id, "integer") .
4878 " AND anonymous_id = " . $ilDB->quote($a_anonymous_id, "integer"));
4879 }
4880
4884 public function closeAppraisee(
4885 int $a_user_id
4886 ): void {
4887 global $DIC;
4888
4889 $ilDB = $DIC->database();
4890 $user = $DIC->user();
4891
4892 // close the appraisee
4893 $ilDB->manipulate("UPDATE svy_360_appr" .
4894 " SET has_closed = " . $ilDB->quote(time(), "integer") .
4895 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer") .
4896 " AND user_id = " . $ilDB->quote($a_user_id, "integer"));
4897
4898 // write competences
4899 $skmg_set = new ilSkillManagementSettings();
4900 if ($this->getSkillService() && $skmg_set->isActivated()) {
4901 $sskill = new ilSurveySkill($this);
4902 $sskill->writeAndAddAppraiseeSkills($a_user_id);
4903 }
4904
4905 // send notification
4906 if ($user->getId() !== $a_user_id) {
4907 $this->sendAppraiseeCloseNotification($a_user_id);
4908 }
4909 }
4910
4914 public function openAllAppraisees(): void
4915 {
4916 $ilDB = $this->db;
4917
4918 $ilDB->manipulate("UPDATE svy_360_appr" .
4919 " SET has_closed = " . $ilDB->quote(null, "integer") .
4920 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer"));
4921 }
4922
4926 public static function validateExternalRaterCode(
4927 int $a_ref_id,
4928 string $a_code
4929 ): bool {
4931 global $DIC;
4932
4933 $anonym_repo = $DIC->survey()
4934 ->internal()
4935 ->repo()
4936 ->execution()
4937 ->runSession();
4938
4939 if (!$anonym_repo->isExternalRaterValidated($a_ref_id)) {
4940 $svy = new self($a_ref_id);
4941 $svy->loadFromDb();
4942
4943 $domain_service = $DIC->survey()->internal()->domain();
4944 $code_manager = $domain_service->code($svy, 0);
4945 $feature_config = $domain_service->modeFeatureConfig($svy->getMode());
4946 $access_manager = $domain_service->access($a_ref_id, 0);
4947
4948 if ($access_manager->canStartSurvey() &&
4950 $code_manager->exists($a_code)) {
4951 $anonymous_id = $svy->getAnonymousIdByCode($a_code);
4952 if ($anonymous_id) {
4953 if (count($svy->getAppraiseesToRate(null, $anonymous_id))) {
4954 $anonym_repo->setExternalRaterValidation($a_ref_id, true);
4955 return true;
4956 }
4957 }
4958 }
4959 $anonym_repo->setExternalRaterValidation($a_ref_id, false);
4960 return false;
4961 }
4962
4963 return $anonym_repo->isExternalRaterValidated($a_ref_id);
4964 }
4965
4966
4967 //
4968 // reminder/notification
4969 //
4970
4971 public function getReminderStatus(): bool
4972 {
4973 return $this->reminder_status;
4974 }
4975
4976 public function setReminderStatus(bool $a_value): void
4977 {
4978 $this->reminder_status = $a_value;
4979 }
4980
4981 public function getReminderStart(): ?ilDate
4982 {
4983 return $this->reminder_start;
4984 }
4985
4986 public function setReminderStart(?ilDate $a_value = null): void
4987 {
4988 $this->reminder_start = $a_value;
4989 }
4990
4991 public function getReminderEnd(): ?ilDate
4992 {
4993 return $this->reminder_end;
4994 }
4995
4996 public function setReminderEnd(?ilDate $a_value = null): void
4997 {
4998 $this->reminder_end = $a_value;
4999 }
5000
5001 public function getReminderFrequency(): int
5002 {
5003 return $this->reminder_frequency;
5004 }
5005
5006 public function setReminderFrequency(int $a_value): void
5007 {
5008 $this->reminder_frequency = $a_value;
5009 }
5010
5011 public function getReminderTarget(): int
5012 {
5013 return $this->reminder_target;
5014 }
5015
5016 public function setReminderTarget(int $a_value): void
5017 {
5018 $this->reminder_target = $a_value;
5019 }
5020
5021 public function getReminderLastSent(): ?string
5022 {
5023 return $this->reminder_last_sent;
5024 }
5025
5026 public function setReminderLastSent(?string $a_value): void
5027 {
5028 if ($a_value == "") {
5029 $a_value = null;
5030 }
5031 $this->reminder_last_sent = $a_value;
5032 }
5033
5034 public function getReminderTemplate(
5035 bool $selectDefault = false
5036 ): ?int {
5037 if ($selectDefault) {
5038 $defaultTemplateId = 0;
5039 $this->getReminderMailTemplates($defaultTemplateId);
5040
5041 if ($defaultTemplateId > 0) {
5042 return $defaultTemplateId;
5043 }
5044 }
5045
5046 return $this->reminder_tmpl;
5047 }
5048
5049 public function setReminderTemplate(?int $a_value): void
5050 {
5051 $this->reminder_tmpl = $a_value;
5052 }
5053
5054 public function getTutorNotificationStatus(): bool
5055 {
5056 return $this->tutor_ntf_status;
5057 }
5058
5062 public function setTutorNotificationStatus(bool $a_value): void
5063 {
5064 $this->tutor_ntf_status = $a_value;
5065 }
5066
5070 public function getTutorNotificationRecipients(): array
5071 {
5072 return $this->tutor_ntf_recipients;
5073 }
5074
5078 public function setTutorNotificationRecipients(array $a_value): void
5079 {
5080 $this->tutor_ntf_recipients = $a_value;
5081 }
5082
5090 {
5091 return $this->tutor_ntf_target;
5092 }
5093
5094 public function setTutorNotificationTarget(int $a_value): void
5095 {
5096 $this->tutor_ntf_target = $a_value;
5097 }
5098
5099 public function getTutorResultsStatus(): bool
5100 {
5101 return $this->tutor_res_status;
5102 }
5103
5104 public function setTutorResultsStatus(bool $a_value): void
5105 {
5106 $this->tutor_res_status = $a_value;
5107 }
5108
5109 public function getTutorResultsRecipients(): array
5110 {
5111 return $this->tutor_res_recipients;
5112 }
5113
5114 public function setTutorResultsRecipients(array $a_value): void
5115 {
5116 $this->tutor_res_recipients = $a_value;
5117 }
5118
5123 protected function checkTutorNotification(): void
5124 {
5125 $ilDB = $this->db;
5126
5127 if ($this->getTutorNotificationStatus()) {
5128 // get target users, either parent course/group members or
5129 // user with the survey on the dashboard
5130 $user_ids = $this->getNotificationTargetUserIds(($this->getTutorNotificationTarget() === self::NOTIFICATION_INVITED_USERS));
5131 if ($user_ids) {
5132 $set = $ilDB->query("SELECT COUNT(*) numall FROM svy_finished" .
5133 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") .
5134 " AND state = " . $ilDB->quote(1, "integer") .
5135 " AND " . $ilDB->in("user_fi", $user_ids, "", "integer"));
5136 $row = $ilDB->fetchAssoc($set);
5137
5138 // all users finished the survey -> send notifications
5139 if ((int) $row["numall"] === count($user_ids)) {
5140 $this->sendTutorNotification();
5141 }
5142 }
5143 }
5144 }
5145
5149 public function sent360Reminders(): void
5150 {
5151 global $DIC;
5152
5153 $access = $DIC->access();
5154 // collect all open ratings
5155 $rater_ids = array();
5156 foreach ($this->getAppraiseesData() as $app) {
5157 $this->svy_log->debug("Handle appraisee " . $app['user_id']);
5158
5159 if (!$this->isAppraiseeClosed($app['user_id'])) {
5160 $this->svy_log->debug("Check self evaluation, self: " . $this->get360SelfAppraisee() . ", target: " . $this->getReminderTarget());
5161
5162 // self evaluation?
5163 if ($this->get360SelfEvaluation() &&
5164 in_array($this->getReminderTarget(), array(self::NOTIFICATION_APPRAISEES, self::NOTIFICATION_APPRAISEES_AND_RATERS))) {
5165 $this->svy_log->debug("...1");
5166 // did user already finished self evaluation?
5167 if (!$this->is360SurveyStarted((int) $app['user_id'], (int) $app['user_id'])) {
5168 $this->svy_log->debug("...2");
5169 if (!isset($rater_ids[$app['user_id']])) {
5170 $rater_ids[$app['user_id']] = array();
5171 }
5172 if (!isset($app["user_id"], $rater_ids[$app['user_id']])) {
5173 $rater_ids[$app['user_id']][] = $app["user_id"];
5174 }
5175 }
5176 }
5177
5178 $this->svy_log->debug("Check raters.");
5179
5180 // should raters be notified?
5181 if (
5182 in_array(
5183 $this->getReminderTarget(),
5184 array(self::NOTIFICATION_RATERS, self::NOTIFICATION_APPRAISEES_AND_RATERS),
5185 true
5186 )
5187 ) {
5188 foreach ($this->getRatersData($app['user_id']) as $rater) {
5189 $rater_id = 0;
5190 if ($rater["login"] !== "") {
5191 $rater_id = ilObjUser::_lookupId($rater["login"]);
5192 }
5193 if ($rater_id > 0) {
5194 // is rater not anonymous and did not rate yet?
5195 if (!($rater["anonymous_id"] ?? false) && !($rater["finished"] ?? false)) {
5196 if (!isset($rater_ids[$rater_id])) {
5197 $rater_ids[$rater_id] = array();
5198 }
5199 if (!in_array($app["user_id"], $rater_ids[$rater_id])) {
5200 $rater_ids[$rater_id][] = $app["user_id"];
5201 }
5202 }
5203 }
5204 }
5205 }
5206 }
5207 }
5208
5209 $this->svy_log->debug("Found raters:" . count($rater_ids));
5210
5211 foreach ($rater_ids as $id => $app) {
5212 if ($access->checkAccessOfUser((int) $id, "read", "", $this->getRefId())) {
5213 $this->send360ReminderToUser((int) $id, $app);
5214 }
5215 }
5216 }
5217
5218 public function send360ReminderToUser(
5219 int $a_user_id,
5220 array $a_appraisee_ids
5221 ): void {
5222 $this->svy_log->debug("Send mail to:" . $a_user_id);
5223
5224 $ntf = new ilSystemNotification();
5225 $ntf->setLangModules(array("svy", "survey"));
5226 $ntf->setRefId($this->getRefId());
5227 $ntf->setGotoLangId('url');
5228
5229 // user specific language
5230 $lng = $ntf->getUserLanguage($a_user_id);
5231
5232 $ntf->setIntroductionLangId("svy_user_added_rater_reminder_mail");
5233 $subject = str_replace("%1", $this->getTitle(), $lng->txt("svy_user_added_rater"));
5234
5235 foreach ($a_appraisee_ids as $appraisee_id) {
5236 $ntf->addAdditionalInfo("survey_360_appraisee", ilUserUtil::getNamePresentation($appraisee_id, false, false, "", true));
5237 }
5238
5239 // #10044
5240 $mail = new ilMail(ANONYMOUS_USER_ID);
5241 $mail->enqueue(
5242 ilObjUser::_lookupLogin($a_user_id),
5243 "",
5244 "",
5245 $subject,
5246 $ntf->composeAndGetMessage($a_user_id, null, "read", true),
5247 []
5248 );
5249 }
5250
5259 bool $a_use_invited
5260 ): array {
5261 $tree = $this->tree;
5262
5263 $user_ids = [];
5264 if ($a_use_invited) {
5265 $user_ids = $this->invitation_manager->getAllForSurvey($this->getSurveyId());
5266 } else {
5267 $parent_grp_ref_id = $tree->checkForParentType($this->getRefId(), "grp");
5268 if ($parent_grp_ref_id) {
5269 $part = new ilGroupParticipants(ilObject::_lookupObjId($parent_grp_ref_id));
5270 $user_ids = $part->getMembers();
5271 } else {
5272 $parent_crs_ref_id = $tree->checkForParentType($this->getRefId(), "crs");
5273 if ($parent_crs_ref_id) {
5274 $part = new ilCourseParticipants(ilObject::_lookupObjId($parent_crs_ref_id));
5275 $user_ids = $part->getMembers();
5276 }
5277 }
5278 }
5279 return $user_ids;
5280 }
5281
5285 protected function sendTutorNotification(): void
5286 {
5287 $link = ilLink::_getStaticLink($this->getRefId(), "svy");
5288
5289 // get tutors being set in the setting
5290 foreach ($this->getTutorNotificationRecipients() as $user_id) {
5291 // use language of recipient to compose message
5293 $ulng->loadLanguageModule('survey');
5294
5295 $subject = sprintf($ulng->txt('survey_notification_tutor_subject'), $this->getTitle());
5296 $message = sprintf($ulng->txt('survey_notification_tutor_salutation'), ilObjUser::_lookupFullname($user_id)) . "\n\n";
5297
5298 $message .= $ulng->txt('survey_notification_tutor_body') . ":\n\n";
5299 $message .= $ulng->txt('obj_svy') . ": " . $this->getTitle() . "\n";
5300 $message .= "\n" . $ulng->txt('survey_notification_tutor_link') . ": " . $link;
5301
5302 $mail_obj = new ilMail(ANONYMOUS_USER_ID);
5303 $mail_obj->appendInstallationSignature(true);
5304 $mail_obj->enqueue(
5306 "",
5307 "",
5308 $subject,
5309 $message,
5310 array()
5311 );
5312 }
5313 }
5314
5315 public function checkReminder(): ?int
5316 {
5317 $ilDB = $this->db;
5318 $ilAccess = $this->access;
5319
5320 $now = time();
5321 $now_with_format = date("YmdHis", $now);
5322 $today = date("Y-m-d");
5323
5324 $this->svy_log->debug("Check status and dates.");
5325
5326 // object settings / participation period
5327 if (
5328 $this->getOfflineStatus() ||
5329 !$this->getReminderStatus() ||
5330 ($this->getStartDate() && $now_with_format < $this->getStartDate()) ||
5331 ($this->getEndDate() && $now_with_format > $this->getEndDate())) {
5332 return null;
5333 }
5334
5335 // reminder period
5336 $start = $this->getReminderStart();
5337 if ($start) {
5338 $start = $start->get(IL_CAL_DATE);
5339 }
5340 $end = $this->getReminderEnd();
5341 if ($end) {
5342 $end = $end->get(IL_CAL_DATE);
5343 }
5344 if ($today < $start ||
5345 ($end && $today > $end)) {
5346 return null;
5347 }
5348
5349 $this->svy_log->debug("Check access period.");
5350
5351 // object access period
5352 $item_data = ilObjectActivation::getItem($this->getRefId());
5353 if ((int) $item_data["timing_type"] === ilObjectActivation::TIMINGS_ACTIVATION &&
5354 ($now < $item_data["timing_start"] ||
5355 $now > $item_data["timing_end"])) {
5356 return null;
5357 }
5358
5359 $this->svy_log->debug("Check frequency.");
5360
5361 // check frequency
5362 $cut = new ilDate($today, IL_CAL_DATE);
5363 $cut->increment(IL_CAL_DAY, $this->getReminderFrequency() * -1);
5364 if (!$this->getReminderLastSent() ||
5365 $cut->get(IL_CAL_DATE) >= substr($this->getReminderLastSent(), 0, 10)) {
5366 $missing_ids = array();
5367 if (!$this->feature_config->usesAppraisees()) {
5368 $this->svy_log->debug("Entering survey mode.");
5369
5370 // #16871
5371 $user_ids = $this->getNotificationTargetUserIds(($this->getReminderTarget() === self::NOTIFICATION_INVITED_USERS));
5372 if ($user_ids) {
5373 // gather participants who already finished
5374 $finished_ids = array();
5375 $set = $ilDB->query("SELECT user_fi FROM svy_finished" .
5376 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") .
5377 " AND state = " . $ilDB->quote(1, "text") .
5378 " AND " . $ilDB->in("user_fi", $user_ids, "", "integer"));
5379 while ($row = $ilDB->fetchAssoc($set)) {
5380 $finished_ids[] = $row["user_fi"];
5381 }
5382
5383 // some users missing out?
5384 $missing_ids = array_diff($user_ids, $finished_ids);
5385 if ($missing_ids) {
5386 foreach ($missing_ids as $idx => $user_id) {
5387 // should be able to participate
5388 if (!$ilAccess->checkAccessOfUser($user_id, "read", "", $this->getRefId(), "svy", $this->getId())) {
5389 unset($missing_ids[$idx]);
5390 }
5391 }
5392 }
5393 if ($missing_ids) {
5394 $this->sentReminder($missing_ids);
5395 }
5396 }
5397 } else {
5398 $this->svy_log->debug("Entering 360 mode.");
5399
5400 $this->sent360Reminders();
5401 }
5402
5403
5404 $this->setReminderLastSent($today);
5405 $this->saveToDb();
5406
5407 return count($missing_ids);
5408 }
5409
5410 return null;
5411 }
5412
5413 protected function sentReminder(
5414 array $a_recipient_ids
5415 ): void {
5416 global $DIC;
5417
5418 $link = "";
5419
5420 // use mail template
5421 if ($this->getReminderTemplate() &&
5422 array_key_exists($this->getReminderTemplate(), $this->getReminderMailTemplates())) {
5424 $templateService = $DIC->mail()->textTemplates();
5425 $tmpl = $templateService->loadTemplateForId($this->getReminderTemplate());
5426
5427 $tmpl_params = array(
5428 "ref_id" => $this->getRefId(),
5429 "ts" => time()
5430 );
5431 } else {
5432 $tmpl = null;
5433 $tmpl_params = null;
5434 $link = ilLink::_getStaticLink($this->getRefId(), "svy");
5435 }
5436
5437 foreach ($a_recipient_ids as $user_id) {
5438 if ($tmpl) {
5439 $subject = $tmpl->getSubject();
5440 $message = $this->sentReminderPlaceholders($tmpl->getMessage(), $user_id, $tmpl_params);
5441 }
5442 // use lng
5443 else {
5444 // use language of recipient to compose message
5446 $ulng->loadLanguageModule('survey');
5447
5448 $subject = sprintf($ulng->txt('survey_reminder_subject'), $this->getTitle());
5449 $message = sprintf($ulng->txt('survey_reminder_salutation'), ilObjUser::_lookupFullname($user_id)) . "\n\n";
5450
5451 $message .= $ulng->txt('survey_reminder_body') . ":\n\n";
5452 $message .= $ulng->txt('obj_svy') . ": " . $this->getTitle() . "\n";
5453 $message .= "\n" . $ulng->txt('survey_reminder_link') . ": " . $link;
5454 }
5455
5456 $mail_obj = new ilMail(ANONYMOUS_USER_ID);
5457 $mail_obj->appendInstallationSignature(true);
5458 $mail_obj->enqueue(
5460 "",
5461 "",
5462 $subject,
5463 $message,
5464 array()
5465 );
5466 }
5467 }
5468
5469 public function setActivationStartDate(
5470 ?int $starting_time = null
5471 ): void {
5472 $this->activation_starting_time = $starting_time;
5473 }
5474
5475 public function setActivationEndDate(
5476 ?int $ending_time = null
5477 ): void {
5478 $this->activation_ending_time = $ending_time;
5479 }
5480
5481 public function getActivationStartDate(): ?int
5482 {
5483 return $this->activation_starting_time;
5484 }
5485
5486 public function getActivationEndDate(): ?int
5487 {
5488 return $this->activation_ending_time;
5489 }
5490
5491 public function setViewOwnResults(bool $a_value): void
5492 {
5493 $this->view_own_results = $a_value;
5494 }
5495
5496 public function hasViewOwnResults(): bool
5497 {
5498 return $this->view_own_results;
5499 }
5500
5501 public function setMailOwnResults(bool $a_value): void
5502 {
5503 $this->mail_own_results = $a_value;
5504 }
5505
5506 public function hasMailOwnResults(): bool
5507 {
5508 return $this->mail_own_results;
5509 }
5510
5511 public function setMailConfirmation(bool $a_value): void
5512 {
5513 $this->mail_confirmation = $a_value;
5514 }
5515
5516 public function hasMailConfirmation(): bool
5517 {
5518 return $this->mail_confirmation;
5519 }
5520
5521 public function setAnonymousUserList(bool $a_value): void
5522 {
5523 $this->anon_user_list = $a_value;
5524 }
5525
5526 public function hasAnonymousUserList(): bool
5527 {
5528 return $this->anon_user_list;
5529 }
5530
5531 public static function getSurveySkippedValue(): string
5532 {
5533 global $DIC;
5534
5535 $lng = $DIC->language();
5536
5537 // #13541
5538
5539 $surveySetting = new ilSetting("survey");
5540 if (!$surveySetting->get("skipped_is_custom", false)) {
5541 return $lng->txt("skipped");
5542 } else {
5543 return $surveySetting->get("skipped_custom_value", "");
5544 }
5545 }
5546
5548 ?int &$defaultTemplateId = null
5549 ): array {
5550 global $DIC;
5551
5552 $res = array();
5553
5554 $templateService = $DIC->mail()->textTemplates();
5555 foreach ($templateService->loadTemplatesForContextId(ilSurveyMailTemplateReminderContext::ID) as $tmpl) {
5556 $res[$tmpl->getTplId()] = $tmpl->getTitle();
5557 if (null !== $defaultTemplateId && $tmpl->isDefault()) {
5558 $defaultTemplateId = $tmpl->getTplId();
5559 }
5560 }
5561
5562 return $res;
5563 }
5564
5565 protected function sentReminderPlaceholders(
5566 string $a_message,
5567 int $a_user_id,
5568 array $a_context_params
5569 ): string {
5570 // see ilMail::replacePlaceholders()
5571 try {
5572 $context = \ilMailTemplateContextService::getTemplateContextById(ilSurveyMailTemplateReminderContext::ID);
5573
5574 $user = new \ilObjUser($a_user_id);
5575
5576 $a_message = $this->placeholder_resolver->resolve($context, $a_message, $user, $a_context_params);
5577 } catch (\Exception $e) {
5578 ilLoggerFactory::getLogger('mail')->error(__METHOD__ . ' has been called with invalid context.');
5579 }
5580
5581 return $a_message;
5582 }
5583
5584 public function setMode(int $a_value): void
5585 {
5586 $this->mode = $a_value;
5587 }
5588
5589 public function getMode(): int
5590 {
5591 return $this->mode;
5592 }
5593
5594 public function setSelfEvaluationResults(int $a_value): void
5595 {
5596 $this->mode_self_eval_results = $a_value;
5597 }
5598
5600 {
5601 return $this->mode_self_eval_results;
5602 }
5603
5607 public static function getSurveysWithTutorResults(): array
5608 {
5609 global $ilDB;
5610
5611 $res = array();
5612
5614
5615
5616 $q = "SELECT obj_fi FROM svy_svy" .
5617 " WHERE tutor_res_cron IS NULL" .
5618 " AND tutor_res_status = " . $ilDB->quote(1, "integer") .
5619 " AND enddate < " . $ilDB->quote(date("Ymd000000"), "text");
5620
5621 if (DEVMODE) {
5622 $q = "SELECT obj_fi FROM svy_svy" .
5623 " WHERE tutor_res_status = " . $ilDB->quote(1, "integer") .
5624 " AND enddate < " . $ilDB->quote(date("Ymd000000"), "text");
5625 }
5626
5627 $set = $ilDB->query($q);
5628
5629 $log->debug($q);
5630
5631 while ($row = $ilDB->fetchAssoc($set)) {
5632 $res[] = (int) $row["obj_fi"];
5633 }
5634
5635 return $res;
5636 }
5637
5638 public function getMaxSumScore(): int
5639 {
5640 $sum_score = 0;
5642 $sum_score += call_user_func([$c, "getMaxSumScore"], $this->getSurveyId());
5643 }
5644 return $sum_score;
5645 }
5646}
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
$relation
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 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
Import class.
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.
setLangModules(array $a_modules)
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.
static _getMobsOfObject(string $a_type, int $a_id, int|false $a_usage_hist_nr=0, string $a_lang="-")
Class ilObjSurveyQuestionPool.
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
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.
getSurveyPages()
Returns the survey pages in an array (a page contains one or more questions)
getNextPage(int $active_page_question_id, int $direction)
Get current, previous or next page.
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)
is360SurveyStarted(int $appr_id, int $user_id, ?string $anonymous_code=null)
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)
getSurveyCodesTableData(?array $ids=null, ?string $lang=null)
Fetches the data for the survey codes table.
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.
set360RaterSent(int $a_appraisee_id, int $a_user_id, int $a_anonymous_id, ?int $a_tstamp=null)
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="")
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.
setActivationEndDate(?int $ending_time=null)
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)
InternalDomainService $domain
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.
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.
setActivationStartDate(?int $starting_time=null)
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)
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)
ilMailTemplatePlaceholderResolver $placeholder_resolver
addConstraint(int $if_question_id, int $relation, float $value, int $conjunction)
Adds a constraint.
getQuestionGUI(string $questiontype, int $question_id)
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()
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.
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.
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.
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)
getReminderMailTemplates(?int &$defaultTemplateId=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)
getAppraiseesToRate(?int $a_user_id, ?int $a_anonymous_id=null)
getUserSurveyExecutionStatus(?string $a_code=null)
Get current user execution status.
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)
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.
static _lookupFullname(int $a_user_id)
getFullname(int $max_strlen=0)
static _getUserData(array $a_internalids)
static getUserIdsByEmail(string $a_email)
static _lookupName(int $a_user_id)
static _lookupId(string|array $a_user_str)
static _lookupLogin(int $a_user_id)
static _lookupEmail(int $a_user_id)
Class ilObjectActivation.
static getItem(int $ref_id)
static getInstance(int $obj_id)
Class ilObject Basic functions for all objects.
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 _getUsedHTMLTagsAsString(string $module='')
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...
ILIAS Setting Class.
static subStr(string $a_str, int $a_start, ?int $a_length=null)
Definition: class.ilStr.php:21
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=null)
Default behaviour is:
static is_email(string $a_email, ?ilMailRfc822AddressParserFactory $mailAddressParserFactory=null)
This preg-based function checks whether an e-mail address is formally valid.
static stripSlashes(string $a_str, bool $a_strip_html=true, string $a_allow="")
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.
const IL_INST_ID
Definition: constants.php:40
const ANONYMOUS_USER_ID
Definition: constants.php:27
$c
Definition: deliver.php:25
return['delivery_method'=> 'php',]
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
usesAppraisees()
If raters rate single persons (appraisees) this mode is activated.
Interface ilAccessHandler This interface combines all available interfaces which can be called via gl...
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:31
$log
Definition: ltiresult.php:34
$res
Definition: ltiservices.php:69
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc
try
handle Lrs Init
Definition: xapiproxy.php:73
global $lng
Definition: privfeed.php:31
if(!file_exists('../ilias.ini.php'))
global $DIC
Definition: shib_login.php:26
$q
Definition: shib_logout.php:25
$url
Definition: shib_logout.php:70
$counter
$text
Definition: xapiexit.php:21