1<?php
2 /*
3   +----------------------------------------------------------------------------+
4   | ILIAS open source                                                          |
5   +----------------------------------------------------------------------------+
6   | Copyright (c) 1998-2001 ILIAS open source, University of Cologne           |
7   |                                                                            |
8   | This program is free software; you can redistribute it and/or              |
9   | modify it under the terms of the GNU General Public License                |
10   | as published by the Free Software Foundation; either version 2             |
11   | of the License, or (at your option) any later version.                     |
12   |                                                                            |
13   | This program is distributed in the hope that it will be useful,            |
14   | but WITHOUT ANY WARRANTY; without even the implied warranty of             |
15   | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              |
16   | GNU General Public License for more details.                               |
17   |                                                                            |
18   | You should have received a copy of the GNU General Public License          |
19   | along with this program; if not, write to the Free Software                |
20   | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
21   +----------------------------------------------------------------------------+
22*/
23
24/**
25* Basic class for all survey question types
26*
27* The SurveyQuestion class defines and encapsulates basic methods and attributes
28* for survey question types to be used for all parent classes.
29*
30* @author		Helmut Schottmüller <helmut.schottmueller@mac.com>
31* @version	$Id$
32* @ingroup ModulesSurveyQuestionPool
33*/
34class SurveyQuestion
35{
36    /**
37     * @var ilObjUser
38     */
39    protected $user;
40
41    /**
42     * @var ilDB
43     */
44    protected $db;
45
46    /**
47    * A unique question id
48    *
49    * @var integer
50    */
51    public $id;
52
53    /**
54    * A title string to describe the question
55    *
56    * @var string
57    */
58    public $title;
59    /**
60    * A description string to describe the question more detailed as the title
61    *
62    * @var string
63    */
64    public $description;
65    /**
66    * A unique positive numerical ID which identifies the owner/creator of the question.
67    * This can be a primary key from a database table for example.
68    *
69    * @var integer
70    */
71    public $owner;
72
73    /**
74    * A text representation of the authors name. The name of the author must
75    * not necessary be the name of the owner.
76    *
77    * @var string
78    */
79    public $author;
80
81    /**
82    * Contains uris name and uris to additional materials
83    *
84    * @var array
85    */
86    public $materials;
87
88    /**
89    * The database id of a survey in which the question is contained
90    *
91    * @var integer
92    */
93    public $survey_id;
94
95    /**
96    * Object id of the container object
97    *
98    * @var double
99    */
100    public $obj_id;
101
102    /**
103    * Questiontext string
104    *
105    * @var string
106    */
107    public $questiontext;
108
109    /**
110    * Contains the obligatory state of the question
111    *
112    * @var boolean
113    */
114    public $obligatory;
115
116    /**
117    * The reference to the Language class
118    *
119    * @var object
120    */
121    public $lng;
122
123    /**
124    * The orientation of the question output (0 = vertical, 1 = horizontal)
125    *
126    * @var integer
127    */
128    public $orientation;
129
130    public $material;
131    public $complete;
132
133    /**
134    * An array containing the cumulated results of the question for a given survey
135    */
136    protected $cumulated;
137
138    /**
139    * data array containing the question data
140    */
141    private $arrData;
142
143    /**
144    * @var ilLogger
145    */
146    protected $log;
147
148    /**
149    * SurveyQuestion constructor
150    * The constructor takes possible arguments an creates an instance of the SurveyQuestion object.
151    *
152    * @param string $title A title string to describe the question
153    * @param string $description A description string to describe the question
154    * @param string $author A string containing the name of the questions author
155    * @param integer $owner A numerical ID to identify the owner/creator
156    * @access public
157    */
158    public function __construct($title = "", $description = "", $author = "", $questiontext = "", $owner = -1)
159    {
160        global $DIC;
161
162        $this->user = $DIC->user();
163        $this->db = $DIC->database();
164        $lng = $DIC->language();
165        $ilUser = $DIC->user();
166
167        $this->lng = $lng;
168        $this->complete = 0;
169        $this->title = $title;
170        $this->description = $description;
171        $this->questiontext = $questiontext;
172        $this->author = $author;
173        $this->cumulated = array();
174        if (!$this->author) {
175            $this->author = $ilUser->fullname;
176        }
177        $this->owner = $owner;
178        if ($this->owner == -1) {
179            $this->owner = $ilUser->getId();
180        }
181        $this->id = -1;
182        $this->survey_id = -1;
183        $this->obligatory = 1;
184        $this->orientation = 0;
185        $this->materials = array();
186        $this->material = array();
187        $this->arrData = array();
188
189        $this->log = ilLoggerFactory::getLogger('svy');
190    }
191
192    /**
193    * Sets the complete state of the question
194    *
195    * @param integer $a_complete 1 if complete, 0 otherwise
196    * @access public
197    */
198    public function setComplete($a_complete)
199    {
200        $this->complete = ($a_complete) ? 1 : 0;
201    }
202
203    /**
204    * Returns 1, if a question is complete for use
205    *
206    * @return integer 1, if the question is complete for use, otherwise 0
207    * @access public
208    */
209    public function isComplete()
210    {
211        return 0;
212    }
213
214    /**
215    * Returns TRUE if the question title exists in the database
216    *
217    * @param string $title The title of the question
218    * @param string $questionpool_reference The reference id of a container question pool
219    * @return boolean The result of the title check
220    * @access public
221    */
222    public function questionTitleExists($title, $questionpool_object = "")
223    {
224        $ilDB = $this->db;
225
226        $refwhere = "";
227        if (strcmp($questionpool_object, "") != 0) {
228            $refwhere = sprintf(
229                " AND obj_fi = %s",
230                $ilDB->quote($questionpool_object, 'integer')
231            );
232        }
233        $result = $ilDB->queryF(
234            "SELECT question_id FROM svy_question WHERE title = %s$refwhere",
235            array('text'),
236            array($title)
237        );
238        return ($result->numRows() > 0) ? true : false;
239    }
240
241    /**
242    * Sets the title string of the SurveyQuestion object
243    *
244    * @param string $title A title string to describe the question
245    * @access public
246    * @see $title
247    */
248    public function setTitle($title = "")
249    {
250        $this->title = $title;
251    }
252
253    /**
254    * Sets the obligatory state of the question
255    *
256    * @param integer $obligatory 1, if the question is obligatory, otherwise 0
257    * @access public
258    * @see $obligatory
259    */
260    public function setObligatory($obligatory = 1)
261    {
262        $this->obligatory = ($obligatory) ? 1 : 0;
263    }
264
265    /**
266    * Sets the orientation of the question output
267    *
268    * @param integer $orientation 0 = vertical, 1 = horizontal
269    * @access public
270    * @see $orientation
271    */
272    public function setOrientation($orientation = 0)
273    {
274        $this->orientation = ($orientation) ? $orientation : 0;
275    }
276
277    /**
278    * Sets the id of the SurveyQuestion object
279    *
280    * @param integer $id A unique integer value
281    * @access public
282    * @see $id
283    */
284    public function setId($id = -1)
285    {
286        $this->id = $id;
287    }
288
289    /**
290    * Sets the survey id of the SurveyQuestion object
291    *
292    * @param integer $id A unique integer value
293    * @access public
294    * @see $survey_id
295    */
296    public function setSurveyId($id = -1)
297    {
298        $this->survey_id = $id;
299    }
300
301    /**
302    * Sets the description string of the SurveyQuestion object
303    *
304    * @param string $description A description string to describe the question
305    * @access public
306    * @see $description
307    */
308    public function setDescription($description = "")
309    {
310        $this->description = $description;
311    }
312
313    /**
314    * Sets the materials uri
315    *
316    * @param string $materials_file An uri to additional materials
317    * @param string $materials_name An uri name to additional materials
318    * @access public
319    * @see $materials
320    */
321    public function addMaterials($materials_file, $materials_name = "")
322    {
323        if (empty($materials_name)) {
324            $materials_name = $materials_file;
325        }
326        if ((!empty($materials_name)) && (!array_key_exists($materials_name, $this->materials))) {
327            $this->materials[$materials_name] = $materials_file;
328        }
329    }
330
331    /**
332    * Sets and uploads the materials uri
333    *
334    * @param string $materials_filename, string $materials_tempfilename, string $materials
335    * @access public
336    * @see $materials
337    */
338    public function setMaterialsfile($materials_filename, $materials_tempfilename = "", $materials_name = "")
339    {
340        if (!empty($materials_filename)) {
341            $materialspath = $this->getMaterialsPath();
342            if (!file_exists($materialspath)) {
343                ilUtil::makeDirParents($materialspath);
344            }
345            if (ilUtil::moveUploadedFile(
346                $materials_tempfilename,
347                $materials_filename,
348                $materialspath . $materials_filename
349            )) {
350                print "image not uploaded!!!! ";
351            } else {
352                $this->addMaterials($materials_filename, $materials_name);
353            }
354        }
355    }
356
357    /**
358    * Deletes a materials uri with a given name.
359    *
360    * @param string $index A materials_name of the materials uri
361    * @access public
362    * @see $materials
363    */
364    public function deleteMaterial($materials_name = "")
365    {
366        foreach ($this->materials as $key => $value) {
367            if (strcmp($key, $materials_name) == 0) {
368                if (file_exists($this->getMaterialsPath() . $value)) {
369                    unlink($this->getMaterialsPath() . $value);
370                }
371                unset($this->materials[$key]);
372            }
373        }
374    }
375
376    /**
377    * Deletes all materials uris
378    *
379    * @access public
380    * @see $materials
381    */
382    public function flushMaterials()
383    {
384        $this->materials = array();
385    }
386
387    /**
388    * Sets the authors name of the SurveyQuestion object
389    *
390    * @param string $author A string containing the name of the questions author
391    * @access public
392    * @see $author
393    */
394    public function setAuthor($author = "")
395    {
396        $ilUser = $this->user;
397
398        if (!$author) {
399            $author = $ilUser->fullname;
400        }
401        $this->author = $author;
402    }
403
404    /**
405    * Sets the questiontext of the SurveyQuestion object
406    *
407    * @param string $questiontext A string containing the questiontext
408    * @access public
409    * @see $questiontext
410    */
411    public function setQuestiontext($questiontext = "")
412    {
413        $this->questiontext = $questiontext;
414    }
415
416    /**
417    * Sets the creator/owner ID of the SurveyQuestion object
418    *
419    * @param integer $owner A numerical ID to identify the owner/creator
420    * @access public
421    * @see $owner
422    */
423    public function setOwner($owner = "")
424    {
425        $this->owner = $owner;
426    }
427
428    /**
429    * Gets the title string of the SurveyQuestion object
430    *
431    * @return string The title string to describe the question
432    * @access public
433    * @see $title
434    */
435    public function getTitle()
436    {
437        return $this->title;
438    }
439
440    public function getLabel()
441    {
442        return $this->label;
443    }
444
445    /**
446    * Gets the id of the SurveyQuestion object
447    *
448    * @return integer The id of the SurveyQuestion object
449    * @access public
450    * @see $id
451    */
452    public function getId()
453    {
454        return $this->id;
455    }
456
457    /**
458    * Gets the obligatory state of the question
459    *
460    * @return integer 1, if the question is obligatory, otherwise 0
461    * @see $obligatory
462    */
463    public function getObligatory($survey_id = "")
464    {
465        return ($this->obligatory) ? 1 : 0;
466    }
467
468    /**
469    * Gets the survey id of the SurveyQuestion object
470    *
471    * @return integer The survey id of the SurveyQuestion object
472    * @access public
473    * @see $survey_id
474    */
475    public function getSurveyId()
476    {
477        return $this->survey_id;
478    }
479
480    /**
481    * Gets the orientation of the question output
482    *
483    * @return integer 0 = vertical, 1 = horizontal
484    * @access public
485    * @see $orientation
486    */
487    public function getOrientation()
488    {
489        switch ($this->orientation) {
490            case 0:
491            case 1:
492            case 2:
493                break;
494            default:
495                $this->orientation = 0;
496                break;
497        }
498        return $this->orientation;
499    }
500
501
502    /**
503    * Gets the description string of the SurveyQuestion object
504    *
505    * @return string The description string to describe the question
506    * @access public
507    * @see $description
508    */
509    public function getDescription()
510    {
511        return (strlen($this->description)) ? $this->description : null;
512    }
513
514    /**
515    * Gets the authors name of the SurveyQuestion object
516    *
517    * @return string The string containing the name of the questions author
518    * @access public
519    * @see $author
520    */
521    public function getAuthor()
522    {
523        return (strlen($this->author)) ? $this->author : null;
524    }
525
526    /**
527    * Gets the creator/owner ID of the SurveyQuestion object
528    *
529    * @return integer The numerical ID to identify the owner/creator
530    * @access public
531    * @see $owner
532    */
533    public function getOwner()
534    {
535        return $this->owner;
536    }
537
538    /**
539    * Gets the questiontext of the SurveyQuestion object
540    *
541    * @return string The questiontext of the question object
542    * @access public
543    * @see $questiontext
544    */
545    public function getQuestiontext()
546    {
547        return (strlen($this->questiontext)) ? $this->questiontext : null;
548    }
549
550    /**
551    * Get the reference id of the container object
552    *
553    * @return integer The reference id of the container object
554    * @access public
555    * @see $obj_id
556    */
557    public function getObjId()
558    {
559        return $this->obj_id;
560    }
561
562    /**
563    * Set the reference id of the container object
564    *
565    * @param integer $obj_id The reference id of the container object
566    * @access public
567    * @see $obj_id
568    */
569    public function setObjId($obj_id = 0)
570    {
571        $this->obj_id = $obj_id;
572    }
573
574    /**
575    * Duplicates a survey question
576    *
577    * @access public
578    */
579    public function duplicate($for_survey = true, $title = "", $author = "", $owner = "", $a_survey_id = 0)
580    {
581        if ($this->getId() <= 0) {
582            // The question has not been saved. It cannot be duplicated
583            return;
584        }
585        // duplicate the question in database
586        $clone = $this;
587        $original_id = $this->getId();
588        $clone->setId(-1);
589        if ($a_survey_id > 0) {
590            $clone->setObjId($a_survey_id);
591        }
592        if ($title) {
593            $clone->setTitle($title);
594        }
595        if ($author) {
596            $clone->setAuthor($author);
597        }
598        if ($owner) {
599            $clone->setOwner($owner);
600        }
601        if ($for_survey) {
602            $clone->saveToDb($original_id);
603        } else {
604            $clone->saveToDb();
605        }
606        // duplicate the materials
607        $clone->duplicateMaterials($original_id);
608        // copy XHTML media objects
609        $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
610        return $clone->getId();
611    }
612
613    /**
614    * Copies an assOrderingQuestion object
615    *
616    * @access public
617    */
618    public function copyObject($target_questionpool, $title = "")
619    {
620        if ($this->getId() <= 0) {
621            // The question has not been saved. It cannot be copied
622            return;
623        }
624        $clone = $this;
625        $original_id = self::_getOriginalId($this->getId(), false);
626        $clone->setId(-1);
627        $source_questionpool = $this->getObjId();
628        $clone->setObjId($target_questionpool);
629        if ($title) {
630            $clone->setTitle($title);
631        }
632
633        $clone->saveToDb();
634
635        // duplicate the materials
636        $clone->duplicateMaterials($original_id);
637        // copy XHTML media objects
638        $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
639        return $clone->getId();
640    }
641
642    /**
643    * Increases the media object usage counter when a question is duplicated
644    *
645    * @param integer $a_q_id The question id of the original question
646    * @access public
647    */
648    public function copyXHTMLMediaObjectsOfQuestion($a_q_id)
649    {
650        include_once("./Services/MediaObjects/classes/class.ilObjMediaObject.php");
651        $mobs = ilObjMediaObject::_getMobsOfObject("spl:html", $a_q_id);
652        foreach ($mobs as $mob) {
653            ilObjMediaObject::_saveUsage($mob, "spl:html", $this->getId());
654        }
655    }
656
657    /**
658    * Loads a SurveyQuestion object from the database
659    *
660    * @param integer $question_id A unique key which defines the question in the database
661    * @access public
662    */
663    public function loadFromDb($question_id)
664    {
665        $ilDB = $this->db;
666
667        $result = $ilDB->queryF(
668            "SELECT * FROM svy_material WHERE question_fi = %s",
669            array('integer'),
670            array($this->getId())
671        );
672        $this->material = array();
673        if ($result->numRows()) {
674            include_once "./Modules/SurveyQuestionPool/classes/class.ilSurveyMaterial.php";
675            while ($row = $ilDB->fetchAssoc($result)) {
676                $mat = new ilSurveyMaterial();
677                $mat->type = $row['material_type'];
678                $mat->internal_link = $row['internal_link'];
679                $mat->title = $row['material_title'];
680                $mat->import_id = $row['import_id'];
681                $mat->text_material = $row['text_material'];
682                $mat->external_link = $row['external_link'];
683                $mat->file_material = $row['file_material'];
684                array_push($this->material, $mat);
685            }
686        }
687    }
688
689    /**
690    * Checks whether the question is complete or not
691    *
692    * @return boolean TRUE if the question is complete, FALSE otherwise
693    * @access public
694    */
695    public static function _isComplete($question_id)
696    {
697        global $DIC;
698
699        $ilDB = $DIC->database();
700
701        $result = $ilDB->queryF(
702            "SELECT complete FROM svy_question WHERE question_id = %s",
703            array('integer'),
704            array($question_id)
705        );
706        if ($result->numRows()) {
707            $row = $ilDB->fetchAssoc($result);
708            if ($row["complete"] == 1) {
709                return true;
710            }
711        }
712        return false;
713    }
714
715    /**
716    * Saves the complete flag to the database
717    *
718    * @access public
719    */
720    public function saveCompletionStatus($original_id = "")
721    {
722        $ilDB = $this->db;
723
724        $question_id = $this->getId();
725        if (strlen($original_id)) {
726            $question_id = $original_id;
727        }
728
729        if ($this->getId() > 0) {
730            $this->log->debug("UPDATE svy_question question_id=" . $question_id);
731
732            // update existing dataset
733            $affectedRows = $ilDB->manipulateF(
734                "UPDATE svy_question SET complete = %s, tstamp = %s WHERE question_id = %s",
735                array('text', 'integer', 'integer'),
736                array($this->isComplete(), time(), $question_id)
737            );
738        }
739    }
740
741    /**
742    * Saves a SurveyQuestion object to a database
743    *
744    * @param integer $original_id
745    * @access public
746    */
747    public function saveToDb($original_id = "")
748    {
749        $ilDB = $this->db;
750
751        // cleanup RTE images which are not inserted into the question text
752        include_once("./Services/RTE/classes/class.ilRTE.php");
753        ilRTE::_cleanupMediaObjectUsage($this->getQuestiontext(), "spl:html", $this->getId());
754        $affectedRows = 0;
755        if ($this->getId() == -1) {
756            // Write new dataset
757            $next_id = $ilDB->nextId('svy_question');
758            $affectedRows = $ilDB->insert("svy_question", array(
759                "question_id" => array("integer", $next_id),
760                "questiontype_fi" => array("integer", $this->getQuestionTypeID()),
761                "obj_fi" => array("integer", $this->getObjId()),
762                "owner_fi" => array("integer", $this->getOwner()),
763                "title" => array("text", $this->getTitle()),
764                "label" => array("text", (strlen($this->label)) ? $this->label : null),
765                "description" => array("text", $this->getDescription()),
766                "author" => array("text", $this->getAuthor()),
767                "questiontext" => array("clob", ilRTE::_replaceMediaObjectImageSrc($this->getQuestiontext(), 0)),
768                "obligatory" => array("text", $this->getObligatory()),
769                "complete" => array("text", $this->isComplete()),
770                "created" => array("integer", time()),
771                "original_id" => array("integer", ($original_id) ? $original_id : null),
772                "tstamp" => array("integer", time())
773            ));
774
775            //$this->log->debug("INSERT: svy_question id=".$next_id." questiontype_fi=".$this->getQuestionTypeID()." obj_fi".$this->getObjId()." title=".$this->getTitle()." ...");
776
777            $this->setId($next_id);
778        } else {
779            // update existing dataset
780            $affectedRows = $ilDB->update("svy_question", array(
781                "title" => array("text", $this->getTitle()),
782                "label" => array("text", (strlen($this->label)) ? $this->label : null),
783                "description" => array("text", $this->getDescription()),
784                "author" => array("text", $this->getAuthor()),
785                "questiontext" => array("clob", ilRTE::_replaceMediaObjectImageSrc($this->getQuestiontext(), 0)),
786                "obligatory" => array("text", $this->getObligatory()),
787                "complete" => array("text", $this->isComplete()),
788                "tstamp" => array("integer", time())
789            ), array(
790            "question_id" => array("integer", $this->getId())
791            ));
792
793            $this->log->debug("UPDATE svy_question id=" . $this->getId() . " SET: title=" . $this->getTitle() . " ...");
794        }
795
796        return $affectedRows;
797    }
798
799    /**
800    * save material to db
801    */
802    public function saveMaterial()
803    {
804        $ilDB = $this->db;
805
806        include_once "./Services/Link/classes/class.ilInternalLink.php";
807
808        $this->log->debug("DELETE: svy_material question_fi=" . $this->getId());
809
810        $affectedRows = $ilDB->manipulateF(
811            "DELETE FROM svy_material WHERE question_fi = %s",
812            array('integer'),
813            array($this->getId())
814        );
815        ilInternalLink::_deleteAllLinksOfSource("sqst", $this->getId());
816
817        foreach ($this->material as $material) {
818            $next_id = $ilDB->nextId('svy_material');
819
820            $this->log->debug("INSERT: svy_material question_fi=" . $this->getId());
821
822            $affectedRows = $ilDB->manipulateF(
823                "INSERT INTO svy_material " .
824                "(material_id, question_fi, internal_link, import_id, material_title, tstamp," .
825                "text_material, external_link, file_material, material_type) " .
826                "VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)",
827                array('integer','integer','text','text','text','integer','text','text','text','integer'),
828                array(
829                    $next_id, $this->getId(), $material->internal_link, $material->import_id,
830                    $material->title, time(), $material->text_material, $material->external_link,
831                    $material->file_material, $material->type)
832            );
833            if (preg_match("/il_(\d*?)_(\w+)_(\d+)/", $material->internal_link, $matches)) {
834                ilInternalLink::_saveLink("sqst", $this->getId(), $matches[2], $matches[3], $matches[1]);
835            }
836        }
837    }
838
839    /**
840    * Creates a new question with a 0 timestamp when a new question is created
841    * This assures that an ID is given to the question if a file upload or something else occurs
842    *
843    * @return integer ID of the new question
844    */
845    public function createNewQuestion()
846    {
847        $ilDB = $this->db;
848
849        $obj_id = $this->getObjId();
850        if ($obj_id > 0) {
851            $next_id = $ilDB->nextId('svy_question');
852            $affectedRows = $ilDB->manipulateF(
853                "INSERT INTO svy_question (question_id, questiontype_fi, " .
854                "obj_fi, owner_fi, title, description, author, questiontext, obligatory, complete, " .
855                "created, original_id, tstamp) VALUES " .
856                "(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)",
857                array('integer', 'integer', 'integer', 'integer', 'text', 'text', 'text', 'text',
858                    'text', 'text', 'integer', 'integer', 'integer'),
859                array(
860                    $next_id,
861                    $this->getQuestionTypeID(),
862                    $obj_id,
863                    $this->getOwner(),
864                    null,
865                    null,
866                    $this->getAuthor(),
867                    null,
868                    "1",
869                    "0",
870                    time(),
871                    null,
872                    0
873                )
874            );
875            $this->log->debug("INSERT INTO svy_question question_id= " . $next_id . " questiontype_fi= " . $this->getQuestionTypeID());
876
877            $this->setId($next_id);
878        }
879        return $this->getId();
880    }
881
882    /**
883    * Saves the learners input of the question to the database
884    *
885    * @access public
886    * @see $answers
887    */
888    public function saveWorkingData($limit_to = LIMIT_NO_LIMIT)
889    {
890    }
891
892    /**
893    * Returns the image path for web accessable images of a question.
894    * The image path is under the CLIENT_WEB_DIR in assessment/REFERENCE_ID_OF_QUESTION_POOL/ID_OF_QUESTION/images
895    *
896    * @access public
897    */
898    public function getImagePath()
899    {
900        return CLIENT_WEB_DIR . "/survey/$this->obj_id/$this->id/images/";
901    }
902
903    /**
904    * Returns the materials path for web accessable materials of a question.
905    * The materials path is under the CLIENT_WEB_DIR in assessment/REFERENCE_ID_OF_QUESTION_POOL/ID_OF_QUESTION/materials
906    *
907    * @access public
908    */
909    public function getMaterialsPath()
910    {
911        return CLIENT_WEB_DIR . "/survey/$this->obj_id/$this->id/materials/";
912    }
913
914    /**
915    * Returns the web image path for web accessable images of a question.
916    * The image path is under the web accessable data dir in assessment/REFERENCE_ID_OF_QUESTION_POOL/ID_OF_QUESTION/images
917    *
918    * @access public
919    */
920    public function getImagePathWeb()
921    {
922        $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/survey/$this->obj_id/$this->id/images/";
923        return str_replace(ilUtil::removeTrailingPathSeparators(ILIAS_ABSOLUTE_PATH), ilUtil::removeTrailingPathSeparators(ILIAS_HTTP_PATH), $webdir);
924    }
925
926    /**
927    * Returns the web image path for web accessable images of a question.
928    * The image path is under the web accessable data dir in assessment/REFERENCE_ID_OF_QUESTION_POOL/ID_OF_QUESTION/images
929    *
930    * @access public
931    */
932    public function getMaterialsPathWeb()
933    {
934        $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/survey/$this->obj_id/$this->id/materials/";
935        return str_replace(ilUtil::removeTrailingPathSeparators(ILIAS_ABSOLUTE_PATH), ilUtil::removeTrailingPathSeparators(ILIAS_HTTP_PATH), $webdir);
936    }
937
938    /**
939    * Saves a category to the database
940    *
941    * @param string $categorytext The description of the category
942    * @result integer The database id of the category
943    * @access public
944    * @see $categories
945    */
946    public function saveCategoryToDb($categorytext, $neutral = 0)
947    {
948        $ilUser = $this->user;
949        $ilDB = $this->db;
950
951        $result = $ilDB->queryF(
952            "SELECT title, category_id FROM svy_category WHERE title = %s AND neutral = %s AND owner_fi = %s",
953            array('text','text','integer'),
954            array($categorytext, $neutral, $ilUser->getId())
955        );
956        $insert = false;
957        $returnvalue = "";
958        if ($result->numRows()) {
959            $insert = true;
960            while ($row = $ilDB->fetchAssoc($result)) {
961                if (strcmp($row["title"], $categorytext) == 0) {
962                    $returnvalue = $row["category_id"];
963                    $insert = false;
964                }
965            }
966        } else {
967            $insert = true;
968        }
969        if ($insert) {
970            $next_id = $ilDB->nextId('svy_category');
971            $affectedRows = $ilDB->manipulateF(
972                "INSERT INTO svy_category (category_id, title, neutral, owner_fi, tstamp) VALUES (%s, %s, %s, %s, %s)",
973                array('integer','text','text','integer','integer'),
974                array($next_id, $categorytext, $neutral, $ilUser->getId(), time())
975            );
976
977            $this->log->debug("INSERT INTO svy_category id=" . $next_id);
978
979            $returnvalue = $next_id;
980        }
981        return $returnvalue;
982    }
983
984    /**
985    * Deletes datasets from the additional question table in the database
986    *
987    * @param integer $question_id The question id which should be deleted in the additional question table
988    * @access public
989    */
990    public function deleteAdditionalTableData($question_id)
991    {
992        $ilDB = $this->db;
993
994        $this->log->debug("DELETE FROM " . $this->getAdditionalTableName());
995
996        $affectedRows = $ilDB->manipulateF(
997            "DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
998            array('integer'),
999            array($question_id)
1000        );
1001    }
1002
1003    /**
1004    * Deletes a question and all materials from the database
1005    *
1006    * @param integer $question_id The database id of the question
1007    * @access private
1008    */
1009    public function delete($question_id)
1010    {
1011        $ilDB = $this->db;
1012
1013        if ($question_id < 1) {
1014            return;
1015        }
1016
1017        $result = $ilDB->queryF(
1018            "SELECT obj_fi FROM svy_question WHERE question_id = %s",
1019            array('integer'),
1020            array($question_id)
1021        );
1022        if ($result->numRows() == 1) {
1023            $row = $ilDB->fetchAssoc($result);
1024            $obj_id = $row["obj_fi"];
1025        } else {
1026            return;
1027        }
1028
1029        $affectedRows = $ilDB->manipulateF(
1030            "DELETE FROM svy_answer WHERE question_fi = %s",
1031            array('integer'),
1032            array($question_id)
1033        );
1034
1035        $affectedRows = $ilDB->manipulateF(
1036            "DELETE FROM svy_constraint WHERE question_fi = %s",
1037            array('integer'),
1038            array($question_id)
1039        );
1040
1041        $result = $ilDB->queryF(
1042            "SELECT constraint_fi FROM svy_qst_constraint WHERE question_fi = %s",
1043            array('integer'),
1044            array($question_id)
1045        );
1046        while ($row = $ilDB->fetchObject($result)) {
1047            $affectedRows = $ilDB->manipulateF(
1048                "DELETE FROM svy_constraint WHERE constraint_id = %s",
1049                array('integer'),
1050                array($row->constraint_fi)
1051            );
1052        }
1053
1054        $affectedRows = $ilDB->manipulateF(
1055            "DELETE FROM svy_qst_constraint WHERE question_fi = %s",
1056            array('integer'),
1057            array($question_id)
1058        );
1059        $affectedRows = $ilDB->manipulateF(
1060            "DELETE FROM svy_qblk_qst WHERE question_fi = %s",
1061            array('integer'),
1062            array($question_id)
1063        );
1064        $affectedRows = $ilDB->manipulateF(
1065            "DELETE FROM svy_svy_qst WHERE question_fi = %s",
1066            array('integer'),
1067            array($question_id)
1068        );
1069        $affectedRows = $ilDB->manipulateF(
1070            "DELETE FROM svy_variable WHERE question_fi = %s",
1071            array('integer'),
1072            array($question_id)
1073        );
1074        $affectedRows = $ilDB->manipulateF(
1075            "DELETE FROM svy_question WHERE question_id = %s",
1076            array('integer'),
1077            array($question_id)
1078        );
1079
1080        $this->deleteAdditionalTableData($question_id);
1081
1082        $affectedRows = $ilDB->manipulateF(
1083            "DELETE FROM svy_material WHERE question_fi = %s",
1084            array('integer'),
1085            array($question_id)
1086        );
1087
1088        $this->log->debug("SET OF DELETES svy_answer, svy_constraint, svy_qst_constraint, svy_qblk_qst, svy_qst_oblig, svy_svy_qst, svy_variable, svy_question, svy_material WHERE question_fi = " . $question_id);
1089
1090        include_once "./Services/Link/classes/class.ilInternalLink.php";
1091        ilInternalLink::_deleteAllLinksOfSource("sqst", $question_id);
1092
1093        $directory = CLIENT_WEB_DIR . "/survey/" . $obj_id . "/$question_id";
1094        if (preg_match("/\d+/", $obj_id) and preg_match("/\d+/", $question_id) and is_dir($directory)) {
1095            ilUtil::delDir($directory);
1096        }
1097
1098        include_once("./Services/MediaObjects/classes/class.ilObjMediaObject.php");
1099        $mobs = ilObjMediaObject::_getMobsOfObject("spl:html", $question_id);
1100        // remaining usages are not in text anymore -> delete them
1101        // and media objects (note: delete method of ilObjMediaObject
1102        // checks whether object is used in another context; if yes,
1103        // the object is not deleted!)
1104        foreach ($mobs as $mob) {
1105            ilObjMediaObject::_removeUsage($mob, "spl:html", $question_id);
1106            $mob_obj = new ilObjMediaObject($mob);
1107            $mob_obj->delete();
1108        }
1109
1110        include_once("./Modules/Survey/classes/class.ilSurveySkill.php");
1111        ilSurveySkill::handleQuestionDeletion($question_id, $obj_id);
1112
1113        $this->log->debug("UPDATE svy_question");
1114
1115        // #12772 - untie question copies from pool question
1116        $ilDB->manipulate("UPDATE svy_question" .
1117            " SET original_id = NULL" .
1118            " WHERE original_id  = " . $ilDB->quote($question_id, "integer"));
1119    }
1120
1121    /**
1122    * Returns the question type of a question with a given id
1123    *
1124    * @param integer $question_id The database id of the question
1125    * @result string The question type string
1126    * @access private
1127    */
1128    public static function _getQuestionType($question_id)
1129    {
1130        global $DIC;
1131
1132        $ilDB = $DIC->database();
1133
1134        if ($question_id < 1) {
1135            return "";
1136        }
1137
1138        $result = $ilDB->queryF(
1139            "SELECT type_tag FROM svy_question, svy_qtype WHERE svy_question.question_id = %s AND svy_question.questiontype_fi = svy_qtype.questiontype_id",
1140            array('integer'),
1141            array($question_id)
1142        );
1143        if ($result->numRows() == 1) {
1144            $data = $ilDB->fetchAssoc($result);
1145            return $data["type_tag"];
1146        } else {
1147            return "";
1148        }
1149    }
1150
1151    /**
1152    * Returns the question title of a question with a given id
1153    *
1154    * @param integer $question_id The database id of the question
1155    * @result string The question title
1156    * @access private
1157    */
1158    public static function _getTitle($question_id)
1159    {
1160        global $DIC;
1161
1162        $ilDB = $DIC->database();
1163
1164        if ($question_id < 1) {
1165            return "";
1166        }
1167
1168        $result = $ilDB->queryF(
1169            "SELECT title FROM svy_question WHERE svy_question.question_id = %s",
1170            array('integer'),
1171            array($question_id)
1172        );
1173        if ($result->numRows() == 1) {
1174            $data = $ilDB->fetchAssoc($result);
1175            return $data["title"];
1176        } else {
1177            return "";
1178        }
1179    }
1180
1181    /**
1182    * Returns the original id of a question
1183    *
1184    * @param integer $question_id The database id of the question
1185    * @return integer The database id of the original question
1186    * @access public
1187    */
1188    public static function _getOriginalId($question_id, $a_return_question_id_if_no_original = true)
1189    {
1190        global $DIC;
1191
1192        $ilDB = $DIC->database();
1193        $result = $ilDB->queryF(
1194            "SELECT * FROM svy_question WHERE question_id = %s",
1195            array('integer'),
1196            array($question_id)
1197        );
1198        if ($result->numRows() > 0) {
1199            $row = $ilDB->fetchAssoc($result);
1200            if ($row["original_id"] > 0) {
1201                return $row["original_id"];
1202            } elseif ((bool) $a_return_question_id_if_no_original) { // #12419
1203                return $row["question_id"];
1204            }
1205        } else {
1206            return "";
1207        }
1208    }
1209
1210    public function syncWithOriginal()
1211    {
1212        $ilDB = $this->db;
1213
1214        if ($this->getOriginalId()) {
1215            $id = $this->getId();
1216            $original = $this->getOriginalId();
1217
1218            $this->setId($this->getOriginalId());
1219            $this->setOriginalId(null);
1220            $this->saveToDb();
1221
1222            $this->setId($id);
1223            $this->setOriginalId($original);
1224
1225            $this->log->debug("DELETE FROM svy_material WHERE question_fi = " . $this->getOriginalId());
1226
1227            include_once "./Services/Link/classes/class.ilInternalLink.php";
1228            $affectedRows = $ilDB->manipulateF(
1229                "DELETE FROM svy_material WHERE question_fi = %s",
1230                array('integer'),
1231                array($this->getOriginalId())
1232            );
1233            ilInternalLink::_deleteAllLinksOfSource("sqst", $this->original_id);
1234            if (strlen($this->material["internal_link"])) {
1235                $next_id = $ilDB->nextId('svy_material');
1236                $affectedRows = $ilDB->manipulateF(
1237                    "INSERT INTO svy_material (material_id, question_fi, internal_link, import_id, material_title, tstamp) VALUES (%s, %s, %s, %s, %s, %s)",
1238                    array('integer', 'integer', 'text', 'text', 'text', 'integer'),
1239                    array($next_id, $this->getOriginalId(), $this->material["internal_link"], $this->material["import_id"], $this->material["title"], time())
1240                );
1241
1242                $this->log->debug("INSERT svy_material material_id=" . $next_id . " question_fi=" . $this->getOriginalId());
1243
1244                if (preg_match("/il_(\d*?)_(\w+)_(\d+)/", $this->material["internal_link"], $matches)) {
1245                    ilInternalLink::_saveLink("sqst", $this->getOriginalId(), $matches[2], $matches[3], $matches[1]);
1246                }
1247            }
1248        }
1249    }
1250
1251    /**
1252    * Returns a phrase for a given database id
1253    *
1254    * @result String The title of the phrase
1255    * @access public
1256    */
1257    public function getPhrase($phrase_id)
1258    {
1259        $ilDB = $this->db;
1260
1261        $result = $ilDB->queryF(
1262            "SELECT title FROM svy_phrase WHERE phrase_id = %s",
1263            array('integer'),
1264            array($phrase_id)
1265        );
1266        if ($row = $ilDB->fetchAssoc($result)) {
1267            return $row["title"];
1268        }
1269        return "";
1270    }
1271
1272    /**
1273    * Returns true if the phrase title already exists for the current user
1274    *
1275    * @param string $title The title of the phrase
1276    * @result boolean True, if the title exists, otherwise False
1277    * @access public
1278    */
1279    public function phraseExists($title)
1280    {
1281        $ilUser = $this->user;
1282        $ilDB = $this->db;
1283
1284        $result = $ilDB->queryF(
1285            "SELECT phrase_id FROM svy_phrase WHERE title = %s AND owner_fi = %s",
1286            array('text', 'integer'),
1287            array($title, $ilUser->getId())
1288        );
1289        return ($result->numRows() == 0) ? false : true;
1290    }
1291
1292    /**
1293    * Returns true if the question already exists in the database
1294    *
1295    * @param integer $question_id The database id of the question
1296    * @result boolean True, if the question exists, otherwise False
1297    * @access public
1298    */
1299    public static function _questionExists($question_id)
1300    {
1301        global $DIC;
1302
1303        $ilDB = $DIC->database();
1304
1305        if ($question_id < 1) {
1306            return false;
1307        }
1308
1309        $result = $ilDB->queryF(
1310            "SELECT question_id FROM svy_question WHERE question_id = %s",
1311            array('integer'),
1312            array($question_id)
1313        );
1314        return ($result->numRows() == 1) ? true : false;
1315    }
1316
1317    public function addInternalLink($material_id, $title = "")
1318    {
1319        if (strlen($material_id)) {
1320            if (strcmp($material_title, "") == 0) {
1321                if (preg_match("/il__(\w+)_(\d+)/", $material_id, $matches)) {
1322                    $type = $matches[1];
1323                    $target_id = $matches[2];
1324                    $material_title = $this->lng->txt("obj_$type") . ": ";
1325                    switch ($type) {
1326                        case "lm":
1327                            include_once("./Modules/LearningModule/classes/class.ilObjContentObjectGUI.php");
1328                            $cont_obj_gui = new ilObjContentObjectGUI("", $target_id, true);
1329                            $cont_obj = $cont_obj_gui->object;
1330                            $material_title .= $cont_obj->getTitle();
1331                            break;
1332                        case "pg":
1333                            include_once("./Modules/LearningModule/classes/class.ilLMPageObject.php");
1334                            include_once("./Modules/LearningModule/classes/class.ilLMObject.php");
1335                            $lm_id = ilLMObject::_lookupContObjID($target_id);
1336                            include_once("./Modules/LearningModule/classes/class.ilObjContentObjectGUI.php");
1337                            $cont_obj_gui = new ilObjContentObjectGUI("", $lm_id, false);
1338                            $cont_obj = $cont_obj_gui->object;
1339                            $pg_obj = new ilLMPageObject($cont_obj, $target_id);
1340                            $material_title .= $pg_obj->getTitle();
1341                            break;
1342                        case "st":
1343                            include_once("./Modules/LearningModule/classes/class.ilStructureObject.php");
1344                            include_once("./Modules/LearningModule/classes/class.ilLMObject.php");
1345                            $lm_id = ilLMObject::_lookupContObjID($target_id);
1346                            include_once("./Modules/LearningModule/classes/class.ilObjContentObjectGUI.php");
1347                            $cont_obj_gui = new ilObjContentObjectGUI("", $lm_id, false);
1348                            $cont_obj = $cont_obj_gui->object;
1349                            $st_obj = new ilStructureObject($cont_obj, $target_id);
1350                            $material_title .= $st_obj->getTitle();
1351                            break;
1352                        case "git":
1353                            include_once "./Modules/Glossary/classes/class.ilGlossaryTerm.php";
1354                            $material_title = $this->lng->txt("glossary_term") . ": " . ilGlossaryTerm::_lookGlossaryTerm($target_id);
1355                            break;
1356                        case "mob":
1357                            break;
1358                    }
1359                }
1360            }
1361            include_once "./Modules/SurveyQuestionPool/classes/class.ilSurveyMaterial.php";
1362            $mat = new ilSurveyMaterial();
1363            $mat->type = 0;
1364            $mat->internal_link = $material_id;
1365            $mat->title = $material_title;
1366            $this->addMaterial($mat);
1367            $this->saveMaterial();
1368        }
1369    }
1370
1371    /**
1372    * Deletes materials
1373    *
1374    * @param array $a_array Array with indexes of the materials to delete
1375    */
1376    public function deleteMaterials($a_array)
1377    {
1378        foreach ($a_array as $idx) {
1379            unset($this->material[$idx]);
1380        }
1381        $this->material = array_values($this->material);
1382        $this->saveMaterial();
1383    }
1384
1385    /**
1386    * Duplicates the materials of a question
1387    *
1388    * @param integer $question_id The database id of the original survey question
1389    * @access public
1390    */
1391    public function duplicateMaterials($question_id)
1392    {
1393        foreach ($this->materials as $filename) {
1394            $materialspath = $this->getMaterialsPath();
1395            $materialspath_original = preg_replace("/([^\d])$this->id([^\d])/", "\${1}$question_id\${2}", $materialspath);
1396            if (!file_exists($materialspath)) {
1397                ilUtil::makeDirParents($materialspath);
1398            }
1399            if (!copy($materialspath_original . $filename, $materialspath . $filename)) {
1400                print "material could not be duplicated!!!! ";
1401            }
1402        }
1403    }
1404
1405    public function addMaterial($obj_material)
1406    {
1407        array_push($this->material, $obj_material);
1408    }
1409
1410    /**
1411    * Sets a material link for the question
1412    *
1413    * @param string $material_id An internal link pointing to the material
1414    * @param boolean $is_import A boolean indication that the internal link was imported from another ILIAS installation
1415    * @access public
1416    */
1417    public function setMaterial($material_id = "", $is_import = false, $material_title = "")
1418    {
1419        if (strcmp($material_id, "") != 0) {
1420            $import_id = "";
1421            if ($is_import) {
1422                $import_id = $material_id;
1423                $material_id = self::_resolveInternalLink($import_id);
1424            }
1425            if (strcmp($material_title, "") == 0) {
1426                if (preg_match("/il__(\w+)_(\d+)/", $material_id, $matches)) {
1427                    $type = $matches[1];
1428                    $target_id = $matches[2];
1429                    $material_title = $this->lng->txt("obj_$type") . ": ";
1430                    switch ($type) {
1431                        case "lm":
1432                            include_once("./Modules/LearningModule/classes/class.ilObjContentObjectGUI.php");
1433                            $cont_obj_gui = new ilObjContentObjectGUI("", $target_id, true);
1434                            $cont_obj = $cont_obj_gui->object;
1435                            $material_title .= $cont_obj->getTitle();
1436                            break;
1437                        case "pg":
1438                            include_once("./Modules/LearningModule/classes/class.ilLMPageObject.php");
1439                            include_once("./Modules/LearningModule/classes/class.ilLMObject.php");
1440                            $lm_id = ilLMObject::_lookupContObjID($target_id);
1441                            include_once("./Modules/LearningModule/classes/class.ilObjContentObjectGUI.php");
1442                            $cont_obj_gui = new ilObjContentObjectGUI("", $lm_id, false);
1443                            $cont_obj = $cont_obj_gui->object;
1444                            $pg_obj = new ilLMPageObject($cont_obj, $target_id);
1445                            $material_title .= $pg_obj->getTitle();
1446                            break;
1447                        case "st":
1448                            include_once("./Modules/LearningModule/classes/class.ilStructureObject.php");
1449                            include_once("./Modules/LearningModule/classes/class.ilLMObject.php");
1450                            $lm_id = ilLMObject::_lookupContObjID($target_id);
1451                            include_once("./Modules/LearningModule/classes/class.ilObjContentObjectGUI.php");
1452                            $cont_obj_gui = new ilObjContentObjectGUI("", $lm_id, false);
1453                            $cont_obj = $cont_obj_gui->object;
1454                            $st_obj = new ilStructureObject($cont_obj, $target_id);
1455                            $material_title .= $st_obj->getTitle();
1456                            break;
1457                        case "git":
1458                            include_once "./Modules/Glossary/classes/class.ilGlossaryTerm.php";
1459                            $material_title = $this->lng->txt("glossary_term") . ": " . ilGlossaryTerm::_lookGlossaryTerm($target_id);
1460                            break;
1461                        case "mob":
1462                            break;
1463                    }
1464                }
1465            }
1466            $this->material = array(
1467                "internal_link" => $material_id,
1468                "import_id" => $import_id,
1469                "title" => $material_title
1470            );
1471        }
1472        $this->saveMaterial();
1473    }
1474
1475    public static function _resolveInternalLink($internal_link)
1476    {
1477        if (preg_match("/il_(\d+)_(\w+)_(\d+)/", $internal_link, $matches)) {
1478            include_once "./Services/Link/classes/class.ilInternalLink.php";
1479            include_once "./Modules/LearningModule/classes/class.ilLMObject.php";
1480            include_once "./Modules/Glossary/classes/class.ilGlossaryTerm.php";
1481            switch ($matches[2]) {
1482                case "lm":
1483                    $resolved_link = ilLMObject::_getIdForImportId($internal_link);
1484                    break;
1485                case "pg":
1486                    $resolved_link = ilInternalLink::_getIdForImportId("PageObject", $internal_link);
1487                    break;
1488                case "st":
1489                    $resolved_link = ilInternalLink::_getIdForImportId("StructureObject", $internal_link);
1490                    break;
1491                case "git":
1492                    $resolved_link = ilInternalLink::_getIdForImportId("GlossaryItem", $internal_link);
1493                    break;
1494                case "mob":
1495                    $resolved_link = ilInternalLink::_getIdForImportId("MediaObject", $internal_link);
1496                    break;
1497            }
1498            if (strcmp($resolved_link, "") == 0) {
1499                $resolved_link = $internal_link;
1500            }
1501        } else {
1502            $resolved_link = $internal_link;
1503        }
1504        return $resolved_link;
1505    }
1506
1507    public static function _resolveIntLinks($question_id)
1508    {
1509        global $DIC;
1510
1511        $ilDB = $DIC->database();
1512        $resolvedlinks = 0;
1513        $result = $ilDB->queryF(
1514            "SELECT * FROM svy_material WHERE question_fi = %s",
1515            array('integer'),
1516            array($question_id)
1517        );
1518        if ($result->numRows()) {
1519            while ($row = $ilDB->fetchAssoc($result)) {
1520                $internal_link = $row["internal_link"];
1521                include_once "./Modules/SurveyQuestionPool/classes/class.SurveyQuestion.php";
1522                $resolved_link = self::_resolveInternalLink($internal_link);
1523                if (strcmp($internal_link, $resolved_link) != 0) {
1524                    // internal link was resolved successfully
1525                    $affectedRows = $ilDB->manipulateF(
1526                        "UPDATE svy_material SET internal_link = %s, tstamp = %s WHERE material_id = %s",
1527                        array('text', 'integer', 'integer'),
1528                        array($resolved_link, time(), $row["material_id"])
1529                    );
1530                    $resolvedlinks++;
1531                }
1532            }
1533        }
1534        if ($resolvedlinks) {
1535            // there are resolved links -> reenter theses links to the database
1536
1537            // delete all internal links from the database
1538            include_once "./Services/Link/classes/class.ilInternalLink.php";
1539            ilInternalLink::_deleteAllLinksOfSource("sqst", $question_id);
1540
1541            $result = $ilDB->queryF(
1542                "SELECT * FROM svy_material WHERE question_fi = %s",
1543                array('integer'),
1544                array($question_id)
1545            );
1546            if ($result->numRows()) {
1547                while ($row = $ilDB->fetchAssoc($result)) {
1548                    if (preg_match("/il_(\d*?)_(\w+)_(\d+)/", $row["internal_link"], $matches)) {
1549                        ilInternalLink::_saveLink("sqst", $question_id, $matches[2], $matches[3], $matches[1]);
1550                    }
1551                }
1552            }
1553        }
1554    }
1555
1556    public static function _getInternalLinkHref($target = "", $a_parent_ref_id = null)
1557    {
1558        global $DIC;
1559
1560        $ilDB = $DIC->database();
1561        $linktypes = array(
1562            "lm" => "LearningModule",
1563            "pg" => "PageObject",
1564            "st" => "StructureObject",
1565            "git" => "GlossaryItem",
1566            "mob" => "MediaObject"
1567        );
1568        $href = "";
1569        if (preg_match("/il__(\w+)_(\d+)/", $target, $matches)) {
1570            $type = $matches[1];
1571            $target_id = $matches[2];
1572            switch ($linktypes[$matches[1]]) {
1573                case "LearningModule":
1574                    $href = ilUtil::removeTrailingPathSeparators(ILIAS_HTTP_PATH) . "/goto.php?target=" . $type . "_" . $target_id;
1575                    break;
1576                case "PageObject":
1577                case "StructureObject":
1578                    $href = ilUtil::removeTrailingPathSeparators(ILIAS_HTTP_PATH) . "/goto.php?target=" . $type . "_" . $target_id;
1579                    break;
1580                case "GlossaryItem":
1581                    $href = ilUtil::removeTrailingPathSeparators(ILIAS_HTTP_PATH) . "/goto.php?target=" . $type . "_" . $target_id;
1582                    break;
1583                case "MediaObject":
1584                    $href = ilUtil::removeTrailingPathSeparators(ILIAS_HTTP_PATH) . "/ilias.php?baseClass=ilLMPresentationGUI&obj_type=" . $linktypes[$type] . "&cmd=media&ref_id=" . $a_parent_ref_id . "&mob_id=" . $target_id;
1585                    break;
1586            }
1587        }
1588        return $href;
1589    }
1590
1591    /**
1592    * Returns true if the question is writeable by a certain user
1593    *
1594    * @param integer $question_id The database id of the question
1595    * @param integer $user_id The database id of the user
1596    * @result boolean True, if the question exists, otherwise False
1597    * @access public
1598    */
1599    public static function _isWriteable($question_id, $user_id)
1600    {
1601        global $DIC;
1602
1603        $ilDB = $DIC->database();
1604
1605        if (($question_id < 1) || ($user_id < 1)) {
1606            return false;
1607        }
1608
1609        $result = $ilDB->queryF(
1610            "SELECT obj_fi FROM svy_question WHERE question_id = %s",
1611            array('integer'),
1612            array($question_id)
1613        );
1614        if ($result->numRows() == 1) {
1615            $row = $ilDB->fetchAssoc($result);
1616            $qpl_object_id = $row["obj_fi"];
1617            include_once "./Modules/SurveyQuestionPool/classes/class.ilObjSurveyQuestionPool.php";
1618            return ilObjSurveyQuestionPool::_isWriteable($qpl_object_id, $user_id);
1619        } else {
1620            return false;
1621        }
1622    }
1623
1624    /**
1625    * Returns the question type ID of the question
1626    *
1627    * @return integer The question type of the question
1628    * @access public
1629    */
1630    public function getQuestionTypeID()
1631    {
1632        $ilDB = $this->db;
1633        $result = $ilDB->queryF(
1634            "SELECT questiontype_id FROM svy_qtype WHERE type_tag = %s",
1635            array('text'),
1636            array($this->getQuestionType())
1637        );
1638        if ($result->numRows() == 1) {
1639            $row = $ilDB->fetchAssoc($result);
1640            return $row["questiontype_id"];
1641        } else {
1642            return 0;
1643        }
1644    }
1645
1646    /**
1647    * Returns the question type of the question
1648    *
1649    * @return integer The question type of the question
1650    * @access public
1651    */
1652    public function getQuestionType()
1653    {
1654        return "";
1655    }
1656
1657    /**
1658    * Include the php class file for a given question type
1659    *
1660    * @param string $question_type The type tag of the question type
1661    * @return integer 0 if the class should be included, 1 if the GUI class should be included
1662    * @access public
1663    */
1664    public static function _includeClass($question_type, $gui = 0)
1665    {
1666        $type = $question_type;
1667        if ($gui == 1) {
1668            $type .= "GUI";
1669        } elseif ($gui == 2) {
1670            $type .= "Evaluation";
1671        }
1672        if (file_exists("./Modules/SurveyQuestionPool/classes/class." . $type . ".php")) {
1673            include_once "./Modules/SurveyQuestionPool/classes/class." . $type . ".php";
1674            return true;
1675        } else {
1676            global $DIC;
1677
1678            $ilPluginAdmin = $DIC["ilPluginAdmin"];
1679            $pl_names = $ilPluginAdmin->getActivePluginsForSlot(IL_COMP_MODULE, "SurveyQuestionPool", "svyq");
1680            foreach ($pl_names as $pl_name) {
1681                $pl = ilPlugin::getPluginObject(IL_COMP_MODULE, "SurveyQuestionPool", "svyq", $pl_name);
1682                if (strcmp($pl->getQuestionType(), $question_type) == 0) {
1683                    $pl->includeClass("class." . $type . ".php");
1684                    return true;
1685                }
1686            }
1687        }
1688        return false;
1689    }
1690
1691    /**
1692    * Return the translation for a given question type tag
1693    *
1694    * @param string $type_tag The type tag of the question type
1695    * @access public
1696    */
1697    public static function _getQuestionTypeName($type_tag)
1698    {
1699        if (file_exists("./Modules/SurveyQuestionPool/classes/class." . $type_tag . ".php")) {
1700            global $DIC;
1701
1702            $lng = $DIC->language();
1703            return $lng->txt($type_tag);
1704        } else {
1705            global $DIC;
1706
1707            $ilPluginAdmin = $DIC["ilPluginAdmin"];
1708            $pl_names = $ilPluginAdmin->getActivePluginsForSlot(IL_COMP_MODULE, "SurveyQuestionPool", "svyq");
1709            foreach ($pl_names as $pl_name) {
1710                $pl = ilPlugin::getPluginObject(IL_COMP_MODULE, "SurveyQuestionPool", "svyq", $pl_name);
1711                if (strcmp($pl->getQuestionType(), $type_tag) == 0) {
1712                    return $pl->getQuestionTypeTranslation();
1713                }
1714            }
1715        }
1716        return "";
1717    }
1718
1719
1720    /**
1721    * Creates an instance of a question with a given question id
1722    *
1723    * @param integer $question_id The question id
1724    * @return object The question instance
1725    * @access public
1726    */
1727    public static function _instanciateQuestion($question_id)
1728    {
1729        $question_type = self::_getQuestionType($question_id);
1730        if ($question_type) {
1731            self::_includeClass($question_type);
1732            $question = new $question_type();
1733            $question->loadFromDb($question_id);
1734            return $question;
1735        }
1736    }
1737
1738    /**
1739    * Creates an instance of a question GUI with a given question id
1740    *
1741    * @param integer $question_id The question id
1742    * @return object The question GUI instance
1743    * @access public
1744    */
1745    public static function _instanciateQuestionGUI($question_id)
1746    {
1747        $question_type = self::_getQuestionType($question_id);
1748        if ($question_type) {
1749            self::_includeClass($question_type, 1);
1750            $guitype = $question_type . "GUI";
1751            $question = new $guitype($question_id);
1752            return $question;
1753        }
1754    }
1755
1756    /**
1757    * Creates an instance of a question evaluation with a given question id
1758    *
1759    * @param integer $question_id The question id
1760    * @return object The question evaluation instance
1761    * @access public
1762    */
1763    public static function _instanciateQuestionEvaluation($question_id, array $a_finished_ids = null)
1764    {
1765        $question = self::_instanciateQuestion($question_id);
1766        if ($question) {
1767            $question_type = self::_getQuestionType($question_id);
1768            self::_includeClass($question_type, 2);
1769            $class = $question_type . "Evaluation";
1770            $ev = new $class($question, $a_finished_ids);
1771            return $ev;
1772        }
1773    }
1774
1775    /**
1776    * Checks if a given string contains HTML or not
1777    *
1778    * @param string $a_text Text which should be checked
1779    *
1780    * @return boolean
1781    * @access public
1782    */
1783    public function isHTML($a_text)
1784    {
1785        if (preg_match("/<[^>]*?>/", $a_text)) {
1786            return true;
1787        } else {
1788            return false;
1789        }
1790    }
1791
1792    /**
1793    * Reads an QTI material tag an creates a text string
1794    *
1795    * @param string $a_material QTI material tag
1796    * @return string text or xhtml string
1797    * @access public
1798    */
1799    public function QTIMaterialToString($a_material)
1800    {
1801        $svy_log = ilLoggerFactory::getLogger("svy");
1802        $svy_log->debug("material count: " . $a_material->getMaterialCount());
1803
1804        $result = "";
1805        for ($i = 0; $i < $a_material->getMaterialCount(); $i++) {
1806            $material = $a_material->getMaterial($i);
1807            if (strcmp($material["type"], "mattext") == 0) {
1808                $result .= $material["material"]->getContent();
1809            }
1810            if (strcmp($material["type"], "matimage") == 0) {
1811                $matimage = $material["material"];
1812                if (preg_match("/(il_([0-9]+)_mob_([0-9]+))/", $matimage->getLabel(), $matches)) {
1813                    // import an mediaobject which was inserted using tiny mce
1814                    if (!is_array($_SESSION["import_mob_xhtml"])) {
1815                        $_SESSION["import_mob_xhtml"] = array();
1816                    }
1817                    array_push($_SESSION["import_mob_xhtml"], array("mob" => $matimage->getLabel(), "uri" => $matimage->getUri()));
1818                }
1819            }
1820        }
1821        return $result;
1822    }
1823
1824    /**
1825    * Creates an XML material tag from a plain text or xhtml text
1826    *
1827    * @param object $a_xml_writer Reference to the ILIAS XML writer
1828    * @param string $a_material plain text or html text containing the material
1829    * @return string XML material tag
1830    * @access public
1831    */
1832    public function addMaterialTag(&$a_xml_writer, $a_material, $close_material_tag = true, $add_mobs = true, $a_attrs = null)
1833    {
1834        include_once "./Services/RTE/classes/class.ilRTE.php";
1835        include_once("./Services/MediaObjects/classes/class.ilObjMediaObject.php");
1836
1837        $a_xml_writer->xmlStartTag("material");
1838        $attrs = array(
1839            "type" => "text/plain"
1840        );
1841        if ($this->isHTML($a_material)) {
1842            $attrs["type"] = "text/xhtml";
1843        }
1844        if (is_array($a_attrs)) {
1845            $attrs = array_merge($attrs, $a_attrs);
1846        }
1847        $a_xml_writer->xmlElement("mattext", $attrs, ilRTE::_replaceMediaObjectImageSrc($a_material, 0));
1848
1849        if ($add_mobs) {
1850            $mobs = ilObjMediaObject::_getMobsOfObject("spl:html", $this->getId());
1851            foreach ($mobs as $mob) {
1852                $mob_obj = new ilObjMediaObject($mob);
1853                $imgattrs = array(
1854                    "label" => "il_" . IL_INST_ID . "_mob_" . $mob,
1855                    "uri" => "objects/" . "il_" . IL_INST_ID . "_mob_" . $mob . "/" . $mob_obj->getTitle(),
1856                    "type" => "spl:html",
1857                    "id" => $this->getId()
1858                );
1859                $a_xml_writer->xmlElement("matimage", $imgattrs, null);
1860            }
1861        }
1862        if ($close_material_tag) {
1863            $a_xml_writer->xmlEndTag("material");
1864        }
1865    }
1866
1867    /**
1868    * Prepares a string for a text area output in surveys
1869    *
1870    * @param string $txt_output String which should be prepared for output
1871    * @access public
1872    */
1873    public function prepareTextareaOutput($txt_output, $prepare_for_latex_output = false)
1874    {
1875        return ilUtil::prepareTextareaOutput($txt_output, $prepare_for_latex_output);
1876    }
1877
1878    /**
1879    * Returns the question data fields from the database
1880    *
1881    * @param integer $id The question ID from the database
1882    * @return array Array containing the question fields and data from the database
1883    * @access public
1884    */
1885    public function getQuestionDataArray($id)
1886    {
1887        return array();
1888    }
1889
1890    /**
1891    * Creates the user data of the svy_answer table from the POST data
1892    *
1893    * @return array User data according to the svy_answer table
1894    * @access public
1895    */
1896    public function &getWorkingDataFromUserInput($post_data)
1897    {
1898        // overwrite in inherited classes
1899        $data = array();
1900        return $data;
1901    }
1902
1903    /**
1904    * Import additional meta data from the question import file. Usually
1905    * the meta data section is used to store question elements which are not
1906    * part of the standard XML schema.
1907    *
1908    * @return array $a_meta Array containing the additional meta data
1909    * @access public
1910    */
1911    public function importAdditionalMetadata($a_meta)
1912    {
1913        // overwrite in inherited classes
1914    }
1915
1916    /**
1917    * Import response data from the question import file
1918    *
1919    * @return array $a_data Array containing the response data
1920    * @access public
1921    */
1922    public function importResponses($a_data)
1923    {
1924        // overwrite in inherited classes
1925    }
1926
1927    /**
1928    * Import bipolar adjectives from the question import file
1929    *
1930    * @return array $a_data Array containing the adjectives
1931    * @access public
1932    */
1933    public function importAdjectives($a_data)
1934    {
1935        // overwrite in inherited classes
1936    }
1937
1938    /**
1939    * Import matrix rows from the question import file
1940    *
1941    * @return array $a_data Array containing the matrix rows
1942    * @access public
1943    */
1944    public function importMatrix($a_data)
1945    {
1946        // overwrite in inherited classes
1947    }
1948
1949    /**
1950    * Returns if the question is usable for preconditions
1951    *
1952    * @return boolean TRUE if the question is usable for a precondition, FALSE otherwise
1953    * @access public
1954    */
1955    public function usableForPrecondition()
1956    {
1957        // overwrite in inherited classes
1958        return false;
1959    }
1960
1961    /**
1962    * Returns the available relations for the question
1963    *
1964    * @return array An array containing the available relations
1965    * @access public
1966    */
1967    public function getAvailableRelations()
1968    {
1969        // overwrite in inherited classes
1970        return array();
1971    }
1972
1973    /**
1974    * Returns the options for preconditions
1975    *
1976    * @return array
1977    */
1978    public function getPreconditionOptions()
1979    {
1980        // overwrite in inherited classes
1981    }
1982
1983    /**
1984    * Returns the output for a precondition value
1985    *
1986    * @param string $value The precondition value
1987    * @return string The output of the precondition value
1988    * @access public
1989    */
1990    public function getPreconditionValueOutput($value)
1991    {
1992        // overwrite in inherited classes
1993        return $value;
1994    }
1995
1996    /**
1997    * Creates a form property for the precondition value
1998    *
1999    * @return The ILIAS form element
2000    * @access public
2001    */
2002    public function getPreconditionSelectValue($default = "", $title, $variable)
2003    {
2004        // overwrite in inherited classes
2005        return null;
2006    }
2007
2008    public function setOriginalId($original_id)
2009    {
2010        $this->original_id = $original_id;
2011    }
2012
2013    public function getOriginalId()
2014    {
2015        return $this->original_id;
2016    }
2017
2018    public function getMaterial()
2019    {
2020        return $this->material;
2021    }
2022
2023    public function setSubtype($a_subtype)
2024    {
2025        // do nothing
2026    }
2027
2028    public function getSubtype()
2029    {
2030        // do nothing
2031        return null;
2032    }
2033
2034    /**
2035    * Object getter
2036    */
2037    public function __get($value)
2038    {
2039        switch ($value) {
2040            default:
2041                if (array_key_exists($value, $this->arrData)) {
2042                    return $this->arrData[$value];
2043                } else {
2044                    return null;
2045                }
2046                break;
2047        }
2048    }
2049
2050    /**
2051    * Object setter
2052    */
2053    public function __set($key, $value)
2054    {
2055        switch ($key) {
2056            default:
2057                $this->arrData[$key] = $value;
2058                break;
2059        }
2060    }
2061
2062    /**
2063     * Change original id of existing question in db
2064     *
2065     * @param int $a_question_id
2066     * @param int $a_original_id
2067     * @param int $a_object_id
2068     */
2069    public static function _changeOriginalId($a_question_id, $a_original_id, $a_object_id)
2070    {
2071        global $DIC;
2072
2073        $ilDB = $DIC->database();
2074
2075        $ilDB->manipulate("UPDATE svy_question" .
2076            " SET original_id = " . $ilDB->quote($a_original_id, "integer") . "," .
2077            " obj_fi = " . $ilDB->quote($a_object_id, "integer") .
2078            " WHERE question_id = " . $ilDB->quote($a_question_id, "integer"));
2079    }
2080
2081    public function getCopyIds($a_group_by_survey = false)
2082    {
2083        $ilDB = $this->db;
2084
2085        $set = $ilDB->query("SELECT q.question_id,s.obj_fi" .
2086            " FROM svy_question q" .
2087            " JOIN svy_svy_qst sq ON (sq.question_fi = q.question_id)" .
2088            " JOIN svy_svy s ON (s.survey_id = sq.survey_fi)" .
2089            " WHERE original_id = " . $ilDB->quote($this->getId(), "integer"));
2090        $res = array();
2091        while ($row = $ilDB->fetchAssoc($set)) {
2092            if (!$a_group_by_survey) {
2093                $res[] = $row["question_id"];
2094            } else {
2095                $res[$row["obj_fi"]][] = $row["question_id"];
2096            }
2097        }
2098        return $res;
2099    }
2100
2101    public function hasCopies()
2102    {
2103        return (bool) sizeof($this->getCopyIds());
2104    }
2105
2106    public static function _lookupSurveyObjId($a_question_id)
2107    {
2108        global $DIC;
2109
2110        $ilDB = $DIC->database();
2111
2112        $set = $ilDB->query("SELECT svy_svy.obj_fi FROM svy_svy_qst" .
2113            " JOIN svy_svy ON (svy_svy.survey_id = svy_svy_qst.survey_fi)" .
2114            " WHERE svy_svy_qst.question_fi = " . $ilDB->quote($a_question_id, "integer"));
2115        $row = $ilDB->fetchAssoc($set);
2116        if ($ilDB->numRows($set)) {
2117            return $row["obj_fi"];
2118        }
2119    }
2120
2121    /**
2122     * Lookip obj fi
2123     *
2124     * @param
2125     * @return
2126     */
2127    public static function lookupObjFi($a_qid)
2128    {
2129        global $DIC;
2130
2131        $ilDB = $DIC->database();
2132
2133        $set = $ilDB->query(
2134            "SELECT obj_fi FROM svy_question " .
2135            " WHERE question_id = " . $ilDB->quote($a_qid, "integer")
2136        );
2137        $rec = $ilDB->fetchAssoc($set);
2138        return $rec["obj_fi"];
2139    }
2140
2141    /**
2142     * Strip slashes with add space fallback, see https://mantis.ilias.de/view.php?id=19727
2143     *                                        and https://mantis.ilias.de/view.php?id=24200
2144     *
2145     * @param string $a_str string
2146     * @return string
2147     */
2148    public function stripSlashesAddSpaceFallback($a_str)
2149    {
2150        $str = ilUtil::stripSlashes($a_str);
2151        if ($str != $a_str) {
2152            $str = ilUtil::stripSlashes(str_replace("<", "< ", $a_str));
2153        }
2154        return $str;
2155    }
2156}
2157