1<?php
2/* Copyright (c) 1998-2013 ILIAS open source, Extended GPL, see docs/LICENSE */
3
4require_once "./Services/Object/classes/class.ilObject.php";
5require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionProcessLocker.php';
6
7/**
8 * Class ilObjAssessmentFolder
9 *
10 * @author	Helmut Schottmüller <hschottm@gmx.de>
11 * @author	Björn Heyser <bheyser@databay.de>
12 *
13 * @version	$Id$
14 *
15 * @ingroup ModulesTest
16 */
17class ilObjAssessmentFolder extends ilObject
18{
19    const ADDITIONAL_QUESTION_CONTENT_EDITING_MODE_PAGE_OBJECT_DISABLED = 0;
20    const ADDITIONAL_QUESTION_CONTENT_EDITING_MODE_PAGE_OBJECT_ENABLED = 1;
21
22    const ASS_PROC_LOCK_MODE_NONE = 'none';
23    const ASS_PROC_LOCK_MODE_FILE = 'file';
24    const ASS_PROC_LOCK_MODE_DB = 'db';
25
26    const SETTINGS_KEY_SKL_TRIG_NUM_ANSWERS_BARRIER = 'ass_skl_trig_num_answ_barrier';
27    const DEFAULT_SKL_TRIG_NUM_ANSWERS_BARRIER = 1;
28
29    public $setting;
30
31    /**
32    * Constructor
33    * @access	public
34    * @param	integer	reference_id or object_id
35    * @param	boolean	treat the id as reference_id (true) or object_id (false)
36    */
37    public function __construct($a_id = 0, $a_call_by_reference = true)
38    {
39        include_once "./Services/Administration/classes/class.ilSetting.php";
40        $this->setting = new ilSetting("assessment");
41        $this->type = "assf";
42        parent::__construct($a_id, $a_call_by_reference);
43    }
44
45    /**
46    * update object data
47    *
48    * @access	public
49    * @return	boolean
50    */
51    public function update()
52    {
53        if (!parent::update()) {
54            return false;
55        }
56
57        // put here object specific stuff
58
59        return true;
60    }
61
62    public static function getSkillTriggerAnswerNumberBarrier()
63    {
64        require_once 'Services/Administration/classes/class.ilSetting.php';
65        $assSettings = new ilSetting('assessment');
66
67        return $assSettings->get(
68            self::SETTINGS_KEY_SKL_TRIG_NUM_ANSWERS_BARRIER,
69            self::DEFAULT_SKL_TRIG_NUM_ANSWERS_BARRIER
70        );
71    }
72
73    /**
74    * delete object and all related data
75    *
76    * @access	public
77    * @return	boolean	true if all object data were removed; false if only a references were removed
78    */
79    public function delete()
80    {
81        // always call parent delete function first!!
82        if (!parent::delete()) {
83            return false;
84        }
85
86        //put here your module specific stuff
87
88        return true;
89    }
90
91    /**
92    * enable assessment logging
93    */
94    public function _enableAssessmentLogging($a_enable)
95    {
96        $setting = new ilSetting("assessment");
97
98        if ($a_enable) {
99            $setting->set("assessment_logging", 1);
100        } else {
101            $setting->set("assessment_logging", 0);
102        }
103    }
104
105    /**
106    * set the log language
107    */
108    public function _setLogLanguage($a_language)
109    {
110        $setting = new ilSetting("assessment");
111
112        $setting->set("assessment_log_language", $a_language);
113    }
114
115    /**
116     * check wether assessment logging is enabled or not
117     */
118    public static function _enabledAssessmentLogging()
119    {
120        $setting = new ilSetting("assessment");
121
122        return (boolean) $setting->get("assessment_logging");
123    }
124
125    /**
126    * Returns the forbidden questiontypes for ILIAS
127    */
128    public static function _getForbiddenQuestionTypes()
129    {
130        $setting = new ilSetting("assessment");
131        $types = $setting->get("forbidden_questiontypes");
132        $result = array();
133        if (strlen(trim($types)) == 0) {
134            $result = array();
135        } else {
136            $result = unserialize($types);
137        }
138        return $result;
139    }
140
141    /**
142    * Sets the forbidden questiontypes for ILIAS
143    *
144    * @param array $a_types An array containing the database ID's of the forbidden question types
145    */
146    public function _setForbiddenQuestionTypes($a_types)
147    {
148        $setting = new ilSetting("assessment");
149        $types = "";
150        if (is_array($a_types) && (count($a_types) > 0)) {
151            $types = serialize($a_types);
152        }
153        $setting->set("forbidden_questiontypes", $types);
154    }
155
156    /**
157    * retrieve the log language for assessment logging
158    */
159    public static function _getLogLanguage()
160    {
161        $setting = new ilSetting("assessment");
162
163        $lang = $setting->get("assessment_log_language");
164        if (strlen($lang) == 0) {
165            $lang = "en";
166        }
167        return $lang;
168    }
169
170    /**
171     * Returns the fact wether manually scoreable
172     * question types exist or not
173     *
174     * @static
175     * @access	public
176     *
177     * @return	boolean		$mananuallyScoreableQuestionTypesExists
178     */
179    public static function _mananuallyScoreableQuestionTypesExists()
180    {
181        if (count(self::_getManualScoring()) > 0) {
182            return true;
183        }
184
185        return false;
186    }
187
188    /**
189    * Retrieve the manual scoring settings
190    */
191    public static function _getManualScoring()
192    {
193        $setting = new ilSetting("assessment");
194
195        $types = $setting->get("assessment_manual_scoring");
196        return explode(",", $types);
197    }
198
199    /**
200    * Retrieve the manual scoring settings as type strings
201    */
202    public static function _getManualScoringTypes()
203    {
204        global $DIC;
205        $ilDB = $DIC['ilDB'];
206
207        $result = $ilDB->query("SELECT * FROM qpl_qst_type");
208        $dbtypes = array();
209        while ($row = $ilDB->fetchAssoc($result)) {
210            $dbtypes[$row["question_type_id"]] = $row["type_tag"];
211        }
212        $setting = new ilSetting("assessment");
213        $types = $setting->get("assessment_manual_scoring");
214        $ids = explode(",", $types);
215        foreach ($ids as $key => $value) {
216            $ids[$key] = $dbtypes[$value];
217        }
218        return $ids;
219    }
220
221    /**
222    * Set the manual scoring settings
223    *
224    * @param array $type_ids An array containing the database ids of the question types which could be scored manually
225    */
226    public function _setManualScoring($type_ids)
227    {
228        $setting = new ilSetting("assessment");
229        if ((!is_array($type_ids)) || (count($type_ids) == 0)) {
230            $setting->delete("assessment_manual_scoring");
231        } else {
232            $setting->set("assessment_manual_scoring", implode(",", $type_ids));
233        }
234    }
235
236    public static function getScoringAdjustableQuestions()
237    {
238        $setting = new ilSetting("assessment");
239
240        $types = $setting->get("assessment_scoring_adjustment");
241        return explode(",", $types);
242    }
243
244    public static function setScoringAdjustableQuestions($type_ids)
245    {
246        $setting = new ilSetting("assessment");
247        if ((!is_array($type_ids)) || (count($type_ids) == 0)) {
248            $setting->delete("assessment_scoring_adjustment");
249        } else {
250            $setting->set("assessment_scoring_adjustment", implode(",", $type_ids));
251        }
252    }
253
254    public static function getScoringAdjustmentEnabled()
255    {
256        $setting = new ilSetting("assessment");
257        return $setting->get('assessment_adjustments_enabled');
258    }
259
260    public static function setScoringAdjustmentEnabled($active)
261    {
262        $setting = new ilSetting('assessment');
263        $setting->set('assessment_adjustments_enabled', (bool) $active);
264    }
265
266    /**
267    * Add an assessment log entry
268    *
269    * @param integer $user_id The user id of the acting user
270    * @param integer $object_id The database id of the modified test object
271    * @param string $logtext The textual description for the log entry
272    * @param integer $question_id The database id of a modified question (optional)
273    * @param integer $original_id The database id of the original of a modified question (optional)
274    * @return array Array containing the datasets between $ts_from and $ts_to for the test with the id $test_id
275    */
276    public static function _addLog($user_id, $object_id, $logtext, $question_id = "", $original_id = "", $test_only = false, $test_ref_id = null)
277    {
278        global $DIC;
279        $ilUser = $DIC['ilUser'];
280        $ilDB = $DIC['ilDB'];
281        if (strlen($question_id) == 0) {
282            $question_id = null;
283        }
284        if (strlen($original_id) == 0) {
285            $original_id = null;
286        }
287        if (strlen($test_ref_id) == 0) {
288            $test_ref_id = null;
289        }
290        $only = ($test_only == true) ? 1 : 0;
291        $next_id = $ilDB->nextId('ass_log');
292        $affectedRows = $ilDB->manipulateF(
293            "INSERT INTO ass_log (ass_log_id, user_fi, obj_fi, logtext, question_fi, original_fi, test_only, ref_id, tstamp) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)",
294            array('integer', 'integer', 'integer', 'text', 'integer', 'integer', 'text', 'integer', 'integer'),
295            array(
296                $next_id,
297                $user_id,
298                $object_id,
299                $logtext,
300                $question_id,
301                $original_id,
302                $only,
303                $test_ref_id,
304                time()
305            )
306        );
307    }
308
309    /**
310    * Retrieve assessment log datasets from the database
311    *
312    * @param string $ts_from Timestamp of the starting date/time period
313    * @param string $ts_to Timestamp of the ending date/time period
314    * @param integer $test_id Database id of the ILIAS test object
315    * @return array Array containing the datasets between $ts_from and $ts_to for the test with the id $test_id
316    */
317    public static function getLog($ts_from, $ts_to, $test_id, $test_only = false)
318    {
319        global $DIC;
320        $ilDB = $DIC['ilDB'];
321
322        $log = array();
323        if ($test_only == true) {
324            $result = $ilDB->queryF(
325                "SELECT * FROM ass_log WHERE obj_fi = %s AND tstamp > %s AND tstamp < %s AND test_only = %s ORDER BY tstamp",
326                array('integer','integer','integer','text'),
327                array(
328                    $test_id,
329                    $ts_from,
330                    $ts_to,
331                    1
332                )
333            );
334        } else {
335            $result = $ilDB->queryF(
336                "SELECT * FROM ass_log WHERE obj_fi = %s AND tstamp > %s AND tstamp < %s ORDER BY tstamp",
337                array('integer','integer','integer'),
338                array(
339                    $test_id,
340                    $ts_from,
341                    $ts_to
342                )
343            );
344        }
345        while ($row = $ilDB->fetchAssoc($result)) {
346            if (!array_key_exists($row["tstamp"], $log)) {
347                $log[$row["tstamp"]] = array();
348            }
349            array_push($log[$row["tstamp"]], $row);
350        }
351        krsort($log);
352        // flatten array
353        $log_array = array();
354        foreach ($log as $key => $value) {
355            foreach ($value as $index => $row) {
356                array_push($log_array, $row);
357            }
358        }
359        return $log_array;
360    }
361
362    /**
363    * Retrieve assessment log datasets from the database
364    *
365    * @param string $ts_from Timestamp of the starting date/time period
366    * @param string $ts_to Timestamp of the ending date/time period
367    * @param integer $test_id Database id of the ILIAS test object
368    * @return array Array containing the datasets between $ts_from and $ts_to for the test with the id $test_id
369    */
370    public static function _getLog($ts_from, $ts_to, $test_id, $test_only = false)
371    {
372        global $DIC;
373        $ilDB = $DIC['ilDB'];
374
375        $log = array();
376        if ($test_only == true) {
377            $result = $ilDB->queryF(
378                "SELECT * FROM ass_log WHERE obj_fi = %s AND tstamp > %s AND tstamp < %s AND test_only = %s ORDER BY tstamp",
379                array('integer', 'integer', 'integer', 'text'),
380                array($test_id, $ts_from, $ts_to, 1)
381            );
382        } else {
383            $result = $ilDB->queryF(
384                "SELECT * FROM ass_log WHERE obj_fi = %s AND tstamp > %s AND tstamp < %s ORDER BY tstamp",
385                array('integer', 'integer', 'integer'),
386                array($test_id, $ts_from, $ts_to)
387            );
388        }
389        while ($row = $ilDB->fetchAssoc($result)) {
390            if (!array_key_exists($row["tstamp"], $log)) {
391                $log[$row["tstamp"]] = array();
392            }
393            $type_href = "";
394            if (array_key_exists("ref_id", $row)) {
395                if ($row["ref_id"] > 0) {
396                    $type = ilObject::_lookupType($row['ref_id'], true);
397                    switch ($type) {
398                        case "tst":
399                            $type_href = sprintf("goto.php?target=tst_%s&amp;client_id=" . CLIENT_ID, $row["ref_id"]);
400                            break;
401                        case "cat":
402                            $type_href = sprintf("goto.php?target=cat_%s&amp;client_id=" . CLIENT_ID, $row["ref_id"]);
403                            break;
404                    }
405                }
406            }
407            $row["href"] = $type_href;
408            array_push($log[$row["tstamp"]], $row);
409        }
410        krsort($log);
411        // flatten array
412        $log_array = array();
413        foreach ($log as $key => $value) {
414            foreach ($value as $index => $row) {
415                array_push($log_array, $row);
416            }
417        }
418        return $log_array;
419    }
420
421    /**
422    * Returns the number of log entries for a given test id
423    *
424    * @param integer $test_obj_id Database id of the ILIAS test object
425    * @return integer The number of log entries for the test object
426    */
427    public function getNrOfLogEntries($test_obj_id)
428    {
429        global $DIC;
430        $ilDB = $DIC['ilDB'];
431        $result = $ilDB->queryF(
432            "SELECT COUNT(obj_fi) logcount FROM ass_log WHERE obj_fi = %s",
433            array('integer'),
434            array($test_obj_id)
435        );
436        if ($result->numRows()) {
437            $row = $ilDB->fetchAssoc($result);
438            return $row["logcount"];
439        } else {
440            return 0;
441        }
442    }
443
444    /**
445    * Returns the full path output of an object
446    *
447    * @param integer $ref_id The reference id of the object
448    * @return string The full path with hyperlinks to the path elements
449    */
450    public function getFullPath($ref_id)
451    {
452        global $DIC;
453        $tree = $DIC['tree'];
454        $path = $tree->getPathFull($ref_id);
455        $pathelements = array();
456        foreach ($path as $id => $data) {
457            if ($id == 0) {
458                array_push($pathelements, ilUtil::prepareFormOutput($this->lng->txt("repository")));
459            } else {
460                array_push($pathelements, "<a href=\"./goto.php?target=" . $data["type"] . "_" . $data["ref_id"] . "&amp;client=" . CLIENT_ID . "\">" .
461                    ilUtil::prepareFormOutput($data["title"]) . "</a>");
462            }
463        }
464        return implode("&nbsp;&gt;&nbsp;", $pathelements);
465    }
466
467    /**
468    * Deletes the log entries for a given array of test object IDs
469    *
470    * @param array $a_array An array containing the object IDs of the tests
471    */
472    public function deleteLogEntries($a_array)
473    {
474        global $DIC;
475        $ilDB = $DIC['ilDB'];
476        $ilUser = $DIC['ilUser'];
477
478        foreach ($a_array as $object_id) {
479            $affectedRows = $ilDB->manipulateF(
480                "DELETE FROM ass_log WHERE obj_fi = %s",
481                array('integer'),
482                array($object_id)
483            );
484            self::_addLog($ilUser->getId(), $object_id, $this->lng->txt("assessment_log_deleted"));
485        }
486    }
487
488    /**
489     * returns the fact wether content editing with ilias page editor is enabled for questions or not
490     *
491     * @global ilSetting $ilSetting
492     * @return bool $isPageEditorEnabled
493     */
494    public static function isAdditionalQuestionContentEditingModePageObjectEnabled()
495    {
496        require_once 'Modules/TestQuestionPool/classes/class.assQuestion.php';
497
498        global $DIC;
499        $ilSetting = $DIC['ilSetting'];
500
501        $isPageEditorEnabled = $ilSetting->get(
502            'enable_tst_page_edit',
503            self::ADDITIONAL_QUESTION_CONTENT_EDITING_MODE_PAGE_OBJECT_DISABLED
504        );
505
506        return $isPageEditorEnabled;
507    }
508
509    public function getAssessmentProcessLockMode()
510    {
511        return $this->setting->get('ass_process_lock_mode', self::ASS_PROC_LOCK_MODE_NONE);
512    }
513
514    public function setAssessmentProcessLockMode($lockMode)
515    {
516        $this->setting->set('ass_process_lock_mode', $lockMode);
517    }
518
519    public static function getValidAssessmentProcessLockModes()
520    {
521        return array(self::ASS_PROC_LOCK_MODE_NONE, self::ASS_PROC_LOCK_MODE_FILE, self::ASS_PROC_LOCK_MODE_DB);
522    }
523
524    public function getSkillTriggeringNumAnswersBarrier()
525    {
526        return $this->setting->get(
527            'ass_skl_trig_num_answ_barrier',
528            self::DEFAULT_SKL_TRIG_NUM_ANSWERS_BARRIER
529        );
530    }
531
532    public function setSkillTriggeringNumAnswersBarrier($skillTriggeringNumAnswersBarrier)
533    {
534        $this->setting->set('ass_skl_trig_num_answ_barrier', $skillTriggeringNumAnswersBarrier);
535    }
536
537    public function setExportEssayQuestionsWithHtml($value)
538    {
539        $this->setting->set('export_essay_qst_with_html', $value);
540    }
541
542    public function getExportEssayQuestionsWithHtml()
543    {
544        return $this->setting->get('export_essay_qst_with_html');
545    }
546
547    public function fetchScoringAdjustableTypes($allQuestionTypes)
548    {
549        require_once 'Modules/TestQuestionPool/classes/class.assQuestionGUI.php';
550        $scoringAdjustableQuestionTypes = array();
551
552        foreach ($allQuestionTypes as $type => $typeData) {
553            $questionGui = assQuestionGUI::_getQuestionGUI($typeData['type_tag']);
554
555            if ($this->questionSupportsScoringAdjustment($questionGui)) {
556                $scoringAdjustableQuestionTypes[$type] = $typeData;
557            }
558        }
559
560        return $scoringAdjustableQuestionTypes;
561    }
562
563    private function questionSupportsScoringAdjustment(\assQuestionGUI $question_object)
564    {
565        return ($question_object instanceof ilGuiQuestionScoringAdjustable
566            || $question_object instanceof ilGuiAnswerScoringAdjustable)
567        && ($question_object->object instanceof ilObjQuestionScoringAdjustable
568            || $question_object->object instanceof ilObjAnswerScoringAdjustable);
569    }
570}
571