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