1<?php if (!defined('BASEPATH')) {
2    exit('No direct script access allowed');
3}
4/*
5* LimeSurvey
6* Copyright (C) 2013 The LimeSurvey Project Team / Carsten Schmitz
7* All rights reserved.
8* License: GNU/GPL License v2 or later, see LICENSE.php
9* LimeSurvey is free software. This version may have been modified pursuant
10* to the GNU General Public License, and as distributed it includes or
11* is derivative of works licensed under the GNU General Public License or
12* other free or open source software licenses.
13* See COPYRIGHT.php for copyright notices and details.
14*
15*    Files Purpose: lots of common functions
16*/
17
18/**
19 * Class QuestionGroup
20 *
21 * @property integer $gid ID
22 * @property integer $sid Survey ID
23 * @property string $group_name Question group display name
24 * @property integer $group_order Group order number (max 100 chars)
25 * @property string $description Group display description
26 * @property string $language Language code (eg: 'en')
27 * @property string $randomization_group  Randomization group
28 * @property string $grelevance Group's relevane equation
29 *
30 * @property Survey $survey
31 * @property Question[] $questions Questions without subquestions
32 */
33class QuestionGroup extends LSActiveRecord
34{
35    public $aQuestions; // to stock array of questions of the group
36    /**
37     * @inheritdoc
38     * @return QuestionGroup
39     */
40    public static function model($class = __CLASS__)
41    {
42        /** @var self $model */
43        $model = parent::model($class);
44        return $model;
45    }
46
47    /** @inheritdoc */
48    public function tableName()
49    {
50        return '{{groups}}';
51    }
52
53    /** @inheritdoc */
54    public function primaryKey()
55    {
56        return array('gid', 'language');
57    }
58
59
60    /** @inheritdoc */
61    public function rules()
62    {
63        return array(
64            array('gid', 'unique', 'caseSensitive'=>true, 'criteria'=>array(
65                'condition'=>'language=:language',
66                'params'=>array(':language'=>$this->language)
67                ),
68                'message'=>'{attribute} "{value}" is already in use.'),
69            array('language', 'length', 'min' => 2, 'max'=>20), // in array languages ?
70            array('group_name,description', 'LSYii_Validators'),
71            array('group_name', 'length', 'min' => 0, 'max'=>100),
72            array('group_order', 'numerical', 'integerOnly'=>true, 'allowEmpty'=>true),
73        );
74    }
75
76
77    /** @inheritdoc */
78    public function attributeLabels()
79    {
80        return array(
81            'language' => gt('Language'),
82            'group_name' => gt('Group name')
83        );
84    }
85
86    /** @inheritdoc */
87    public function relations()
88    {
89        return array(
90            'survey'    => array(self::BELONGS_TO, 'Survey', 'sid'),
91            'questions' => array(self::HAS_MANY, 'Question', 'gid, language', 'condition'=>'parent_qid=0', 'order'=>'question_order ASC')
92        );
93    }
94
95
96    public function getAllRecords($condition = false, $order = false, $return_query = true)
97    {
98        $query = Yii::app()->db->createCommand()->select('*')->from('{{groups}}');
99
100        if ($condition != false) {
101            $query->where($condition);
102        }
103
104        if ($order != false) {
105            $query->order($order);
106        }
107
108        return ($return_query) ? $query->queryAll() : $query;
109    }
110
111    /**
112     * @param integer $sid
113     * @param string $lang
114     * @param int $position
115     */
116    public function updateGroupOrder($sid, $lang, $position = 0)
117    {
118        $data = Yii::app()->db->createCommand()->select('gid')
119            ->where(array('and', 'sid=:sid', 'language=:language'))
120            ->order('group_order, group_name ASC')
121            ->from('{{groups}}')
122            ->bindParam(':sid', $sid, PDO::PARAM_INT)
123            ->bindParam(':language', $lang, PDO::PARAM_STR)
124            ->query();
125
126        $position = intval($position);
127        foreach ($data->readAll() as $row) {
128            Yii::app()->db->createCommand()->update($this->tableName(), array('group_order' => $position), 'gid='.$row['gid']);
129            $position++;
130        }
131    }
132
133    public function cleanOrder($surveyid){
134        $iSurveyId = (int) $surveyid;
135        $oSurvey = Survey::model()->findByPk($iSurveyId);
136
137        $aSurveyLanguages = array_merge([$oSurvey->language], explode(" ", $oSurvey->additional_languages));
138
139        foreach ($aSurveyLanguages as $sSurveyLanguage) {
140            $oCriteria=new CDbCriteria;
141            $oCriteria->compare('sid',$iSurveyId);
142            $oCriteria->compare('language',$sSurveyLanguage);
143            $oCriteria->order = 'group_order ASC';
144
145            $aQuestiongroups = QuestionGroup::model()->findAll($oCriteria);
146            foreach($aQuestiongroups as $itrt => $oQuestiongroup) {
147                $iQuestionGroupOrder = $itrt+1;
148                $oQuestiongroup->group_order = $iQuestionGroupOrder;
149                $oQuestiongroup->save();
150
151                $aQuestions = $oQuestiongroup->questions;
152                foreach ($aQuestions as $qitrt => $oQuestion) {
153                    $iQuestionOrder = $qitrt+1;
154                    $oQuestion->question_order = $iQuestionOrder;
155                    $oQuestion->save(true);
156                }
157            }
158        }
159    }
160    /**
161     * Insert an array into the groups table
162     * Returns false if insertion fails, otherwise the new GID
163     *
164     * @param array $data
165     * @return bool|int
166     */
167    public function insertRecords($data)
168    {
169        $group = new self;
170        foreach ($data as $k => $v) {
171            $group->$k = $v;
172        }
173        if (!$group->save()) {
174            return false;
175        } else {
176            return $group->gid;
177        }
178    }
179
180
181    /**
182     * This functions insert question group data in the form of array('<grouplanguage>'=>array( <array of fieldnames => values >))
183     * It will take care of maintaining the group ID
184     *
185     * @param mixed $aQuestionGroupData
186     * @return bool|int
187     */
188    public function insertNewGroup($aQuestionGroupData)
189    {
190        $aFirstRecord = reset($aQuestionGroupData);
191        $iSurveyID = $aFirstRecord['sid'];
192        $sBaseLangauge = Survey::model()->findByPk($iSurveyID)->language;
193        $aAdditionalLanguages = Survey::model()->findByPk($iSurveyID)->additionalLanguages;
194        $aSurveyLanguages = array($sBaseLangauge) + $aAdditionalLanguages;
195        $bFirst = true;
196        $iGroupID = null;
197        foreach ($aSurveyLanguages as $sLanguage) {
198            if ($bFirst) {
199                $iGroupID = $this->insertRecords($aQuestionGroupData[$sLanguage]);
200                $bFirst = false;
201            } else {
202                $aQuestionGroupData[$sLanguage]['gid'] = $iGroupID;
203                switchMSSQLIdentityInsert('groups', true);
204                $this->insertRecords($aQuestionGroupData[$sLanguage]);
205                switchMSSQLIdentityInsert('groups', false);
206            }
207        }
208        return $iGroupID;
209    }
210
211
212    /**
213     * @param int $surveyid
214     * @return array
215     */
216    public function getGroups($surveyid)
217    {
218        $language = Survey::model()->findByPk($surveyid)->language;
219        return Yii::app()->db->createCommand()
220            ->select(array('gid', 'group_name'))
221            ->from($this->tableName())
222            ->where(array('and', 'sid=:surveyid', 'language=:language'))
223            ->order('group_order asc')
224            ->bindParam(":language", $language, PDO::PARAM_STR)
225            ->bindParam(":surveyid", $surveyid, PDO::PARAM_INT)
226            ->query()->readAll();
227    }
228
229    /**
230     * @param integer $groupId
231     * @param integer $surveyId
232     * @return int|null
233     */
234    public static function deleteWithDependency($groupId, $surveyId)
235    {
236        // Abort if the survey is active
237        $surveyIsActive = Survey::model()->findByPk($surveyId)->active !== 'N';
238        if ($surveyIsActive) {
239            Yii::app()->user->setFlash('error', gt("Can't delete question group when the survey is active"));
240            return null;
241        }
242
243        $questionIds = QuestionGroup::getQuestionIdsInGroup($groupId);
244        Question::deleteAllById($questionIds);
245        Assessment::model()->deleteAllByAttributes(array('sid' => $surveyId, 'gid' => $groupId));
246        return QuestionGroup::model()->deleteAllByAttributes(array('sid' => $surveyId, 'gid' => $groupId));
247    }
248
249    /**
250     * Get group description
251     *
252     * @param int $iGroupId
253     * @param string $sLanguage
254     * @return string
255     */
256    public function getGroupDescription($iGroupId, $sLanguage)
257    {
258        return $this->findByPk(array('gid' => $iGroupId, 'language' => $sLanguage))->description;
259    }
260
261    /**
262     * @param integer $groupId
263     * @return array
264     */
265    private static function getQuestionIdsInGroup($groupId)
266    {
267        $questions = Yii::app()->db->createCommand()
268            ->select('qid')
269            ->from('{{questions}} q')
270            ->join('{{groups}} g', 'g.gid=q.gid AND g.gid=:groupid AND q.parent_qid=0')
271            ->group('qid')
272            ->bindParam(":groupid", $groupId, PDO::PARAM_INT)
273            ->queryAll();
274
275        $questionIds = array();
276        foreach ($questions as $question) {
277            $questionIds[] = $question['qid'];
278        }
279
280        return $questionIds;
281    }
282
283    /**
284     * @param mixed|array $condition
285     * @param string[] $order
286     * @return CDbDataReader
287     */
288    public function getAllGroups($condition, $order = false)
289    {
290        $command = Yii::app()->db->createCommand()
291            ->where($condition)
292            ->select('*')
293            ->from($this->tableName());
294        if ($order != false) {
295            $command->order($order);
296        }
297        return $command->query();
298    }
299
300    /**
301     * @return string
302     */
303    public function getbuttons()
304    {
305        // Find out if the survey is active to disable add-button
306        $oSurvey = Survey::model()->findByPk($this->sid);
307        $surveyIsActive = $oSurvey->active !== 'N';
308        $button = '';
309
310        // Add question to this group
311        if (Permission::model()->hasSurveyPermission($this->sid, 'surveycontent', 'update')) {
312            $url = Yii::app()->createUrl("admin/questions/sa/newquestion/surveyid/$this->sid/gid/$this->gid");
313            $button .= '<a class="btn btn-default list-btn '.($surveyIsActive ? 'disabled' : '').' "  data-toggle="tooltip"  data-placement="left" title="'.gT('Add new question to group').'" href="'.$url.'" role="button"><i class="fa fa-plus " ></i></a>';
314        }
315
316        // Group edition
317        // Edit
318        if (Permission::model()->hasSurveyPermission($this->sid, 'surveycontent', 'update')) {
319            $url = Yii::app()->createUrl("admin/questiongroups/sa/edit/surveyid/$this->sid/gid/$this->gid");
320            $button .= '  <a class="btn btn-default  list-btn" href="'.$url.'" role="button" data-toggle="tooltip" title="'.gT('Edit group').'"><i class="fa fa-pencil " ></i></a>';
321        }
322
323        // View summary
324        if (Permission::model()->hasSurveyPermission($this->sid, 'surveycontent', 'read')) {
325            $url = Yii::app()->createUrl("/admin/questiongroups/sa/view/surveyid/");
326            $url .= '/'.$this->sid.'/gid/'.$this->gid;
327            $button .= '  <a class="btn btn-default  list-btn" href="'.$url.'" role="button" data-toggle="tooltip" title="'.gT('Group summary').'"><i class="fa fa-list-alt " ></i></a>';
328        }
329
330        // Delete
331        if ($oSurvey->active != "Y" && Permission::model()->hasSurveyPermission($this->sid, 'surveycontent', 'delete')) {
332            $condarray = getGroupDepsForConditions($this->sid, "all", $this->gid, "by-targgid");
333            if (is_null($condarray)) {
334                $button .= '<span data-toggle="tooltip" title="'.gT('Delete survey group').'">'
335                    .'<button class="btn btn-default" '
336                    .' data-onclick="(function() { '.CHtml::encode(convertGETtoPOST(Yii::app()->createUrl("admin/questiongroups/sa/delete/", ["surveyid" => $this->sid,  "gid"=>$this->gid]))).' })" '
337                    .' data-target="#confirmation-modal"'
338                    .' role="button"'
339                    .' data-toggle="modal"'
340                    .' data-message="'.gT("Deleting this group will also delete any questions and answers it contains. Are you sure you want to continue?", "js").'"'
341                    .'>'
342                        .'<i class="fa fa-trash text-danger "></i>'
343                        .'<span class="sr-only">'.gT('Delete survey group').'</span>'
344                    .'</button>'
345                    .'</span>';
346
347            } else {
348                $button .= '<span data-toggle="tooltip" title="'.gT('Group cant be deleted, because of depending conditions').'">'
349                    .'<button class="btn btn-default" '
350                    .' disabled '
351                    .' role="button"'
352                    .' data-toggle="popover"'
353                    .' data-tooltip="true"'
354                    .' title="'.gT("Impossible to delete this group because there is at least one question having a condition on its content", "js").'">'
355                        .'<i class="fa fa-trash text-muted "></i>'
356                        .'<span class="sr-only">'.gT('Delete survey group not possible').'</span>'
357                    .'</button>'
358                    .'</span>';
359            }
360        }
361
362        return $button;
363    }
364
365
366    /**
367     * @return CActiveDataProvider
368     */
369    public function search()
370    {
371        $pageSize = Yii::app()->user->getState('pageSize', Yii::app()->params['defaultPageSize']);
372
373        $sort = new CSort();
374        $sort->defaultOrder = array('group_order'=> false);
375        $sort->attributes = array(
376            'group_id'=>array(
377                'asc'=>'gid',
378                'desc'=>'gid desc',
379            ),
380            'group_order'=>array(
381                'asc'=>'group_order',
382                'desc'=>'group_order desc',
383            ),
384            'group_name'=>array(
385                'asc'=>'group_name',
386                'desc'=>'group_name desc',
387            ),
388        );
389
390        $criteria = new CDbCriteria;
391        $criteria->condition = 'sid=:surveyid AND language=:language';
392        $criteria->params = (array(':surveyid'=>$this->sid, ':language'=>$this->language));
393        $criteria->compare('group_name', $this->group_name, true);
394
395        $dataProvider = new CActiveDataProvider(get_class($this), array(
396            'criteria'=>$criteria,
397
398            'sort'=>$sort,
399
400            'pagination'=>array(
401                'pageSize'=>$pageSize,
402            ),
403        ));
404        return $dataProvider;
405    }
406
407    /**
408     * Make sure we don't save a new question group
409     * while the survey is active.
410     *
411     * @inheritdoc
412     */
413    protected function beforeSave()
414    {
415        if (parent::beforeSave()) {
416            $surveyIsActive = Survey::model()->findByPk($this->sid)->active !== 'N';
417            if ($surveyIsActive && $this->getIsNewRecord()) {
418/* And for multi lingual, when add a new language ? */
419                $this->addError('gid', gT("You can not add a group if survey is active."));
420                return false;
421            }
422            return true;
423        } else {
424            return false;
425        }
426    }
427
428    /**
429     * Returns the first question group in the survey
430     * @param int $surveyId
431     * @return QuestionGroup
432     */
433    public static function getFirstGroup($surveyId)
434    {
435        $criteria = new CDbCriteria();
436        $criteria->addCondition('sid = '.$surveyId);
437        $criteria->mergeWith(array(
438            'order' => 'gid DESC'
439        ));
440        return self::model()->find($criteria);
441    }
442
443    /*
444     * Used in frontend helper, buildsurveysession.
445     * @param int $surveyid
446     * @return int
447     */
448    public static function getTotalGroupsWithoutQuestions($surveyid)
449    {
450        $sQuery = "select count(*) from ".Yii::app()->db->quoteTableName('{{groups}}')." g
451            left join {{questions}} on  g.gid={{questions}}.gid
452            where g.sid={$surveyid} and qid is null";
453        return Yii::app()->db->createCommand($sQuery)->queryScalar();
454    }
455
456    /**
457     * Used in frontend helper, buildsurveysession.
458     * @param int $surveyid
459     * @return int
460     */
461    public static function getTotalGroupsWithQuestions($surveyid)
462    {
463        $sQuery = "select count(DISTINCT g.gid) from ".Yii::app()->db->quoteTableName('{{groups}}')." g
464            left join {{questions}} q on  g.gid=q.gid
465            where g.sid={$surveyid} and qid is not null";
466        return Yii::app()->db->createCommand($sQuery)->queryScalar();
467    }
468
469}
470