1<?php 2/* Copyright (c) 1998-2010 ILIAS open source, Extended GPL, see docs/LICENSE */ 3 4include_once "./Services/Object/classes/class.ilObject.php"; 5 6/** 7* Class ilObjSurvey 8* 9* @author Helmut Schottmüller <helmut.schottmueller@mac.com> 10* @version $Id$ 11* 12* @extends ilObject 13* @defgroup ModulesSurvey Modules/Survey 14*/ 15class ilObjSurvey extends ilObject 16{ 17 /** 18 * @var ilObjUser 19 */ 20 protected $user; 21 22 /** 23 * @var ilAccessHandler 24 */ 25 protected $access; 26 27 /** 28 * @var ilPluginAdmin 29 */ 30 protected $plugin_admin; 31 32 const EVALUATION_ACCESS_OFF = 0; 33 const EVALUATION_ACCESS_ALL = 1; 34 const EVALUATION_ACCESS_PARTICIPANTS = 2; 35 36 const INVITATION_OFF = 0; 37 const INVITATION_ON = 1; 38 39 const MODE_UNLIMITED = 0; 40 const MODE_PREDEFINED_USERS = 1; 41 42 const ANONYMIZE_OFF = 0; // personalized, no codes 43 const ANONYMIZE_ON = 1; // anonymized, codes 44 const ANONYMIZE_FREEACCESS = 2; // anonymized, no codes 45 const ANONYMIZE_CODE_ALL = 3; // personalized, codes 46 47 const QUESTIONTITLES_HIDDEN = 0; 48 const QUESTIONTITLES_VISIBLE = 1; 49 50 // constants to define the print view values. 51 const PRINT_HIDE_LABELS = 1; // Show only the titles in "print" and "PDF Export" 52 const PRINT_SHOW_LABELS = 3; // Show titles and labels in "print" and "PDF Export" 53 54 /** 55 * A unique positive numerical ID which identifies the survey. 56 * This is the primary key from a database table. 57 * 58 * @var integer 59 */ 60 public $survey_id; 61 62 /** 63 * A text representation of the authors name. The name of the author must 64 * not necessary be the name of the owner. 65 * 66 * @var string 67 */ 68 public $author; 69 70 /** 71 * A text representation of the surveys introduction. 72 * 73 * @var string 74 */ 75 public $introduction; 76 77 /** 78 * A text representation of the surveys outro. 79 * 80 * @var string 81 */ 82 public $outro; 83 84 85 /** 86 * Indicates the evaluation access for learners 87 * 88 * @var string 89 */ 90 public $evaluation_access; 91 92 /** 93 * The start date of the survey 94 * 95 * @var string 96 */ 97 public $start_date; 98 99 /** 100 * The end date of the survey 101 * 102 * @var string 103 */ 104 public $end_date; 105 106 /** 107 * The questions contained in this survey 108 * 109 * @var array 110 */ 111 public $questions; 112 113 /** 114 * Defines if the survey will be places on users personal desktops 115 * 116 * @var integer 117 */ 118 public $invitation; 119 120 /** 121 * Defines the type of user invitation 122 * 123 * @var integer 124 */ 125 public $invitation_mode; 126 127 /** 128 * Indicates the anonymization of the survey 129 * @var integer 130 */ 131 public $anonymize; 132 133 /** 134 * Indicates if the question titles are shown during a query 135 * @var integer 136 */ 137 public $display_question_titles; 138 139 /** 140 * Indicates if a survey code may be exported with the survey statistics 141 * 142 * @var boolean 143 **/ 144 public $surveyCodeSecurity; 145 146 public $mailnotification; 147 public $mailaddresses; 148 public $mailparticipantdata; 149 public $template_id; 150 public $pool_usage; 151 152 /** 153 * @var ilLogger 154 */ 155 protected $log; 156 157 protected $activation_visibility; 158 protected $activation_starting_time; 159 protected $activation_ending_time; 160 161 // 360° 162 protected $mode_360_self_eval; // [bool] 163 protected $mode_360_self_appr; // [bool] 164 protected $mode_360_self_rate; // [bool] 165 protected $mode_360_results; // [int] 166 protected $mode_skill_service; // [bool] 167 168 const RESULTS_360_NONE = 0; 169 const RESULTS_360_OWN = 1; 170 const RESULTS_360_ALL = 2; 171 172 // reminder/notification 173 protected $reminder_status; // [bool] 174 protected $reminder_start; // [ilDate] 175 protected $reminder_end; // [ilDate] 176 protected $reminder_frequency; // [int] 177 protected $reminder_target; // [int] 178 protected $reminder_last_sent; // [bool] 179 protected $reminder_tmpl; // [int] 180 protected $tutor_ntf_status; // [bool] 181 protected $tutor_ntf_recipients; // [array] 182 protected $tutor_ntf_target; // [int] 183 184 protected $view_own_results; // [bool] 185 protected $mail_own_results; // [bool] 186 protected $mail_confirmation; // [bool] 187 188 protected $anon_user_list; // [bool] 189 190 const NOTIFICATION_PARENT_COURSE = 1; 191 const NOTIFICATION_INVITED_USERS = 2; 192 const NOTIFICATION_APPRAISEES = 3; 193 const NOTIFICATION_RATERS = 4; 194 const NOTIFICATION_APPRAISEES_AND_RATERS = 5; 195 196 protected $mode; //[int] 197 protected $mode_self_eval_results; //[int] 198 199 //MODE TYPES 200 const MODE_STANDARD = 0; 201 const MODE_360 = 1; 202 const MODE_SELF_EVAL = 2; 203 204 //self evaluation only access to results 205 const RESULTS_SELF_EVAL_NONE = 0; 206 const RESULTS_SELF_EVAL_OWN = 1; 207 const RESULTS_SELF_EVAL_ALL = 2; 208 209 210 /** 211 * Constructor 212 * @access public 213 * @param integer reference_id or object_id 214 * @param boolean treat the id as reference_id (true) or object_id (false) 215 */ 216 public function __construct($a_id = 0, $a_call_by_reference = true) 217 { 218 global $DIC; 219 220 $this->user = $DIC->user(); 221 $this->lng = $DIC->language(); 222 $this->db = $DIC->database(); 223 $this->access = $DIC->access(); 224 $this->log = $DIC["ilLog"]; 225 $this->plugin_admin = $DIC["ilPluginAdmin"]; 226 $this->tree = $DIC->repositoryTree(); 227 $ilUser = $DIC->user(); 228 $lng = $DIC->language(); 229 230 $this->type = "svy"; 231 $this->survey_id = -1; 232 $this->introduction = ""; 233 $this->outro = $lng->txt("survey_finished"); 234 $this->author = $ilUser->getFullname(); 235 $this->evaluation_access = self::EVALUATION_ACCESS_OFF; 236 $this->questions = array(); 237 $this->invitation = self::INVITATION_OFF; 238 $this->invitation_mode = self::MODE_PREDEFINED_USERS; 239 $this->anonymize = self::ANONYMIZE_OFF; 240 $this->display_question_titles = self::QUESTIONTITLES_VISIBLE; 241 $this->surveyCodeSecurity = true; 242 $this->template_id = null; 243 $this->pool_usage = true; 244 $this->log = ilLoggerFactory::getLogger("svy"); 245 $this->mode = self::MODE_STANDARD; 246 $this->mode_self_eval_results = self::RESULTS_SELF_EVAL_OWN; 247 248 parent::__construct($a_id, $a_call_by_reference); 249 } 250 251 /** 252 * create survey object 253 */ 254 public function create($a_upload = false) 255 { 256 parent::create(); 257 if (!$a_upload) { 258 $this->createMetaData(); 259 } 260 $this->setOfflineStatus(true); 261 $this->update($a_upload); 262 } 263 264 /** 265 * Create meta data entry 266 * 267 * @access public 268 */ 269 public function createMetaData() 270 { 271 parent::createMetaData(); 272 $this->saveAuthorToMetadata(); 273 } 274 275 /** 276 * update object data 277 * 278 * @access public 279 * @return boolean 280 */ 281 public function update($a_upload = false) 282 { 283 if (!$a_upload) { 284 $this->updateMetaData(); 285 } 286 287 if (!parent::update()) { 288 return false; 289 } 290 291 // put here object specific stuff 292 293 return true; 294 } 295 296 public function createReference() 297 { 298 $result = parent::createReference(); 299 $this->saveToDb(); 300 return $result; 301 } 302 303 /** 304 * read object data from db into object 305 * @access public 306 */ 307 public function read() 308 { 309 parent::read(); 310 $this->loadFromDb(); 311 } 312 313 /** 314 * Adds a question to the survey (used in importer!) 315 * 316 * @param integer $question_id The question id of the question 317 * @access public 318 */ 319 public function addQuestion($question_id) 320 { 321 array_push($this->questions, $question_id); 322 } 323 324 /** 325 * delete object and all related data 326 * 327 * @access public 328 * @return boolean true if all object data were removed; false if only a references were removed 329 */ 330 public function delete() 331 { 332 if ($this->countReferences() == 1) { 333 $this->deleteMetaData(); 334 335 // Delete all survey questions, constraints and materials 336 foreach ($this->questions as $question_id) { 337 $this->removeQuestion($question_id); 338 } 339 $this->deleteSurveyRecord(); 340 341 ilUtil::delDir($this->getImportDirectory()); 342 } 343 344 $remove = parent::delete(); 345 346 // always call parent delete function first!! 347 if (!$remove) { 348 return false; 349 } 350 return true; 351 } 352 353 /** 354 * Deletes the survey from the database 355 * 356 * @access public 357 */ 358 public function deleteSurveyRecord() 359 { 360 $ilDB = $this->db; 361 362 $affectedRows = $ilDB->manipulateF( 363 "DELETE FROM svy_svy WHERE survey_id = %s", 364 array('integer'), 365 array($this->getSurveyId()) 366 ); 367 368 $result = $ilDB->queryF( 369 "SELECT questionblock_fi FROM svy_qblk_qst WHERE survey_fi = %s", 370 array('integer'), 371 array($this->getSurveyId()) 372 ); 373 $questionblocks = array(); 374 while ($row = $ilDB->fetchAssoc($result)) { 375 array_push($questionblocks, $row["questionblock_fi"]); 376 } 377 if (count($questionblocks)) { 378 $affectedRows = $ilDB->manipulate("DELETE FROM svy_qblk WHERE " . $ilDB->in('questionblock_id', $questionblocks, false, 'integer')); 379 } 380 $affectedRows = $ilDB->manipulateF( 381 "DELETE FROM svy_qblk_qst WHERE survey_fi = %s", 382 array('integer'), 383 array($this->getSurveyId()) 384 ); 385 $this->deleteAllUserData(false); 386 387 $affectedRows = $ilDB->manipulateF( 388 "DELETE FROM svy_anonymous WHERE survey_fi = %s", 389 array('integer'), 390 array($this->getSurveyId()) 391 ); 392 393 // delete export files 394 $svy_data_dir = ilUtil::getDataDir() . "/svy_data"; 395 $directory = $svy_data_dir . "/svy_" . $this->getId(); 396 if (is_dir($directory)) { 397 ilUtil::delDir($directory); 398 } 399 400 include_once("./Services/MediaObjects/classes/class.ilObjMediaObject.php"); 401 $mobs = ilObjMediaObject::_getMobsOfObject("svy:html", $this->getId()); 402 // remaining usages are not in text anymore -> delete them 403 // and media objects (note: delete method of ilObjMediaObject 404 // checks whether object is used in another context; if yes, 405 // the object is not deleted!) 406 foreach ($mobs as $mob) { 407 ilObjMediaObject::_removeUsage($mob, "svy:html", $this->getId()); 408 $mob_obj = new ilObjMediaObject($mob); 409 $mob_obj->delete(); 410 } 411 } 412 413 /** 414 * Deletes all user data of a survey 415 * 416 * @access public 417 * @param bool $reset_LP notice that the LP can only be reset it the determining components still exist 418 */ 419 public function deleteAllUserData($reset_LP = true) 420 { 421 $ilDB = $this->db; 422 423 $result = $ilDB->queryF( 424 "SELECT finished_id FROM svy_finished WHERE survey_fi = %s", 425 array('integer'), 426 array($this->getSurveyId()) 427 ); 428 $active_array = array(); 429 while ($row = $ilDB->fetchAssoc($result)) { 430 array_push($active_array, $row["finished_id"]); 431 } 432 433 $affectedRows = $ilDB->manipulateF( 434 "DELETE FROM svy_finished WHERE survey_fi = %s", 435 array('integer'), 436 array($this->getSurveyId()) 437 ); 438 439 foreach ($active_array as $active_fi) { 440 $affectedRows = $ilDB->manipulateF( 441 "DELETE FROM svy_answer WHERE active_fi = %s", 442 array('integer'), 443 array($active_fi) 444 ); 445 $affectedRows = $ilDB->manipulateF( 446 "DELETE FROM svy_times WHERE finished_fi = %s", 447 array('integer'), 448 array($active_fi) 449 ); 450 } 451 452 if ($reset_LP) { 453 include_once "Services/Object/classes/class.ilObjectLP.php"; 454 $lp_obj = ilObjectLP::getInstance($this->getId()); 455 $lp_obj->resetLPDataForCompleteObject(); 456 } 457 } 458 459 /** 460 * Deletes the user data of a given array of survey participants 461 * 462 * @access public 463 */ 464 public function removeSelectedSurveyResults($finished_ids) 465 { 466 $ilDB = $this->db; 467 468 $user_ids[] = array(); 469 470 foreach ($finished_ids as $finished_id) { 471 $result = $ilDB->queryF( 472 "SELECT finished_id FROM svy_finished WHERE finished_id = %s", 473 array('integer'), 474 array($finished_id) 475 ); 476 $row = $ilDB->fetchAssoc($result); 477 478 if ($row["user_fi"]) { 479 $user_ids[] = $row["user_fi"]; 480 } 481 482 $affectedRows = $ilDB->manipulateF( 483 "DELETE FROM svy_answer WHERE active_fi = %s", 484 array('integer'), 485 array($row["finished_id"]) 486 ); 487 488 $affectedRows = $ilDB->manipulateF( 489 "DELETE FROM svy_finished WHERE finished_id = %s", 490 array('integer'), 491 array($finished_id) 492 ); 493 494 $affectedRows = $ilDB->manipulateF( 495 "DELETE FROM svy_times WHERE finished_fi = %s", 496 array('integer'), 497 array($row["finished_id"]) 498 ); 499 } 500 501 if (sizeof($user_ids)) { 502 include_once "Services/Object/classes/class.ilObjectLP.php"; 503 $lp_obj = ilObjectLP::getInstance($this->getId()); 504 $lp_obj->resetLPDataForUserIds($user_ids); 505 } 506 } 507 508 public function &getSurveyParticipants($finished_ids = null, $force_non_anonymous = false) 509 { 510 $ilDB = $this->db; 511 512 $sql = "SELECT * FROM svy_finished" . 513 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer"); 514 if ($finished_ids) { 515 $sql .= " AND " . $ilDB->in("finished_id", $finished_ids, "", "integer"); 516 } 517 518 $result = $ilDB->query($sql); 519 $participants = array(); 520 if ($result->numRows() > 0) { 521 while ($row = $ilDB->fetchAssoc($result)) { 522 $userdata = $this->getUserDataFromActiveId($row["finished_id"], $force_non_anonymous); 523 $userdata["finished"] = (bool) $row["state"]; 524 $userdata["finished_tstamp"] = $row["tstamp"]; 525 $participants[$userdata["sortname"] . $userdata["active_id"]] = $userdata; 526 } 527 } 528 return $participants; 529 } 530 531 /** 532 * Returns 1, if a survey is complete for use 533 * 534 * @return boolean 1, if the survey is complete for use, otherwise 0 535 * @access public 536 */ 537 public function isComplete() 538 { 539 if (($this->getTitle()) and (count($this->questions))) { 540 return 1; 541 } else { 542 return 0; 543 } 544 } 545 546 /** 547 * Saves the completion status of the survey 548 * 549 * @access public 550 */ 551 public function saveCompletionStatus() 552 { 553 $ilDB = $this->db; 554 555 $complete = 0; 556 if ($this->isComplete()) { 557 $complete = 1; 558 } 559 if ($this->getSurveyId() > 0) { 560 $affectedRows = $ilDB->manipulateF( 561 "UPDATE svy_svy SET complete = %s, tstamp = %s WHERE survey_id = %s", 562 array('text','integer','integer'), 563 array($this->isComplete(), time(), $this->getSurveyId()) 564 ); 565 } 566 } 567 568 /** 569 * Takes a question and creates a copy of the question for use in the survey 570 * 571 * @param integer $question_id The database id of the question 572 * @result integer The database id of the copied question 573 * @access public 574 */ 575 public function duplicateQuestionForSurvey($question_id, $a_force = false) 576 { 577 $ilUser = $this->user; 578 579 $questiontype = $this->getQuestionType($question_id); 580 $question_gui = $this->getQuestionGUI($questiontype, $question_id); 581 582 // check if question is a pool question at all, if not do nothing 583 if ($this->getId() == $question_gui->object->getObjId() && !$a_force) { 584 return $question_id; 585 } 586 587 $duplicate_id = $question_gui->object->duplicate(true, "", "", "", $this->getId()); 588 return $duplicate_id; 589 } 590 591 /** 592 * Inserts a question in the survey and saves the relation to the database 593 * 594 * @access public 595 */ 596 public function insertQuestion($question_id) 597 { 598 $ilDB = $this->db; 599 600 $this->log->debug("insert question, id:" . $question_id); 601 602 include_once "./Modules/SurveyQuestionPool/classes/class.SurveyQuestion.php"; 603 if (!SurveyQuestion::_isComplete($question_id)) { 604 $this->log->debug("question is not complete"); 605 return false; 606 } else { 607 // get maximum sequence index in test 608 // @todo: refactor this 609 $result = $ilDB->queryF( 610 "SELECT survey_question_id FROM svy_svy_qst WHERE survey_fi = %s", 611 array('integer'), 612 array($this->getSurveyId()) 613 ); 614 $sequence = $result->numRows(); 615 $duplicate_id = $this->duplicateQuestionForSurvey($question_id); 616 $this->log->debug("duplicate, id: " . $question_id . ", duplicate id: " . $duplicate_id); 617 618 // check if question is not already in the survey, see #22018 619 if ($this->isQuestionInSurvey($duplicate_id)) { 620 return false; 621 } 622 623 $next_id = $ilDB->nextId('svy_svy_qst'); 624 $affectedRows = $ilDB->manipulateF( 625 "INSERT INTO svy_svy_qst (survey_question_id, survey_fi, question_fi, sequence, tstamp) VALUES (%s, %s, %s, %s, %s)", 626 array('integer', 'integer', 'integer', 'integer', 'integer'), 627 array($next_id, $this->getSurveyId(), $duplicate_id, $sequence, time()) 628 ); 629 630 $this->log->debug("added entry to svy_svy_qst, id: " . $next_id . ", question id: " . $duplicate_id . ", sequence: " . $sequence); 631 632 $this->loadQuestionsFromDb(); 633 return true; 634 } 635 } 636 637 /** 638 * Check if a question is already in the survey 639 * 640 * @param question id (as primary key from svy_question table) 641 * @return bool 642 */ 643 public function isQuestionInSurvey($a_question_fi) 644 { 645 global $DIC; 646 //return false; 647 $ilDB = $DIC->database(); 648 649 $set = $ilDB->query("SELECT * FROM svy_svy_qst " . 650 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") . 651 " AND question_fi = " . $ilDB->quote($a_question_fi, "integer")); 652 if ($rec = $ilDB->fetchAssoc($set)) { 653 return true; 654 } 655 return false; 656 } 657 658 659 660 /** 661 * Inserts a questionblock in the survey and saves the relation to the database 662 * 663 * @access public 664 */ 665 public function insertQuestionblock($questionblock_id) 666 { 667 $ilDB = $this->db; 668 $result = $ilDB->queryF( 669 "SELECT svy_qblk.title, svy_qblk.show_questiontext, svy_qblk.show_blocktitle," . 670 " svy_qblk_qst.question_fi FROM svy_qblk, svy_qblk_qst, svy_svy_qst" . 671 " WHERE svy_qblk.questionblock_id = svy_qblk_qst.questionblock_fi" . 672 " AND svy_svy_qst.question_fi = svy_qblk_qst.question_fi" . 673 " AND svy_qblk.questionblock_id = %s" . 674 " ORDER BY svy_svy_qst.sequence", 675 array('integer'), 676 array($questionblock_id) 677 ); 678 $questions = array(); 679 $show_questiontext = 0; 680 $show_blocktitle = 0; 681 while ($row = $ilDB->fetchAssoc($result)) { 682 $duplicate_id = $this->duplicateQuestionForSurvey($row["question_fi"]); 683 array_push($questions, $duplicate_id); 684 $title = $row["title"]; 685 $show_questiontext = $row["show_questiontext"]; 686 $show_blocktitle = $row["show_blocktitle"]; 687 } 688 $this->createQuestionblock($title, $show_questiontext, $show_blocktitle, $questions); 689 } 690 691 public function saveUserSettings($usr_id, $key, $title, $value) 692 { 693 $ilDB = $this->db; 694 695 $next_id = $ilDB->nextId('svy_settings'); 696 $affectedRows = $ilDB->insert("svy_settings", array( 697 "settings_id" => array("integer", $next_id), 698 "usr_id" => array("integer", $usr_id), 699 "keyword" => array("text", $key), 700 "title" => array("text", $title), 701 "value" => array("clob", $value) 702 )); 703 } 704 705 public function deleteUserSettings($id) 706 { 707 $ilDB = $this->db; 708 709 $affectedRows = $ilDB->manipulateF( 710 "DELETE FROM svy_settings WHERE settings_id = %s", 711 array('integer'), 712 array($id) 713 ); 714 return $affectedRows; 715 } 716 717 public function getUserSettings($usr_id, $key) 718 { 719 $ilDB = $this->db; 720 721 $result = $ilDB->queryF( 722 "SELECT * FROM svy_settings WHERE usr_id = %s AND keyword = %s", 723 array('integer', 'text'), 724 array($usr_id, $key) 725 ); 726 $found = array(); 727 if ($result->numRows()) { 728 while ($row = $ilDB->fetchAssoc($result)) { 729 $found[$row['settings_id']] = $row; 730 } 731 } 732 return $found; 733 } 734 735 /** 736 * Saves a survey object to a database 737 * 738 * @access public 739 */ 740 public function saveToDb() 741 { 742 $ilDB = $this->db; 743 744 // date handling 745 $rmd_start = $this->getReminderStart(); 746 if (is_object($rmd_start)) { 747 $rmd_start = $rmd_start->get(IL_CAL_DATE); 748 } 749 $rmd_end = $this->getReminderEnd(); 750 if (is_object($rmd_end)) { 751 $rmd_end = $rmd_end->get(IL_CAL_DATE); 752 } 753 754 include_once("./Services/RTE/classes/class.ilRTE.php"); 755 if ($this->getSurveyId() < 1) { 756 $next_id = $ilDB->nextId('svy_svy'); 757 $affectedRows = $ilDB->insert("svy_svy", array( 758 "survey_id" => array("integer", $next_id), 759 "obj_fi" => array("integer", $this->getId()), 760 "author" => array("text", $this->getAuthor()), 761 "introduction" => array("clob", ilRTE::_replaceMediaObjectImageSrc($this->getIntroduction(), 0)), 762 "outro" => array("clob", ilRTE::_replaceMediaObjectImageSrc($this->getOutro(), 0)), 763 "startdate" => array("text", $this->getStartDate()), 764 "enddate" => array("text", $this->getEndDate()), 765 "evaluation_access" => array("text", $this->getEvaluationAccess()), 766 "invitation" => array("text", $this->getInvitation()), 767 "invitation_mode" => array("text", $this->getInvitationMode()), 768 "complete" => array("text", $this->isComplete()), 769 "created" => array("integer", time()), 770 "anonymize" => array("text", $this->getAnonymize()), 771 "show_question_titles" => array("text", $this->getShowQuestionTitles()), 772 "mailnotification" => array('integer', ($this->getMailNotification()) ? 1 : 0), 773 "mailaddresses" => array('text', strlen($this->getMailAddresses()) ? $this->getMailAddresses() : null), 774 "mailparticipantdata" => array('text', strlen($this->getMailParticipantData()) ? $this->getMailParticipantData() : null), 775 "tstamp" => array("integer", time()), 776 "template_id" => array("integer", $this->getTemplate()), 777 "pool_usage" => array("integer", $this->getPoolUsage()), 778 // Mode type 779 "mode" => array("integer", $this->getMode()), 780 // 360° 781 "mode_360_self_eval" => array("integer", $this->get360SelfEvaluation()), 782 "mode_360_self_rate" => array("integer", $this->get360SelfRaters()), 783 "mode_360_self_appr" => array("integer", $this->get360SelfAppraisee()), 784 "mode_360_results" => array("integer", $this->get360Results()), 785 // competences 786 "mode_skill_service" => array("integer", (int) $this->getSkillService()), 787 // Self Evaluation Only 788 "mode_self_eval_results" => array("integer", ilObjSurvey::RESULTS_SELF_EVAL_OWN), 789 // reminder/notification 790 "reminder_status" => array("integer", (int) $this->getReminderStatus()), 791 "reminder_start" => array("datetime", $rmd_start), 792 "reminder_end" => array("datetime", $rmd_end), 793 "reminder_frequency" => array("integer", (int) $this->getReminderFrequency()), 794 "reminder_target" => array("integer", (int) $this->getReminderTarget()), 795 "reminder_last_sent" => array("datetime", $this->getReminderLastSent()), 796 "reminder_tmpl" => array("text", $this->getReminderTemplate(true)), 797 "tutor_ntf_status" => array("integer", (int) $this->getTutorNotificationStatus()), 798 "tutor_ntf_reci" => array("text", implode(";", (array) $this->getTutorNotificationRecipients())), 799 "tutor_ntf_target" => array("integer", (int) $this->getTutorNotificationTarget()), 800 "own_results_view" => array("integer", $this->hasViewOwnResults()), 801 "own_results_mail" => array("integer", $this->hasMailOwnResults()), 802 "confirmation_mail" => array("integer", $this->hasMailConfirmation()), 803 "anon_user_list" => array("integer", $this->hasAnonymousUserList()) 804 )); 805 $this->setSurveyId($next_id); 806 } else { 807 $affectedRows = $ilDB->update("svy_svy", array( 808 "author" => array("text", $this->getAuthor()), 809 "introduction" => array("clob", ilRTE::_replaceMediaObjectImageSrc($this->getIntroduction(), 0)), 810 "outro" => array("clob", ilRTE::_replaceMediaObjectImageSrc($this->getOutro(), 0)), 811 "startdate" => array("text", $this->getStartDate()), 812 "enddate" => array("text", $this->getEndDate()), 813 "evaluation_access" => array("text", $this->getEvaluationAccess()), 814 "invitation" => array("text", $this->getInvitation()), 815 "invitation_mode" => array("text", $this->getInvitationMode()), 816 "complete" => array("text", $this->isComplete()), 817 "anonymize" => array("text", $this->getAnonymize()), 818 "show_question_titles" => array("text", $this->getShowQuestionTitles()), 819 "mailnotification" => array('integer', ($this->getMailNotification()) ? 1 : 0), 820 "mailaddresses" => array('text', strlen($this->getMailAddresses()) ? $this->getMailAddresses() : null), 821 "mailparticipantdata" => array('text', strlen($this->getMailParticipantData()) ? $this->getMailParticipantData() : null), 822 "tstamp" => array("integer", time()), 823 "template_id" => array("integer", $this->getTemplate()), 824 "pool_usage" => array("integer", $this->getPoolUsage()), 825 //MODE TYPE 826 "mode" => array("integer", $this->getMode()), 827 // 360° 828 "mode_360_self_eval" => array("integer", $this->get360SelfEvaluation()), 829 "mode_360_self_rate" => array("integer", $this->get360SelfRaters()), 830 "mode_360_self_appr" => array("integer", $this->get360SelfAppraisee()), 831 "mode_360_results" => array("integer", $this->get360Results()), 832 // Competences 833 "mode_skill_service" => array("integer", (int) $this->getSkillService()), 834 // Self Evaluation Only 835 "mode_self_eval_results" => array("integer", $this->getSelfEvaluationResults()), 836 // reminder/notification 837 "reminder_status" => array("integer", $this->getReminderStatus()), 838 "reminder_start" => array("datetime", $rmd_start), 839 "reminder_end" => array("datetime", $rmd_end), 840 "reminder_frequency" => array("integer", $this->getReminderFrequency()), 841 "reminder_target" => array("integer", $this->getReminderTarget()), 842 "reminder_last_sent" => array("datetime", $this->getReminderLastSent()), 843 "reminder_tmpl" => array("text", $this->getReminderTemplate()), 844 "tutor_ntf_status" => array("integer", $this->getTutorNotificationStatus()), 845 "tutor_ntf_reci" => array("text", implode(";", (array) $this->getTutorNotificationRecipients())), 846 "tutor_ntf_target" => array("integer", $this->getTutorNotificationTarget()), 847 "own_results_view" => array("integer", $this->hasViewOwnResults()), 848 "own_results_mail" => array("integer", $this->hasMailOwnResults()), 849 "confirmation_mail" => array("integer", $this->hasMailConfirmation()), 850 "anon_user_list" => array("integer", $this->hasAnonymousUserList()) 851 ), array( 852 "survey_id" => array("integer", $this->getSurveyId()) 853 )); 854 } 855 if ($affectedRows) { 856 // save questions to db 857 $this->saveQuestionsToDb(); 858 } 859 860 // moved activation to ilObjectActivation 861 if ($this->ref_id) { 862 include_once "./Services/Object/classes/class.ilObjectActivation.php"; 863 ilObjectActivation::getItem($this->ref_id); 864 865 $item = new ilObjectActivation; 866 if (!$this->isActivationLimited()) { 867 $item->setTimingType(ilObjectActivation::TIMINGS_DEACTIVATED); 868 } else { 869 $item->setTimingType(ilObjectActivation::TIMINGS_ACTIVATION); 870 $item->setTimingStart($this->getActivationStartDate()); 871 $item->setTimingEnd($this->getActivationEndDate()); 872 $item->toggleVisible($this->getActivationVisibility()); 873 } 874 875 $item->update($this->ref_id); 876 } 877 } 878 879 /** 880 * Saves the survey questions to the database 881 * 882 * @access public 883 * @see $questions 884 */ 885 public function saveQuestionsToDb() 886 { 887 $ilDB = $this->db; 888 889 $this->log->debug("save questions"); 890 891 // gather old questions state 892 $old_questions = array(); 893 $result = $ilDB->queryF( 894 "SELECT survey_question_id,question_fi,sequence" . 895 " FROM svy_svy_qst WHERE survey_fi = %s", 896 array('integer'), 897 array($this->getSurveyId()) 898 ); 899 while ($row = $ilDB->fetchAssoc($result)) { 900 $old_questions[$row["question_fi"]] = $row; // problem, as soon as duplicates exist, they will be hidden here 901 } 902 903 // #15231 - diff with current questions state 904 $insert = $update = $delete = array(); 905 foreach ($this->questions as $seq => $fi) { 906 if (!array_key_exists($fi, $old_questions)) { // really new fi IDs 907 $insert[] = $fi; // this should be ok, should not create duplicates here 908 } elseif ($old_questions[$fi]["sequence"] != $seq) { // we are updating one of the duplicates (if any) 909 $update[$fi] = $old_questions[$fi]["survey_question_id"]; 910 } 911 // keep track of still relevant questions 912 unset($old_questions[$fi]); // deleting old question, if they are not in current array 913 } 914 915 // delete obsolete question relations 916 if (sizeof($old_questions)) { 917 $del_ids = array(); 918 foreach ($old_questions as $fi => $old) { 919 $del_ids[] = $old["survey_question_id"]; 920 } 921 $ilDB->manipulate($q = "DELETE FROM svy_svy_qst" . 922 " WHERE " . $ilDB->in("survey_question_id", $del_ids, "", "integer")); 923 $this->log->debug("delete: " . $q); 924 } 925 unset($old_questions); 926 927 // create/update question relations 928 foreach ($this->questions as $seq => $fi) { 929 if (in_array($fi, $insert)) { 930 // check if question is not already in the survey, see #22018 931 if (!$this->isQuestionInSurvey($fi)) { 932 $next_id = $ilDB->nextId('svy_svy_qst'); 933 $ilDB->manipulateF( 934 "INSERT INTO svy_svy_qst" . 935 " (survey_question_id, survey_fi, question_fi, heading, sequence, tstamp)" . 936 " VALUES (%s, %s, %s, %s, %s, %s)", 937 array('integer', 'integer', 'integer', 'text', 'integer', 'integer'), 938 array($next_id, $this->getSurveyId(), $fi, null, $seq, time()) 939 ); 940 $this->log->debug("insert svy_svy_qst, id:" . $next_id . ", fi: " . $fi . ", seq:" . $seq); 941 } 942 } elseif (array_key_exists($fi, $update)) { 943 $ilDB->manipulate("UPDATE svy_svy_qst" . 944 " SET sequence = " . $ilDB->quote($seq, "integer") . 945 ", tstamp = " . $ilDB->quote(time(), "integer") . 946 " WHERE survey_question_id = " . $ilDB->quote($update[$fi], "integer")); 947 $this->log->debug("update svy_svy_qst, id:" . $update[$fi] . ", fi: " . $fi . ", seq:" . $seq); 948 } 949 } 950 } 951 952 /** 953 * Checks for an anomyous survey id in the database an returns the id 954 * 955 * @param string $id A survey access code 956 * @result object Anonymous survey id if found, empty string otherwise 957 * @access public 958 */ 959 public function getAnonymousId($id) 960 { 961 $ilDB = $this->db; 962 $result = $ilDB->queryF( 963 "SELECT anonymous_id FROM svy_finished WHERE anonymous_id = %s", 964 array('text'), 965 array($id) 966 ); 967 if ($result->numRows()) { 968 $row = $ilDB->fetchAssoc($result); 969 return $row["anonymous_id"]; 970 } else { 971 return ""; 972 } 973 } 974 975 /** 976 * Returns a question gui object to a given questiontype and question id 977 * 978 * @result object Resulting question gui object 979 * @access public 980 */ 981 public function getQuestionGUI($questiontype, $question_id) 982 { 983 include_once "./Modules/SurveyQuestionPool/classes/class.SurveyQuestionGUI.php"; 984 return SurveyQuestionGUI::_getQuestionGUI($questiontype, $question_id); 985 } 986 987 /** 988 * Returns the question type of a question with a given id 989 * 990 * @param integer $question_id The database id of the question 991 * @result string The question type string 992 * @access private 993 */ 994 public function getQuestionType($question_id) 995 { 996 $ilDB = $this->db; 997 if ($question_id < 1) { 998 return -1; 999 } 1000 $result = $ilDB->queryF( 1001 "SELECT type_tag FROM svy_question, svy_qtype WHERE svy_question.question_id = %s AND " . 1002 "svy_question.questiontype_fi = svy_qtype.questiontype_id", 1003 array('integer'), 1004 array($question_id) 1005 ); 1006 if ($result->numRows() == 1) { 1007 $data = $ilDB->fetchAssoc($result); 1008 return $data["type_tag"]; 1009 } else { 1010 return ""; 1011 } 1012 } 1013 1014 /** 1015 * Returns the survey database id 1016 * 1017 * @result integer survey database id 1018 * @access public 1019 */ 1020 public function getSurveyId() 1021 { 1022 return $this->survey_id; 1023 } 1024 1025 /** 1026 * set anonymize status 1027 */ 1028 public function setAnonymize($a_anonymize) 1029 { 1030 switch ($a_anonymize) { 1031 case self::ANONYMIZE_OFF: 1032 case self::ANONYMIZE_ON: 1033 case self::ANONYMIZE_FREEACCESS: 1034 case self::ANONYMIZE_CODE_ALL: 1035 $this->anonymize = $a_anonymize; 1036 break; 1037 default: 1038 $this->anonymize = self::ANONYMIZE_OFF; 1039 break; 1040 } 1041 } 1042 1043 /** 1044 * get anonymize status 1045 * 1046 * @return integer status 1047 */ 1048 public function getAnonymize() 1049 { 1050 return ($this->anonymize) ? $this->anonymize : 0; 1051 } 1052 1053 /** 1054 * Checks if the survey is accessable without a survey code 1055 * 1056 * @return boolean status 1057 */ 1058 public function isAccessibleWithoutCode() 1059 { 1060 return ($this->getAnonymize() == self::ANONYMIZE_OFF || 1061 $this->getAnonymize() == self::ANONYMIZE_FREEACCESS); 1062 } 1063 1064 /** 1065 * Checks if the survey results are to be anonymized 1066 * 1067 * @return boolean status 1068 */ 1069 public function hasAnonymizedResults() 1070 { 1071 return ($this->getAnonymize() == self::ANONYMIZE_ON || 1072 $this->getAnonymize() == self::ANONYMIZE_FREEACCESS); 1073 } 1074 1075 /** 1076 * Loads a survey object from a database 1077 * 1078 * @access public 1079 */ 1080 public function loadFromDb() 1081 { 1082 $ilDB = $this->db; 1083 $result = $ilDB->queryF( 1084 "SELECT * FROM svy_svy WHERE obj_fi = %s", 1085 array('integer'), 1086 array($this->getId()) 1087 ); 1088 if ($result->numRows() == 1) { 1089 $data = $ilDB->fetchAssoc($result); 1090 $this->setSurveyId($data["survey_id"]); 1091 $this->setAuthor($data["author"]); 1092 include_once("./Services/RTE/classes/class.ilRTE.php"); 1093 $this->setIntroduction(ilRTE::_replaceMediaObjectImageSrc($data["introduction"], 1)); 1094 if (strcmp($data["outro"], "survey_finished") == 0) { 1095 $this->setOutro($this->lng->txt("survey_finished")); 1096 } else { 1097 $this->setOutro(ilRTE::_replaceMediaObjectImageSrc($data["outro"], 1)); 1098 } 1099 $this->setInvitation($data["invitation"]); 1100 $this->setInvitationMode($data["invitation_mode"]); 1101 $this->setShowQuestionTitles($data["show_question_titles"]); 1102 $this->setStartDate($data["startdate"]); 1103 $this->setEndDate($data["enddate"]); 1104 $this->setAnonymize($data["anonymize"]); 1105 $this->setEvaluationAccess($data["evaluation_access"]); 1106 $this->loadQuestionsFromDb(); 1107 $this->setMailNotification($data['mailnotification']); 1108 $this->setMailAddresses($data['mailaddresses']); 1109 $this->setMailParticipantData($data['mailparticipantdata']); 1110 $this->setTemplate($data['template_id']); 1111 $this->setPoolUsage($data['pool_usage']); 1112 // Mode 1113 $this->setMode($data['mode']); 1114 // 360° 1115 $this->set360SelfEvaluation($data['mode_360_self_eval']); 1116 $this->set360SelfRaters($data['mode_360_self_rate']); 1117 $this->set360SelfAppraisee($data['mode_360_self_appr']); 1118 $this->set360Results($data['mode_360_results']); 1119 // Mode self evaluated 1120 $this->setSelfEvaluationResults($data['mode_self_eval_results']); 1121 // Competences 1122 $this->setSkillService($data['mode_skill_service']); 1123 // reminder/notification 1124 $this->setReminderStatus($data["reminder_status"]); 1125 $this->setReminderStart($data["reminder_start"] ? new ilDate($data["reminder_start"], IL_CAL_DATE) : null); 1126 $this->setReminderEnd($data["reminder_end"] ? new ilDate($data["reminder_end"], IL_CAL_DATE) : null); 1127 $this->setReminderFrequency($data["reminder_frequency"]); 1128 $this->setReminderTarget($data["reminder_target"]); 1129 $this->setReminderLastSent($data["reminder_last_sent"]); 1130 $this->setReminderTemplate($data["reminder_tmpl"]); 1131 $this->setTutorNotificationStatus($data["tutor_ntf_status"]); 1132 $this->setTutorNotificationRecipients(explode(";", $data["tutor_ntf_reci"])); 1133 $this->setTutorNotificationTarget($data["tutor_ntf_target"]); 1134 1135 $this->setViewOwnResults($data["own_results_view"]); 1136 $this->setMailOwnResults($data["own_results_mail"]); 1137 $this->setMailConfirmation($data["confirmation_mail"]); 1138 1139 $this->setAnonymousUserList($data["anon_user_list"]); 1140 } 1141 1142 // moved activation to ilObjectActivation 1143 if ($this->ref_id) { 1144 include_once "./Services/Object/classes/class.ilObjectActivation.php"; 1145 $activation = ilObjectActivation::getItem($this->ref_id); 1146 switch ($activation["timing_type"]) { 1147 case ilObjectActivation::TIMINGS_ACTIVATION: 1148 $this->setActivationLimited(true); 1149 $this->setActivationStartDate($activation["timing_start"]); 1150 $this->setActivationEndDate($activation["timing_end"]); 1151 $this->setActivationVisibility($activation["visible"]); 1152 break; 1153 1154 default: 1155 $this->setActivationLimited(false); 1156 break; 1157 } 1158 } 1159 } 1160 1161 /** 1162 * Loads the survey questions from the database 1163 * 1164 * @access public 1165 * @see $questions 1166 */ 1167 public function loadQuestionsFromDb() 1168 { 1169 $ilDB = $this->db; 1170 $this->questions = array(); 1171 $result = $ilDB->queryF( 1172 "SELECT * FROM svy_svy_qst WHERE survey_fi = %s ORDER BY sequence", 1173 array('integer'), 1174 array($this->getSurveyId()) 1175 ); 1176 while ($data = $ilDB->fetchAssoc($result)) { 1177 $this->questions[$data["sequence"]] = $data["question_fi"]; 1178 } 1179 } 1180 1181 /** 1182 * Remove duplicate sequence entries, see #22018 1183 */ 1184 public function fixSequenceStructure() 1185 { 1186 global $DIC; 1187 1188 $ilDB = $DIC->database(); 1189 //return; 1190 // we keep all survey question ids with their lowest sequence 1191 $result = $ilDB->queryF( 1192 "SELECT * FROM svy_svy_qst WHERE survey_fi = %s ORDER BY sequence", 1193 array('integer'), 1194 array($this->getSurveyId()) 1195 ); 1196 1197 // step 1: find duplicates -> $to_delete_ids 1198 $fis = array(); 1199 $to_delete_ids = array(); 1200 while ($data = $ilDB->fetchAssoc($result)) { 1201 if (in_array($data["question_fi"], $fis)) { // found a duplicate 1202 $to_delete_ids[] = $data["survey_question_id"]; 1203 } else { 1204 $fis[] = $data["question_fi"]; 1205 } 1206 } 1207 1208 // step 2: we delete the duplicates 1209 if (count($to_delete_ids) > 0) { 1210 $ilDB->manipulate($q = "DELETE FROM svy_svy_qst" . 1211 " WHERE " . $ilDB->in("survey_question_id", $to_delete_ids, false, "integer") . 1212 " AND survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer")); 1213 $this->log->debug("delete: " . $q); 1214 1215 $ilDB->manipulate($q = "DELETE FROM svy_qblk_qst " . 1216 " WHERE " . $ilDB->in("question_fi", $fis, true, "integer") . 1217 " AND survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer")); 1218 $this->log->debug("delete: " . $q); 1219 } 1220 1221 // step 3: we fix the sequence 1222 $set = $ilDB->query("SELECT * FROM svy_svy_qst " . 1223 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") . " ORDER BY sequence"); 1224 $seq = 0; 1225 while ($rec = $ilDB->fetchAssoc($set)) { 1226 $ilDB->manipulate( 1227 $q = "UPDATE svy_svy_qst SET " . 1228 " sequence = " . $ilDB->quote($seq++, "integer") . 1229 " WHERE survey_question_id = " . $ilDB->quote($rec["survey_question_id"], "integer") 1230 ); 1231 $this->log->debug("update: " . $q); 1232 } 1233 } 1234 1235 1236 /** 1237 * Sets the authors name of the ilObjSurvey object 1238 * 1239 * @param string $author A string containing the name of the test author 1240 * @access public 1241 * @see $author 1242 */ 1243 public function setAuthor($author = "") 1244 { 1245 $this->author = $author; 1246 } 1247 1248 /** 1249 * Saves an authors name into the lifecycle metadata if no lifecycle metadata exists 1250 * This will only be called for conversion of "old" tests where the author hasn't been 1251 * stored in the lifecycle metadata 1252 * 1253 * @param string $a_author A string containing the name of the test author 1254 * @access private 1255 * @see $author 1256 */ 1257 public function saveAuthorToMetadata($a_author = "") 1258 { 1259 $md = new ilMD($this->getId(), 0, $this->getType()); 1260 $md_life = &$md->getLifecycle(); 1261 if (!$md_life) { 1262 if (strlen($a_author) == 0) { 1263 $ilUser = $this->user; 1264 $a_author = $ilUser->getFullname(); 1265 } 1266 1267 $md_life = &$md->addLifecycle(); 1268 $md_life->save(); 1269 $con = &$md_life->addContribute(); 1270 $con->setRole("Author"); 1271 $con->save(); 1272 $ent = &$con->addEntity(); 1273 $ent->setEntity($a_author); 1274 $ent->save(); 1275 } 1276 } 1277 1278 /** 1279 * Gets the authors name of the ilObjSurvey object 1280 * 1281 * @return string The string containing the name of the test author 1282 * @access public 1283 * @see $author 1284 */ 1285 public function getAuthor() 1286 { 1287 $author = array(); 1288 include_once "./Services/MetaData/classes/class.ilMD.php"; 1289 $md = new ilMD($this->getId(), 0, $this->getType()); 1290 $md_life = &$md->getLifecycle(); 1291 if ($md_life) { 1292 $ids = &$md_life->getContributeIds(); 1293 foreach ($ids as $id) { 1294 $md_cont = &$md_life->getContribute($id); 1295 if (strcmp($md_cont->getRole(), "Author") == 0) { 1296 $entids = &$md_cont->getEntityIds(); 1297 foreach ($entids as $entid) { 1298 $md_ent = &$md_cont->getEntity($entid); 1299 array_push($author, $md_ent->getEntity()); 1300 } 1301 } 1302 } 1303 } 1304 return join(",", $author); 1305 } 1306 1307 /** 1308 * Gets the status of the display_question_titles attribute 1309 * 1310 * @return integer The status of the display_question_titles attribute 1311 * @see $display_question_titles 1312 */ 1313 public function getShowQuestionTitles() 1314 { 1315 return ($this->display_question_titles) ? 1 : 0; 1316 } 1317 1318 /** 1319 * Sets the status of the display_question_titles attribute 1320 * 1321 * @param integer $a_show The status of the display_question_titles attribute 1322 * @see $display_question_titles 1323 */ 1324 public function setShowQuestionTitles($a_show) 1325 { 1326 $this->display_question_titles = ($a_show) ? 1 : 0; 1327 } 1328 1329 /** 1330 * Sets the question titles visible during the query 1331 * 1332 * @access public 1333 * @see $display_question_titles 1334 */ 1335 public function showQuestionTitles() 1336 { 1337 $this->display_question_titles = 1; 1338 } 1339 1340 /** 1341 * Sets the question titles hidden during the query 1342 * 1343 * @access public 1344 * @see $display_question_titles 1345 */ 1346 public function hideQuestionTitles() 1347 { 1348 $this->display_question_titles = 0; 1349 } 1350 1351 /** 1352 * Sets the invitation status 1353 * 1354 * @param integer $invitation The invitation status 1355 * @access public 1356 * @see $invitation 1357 */ 1358 public function setInvitation($invitation = 0) 1359 { 1360 $ilDB = $this->db; 1361 $ilAccess = $this->access; 1362 1363 $this->invitation = $invitation; 1364 if ($invitation == self::INVITATION_OFF) { 1365 $this->disinviteAllUsers(); 1366 } elseif ($invitation == self::INVITATION_ON) { 1367 if ($this->getInvitationMode() == self::MODE_UNLIMITED) { 1368 $result = $ilDB->query("SELECT usr_id FROM usr_data"); 1369 while ($row = $ilDB->fetchAssoc($result)) { 1370 if ($ilAccess->checkAccessOfUser($row["usr_id"], "read", "", $this->getRefId(), "svy", $this->getId())) { 1371 $this->inviteUser($row['usr_id']); 1372 } 1373 } 1374 } 1375 } 1376 } 1377 1378 /** 1379 * Sets the invitation mode 1380 * 1381 * @param integer $invitation_mode The invitation mode 1382 * @access public 1383 * @see $invitation_mode 1384 */ 1385 public function setInvitationMode($invitation_mode = 0) 1386 { 1387 $this->invitation_mode = $invitation_mode; 1388 } 1389 1390 /** 1391 * Sets the invitation status and mode (a more performant solution if you change both) 1392 * 1393 * @param integer $invitation The invitation status 1394 * @param integer $invitation_mode The invitation mode 1395 * @access public 1396 * @see $invitation_mode 1397 */ 1398 public function setInvitationAndMode($invitation = 0, $invitation_mode = 0) 1399 { 1400 $this->invitation_mode = $invitation_mode; 1401 $this->setInvitation($invitation); 1402 } 1403 1404 /** 1405 * Sets the introduction text 1406 * 1407 * @param string $introduction A string containing the introduction 1408 * @see $introduction 1409 */ 1410 public function setIntroduction($introduction = "") 1411 { 1412 $this->introduction = $introduction; 1413 } 1414 1415 /** 1416 * Sets the outro text 1417 * 1418 * @param string $outro A string containing the outro 1419 * @see $outro 1420 */ 1421 public function setOutro($outro = "") 1422 { 1423 $this->outro = $outro; 1424 } 1425 1426 /** 1427 * Gets the invitation status 1428 * 1429 * @return integer The invitation status 1430 * @access public 1431 * @see $invitation 1432 */ 1433 public function getInvitation() 1434 { 1435 return ($this->invitation) ? $this->invitation : self::INVITATION_OFF; 1436 } 1437 1438 /** 1439 * Gets the invitation mode 1440 * 1441 * @return integer The invitation mode 1442 * @access public 1443 * @see $invitation 1444 */ 1445 public function getInvitationMode() 1446 { 1447 include_once "./Services/Administration/classes/class.ilSetting.php"; 1448 $surveySetting = new ilSetting("survey"); 1449 $unlimited_invitation = $surveySetting->get("unlimited_invitation"); 1450 if (!$unlimited_invitation && $this->invitation_mode == self::MODE_UNLIMITED) { 1451 return self::MODE_PREDEFINED_USERS; 1452 } else { 1453 return ($this->invitation_mode) ? $this->invitation_mode : self::MODE_UNLIMITED; 1454 } 1455 } 1456 1457 /** 1458 * Gets the start date of the survey 1459 * 1460 * @return string Survey start date (YYYY-MM-DD) 1461 * @access public 1462 * @see $start_date 1463 */ 1464 public function getStartDate() 1465 { 1466 return (strlen($this->start_date)) ? $this->start_date : null; 1467 } 1468 1469 /** 1470 * Checks if the survey can be started 1471 * 1472 * @return array An array containing the following keys: result (boolean) and messages (array) 1473 * @access public 1474 */ 1475 public function canStartSurvey($anonymous_id = null, $a_no_rbac = false) 1476 { 1477 $ilAccess = $this->access; 1478 1479 $result = true; 1480 $messages = array(); 1481 $edit_settings = false; 1482 // check start date 1483 if (preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getStartDate(), $matches)) { 1484 $epoch_time = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]); 1485 $now = time(); 1486 if ($now < $epoch_time) { 1487 array_push($messages, $this->lng->txt('start_date_not_reached') . ' (' . 1488 ilDatePresentation::formatDate(new ilDateTime($this->getStartDate(), IL_CAL_TIMESTAMP)) . ")"); 1489 $result = false; 1490 $edit_settings = true; 1491 } 1492 } 1493 // check end date 1494 if (preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getEndDate(), $matches)) { 1495 $epoch_time = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]); 1496 $now = time(); 1497 if ($now > $epoch_time) { 1498 array_push($messages, $this->lng->txt('end_date_reached') . ' (' . 1499 ilDatePresentation::formatDate(new ilDateTime($this->getEndDate(), IL_CAL_TIMESTAMP)) . ")"); 1500 $result = false; 1501 $edit_settings = true; 1502 } 1503 } 1504 1505 // check online status 1506 if ($this->getOfflineStatus()) { 1507 array_push($messages, $this->lng->txt("survey_is_offline")); 1508 $result = false; 1509 $edit_settings = true; 1510 } 1511 // check rbac permissions 1512 if (!$a_no_rbac && !$ilAccess->checkAccess("read", "", $this->ref_id)) { 1513 array_push($messages, $this->lng->txt("cannot_participate_survey")); 1514 $result = false; 1515 } 1516 /* 1517 // 2. check previous access 1518 if (!$result["error"]) 1519 { 1520 $ilUser = $this->user; 1521 $survey_started = $this->isSurveyStarted($ilUser->getId(), $anonymous_id); 1522 if ($survey_started === 1) 1523 { 1524 array_push($messages, $this->lng->txt("already_completed_survey")); 1525 $result = FALSE; 1526 } 1527 } 1528 */ 1529 return array( 1530 "result" => $result, 1531 "messages" => $messages, 1532 "edit_settings" => $edit_settings 1533 ); 1534 } 1535 1536 /** 1537 * Sets the start date of the survey 1538 * 1539 * @param string $start_data Survey start date (YYYYMMDDHHMMSS) 1540 * @access public 1541 * @see $start_date 1542 */ 1543 public function setStartDate($start_date = "") 1544 { 1545 $this->start_date = $start_date; 1546 } 1547 1548 /** 1549 * Sets the start date of the survey 1550 * 1551 * @param string $start_date Survey start date (YYYY-MM-DD) 1552 * @param string $start_time Survey start time (HH:MM:SS) 1553 * @access public 1554 * @see $start_date 1555 */ 1556 public function setStartDateAndTime($start_date = "", $start_time) 1557 { 1558 $y = ''; 1559 $m = ''; 1560 $d = ''; 1561 $h = ''; 1562 $i = ''; 1563 $s = ''; 1564 if (preg_match("/(\d{4})-(\d{2})-(\d{2})/", $start_date, $matches)) { 1565 $y = $matches[1]; 1566 $m = $matches[2]; 1567 $d = $matches[3]; 1568 } 1569 if (preg_match("/(\d{2}):(\d{2}):(\d{2})/", $start_time, $matches)) { 1570 $h = $matches[1]; 1571 $i = $matches[2]; 1572 $s = $matches[3]; 1573 } 1574 $this->start_date = sprintf('%04d%02d%02d%02d%02d%02d', $y, $m, $d, $h, $i, $s); 1575 } 1576 1577 /** 1578 * Gets the end date of the survey 1579 * 1580 * @return string Survey end date (YYYY-MM-DD) 1581 * @access public 1582 * @see $end_date 1583 */ 1584 public function getEndDate() 1585 { 1586 return (strlen($this->end_date)) ? $this->end_date : null; 1587 } 1588 1589 /** 1590 * Sets the end date of the survey 1591 * 1592 * @param string $end_date Survey end date (YYYYMMDDHHMMSS) 1593 * @access public 1594 * @see $end_date 1595 */ 1596 public function setEndDate($end_date = "") 1597 { 1598 $this->end_date = $end_date; 1599 } 1600 1601 /** 1602 * Sets the end date of the survey 1603 * 1604 * @param string $end_date Survey end date (YYYY-MM-DD) 1605 * @param string $end_time Survey end time (HH:MM:SS) 1606 * @access public 1607 * @see $start_date 1608 */ 1609 public function setEndDateAndTime($end_date = "", $end_time) 1610 { 1611 $y = ''; 1612 $m = ''; 1613 $d = ''; 1614 $h = ''; 1615 $i = ''; 1616 $s = ''; 1617 if (preg_match("/(\d{4})-(\d{2})-(\d{2})/", $end_date, $matches)) { 1618 $y = $matches[1]; 1619 $m = $matches[2]; 1620 $d = $matches[3]; 1621 } 1622 if (preg_match("/(\d{2}):(\d{2}):(\d{2})/", $end_time, $matches)) { 1623 $h = $matches[1]; 1624 $i = $matches[2]; 1625 $s = $matches[3]; 1626 } 1627 $this->end_date = sprintf('%04d%02d%02d%02d%02d%02d', $y, $m, $d, $h, $i, $s); 1628 } 1629 1630 /** 1631 * Gets the learners evaluation access 1632 * 1633 * @return integer The evaluation access 1634 * @access public 1635 * @see $evaluation_access 1636 */ 1637 public function getEvaluationAccess() 1638 { 1639 return ($this->evaluation_access) ? $this->evaluation_access : self::EVALUATION_ACCESS_OFF; 1640 } 1641 1642 /** 1643 * Sets the learners evaluation access 1644 * 1645 * @param integer $evaluation_access The evaluation access 1646 * @access public 1647 * @see $evaluation_access 1648 */ 1649 public function setEvaluationAccess($evaluation_access = self::EVALUATION_ACCESS_OFF) 1650 { 1651 $this->evaluation_access = ($evaluation_access) ? $evaluation_access : self::EVALUATION_ACCESS_OFF; 1652 } 1653 1654 public function setActivationVisibility($a_value) 1655 { 1656 $this->activation_visibility = (bool) $a_value; 1657 } 1658 1659 public function getActivationVisibility() 1660 { 1661 return $this->activation_visibility; 1662 } 1663 1664 public function isActivationLimited() 1665 { 1666 return (bool) $this->activation_limited; 1667 } 1668 1669 public function setActivationLimited($a_value) 1670 { 1671 $this->activation_limited = (bool) $a_value; 1672 } 1673 1674 /** 1675 * Gets the introduction text 1676 * 1677 * @return string The introduction of the survey object 1678 * @access public 1679 * @see $introduction 1680 */ 1681 public function getIntroduction() 1682 { 1683 return (strlen($this->introduction)) ? $this->introduction : null; 1684 } 1685 1686 /** 1687 * Gets the outro text 1688 * 1689 * @return string The outro of the survey object 1690 * @access public 1691 * @see $outro 1692 */ 1693 public function getOutro() 1694 { 1695 return (strlen($this->outro)) ? $this->outro : null; 1696 } 1697 1698 /** 1699 * Gets the question id's of the questions which are already in the survey 1700 * 1701 * @return array The questions of the survey 1702 * @access public 1703 */ 1704 public function &getExistingQuestions() 1705 { 1706 $ilDB = $this->db; 1707 $existing_questions = array(); 1708 $result = $ilDB->queryF( 1709 "SELECT svy_question.original_id FROM svy_question, svy_svy_qst WHERE " . 1710 "svy_svy_qst.survey_fi = %s AND svy_svy_qst.question_fi = svy_question.question_id", 1711 array('integer'), 1712 array($this->getSurveyId()) 1713 ); 1714 while ($data = $ilDB->fetchAssoc($result)) { 1715 if ($data["original_id"]) { 1716 array_push($existing_questions, $data["original_id"]); 1717 } 1718 } 1719 return $existing_questions; 1720 } 1721 1722 /** 1723 * Get the titles of all available survey question pools 1724 * 1725 * @return array An array of survey question pool titles 1726 * @access public 1727 */ 1728 public function &getQuestionpoolTitles($could_be_offline = false, $showPath = false) 1729 { 1730 include_once "./Modules/SurveyQuestionPool/classes/class.ilObjSurveyQuestionPool.php"; 1731 return ilObjSurveyQuestionPool::_getAvailableQuestionpools($use_object_id = true, $could_be_offline, $showPath); 1732 } 1733 1734 /** 1735 * Move questions and/or questionblocks to another position 1736 * 1737 * @param array $move_questions An array with the question id's of the questions to move 1738 * @param integer $target_index The question id of the target position 1739 * @param integer $insert_mode 0, if insert before the target position, 1 if insert after the target position 1740 * @access public 1741 */ 1742 public function moveQuestions($move_questions, $target_index, $insert_mode) 1743 { 1744 $array_pos = array_search($target_index, $this->questions); 1745 if ($insert_mode == 0) { 1746 $part1 = array_slice($this->questions, 0, $array_pos); 1747 $part2 = array_slice($this->questions, $array_pos); 1748 } elseif ($insert_mode == 1) { 1749 $part1 = array_slice($this->questions, 0, $array_pos + 1); 1750 $part2 = array_slice($this->questions, $array_pos + 1); 1751 } 1752 $found = 0; 1753 foreach ($move_questions as $question_id) { 1754 if (!(array_search($question_id, $part1) === false)) { 1755 unset($part1[array_search($question_id, $part1)]); 1756 $found++; 1757 } 1758 if (!(array_search($question_id, $part2) === false)) { 1759 unset($part2[array_search($question_id, $part2)]); 1760 $found++; 1761 } 1762 } 1763 // sanity check: do not move questions if they have not be found in the array 1764 if ($found != count($move_questions)) { 1765 return; 1766 } 1767 $part1 = array_values($part1); 1768 $part2 = array_values($part2); 1769 $this->questions = array_values(array_merge($part1, $move_questions, $part2)); 1770 foreach ($move_questions as $question_id) { 1771 $constraints = $this->getConstraints($question_id); 1772 foreach ($constraints as $idx => $constraint) { 1773 foreach ($part2 as $next_question_id) { 1774 if ($constraint["question"] == $next_question_id) { 1775 // constraint concerning a question that follows -> delete constraint 1776 $this->deleteConstraint($constraint["id"]); 1777 } 1778 } 1779 } 1780 } 1781 $this->saveQuestionsToDb(); 1782 } 1783 1784 /** 1785 * Remove a question from the survey 1786 * 1787 * @param integer $question_id The database id of the question 1788 * @access public 1789 */ 1790 public function removeQuestion($question_id) 1791 { 1792 include_once "./Modules/SurveyQuestionPool/classes/class.SurveyQuestion.php"; 1793 $question = self::_instanciateQuestion($question_id); 1794 #20610 if no question found, do nothing. 1795 if ($question) { 1796 $question->delete($question_id); 1797 $this->removeConstraintsConcerningQuestion($question_id); 1798 } 1799 } 1800 1801 /** 1802 * Remove constraints concerning a question with a given question_id 1803 * 1804 * @param integer $question_id The database id of the question 1805 * @access public 1806 */ 1807 public function removeConstraintsConcerningQuestion($question_id) 1808 { 1809 $ilDB = $this->db; 1810 $result = $ilDB->queryF( 1811 "SELECT constraint_fi FROM svy_qst_constraint WHERE question_fi = %s AND survey_fi = %s", 1812 array('integer','integer'), 1813 array($question_id, $this->getSurveyId()) 1814 ); 1815 if ($result->numRows() > 0) { 1816 $remove_constraints = array(); 1817 while ($row = $ilDB->fetchAssoc($result)) { 1818 array_push($remove_constraints, $row["constraint_fi"]); 1819 } 1820 $affectedRows = $ilDB->manipulateF( 1821 "DELETE FROM svy_qst_constraint WHERE question_fi = %s AND survey_fi = %s", 1822 array('integer','integer'), 1823 array($question_id, $this->getSurveyId()) 1824 ); 1825 foreach ($remove_constraints as $key => $constraint_id) { 1826 $affectedRows = $ilDB->manipulateF( 1827 "DELETE FROM svy_constraint WHERE constraint_id = %s", 1828 array('integer'), 1829 array($constraint_id) 1830 ); 1831 } 1832 } 1833 } 1834 1835 /** 1836 * Remove questions from the survey 1837 * 1838 * @param array $remove_questions An array with the question id's of the questions to remove 1839 * @param array $remove_questionblocks An array with the questionblock id's of the questions blocks to remove 1840 * @access public 1841 */ 1842 public function removeQuestions($remove_questions, $remove_questionblocks) 1843 { 1844 $ilDB = $this->db; 1845 1846 $block_sizes = array(); 1847 foreach ($this->getSurveyQuestions() as $question_id => $data) { 1848 if (in_array($question_id, $remove_questions) or in_array($data["questionblock_id"], $remove_questionblocks)) { 1849 unset($this->questions[array_search($question_id, $this->questions)]); 1850 $this->removeQuestion($question_id); 1851 } elseif ($data["questionblock_id"]) { 1852 $block_sizes[$data["questionblock_id"]]++; 1853 } 1854 } 1855 1856 // blocks with just 1 question need to be deleted 1857 foreach ($block_sizes as $block_id => $size) { 1858 if ($size < 2) { 1859 $remove_questionblocks[] = $block_id; 1860 } 1861 } 1862 1863 foreach (array_unique($remove_questionblocks) as $questionblock_id) { 1864 $affectedRows = $ilDB->manipulateF( 1865 "DELETE FROM svy_qblk WHERE questionblock_id = %s", 1866 array('integer'), 1867 array($questionblock_id) 1868 ); 1869 $affectedRows = $ilDB->manipulateF( 1870 "DELETE FROM svy_qblk_qst WHERE questionblock_fi = %s AND survey_fi = %s", 1871 array('integer','integer'), 1872 array($questionblock_id, $this->getSurveyId()) 1873 ); 1874 } 1875 1876 $this->questions = array_values($this->questions); 1877 $this->saveQuestionsToDb(); 1878 } 1879 1880 /** 1881 * Unfolds question blocks of a question pool 1882 * 1883 * @param array $questionblocks An array of question block id's 1884 * @access public 1885 */ 1886 public function unfoldQuestionblocks($questionblocks) 1887 { 1888 $ilDB = $this->db; 1889 foreach ($questionblocks as $index) { 1890 $affectedRows = $ilDB->manipulateF( 1891 "DELETE FROM svy_qblk WHERE questionblock_id = %s", 1892 array('integer'), 1893 array($index) 1894 ); 1895 $affectedRows = $ilDB->manipulateF( 1896 "DELETE FROM svy_qblk_qst WHERE questionblock_fi = %s AND survey_fi = %s", 1897 array('integer','integer'), 1898 array($index, $this->getSurveyId()) 1899 ); 1900 } 1901 } 1902 1903 public function removeQuestionFromBlock($question_id, $questionblock_id) 1904 { 1905 $ilDB = $this->db; 1906 1907 $affectedRows = $ilDB->manipulateF( 1908 "DELETE FROM svy_qblk_qst WHERE questionblock_fi = %s AND survey_fi = %s AND question_fi = %s", 1909 array('integer','integer','integer'), 1910 array($questionblock_id, $this->getSurveyId(), $question_id) 1911 ); 1912 } 1913 1914 public function addQuestionToBlock($question_id, $questionblock_id) 1915 { 1916 $ilDB = $this->db; 1917 1918 // see #22018 1919 if (!$this->isQuestionInAnyBlock($question_id)) { 1920 $next_id = $ilDB->nextId('svy_qblk_qst'); 1921 $affectedRows = $ilDB->manipulateF( 1922 "INSERT INTO svy_qblk_qst (qblk_qst_id, survey_fi, questionblock_fi, " . 1923 "question_fi) VALUES (%s, %s, %s, %s)", 1924 array('integer', 'integer', 'integer', 'integer'), 1925 array($next_id, $this->getSurveyId(), $questionblock_id, $question_id) 1926 ); 1927 1928 $this->deleteConstraints($question_id); // #13713 1929 } 1930 } 1931 1932 /** 1933 * Is question already in a block? 1934 * 1935 * @param int $a_question_fi question id as in svy_question 1936 * @return bool 1937 */ 1938 public function isQuestionInAnyBlock($a_question_fi) 1939 { 1940 global $DIC; 1941 1942 $ilDB = $DIC->database(); 1943 1944 $set = $ilDB->query("SELECT * FROM svy_qblk_qst " . 1945 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") . 1946 " AND question_fi = " . $ilDB->quote($a_question_fi, "integer")); 1947 if ($rec = $ilDB->fetchAssoc($set)) { 1948 return true; 1949 } 1950 return false; 1951 } 1952 1953 1954 /** 1955 * Returns the question titles of all questions of a question block 1956 * 1957 * @result array The titles of the the question block questions 1958 * @access public 1959 */ 1960 public function &getQuestionblockQuestions($questionblock_id) 1961 { 1962 $ilDB = $this->db; 1963 $titles = array(); 1964 $result = $ilDB->queryF( 1965 "SELECT svy_question.title, svy_qblk_qst.question_fi, svy_qblk_qst.survey_fi FROM " . 1966 "svy_qblk, svy_qblk_qst, svy_question WHERE svy_qblk.questionblock_id = svy_qblk_qst.questionblock_fi AND " . 1967 "svy_question.question_id = svy_qblk_qst.question_fi AND svy_qblk.questionblock_id = %s", 1968 array('integer'), 1969 array($questionblock_id) 1970 ); 1971 $survey_id = ""; 1972 while ($row = $ilDB->fetchAssoc($result)) { 1973 $titles[$row["question_fi"]] = $row["title"]; 1974 $survey_id = $row["survey_fi"]; 1975 } 1976 $result = $ilDB->queryF( 1977 "SELECT question_fi, sequence FROM svy_svy_qst WHERE survey_fi = %s ORDER BY sequence", 1978 array('integer'), 1979 array($survey_id) 1980 ); 1981 $resultarray = array(); 1982 $counter = 1; 1983 while ($row = $ilDB->fetchAssoc($result)) { 1984 if (array_key_exists($row["question_fi"], $titles)) { 1985 $resultarray[$counter++] = $titles[$row["question_fi"]]; 1986 } 1987 } 1988 return $resultarray; 1989 } 1990 1991 /** 1992 * Returns the question id's of all questions of a question block 1993 * 1994 * @result array The id's of the the question block questions 1995 * @access public 1996 */ 1997 public function &getQuestionblockQuestionIds($questionblock_id) 1998 { 1999 $ilDB = $this->db; 2000 2001 // we need a correct order here, see #22011 2002 $result = $ilDB->queryF( 2003 "SELECT a.question_fi FROM svy_qblk_qst a JOIN svy_svy_qst b ON (a.question_fi = b.question_fi) " . 2004 " WHERE a.questionblock_fi = %s ORDER BY b.sequence", 2005 array("integer"), 2006 array($questionblock_id) 2007 ); 2008 $ids = array(); 2009 if ($result->numRows()) { 2010 while ($data = $ilDB->fetchAssoc($result)) { 2011 if (!in_array($data['question_fi'], $ids)) { // no duplicates, see #22018 2012 array_push($ids, $data['question_fi']); 2013 } 2014 } 2015 } 2016 2017 return $ids; 2018 } 2019 2020 /** 2021 * Returns the database row for a given question block 2022 * 2023 * @param integer $questionblock_id The database id of the question block 2024 * @result array The database row of the question block 2025 * @access public 2026 */ 2027 public static function _getQuestionblock($questionblock_id) 2028 { 2029 global $DIC; 2030 2031 $ilDB = $DIC->database(); 2032 $result = $ilDB->queryF( 2033 "SELECT * FROM svy_qblk WHERE questionblock_id = %s", 2034 array('integer'), 2035 array($questionblock_id) 2036 ); 2037 $row = $ilDB->fetchAssoc($result); 2038 return $row; 2039 } 2040 2041 /** 2042 * Adds a questionblock to the database 2043 * 2044 * @param string $title The questionblock title 2045 * @param integer $owner The database id of the owner 2046 * @return integer The database id of the newly created questionblock 2047 * @access public 2048 */ 2049 public static function _addQuestionblock($title = "", $owner = 0, $show_questiontext = true, $show_blocktitle = false) 2050 { 2051 global $DIC; 2052 2053 $ilDB = $DIC->database(); 2054 $next_id = $ilDB->nextId('svy_qblk'); 2055 $ilDB->manipulateF( 2056 "INSERT INTO svy_qblk (questionblock_id, title, show_questiontext," . 2057 " show_blocktitle, owner_fi, tstamp) " . 2058 "VALUES (%s, %s, %s, %s, %s, %s)", 2059 array('integer','text','integer','integer','integer','integer'), 2060 array($next_id, $title, $show_questiontext, $show_blocktitle, $owner, time()) 2061 ); 2062 return $next_id; 2063 } 2064 2065 /** 2066 * Creates a question block for the survey 2067 * 2068 * @param string $title The title of the question block 2069 * @param array $questions An array with the database id's of the question block questions 2070 * @access public 2071 */ 2072 public function createQuestionblock($title, $show_questiontext, $show_blocktitle, $questions) 2073 { 2074 $ilDB = $this->db; 2075 2076 // if the selected questions are not in a continous selection, move all questions of the 2077 // questionblock at the position of the first selected question 2078 $this->moveQuestions($questions, $questions[0], 0); 2079 2080 // now save the question block 2081 $ilUser = $this->user; 2082 $next_id = $ilDB->nextId('svy_qblk'); 2083 $affectedRows = $ilDB->manipulateF( 2084 "INSERT INTO svy_qblk (questionblock_id, title, show_questiontext," . 2085 " show_blocktitle, owner_fi, tstamp) VALUES (%s, %s, %s, %s, %s, %s)", 2086 array('integer','text','text','text','integer','integer'), 2087 array($next_id, $title, $show_questiontext, $show_blocktitle, $ilUser->getId(), time()) 2088 ); 2089 if ($affectedRows) { 2090 $questionblock_id = $next_id; 2091 foreach ($questions as $index) { 2092 if (!$this->isQuestionInAnyBlock($index)) { 2093 $next_id = $ilDB->nextId('svy_qblk_qst'); // #22018 2094 $affectedRows = $ilDB->manipulateF( 2095 "INSERT INTO svy_qblk_qst (qblk_qst_id, survey_fi, questionblock_fi, " . 2096 "question_fi) VALUES (%s, %s, %s, %s)", 2097 array('integer', 'integer', 'integer', 'integer'), 2098 array($next_id, $this->getSurveyId(), $questionblock_id, $index) 2099 ); 2100 $this->deleteConstraints($index); 2101 } 2102 } 2103 } 2104 } 2105 2106 /** 2107 * Modifies a question block 2108 * 2109 * @param integer $questionblock_id The database id of the question block 2110 * @param string $title The title of the question block 2111 * @access public 2112 */ 2113 public function modifyQuestionblock($questionblock_id, $title, $show_questiontext, $show_blocktitle) 2114 { 2115 $ilDB = $this->db; 2116 $affectedRows = $ilDB->manipulateF( 2117 "UPDATE svy_qblk SET title = %s, show_questiontext = %s," . 2118 " show_blocktitle = %s WHERE questionblock_id = %s", 2119 array('text','text','text','integer'), 2120 array($title, $show_questiontext, $show_blocktitle, $questionblock_id) 2121 ); 2122 } 2123 2124 /** 2125 * Deletes the constraints for a question 2126 * 2127 * @param integer $question_id The database id of the question 2128 * @access public 2129 */ 2130 public function deleteConstraints($question_id) 2131 { 2132 $ilDB = $this->db; 2133 $result = $ilDB->queryF( 2134 "SELECT constraint_fi FROM svy_qst_constraint WHERE question_fi = %s AND survey_fi = %s", 2135 array('integer','integer'), 2136 array($question_id, $this->getSurveyId()) 2137 ); 2138 $constraints = array(); 2139 while ($row = $ilDB->fetchAssoc($result)) { 2140 array_push($constraints, $row["constraint_fi"]); 2141 } 2142 foreach ($constraints as $constraint_id) { 2143 $this->deleteConstraint($constraint_id); 2144 } 2145 } 2146 2147 /** 2148 * Deletes a constraint of a question 2149 * 2150 * @param integer $constraint_id The database id of the constraint 2151 * @param integer $question_id The database id of the question 2152 * @access public 2153 */ 2154 public function deleteConstraint($constraint_id) 2155 { 2156 $ilDB = $this->db; 2157 $affectedRows = $ilDB->manipulateF( 2158 "DELETE FROM svy_constraint WHERE constraint_id = %s", 2159 array('integer'), 2160 array($constraint_id) 2161 ); 2162 $affectedRows = $ilDB->manipulateF( 2163 "DELETE FROM svy_qst_constraint WHERE constraint_fi = %s", 2164 array('integer'), 2165 array($constraint_id) 2166 ); 2167 } 2168 2169 /** 2170 * Returns the survey questions and questionblocks in an array 2171 * 2172 * @access public 2173 */ 2174 public function &getSurveyQuestions($with_answers = false) 2175 { 2176 $ilDB = $this->db; 2177 // get questionblocks 2178 $all_questions = array(); 2179 $result = $ilDB->queryF( 2180 "SELECT svy_qtype.type_tag, svy_qtype.plugin, svy_question.question_id, " . 2181 "svy_svy_qst.heading FROM svy_qtype, svy_question, svy_svy_qst WHERE svy_svy_qst.survey_fi = %s AND " . 2182 "svy_svy_qst.question_fi = svy_question.question_id AND svy_question.questiontype_fi = svy_qtype.questiontype_id " . 2183 "ORDER BY svy_svy_qst.sequence", 2184 array('integer'), 2185 array($this->getSurveyId()) 2186 ); 2187 include_once "./Modules/SurveyQuestionPool/classes/class.SurveyQuestion.php"; 2188 while ($row = $ilDB->fetchAssoc($result)) { 2189 $add = true; 2190 if ($row["plugin"]) { 2191 if (!$this->isPluginActive($row["type_tag"])) { 2192 $add = false; 2193 } 2194 } 2195 if ($add) { 2196 $question = self::_instanciateQuestion($row["question_id"]); 2197 $questionrow = $question->getQuestionDataArray($row["question_id"]); 2198 foreach ($row as $key => $value) { 2199 $questionrow[$key] = $value; 2200 } 2201 $all_questions[$row["question_id"]] = $questionrow; 2202 $all_questions[$row["question_id"]]["usableForPrecondition"] = $question->usableForPrecondition(); 2203 $all_questions[$row["question_id"]]["availableRelations"] = $question->getAvailableRelations(); 2204 } 2205 } 2206 // get all questionblocks 2207 $questionblocks = array(); 2208 if (count($all_questions)) { 2209 $result = $ilDB->queryF( 2210 "SELECT svy_qblk.*, svy_qblk_qst.question_fi FROM svy_qblk, svy_qblk_qst WHERE " . 2211 "svy_qblk.questionblock_id = svy_qblk_qst.questionblock_fi AND svy_qblk_qst.survey_fi = %s " . 2212 "AND " . $ilDB->in('svy_qblk_qst.question_fi', array_keys($all_questions), false, 'integer'), 2213 array('integer'), 2214 array($this->getSurveyId()) 2215 ); 2216 while ($row = $ilDB->fetchAssoc($result)) { 2217 $questionblocks[$row['question_fi']] = $row; 2218 } 2219 } 2220 2221 foreach ($all_questions as $question_id => $row) { 2222 $constraints = $this->getConstraints($question_id); 2223 if (isset($questionblocks[$question_id])) { 2224 $all_questions[$question_id]["questionblock_title"] = $questionblocks[$question_id]['title']; 2225 $all_questions[$question_id]["questionblock_id"] = $questionblocks[$question_id]['questionblock_id']; 2226 $all_questions[$question_id]["constraints"] = $constraints; 2227 } else { 2228 $all_questions[$question_id]["questionblock_title"] = ""; 2229 $all_questions[$question_id]["questionblock_id"] = ""; 2230 $all_questions[$question_id]["constraints"] = $constraints; 2231 } 2232 if ($with_answers) { 2233 $answers = array(); 2234 $result = $ilDB->queryF( 2235 "SELECT svy_variable.*, svy_category.title FROM svy_variable, svy_category " . 2236 "WHERE svy_variable.question_fi = %s AND svy_variable.category_fi = svy_category.category_id " . 2237 "ORDER BY sequence ASC", 2238 array('integer'), 2239 array($question_id) 2240 ); 2241 if ($result->numRows() > 0) { 2242 while ($data = $ilDB->fetchAssoc($result)) { 2243 array_push($answers, $data["title"]); 2244 } 2245 } 2246 $all_questions[$question_id]["answers"] = $answers; 2247 } 2248 } 2249 return $all_questions; 2250 } 2251 2252 /** 2253 * Sets the obligatory states for questions in a survey from the questions form 2254 * 2255 * @param array $obligatory_questions The questions which should be set obligatory from the questions form, the remaining questions should be setted not obligatory 2256 * @access public 2257 */ 2258 public function setObligatoryStates($obligatory_questions) 2259 { 2260 $ilDB = $this->db; 2261 $result = $ilDB->queryF( 2262 "SELECT * FROM svy_svy_qst WHERE survey_fi = %s", 2263 array('integer'), 2264 array($this->getSurveyId()) 2265 ); 2266 if ($result->numRows()) { 2267 while ($row = $ilDB->fetchAssoc($result)) { 2268 if (!array_key_exists($row["question_fi"], $obligatory_questions)) { 2269 $obligatory_questions[$row["question_fi"]] = 0; 2270 } 2271 } 2272 } 2273 2274 // set the obligatory states in the database 2275 foreach ($obligatory_questions as $question_fi => $obligatory) { 2276 // #12420 2277 $ilDB->manipulate("UPDATE svy_question" . 2278 " SET obligatory = " . $ilDB->quote($obligatory, "integer") . 2279 " WHERE question_id = " . $ilDB->quote($question_fi, "integer")); 2280 } 2281 } 2282 2283 /** 2284 * Returns the survey pages in an array (a page contains one or more questions) 2285 * 2286 * @access public 2287 */ 2288 public function &getSurveyPages() 2289 { 2290 $ilDB = $this->db; 2291 // get questionblocks 2292 $all_questions = array(); 2293 $result = $ilDB->queryF( 2294 "SELECT svy_question.*, svy_qtype.type_tag, svy_svy_qst.heading FROM " . 2295 "svy_question, svy_qtype, svy_svy_qst WHERE svy_svy_qst.survey_fi = %s AND " . 2296 "svy_svy_qst.question_fi = svy_question.question_id AND svy_question.questiontype_fi = svy_qtype.questiontype_id " . 2297 "ORDER BY svy_svy_qst.sequence", 2298 array('integer'), 2299 array($this->getSurveyId()) 2300 ); 2301 while ($row = $ilDB->fetchAssoc($result)) { 2302 $all_questions[$row["question_id"]] = $row; 2303 } 2304 // get all questionblocks 2305 $questionblocks = array(); 2306 if (count($all_questions)) { 2307 $result = $ilDB->queryF( 2308 "SELECT svy_qblk.*, svy_qblk_qst.question_fi FROM svy_qblk, svy_qblk_qst " . 2309 "WHERE svy_qblk.questionblock_id = svy_qblk_qst.questionblock_fi AND svy_qblk_qst.survey_fi = %s " . 2310 "AND " . $ilDB->in('svy_qblk_qst.question_fi', array_keys($all_questions), false, 'integer'), 2311 array('integer'), 2312 array($this->getSurveyId()) 2313 ); 2314 while ($row = $ilDB->fetchAssoc($result)) { 2315 $questionblocks[$row['question_fi']] = $row; 2316 } 2317 } 2318 2319 $all_pages = array(); 2320 $pageindex = -1; 2321 $currentblock = ""; 2322 foreach ($all_questions as $question_id => $row) { 2323 $constraints = array(); 2324 if (isset($questionblocks[$question_id])) { 2325 if (!$currentblock or ($currentblock != $questionblocks[$question_id]['questionblock_id'])) { 2326 $pageindex++; 2327 } 2328 $all_questions[$question_id]['page'] = $pageindex; 2329 $all_questions[$question_id]["questionblock_title"] = $questionblocks[$question_id]['title']; 2330 $all_questions[$question_id]["questionblock_id"] = $questionblocks[$question_id]['questionblock_id']; 2331 $all_questions[$question_id]["questionblock_show_questiontext"] = $questionblocks[$question_id]['show_questiontext']; 2332 $all_questions[$question_id]["questionblock_show_blocktitle"] = $questionblocks[$question_id]['show_blocktitle']; 2333 $currentblock = $questionblocks[$question_id]['questionblock_id']; 2334 $constraints = $this->getConstraints($question_id); 2335 $all_questions[$question_id]["constraints"] = $constraints; 2336 } else { 2337 $pageindex++; 2338 $all_questions[$question_id]['page'] = $pageindex; 2339 $all_questions[$question_id]["questionblock_title"] = ""; 2340 $all_questions[$question_id]["questionblock_id"] = ""; 2341 $all_questions[$question_id]["questionblock_show_questiontext"] = 1; 2342 $all_questions[$question_id]["questionblock_show_blocktitle"] = 1; 2343 $currentblock = ""; 2344 $constraints = $this->getConstraints($question_id); 2345 $all_questions[$question_id]["constraints"] = $constraints; 2346 } 2347 if (!isset($all_pages[$pageindex])) { 2348 $all_pages[$pageindex] = array(); 2349 } 2350 array_push($all_pages[$pageindex], $all_questions[$question_id]); 2351 } 2352 // calculate position percentage for every page 2353 $max = count($all_pages); 2354 $counter = 1; 2355 foreach ($all_pages as $index => $block) { 2356 foreach ($block as $blockindex => $question) { 2357 $all_pages[$index][$blockindex]["position"] = $counter / $max; 2358 } 2359 $counter++; 2360 } 2361 2362 return $all_pages; 2363 } 2364 2365 /** 2366 * Returns the next "page" of a running test 2367 * 2368 * @param integer $active_page_question_id The database id of one of the questions on that page 2369 * @param integer $direction The direction of the next page (-1 = previous page, 1 = next page) 2370 * @return mixed An array containing the question id's of the questions on the next page if there is a next page, 0 if the next page is before the start page, 1 if the next page is after the last page 2371 * @access public 2372 */ 2373 public function getNextPage($active_page_question_id, $direction) 2374 { 2375 $foundpage = -1; 2376 $pages = &$this->getSurveyPages(); 2377 if (strcmp($active_page_question_id, "") == 0) { 2378 return $pages[0]; 2379 } 2380 foreach ($pages as $key => $question_array) { 2381 foreach ($question_array as $question) { 2382 if ($active_page_question_id == $question["question_id"]) { 2383 $foundpage = $key; 2384 } 2385 } 2386 } 2387 if ($foundpage == -1) { 2388 // error: page not found 2389 } else { 2390 $foundpage += $direction; 2391 if ($foundpage < 0) { 2392 return 0; 2393 } 2394 if ($foundpage >= count($pages)) { 2395 return 1; 2396 } 2397 return $pages[$foundpage]; 2398 } 2399 } 2400 2401 /** 2402 * Returns the available question pools for the active user 2403 * 2404 * @return array The available question pools 2405 * @access public 2406 */ 2407 public function &getAvailableQuestionpools($use_obj_id = false, $could_be_offline = false, $showPath = false, $permission = "read") 2408 { 2409 include_once "./Modules/SurveyQuestionPool/classes/class.ilObjSurveyQuestionPool.php"; 2410 return ilObjSurveyQuestionPool::_getAvailableQuestionpools($use_obj_id, $could_be_offline, $showPath, $permission); 2411 } 2412 2413 /** 2414 * Returns a precondition with a given id 2415 * 2416 * @access public 2417 */ 2418 public function getPrecondition($id) 2419 { 2420 $ilDB = $this->db; 2421 2422 $result_array = array(); 2423 $result = $ilDB->queryF( 2424 "SELECT svy_constraint.*, svy_relation.*, svy_qst_constraint.question_fi ref_question_fi FROM svy_qst_constraint, svy_constraint, " . 2425 "svy_relation WHERE svy_constraint.relation_fi = svy_relation.relation_id AND " . 2426 "svy_qst_constraint.constraint_fi = svy_constraint.constraint_id AND svy_constraint.constraint_id = %s", 2427 array('integer'), 2428 array($id) 2429 ); 2430 $pc = array(); 2431 if ($result->numRows()) { 2432 $pc = $ilDB->fetchAssoc($result); 2433 } 2434 return $pc; 2435 } 2436 2437 /** 2438 * Returns the constraints to a given question or questionblock 2439 * 2440 * @access public 2441 */ 2442 public function getConstraints($question_id) 2443 { 2444 $ilDB = $this->db; 2445 2446 $result_array = array(); 2447 $result = $ilDB->queryF( 2448 "SELECT svy_constraint.*, svy_relation.* FROM svy_qst_constraint, svy_constraint, svy_relation " . 2449 "WHERE svy_constraint.relation_fi = svy_relation.relation_id AND " . 2450 "svy_qst_constraint.constraint_fi = svy_constraint.constraint_id AND svy_qst_constraint.question_fi = %s " . 2451 "AND svy_qst_constraint.survey_fi = %s", 2452 array('integer','integer'), 2453 array($question_id, $this->getSurveyId()) 2454 ); 2455 while ($row = $ilDB->fetchAssoc($result)) { 2456 include_once "./Modules/SurveyQuestionPool/classes/class.SurveyQuestion.php"; 2457 $question_type = SurveyQuestion::_getQuestionType($row["question_fi"]); 2458 SurveyQuestion::_includeClass($question_type); 2459 $question = new $question_type(); 2460 $question->loadFromDb($row["question_fi"]); 2461 $valueoutput = $question->getPreconditionValueOutput($row["value"]); 2462 array_push($result_array, array("id" => $row["constraint_id"], "question" => $row["question_fi"], "short" => $row["shortname"], "long" => $row["longname"], "value" => $row["value"], "conjunction" => $row["conjunction"], "valueoutput" => $valueoutput)); 2463 } 2464 return $result_array; 2465 } 2466 2467 /** 2468 * Returns the constraints to a given question or questionblock 2469 * 2470 * @access public 2471 */ 2472 public static function _getConstraints($survey_id) 2473 { 2474 global $DIC; 2475 2476 $ilDB = $DIC->database(); 2477 $result_array = array(); 2478 $result = $ilDB->queryF( 2479 "SELECT svy_qst_constraint.question_fi as for_question, svy_constraint.*, svy_relation.* " . 2480 "FROM svy_qst_constraint, svy_constraint, svy_relation WHERE svy_constraint.relation_fi = svy_relation.relation_id " . 2481 "AND svy_qst_constraint.constraint_fi = svy_constraint.constraint_id AND svy_qst_constraint.survey_fi = %s", 2482 array('integer'), 2483 array($survey_id) 2484 ); 2485 while ($row = $ilDB->fetchAssoc($result)) { 2486 array_push($result_array, array("id" => $row["constraint_id"], "for_question" => $row["for_question"], "question" => $row["question_fi"], "short" => $row["shortname"], "long" => $row["longname"], "relation_id" => $row["relation_id"], "value" => $row["value"], 'conjunction' => $row['conjunction'])); 2487 } 2488 return $result_array; 2489 } 2490 2491 2492 /** 2493 * Returns all variables of a question 2494 * 2495 * @access public 2496 */ 2497 public function &getVariables($question_id) 2498 { 2499 $ilDB = $this->db; 2500 2501 $result_array = array(); 2502 $result = $ilDB->queryF( 2503 "SELECT svy_variable.*, svy_category.title FROM svy_variable LEFT JOIN " . 2504 "svy_category ON svy_variable.category_fi = svy_category.category_id WHERE svy_variable.question_fi = %s " . 2505 "ORDER BY svy_variable.sequence", 2506 array('integer'), 2507 array($question_id) 2508 ); 2509 while ($row = $ilDB->fetchObject($result)) { 2510 $result_array[$row->sequence] = $row; 2511 } 2512 return $result_array; 2513 } 2514 2515 /** 2516 * Adds a constraint 2517 * 2518 * @param integer $if_question_id The question id of the question which defines a precondition 2519 * @param integer $relation The database id of the relation 2520 * @param mixed $value The value compared with the relation 2521 * @access public 2522 */ 2523 public function addConstraint($if_question_id, $relation, $value, $conjunction) 2524 { 2525 $ilDB = $this->db; 2526 2527 $next_id = $ilDB->nextId('svy_constraint'); 2528 $affectedRows = $ilDB->manipulateF( 2529 "INSERT INTO svy_constraint (constraint_id, question_fi, relation_fi, value, conjunction) VALUES " . 2530 "(%s, %s, %s, %s, %s)", 2531 array('integer','integer','integer','float', 'integer'), 2532 array($next_id, $if_question_id, $relation, $value, $conjunction) 2533 ); 2534 if ($affectedRows) { 2535 return $next_id; 2536 } else { 2537 return null; 2538 } 2539 } 2540 2541 2542 /** 2543 * Adds a constraint to a question 2544 * 2545 * @param integer $to_question_id The question id of the question where to add the constraint 2546 * @param integer $constraint_id The id of the constraint 2547 */ 2548 public function addConstraintToQuestion($to_question_id, $constraint_id) 2549 { 2550 $ilDB = $this->db; 2551 2552 $next_id = $ilDB->nextId('svy_qst_constraint'); 2553 $affectedRows = $ilDB->manipulateF( 2554 "INSERT INTO svy_qst_constraint (question_constraint_id, survey_fi, question_fi, " . 2555 "constraint_fi) VALUES (%s, %s, %s, %s)", 2556 array('integer','integer','integer','integer'), 2557 array($next_id, $this->getSurveyId(), $to_question_id, $constraint_id) 2558 ); 2559 } 2560 2561 /** 2562 * Updates a precondition 2563 * 2564 * @param integer $precondition_id The id of the original precondition 2565 * @param integer $to_question_id The question id of the question where to add the constraint 2566 * @param integer $if_question_id The question id of the question which defines a precondition 2567 * @param integer $relation The database id of the relation 2568 * @param mixed $value The value compared with the relation 2569 * @access public 2570 */ 2571 public function updateConstraint($precondition_id, $if_question_id, $relation, $value, $conjunction) 2572 { 2573 $ilDB = $this->db; 2574 $affectedRows = $ilDB->manipulateF( 2575 "UPDATE svy_constraint SET question_fi = %s, relation_fi = %s, value = %s, conjunction = %s " . 2576 "WHERE constraint_id = %s", 2577 array('integer','integer','float','integer','integer'), 2578 array($if_question_id, $relation, $value, $conjunction, $precondition_id) 2579 ); 2580 } 2581 2582 public function updateConjunctionForQuestions($questions, $conjunction) 2583 { 2584 $ilDB = $this->db; 2585 foreach ($questions as $question_id) { 2586 $affectedRows = $ilDB->manipulateF( 2587 "UPDATE svy_constraint SET conjunction = %s " . 2588 "WHERE constraint_id IN (SELECT constraint_fi FROM svy_qst_constraint WHERE svy_qst_constraint.question_fi = %s)", 2589 array('integer','integer'), 2590 array($conjunction, $question_id) 2591 ); 2592 } 2593 } 2594 2595 /** 2596 * Returns all available relations 2597 * 2598 * @access public 2599 */ 2600 public function getAllRelations($short_as_key = false) 2601 { 2602 $ilDB = $this->db; 2603 2604 // #7987 2605 $custom_order = array("equal", "not_equal", "less", "less_or_equal", "more", "more_or_equal"); 2606 $custom_order = array_flip($custom_order); 2607 2608 $result_array = array(); 2609 $result = $ilDB->query("SELECT * FROM svy_relation"); 2610 while ($row = $ilDB->fetchAssoc($result)) { 2611 if ($short_as_key) { 2612 $result_array[$row["shortname"]] = array("short" => $row["shortname"], "long" => $row["longname"], "id" => $row["relation_id"], "order" => $custom_order[$row["longname"]]); 2613 } else { 2614 $result_array[$row["relation_id"]] = array("short" => $row["shortname"], "long" => $row["longname"], "order" => $custom_order[$row["longname"]]); 2615 } 2616 } 2617 2618 $result_array = ilUtil::sortArray($result_array, "order", "ASC", true, true); 2619 foreach ($result_array as $idx => $item) { 2620 unset($result_array[$idx]["order"]); 2621 } 2622 2623 return $result_array; 2624 } 2625 2626 /** 2627 * Disinvite all users 2628 */ 2629 public function disinviteAllUsers() 2630 { 2631 $ilDB = $this->db; 2632 $result = $ilDB->queryF( 2633 "SELECT user_fi FROM svy_inv_usr WHERE survey_fi = %s", 2634 array('integer'), 2635 array($this->getSurveyId()) 2636 ); 2637 while ($row = $ilDB->fetchAssoc($result)) { 2638 $this->disinviteUser($row['user_fi']); 2639 } 2640 } 2641 2642 /** 2643 * Disinvites a user from a survey 2644 * 2645 * @param integer $user_id The database id of the disinvited user 2646 */ 2647 public function disinviteUser($user_id) 2648 { 2649 $ilDB = $this->db; 2650 2651 $affectedRows = $ilDB->manipulateF( 2652 "DELETE FROM svy_inv_usr WHERE survey_fi = %s AND user_fi = %s", 2653 array('integer','integer'), 2654 array($this->getSurveyId(), $user_id) 2655 ); 2656 include_once './Services/User/classes/class.ilObjUser.php'; 2657 ilObjUser::_dropDesktopItem($user_id, $this->getRefId(), "svy"); 2658 } 2659 2660 /** 2661 * Invites a user to a survey 2662 * 2663 * @param integer $user_id The database id of the invited user 2664 * @access public 2665 */ 2666 public function inviteUser($user_id) 2667 { 2668 $ilDB = $this->db; 2669 2670 $result = $ilDB->queryF( 2671 "SELECT user_fi FROM svy_inv_usr WHERE user_fi = %s AND survey_fi = %s", 2672 array('integer','integer'), 2673 array($user_id, $this->getSurveyId()) 2674 ); 2675 if ($result->numRows() < 1) { 2676 $next_id = $ilDB->nextId('svy_inv_usr'); 2677 $affectedRows = $ilDB->manipulateF( 2678 "INSERT INTO svy_inv_usr (invited_user_id, survey_fi, user_fi, tstamp) " . 2679 "VALUES (%s, %s, %s, %s)", 2680 array('integer','integer','integer','integer'), 2681 array($next_id, $this->getSurveyId(), $user_id, time()) 2682 ); 2683 } 2684 if ($this->getInvitation() == self::INVITATION_ON) { 2685 include_once './Services/User/classes/class.ilObjUser.php'; 2686 ilObjUser::_addDesktopItem($user_id, $this->getRefId(), "svy"); 2687 } 2688 } 2689 2690 /** 2691 * Returns a list of all invited users in a survey 2692 * 2693 * @return array The user id's of the invited users 2694 * @access public 2695 */ 2696 public function &getInvitedUsers() 2697 { 2698 $ilDB = $this->db; 2699 2700 $result_array = array(); 2701 $result = $ilDB->queryF( 2702 "SELECT user_fi FROM svy_inv_usr WHERE survey_fi = %s", 2703 array('integer'), 2704 array($this->getSurveyId()) 2705 ); 2706 while ($row = $ilDB->fetchAssoc($result)) { 2707 array_push($result_array, $row["user_fi"]); 2708 } 2709 return $result_array; 2710 } 2711 2712 /** 2713 * Deletes the working data of a question in the database 2714 * 2715 * @param integer $question_id The database id of the question 2716 * @param integer $active_id The active id of the user who worked through the question 2717 * @access public 2718 */ 2719 public function deleteWorkingData($question_id, $active_id) 2720 { 2721 $ilDB = $this->db; 2722 2723 $affectedRows = $ilDB->manipulateF( 2724 "DELETE FROM svy_answer WHERE question_fi = %s AND active_fi = %s", 2725 array('integer','integer'), 2726 array($question_id, $active_id) 2727 ); 2728 } 2729 2730 /** 2731 * Gets the working data of question from the database 2732 * 2733 * @param integer $question_id The database id of the question 2734 * @param integer $active_id The active id of the user who worked through the question 2735 * @return array The resulting database dataset as an array 2736 * @access public 2737 */ 2738 public function loadWorkingData($question_id, $active_id) 2739 { 2740 $ilDB = $this->db; 2741 $result_array = array(); 2742 $result = $ilDB->queryF( 2743 "SELECT * FROM svy_answer WHERE question_fi = %s AND active_fi = %s", 2744 array('integer','integer'), 2745 array($question_id, $active_id) 2746 ); 2747 if ($result->numRows() >= 1) { 2748 while ($row = $ilDB->fetchAssoc($result)) { 2749 array_push($result_array, $row); 2750 } 2751 return $result_array; 2752 } else { 2753 return $result_array; 2754 } 2755 } 2756 2757 /** 2758 * Starts the survey creating an entry in the database 2759 * 2760 * @param integer $user_id The database id of the user who starts the survey 2761 * @access public 2762 */ 2763 public function startSurvey($user_id, $anonymous_id, $appraisee_id) 2764 { 2765 $ilDB = $this->db; 2766 2767 if ($this->getAnonymize() && (strlen($anonymous_id) == 0)) { 2768 return; 2769 } 2770 2771 if (strcmp($user_id, "") == 0) { 2772 if ($user_id == ANONYMOUS_USER_ID) { 2773 $user_id = 0; 2774 } 2775 } 2776 $next_id = $ilDB->nextId('svy_finished'); 2777 $affectedRows = $ilDB->manipulateF( 2778 "INSERT INTO svy_finished (finished_id, survey_fi, user_fi, anonymous_id, state, tstamp, appr_id) " . 2779 "VALUES (%s, %s, %s, %s, %s, %s, %s)", 2780 array('integer','integer','integer','text','text','integer','integer'), 2781 array($next_id, $this->getSurveyId(), $user_id, $anonymous_id, 0, time(), $appraisee_id) 2782 ); 2783 return $next_id; 2784 } 2785 2786 /** 2787 * Finishes the survey creating an entry in the database 2788 * 2789 * @param integer $user_id The database id of the user who finishes the survey 2790 * @access public 2791 */ 2792 public function finishSurvey($finished_id) 2793 { 2794 $ilDB = $this->db; 2795 2796 $ilDB->manipulateF( 2797 "UPDATE svy_finished SET state = %s, tstamp = %s" . 2798 " WHERE survey_fi = %s AND finished_id = %s", 2799 array('text','integer','integer','integer'), 2800 array(1, time(), $this->getSurveyId(), $finished_id) 2801 ); 2802 2803 // self eval writes skills on finishing 2804 if ($this->getMode() == ilObjSurvey::MODE_SELF_EVAL) { 2805 $user = $this->getUserDataFromActiveId($finished_id); 2806 $sskill = new ilSurveySkill($this); 2807 $sskill->writeSelfEvalSkills($user['usr_id']); 2808 } 2809 2810 $this->checkTutorNotification(); 2811 } 2812 2813 /** 2814 * Sets the number of the active survey page 2815 * 2816 * @param integer $finished_id The database id of the active user 2817 * @param integer $page_id The index of the page 2818 * @access public 2819 */ 2820 public function setPage($finished_id, $page_id) 2821 { 2822 $ilDB = $this->db; 2823 2824 $affectedRows = $ilDB->manipulateF( 2825 "UPDATE svy_finished SET lastpage = %s WHERE finished_id = %s", 2826 array('integer','integer'), 2827 array(($page_id) ? $page_id : 0, $finished_id) 2828 ); 2829 } 2830 2831 /** 2832 * @param $a_user_id user who did the survey 2833 * @param $a_anonymize_id 2834 * @param $a_appr_id 2835 */ 2836 public function sendNotificationMail($a_user_id, $a_anonymize_id, $a_appr_id) 2837 { 2838 // #12755 2839 $placeholders = array( 2840 "FIRST_NAME" => "firstname", 2841 "LAST_NAME" => "lastname", 2842 "LOGIN" => "login", 2843 // old style 2844 "firstname" => "firstname" 2845 ); 2846 2847 //mailaddresses is just text split by commas. 2848 //sendMail can send emails if it gets an user id or an email as first parameter. 2849 $recipients = preg_split('/,/', $this->mailaddresses); 2850 foreach ($recipients as $recipient) { 2851 // #11298 2852 $ntf = new ilSystemNotification(); 2853 $ntf->setLangModules(array("survey")); 2854 $ntf->setRefId($this->getRefId()); 2855 $ntf->setSubjectLangId('finished_mail_subject'); 2856 2857 $messagetext = $this->mailparticipantdata; 2858 if (trim($messagetext)) { 2859 if (!$this->hasAnonymizedResults()) { 2860 $data = ilObjUser::_getUserData(array($a_user_id)); 2861 $data = $data[0]; 2862 } 2863 foreach ($placeholders as $key => $mapping) { 2864 if ($this->hasAnonymizedResults()) { // #16480 2865 $messagetext = str_replace('[' . $key . ']', '', $messagetext); 2866 } else { 2867 $messagetext = str_replace('[' . $key . ']', trim($data[$mapping]), $messagetext); 2868 } 2869 } 2870 $ntf->setIntroductionDirect($messagetext); 2871 } else { 2872 $ntf->setIntroductionLangId('survey_notification_finished_introduction'); 2873 } 2874 2875 // 360°? add appraisee data 2876 if ($a_appr_id) { 2877 $ntf->addAdditionalInfo( 2878 'survey_360_appraisee', 2879 ilUserUtil::getNamePresentation($a_appr_id) 2880 ); 2881 } 2882 2883 $active_id = $this->getActiveID($a_user_id, $a_anonymize_id, $a_appr_id); 2884 $ntf->addAdditionalInfo( 2885 'results', 2886 $this->getParticipantTextResults($active_id), 2887 true 2888 ); 2889 2890 $ntf->setGotoLangId('survey_notification_tutor_link'); 2891 $ntf->setReasonLangId('survey_notification_finished_reason'); 2892 2893 if (is_numeric($recipient)) { 2894 $lng = $ntf->getUserLanguage($recipient); 2895 $ntf->sendMail(array($recipient), null, null); 2896 } else { 2897 $recipient = trim($recipient); 2898 $user_ids = ilObjUser::getUserIdsByEmail($recipient); 2899 if (empty($user_ids)) { 2900 $ntf->sendMail(array($recipient), null, null); 2901 } else { 2902 foreach ($user_ids as $user_id) { 2903 $lng = $ntf->getUserLanguage($user_id); 2904 $ntf->sendMail(array($user_id), null, null); 2905 } 2906 } 2907 } 2908 } 2909 } 2910 2911 protected function getParticipantTextResults($active_id) 2912 { 2913 $textresult = ""; 2914 $userResults = &$this->getUserSpecificResults(array($active_id)); 2915 $questions = &$this->getSurveyQuestions(true); 2916 $questioncounter = 1; 2917 foreach ($questions as $question_id => $question_data) { 2918 $textresult .= $questioncounter++ . ". " . $question_data["title"] . "\n"; 2919 $found = $userResults[$question_id][$active_id]; 2920 $text = ""; 2921 if (is_array($found)) { 2922 $text = implode("\n", $found); 2923 } else { 2924 $text = $found; 2925 } 2926 if (strlen($text) == 0) { 2927 $text = self::getSurveySkippedValue(); 2928 } 2929 $text = str_replace("<br />", "\n", $text); 2930 $textresult .= $text . "\n\n"; 2931 } 2932 return $textresult; 2933 } 2934 2935 /** 2936 * Checks if a user already started a survey 2937 * 2938 * @param integer $user_id The database id of the user 2939 * @return mixed false, if the user has not started the survey, 0 if the user has started the survey but not finished it, 1 if the user has finished the survey 2940 * @access public 2941 */ 2942 public function isSurveyStarted($user_id, $anonymize_id, $appr_id = 0) 2943 { 2944 $ilDB = $this->db; 2945 2946 // #15031 - should not matter if code was used by registered or anonymous (each code must be unique) 2947 if ($anonymize_id) { 2948 $result = $ilDB->queryF( 2949 "SELECT * FROM svy_finished" . 2950 " WHERE survey_fi = %s AND anonymous_id = %s AND appr_id = %s", 2951 array('integer','text','integer'), 2952 array($this->getSurveyId(), $anonymize_id, $appr_id) 2953 ); 2954 } else { 2955 $result = $ilDB->queryF( 2956 "SELECT * FROM svy_finished" . 2957 " WHERE survey_fi = %s AND user_fi = %s AND appr_id = %s", 2958 array('integer','integer','integer'), 2959 array($this->getSurveyId(), $user_id, $appr_id) 2960 ); 2961 } 2962 if ($result->numRows() == 0) { 2963 return false; 2964 } else { 2965 $row = $ilDB->fetchAssoc($result); 2966 // yes, we are doing it this way 2967 $_SESSION["finished_id"][$this->getId()] = $row["finished_id"]; 2968 2969 return (int) $row["state"]; 2970 } 2971 } 2972 2973 /** 2974 * Checks if a user already started a survey 2975 * 2976 * @param integer $user_id The database id of the user 2977 * @return mixed false, if the user has not started the survey, 0 if the user has started the survey but not finished it, 1 if the user has finished the survey 2978 * @access public 2979 */ 2980 public function getActiveID($user_id, $anonymize_id, $appr_id) 2981 { 2982 $ilDB = $this->db; 2983 2984 // see self::isSurveyStarted() 2985 2986 // #15031 - should not matter if code was used by registered or anonymous (each code must be unique) 2987 if ($anonymize_id) { 2988 $result = $ilDB->queryF( 2989 "SELECT finished_id FROM svy_finished" . 2990 " WHERE survey_fi = %s AND anonymous_id = %s AND appr_id = %s", 2991 array('integer','text','integer'), 2992 array($this->getSurveyId(), $anonymize_id, $appr_id) 2993 ); 2994 } else { 2995 $result = $ilDB->queryF( 2996 "SELECT finished_id FROM svy_finished" . 2997 " WHERE survey_fi = %s AND user_fi = %s AND appr_id = %s", 2998 array('integer','integer','integer'), 2999 array($this->getSurveyId(), $user_id, $appr_id) 3000 ); 3001 } 3002 if ($result->numRows() == 0) { 3003 return false; 3004 } else { 3005 $row = $ilDB->fetchAssoc($result); 3006 return $row["finished_id"]; 3007 } 3008 } 3009 3010 /** 3011 * Returns the question id of the last active page a user visited in a survey 3012 * 3013 * @param integer $active_id The active id of the user 3014 * @return mixed Empty string if the user has not worked through a page, question id of the last page otherwise 3015 * @access public 3016 */ 3017 public function getLastActivePage($active_id) 3018 { 3019 $ilDB = $this->db; 3020 $result = $ilDB->queryF( 3021 "SELECT lastpage FROM svy_finished WHERE finished_id = %s", 3022 array('integer'), 3023 array($active_id) 3024 ); 3025 if ($result->numRows() == 0) { 3026 return ""; 3027 } else { 3028 $row = $ilDB->fetchAssoc($result); 3029 return ($row["lastpage"]) ? $row["lastpage"] : ''; 3030 } 3031 } 3032 3033 /** 3034 * Checks if a constraint is valid 3035 * 3036 * @param array $constraint_data The database row containing the constraint data 3037 * @param array $working_data The user input of the related question 3038 * @return boolean true if the constraint is valid, otherwise false 3039 * @access public 3040 */ 3041 public function checkConstraint($constraint_data, $working_data) 3042 { 3043 if (!is_array($working_data) || count($working_data) == 0) { 3044 return 0; 3045 } 3046 3047 if ((count($working_data) == 1) and (strcmp($working_data[0]["value"], "") == 0)) { 3048 return 0; 3049 } 3050 3051 $found = false; 3052 foreach ($working_data as $data) { 3053 switch ($constraint_data["short"]) { 3054 case "<": 3055 if ($data["value"] < $constraint_data["value"]) { 3056 $found = true; 3057 } 3058 break; 3059 3060 case "<=": 3061 if ($data["value"] <= $constraint_data["value"]) { 3062 $found = true; 3063 } 3064 break; 3065 3066 case "=": 3067 if ($data["value"] == $constraint_data["value"]) { 3068 $found = true; 3069 } 3070 break; 3071 3072 case "<>": 3073 if ($data["value"] <> $constraint_data["value"]) { 3074 $found = true; 3075 } 3076 break; 3077 3078 case ">=": 3079 if ($data["value"] >= $constraint_data["value"]) { 3080 $found = true; 3081 } 3082 break; 3083 3084 case ">": 3085 if ($data["value"] > $constraint_data["value"]) { 3086 $found = true; 3087 } 3088 break; 3089 } 3090 if ($found) { 3091 break; 3092 } 3093 } 3094 3095 return (int) $found; 3096 } 3097 3098 public static function _hasDatasets($survey_id) 3099 { 3100 global $DIC; 3101 3102 $ilDB = $DIC->database(); 3103 3104 $result = $ilDB->queryF( 3105 "SELECT finished_id FROM svy_finished WHERE survey_fi = %s", 3106 array('integer'), 3107 array($survey_id) 3108 ); 3109 return ($result->numRows()) ? true : false; 3110 } 3111 3112 /** 3113 * Get the finished id's of all survey participants 3114 * 3115 * @return array An array containing finished_id's of all survey participants 3116 * @access public 3117 */ 3118 public function &getSurveyFinishedIds() 3119 { 3120 $ilDB = $this->db; 3121 $ilLog = $this->log; 3122 3123 $users = array(); 3124 $result = $ilDB->queryF( 3125 "SELECT * FROM svy_finished WHERE survey_fi = %s", 3126 array('integer'), 3127 array($this->getSurveyId()) 3128 ); 3129 if ($result->numRows()) { 3130 while ($row = $ilDB->fetchAssoc($result)) { 3131 array_push($users, $row["finished_id"]); 3132 } 3133 } 3134 return $users; 3135 } 3136 3137 /** 3138 * Calculates the evaluation data for the user specific results 3139 * 3140 * @return array An array containing the user specific results 3141 * @access public 3142 */ 3143 public function getUserSpecificResults($finished_ids) 3144 { 3145 $evaluation = array(); 3146 3147 include_once "./Modules/SurveyQuestionPool/classes/class.SurveyQuestion.php"; 3148 foreach (array_keys($this->getSurveyQuestions()) as $question_id) { 3149 // get question instance 3150 $question_type = SurveyQuestion::_getQuestionType($question_id); 3151 SurveyQuestion::_includeClass($question_type); 3152 $question = new $question_type(); 3153 $question->loadFromDb($question_id); 3154 3155 $q_eval = SurveyQuestion::_instanciateQuestionEvaluation($question_id, $finished_ids); 3156 $q_res = $q_eval->getResults(); 3157 3158 $data = array(); 3159 foreach ($finished_ids as $user_id) { 3160 $data[$user_id] = $q_eval->parseUserSpecificResults($q_res, $user_id); 3161 } 3162 3163 $evaluation[$question_id] = $data; 3164 } 3165 3166 return $evaluation; 3167 } 3168 3169 /** 3170 * Returns the user information from an active_id (survey_finished.finished_id) 3171 * 3172 * @param integer $active_id The active id of the user 3173 * @return array An array containing the user data 3174 * @access public 3175 */ 3176 public function getUserDataFromActiveId($active_id, $force_non_anonymous = false) 3177 { 3178 $ilDB = $this->db; 3179 3180 $surveySetting = new ilSetting("survey"); 3181 $use_anonymous_id = array_key_exists("use_anonymous_id", $_GET) ? $_GET["use_anonymous_id"] : $surveySetting->get("use_anonymous_id"); 3182 $result = $ilDB->queryF( 3183 "SELECT * FROM svy_finished WHERE finished_id = %s", 3184 array('integer'), 3185 array($active_id) 3186 ); 3187 $row = array(); 3188 $foundrows = $result->numRows(); 3189 if ($foundrows) { 3190 $row = $ilDB->fetchAssoc($result); 3191 } 3192 $name = ($use_anonymous_id) ? $row["anonymous_id"] : $this->lng->txt("anonymous"); 3193 $userdata = array( 3194 "fullname" => $name, 3195 "sortname" => $name, 3196 "firstname" => "", 3197 "lastname" => "", 3198 "login" => "", 3199 "gender" => "", 3200 "active_id" => "$active_id" 3201 ); 3202 if ($foundrows) { 3203 if (($row["user_fi"] > 0) && 3204 (($row["user_fi"] != ANONYMOUS_USER_ID && 3205 !$this->hasAnonymizedResults() && 3206 !$this->get360Mode()) || // 360° uses ANONYMIZE_CODE_ALL which is wrong - see ilObjSurveyGUI::afterSave() 3207 (bool) $force_non_anonymous)) { 3208 include_once './Services/User/classes/class.ilObjUser.php'; 3209 if (strlen(ilObjUser::_lookupLogin($row["user_fi"])) == 0) { 3210 $userdata["fullname"] = $userdata["sortname"] = $this->lng->txt("deleted_user"); 3211 } else { 3212 $user = new ilObjUser($row["user_fi"]); 3213 $userdata['usr_id'] = $row['user_fi']; 3214 $userdata["fullname"] = $user->getFullname(); 3215 $gender = $user->getGender(); 3216 if (strlen($gender) == 1) { 3217 $gender = $this->lng->txt("gender_$gender"); 3218 } 3219 $userdata["gender"] = $gender; 3220 $userdata["firstname"] = $user->getFirstname(); 3221 $userdata["lastname"] = $user->getLastname(); 3222 $userdata["sortname"] = $user->getLastname() . ", " . $user->getFirstname(); 3223 $userdata["login"] = $user->getLogin(); 3224 } 3225 } 3226 } 3227 return $userdata; 3228 } 3229 3230 /** 3231 * Calculates the evaluation data for a given user or anonymous id 3232 * 3233 * @param array $questions An array containing all relevant information on the survey's questions 3234 * @param integer $user_id The database id of the user 3235 * @param string $anonymous_id The unique anonymous id for an anonymous survey 3236 * @return array An array containing the evaluation parameters for the user 3237 * @access public 3238 */ 3239 public function &getEvaluationByUser($questions, $active_id) 3240 { 3241 $ilDB = $this->db; 3242 3243 // collect all answers 3244 $answers = array(); 3245 $result = $ilDB->queryF( 3246 "SELECT * FROM svy_answer WHERE active_fi = %s", 3247 array('integer'), 3248 array($active_id) 3249 ); 3250 while ($row = $ilDB->fetchAssoc($result)) { 3251 if (!is_array($answers[$row["question_fi"]])) { 3252 $answers[$row["question_fi"]] = array(); 3253 } 3254 array_push($answers[$row["question_fi"]], $row); 3255 } 3256 $userdata = $this->getUserDataFromActiveId($active_id); 3257 $resultset = array( 3258 "name" => $userdata["fullname"], 3259 "firstname" => $userdata["firstname"], 3260 "lastname" => $userdata["lastname"], 3261 "login" => $userdata["login"], 3262 "gender" => $userdata["gender"], 3263 "answers" => array() 3264 ); 3265 foreach ($questions as $key => $question) { 3266 if (array_key_exists($key, $answers)) { 3267 $resultset["answers"][$key] = $answers[$key]; 3268 } else { 3269 $resultset["answers"][$key] = array(); 3270 } 3271 sort($resultset["answers"][$key]); 3272 } 3273 return $resultset; 3274 } 3275 3276 /** 3277 * Calculates the data for the output of the question browser 3278 * 3279 * @access public 3280 */ 3281 public function getQuestionsTable($arrFilter) 3282 { 3283 $ilUser = $this->user; 3284 $ilDB = $this->db; 3285 $where = ""; 3286 if (is_array($arrFilter)) { 3287 if (array_key_exists('title', $arrFilter) && strlen($arrFilter['title'])) { 3288 $where .= " AND " . $ilDB->like('svy_question.title', 'text', "%%" . $arrFilter['title'] . "%%"); 3289 } 3290 if (array_key_exists('description', $arrFilter) && strlen($arrFilter['description'])) { 3291 $where .= " AND " . $ilDB->like('svy_question.description', 'text', "%%" . $arrFilter['description'] . "%%"); 3292 } 3293 if (array_key_exists('author', $arrFilter) && strlen($arrFilter['author'])) { 3294 $where .= " AND " . $ilDB->like('svy_question.author', 'text', "%%" . $arrFilter['author'] . "%%"); 3295 } 3296 if (array_key_exists('type', $arrFilter) && strlen($arrFilter['type'])) { 3297 $where .= " AND svy_qtype.type_tag = " . $ilDB->quote($arrFilter['type'], 'text'); 3298 } 3299 if (array_key_exists('spl', $arrFilter) && strlen($arrFilter['spl'])) { 3300 $where .= " AND svy_question.obj_fi = " . $ilDB->quote($arrFilter['spl'], 'integer'); 3301 } 3302 } 3303 3304 $spls = &$this->getAvailableQuestionpools($use_obj_id = true, $could_be_offline = false, $showPath = false); 3305 $forbidden = ""; 3306 $forbidden = " AND " . $ilDB->in('svy_question.obj_fi', array_keys($spls), false, 'integer'); 3307 $forbidden .= " AND svy_question.complete = " . $ilDB->quote("1", 'text'); 3308 $existing = ""; 3309 $existing_questions = &$this->getExistingQuestions(); 3310 if (count($existing_questions)) { 3311 $existing = " AND " . $ilDB->in('svy_question.question_id', $existing_questions, true, 'integer'); 3312 } 3313 3314 include_once "./Modules/SurveyQuestionPool/classes/class.ilObjSurveyQuestionPool.php"; 3315 $trans = ilObjSurveyQuestionPool::_getQuestionTypeTranslations(); 3316 3317 $query_result = $ilDB->query("SELECT svy_question.*, svy_qtype.type_tag, svy_qtype.plugin, object_reference.ref_id" . 3318 " FROM svy_question, svy_qtype, object_reference" . 3319 " WHERE svy_question.original_id IS NULL" . $forbidden . $existing . 3320 " AND svy_question.obj_fi = object_reference.obj_id AND svy_question.tstamp > 0" . 3321 " AND svy_question.questiontype_fi = svy_qtype.questiontype_id " . $where); 3322 3323 $rows = array(); 3324 if ($query_result->numRows()) { 3325 while ($row = $ilDB->fetchAssoc($query_result)) { 3326 if (array_key_exists('spl_txt', $arrFilter) && strlen($arrFilter['spl_txt'])) { 3327 if (!stristr($spls[$row["obj_fi"]], $arrFilter['spl_txt'])) { 3328 continue; 3329 } 3330 } 3331 3332 $row['ttype'] = $trans[$row['type_tag']]; 3333 if ($row["plugin"]) { 3334 if ($this->isPluginActive($row["type_tag"])) { 3335 array_push($rows, $row); 3336 } 3337 } else { 3338 array_push($rows, $row); 3339 } 3340 } 3341 } 3342 return $rows; 3343 } 3344 3345 /** 3346 * Calculates the data for the output of the questionblock browser 3347 * 3348 * @access public 3349 */ 3350 public function getQuestionblocksTable($arrFilter) 3351 { 3352 $ilUser = $this->user; 3353 $ilDB = $this->db; 3354 3355 $where = ""; 3356 if (is_array($arrFilter)) { 3357 if (array_key_exists('title', $arrFilter) && strlen($arrFilter['title'])) { 3358 $where .= " AND " . $ilDB->like('svy_qblk.title', 'text', "%%" . $arrFilter['title'] . "%%"); 3359 } 3360 } 3361 3362 $query_result = $ilDB->query("SELECT svy_qblk.*, svy_svy.obj_fi FROM svy_qblk , svy_qblk_qst, svy_svy WHERE " . 3363 "svy_qblk.questionblock_id = svy_qblk_qst.questionblock_fi AND svy_svy.survey_id = svy_qblk_qst.survey_fi " . 3364 "$where GROUP BY svy_qblk.questionblock_id, svy_qblk.title, svy_qblk.show_questiontext, svy_qblk.show_blocktitle, " . 3365 "svy_qblk.owner_fi, svy_qblk.tstamp, svy_svy.obj_fi"); 3366 $rows = array(); 3367 if ($query_result->numRows()) { 3368 $survey_ref_ids = ilUtil::_getObjectsByOperations("svy", "write"); 3369 $surveytitles = array(); 3370 foreach ($survey_ref_ids as $survey_ref_id) { 3371 $survey_id = ilObject::_lookupObjId($survey_ref_id); 3372 $surveytitles[$survey_id] = ilObject::_lookupTitle($survey_id); 3373 } 3374 while ($row = $ilDB->fetchAssoc($query_result)) { 3375 $questions_array = &$this->getQuestionblockQuestions($row["questionblock_id"]); 3376 $counter = 1; 3377 foreach ($questions_array as $key => $value) { 3378 $questions_array[$key] = "$counter. $value"; 3379 $counter++; 3380 } 3381 if (strlen($surveytitles[$row["obj_fi"]])) { // only questionpools which are not in trash 3382 $rows[$row["questionblock_id"]] = array( 3383 "questionblock_id" => $row["questionblock_id"], 3384 "title" => $row["title"], 3385 "svy" => $surveytitles[$row["obj_fi"]], 3386 "contains" => join(", ", $questions_array), 3387 "owner" => $row["owner_fi"] 3388 ); 3389 } 3390 } 3391 } 3392 return $rows; 3393 } 3394 3395 /** 3396 * Returns a QTI xml representation of the survey 3397 * 3398 * @return string The QTI xml representation of the survey 3399 * @access public 3400 */ 3401 public function toXML() 3402 { 3403 include_once("./Services/Xml/classes/class.ilXmlWriter.php"); 3404 $a_xml_writer = new ilXmlWriter; 3405 // set xml header 3406 $a_xml_writer->xmlHeader(); 3407 $attrs = array( 3408 "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance", 3409 "xsi:noNamespaceSchemaLocation" => "http://www.ilias.de/download/xsd/ilias_survey_4_2.xsd" 3410 ); 3411 $a_xml_writer->xmlStartTag("surveyobject", $attrs); 3412 $attrs = array( 3413 "id" => $this->getSurveyId(), 3414 "title" => $this->getTitle() 3415 ); 3416 $a_xml_writer->xmlStartTag("survey", $attrs); 3417 3418 $a_xml_writer->xmlElement("description", null, $this->getDescription()); 3419 $a_xml_writer->xmlElement("author", null, $this->getAuthor()); 3420 $a_xml_writer->xmlStartTag("objectives"); 3421 $attrs = array( 3422 "label" => "introduction" 3423 ); 3424 $this->addMaterialTag($a_xml_writer, $this->getIntroduction(), true, true, $attrs); 3425 $attrs = array( 3426 "label" => "outro" 3427 ); 3428 $this->addMaterialTag($a_xml_writer, $this->getOutro(), true, true, $attrs); 3429 $a_xml_writer->xmlEndTag("objectives"); 3430 3431 if ($this->getAnonymize()) { 3432 $attribs = array("enabled" => "1"); 3433 } else { 3434 $attribs = array("enabled" => "0"); 3435 } 3436 $a_xml_writer->xmlElement("anonymisation", $attribs); 3437 $a_xml_writer->xmlStartTag("restrictions"); 3438 if ($this->getAnonymize() == 2) { 3439 $attribs = array("type" => "free"); 3440 } else { 3441 $attribs = array("type" => "restricted"); 3442 } 3443 $a_xml_writer->xmlElement("access", $attribs); 3444 if ($this->getStartDate()) { 3445 $attrs = array("type" => "date"); 3446 preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getStartDate(), $matches); 3447 $a_xml_writer->xmlElement("startingtime", $attrs, sprintf("%04d-%02d-%02dT%02d:%02d:00", $matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6])); 3448 } 3449 if ($this->getEndDate()) { 3450 $attrs = array("type" => "date"); 3451 preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getEndDate(), $matches); 3452 $a_xml_writer->xmlElement("endingtime", $attrs, sprintf("%04d-%02d-%02dT%02d:%02d:00", $matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6])); 3453 } 3454 $a_xml_writer->xmlEndTag("restrictions"); 3455 3456 // constraints 3457 $pages = &$this->getSurveyPages(); 3458 $hasconstraints = false; 3459 foreach ($pages as $question_array) { 3460 foreach ($question_array as $question) { 3461 if (count($question["constraints"])) { 3462 $hasconstraints = true; 3463 } 3464 } 3465 } 3466 3467 if ($hasconstraints) { 3468 $a_xml_writer->xmlStartTag("constraints"); 3469 foreach ($pages as $question_array) { 3470 foreach ($question_array as $question) { 3471 if (count($question["constraints"])) { 3472 // found constraints 3473 foreach ($question["constraints"] as $constraint) { 3474 $attribs = array( 3475 "sourceref" => $question["question_id"], 3476 "destref" => $constraint["question"], 3477 "relation" => $constraint["short"], 3478 "value" => $constraint["value"], 3479 "conjunction" => $constraint["conjunction"] 3480 ); 3481 $a_xml_writer->xmlElement("constraint", $attribs); 3482 } 3483 } 3484 } 3485 } 3486 $a_xml_writer->xmlEndTag("constraints"); 3487 } 3488 3489 // add the rest of the preferences in qtimetadata tags, because there is no correspondent definition in QTI 3490 $a_xml_writer->xmlStartTag("metadata"); 3491 3492 $custom_properties = array(); 3493 $custom_properties["evaluation_access"] = $this->getEvaluationAccess(); 3494 $custom_properties["status"] = !$this->getOfflineStatus(); 3495 $custom_properties["display_question_titles"] = $this->getShowQuestionTitles(); 3496 $custom_properties["pool_usage"] = (int) $this->getPoolUsage(); 3497 3498 $custom_properties["own_results_view"] = (int) $this->hasViewOwnResults(); 3499 $custom_properties["own_results_mail"] = (int) $this->hasMailOwnResults(); 3500 $custom_properties["confirmation_mail"] = (int) $this->hasMailConfirmation(); 3501 3502 $custom_properties["anon_user_list"] = (int) $this->hasAnonymousUserList(); 3503 $custom_properties["mode"] = (int) $this->getMode(); 3504 $custom_properties["mode_360_self_eval"] = (int) $this->get360SelfEvaluation(); 3505 $custom_properties["mode_360_self_rate"] = (int) $this->get360SelfRaters(); 3506 $custom_properties["mode_360_self_appr"] = (int) $this->get360SelfAppraisee(); 3507 $custom_properties["mode_360_results"] = $this->get360Results(); 3508 $custom_properties["mode_skill_service"] = (int) $this->getSkillService(); 3509 $custom_properties["mode_self_eval_results"] = (int) $this->getSelfEvaluationResults(); 3510 3511 3512 // :TODO: skills? 3513 3514 // reminder/tutor notification are (currently?) not exportable 3515 3516 foreach ($custom_properties as $label => $value) { 3517 $a_xml_writer->xmlStartTag("metadatafield"); 3518 $a_xml_writer->xmlElement("fieldlabel", null, $label); 3519 $a_xml_writer->xmlElement("fieldentry", null, $value); 3520 $a_xml_writer->xmlEndTag("metadatafield"); 3521 } 3522 3523 $a_xml_writer->xmlStartTag("metadatafield"); 3524 $a_xml_writer->xmlElement("fieldlabel", null, "SCORM"); 3525 include_once "./Services/MetaData/classes/class.ilMD.php"; 3526 $md = new ilMD($this->getId(), 0, $this->getType()); 3527 $writer = new ilXmlWriter(); 3528 $md->toXml($writer); 3529 $metadata = $writer->xmlDumpMem(); 3530 $a_xml_writer->xmlElement("fieldentry", null, $metadata); 3531 $a_xml_writer->xmlEndTag("metadatafield"); 3532 3533 $a_xml_writer->xmlEndTag("metadata"); 3534 $a_xml_writer->xmlEndTag("survey"); 3535 3536 $attribs = array("id" => $this->getId()); 3537 $a_xml_writer->xmlStartTag("surveyquestions", $attribs); 3538 // add questionblock descriptions 3539 foreach ($pages as $question_array) { 3540 if (count($question_array) > 1) { 3541 $attribs = array("id" => $question_array[0]["question_id"]); 3542 $attribs = array("showQuestiontext" => $question_array[0]["questionblock_show_questiontext"], 3543 "showBlocktitle" => $question_array[0]["questionblock_show_blocktitle"]); 3544 $a_xml_writer->xmlStartTag("questionblock", $attribs); 3545 if (strlen($question_array[0]["questionblock_title"])) { 3546 $a_xml_writer->xmlElement("questionblocktitle", null, $question_array[0]["questionblock_title"]); 3547 } 3548 } 3549 foreach ($question_array as $question) { 3550 if (strlen($question["heading"])) { 3551 $a_xml_writer->xmlElement("textblock", null, $question["heading"]); 3552 } 3553 $questionObject = self::_instanciateQuestion($question["question_id"]); 3554 //questionObject contains all the fields from the database. (loadFromDb) 3555 //we don't need the value from svy_qst_oblig table, we already have the values from svy_question table. 3556 //if ($questionObject !== FALSE) $questionObject->insertXML($a_xml_writer, FALSE, $obligatory_states[$question["question_id"]]); 3557 if ($questionObject !== false) { 3558 $questionObject->insertXML($a_xml_writer, false); 3559 } 3560 } 3561 if (count($question_array) > 1) { 3562 $a_xml_writer->xmlEndTag("questionblock"); 3563 } 3564 } 3565 3566 $a_xml_writer->xmlEndTag("surveyquestions"); 3567 $a_xml_writer->xmlEndTag("surveyobject"); 3568 $xml = $a_xml_writer->xmlDumpMem(false); 3569 return $xml; 3570 } 3571 3572 /** 3573 * Creates an instance of a question with a given question id 3574 * 3575 * @param integer $question_id The question id 3576 * @return object The question instance 3577 * @access public 3578 */ 3579 public static function _instanciateQuestion($question_id) 3580 { 3581 if ($question_id < 1) { 3582 return false; 3583 } 3584 include_once "./Modules/SurveyQuestionPool/classes/class.SurveyQuestion.php"; 3585 $question_type = SurveyQuestion::_getQuestionType($question_id); 3586 if (strlen($question_type) == 0) { 3587 return false; 3588 } 3589 SurveyQuestion::_includeClass($question_type); 3590 $question = new $question_type(); 3591 $question->loadFromDb($question_id); 3592 return $question; 3593 } 3594 3595 /** 3596 * Locates the import directory and the xml file in a directory with an unzipped import file 3597 * 3598 * @return array An associative array containing "dir" (import directory) and "xml" (xml file) 3599 * @access private 3600 */ 3601 public function locateImportFiles($a_dir) 3602 { 3603 if (!is_dir($a_dir) || is_int(strpos($a_dir, ".."))) { 3604 return; 3605 } 3606 $importDirectory = ""; 3607 $xmlFile = ""; 3608 3609 $current_dir = opendir($a_dir); 3610 $files = array(); 3611 while ($entryname = readdir($current_dir)) { 3612 $files[] = $entryname; 3613 } 3614 3615 foreach ($files as $file) { 3616 if (is_dir($a_dir . "/" . $file) and ($file != "." and $file != "..")) { 3617 // found directory created by zip 3618 $importDirectory = $a_dir . "/" . $file; 3619 } 3620 } 3621 closedir($current_dir); 3622 if (strlen($importDirectory)) { 3623 // find the xml file 3624 $current_dir = opendir($importDirectory); 3625 $files = array(); 3626 while ($entryname = readdir($current_dir)) { 3627 $files[] = $entryname; 3628 } 3629 foreach ($files as $file) { 3630 if (@is_file($importDirectory . "/" . $file) && 3631 ($file != "." && $file != "..") && 3632 (preg_match("/^[0-9]{10}__[0-9]+__(svy_)*[0-9]+\.[A-Za-z]{1,3}$/", $file) || 3633 preg_match("/^[0-9]{10}__[0-9]+__(survey__)*[0-9]+\.[A-Za-z]{1,3}$/", $file))) { 3634 // found xml file 3635 $xmlFile = $importDirectory . "/" . $file; 3636 } 3637 } 3638 } 3639 return array("dir" => $importDirectory, "xml" => $xmlFile); 3640 } 3641 3642 /** 3643 * Imports a survey from XML into the ILIAS database 3644 * @param $file_info 3645 * @param $svy_qpl_id 3646 * @return string 3647 * @throws ilFileUtilsException 3648 * @throws ilInvalidSurveyImportFileException 3649 */ 3650 public function importObject($file_info, $svy_qpl_id) 3651 { 3652 if ($svy_qpl_id < 1) { 3653 $svy_qpl_id = -1; 3654 } 3655 // check if file was uploaded 3656 $source = $file_info["tmp_name"]; 3657 $error = ""; 3658 if (($source == 'none') || (!$source) || $file_info["error"] > UPLOAD_ERR_OK) { 3659 $error = $this->lng->txt("import_no_file_selected"); 3660 } 3661 // check correct file type 3662 $isXml = false; 3663 $isZip = false; 3664 if ((strcmp($file_info["type"], "text/xml") == 0) || (strcmp($file_info["type"], "application/xml") == 0)) { 3665 $this->log->debug("isXML"); 3666 $isXml = true; 3667 } 3668 // too many different mime-types, so we use the suffix 3669 $suffix = pathinfo($file_info["name"]); 3670 if (strcmp(strtolower($suffix["extension"]), "zip") == 0) { 3671 $this->log->debug("isZip"); 3672 $isZip = true; 3673 } 3674 if (!$isXml && !$isZip) { 3675 $error = $this->lng->txt("import_wrong_file_type"); 3676 $this->log->debug("Survey: Import error. Filetype was \"" . $file_info["type"] . "\""); 3677 } 3678 if (strlen($error) == 0) { 3679 // import file as a survey 3680 $import_dir = $this->getImportDirectory(); 3681 $import_subdir = ""; 3682 $importfile = ""; 3683 if ($isZip) { 3684 $importfile = $import_dir . "/" . $file_info["name"]; 3685 ilUtil::moveUploadedFile($source, $file_info["name"], $importfile); 3686 ilUtil::unzip($importfile); 3687 $found = $this->locateImportFiles($import_dir); 3688 if (!((strlen($found["dir"]) > 0) && (strlen($found["xml"]) > 0))) { 3689 $error = $this->lng->txt("wrong_import_file_structure"); 3690 return $error; 3691 } 3692 $importfile = $found["xml"]; 3693 $import_subdir = $found["dir"]; 3694 } else { 3695 $importfile = tempnam($import_dir, "survey_import"); 3696 ilUtil::moveUploadedFile($source, $file_info["name"], $importfile); 3697 } 3698 3699 $this->log->debug("Import file = $importfile"); 3700 $this->log->debug("Import subdir = $import_subdir"); 3701 3702 $fh = fopen($importfile, "r"); 3703 if (!$fh) { 3704 $error = $this->lng->txt("import_error_opening_file"); 3705 return $error; 3706 } 3707 $xml = fread($fh, filesize($importfile)); 3708 $result = fclose($fh); 3709 if (!$result) { 3710 $error = $this->lng->txt("import_error_closing_file"); 3711 return $error; 3712 } 3713 3714 unset($_SESSION["import_mob_xhtml"]); 3715 if (strpos($xml, "questestinterop")) { 3716 include_once("./Modules/Survey/exceptions/class.ilInvalidSurveyImportFileException.php"); 3717 throw new ilInvalidSurveyImportFileException("Unsupported survey version (< 3.8) found."); 3718 } else { 3719 $this->log->debug("survey id = " . $this->getId()); 3720 $this->log->debug("question pool id = " . $svy_qpl_id); 3721 3722 include_once("./Services/Export/classes/class.ilImport.php"); 3723 $imp = new ilImport(); 3724 $config = $imp->getConfig("Modules/Survey"); 3725 $config->setQuestionPoolID($svy_qpl_id); 3726 $imp->getMapping()->addMapping("Modules/Survey", "svy", 0, $this->getId()); 3727 $imp->importFromDirectory($import_subdir, "svy", "Modules/Survey"); 3728 $this->log->debug("config(Modules/survey)->getQuestionPoolId =" . $config->getQuestionPoolID()); 3729 return ""; 3730 3731 //old code 3732 include_once "./Services/Survey/classes/class.SurveyImportParser.php"; 3733 $import = new SurveyImportParser($svy_qpl_id, "", true); 3734 $import->setSurveyObject($this); 3735 $import->setXMLContent($xml); 3736 $import->startParsing(); 3737 } 3738 3739 if (is_array($_SESSION["import_mob_xhtml"])) { 3740 include_once "./Services/MediaObjects/classes/class.ilObjMediaObject.php"; 3741 include_once "./Services/RTE/classes/class.ilRTE.php"; 3742 include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php"; 3743 foreach ($_SESSION["import_mob_xhtml"] as $mob) { 3744 $importfile = $import_subdir . "/" . $mob["uri"]; 3745 if (file_exists($importfile)) { 3746 if (!$mob["type"]) { 3747 $mob["type"] = "svy:html"; 3748 } 3749 3750 $media_object = ilObjMediaObject::_saveTempFileAsMediaObject(basename($importfile), $importfile, false); 3751 3752 // survey mob 3753 if ($mob["type"] == "svy:html") { 3754 ilObjMediaObject::_saveUsage($media_object->getId(), "svy:html", $this->getId()); 3755 $this->setIntroduction(str_replace("src=\"" . $mob["mob"] . "\"", "src=\"" . "il_" . IL_INST_ID . "_mob_" . $media_object->getId() . "\"", $this->getIntroduction())); 3756 $this->setOutro(str_replace("src=\"" . $mob["mob"] . "\"", "src=\"" . "il_" . IL_INST_ID . "_mob_" . $media_object->getId() . "\"", $this->getOutro())); 3757 } 3758 // question mob 3759 elseif ($import->questions[$mob["id"]]) { 3760 $new_qid = $import->questions[$mob["id"]]; 3761 ilObjMediaObject::_saveUsage($media_object->getId(), $mob["type"], $new_qid); 3762 $new_question = SurveyQuestion::_instanciateQuestion($new_qid); 3763 $qtext = $new_question->getQuestiontext(); 3764 $qtext = ilRTE::_replaceMediaObjectImageSrc($qtext, 0); 3765 $qtext = str_replace("src=\"" . $mob["mob"] . "\"", "src=\"" . "il_" . IL_INST_ID . "_mob_" . $media_object->getId() . "\"", $qtext); 3766 $qtext = ilRTE::_replaceMediaObjectImageSrc($qtext, 1); 3767 $new_question->setQuestiontext($qtext); 3768 $new_question->saveToDb(); 3769 3770 // also fix existing original in pool 3771 if ($new_question->getOriginalId()) { 3772 $pool_question = SurveyQuestion::_instanciateQuestion($new_question->getOriginalId()); 3773 $pool_question->setQuestiontext($qtext); 3774 $pool_question->saveToDb(); 3775 } 3776 } 3777 } else { 3778 $ilLog = $this->log; 3779 $ilLog->write("Error: Could not open XHTML mob file for test introduction during test import. File $importfile does not exist!"); 3780 } 3781 } 3782 $this->setIntroduction(ilRTE::_replaceMediaObjectImageSrc($this->getIntroduction(), 1)); 3783 $this->setOutro(ilRTE::_replaceMediaObjectImageSrc($this->getOutro(), 1)); 3784 $this->saveToDb(); 3785 } 3786 3787 // delete import directory 3788 ilUtil::delDir($this->getImportDirectory()); 3789 } 3790 return $error; 3791 } 3792 3793 /** 3794 * Clone object 3795 * 3796 * @access public 3797 * @param int ref_id of target container 3798 * @param int copy id 3799 * @return object new svy object 3800 */ 3801 public function cloneObject($a_target_id, $a_copy_id = 0, $a_omit_tree = false) 3802 { 3803 $ilDB = $this->db; 3804 3805 $this->loadFromDb(); 3806 3807 //survey mode 3808 $svy_type = $this->getMode(); 3809 3810 // Copy settings 3811 $newObj = parent::cloneObject($a_target_id, $a_copy_id, $a_omit_tree); 3812 $this->cloneMetaData($newObj); 3813 $newObj->updateMetaData(); 3814 3815 $newObj->setAuthor($this->getAuthor()); 3816 $newObj->setIntroduction($this->getIntroduction()); 3817 $newObj->setOutro($this->getOutro()); 3818 $newObj->setEvaluationAccess($this->getEvaluationAccess()); 3819 $newObj->setStartDate($this->getStartDate()); 3820 $newObj->setEndDate($this->getEndDate()); 3821 $newObj->setInvitation($this->getInvitation()); 3822 $newObj->setInvitationMode($this->getInvitationMode()); 3823 $newObj->setAnonymize($this->getAnonymize()); 3824 $newObj->setShowQuestionTitles($this->getShowQuestionTitles()); 3825 $newObj->setTemplate($this->getTemplate()); 3826 $newObj->setPoolUsage($this->getPoolUsage()); 3827 $newObj->setViewOwnResults($this->hasViewOwnResults()); 3828 $newObj->setMailOwnResults($this->hasMailOwnResults()); 3829 $newObj->setMailConfirmation($this->hasMailConfirmation()); 3830 $newObj->setAnonymousUserList($this->hasAnonymousUserList()); 3831 3832 // #12661 3833 if ($this->get360Mode()) { 3834 $newObj->setMode(ilObjSurvey::MODE_360); 3835 $newObj->set360SelfEvaluation($this->get360SelfEvaluation()); 3836 $newObj->set360SelfAppraisee($this->get360SelfAppraisee()); 3837 $newObj->set360SelfRaters($this->get360SelfRaters()); 3838 $newObj->set360Results($this->get360Results()); 3839 $newObj->setSkillService($this->getSkillService()); 3840 } 3841 //svy mode self eval: skills + view results 3842 if ($svy_type == ilObjSurvey::MODE_SELF_EVAL) { 3843 $newObj->setMode(ilObjSurvey::MODE_SELF_EVAL); 3844 $newObj->setSkillService($this->getSkillService()); 3845 $newObj->setSelfEvaluationResults($this->getSelfEvaluationResults()); 3846 } 3847 3848 // reminder/notification 3849 $newObj->setReminderStatus($this->getReminderStatus()); 3850 $newObj->setReminderStart($this->getReminderStart()); 3851 $newObj->setReminderEnd($this->getReminderEnd()); 3852 $newObj->setReminderFrequency($this->getReminderFrequency()); 3853 $newObj->setReminderTarget($this->getReminderTarget()); 3854 $newObj->setReminderTemplate($this->getReminderTemplate()); 3855 // reminder_last_sent must not be copied! 3856 $newObj->setTutorNotificationStatus($this->getTutorNotificationStatus()); 3857 $newObj->setTutorNotificationRecipients($this->getTutorNotificationRecipients()); 3858 $newObj->setTutorNotificationTarget($this->getTutorNotificationTarget()); 3859 3860 $newObj->setMailNotification($this->getMailNotification()); 3861 $newObj->setMailAddresses($this->getMailAddresses()); 3862 $newObj->setMailParticipantData($this->getMailParticipantData()); 3863 3864 $question_pointer = array(); 3865 // clone the questions 3866 $mapping = array(); 3867 include_once "./Modules/SurveyQuestionPool/classes/class.SurveyQuestion.php"; 3868 3869 foreach ($this->questions as $key => $question_id) { 3870 /** @var $question SurveyQuestion */ 3871 $question = self::_instanciateQuestion($question_id); 3872 if ($question) { // #10824 3873 $question->id = -1; 3874 $original_id = SurveyQuestion::_getOriginalId($question_id, false); 3875 $question->setObjId($newObj->getId()); 3876 $question->saveToDb($original_id); 3877 $newObj->questions[$key] = $question->getId(); 3878 $question_pointer[$question_id] = $question->getId(); 3879 $mapping[$question_id] = $question->getId(); 3880 } 3881 } 3882 3883 //copy online status if object is not the root copy object 3884 $cp_options = ilCopyWizardOptions::_getInstance($a_copy_id); 3885 3886 if (!$cp_options->isRootNode($this->getRefId())) { 3887 $newObj->setOfflineStatus($this->getOfflineStatus()); 3888 } 3889 3890 $newObj->saveToDb(); 3891 $newObj->cloneTextblocks($mapping); 3892 3893 // #14929 3894 if (($svy_type == ilObjSurvey::MODE_360 || $svy_type == ilObjSurvey::MODE_SELF_EVAL) && 3895 $this->getSkillService()) { 3896 include_once "./Modules/Survey/classes/class.ilSurveySkill.php"; 3897 $src_skills = new ilSurveySkill($this); 3898 $tgt_skills = new ilSurveySkill($newObj); 3899 3900 foreach ($mapping as $src_qst_id => $tgt_qst_id) { 3901 $qst_skill = $src_skills->getSkillForQuestion($src_qst_id); 3902 if ($qst_skill) { 3903 $tgt_skills->addQuestionSkillAssignment($tgt_qst_id, $qst_skill["base_skill_id"], $qst_skill["tref_id"]); 3904 } 3905 } 3906 } 3907 3908 // clone the questionblocks 3909 $questionblocks = array(); 3910 $questionblock_questions = array(); 3911 $result = $ilDB->queryF( 3912 "SELECT * FROM svy_qblk_qst WHERE survey_fi = %s", 3913 array('integer'), 3914 array($this->getSurveyId()) 3915 ); 3916 if ($result->numRows() > 0) { 3917 while ($row = $ilDB->fetchAssoc($result)) { 3918 array_push($questionblock_questions, $row); 3919 $questionblocks[$row["questionblock_fi"]] = $row["questionblock_fi"]; 3920 } 3921 } 3922 // create new questionblocks 3923 foreach ($questionblocks as $key => $value) { 3924 $questionblock = self::_getQuestionblock($key); 3925 $questionblock_id = self::_addQuestionblock($questionblock["title"], $questionblock["owner_fi"], $questionblock["show_questiontext"], $questionblock["show_blocktitle"]); 3926 $questionblocks[$key] = $questionblock_id; 3927 } 3928 // create new questionblock questions 3929 foreach ($questionblock_questions as $key => $value) { 3930 if ($questionblocks[$value["questionblock_fi"]] && 3931 $question_pointer[$value["question_fi"]]) { 3932 $next_id = $ilDB->nextId('svy_qblk_qst'); 3933 $affectedRows = $ilDB->manipulateF( 3934 "INSERT INTO svy_qblk_qst (qblk_qst_id, survey_fi, questionblock_fi, question_fi) " . 3935 "VALUES (%s, %s, %s, %s)", 3936 array('integer','integer','integer','integer'), 3937 array($next_id, $newObj->getSurveyId(), $questionblocks[$value["questionblock_fi"]], $question_pointer[$value["question_fi"]]) 3938 ); 3939 } 3940 } 3941 3942 // clone the constraints 3943 $constraints = self::_getConstraints($this->getSurveyId()); 3944 $newConstraints = array(); 3945 foreach ($constraints as $key => $constraint) { 3946 if ($question_pointer[$constraint["for_question"]] && 3947 $question_pointer[$constraint["question"]]) { 3948 if (!array_key_exists($constraint['id'], $newConstraints)) { 3949 $constraint_id = $newObj->addConstraint($question_pointer[$constraint["question"]], $constraint["relation_id"], $constraint["value"], $constraint['conjunction']); 3950 $newConstraints[$constraint['id']] = $constraint_id; 3951 } 3952 $newObj->addConstraintToQuestion($question_pointer[$constraint["for_question"]], $newConstraints[$constraint['id']]); 3953 } 3954 } 3955 3956 // #16210 - clone LP settings 3957 include_once('./Services/Tracking/classes/class.ilLPObjSettings.php'); 3958 $obj_settings = new ilLPObjSettings($this->getId()); 3959 $obj_settings->cloneSettings($newObj->getId()); 3960 unset($obj_settings); 3961 3962 return $newObj; 3963 } 3964 3965 public function getTextblock($question_id) 3966 { 3967 $ilDB = $this->db; 3968 $result = $ilDB->queryF( 3969 "SELECT * FROM svy_svy_qst WHERE question_fi = %s", 3970 array('integer'), 3971 array($question_id) 3972 ); 3973 if ($result->numRows()) { 3974 $row = $ilDB->fetchAssoc($result); 3975 return $row["heading"]; 3976 } else { 3977 return ""; 3978 } 3979 } 3980 3981 /** 3982 * Clones the textblocks of survey questions 3983 * 3984 * @access public 3985 */ 3986 public function cloneTextblocks($mapping) 3987 { 3988 foreach ($mapping as $original_id => $new_id) { 3989 $textblock = $this->getTextblock($original_id); 3990 include_once "./Services/AdvancedEditing/classes/class.ilObjAdvancedEditing.php"; 3991 $this->saveHeading(ilUtil::stripSlashes($textblock, true, ilObjAdvancedEditing::_getUsedHTMLTagsAsString("survey")), $new_id); 3992 } 3993 } 3994 3995 /** 3996 * creates data directory for export files 3997 * (data_dir/svy_data/svy_<id>/export, depending on data 3998 * directory that is set in ILIAS setup/ini) 3999 * 4000 * @throws ilSurveyException 4001 */ 4002 public function createExportDirectory() 4003 { 4004 $svy_data_dir = ilUtil::getDataDir() . "/svy_data"; 4005 ilUtil::makeDir($svy_data_dir); 4006 if (!is_writable($svy_data_dir)) { 4007 include_once "Modules/Survey/exceptions/class.ilSurveyException.php"; 4008 throw new ilSurveyException("Survey Data Directory (" . $svy_data_dir . ") not writeable."); 4009 } 4010 4011 // create learning module directory (data_dir/lm_data/lm_<id>) 4012 $svy_dir = $svy_data_dir . "/svy_" . $this->getId(); 4013 ilUtil::makeDir($svy_dir); 4014 if (!@is_dir($svy_dir)) { 4015 include_once "Modules/Survey/exceptions/class.ilSurveyException.php"; 4016 throw new ilSurveyException("Creation of Survey Directory failed."); 4017 } 4018 // create Export subdirectory (data_dir/lm_data/lm_<id>/Export) 4019 $export_dir = $svy_dir . "/export"; 4020 ilUtil::makeDir($export_dir); 4021 if (!@is_dir($export_dir)) { 4022 include_once "Modules/Survey/exceptions/class.ilSurveyException.php"; 4023 throw new ilSurveyException("Creation of Export Directory failed."); 4024 } 4025 } 4026 4027 /** 4028 * get export directory of survey 4029 */ 4030 public function getExportDirectory() 4031 { 4032 $export_dir = ilUtil::getDataDir() . "/svy_data" . "/svy_" . $this->getId() . "/export"; 4033 4034 return $export_dir; 4035 } 4036 4037 /** 4038 * creates data directory for import files 4039 * (data_dir/svy_data/svy_<id>/import, depending on data 4040 * directory that is set in ILIAS setup/ini) 4041 * 4042 * @throws ilSurveyException 4043 */ 4044 public function createImportDirectory() 4045 { 4046 $svy_data_dir = ilUtil::getDataDir() . "/svy_data"; 4047 ilUtil::makeDir($svy_data_dir); 4048 4049 if (!is_writable($svy_data_dir)) { 4050 include_once "Modules/Survey/exceptions/class.ilSurveyException.php"; 4051 throw new ilSurveyException("Survey Data Directory (" . $svy_data_dir . ") not writeable."); 4052 } 4053 4054 // create test directory (data_dir/svy_data/svy_<id>) 4055 $svy_dir = $svy_data_dir . "/svy_" . $this->getId(); 4056 ilUtil::makeDir($svy_dir); 4057 if (!@is_dir($svy_dir)) { 4058 include_once "Modules/Survey/exceptions/class.ilSurveyException.php"; 4059 throw new ilSurveyException("Creation of Survey Directory failed."); 4060 } 4061 4062 // create import subdirectory (data_dir/svy_data/svy_<id>/import) 4063 $import_dir = $svy_dir . "/import"; 4064 ilUtil::makeDir($import_dir); 4065 if (!@is_dir($import_dir)) { 4066 include_once "Modules/Survey/exceptions/class.ilSurveyException.php"; 4067 throw new ilSurveyException("Creation of Import Directory failed."); 4068 } 4069 } 4070 4071 /** 4072 * get import directory of survey 4073 */ 4074 public function getImportDirectory() 4075 { 4076 $import_dir = ilUtil::getDataDir() . "/svy_data" . 4077 "/svy_" . $this->getId() . "/import"; 4078 if (!is_dir($import_dir)) { 4079 ilUtil::makeDirParents($import_dir); 4080 } 4081 if (@is_dir($import_dir)) { 4082 return $import_dir; 4083 } else { 4084 return false; 4085 } 4086 } 4087 4088 public function saveHeading($heading = "", $insertbefore) 4089 { 4090 $ilDB = $this->db; 4091 if ($heading) { 4092 $affectedRows = $ilDB->manipulateF( 4093 "UPDATE svy_svy_qst SET heading=%s WHERE survey_fi=%s AND question_fi=%s", 4094 array('text','integer','integer'), 4095 array($heading, $this->getSurveyId(), $insertbefore) 4096 ); 4097 } else { 4098 $affectedRows = $ilDB->manipulateF( 4099 "UPDATE svy_svy_qst SET heading=%s WHERE survey_fi=%s AND question_fi=%s", 4100 array('text','integer','integer'), 4101 array(null, $this->getSurveyId(), $insertbefore) 4102 ); 4103 } 4104 } 4105 4106 public function isAnonymousKey($key) 4107 { 4108 $ilDB = $this->db; 4109 4110 $result = $ilDB->queryF( 4111 "SELECT anonymous_id FROM svy_anonymous WHERE survey_key = %s AND survey_fi = %s", 4112 array('text','integer'), 4113 array($key, $this->getSurveyId()) 4114 ); 4115 return ($result->numRows() == 1) ? true : false; 4116 } 4117 4118 public function bindSurveyCodeToUser($user_id, $code) 4119 { 4120 $ilDB = $this->db; 4121 4122 if ($user_id == ANONYMOUS_USER_ID) { 4123 return; 4124 } 4125 4126 if ($this->checkSurveyCode($code)) { 4127 $ilDB->manipulate("UPDATE svy_anonymous" . 4128 " SET user_key = " . $ilDB->quote(md5($user_id), "text") . 4129 " WHERE survey_key = " . $ilDB->quote($code, "text")); 4130 } 4131 } 4132 4133 public function isAnonymizedParticipant($key) 4134 { 4135 $ilDB = $this->db; 4136 4137 $result = $ilDB->queryF( 4138 "SELECT finished_id FROM svy_finished WHERE anonymous_id = %s AND survey_fi = %s", 4139 array('text','integer'), 4140 array($key, $this->getSurveyId()) 4141 ); 4142 return ($result->numRows() == 1) ? true : false; 4143 } 4144 4145 public function checkSurveyCode($code) 4146 { 4147 if ($this->isAnonymousKey($code)) { 4148 if ($this->isSurveyStarted("", $code) == 1) { 4149 return false; 4150 } else { 4151 return true; 4152 } 4153 } else { 4154 return false; 4155 } 4156 } 4157 4158 /** 4159 * Returns a list of survey codes for file export 4160 * 4161 * @param array $a_array An array of all survey codes that should be exported 4162 * @return string A comma separated list of survey codes an URLs for file export 4163 * @access public 4164 */ 4165 public function getSurveyCodesForExport(array $a_codes = null, array $a_ids = null) 4166 { 4167 $ilDB = $this->db; 4168 $ilUser = $this->user; 4169 $lng = $this->lng; 4170 4171 include_once "./Services/Link/classes/class.ilLink.php"; 4172 4173 $sql = "SELECT svy_anonymous.*, svy_finished.state" . 4174 " FROM svy_anonymous" . 4175 " LEFT JOIN svy_finished ON (svy_anonymous.survey_key = svy_finished.anonymous_id)" . 4176 " WHERE svy_anonymous.survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") . 4177 " AND svy_anonymous.user_key IS NULL"; 4178 4179 if ($a_codes) { 4180 $sql .= " AND " . $ilDB->in("svy_anonymous.survey_key", $a_codes, "", "text"); 4181 } elseif ($a_ids) { 4182 $sql .= " AND " . $ilDB->in("svy_anonymous.anonymous_id", $a_ids, "", "text"); 4183 } 4184 4185 $export = array(); 4186 4187 // #14905 4188 $titles = array(); 4189 $titles[] = '"' . $lng->txt("survey_code") . '"'; 4190 $titles[] = '"' . $lng->txt("email") . '"'; 4191 $titles[] = '"' . $lng->txt("lastname") . '"'; 4192 $titles[] = '"' . $lng->txt("firstname") . '"'; 4193 $titles[] = '"' . $lng->txt("create_date") . '"'; 4194 $titles[] = '"' . $lng->txt("used") . '"'; 4195 $titles[] = '"' . $lng->txt("mail_sent_short") . '"'; 4196 $titles[] = '"' . $lng->txt("survey_code_url") . '"'; 4197 $export[] = implode(";", $titles); 4198 4199 $result = $ilDB->query($sql); 4200 $default_lang = $ilUser->getPref("survey_code_language"); 4201 while ($row = $ilDB->fetchAssoc($result)) { 4202 $item = array(); 4203 $item[] = $row["survey_key"]; 4204 4205 if ($row["externaldata"]) { 4206 $ext = unserialize($row["externaldata"]); 4207 $item[] = $ext["email"]; 4208 $item[] = $ext["lastname"]; 4209 $item[] = $ext["firstname"]; 4210 } else { 4211 $item[] = ""; 4212 $item[] = ""; 4213 $item[] = ""; 4214 } 4215 4216 // No relative (today, tomorrow...) dates in export. 4217 $date = new ilDateTime($row['tstamp'], IL_CAL_UNIX); 4218 $item[] = $date->get(IL_CAL_DATETIME); 4219 4220 $item[] = ($this->isSurveyCodeUsed($row["survey_key"])) ? 1 : 0; 4221 $item[] = ($row["sent"]) ? 1 : 0; 4222 4223 $params = array("accesscode" => $row["survey_key"]); 4224 if ($default_lang) { 4225 $params["lang"] = $default_lang; 4226 } 4227 $item[] = ilLink::_getLink($this->getRefId(), "svy", $params); 4228 4229 $export[] = '"' . implode('";"', $item) . '"'; 4230 } 4231 return implode("\n", $export); 4232 } 4233 4234 /** 4235 * Fetches the data for the survey codes table 4236 * 4237 * @param string $lang Language for the survey code URL 4238 * @return array The requested data 4239 * @access public 4240 */ 4241 public function getSurveyCodesTableData(array $ids = null, $lang = null) 4242 { 4243 $ilDB = $this->db; 4244 4245 include_once "./Services/Link/classes/class.ilLink.php"; 4246 4247 $codes = array(); 4248 4249 $sql = "SELECT svy_anonymous.*, svy_finished.state" . 4250 " FROM svy_anonymous" . 4251 " LEFT JOIN svy_finished ON (svy_anonymous.survey_key = svy_finished.anonymous_id)" . 4252 " WHERE svy_anonymous.survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") /*. 4253 " AND svy_anonymous.user_key IS NULL" */; // #15860 4254 4255 if ($ids) { 4256 $sql .= " AND " . $ilDB->in("svy_anonymous.anonymous_id", $ids, "", "integer"); 4257 } 4258 4259 $sql .= " ORDER BY tstamp, survey_key ASC"; 4260 $result = $ilDB->query($sql); 4261 if ($result->numRows() > 0) { 4262 while ($row = $ilDB->fetchAssoc($result)) { 4263 $href = ""; 4264 $used = false; 4265 if ($this->isSurveyCodeUsed($row["survey_key"])) { 4266 $used = true; 4267 } else { 4268 $params = array("accesscode" => $row["survey_key"]); 4269 if ($lang) { 4270 $params["lang"] = $lang; 4271 } 4272 $href = ilLink::_getLink($this->getRefId(), "svy", $params); 4273 } 4274 4275 4276 $item = array( 4277 'id' => $row["anonymous_id"], 4278 'code' => $row["survey_key"], 4279 'date' => $row["tstamp"], 4280 'used' => $used, 4281 'sent' => $row['sent'], 4282 'href' => $href, 4283 'email' => '', 4284 'last_name' => '', 4285 'first_name' => '' 4286 ); 4287 4288 if ($row["externaldata"]) { 4289 $ext = unserialize($row["externaldata"]); 4290 $item['email'] = $ext['email']; 4291 $item['last_name'] = $ext['lastname']; 4292 $item['first_name'] = $ext['firstname']; 4293 } 4294 4295 array_push($codes, $item); 4296 } 4297 } 4298 return $codes; 4299 } 4300 4301 public function isSurveyCodeUsed($code) 4302 { 4303 $ilDB = $this->db; 4304 $result = $ilDB->queryF( 4305 "SELECT finished_id FROM svy_finished WHERE survey_fi = %s AND anonymous_id = %s", 4306 array('integer','text'), 4307 array($this->getSurveyId(), $code) 4308 ); 4309 return ($result->numRows() > 0) ? true : false; 4310 } 4311 4312 public function isSurveyCodeUnique($code) 4313 { 4314 $ilDB = $this->db; 4315 $result = $ilDB->queryF( 4316 "SELECT anonymous_id FROM svy_anonymous WHERE survey_fi = %s AND survey_key = %s", 4317 array('integer','text'), 4318 array($this->getSurveyId(), $code) 4319 ); 4320 return ($result->numRows() > 0) ? false : true; 4321 } 4322 4323 public function createSurveyCodes($nrOfCodes) 4324 { 4325 $ilDB = $this->db; 4326 4327 $res = array(); 4328 4329 for ($i = 0; $i < $nrOfCodes; $i++) { 4330 $next_id = $ilDB->nextId('svy_anonymous'); 4331 $ilDB->manipulateF( 4332 "INSERT INTO svy_anonymous (anonymous_id, survey_key, survey_fi, tstamp) " . 4333 "VALUES (%s, %s, %s, %s)", 4334 array('integer','text','integer','integer'), 4335 array($next_id, $this->createNewAccessCode(), $this->getSurveyId(), time()) 4336 ); 4337 $res[] = $next_id; 4338 } 4339 4340 return $res; 4341 } 4342 4343 public function importSurveyCode($a_anonymize_key, $a_created, $a_data) 4344 { 4345 $ilDB = $this->db; 4346 4347 $next_id = $ilDB->nextId('svy_anonymous'); 4348 $ilDB->manipulateF( 4349 "INSERT INTO svy_anonymous (anonymous_id, survey_key, survey_fi, externaldata, tstamp) " . 4350 "VALUES (%s, %s, %s, %s, %s)", 4351 array('integer','text','integer','text','integer'), 4352 array($next_id, $a_anonymize_key, $this->getSurveyId(), serialize($a_data), $a_created) 4353 ); 4354 } 4355 4356 public function createSurveyCodesForExternalData($data) 4357 { 4358 $ilDB = $this->db; 4359 4360 $ids = array(); 4361 foreach ($data as $dataset) { 4362 $anonymize_key = $this->createNewAccessCode(); 4363 $next_id = $ilDB->nextId('svy_anonymous'); 4364 $affectedRows = $ilDB->manipulateF( 4365 "INSERT INTO svy_anonymous (anonymous_id, survey_key, survey_fi, externaldata, tstamp) " . 4366 "VALUES (%s, %s, %s, %s, %s)", 4367 array('integer','text','integer','text','integer'), 4368 array($next_id, $anonymize_key, $this->getSurveyId(), serialize($dataset), time()) 4369 ); 4370 $ids[] = $next_id; 4371 } 4372 return $ids; 4373 } 4374 4375 public function sendCodes($not_sent, $subject, $message, $lang) 4376 { 4377 global $DIC; 4378 /* 4379 * 0 = all 4380 * 1 = not sent 4381 * 2 = finished 4382 * 3 = not finished 4383 */ 4384 $check_finished = ($not_sent > 1); 4385 4386 include_once "./Services/Mail/classes/class.ilMail.php"; 4387 include_once "./Services/Link/classes/class.ilLink.php"; 4388 4389 $mail = new ilMail(ANONYMOUS_USER_ID); 4390 $recipients = $this->getExternalCodeRecipients($check_finished); 4391 foreach ($recipients as $data) { 4392 if ($data['email'] && $data['code']) { 4393 $do_send = false; 4394 switch ((int) $not_sent) { 4395 case 1: 4396 $do_send = !(bool) $data['sent']; 4397 break; 4398 4399 case 2: 4400 $do_send = $data['finished']; 4401 break; 4402 4403 case 3: 4404 $do_send = !$data['finished']; 4405 break; 4406 4407 default: 4408 $do_send = true; 4409 break; 4410 } 4411 if ($do_send) { 4412 // build text 4413 $messagetext = $message; 4414 $url = ilLink::_getLink( 4415 $this->getRefId(), 4416 "svy", 4417 array( 4418 "accesscode" => $data["code"], 4419 "lang" => $lang 4420 ) 4421 ); 4422 $messagetext = str_replace('[url]', $url, $messagetext); 4423 foreach ($data as $key => $value) { 4424 $messagetext = str_replace('[' . $key . ']', $value, $messagetext); 4425 } 4426 4427 // send mail 4428 $mail->sendMail( 4429 $data['email'], // to 4430 "", // cc 4431 "", // bcc 4432 $subject, // subject 4433 $messagetext, // message 4434 array(), // attachments 4435 array('normal') // type 4436 ); 4437 } 4438 } 4439 } 4440 4441 $ilDB = $this->db; 4442 $ilDB->manipulateF( 4443 "UPDATE svy_anonymous SET sent = %s WHERE survey_fi = %s AND externaldata IS NOT NULL", 4444 array('integer','integer'), 4445 array(1, $this->getSurveyId()) 4446 ); 4447 } 4448 4449 public function getExternalCodeRecipients($a_check_finished = false) 4450 { 4451 $ilDB = $this->db; 4452 $result = $ilDB->queryF( 4453 "SELECT survey_key code, externaldata, sent FROM svy_anonymous WHERE survey_fi = %s", 4454 array('integer'), 4455 array($this->getSurveyId()) 4456 ); 4457 $res = array(); 4458 while ($row = $ilDB->fetchAssoc($result)) { 4459 if (!$row['externaldata']) { 4460 continue; 4461 } 4462 4463 $externaldata = unserialize($row['externaldata']); 4464 if (!$externaldata['email']) { 4465 continue; 4466 } 4467 4468 $externaldata['code'] = $row['code']; 4469 $externaldata['sent'] = $row['sent']; 4470 4471 if ($a_check_finished) { 4472 #23294 4473 //$externaldata['finished'] = $this->isSurveyCodeUsed($row['code']); 4474 $externaldata['finished'] = $this->isSurveyFinishedByCode($row['code']); 4475 } 4476 4477 array_push($res, $externaldata); 4478 } 4479 return $res; 4480 } 4481 4482 /** 4483 * Get if survey is finished for an specific anonymous user code. 4484 * @param $a_code anonymous user code 4485 * @return bool 4486 */ 4487 public function isSurveyFinishedByCode($a_code) 4488 { 4489 $result = $this->db->queryF( 4490 "SELECT state FROM svy_finished WHERE survey_fi = %s AND anonymous_id = %s", 4491 array('integer','text'), 4492 array($this->getSurveyId(), $a_code) 4493 ); 4494 4495 $row = $this->db->fetchAssoc($result); 4496 4497 return $row['state']; 4498 } 4499 4500 /** 4501 * Deletes a given survey access code 4502 * 4503 * @param string $survey_code The survey code that should be deleted 4504 */ 4505 public function deleteSurveyCode($survey_code) 4506 { 4507 $ilDB = $this->db; 4508 4509 if (strlen($survey_code) > 0) { 4510 $affectedRows = $ilDB->manipulateF( 4511 "DELETE FROM svy_anonymous WHERE survey_fi = %s AND survey_key = %s", 4512 array('integer', 'text'), 4513 array($this->getSurveyId(), $survey_code) 4514 ); 4515 } 4516 } 4517 4518 /** 4519 * Returns a survey access code that was saved for a registered user 4520 * 4521 * @param int $user_id The database id of the user 4522 * @return string The survey access code of the user 4523 */ 4524 public function getUserAccessCode($user_id) 4525 { 4526 $ilDB = $this->db; 4527 $access_code = ""; 4528 $result = $ilDB->queryF( 4529 "SELECT survey_key FROM svy_anonymous WHERE survey_fi = %s AND user_key = %s", 4530 array('integer','text'), 4531 array($this->getSurveyId(), md5($user_id)) 4532 ); 4533 if ($result->numRows()) { 4534 $row = $ilDB->fetchAssoc($result); 4535 $access_code = $row["survey_key"]; 4536 } 4537 return $access_code; 4538 } 4539 4540 /** 4541 * Saves a survey access code for a registered user to the database 4542 * 4543 * @param int $user_id The database id of the user 4544 * @param string $access_code The survey access code 4545 */ 4546 public function saveUserAccessCode($user_id, $access_code) 4547 { 4548 $ilDB = $this->db; 4549 4550 // not really sure what to do about ANONYMOUS_USER_ID 4551 4552 $next_id = $ilDB->nextId('svy_anonymous'); 4553 $affectedRows = $ilDB->manipulateF( 4554 "INSERT INTO svy_anonymous (anonymous_id, survey_key, survey_fi, user_key, tstamp) " . 4555 "VALUES (%s, %s, %s, %s, %s)", 4556 array('integer','text', 'integer', 'text', 'integer'), 4557 array($next_id, $access_code, $this->getSurveyId(), md5($user_id), time()) 4558 ); 4559 } 4560 4561 /** 4562 * Returns a new, unused survey access code 4563 * 4564 * @return string A new survey access code 4565 */ 4566 public function createNewAccessCode() 4567 { 4568 // create a 5 character code 4569 $codestring = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 4570 mt_srand(); 4571 $code = ""; 4572 for ($i = 1; $i <= 5; $i++) { 4573 $index = mt_rand(0, strlen($codestring) - 1); 4574 $code .= substr($codestring, $index, 1); 4575 } 4576 // verify it against the database 4577 while (!$this->isSurveyCodeUnique($code)) { 4578 $code = $this->createNewAccessCode(); 4579 } 4580 return $code; 4581 } 4582 4583 public function getLastAccess($finished_id) 4584 { 4585 $ilDB = $this->db; 4586 4587 $result = $ilDB->queryF( 4588 "SELECT tstamp FROM svy_answer WHERE active_fi = %s ORDER BY tstamp DESC", 4589 array('integer'), 4590 array($finished_id) 4591 ); 4592 if ($result->numRows()) { 4593 $row = $ilDB->fetchAssoc($result); 4594 return $row["tstamp"]; 4595 } else { 4596 $result = $ilDB->queryF( 4597 "SELECT tstamp FROM svy_finished WHERE finished_id = %s", 4598 array('integer'), 4599 array($finished_id) 4600 ); 4601 if ($result->numRows()) { 4602 $row = $ilDB->fetchAssoc($result); 4603 return $row["tstamp"]; 4604 } 4605 } 4606 return ""; 4607 } 4608 4609 /** 4610 * Prepares a string for a text area output in surveys 4611 * 4612 * @param string $txt_output String which should be prepared for output 4613 * @access public 4614 */ 4615 public function prepareTextareaOutput($txt_output) 4616 { 4617 return ilUtil::prepareTextareaOutput($txt_output, $prepare_for_latex_output); 4618 } 4619 4620 /** 4621 * Checks if a given string contains HTML or not 4622 * 4623 * @param string $a_text Text which should be checked 4624 * @return boolean 4625 * @access public 4626 */ 4627 public function isHTML($a_text) 4628 { 4629 if (preg_match("/<[^>]*?>/", $a_text)) { 4630 return true; 4631 } else { 4632 return false; 4633 } 4634 } 4635 4636 /** 4637 * Creates an XML material tag from a plain text or xhtml text 4638 * 4639 * @param object $a_xml_writer Reference to the ILIAS XML writer 4640 * @param string $a_material plain text or html text containing the material 4641 * @return string XML material tag 4642 * @access public 4643 */ 4644 public function addMaterialTag(&$a_xml_writer, $a_material, $close_material_tag = true, $add_mobs = true, $attribs = null) 4645 { 4646 include_once "./Services/RTE/classes/class.ilRTE.php"; 4647 include_once("./Services/MediaObjects/classes/class.ilObjMediaObject.php"); 4648 4649 $a_xml_writer->xmlStartTag("material", $attribs); 4650 $attrs = array( 4651 "type" => "text/plain" 4652 ); 4653 if ($this->isHTML($a_material)) { 4654 $attrs["type"] = "text/xhtml"; 4655 } 4656 $mattext = ilRTE::_replaceMediaObjectImageSrc($a_material, 0); 4657 $a_xml_writer->xmlElement("mattext", $attrs, $mattext); 4658 4659 if ($add_mobs) { 4660 $mobs = ilObjMediaObject::_getMobsOfObject("svy:html", $this->getId()); 4661 foreach ($mobs as $mob) { 4662 $mob_id = "il_" . IL_INST_ID . "_mob_" . $mob; 4663 if (strpos($mattext, $mob_id) !== false) { 4664 $mob_obj = new ilObjMediaObject($mob); 4665 $imgattrs = array( 4666 "label" => $mob_id, 4667 "uri" => "objects/" . "il_" . IL_INST_ID . "_mob_" . $mob . "/" . $mob_obj->getTitle(), 4668 "type" => "svy:html", 4669 "id" => $this->getId() 4670 ); 4671 $a_xml_writer->xmlElement("matimage", $imgattrs, null); 4672 } 4673 } 4674 } 4675 if ($close_material_tag) { 4676 $a_xml_writer->xmlEndTag("material"); 4677 } 4678 } 4679 4680 /** 4681 * Checks if the survey code can be exported with the survey evaluation. In some cases this may be 4682 * necessary but usually you should prevent it because people who sent the survey codes could connect 4683 * real people with the survey code in the evaluation and undermine the anonymity 4684 * 4685 * @return boolean TRUE if the survey is anonymized and the survey code may be shown in the export file 4686 * @author Helmut Schottmüller 4687 **/ 4688 public function canExportSurveyCode() 4689 { 4690 if ($this->getAnonymize() != self::ANONYMIZE_OFF) { 4691 if ($this->surveyCodeSecurity == false) { 4692 return true; 4693 } 4694 } 4695 return false; 4696 } 4697 4698 /** 4699 * Convert a print output to XSL-FO 4700 * 4701 * @param string $print_output The print output 4702 * @return string XSL-FO code 4703 * @access public 4704 */ 4705 public function processPrintoutput2FO($print_output) 4706 { 4707 if (extension_loaded("tidy")) { 4708 $config = array( 4709 "indent" => false, 4710 "output-xml" => true, 4711 "numeric-entities" => true 4712 ); 4713 $tidy = new tidy(); 4714 $tidy->parseString($print_output, $config, 'utf8'); 4715 $tidy->cleanRepair(); 4716 $print_output = tidy_get_output($tidy); 4717 $print_output = preg_replace("/^.*?(<html)/", "\\1", $print_output); 4718 } else { 4719 $print_output = str_replace(" ", " ", $print_output); 4720 $print_output = str_replace("⊗", "X", $print_output); 4721 4722 // #17680 - metric questions use   in print view 4723 $print_output = str_replace(">", "~|gt|~", $print_output); // see #21550 4724 $print_output = str_replace("<", "~|lt|~", $print_output); 4725 $print_output = str_replace(" ", "~|nbsp|~", $print_output); 4726 $print_output = preg_replace('/&(?!amp)/', '&', $print_output); 4727 $print_output = str_replace("~|nbsp|~", " ", $print_output); 4728 $print_output = str_replace("~|gt|~", ">", $print_output); 4729 $print_output = str_replace("~|lt|~", "<", $print_output); 4730 } 4731 $xsl = file_get_contents("./Modules/Survey/xml/question2fo.xsl"); 4732 4733 // additional font support 4734 $xsl = str_replace( 4735 'font-family="Helvetica, unifont"', 4736 'font-family="' . $GLOBALS['ilSetting']->get('rpc_pdf_font', 'Helvetica, unifont') . '"', 4737 $xsl 4738 ); 4739 $args = array( '/_xml' => $print_output, '/_xsl' => $xsl ); 4740 $xh = xslt_create(); 4741 $params = array(); 4742 try { 4743 $output = xslt_process($xh, "arg:/_xml", "arg:/_xsl", null, $args, $params); 4744 } catch (Exception $e) { 4745 $this->log->error("Print XSLT failed:"); 4746 $this->log->error("Content: " . $print_output); 4747 $this->log->error("Xsl: " . $xsl); 4748 throw ($e); 4749 } 4750 xslt_error($xh); 4751 xslt_free($xh); 4752 4753 return $output; 4754 } 4755 4756 /** 4757 * Delivers a PDF file from a XSL-FO string 4758 * 4759 * @param string $fo The XSL-FO string 4760 * @access public 4761 */ 4762 public function deliverPDFfromFO($fo) 4763 { 4764 $ilLog = $this->log; 4765 4766 $fo_file = ilUtil::ilTempnam() . ".fo"; 4767 $fp = fopen($fo_file, "w"); 4768 fwrite($fp, $fo); 4769 fclose($fp); 4770 4771 include_once './Services/WebServices/RPC/classes/class.ilRpcClientFactory.php'; 4772 try { 4773 $pdf_base64 = ilRpcClientFactory::factory('RPCTransformationHandler')->ilFO2PDF($fo); 4774 ilUtil::deliverData($pdf_base64->scalar, ilUtil::getASCIIFilename($this->getTitle()) . ".pdf", "application/pdf"); 4775 return true; 4776 } catch (Exception $e) { 4777 $ilLog->write(__METHOD__ . ': ' . $e->getMessage()); 4778 return false; 4779 } 4780 } 4781 4782 /** 4783 * Checks whether or not a question plugin with a given name is active 4784 * 4785 * @param string $a_pname The plugin name 4786 * @access public 4787 */ 4788 public function isPluginActive($a_pname) 4789 { 4790 $ilPluginAdmin = $this->plugin_admin; 4791 if ($ilPluginAdmin->isActive(IL_COMP_MODULE, "SurveyQuestionPool", "svyq", $a_pname)) { 4792 return true; 4793 } else { 4794 return false; 4795 } 4796 } 4797 4798 /** 4799 * Sets the survey id 4800 * 4801 * @param integer $survey_id The survey id 4802 */ 4803 public function setSurveyId($survey_id) 4804 { 4805 $this->survey_id = $survey_id; 4806 } 4807 4808 /** 4809 * Returns a data of all users specified by id list 4810 * 4811 * @param $ids array of user id's 4812 * @return array The user data "usr_id, login, lastname, firstname, clientip" of the users with id as key 4813 */ 4814 public function &getUserData($ids) 4815 { 4816 $ilDB = $this->db; 4817 4818 if (!is_array($ids) || count($ids) == 0) { 4819 return array(); 4820 } 4821 4822 $result = $ilDB->query("SELECT usr_id, login, lastname, firstname FROM usr_data WHERE " . $ilDB->in('usr_id', $ids, false, 'integer') . " ORDER BY login"); 4823 $result_array = array(); 4824 while ($row = $ilDB->fetchAssoc($result)) { 4825 $result_array[$row["usr_id"]] = $row; 4826 } 4827 return $result_array; 4828 } 4829 4830 public function getMailNotification() 4831 { 4832 return $this->mailnotification; 4833 } 4834 4835 public function setMailNotification($a_notification) 4836 { 4837 $this->mailnotification = ($a_notification) ? true : false; 4838 } 4839 4840 public function getMailAddresses() 4841 { 4842 return $this->mailaddresses; 4843 } 4844 4845 public function setMailAddresses($a_addresses) 4846 { 4847 $this->mailaddresses = $a_addresses; 4848 } 4849 4850 public function getMailParticipantData() 4851 { 4852 return $this->mailparticipantdata; 4853 } 4854 4855 public function setMailParticipantData($a_data) 4856 { 4857 $this->mailparticipantdata = $a_data; 4858 } 4859 4860 public function setStartTime($finished_id, $first_question) 4861 { 4862 $ilDB = $this->db; 4863 $time = time(); 4864 //primary for table svy_times 4865 $id = $ilDB->nextId('svy_times'); 4866 $_SESSION['svy_entered_page'] = $time; 4867 $affectedRows = $ilDB->manipulateF( 4868 "INSERT INTO svy_times (id, finished_fi, entered_page, left_page, first_question) VALUES (%s, %s, %s, %s,%s)", 4869 array('integer','integer', 'integer', 'integer', 'integer'), 4870 array($id, $finished_id, $time, null, $first_question) 4871 ); 4872 } 4873 4874 public function setEndTime($finished_id) 4875 { 4876 $ilDB = $this->db; 4877 $time = time(); 4878 $affectedRows = $ilDB->manipulateF( 4879 "UPDATE svy_times SET left_page = %s WHERE finished_fi = %s AND entered_page = %s", 4880 array('integer', 'integer', 'integer'), 4881 array($time, $finished_id, $_SESSION['svy_entered_page']) 4882 ); 4883 unset($_SESSION['svy_entered_page']); 4884 } 4885 4886 public function getWorkingtimeForParticipant($finished_id) 4887 { 4888 $ilDB = $this->db; 4889 4890 $result = $ilDB->queryF( 4891 "SELECT * FROM svy_times WHERE finished_fi = %s", 4892 array('integer'), 4893 array($finished_id) 4894 ); 4895 $total = 0; 4896 while ($row = $ilDB->fetchAssoc($result)) { 4897 if ($row['left_page'] > 0 && $row['entered_page'] > 0) { 4898 $total += $row['left_page'] - $row['entered_page']; 4899 } 4900 } 4901 return $total; 4902 } 4903 4904 public function setTemplate($template_id) 4905 { 4906 $this->template_id = (int) $template_id; 4907 } 4908 4909 public function getTemplate() 4910 { 4911 return $this->template_id; 4912 } 4913 4914 public function updateOrder(array $a_order) 4915 { 4916 if (sizeof($this->questions) == sizeof($a_order)) { 4917 $this->questions = array_flip($a_order); 4918 $this->saveQuestionsToDB(); 4919 } 4920 } 4921 4922 public function getPoolUsage() 4923 { 4924 return $this->pool_usage; 4925 } 4926 4927 public function setPoolUsage($a_value) 4928 { 4929 $this->pool_usage = (bool) $a_value; 4930 } 4931 4932 /** 4933 * Get current pool status 4934 * 4935 * @return bool 4936 */ 4937 public function isPoolActive() 4938 { 4939 $use_pool = (bool) $this->getPoolUsage(); 4940 $template_settings = $this->getTemplate(); 4941 if ($template_settings) { 4942 include_once "Services/Administration/classes/class.ilSettingsTemplate.php"; 4943 $template_settings = new ilSettingsTemplate($template_settings); 4944 $template_settings = $template_settings->getSettings(); 4945 $template_settings = $template_settings["use_pool"]; 4946 if ($template_settings && $template_settings["hide"]) { 4947 $use_pool = (bool) $template_settings["value"]; 4948 } 4949 } 4950 return $use_pool; 4951 } 4952 4953 /** 4954 * Apply settings template 4955 * 4956 * @param int $template_id 4957 */ 4958 public function applySettingsTemplate($template_id) 4959 { 4960 if (!$template_id) { 4961 return; 4962 } 4963 4964 include_once "Services/Administration/classes/class.ilSettingsTemplate.php"; 4965 $template = new ilSettingsTemplate($template_id); 4966 $template_settings = $template->getSettings(); 4967 //ilUtil::dumpVar($template_settings); exit; 4968 if ($template_settings) { 4969 if ($template_settings["show_question_titles"] !== null) { 4970 if ($template_settings["show_question_titles"]["value"]) { 4971 $this->setShowQuestionTitles(true); 4972 } else { 4973 $this->setShowQuestionTitles(false); 4974 } 4975 } 4976 4977 if ($template_settings["use_pool"] !== null) { 4978 if ($template_settings["use_pool"]["value"]) { 4979 $this->setPoolUsage(true); 4980 } else { 4981 $this->setPoolUsage(false); 4982 } 4983 } 4984 4985 4986 /* see #0021719 4987 if($template_settings["anonymization_options"]["value"]) 4988 { 4989 $anon_map = array('personalized' => self::ANONYMIZE_OFF, 4990 'anonymize_with_code' => self::ANONYMIZE_ON, 4991 'anonymize_without_code' => self::ANONYMIZE_FREEACCESS); 4992 $this->setAnonymize($anon_map[$template_settings["anonymization_options"]["value"]]); 4993 }*/ 4994 4995 // see #0021719 and ilObjectSurveyGUI::savePropertiesObject 4996 $this->setEvaluationAccess($template_settings["evaluation_access"]["value"]); 4997 $codes = (bool) $template_settings["acc_codes"]["value"]; 4998 $anon = (bool) $template_settings["anonymization_options"]["value"]; 4999 if (!$anon) { 5000 if (!$codes) { 5001 $this->setAnonymize(ilObjSurvey::ANONYMIZE_OFF); 5002 } else { 5003 $this->setAnonymize(ilObjSurvey::ANONYMIZE_CODE_ALL); 5004 } 5005 } else { 5006 if ($codes) { 5007 $this->setAnonymize(ilObjSurvey::ANONYMIZE_ON); 5008 } else { 5009 $this->setAnonymize(ilObjSurvey::ANONYMIZE_FREEACCESS); 5010 } 5011 5012 $this->setAnonymousUserList($_POST["anon_list"]); 5013 } 5014 5015 5016 5017 /* other settings: not needed here 5018 * - enabled_end_date 5019 * - enabled_start_date 5020 * - rte_switch 5021 */ 5022 } 5023 5024 $this->setTemplate($template_id); 5025 $this->saveToDb(); 5026 } 5027 5028 public function updateCode($a_id, $a_email, $a_last_name, $a_first_name, $a_sent) 5029 { 5030 $ilDB = $this->db; 5031 5032 $a_email = trim($a_email); 5033 5034 // :TODO: 5035 if (($a_email && !ilUtil::is_email($a_email)) || $a_email == "") { 5036 return false; 5037 } 5038 5039 $data = array("email" => $a_email, 5040 "lastname" => trim($a_last_name), 5041 "firstname" => trim($a_first_name)); 5042 5043 $fields = array( 5044 "externaldata" => array("text", serialize($data)), 5045 "sent" => array("integer", $a_sent) 5046 ); 5047 5048 $ilDB->update( 5049 "svy_anonymous", 5050 $fields, 5051 array("anonymous_id" => array("integer", $a_id)) 5052 ); 5053 5054 return true; 5055 } 5056 5057 5058 // 5059 // 360° 5060 // 5061 5062 public function get360Mode() 5063 { 5064 if ($this->getMode() == ilObjSurvey::MODE_360) { 5065 return true; 5066 } 5067 return false; 5068 } 5069 5070 public function set360SelfEvaluation($a_value) 5071 { 5072 $this->mode_360_self_eval = (bool) $a_value; 5073 } 5074 5075 public function get360SelfEvaluation() 5076 { 5077 return (bool) $this->mode_360_self_eval; 5078 } 5079 5080 public function set360SelfAppraisee($a_value) 5081 { 5082 $this->mode_360_self_appr = (bool) $a_value; 5083 } 5084 5085 public function get360SelfAppraisee() 5086 { 5087 return (bool) $this->mode_360_self_appr; 5088 } 5089 5090 public function set360SelfRaters($a_value) 5091 { 5092 $this->mode_360_self_rate = (bool) $a_value; 5093 } 5094 5095 public function get360SelfRaters() 5096 { 5097 return (bool) $this->mode_360_self_rate; 5098 } 5099 5100 public function set360Results($a_value) 5101 { 5102 $this->mode_360_results = (int) $a_value; 5103 } 5104 5105 public function get360Results() 5106 { 5107 return (int) $this->mode_360_results; 5108 } 5109 5110 public function addAppraisee($a_user_id) 5111 { 5112 global $DIC; 5113 5114 $ilDB = $DIC->database(); 5115 $access = $DIC->access(); 5116 5117 if (!$this->isAppraisee($a_user_id) && 5118 $a_user_id != ANONYMOUS_USER_ID) { 5119 $fields = array( 5120 "obj_id" => array("integer", $this->getSurveyId()), 5121 "user_id" => array("integer", $a_user_id) 5122 ); 5123 $ilDB->insert("svy_360_appr", $fields); 5124 5125 // send notification and add to desktop 5126 if ($access->checkAccessOfUser($a_user_id, "read", "", $this->getRefId())) { 5127 $this->sendAppraiseeNotification($a_user_id); 5128 $type = ilObject::_lookupType($this->getRefId(), true); 5129 ilObjUser::_addDesktopItem($a_user_id, $this->getRefId(), $type); 5130 } 5131 } 5132 } 5133 5134 /** 5135 * Send appraisee notification 5136 * 5137 * @param int $a_user_id user id 5138 */ 5139 public function sendAppraiseeNotification($a_user_id) 5140 { 5141 include_once "./Services/Notification/classes/class.ilSystemNotification.php"; 5142 $ntf = new ilSystemNotification(); 5143 $ntf->setLangModules(array("svy", "survey")); 5144 $ntf->setRefId($this->getRefId()); 5145 $ntf->setGotoLangId('url'); 5146 5147 // user specific language 5148 $lng = $ntf->getUserLanguage($a_user_id); 5149 5150 $ntf->setIntroductionLangId("svy_user_added_360_appraisee_mail"); 5151 $subject = str_replace("%1", $this->getTitle(), $lng->txt("svy_user_added_360_appraisee")); 5152 5153 // #10044 5154 $mail = new ilMail(ANONYMOUS_USER_ID); 5155 //$mail->enableSOAP(false); // #10410 5156 $mail->sendMail( 5157 ilObjUser::_lookupLogin($a_user_id), 5158 null, 5159 null, 5160 $subject, 5161 $ntf->composeAndGetMessage($a_user_id, null, "read", true), 5162 null, 5163 array("system") 5164 ); 5165 } 5166 5167 /** 5168 * Send appraisee notification 5169 * 5170 * @param int $a_user_id user id 5171 */ 5172 public function sendAppraiseeCloseNotification($a_user_id) 5173 { 5174 include_once "./Services/Notification/classes/class.ilSystemNotification.php"; 5175 $ntf = new ilSystemNotification(); 5176 $ntf->setLangModules(array("svy", "survey")); 5177 $ntf->setRefId($this->getRefId()); 5178 $ntf->setGotoLangId('url'); 5179 5180 // user specific language 5181 $lng = $ntf->getUserLanguage($a_user_id); 5182 5183 $ntf->setIntroductionLangId("svy_user_added_360_appraisee_close_mail"); 5184 $subject = str_replace("%1", $this->getTitle(), $lng->txt("svy_user_added_360_appraisee")); 5185 5186 // #10044 5187 $mail = new ilMail(ANONYMOUS_USER_ID); 5188 //$mail->enableSOAP(false); // #10410 5189 $mail->sendMail( 5190 ilObjUser::_lookupLogin($a_user_id), 5191 null, 5192 null, 5193 $subject, 5194 $ntf->composeAndGetMessage($a_user_id, null, "read", true), 5195 null, 5196 array("system") 5197 ); 5198 } 5199 5200 /** 5201 * Send rater notification 5202 * 5203 * @param int $a_user_id user id 5204 */ 5205 public function sendRaterNotification($a_user_id, $a_appraisee_id) 5206 { 5207 include_once "./Services/Notification/classes/class.ilSystemNotification.php"; 5208 $ntf = new ilSystemNotification(); 5209 $ntf->setLangModules(array("svy", "survey")); 5210 $ntf->setRefId($this->getRefId()); 5211 $ntf->setGotoLangId('url'); 5212 5213 // user specific language 5214 $lng = $ntf->getUserLanguage($a_user_id); 5215 5216 $ntf->setIntroductionLangId("svy_user_added_360_rater_mail"); 5217 $subject = str_replace("%1", $this->getTitle(), $lng->txt("svy_user_added_360_rater")); 5218 $ntf->addAdditionalInfo("survey_360_appraisee", ilUserUtil::getNamePresentation($a_appraisee_id, false, false, "", true)); 5219 5220 // #10044 5221 $mail = new ilMail(ANONYMOUS_USER_ID); 5222 //$mail->enableSOAP(false); // #10410 5223 $mail->sendMail( 5224 ilObjUser::_lookupLogin($a_user_id), 5225 null, 5226 null, 5227 $subject, 5228 $ntf->composeAndGetMessage($a_user_id, null, "read", true), 5229 null, 5230 array("system") 5231 ); 5232 } 5233 5234 public function isAppraisee($a_user_id) 5235 { 5236 $ilDB = $this->db; 5237 5238 $set = $ilDB->query("SELECT user_id" . 5239 " FROM svy_360_appr" . 5240 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer") . 5241 " AND user_id = " . $ilDB->quote($a_user_id, "integer")); 5242 return (bool) $ilDB->numRows($set); 5243 } 5244 5245 public function isAppraiseeClosed($a_user_id) 5246 { 5247 $ilDB = $this->db; 5248 5249 $set = $ilDB->query("SELECT has_closed" . 5250 " FROM svy_360_appr" . 5251 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer") . 5252 " AND user_id = " . $ilDB->quote($a_user_id, "integer")); 5253 $row = $ilDB->fetchAssoc($set); 5254 return $row["has_closed"]; 5255 } 5256 5257 public function deleteAppraisee($a_user_id) 5258 { 5259 $ilDB = $this->db; 5260 5261 $ilDB->manipulate("DELETE FROM svy_360_appr" . 5262 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer") . 5263 " AND user_id = " . $ilDB->quote($a_user_id, "integer")); 5264 5265 $set = $ilDB->query("SELECT user_id" . 5266 " FROM svy_360_rater" . 5267 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer") . 5268 " AND appr_id = " . $ilDB->quote($a_user_id, "integer")); 5269 while ($row = $ilDB->fetchAssoc($set)) { 5270 $this->deleteRater($a_user_id, $row["user_id"]); 5271 } 5272 // appraisee will not be part of raters table 5273 if ($this->get360SelfEvaluation()) { 5274 $this->deleteRater($a_user_id, $a_user_id); 5275 } 5276 } 5277 5278 public function getAppraiseesData() 5279 { 5280 $ilDB = $this->db; 5281 5282 $res = array(); 5283 5284 $set = $ilDB->query("SELECT * FROM svy_360_appr" . 5285 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer")); 5286 while ($row = $ilDB->fetchAssoc($set)) { 5287 $name = ilObjUser::_lookupName($row["user_id"]); 5288 $name["email"] = ilObjUser::_lookupEmail($row["user_id"]); 5289 $name["name"] = $name["lastname"] . ", " . $name["firstname"]; 5290 $res[$row["user_id"]] = $name; 5291 5292 $finished = 0; 5293 $raters = $this->getRatersData($row["user_id"]); 5294 foreach ($raters as $rater) { 5295 if ($rater["finished"]) { 5296 $finished++; 5297 } 5298 } 5299 $res[$row["user_id"]]["finished"] = $finished . "/" . sizeof($raters); 5300 $res[$row["user_id"]]["closed"] = $row["has_closed"]; 5301 } 5302 5303 return $res; 5304 } 5305 5306 public function addRater($a_appraisee_id, $a_user_id, $a_anonymous_id = 0) 5307 { 5308 global $DIC; 5309 5310 $ilDB = $DIC->database(); 5311 $access = $DIC->access(); 5312 5313 if ($this->isAppraisee($a_appraisee_id) && 5314 !$this->isRater($a_appraisee_id, $a_user_id, $a_anonymous_id)) { 5315 $fields = array( 5316 "obj_id" => array("integer", $this->getSurveyId()), 5317 "appr_id" => array("integer", $a_appraisee_id), 5318 "user_id" => array("integer", $a_user_id), 5319 "anonymous_id" => array("integer", $a_anonymous_id) 5320 ); 5321 $ilDB->insert("svy_360_rater", $fields); 5322 5323 // send notification and add to desktop 5324 if ($access->checkAccessOfUser($a_user_id, "read", "", $this->getRefId())) { 5325 $this->sendRaterNotification($a_user_id, $a_appraisee_id); 5326 $type = ilObject::_lookupType($this->getRefId(), true); 5327 ilObjUser::_addDesktopItem($a_user_id, $this->getRefId(), $type); 5328 } 5329 } 5330 } 5331 5332 public function isRater($a_appraisee_id, $a_user_id, $a_anonymous_id = 0) 5333 { 5334 $ilDB = $this->db; 5335 5336 // user is rater if already appraisee and active self-evaluation 5337 if ($this->isAppraisee($a_user_id) && 5338 $this->get360SelfEvaluation() && 5339 (!$a_appraisee_id || $a_appraisee_id == $a_user_id)) { 5340 return true; 5341 } 5342 5343 // :TODO: should we get rid of code as well? 5344 5345 $sql = "SELECT user_id" . 5346 " FROM svy_360_rater" . 5347 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer") . 5348 " AND user_id = " . $ilDB->quote($a_user_id, "integer") . 5349 " AND anonymous_id = " . $ilDB->quote($a_anonymous_id, "integer"); 5350 if ($a_appraisee_id) { 5351 $sql .= " AND appr_id = " . $ilDB->quote($a_appraisee_id, "integer"); 5352 } 5353 $set = $ilDB->query($sql); 5354 return (bool) $ilDB->numRows($set); 5355 } 5356 5357 public function deleteRater($a_appraisee_id, $a_user_id, $a_anonymous_id = 0) 5358 { 5359 $ilDB = $this->db; 5360 5361 $finished_id = $this->getFinishedIdForAppraiseeIdAndRaterId($a_appraisee_id, $a_user_id); 5362 if ($finished_id) { 5363 $this->removeSelectedSurveyResults(array($finished_id)); 5364 } 5365 5366 $ilDB->manipulate("DELETE FROM svy_360_rater" . 5367 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer") . 5368 " AND appr_id = " . $ilDB->quote($a_appraisee_id, "integer") . 5369 " AND user_id = " . $ilDB->quote($a_user_id, "integer") . 5370 " AND anonymous_id = " . $ilDB->quote($a_anonymous_id, "integer")); 5371 } 5372 5373 public function getRatersData($a_appraisee_id) 5374 { 5375 $ilDB = $this->db; 5376 5377 $res = $anonymous_ids = array(); 5378 5379 $set = $ilDB->query("SELECT * FROM svy_360_rater" . 5380 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer") . 5381 " AND appr_id = " . $ilDB->quote($a_appraisee_id, "integer")); 5382 while ($row = $ilDB->fetchAssoc($set)) { 5383 if ($row["anonymous_id"]) { 5384 $res["a" . $row["anonymous_id"]] = array( 5385 "lastname" => "unknown code " . $row["anonymous_id"], 5386 "sent" => $row["mail_sent"], 5387 "finished" => null 5388 ); 5389 $anonymous_ids[] = $row["anonymous_id"]; 5390 } else { 5391 $name = ilObjUser::_lookupName($row["user_id"]); 5392 $name["name"] = $name["lastname"] . ", " . $name["firstname"]; 5393 $name["user_id"] = "u" . $name["user_id"]; 5394 $name["email"] = ilObjUser::_lookupEmail($row["user_id"]); 5395 $name["sent"] = $row["mail_sent"]; 5396 $name["finished"] = (bool) $this->is360SurveyStarted($a_appraisee_id, $row["user_id"]); 5397 $res["u" . $row["user_id"]] = $name; 5398 } 5399 } 5400 5401 if (sizeof($anonymous_ids)) { 5402 $data = $this->getSurveyCodesTableData($anonymous_ids); 5403 foreach ($data as $item) { 5404 if (isset($res["a" . $item["id"]])) { 5405 $res["a" . $item["id"]] = array( 5406 "user_id" => "a" . $item["id"], 5407 "lastname" => $item["last_name"], 5408 "firstname" => $item["first_name"], 5409 "name" => $item["last_name"] . ", " . $item["first_name"], 5410 "login" => "", 5411 "email" => $item["email"], 5412 "code" => $item["code"], 5413 "href" => $item["href"], 5414 "sent" => $res["a" . $item["id"]]["sent"], 5415 "finished" => (bool) $this->is360SurveyStarted($a_appraisee_id, null, $item["code"]) 5416 ); 5417 } 5418 } 5419 } 5420 5421 return $res; 5422 } 5423 5424 public function getAppraiseesToRate($a_user_id, $a_anonymous_id = null) 5425 { 5426 $ilDB = $this->db; 5427 5428 $res = array(); 5429 5430 $sql = "SELECT appr_id FROM svy_360_rater" . 5431 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer"); 5432 5433 if ($a_user_id) { 5434 $sql .= " AND user_id = " . $ilDB->quote($a_user_id, "integer"); 5435 } else { 5436 $sql .= " AND anonymous_id = " . $ilDB->quote($a_anonymous_id, "integer"); 5437 } 5438 5439 $set = $ilDB->query($sql); 5440 while ($row = $ilDB->fetchAssoc($set)) { 5441 $res[] = $row["appr_id"]; 5442 } 5443 5444 // user may evaluate himself if already appraisee 5445 if ($this->get360SelfEvaluation() && 5446 $this->isAppraisee($a_user_id) && 5447 !in_array($a_user_id, $res)) { 5448 $res[] = $a_user_id; 5449 } 5450 5451 return $res; 5452 } 5453 5454 public function getAnonymousIdByCode($a_code) 5455 { 5456 $ilDB = $this->db; 5457 5458 $set = $ilDB->query("SELECT anonymous_id FROM svy_anonymous" . 5459 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") . 5460 " AND survey_key = " . $ilDB->quote($a_code, "text")); 5461 $res = $ilDB->fetchAssoc($set); 5462 return $res["anonymous_id"]; 5463 } 5464 5465 public function is360SurveyStarted($appr_id, $user_id, $anonymous_code = null) 5466 { 5467 $ilDB = $this->db; 5468 5469 $sql = "SELECT * FROM svy_finished" . 5470 " WHERE survey_fi =" . $ilDB->quote($this->getSurveyId(), "integer") . 5471 " AND appr_id = " . $ilDB->quote($appr_id, "integer"); 5472 if ($user_id) { 5473 $sql .= " AND user_fi = " . $ilDB->quote($user_id, "integer"); 5474 } else { 5475 $sql .= " AND anonymous_id = " . $ilDB->quote($anonymous_code, "text"); 5476 } 5477 $result = $ilDB->query($sql); 5478 if ($result->numRows() == 0) { 5479 return false; 5480 } else { 5481 $row = $ilDB->fetchAssoc($result); 5482 return (int) $row["state"]; 5483 } 5484 } 5485 5486 public function getUserSurveyExecutionStatus($a_code = null) 5487 { 5488 $ilUser = $this->user; 5489 $ilDB = $this->db; 5490 5491 $user_id = $ilUser->getId(); 5492 5493 // code is obligatory? 5494 if (!$this->isAccessibleWithoutCode()) { 5495 if (!$a_code) { 5496 // registered raters do not need code 5497 if ($this->get360Mode() && 5498 $user_id != ANONYMOUS_USER_ID && 5499 $this->isRater(0, $user_id)) { 5500 // auto-generate code 5501 $a_code = $this->createNewAccessCode(); 5502 $this->saveUserAccessCode($user_id, $a_code); 5503 } else { 5504 return null; 5505 } 5506 } 5507 } elseif ($user_id == ANONYMOUS_USER_ID || 5508 $this->getAnonymize() == self::ANONYMIZE_FREEACCESS) { 5509 if (!$a_code) { 5510 // auto-generate code 5511 $a_code = $this->createNewAccessCode(); 5512 $this->saveUserAccessCode($user_id, $a_code); 5513 } 5514 } else { 5515 $a_code = null; 5516 } 5517 5518 $res = array(); 5519 5520 $sql = "SELECT * FROM svy_finished" . 5521 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer"); 5522 // if proper user id is given, use it or current code 5523 if ($user_id != ANONYMOUS_USER_ID) { 5524 $sql .= " AND (user_fi = " . $ilDB->quote($user_id, "integer") . 5525 " OR anonymous_id = " . $ilDB->quote($a_code, "text") . ")"; 5526 } 5527 // use anonymous code to find finished id(s) 5528 else { 5529 $sql .= " AND anonymous_id = " . $ilDB->quote($a_code, "text"); 5530 } 5531 $set = $ilDB->query($sql); 5532 while ($row = $ilDB->fetchAssoc($set)) { 5533 $res[$row["finished_id"]] = array("appr_id" => $row["appr_id"], 5534 "user_id" => $row["user_fi"], 5535 "code" => $row["anonymous_id"], 5536 "finished" => (bool) $row["state"]); 5537 } 5538 5539 return array("code" => $a_code, "runs" => $res); 5540 } 5541 5542 public function findCodeForUser($a_user_id) 5543 { 5544 $ilDB = $this->db; 5545 5546 if ($a_user_id != ANONYMOUS_USER_ID) { 5547 $set = $ilDB->query("SELECT sf.anonymous_id FROM svy_finished sf" . 5548 " WHERE sf.survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") . 5549 " AND sf.user_fi = " . $ilDB->quote($a_user_id, "integer")); 5550 $a_code = $ilDB->fetchAssoc($set); 5551 return $a_code["anonymous_id"]; 5552 } 5553 } 5554 5555 public function isUnusedCode($a_code, $a_user_id) 5556 { 5557 $ilDB = $this->db; 5558 5559 $set = $ilDB->query("SELECT user_fi FROM svy_finished" . 5560 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") . 5561 " AND anonymous_id = " . $ilDB->quote($a_code, "text")); 5562 $user_id = $ilDB->fetchAssoc($set); 5563 $user_id = $user_id["user_fi"]; 5564 5565 if ($user_id && ($user_id != $a_user_id || $user_id == ANONYMOUS_USER_ID)) { 5566 return false; 5567 } 5568 return true; 5569 } 5570 5571 public function getFinishedIdsForAppraiseeId($a_appr_id, $a_exclude_appraisee = false) 5572 { 5573 $ilDB = $this->db; 5574 5575 $res = array(); 5576 5577 $set = $ilDB->query("SELECT finished_id, user_fi FROM svy_finished" . 5578 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") . 5579 " AND appr_id = " . $ilDB->quote($a_appr_id, "integer")); 5580 while ($row = $ilDB->fetchAssoc($set)) { 5581 if ($a_exclude_appraisee && $row["user_fi"] == $a_appr_id) { 5582 continue; 5583 } 5584 $res[] = $row["finished_id"]; 5585 } 5586 5587 return $res; 5588 } 5589 5590 /** 5591 * Get finished id for an appraisee and a rater 5592 * 5593 * @param int $a_appraisee_id appraisee id 5594 * @param int $a_rater_id rater id 5595 * @return int finished id 5596 */ 5597 public function getFinishedIdForAppraiseeIdAndRaterId($a_appr_id, $a_rat_id) 5598 { 5599 $ilDB = $this->db; 5600 5601 $set = $ilDB->query("SELECT finished_id, user_fi FROM svy_finished" . 5602 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") . 5603 " AND appr_id = " . $ilDB->quote($a_appr_id, "integer") . 5604 " AND user_fi = " . $ilDB->quote($a_rat_id, "integer")); 5605 $row = $ilDB->fetchAssoc($set); 5606 return $row["finished_id"]; 5607 } 5608 5609 5610 // 360° using competence/skill service 5611 5612 /** 5613 * Set skill service 5614 * 5615 * @param bool $a_val activate skill service 5616 */ 5617 public function setSkillService($a_val) 5618 { 5619 $this->mode_skill_service = $a_val; 5620 } 5621 5622 /** 5623 * Get skill service 5624 * 5625 * @return bool activate skill service 5626 */ 5627 public function getSkillService() 5628 { 5629 return $this->mode_skill_service; 5630 } 5631 5632 public function set360RaterSent($a_appraisee_id, $a_user_id, $a_anonymous_id, $a_tstamp = null) 5633 { 5634 $ilDB = $this->db; 5635 5636 if (!$a_tstamp) { 5637 $a_tstamp = time(); 5638 } 5639 5640 $ilDB->manipulate("UPDATE svy_360_rater" . 5641 " SET mail_sent = " . $ilDB->quote($a_tstamp, "integer") . 5642 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer") . 5643 " AND appr_id = " . $ilDB->quote($a_appraisee_id, "integer") . 5644 " AND user_id = " . $ilDB->quote($a_user_id, "integer") . 5645 " AND anonymous_id = " . $ilDB->quote($a_anonymous_id, "integer")); 5646 } 5647 5648 public function closeAppraisee($a_user_id) 5649 { 5650 global $DIC; 5651 5652 $ilDB = $DIC->database(); 5653 $user = $DIC->user(); 5654 5655 // close the appraisee 5656 $ilDB->manipulate("UPDATE svy_360_appr" . 5657 " SET has_closed = " . $ilDB->quote(time(), "integer") . 5658 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer") . 5659 " AND user_id = " . $ilDB->quote($a_user_id, "integer")); 5660 5661 // write competences 5662 include_once("./Services/Skill/classes/class.ilSkillManagementSettings.php"); 5663 $skmg_set = new ilSkillManagementSettings(); 5664 if ($this->getSkillService() && $skmg_set->isActivated()) { 5665 include_once("./Modules/Survey/classes/class.ilSurveySkill.php"); 5666 $sskill = new ilSurveySkill($this); 5667 $sskill->writeAppraiseeSkills($a_user_id); 5668 } 5669 5670 // send notification 5671 if ($user->getId() != $a_user_id) { 5672 $this->sendAppraiseeCloseNotification($a_user_id); 5673 } 5674 } 5675 5676 public function openAllAppraisees() 5677 { 5678 $ilDB = $this->db; 5679 5680 $ilDB->manipulate("UPDATE svy_360_appr" . 5681 " SET has_closed = " . $ilDB->quote(null, "integer") . 5682 " WHERE obj_id = " . $ilDB->quote($this->getSurveyId(), "integer")); 5683 } 5684 5685 public static function validateExternalRaterCode($a_ref_id, $a_code) 5686 { 5687 if (!isset($_SESSION["360_extrtr"][$a_ref_id])) { 5688 $svy = new self($a_ref_id); 5689 $svy->loadFromDB(); 5690 5691 if ($svy->canStartSurvey(null, true) && 5692 $svy->get360Mode() && 5693 $svy->isAnonymousKey($a_code)) { 5694 $anonymous_id = $svy->getAnonymousIdByCode($a_code); 5695 if ($anonymous_id) { 5696 if (sizeof($svy->getAppraiseesToRate(null, $anonymous_id))) { 5697 $_SESSION["360_extrtr"][$a_ref_id] = true; 5698 return true; 5699 } 5700 } 5701 } 5702 5703 $_SESSION["360_extrtr"][$a_ref_id] = false; 5704 return false; 5705 } 5706 5707 return $_SESSION["360_extrtr"][$a_ref_id]; 5708 } 5709 5710 5711 // 5712 // reminder/notification 5713 // 5714 5715 public function getReminderStatus() 5716 { 5717 return (bool) $this->reminder_status; 5718 } 5719 5720 public function setReminderStatus($a_value) 5721 { 5722 $this->reminder_status = (bool) $a_value; 5723 } 5724 5725 public function getReminderStart() 5726 { 5727 return $this->reminder_start; 5728 } 5729 5730 public function setReminderStart(ilDate $a_value = null) 5731 { 5732 $this->reminder_start = $a_value; 5733 } 5734 5735 public function getReminderEnd() 5736 { 5737 return $this->reminder_end; 5738 } 5739 5740 public function setReminderEnd(ilDate $a_value = null) 5741 { 5742 $this->reminder_end = $a_value; 5743 } 5744 5745 public function getReminderFrequency() 5746 { 5747 return $this->reminder_frequency; 5748 } 5749 5750 public function setReminderFrequency($a_value) 5751 { 5752 $this->reminder_frequency = (int) $a_value; 5753 } 5754 5755 public function getReminderTarget() 5756 { 5757 return $this->reminder_target; 5758 } 5759 5760 public function setReminderTarget($a_value) 5761 { 5762 $this->reminder_target = (int) $a_value; 5763 } 5764 5765 public function getReminderLastSent() 5766 { 5767 return $this->reminder_last_sent; 5768 } 5769 5770 public function setReminderLastSent($a_value) 5771 { 5772 $this->reminder_last_sent = $a_value; 5773 } 5774 5775 /** 5776 * @param bool $selectDefault 5777 * @return mixed 5778 */ 5779 public function getReminderTemplate($selectDefault = false) 5780 { 5781 if ($selectDefault) { 5782 $defaultTemplateId = 0; 5783 $this->getReminderMailTemplates($defaultTemplateId); 5784 5785 if ($defaultTemplateId > 0) { 5786 return $defaultTemplateId; 5787 } 5788 } 5789 5790 return $this->reminder_tmpl; 5791 } 5792 5793 public function setReminderTemplate($a_value) 5794 { 5795 $this->reminder_tmpl = $a_value; 5796 } 5797 5798 public function getTutorNotificationStatus() 5799 { 5800 return (bool) $this->tutor_ntf_status; 5801 } 5802 5803 public function setTutorNotificationStatus($a_value) 5804 { 5805 $this->tutor_ntf_status = (bool) $a_value; 5806 } 5807 5808 public function getTutorNotificationRecipients() 5809 { 5810 return $this->tutor_ntf_recipients; 5811 } 5812 5813 public function setTutorNotificationRecipients(array $a_value) 5814 { 5815 $this->tutor_ntf_recipients = $a_value; 5816 } 5817 5818 public function getTutorNotificationTarget() 5819 { 5820 return $this->tutor_ntf_target; 5821 } 5822 5823 public function setTutorNotificationTarget($a_value) 5824 { 5825 $this->tutor_ntf_target = (int) $a_value; 5826 } 5827 5828 protected function checkTutorNotification() 5829 { 5830 $ilDB = $this->db; 5831 5832 if ($this->getTutorNotificationStatus()) { 5833 $user_ids = $this->getNotificationTargetUserIds(($this->getTutorNotificationTarget() == self::NOTIFICATION_INVITED_USERS)); 5834 if ($user_ids) { 5835 $set = $ilDB->query("SELECT COUNT(*) numall FROM svy_finished" . 5836 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") . 5837 " AND state = " . $ilDB->quote(1, "integer") . 5838 " AND " . $ilDB->in("user_fi", $user_ids, "", "integer")); 5839 $row = $ilDB->fetchAssoc($set); 5840 if ($row["numall"] == sizeof($user_ids)) { 5841 $this->sendTutorNotification(); 5842 } 5843 } 5844 } 5845 } 5846 5847 /** 5848 * Send 360 reminders 5849 * 5850 * @param 5851 * @return 5852 */ 5853 public function sent360Reminders() 5854 { 5855 global $DIC; 5856 5857 $access = $DIC->access(); 5858 5859 // collect all open ratings 5860 $rater_ids = array(); 5861 foreach ($this->getAppraiseesData() as $app) { 5862 $this->log->debug("Handle appraisee " . $app['user_id']); 5863 5864 if (!$this->isAppraiseeClosed($app['user_id'])) { 5865 $this->log->debug("Check self evaluation, self: " . $this->get360SelfAppraisee() . ", target: " . $this->getReminderTarget()); 5866 5867 // self evaluation? 5868 if ($this->get360SelfEvaluation() && 5869 in_array($this->getReminderTarget(), array(ilObjSurvey::NOTIFICATION_APPRAISEES, ilObjSurvey::NOTIFICATION_APPRAISEES_AND_RATERS))) { 5870 $this->log->debug("...1"); 5871 // did user already finished self evaluation? 5872 if (!$this->is360SurveyStarted($app['user_id'], $app['user_id'])) { 5873 $this->log->debug("...2"); 5874 if (!is_array($rater_ids[$app['user_id']])) { 5875 $rater_ids[$app['user_id']] = array(); 5876 } 5877 if (!in_array($app["user_id"], $rater_ids[$app['user_id']])) { 5878 $rater_ids[$app['user_id']][] = $app["user_id"]; 5879 } 5880 } 5881 } 5882 5883 $this->log->debug("Check raters."); 5884 5885 // should raters be notified? 5886 if (in_array($this->getReminderTarget(), array(ilObjSurvey::NOTIFICATION_RATERS, ilObjSurvey::NOTIFICATION_APPRAISEES_AND_RATERS))) { 5887 foreach ($this->getRatersData($app['user_id']) as $rater) { 5888 // is rater not anonymous and did not rate yet? 5889 if (!$rater["anonymous_id"] && !$rater["finished"]) { 5890 if (!is_array($rater_ids[$rater["user_id"]])) { 5891 $rater_ids[$rater["user_id"]] = array(); 5892 } 5893 if (!in_array($app["user_id"], $rater_ids[$rater["user_id"]])) { 5894 $rater_ids[$rater["user_id"]][] = $app["user_id"]; 5895 } 5896 } 5897 } 5898 } 5899 } 5900 } 5901 5902 $this->log->debug("Found raters:" . count($rater_ids)); 5903 5904 foreach ($rater_ids as $id => $app) { 5905 if ($access->checkAccessOfUser($id, "read", "", $this->getRefId())) { 5906 $this->send360ReminderToUser($id, $app); 5907 } 5908 } 5909 } 5910 5911 /** 5912 * Send rater notification 5913 * 5914 * @param int $a_user_id user id 5915 */ 5916 public function send360ReminderToUser($a_user_id, $a_appraisee_ids) 5917 { 5918 $this->log->debug("Send mail to:" . $a_user_id); 5919 5920 include_once "./Services/Notification/classes/class.ilSystemNotification.php"; 5921 $ntf = new ilSystemNotification(); 5922 $ntf->setLangModules(array("svy", "survey")); 5923 $ntf->setRefId($this->getRefId()); 5924 $ntf->setGotoLangId('url'); 5925 5926 // user specific language 5927 $lng = $ntf->getUserLanguage($a_user_id); 5928 5929 $ntf->setIntroductionLangId("svy_user_added_360_rater_reminder_mail"); 5930 $subject = str_replace("%1", $this->getTitle(), $lng->txt("svy_user_added_360_rater")); 5931 5932 foreach ($a_appraisee_ids as $appraisee_id) { 5933 $ntf->addAdditionalInfo("survey_360_appraisee", ilUserUtil::getNamePresentation($appraisee_id, false, false, "", true)); 5934 } 5935 5936 // #10044 5937 $mail = new ilMail(ANONYMOUS_USER_ID); 5938 $mail->enableSOAP(false); // #10410 5939 $mail->sendMail( 5940 ilObjUser::_lookupLogin($a_user_id), 5941 null, 5942 null, 5943 $subject, 5944 $ntf->composeAndGetMessage($a_user_id, null, "read", true), 5945 null, 5946 array("system") 5947 ); 5948 } 5949 5950 5951 public function getNotificationTargetUserIds($a_use_invited) 5952 { 5953 $tree = $this->tree; 5954 5955 if ((bool) $a_use_invited) { 5956 $user_ids = $this->getInvitedUsers(); 5957 } else { 5958 $parent_grp_ref_id = $tree->checkForParentType($this->getRefId(), "grp"); 5959 if ($parent_grp_ref_id) { 5960 include_once "Modules/Group/classes/class.ilGroupParticipants.php"; 5961 $part = new ilGroupParticipants(ilObject::_lookupObjId($parent_grp_ref_id)); 5962 $user_ids = $part->getMembers(); 5963 } else { 5964 $parent_crs_ref_id = $tree->checkForParentType($this->getRefId(), "crs"); 5965 if ($parent_crs_ref_id) { 5966 include_once "Modules/Course/classes/class.ilCourseParticipants.php"; 5967 $part = new ilCourseParticipants(ilObject::_lookupObjId($parent_crs_ref_id)); 5968 $user_ids = $part->getMembers(); 5969 } 5970 } 5971 } 5972 return $user_ids; 5973 } 5974 5975 protected function sendTutorNotification() 5976 { 5977 include_once "./Services/Mail/classes/class.ilMail.php"; 5978 include_once "./Services/User/classes/class.ilObjUser.php"; 5979 include_once "./Services/Language/classes/class.ilLanguageFactory.php"; 5980 include_once "./Services/User/classes/class.ilUserUtil.php"; 5981 include_once "./Services/Link/classes/class.ilLink.php"; 5982 $link = ilLink::_getStaticLink($this->getRefId(), "svy"); 5983 5984 foreach ($this->getTutorNotificationRecipients() as $user_id) { 5985 // use language of recipient to compose message 5986 $ulng = ilLanguageFactory::_getLanguageOfUser($user_id); 5987 $ulng->loadLanguageModule('survey'); 5988 5989 $subject = sprintf($ulng->txt('survey_notification_tutor_subject'), $this->getTitle()); 5990 $message = sprintf($ulng->txt('survey_notification_tutor_salutation'), ilObjUser::_lookupFullname($user_id)) . "\n\n"; 5991 5992 $message .= $ulng->txt('survey_notification_tutor_body') . ":\n\n"; 5993 $message .= $ulng->txt('obj_svy') . ": " . $this->getTitle() . "\n"; 5994 $message .= "\n" . $ulng->txt('survey_notification_tutor_link') . ": " . $link; 5995 5996 $mail_obj = new ilMail(ANONYMOUS_USER_ID); 5997 $mail_obj->appendInstallationSignature(true); 5998 $mail_obj->sendMail( 5999 ilObjUser::_lookupLogin($user_id), 6000 "", 6001 "", 6002 $subject, 6003 $message, 6004 array(), 6005 array("system") 6006 ); 6007 } 6008 } 6009 6010 public function checkReminder() 6011 { 6012 $ilDB = $this->db; 6013 $ilAccess = $this->access; 6014 6015 $now = time(); 6016 $now_with_format = date("YmdHis", $now); 6017 $today = date("Y-m-d"); 6018 6019 $this->log->debug("Check status and dates."); 6020 6021 // object settings / participation period 6022 if ( 6023 $this->getOfflineStatus() || 6024 !$this->getReminderStatus() || 6025 ($this->getStartDate() && $now_with_format < $this->getStartDate()) || 6026 ($this->getEndDate() && $now_with_format > $this->getEndDate())) { 6027 return false; 6028 } 6029 6030 // reminder period 6031 $start = $this->getReminderStart(); 6032 if ($start) { 6033 $start = $start->get(IL_CAL_DATE); 6034 } 6035 $end = $this->getReminderEnd(); 6036 if ($end) { 6037 $end = $end->get(IL_CAL_DATE); 6038 } 6039 if ($today < $start || 6040 ($end && $today > $end)) { 6041 return false; 6042 } 6043 6044 $this->log->debug("Check access period."); 6045 6046 // object access period 6047 include_once "Services/Object/classes/class.ilObjectActivation.php"; 6048 $item_data = ilObjectActivation::getItem($this->getRefId()); 6049 if ($item_data["timing_type"] == ilObjectActivation::TIMINGS_ACTIVATION && 6050 ($now < $item_data["timing_start"] || 6051 $now > $item_data["timing_end"])) { 6052 return false; 6053 } 6054 6055 $this->log->debug("Check frequency."); 6056 6057 // check frequency 6058 $cut = new ilDate($today, IL_CAL_DATE); 6059 $cut->increment(IL_CAL_DAY, $this->getReminderFrequency() * -1); 6060 if (!$this->getReminderLastSent() || 6061 $cut->get(IL_CAL_DATE) >= substr($this->getReminderLastSent(), 0, 10)) { 6062 $missing_ids = array(); 6063 6064 if (!$this->get360Mode()) { 6065 $this->log->debug("Entering survey mode."); 6066 6067 // #16871 6068 $user_ids = $this->getNotificationTargetUserIds(($this->getReminderTarget() == self::NOTIFICATION_INVITED_USERS)); 6069 if ($user_ids) { 6070 // gather participants who already finished 6071 $finished_ids = array(); 6072 $set = $ilDB->query("SELECT user_fi FROM svy_finished" . 6073 " WHERE survey_fi = " . $ilDB->quote($this->getSurveyId(), "integer") . 6074 " AND state = " . $ilDB->quote(1, "text") . 6075 " AND " . $ilDB->in("user_fi", $user_ids, "", "integer")); 6076 while ($row = $ilDB->fetchAssoc($set)) { 6077 $finished_ids[] = $row["user_fi"]; 6078 } 6079 6080 // some users missing out? 6081 $missing_ids = array_diff($user_ids, $finished_ids); 6082 if ($missing_ids) { 6083 foreach ($missing_ids as $idx => $user_id) { 6084 // should be able to participate 6085 if (!$ilAccess->checkAccessOfUser($user_id, "read", "", $this->getRefId(), "svy", $this->getId())) { 6086 unset($missing_ids[$idx]); 6087 } 6088 } 6089 } 6090 if ($missing_ids) { 6091 $this->sentReminder($missing_ids); 6092 } 6093 } 6094 } else { 6095 $this->log->debug("Entering 360 mode."); 6096 6097 $this->sent360Reminders(); 6098 } 6099 6100 6101 $this->setReminderLastSent($today); 6102 $this->saveToDb(); 6103 6104 return sizeof($missing_ids); 6105 } 6106 6107 return false; 6108 } 6109 6110 protected function sentReminder(array $a_recipient_ids) 6111 { 6112 global $DIC; 6113 6114 // use mail template 6115 if ($this->getReminderTemplate() && 6116 array_key_exists($this->getReminderTemplate(), $this->getReminderMailTemplates())) { 6117 /** @var \ilMailTemplateService $templateService */ 6118 $templateService = $DIC['mail.texttemplates.service']; 6119 $tmpl = $templateService->loadTemplateForId((int) $this->getReminderTemplate()); 6120 6121 $tmpl_params = array( 6122 "ref_id" => $this->getRefId(), 6123 "ts" => time() 6124 ); 6125 } else { 6126 $tmpl = null; 6127 6128 include_once "./Services/Link/classes/class.ilLink.php"; 6129 $link = ilLink::_getStaticLink($this->getRefId(), "svy"); 6130 6131 include_once "./Services/Language/classes/class.ilLanguageFactory.php"; 6132 } 6133 6134 foreach ($a_recipient_ids as $user_id) { 6135 if ($tmpl) { 6136 $subject = $tmpl->getSubject(); 6137 $message = $this->sentReminderPlaceholders($tmpl->getMessage(), $user_id, $tmpl_params); 6138 } 6139 // use lng 6140 else { 6141 // use language of recipient to compose message 6142 $ulng = ilLanguageFactory::_getLanguageOfUser($user_id); 6143 $ulng->loadLanguageModule('survey'); 6144 6145 $subject = sprintf($ulng->txt('survey_reminder_subject'), $this->getTitle()); 6146 $message = sprintf($ulng->txt('survey_reminder_salutation'), ilObjUser::_lookupFullname($user_id)) . "\n\n"; 6147 6148 $message .= $ulng->txt('survey_reminder_body') . ":\n\n"; 6149 $message .= $ulng->txt('obj_svy') . ": " . $this->getTitle() . "\n"; 6150 $message .= "\n" . $ulng->txt('survey_reminder_link') . ": " . $link; 6151 } 6152 6153 $mail_obj = new ilMail(ANONYMOUS_USER_ID); 6154 $mail_obj->appendInstallationSignature(true); 6155 $mail_obj->sendMail( 6156 ilObjUser::_lookupLogin($user_id), 6157 "", 6158 "", 6159 $subject, 6160 $message, 6161 array(), 6162 array("system") 6163 ); 6164 } 6165 } 6166 6167 public function setActivationStartDate($starting_time = null) 6168 { 6169 $this->activation_starting_time = $starting_time; 6170 } 6171 6172 public function setActivationEndDate($ending_time = null) 6173 { 6174 $this->activation_ending_time = $ending_time; 6175 } 6176 6177 public function getActivationStartDate() 6178 { 6179 return (strlen($this->activation_starting_time)) ? $this->activation_starting_time : null; 6180 } 6181 6182 public function getActivationEndDate() 6183 { 6184 return (strlen($this->activation_ending_time)) ? $this->activation_ending_time : null; 6185 } 6186 6187 public function setViewOwnResults($a_value) 6188 { 6189 $this->view_own_results = (bool) $a_value; 6190 } 6191 6192 public function hasViewOwnResults() 6193 { 6194 return $this->view_own_results; 6195 } 6196 6197 public function setMailOwnResults($a_value) 6198 { 6199 $this->mail_own_results = (bool) $a_value; 6200 } 6201 6202 public function hasMailOwnResults() 6203 { 6204 return $this->mail_own_results; 6205 } 6206 6207 public function setMailConfirmation($a_value) 6208 { 6209 $this->mail_confirmation = (bool) $a_value; 6210 } 6211 6212 public function hasMailConfirmation() 6213 { 6214 return $this->mail_confirmation; 6215 } 6216 6217 public function setAnonymousUserList($a_value) 6218 { 6219 $this->anon_user_list = (bool) $a_value; 6220 } 6221 6222 public function hasAnonymousUserList() 6223 { 6224 return $this->anon_user_list; 6225 } 6226 6227 public static function getSurveySkippedValue() 6228 { 6229 global $DIC; 6230 6231 $lng = $DIC->language(); 6232 6233 // #13541 6234 6235 include_once "./Services/Administration/classes/class.ilSetting.php"; 6236 $surveySetting = new ilSetting("survey"); 6237 if (!$surveySetting->get("skipped_is_custom", false)) { 6238 return $lng->txt("skipped"); 6239 } else { 6240 return $surveySetting->get("skipped_custom_value", ""); 6241 } 6242 } 6243 6244 /** 6245 * @param int $defaultTemplateId 6246 * @return array 6247 */ 6248 public function getReminderMailTemplates(&$defaultTemplateId = null) 6249 { 6250 global $DIC; 6251 6252 $res = array(); 6253 6254 /** @var \ilMailTemplateService $templateService */ 6255 $templateService = $DIC['mail.texttemplates.service']; 6256 foreach ($templateService->loadTemplatesForContextId((string) ilSurveyMailTemplateReminderContext::ID) as $tmpl) { 6257 $res[$tmpl->getTplId()] = $tmpl->getTitle(); 6258 if (null !== $defaultTemplateId && $tmpl->isDefault()) { 6259 $defaultTemplateId = $tmpl->getTplId(); 6260 } 6261 } 6262 6263 return $res; 6264 } 6265 6266 protected function sentReminderPlaceholders($a_message, $a_user_id, array $a_context_params) 6267 { 6268 // see ilMail::replacePlaceholders() 6269 try { 6270 $context = \ilMailTemplateContextService::getTemplateContextById(ilSurveyMailTemplateReminderContext::ID); 6271 6272 $user = new \ilObjUser($a_user_id); 6273 6274 $processor = new \ilMailTemplatePlaceholderResolver($context, $a_message); 6275 $a_message = $processor->resolve($user, $a_context_params); 6276 } catch (\Exception $e) { 6277 ilLoggerFactory::getLogger('mail')->error(__METHOD__ . ' has been called with invalid context.'); 6278 } 6279 6280 return $a_message; 6281 } 6282 6283 public function setMode($a_value) 6284 { 6285 $this->mode = $a_value; 6286 } 6287 6288 public function getMode() 6289 { 6290 return $this->mode; 6291 } 6292 6293 public function setSelfEvaluationResults($a_value) 6294 { 6295 $this->mode_self_eval_results = $a_value; 6296 } 6297 6298 public function getSelfEvaluationResults() 6299 { 6300 return $this->mode_self_eval_results; 6301 } 6302} // END class.ilObjSurvey 6303