1<?php
2
3/* Copyright (c) 1998-2019 ILIAS open source, Extended GPL, see docs/LICENSE */
4
5/**
6 * Text survey question
7 *
8 * The SurveyTextQuestion class defines and encapsulates basic methods and attributes
9 * for text survey question types.
10 *
11 * @author		Helmut Schottmüller <helmut.schottmueller@mac.com>
12 */
13class SurveyTextQuestion extends SurveyQuestion
14{
15    public $maxchars;
16    public $textwidth;
17    public $textheight;
18
19    /**
20    * The constructor takes possible arguments an creates an instance of the SurveyTextQuestion object.
21    *
22    * @param string $title A title string to describe the question
23    * @param string $description A description string to describe the question
24    * @param string $author A string containing the name of the questions author
25    * @param integer $owner A numerical ID to identify the owner/creator
26    * @access public
27    */
28    public function __construct($title = "", $description = "", $author = "", $questiontext = "", $owner = -1)
29    {
30        global $DIC;
31
32        $this->db = $DIC->database();
33        parent::__construct($title, $description, $author, $questiontext, $owner);
34
35        $this->maxchars = 0;
36        $this->textwidth = 50;
37        $this->textheight = 5;
38    }
39
40    /**
41    * Returns the question data fields from the database
42    *
43    * @param integer $id The question ID from the database
44    * @return array Array containing the question fields and data from the database
45    * @access public
46    */
47    public function getQuestionDataArray($id)
48    {
49        $ilDB = $this->db;
50        $result = $ilDB->queryF(
51            "SELECT svy_question.*, " . $this->getAdditionalTableName() . ".* FROM svy_question, " . $this->getAdditionalTableName() . " WHERE svy_question.question_id = %s AND svy_question.question_id = " . $this->getAdditionalTableName() . ".question_fi",
52            array('integer'),
53            array($id)
54        );
55        if ($result->numRows() == 1) {
56            return $ilDB->fetchAssoc($result);
57        } else {
58            return array();
59        }
60    }
61
62    /**
63    * Loads a SurveyTextQuestion object from the database
64    *
65    * @param integer $id The database id of the text survey question
66    * @access public
67    */
68    public function loadFromDb($id)
69    {
70        $ilDB = $this->db;
71
72        $result = $ilDB->queryF(
73            "SELECT svy_question.*, " . $this->getAdditionalTableName() . ".* FROM svy_question LEFT JOIN " . $this->getAdditionalTableName() . " ON " . $this->getAdditionalTableName() . ".question_fi = svy_question.question_id WHERE svy_question.question_id = %s",
74            array('integer'),
75            array($id)
76        );
77        if ($result->numRows() == 1) {
78            $data = $ilDB->fetchAssoc($result);
79            $this->setId($data["question_id"]);
80            $this->setTitle($data["title"]);
81            $this->label = $data['label'];
82            $this->setDescription($data["description"]);
83            $this->setObjId($data["obj_fi"]);
84            $this->setAuthor($data["author"]);
85            $this->setOwner($data["owner_fi"]);
86            $this->setQuestiontext(ilRTE::_replaceMediaObjectImageSrc($data["questiontext"], 1));
87            $this->setObligatory($data["obligatory"]);
88            $this->setComplete($data["complete"]);
89            $this->setOriginalId($data["original_id"]);
90
91            $this->setMaxChars($data["maxchars"]);
92            $this->setTextWidth($data["width"]);
93            $this->setTextHeight($data["height"]);
94        }
95        parent::loadFromDb($id);
96    }
97
98    /**
99    * Returns true if the question is complete for use
100    *
101    * @result boolean True if the question is complete for use, otherwise false
102    * @access public
103    */
104    public function isComplete()
105    {
106        if (
107            strlen($this->getTitle()) &&
108            strlen($this->getAuthor()) &&
109            strlen($this->getQuestiontext())
110        ) {
111            return 1;
112        } else {
113            return 0;
114        }
115    }
116
117    /**
118    * Sets the maximum number of allowed characters for the text answer
119    *
120    * @access public
121    */
122    public function setMaxChars($maxchars = 0)
123    {
124        $this->maxchars = $maxchars;
125    }
126
127    /**
128    * Returns the maximum number of allowed characters for the text answer
129    *
130    * @access public
131    */
132    public function getMaxChars()
133    {
134        return ($this->maxchars) ? $this->maxchars : null;
135    }
136
137    /**
138    * Saves a SurveyTextQuestion object to a database
139    *
140    * @access public
141    */
142    public function saveToDb($original_id = "")
143    {
144        $ilDB = $this->db;
145
146        $affectedRows = parent::saveToDb($original_id);
147        if ($affectedRows == 1) {
148            $affectedRows = $ilDB->manipulateF(
149                "DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
150                array('integer'),
151                array($this->getId())
152            );
153            $affectedRows = $ilDB->manipulateF(
154                "INSERT INTO " . $this->getAdditionalTableName() . " (question_fi, maxchars, width, height) VALUES (%s, %s, %s, %s)",
155                array('integer', 'integer', 'integer', 'integer'),
156                array($this->getId(), $this->getMaxChars(), $this->getTextWidth(), $this->getTextHeight())
157            );
158
159            $this->saveMaterial();
160        }
161    }
162
163    /**
164    * Returns an xml representation of the question
165    *
166    * @return string The xml representation of the question
167    * @access public
168    */
169    public function toXML($a_include_header = true, $obligatory_state = "")
170    {
171        $a_xml_writer = new ilXmlWriter;
172        $a_xml_writer->xmlHeader();
173        $this->insertXML($a_xml_writer, $a_include_header, $obligatory_state);
174        $xml = $a_xml_writer->xmlDumpMem(false);
175        if (!$a_include_header) {
176            $pos = strpos($xml, "?>");
177            $xml = substr($xml, $pos + 2);
178        }
179        return $xml;
180    }
181
182    /**
183    * Adds the question XML to a given XMLWriter object
184    *
185    * @param object $a_xml_writer The XMLWriter object
186    * @param boolean $a_include_header Determines wheather or not the XML should be used
187    * @access public
188    */
189    public function insertXML(&$a_xml_writer, $a_include_header = true)
190    {
191        $attrs = array(
192            "id" => $this->getId(),
193            "title" => $this->getTitle(),
194            "type" => $this->getQuestiontype(),
195            "obligatory" => $this->getObligatory()
196        );
197        $a_xml_writer->xmlStartTag("question", $attrs);
198
199        $a_xml_writer->xmlElement("description", null, $this->getDescription());
200        $a_xml_writer->xmlElement("author", null, $this->getAuthor());
201        if (strlen($this->label)) {
202            $attrs = array(
203                "label" => $this->label,
204            );
205        } else {
206            $attrs = array();
207        }
208        $a_xml_writer->xmlStartTag("questiontext", $attrs);
209        $this->addMaterialTag($a_xml_writer, $this->getQuestiontext());
210        $a_xml_writer->xmlEndTag("questiontext");
211
212        $a_xml_writer->xmlStartTag("responses");
213        $attrs = array(
214            "id" => "0",
215            "rows" => $this->getTextHeight(),
216            "columns" => $this->getTextWidth()
217        );
218        if ($this->getMaxChars() > 0) {
219            $attrs["maxlength"] = $this->getMaxChars();
220        }
221        $a_xml_writer->xmlElement("response_text", $attrs);
222        $a_xml_writer->xmlEndTag("responses");
223
224        if (count($this->material)) {
225            if (preg_match("/il_(\d*?)_(\w+)_(\d+)/", $this->material["internal_link"], $matches)) {
226                $attrs = array(
227                    "label" => $this->material["title"]
228                );
229                $a_xml_writer->xmlStartTag("material", $attrs);
230                $intlink = "il_" . IL_INST_ID . "_" . $matches[2] . "_" . $matches[3];
231                if (strcmp($matches[1], "") != 0) {
232                    $intlink = $this->material["internal_link"];
233                }
234                $a_xml_writer->xmlElement("mattext", null, $intlink);
235                $a_xml_writer->xmlEndTag("material");
236            }
237        }
238
239        $a_xml_writer->xmlEndTag("question");
240    }
241
242    /**
243    * Returns the question type of the question
244    *
245    * @return integer The question type of the question
246    * @access public
247    */
248    public function getQuestionType()
249    {
250        return "SurveyTextQuestion";
251    }
252
253    /**
254    * Returns the name of the additional question data table in the database
255    *
256    * @return string The additional table name
257    * @access public
258    */
259    public function getAdditionalTableName()
260    {
261        return "svy_qst_text";
262    }
263
264    /**
265    * Creates the user data of the svy_answer table from the POST data
266    *
267    * @return array User data according to the svy_answer table
268    * @access public
269    */
270    public function &getWorkingDataFromUserInput($post_data)
271    {
272        $entered_value = $post_data[$this->getId() . "_text_question"];
273        $data = array();
274        if (strlen($entered_value)) {
275            array_push($data, array("textanswer" => $entered_value));
276        }
277        return $data;
278    }
279
280    /**
281    * Checks the input of the active user for obligatory status
282    * and entered values
283    *
284    * @param array $post_data The contents of the $_POST array
285    * @param integer $survey_id The database ID of the active survey
286    * @return string Empty string if the input is ok, an error message otherwise
287    * @access public
288    */
289    public function checkUserInput($post_data, $survey_id)
290    {
291        $entered_value = $post_data[$this->getId() . "_text_question"];
292
293        if ((!$this->getObligatory($survey_id)) && (strlen($entered_value) == 0)) {
294            return "";
295        }
296
297        if (strlen($entered_value) == 0) {
298            return $this->lng->txt("text_question_not_filled_out");
299        }
300
301        // see bug #22648
302        if ($this->getMaxChars() > 0 && ilStr::strLen($entered_value) > $this->getMaxChars()) {
303            return str_replace("%s", ilStr::strLen($entered_value), $this->lng->txt("svy_answer_too_long"));
304        }
305
306        return "";
307    }
308
309    public function saveUserInput($post_data, $active_id, $a_return = false)
310    {
311        $ilDB = $this->db;
312
313        $entered_value = $this->stripSlashesAddSpaceFallback($post_data[$this->getId() . "_text_question"]);
314        $maxchars = $this->getMaxChars();
315
316        if ($maxchars > 0) {
317            $entered_value = ilStr::subStr($entered_value, 0, $maxchars);
318        }
319
320        if ($a_return) {
321            return array(array("value" => null, "textanswer" => $entered_value));
322        }
323        if (strlen($entered_value) == 0) {
324            return;
325        }
326
327        $next_id = $ilDB->nextId('svy_answer');
328        #20216
329        $fields = array();
330        $fields['answer_id'] = array("integer", $next_id);
331        $fields['question_fi'] = array("integer", $this->getId());
332        $fields['active_fi'] = array("integer", $active_id);
333        $fields['value'] = array("float", null);
334        $fields['textanswer'] = array("clob", (strlen($entered_value)) ? $entered_value : null);
335        $fields['tstamp'] = array("integer", time());
336
337        $affectedRows = $ilDB->insert("svy_answer", $fields);
338    }
339
340    /**
341    * Import response data from the question import file
342    *
343    * @return array $a_data Array containing the response data
344    * @access public
345    */
346    public function importResponses($a_data)
347    {
348        foreach ($a_data as $id => $data) {
349            if ($data["maxlength"] > 0) {
350                $this->setMaxChars($data["maxlength"]);
351            }
352            if ($data["rows"] > 0) {
353                $this->setTextHeight($data["rows"]);
354            }
355            if ($data["columns"] > 0) {
356                $this->setTextWidth($data["columns"]);
357            }
358        }
359    }
360
361    /**
362    * Returns if the question is usable for preconditions
363    *
364    * @return boolean TRUE if the question is usable for a precondition, FALSE otherwise
365    * @access public
366    */
367    public function usableForPrecondition()
368    {
369        return false;
370    }
371
372    /**
373    * Returns the width of the answer field
374    *
375    * @return integer The width of the answer field in characters
376    * @access public
377    */
378    public function getTextWidth()
379    {
380        return ($this->textwidth) ? $this->textwidth : null;
381    }
382
383    /**
384    * Returns the height of the answer field
385    *
386    * @return integer The height of the answer field in characters
387    * @access public
388    */
389    public function getTextHeight()
390    {
391        return ($this->textheight) ? $this->textheight : null;
392    }
393
394    /**
395    * Sets the width of the answer field
396    *
397    * @param integer $a_textwidth The width of the answer field in characters
398    * @access public
399    */
400    public function setTextWidth($a_textwidth)
401    {
402        if ($a_textwidth < 1) {
403            $this->textwidth = 50;
404        } else {
405            $this->textwidth = $a_textwidth;
406        }
407    }
408
409    /**
410    * Sets the height of the answer field
411    *
412    * @param integer $a_textheight The height of the answer field in characters
413    * @access public
414    */
415    public function setTextHeight($a_textheight)
416    {
417        if ($a_textheight < 1) {
418            $this->textheight = 5;
419        } else {
420            $this->textheight = $a_textheight;
421        }
422    }
423}
424