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