· 9 years ago · Oct 18, 2016, 08:20 AM
1<?php
2
3if (!defined('BASEPATH'))
4 die('No direct script access allowed');
5/*
6* LimeSurvey
7* Copyright (C) 2007-2011 The LimeSurvey Project Team / Carsten Schmitz
8* All rights reserved.
9* License: GNU/GPL License v2 or later, see LICENSE.php
10* LimeSurvey is free software. This version may have been modified pursuant
11* to the GNU General Public License, and as distributed it includes or
12* is derivative of works licensed under the GNU General Public License or
13* other free or open source software licenses.
14* See COPYRIGHT.php for copyright notices and details.
15*
16*/
17
18class Survey extends LSActiveRecord
19{
20 /**
21 * This is a static cache, it lasts only during the active request. If you ever need
22 * to clear it, like on activation of a survey when in the same request a row is read,
23 * saved and read again you can use resetCache() method.
24 *
25 * @var array
26 */
27 protected $findByPkCache = array();
28 /* Default settings for new survey */
29 /* This settings happen for whole new Survey, not only admin/survey/sa/newsurvey */
30 public $format = 'G';
31 public $htmlemail='Y';
32
33 public $full_answers_account=null;
34 public $partial_answers_account=null;
35 public $searched_value;
36
37 private $fac;
38 private $pac;
39
40
41 /**
42 * init to set default
43 *
44 */
45 public function init()
46 {
47 $this->template = Template::templateNameFilter(Yii::app()->getConfig('defaulttemplate'));
48 $validator= new LSYii_Validators;
49 $this->language = $validator->languageFilter(Yii::app()->getConfig('defaultlang'));
50 $this->attachEventHandler("onAfterFind", array($this,'fixSurveyAttribute'));
51 // $userId = Yii::app()->session['loginID'];
52 }
53
54 /* Add virtual survey attribute labels for gridView*/
55 public function attributeLabels() {
56 return array(
57 /* Your other attribute labels */
58 'running' => gT('running')
59 );
60 }
61
62 /**
63 * Returns the title of the survey. Uses the current language and
64 * falls back to the surveys' default language if the current language is not available.
65 */
66 public function getLocalizedTitle()
67 {
68 if (isset($this->languagesettings[App()->language]))
69 {
70 return $this->languagesettings[App()->language]->surveyls_title;
71 }
72 else
73 {
74 return $this->languagesettings[$this->language]->surveyls_title;
75 }
76 }
77 /**
78 * Expires a survey. If the object was invoked using find or new surveyId can be ommited.
79 * @param int $surveyId
80 */
81 public function expire($surveyId = null)
82 {
83 $dateTime = dateShift(date("Y-m-d H:i:s"), "Y-m-d H:i:s", Yii::app()->getConfig('timeadjust'));
84 $dateTime = dateShift($dateTime, "Y-m-d H:i:s", '-1 day');
85
86 if (!isset($surveyId))
87 {
88 $this->expires = $dateTime;
89 if ($this->scenario == 'update')
90 {
91 return $this->save();
92 }
93 }
94 else
95 {
96 self::model()->updateByPk($surveyId,array('expires' => $dateTime));
97 }
98
99 }
100
101 /**
102 * Returns the table's name
103 *
104 * @access public
105 * @return string
106 */
107 public function tableName()
108 {
109 return '{{surveys}}';
110 }
111
112 /**
113 * Returns the table's primary key
114 *
115 * @access public
116 * @return string
117 */
118 public function primaryKey()
119 {
120 return 'sid';
121 }
122
123 /**
124 * Returns the static model of Settings table
125 *
126 * @static
127 * @access public
128 * @param string $class
129 * @return Survey
130 */
131 public static function model($class = __CLASS__)
132 {
133 return parent::model($class);
134 }
135
136 /**
137 * Returns this model's relations
138 *
139 * @access public
140 * @return array
141 */
142 public function relations()
143 {
144 $alias = $this->getTableAlias();
145 return array(
146
147 'permissions' => array(self::HAS_MANY, 'Permission', array( 'entity_id'=> 'sid' ), 'together' => true ), //
148 'languagesettings' => array(self::HAS_MANY, 'SurveyLanguageSetting', 'surveyls_survey_id', 'index' => 'surveyls_language', 'together' => true),
149 'defaultlanguage' => array(self::BELONGS_TO, 'SurveyLanguageSetting', array('language' => 'surveyls_language', 'sid' => 'surveyls_survey_id'), 'together' => true),
150 'correct_relation_defaultlanguage' => array(self::HAS_ONE, 'SurveyLanguageSetting', array('surveyls_language' => 'language', 'surveyls_survey_id' => 'sid'), 'together' => true),
151 'owner' => array(self::BELONGS_TO, 'User', 'owner_id', 'together' => true),
152 'groups' => array(self::HAS_MANY, 'QuestionGroup', 'sid', 'together' => true),
153 );
154 }
155
156 /**
157 * Returns this model's scopes
158 *
159 * @access public
160 * @return array
161 */
162 public function scopes()
163 {
164 return array(
165 'active' => array('condition' => "active = 'Y' AND owner_id = '".Yii::app()->session['loginID']."'"),
166 'open' => array('condition' => '(startdate <= :now1 OR startdate IS NULL) AND (expires >= :now2 OR expires IS NULL)', 'params' => array(
167 ':now1' => dateShift(date("Y-m-d H:i:s"), "Y-m-d H:i:s", Yii::app()->getConfig("timeadjust")),
168 ':now2' => dateShift(date("Y-m-d H:i:s"), "Y-m-d H:i:s", Yii::app()->getConfig("timeadjust"))
169 )
170 ),
171 'public' => array('condition' => "listpublic = 'Y'"),
172 'registration' => array('condition' => "allowregister = 'Y' AND startdate > :now3 AND (expires < :now4 OR expires IS NULL) AND owner_id = '".Yii::app()->session['loginID']."'", 'params' => array(
173 ':now3' => dateShift(date("Y-m-d H:i:s"), "Y-m-d H:i:s", Yii::app()->getConfig("timeadjust")),
174 ':now4' => dateShift(date("Y-m-d H:i:s"), "Y-m-d H:i:s", Yii::app()->getConfig("timeadjust"))
175 ))
176 );
177 }
178
179 /**
180 * Returns this model's validation rules
181 *
182 */
183 public function rules()
184 {
185 return array(
186 array('datecreated', 'default','value'=>date("Y-m-d")),
187 array('startdate', 'default','value'=>NULL),
188 array('expires', 'default','value'=>NULL),
189 array('admin,faxto','LSYii_Validators'),
190 array('adminemail','filter', 'filter'=>'trim'),
191 array('bounce_email','filter', 'filter'=>'trim'),
192 array('bounce_email','LSYii_EmailIDNAValidator', 'allowEmpty'=>true),
193 array('active', 'in','range'=>array('Y','N'), 'allowEmpty'=>true),
194 array('anonymized', 'in','range'=>array('Y','N'), 'allowEmpty'=>true),
195 array('savetimings', 'in','range'=>array('Y','N'), 'allowEmpty'=>true),
196 array('datestamp', 'in','range'=>array('Y','N'), 'allowEmpty'=>true),
197 array('usecookie', 'in','range'=>array('Y','N'), 'allowEmpty'=>true),
198 array('allowregister', 'in','range'=>array('Y','N'), 'allowEmpty'=>true),
199 array('allowsave', 'in','range'=>array('Y','N'), 'allowEmpty'=>true),
200 array('autoredirect', 'in','range'=>array('Y','N'), 'allowEmpty'=>true),
201 array('allowprev', 'in','range'=>array('Y','N'), 'allowEmpty'=>true),
202 array('printanswers', 'in','range'=>array('Y','N'), 'allowEmpty'=>true),
203 array('ipaddr', 'in','range'=>array('Y','N'), 'allowEmpty'=>true),
204 array('refurl', 'in','range'=>array('Y','N'), 'allowEmpty'=>true),
205 array('publicstatistics', 'in','range'=>array('Y','N'), 'allowEmpty'=>true),
206 array('publicgraphs', 'in','range'=>array('Y','N'), 'allowEmpty'=>true),
207 array('listpublic', 'in','range'=>array('Y','N'), 'allowEmpty'=>true),
208 array('htmlemail', 'in','range'=>array('Y','N'), 'allowEmpty'=>true),
209 array('sendconfirmation', 'in','range'=>array('Y','N'), 'allowEmpty'=>true),
210 array('tokenanswerspersistence', 'in','range'=>array('Y','N'), 'allowEmpty'=>true),
211 array('assessments', 'in','range'=>array('Y','N'), 'allowEmpty'=>true),
212 array('usetokens', 'in','range'=>array('Y','N'), 'allowEmpty'=>true),
213 array('showxquestions', 'in','range'=>array('Y','N'), 'allowEmpty'=>true),
214 array('shownoanswer', 'in','range'=>array('Y','N'), 'allowEmpty'=>true),
215 array('showwelcome', 'in','range'=>array('Y','N'), 'allowEmpty'=>true),
216 array('showprogress', 'in','range'=>array('Y','N'), 'allowEmpty'=>true),
217 array('questionindex', 'numerical','min' => 0, 'max' => 2, 'allowEmpty'=>false),
218 array('nokeyboard', 'in','range'=>array('Y','N'), 'allowEmpty'=>true),
219 array('alloweditaftercompletion', 'in','range'=>array('Y','N'), 'allowEmpty'=>true),
220 array('bounceprocessing', 'in','range'=>array('L','N','G'), 'allowEmpty'=>true),
221 array('usecaptcha', 'in','range'=>array('A','B','C','D','X','R','S','N'), 'allowEmpty'=>true),
222 array('showgroupinfo', 'in','range'=>array('B','N','D','X'), 'allowEmpty'=>true),
223 array('showqnumcode', 'in','range'=>array('B','N','C','X'), 'allowEmpty'=>true),
224 array('format', 'in','range'=>array('G','S','A'), 'allowEmpty'=>true),
225 array('googleanalyticsstyle', 'numerical', 'integerOnly'=>true, 'min'=>'0', 'max'=>'2', 'allowEmpty'=>true),
226 array('autonumber_start','numerical', 'integerOnly'=>true,'allowEmpty'=>true),
227 array('tokenlength', 'default', 'value'=>15),
228 array('tokenlength','numerical', 'integerOnly'=>true,'allowEmpty'=>false, 'min'=>'5', 'max'=>'36'),
229 array('bouncetime','numerical', 'integerOnly'=>true,'allowEmpty'=>true),
230 array('navigationdelay','numerical', 'integerOnly'=>true,'allowEmpty'=>true),
231 array('template', 'filter', 'filter'=>array($this,'filterTemplateSave')),
232 array('language','LSYii_Validators','isLanguage'=>true),
233 array('language', 'required', 'on' => 'insert'),
234 array('language', 'filter', 'filter'=>'trim'),
235 array('additional_languages', 'filter', 'filter'=>'trim'),
236 array('additional_languages','LSYii_Validators','isLanguageMulti'=>true),
237 array('running', 'safe', 'on'=>'search'),
238 array('expectedresponses','length', 'max'=>255),
239 array('location','length', 'max'=>255),
240 array('gender','length', 'max'=>255),
241 array('age','length', 'max'=>255),
242 array('maritalstatus','length', 'max'=>255),
243 // Date rules currently don't work properly with MSSQL, deactivating for now
244 // array('expires','date', 'format'=>array('yyyy-MM-dd', 'yyyy-MM-dd HH:mm', 'yyyy-MM-dd HH:mm:ss',), 'allowEmpty'=>true),
245 // array('startdate','date', 'format'=>array('yyyy-MM-dd', 'yyyy-MM-dd HH:mm', 'yyyy-MM-dd HH:mm:ss',), 'allowEmpty'=>true),
246 // array('datecreated','date', 'format'=>array('yyyy-MM-dd', 'yyyy-MM-dd HH:mm', 'yyyy-MM-dd HH:mm:ss',), 'allowEmpty'=>true),
247 );
248 }
249
250
251 /**
252 * fixSurveyAttribute to fix and/or add some survey attribute
253 * - Fix template name to be sure template exist
254 */
255 public function fixSurveyAttribute($event)
256 {
257 $this->template=Template::templateNameFilter($this->template);
258 }
259
260 /**
261 * filterTemplateSave to fix some template name
262 */
263 public function filterTemplateSave($sTemplateName)
264 {
265 if(!Permission::model()->hasTemplatePermission($sTemplateName))
266 {
267 if(!$this->isNewRecord)// Reset to default only if different from actual value
268 {
269 $oSurvey=self::model()->findByPk($this->sid);
270 if($oSurvey->template != $sTemplateName)// No need to test !is_null($oSurvey)
271 $sTemplateName = Yii::app()->getConfig('defaulttemplate');
272 }
273 else
274 {
275 $sTemplateName = Yii::app()->getConfig('defaulttemplate');
276 }
277 }
278 return Template::templateNameFilter($sTemplateName);
279 }
280
281 /**
282 * permission scope for this model
283 * Actually only test if user have minimal access to survey (read)
284 * @access public
285 * @param int $loginID
286 * @return CActiveRecord
287 *
288 * TODO: replace this by a correct relation
289 */
290 public function permission($loginID)
291 {
292 $loginID = Yii::app()->session['loginID'];
293 if(Permission::model()->hasGlobalPermission('surveys','read',$loginID))// Test global before adding criteria
294 return $this;
295 $criteria = $this->getDBCriteria();
296 $criteria->mergeWith(array(
297 'condition' => 'sid IN (SELECT entity_id FROM {{permissions}} WHERE entity = :entity AND uid = :uid AND permission = :permission AND read_p = 1)
298 OR owner_id = '.$loginID.' ',
299 ));
300 $criteria->params[':uid'] = $loginID;
301 $criteria->params[':permission'] = 'survey';
302 $criteria->params[':owner_id'] = $loginID;
303 $criteria->params[':entity'] = 'survey';
304
305 return $this;
306 }
307
308
309 /**
310 * Returns additional languages formatted into a string
311 *
312 * @access public
313 * @return array
314 */
315 public function getAdditionalLanguages()
316 {
317 $sLanguages = trim($this->additional_languages);
318 if ($sLanguages != '')
319 return explode(' ', $sLanguages);
320 else
321 return array();
322 }
323
324 /**
325 * Returns all languages array
326 *
327 * @access public
328 * @return array
329 */
330 public function getAllLanguages()
331 {
332 $sLanguages = self::getAdditionalLanguages();
333 $baselang=$this->language;
334 array_unshift($sLanguages,$baselang);
335 return $sLanguages;
336 }
337
338 /**
339 * Returns the additional token attributes
340 *
341 * @access public
342 * @return array
343 */
344 public function getTokenAttributes()
345 {
346 $attdescriptiondata = decodeTokenAttributes($this->attributedescriptions);
347 // checked for invalid data
348 if($attdescriptiondata == null)
349 {
350 return array();
351 }
352
353 // Catches malformed data
354 if ($attdescriptiondata && strpos(key(reset($attdescriptiondata)),'attribute_')===false)
355 {
356 // don't know why yet but this breaks normal tokenAttributes functionning
357 //$attdescriptiondata=array_flip(GetAttributeFieldNames($this->sid));
358 }
359 elseif (is_null($attdescriptiondata))
360 {
361 $attdescriptiondata=array();
362 }
363 // Legacy records support
364 if ($attdescriptiondata === false)
365 {
366 $attdescriptiondata = explode("\n", $this->attributedescriptions);
367 $fields = array();
368 $languagesettings = array();
369 foreach ($attdescriptiondata as $attdescription)
370 {
371 if (trim($attdescription) != '')
372 {
373 $fieldname = substr($attdescription, 0, strpos($attdescription, '='));
374 $desc = substr($attdescription, strpos($attdescription, '=') + 1);
375 $fields[$fieldname] = array(
376 'description' => $desc,
377 'mandatory' => 'N',
378 'show_register' => 'N',
379 'cpdbmap' =>''
380 );
381 $languagesettings[$fieldname] = $desc;
382 }
383 }
384 $ls = SurveyLanguageSetting::model()->findByAttributes(array('surveyls_survey_id' => $this->sid, 'surveyls_language' => $this->language,'owner_id'=>Yii::app()->session['loginID']));
385 self::model()->updateByPk($this->sid, array('attributedescriptions' => json_encode($fields)));
386 $ls->surveyls_attributecaptions = json_encode($languagesettings);
387 $ls->save();
388 $attdescriptiondata = $fields;
389 }
390 $aCompleteData=array();
391 foreach ($attdescriptiondata as $sKey=>$aValues)
392 {
393 if (!is_array($aValues)) $aValues=array();
394 if(preg_match("/^attribute_[0-9]{1,}$/",$sKey))
395 {
396 $aCompleteData[$sKey]= array_merge(array(
397 'description' => '',
398 'mandatory' => 'N',
399 'show_register' => 'N',
400 'cpdbmap' =>''
401 ),$aValues);
402 }
403 }
404 return $aCompleteData;
405 }
406
407 /**
408 * Returns true in a token table exists for the given $surveyId
409 *
410 * @staticvar array $tokens
411 * @param int $iSurveyID
412 * @return boolean
413 */
414 public function hasTokens($iSurveyID) {
415 static $tokens = array();
416 $iSurveyID = (int) $iSurveyID;
417
418 if (!isset($tokens[$iSurveyID])) {
419 // Make sure common_helper is loaded
420 Yii::import('application.helpers.common_helper', true);
421
422 $tokens_table = "{{tokens_{$iSurveyID}}}";
423 if (tableExists($tokens_table)) {
424 $tokens[$iSurveyID] = true;
425 } else {
426 $tokens[$iSurveyID] = false;
427 }
428 }
429
430 return $tokens[$iSurveyID];
431 }
432
433 public function getHasTokens()
434 {
435 $hasTokens = $this->hasTokens($this->sid) ;
436 if($hasTokens)
437 {
438 return gT('Yes');
439 }
440 else
441 {
442 return gT('No');
443 }
444 }
445
446 /**
447 * Returns the value for the SurveyEdit GoogleAnalytics API-Key UseGlobal Setting
448 *
449 */
450 public function getGoogleanalyticsapikeysetting(){
451 if($this->googleanalyticsapikey === "9999useGlobal9999")
452 {
453 return "G";
454 }
455 else if($this->googleanalyticsapikey == "")
456 {
457 return "N";
458 }
459 else
460 {
461 return "Y";
462 }
463 }
464 public function setGoogleanalyticsapikeysetting($value){
465 if($value == "G")
466 {
467 $this->googleanalyticsapikey = "9999useGlobal9999";
468 }
469 else if($value == "N")
470 {
471 $this->googleanalyticsapikey = "";
472 }
473 }
474
475 /**
476 * Returns the value for the SurveyEdit GoogleAnalytics API-Key UseGlobal Setting
477 *
478 */
479 public function getGoogleanalyticsapikey(){
480 if($this->googleanalyticsapikey === "9999useGlobal9999")
481 {
482 return getGlobalSetting(googleanalyticsapikey);
483 }
484 else
485 {
486 return $this->googleanalyticsapikey;
487 }
488 }
489
490
491
492 /**
493 * Creates a new survey - does some basic checks of the suppplied data
494 *
495 * @param array $aData Array with fieldname=>fieldcontents data
496 * @return integer The new survey id
497 */
498 public function insertNewSurvey($aData)
499 {
500 do
501 {
502 if (isset($aData['wishSID'])) // if wishSID is set check if it is not taken already
503 {
504 $aData['sid'] = $aData['wishSID'];
505 unset($aData['wishSID']);
506 }
507 else
508 $aData['sid'] = randomChars(6, '123456789');
509
510 $isresult = self::model()->findByPk($aData['sid']);
511 }
512 while (!is_null($isresult));
513
514 $survey = new self;
515 foreach ($aData as $k => $v)
516 $survey->$k = $v;
517 $sResult= $survey->save();
518
519 if (!$sResult)
520 {
521 tracevar($survey->getErrors());
522 tracevar($aData);
523 return false;
524 }
525 else return $aData['sid'];
526 }
527
528 /**
529 * Deletes a survey and all its data
530 *
531 * @access public
532 * @param int $iSurveyID
533 * @param bool @recursive
534 * @return void
535 */
536 public function deleteSurvey($iSurveyID, $recursive=true)
537 {
538
539 if (Permission::model()->hasSurveyPermission($iSurveyID, 'survey', 'delete'))
540 {
541 if ( Survey::model()->deleteAllByAttributes(array('sid' =>$iSurveyID,'owner_id' => Yii::app()->session['loginID'])))
542 {
543 if ($recursive == true)
544 {
545 if (tableExists("{{survey_".intval($iSurveyID)."}}")) //delete the survey_$iSurveyID table
546 {
547 Yii::app()->db->createCommand()->dropTable("{{survey_".intval($iSurveyID)."}}");
548 }
549
550 if (tableExists("{{survey_".intval($iSurveyID)."_timings}}")) //delete the survey_$iSurveyID_timings table
551 {
552 Yii::app()->db->createCommand()->dropTable("{{survey_".intval($iSurveyID)."_timings}}");
553 }
554
555 if (tableExists("{{tokens_".intval($iSurveyID)."}}")) //delete the tokens_$iSurveyID table
556 {
557 Yii::app()->db->createCommand()->dropTable("{{tokens_".intval($iSurveyID)."}}");
558 }
559
560 /* Remove User/global settings part : need Question and QuestionGroup*/
561 // Settings specific for this survey
562 $oCriteria = new CDbCriteria();
563 $oCriteria->compare('stg_name','last_%',true,'AND',false);
564 $oCriteria->compare('stg_value',$iSurveyID,false,'AND');
565 SettingGlobal::model()->deleteAll($oCriteria);
566 // Settings specific for this survey, 2nd part
567 $oCriteria = new CDbCriteria();
568 $oCriteria->compare('stg_name','last_%'.$iSurveyID.'%',true,'AND',false);
569 SettingGlobal::model()->deleteAll($oCriteria);
570 // All Group id from this survey for ALL users
571 $aGroupId=CHtml::listData(QuestionGroup::model()->findAll(array('select'=>'gid','condition'=>'sid=:sid','params'=>array(':sid'=>$iSurveyID))),'gid','gid');
572 $oCriteria = new CDbCriteria();
573 $oCriteria->compare('stg_name','last_question_gid_%',true,'AND',false);
574 if(Yii::app()->db->getDriverName() == 'pgsql') // pgsql need casting, unsure for mssql
575 {
576 $oCriteria->addInCondition('CAST(stg_value as '.App()->db->schema->getColumnType("integer").')',$aGroupId);
577 }
578 else //mysql App()->db->schema->getColumnType("integer") give int(11), mssql seems to have issue if cast alpha to numeric
579 {
580 $oCriteria->addInCondition('stg_value',$aGroupId);
581 }
582 SettingGlobal::model()->deleteAll($oCriteria);
583 // All Question id from this survey for ALL users
584 $aQuestionId=CHtml::listData(Question::model()->findAll(array('select'=>'qid','condition'=>'sid=:sid','params'=>array(':sid'=>$iSurveyID))),'qid','qid');
585 $oCriteria = new CDbCriteria();
586 $oCriteria->compare('stg_name','last_question_%',true,'OR',false);
587 if(Yii::app()->db->getDriverName() == 'pgsql')
588 {
589 $oCriteria->addInCondition('CAST(stg_value as '.App()->db->schema->getColumnType("integer").')',$aQuestionId);
590 }
591 else
592 {
593 $oCriteria->addInCondition('stg_value',$aQuestionId);
594 }
595 SettingGlobal::model()->deleteAll($oCriteria);
596
597 $oResult = Question::model()->findAllByAttributes(array('sid' => $iSurveyID));
598 foreach ($oResult as $aRow)
599 {
600 Answer::model()->deleteAllByAttributes(array('qid' => $aRow['qid']));
601 Condition::model()->deleteAllByAttributes(array('qid' =>$aRow['qid']));
602 QuestionAttribute::model()->deleteAllByAttributes(array('qid' => $aRow['qid']));
603 DefaultValue::model()->deleteAllByAttributes(array('qid' => $aRow['qid']));
604 }
605
606 Question::model()->deleteAllByAttributes(array('sid' => $iSurveyID));
607 Assessment::model()->deleteAllByAttributes(array('sid' => $iSurveyID));
608 QuestionGroup::model()->deleteAllByAttributes(array('sid' => $iSurveyID));
609 SurveyLanguageSetting::model()->deleteAllByAttributes(array('surveyls_survey_id' => $iSurveyID));
610 Permission::model()->deleteAllByAttributes(array('entity_id' => $iSurveyID, 'entity'=>'survey'));
611 SavedControl::model()->deleteAllByAttributes(array('sid' => $iSurveyID));
612 SurveyURLParameter::model()->deleteAllByAttributes(array('sid' => $iSurveyID));
613 //Remove any survey_links to the CPDB
614 SurveyLink::model()->deleteLinksBySurvey($iSurveyID);
615 Quota::model()->deleteQuota(array('sid' => $iSurveyID), true);
616 }
617 return true;
618 }
619 }
620 return false;
621 }
622
623 public function findByPk($pk, $condition = '', $params = array()) {
624 if (empty($condition) && empty($params)) {
625 if (array_key_exists($pk, $this->findByPkCache)) {
626 return $this->findByPkCache[$pk];
627 } else {
628 $result = parent::findByPk($pk, $condition, $params);
629 if (!is_null($result)) {
630 $this->findByPkCache[$pk] = $result;
631 }
632
633 return $result;
634 }
635 }
636
637 return parent::findByPk($pk, $condition, $params);
638 }
639
640 /**
641 * findByPk uses a cache to store a result. Use this method to force clearing that cache.
642 */
643 public function resetCache() {
644 $this->findByPkCache = array();
645 }
646
647 /**
648 * Attribute renamed to questionindex in dbversion 169
649 * Y maps to 1 otherwise 0;
650 * @param type $value
651 */
652 public function setAllowjumps($value)
653 {
654 if ($value === 'Y') {
655 $this->questionindex = 1;
656 } else {
657 $this->questionindex = 0;
658 }
659 }
660
661 public function getSurveyinfo()
662 {
663 $iSurveyID = $this->sid;
664 $baselang = $this->language;
665
666 $condition = array('sid' => $iSurveyID, 'language' => $baselang);
667
668 //// TODO : replace this with a HAS MANY relation !
669 $sumresult1 = Survey::model()->with(array('languagesettings'=>array('condition'=>'surveyls_language=language')))->find('sid = :surveyid', array(':surveyid' => $iSurveyID),array('owner_id' => Yii::app()->session['loginID'])); //$sumquery1, 1) ; //Checked
670 if (is_null($sumresult1))
671 {
672 Yii::app()->session['flashmessage'] = gT("Invalid survey ID");
673 $this->getController()->redirect(array("admin/index"));
674 } // if surveyid is invalid then die to prevent errors at a later time
675 $surveyinfo = $sumresult1->attributes;
676 $surveyinfo = array_merge($surveyinfo, $sumresult1->defaultlanguage->attributes);
677 $surveyinfo = array_map('flattenText', $surveyinfo);
678 //$surveyinfo["groups"] = $this->groups;
679 return $surveyinfo;
680 }
681
682
683 public function getCreationDate()
684 {
685 $dateformatdata=getDateFormatData(Yii::app()->session['dateformat']);
686 return convertDateTimeFormat($this->datecreated, 'Y-m-d', $dateformatdata['phpdate']);
687 }
688
689 public function getAnonymizedResponses()
690 {
691 $anonymizedResponses = ($this->anonymized == 'Y')?gT('Yes'):gT('No');
692 return $anonymizedResponses;
693 }
694
695 public function getActiveWord()
696 {
697 $activeword = ($this->active == 'Y')?gT('Yes'):gT('No');
698 return $activeword;
699 }
700
701 /**
702 * Get state of survey, which can be one of five:
703 * 1. Not active
704 * 2. Expired
705 * 3. Will expire in the future (running now)
706 * 3. Will run in future
707 * 4. Running now (no expiration date)
708 *
709 * Code copied from getRunning below.
710 *
711 * @return string - 'inactive', 'expired', 'willRun', 'willExpire' or 'running'
712 */
713 public function getState()
714 {
715 if($this->active == 'N')
716 {
717 return 'inactive';
718 }
719 elseif ($this->expires != '' || $this->startdate != '')
720 {
721 // Time adjust
722 $sNow = date("Y-m-d H:i:s", strtotime(Yii::app()->getConfig('timeadjust'), strtotime(date("Y-m-d H:i:s"))) );
723 $sStop = ($this->expires != '')?date("Y-m-d H:i:s", strtotime(Yii::app()->getConfig('timeadjust'), strtotime($this->expires)) ):$sNow;
724 $sStart = ($this->startdate != '')?date("Y-m-d H:i:s", strtotime(Yii::app()->getConfig('timeadjust'), strtotime($this->startdate)) ):$sNow;
725
726 // Time comparaison
727 $oNow = new DateTime($sNow);
728 $oStop = new DateTime($sStop);
729 $oStart = new DateTime($sStart);
730
731 $bExpired = ($oStop < $oNow);
732 $bWillRun = ($oStart > $oNow);
733
734 if ($bExpired)
735 {
736 return 'expired';
737 }
738 elseif ($bWillRun)
739 {
740 return 'willRun';
741 }
742 else
743 {
744 return 'willExpire';
745 }
746 }
747 // If it's active, and doesn't have expire date, it's running
748 else
749 {
750 return 'running';
751 }
752 }
753
754 /**
755 * @todo Document code, please.
756 */
757 public function getRunning()
758 {
759//DO MY CODE TO ENABLE OR DISABLE SURVEY IS CASH INSUFFICIENT
760 // If the survey is not active, no date test is needed
761 if($this->active == 'N')
762 {
763 $running = '<a href="'.App()->createUrl('/admin/survey/sa/view/surveyid/'.$this->sid).'" class="survey-state" data-toggle="tooltip" title="'.gT('Inactive').'"><span class="fa fa-stop text-warning"></span></a>';
764 }
765 // If it's active, then we check if not expired
766 elseif ($this->expires != '' || $this->startdate != '')
767 {
768 // Time adjust
769 $sNow = date("Y-m-d H:i:s", strtotime(Yii::app()->getConfig('timeadjust'), strtotime(date("Y-m-d H:i:s"))) );
770 $sStop = ($this->expires != '')?date("Y-m-d H:i:s", strtotime(Yii::app()->getConfig('timeadjust'), strtotime($this->expires)) ):$sNow;
771 $sStart = ($this->startdate != '')?date("Y-m-d H:i:s", strtotime(Yii::app()->getConfig('timeadjust'), strtotime($this->startdate)) ):$sNow;
772
773 // Time comparaison
774 $oNow = new DateTime($sNow);
775 $oStop = new DateTime($sStop);
776 $oStart = new DateTime($sStart);
777
778 $bExpired = ($oStop < $oNow);
779 $bWillRun = ($oStart > $oNow);
780
781 $sStop = convertToGlobalSettingFormat( $sStop );
782 $sStart = convertToGlobalSettingFormat( $sStart );
783
784 // Icon generaton (for CGridView)
785 $sIconRunning = '<a href="'.App()->createUrl('/admin/survey/sa/view/surveyid/'.$this->sid).'" class="survey-state" data-toggle="tooltip" title="'.gT('Expire').': '.$sStop.'"><span class="fa fa-clock-o text-success"></span></a>';
786 $sIconExpired = '<a href="'.App()->createUrl('/admin/survey/sa/view/surveyid/'.$this->sid).'" class="survey-state" data-toggle="tooltip" title="'.gT('Expired').': '.$sStop.'"><span class="fa fa fa-step-forward text-warning"></span></a>';
787 $sIconFuture = '<a href="'.App()->createUrl('/admin/survey/sa/view/surveyid/'.$this->sid).'" class="survey-state" data-toggle="tooltip" title="'.gT('Start').': '.$sStart.'"><span class="fa fa-clock-o text-warning"></span></a>';
788
789 // Icon parsing
790 if ( $bExpired || $bWillRun )
791 {
792 // Expire prior to will start
793 $running = ($bExpired)?$sIconExpired:$sIconFuture;
794 }
795 else
796 {
797 $running = $sIconRunning;
798 }
799 }
800 // If it's active, and doesn't have expire date, it's running
801 else
802 {
803 $running = '<a href="'.App()->createUrl('/admin/survey/sa/view/surveyid/'.$this->sid).'" class="survey-state" data-toggle="tooltip" title="'.gT('Active').'"><span class="fa fa-play text-success"></span></a>';
804 //$running = '<div class="survey-state"><span class="fa fa-play text-success"></span></div>';
805 }
806
807 return $running;
808
809 }
810
811 public function getPartialAnswers()
812 {
813 $table = '{{survey_' . $this->sid . '}}';
814 Yii::app()->cache->flush();
815 if (!Yii::app()->db->schema->getTable($table))
816 {
817 return null;
818 }
819 else
820 {
821 $answers = Yii::app()->db->createCommand()
822 ->select('*')
823 ->from($table)
824 ->where('submitdate IS NULL')
825 ->queryAll();
826
827 return $answers;
828 }
829 }
830
831 public function getIsActive()
832 {
833 return ($this->active === 'Y');
834 }
835
836 public function getFullAnswers()
837 {
838 $table = '{{survey_' . $this->sid . '}}';
839 Yii::app()->cache->flush();
840 if (!Yii::app()->db->schema->getTable($table))
841 {
842 return null;
843 }
844 else
845 {
846 $answers = Yii::app()->db->createCommand()
847 ->select('*')
848 ->from($table)
849 ->where('submitdate IS NOT NULL')
850 ->queryAll();
851
852 return $answers;
853 }
854 }
855
856 public function getCountFullAnswers()
857 {
858 if($this->fac!==null)
859 {
860 return $this->fac;
861 }
862 else
863 {
864 $sResponseTable = '{{survey_' . $this->sid . '}}';
865 Yii::app()->cache->flush();
866 if ($this->active!='Y')
867 {
868 $this->fac = 0;
869 return '0';
870 }
871 else
872 {
873 $answers = Yii::app()->db->createCommand('select count(*) from '.$sResponseTable.' where submitdate IS NOT NULL')->queryScalar();
874 $this->fac = $answers;
875 return $answers;
876 }
877 }
878 }
879
880 public function getCountPartialAnswers()
881 {
882 if($this->pac!==null)
883 {
884 return $this->pac;
885 }
886 else
887 {
888 $table = '{{survey_' . $this->sid . '}}';
889 Yii::app()->cache->flush();
890 if ($this->active!='Y')
891 {
892 $this->pac = 0;
893 return 0;
894 }
895 else
896 {
897 $answers = Yii::app()->db->createCommand('select count(*) from '.$table.' where submitdate IS NULL')->queryScalar();
898 $this->pac = $answers;
899 return $answers;
900 }
901 }
902 }
903
904 public function getCountTotalAnswers()
905 {
906 if ($this->pac!==null && $this->fac!==null)
907 {
908 return ($this->pac + $this->fac);
909 }
910 else
911 {
912 return ($this->countFullAnswers + $this->countPartialAnswers);
913 }
914 }
915
916 public function getbuttons()
917 {
918 $sSummaryUrl = App()->createUrl("/admin/survey/sa/view/surveyid/".$this->sid);
919 $sEditUrl = App()->createUrl("/admin/survey/sa/editlocalsettings/surveyid/".$this->sid);
920 $sDeleteUrl = App()->createUrl("/admin/survey/sa/delete/surveyid/".$this->sid);
921 $sStatUrl = App()->createUrl("/admin/statistics/sa/simpleStatistics/surveyid/".$this->sid);
922 $sAddGroup = App()->createUrl("/admin/questiongroups/sa/add/surveyid/".$this->sid);;
923 $sAddquestion = App()->createUrl("/admin/questions/sa/newquestion/surveyid/".$this->sid);;
924
925 $button = '';
926
927 if (Permission::model()->hasSurveyPermission($this->sid, 'survey', 'update'))
928 {
929 $button .= '<a class="btn btn-default" href="'.$sEditUrl.'" role="button" data-toggle="tooltip" title="'.gT('Edit Survey').'"> <span class="glyphicon glyphicon-pencil"></span></a>';
930 }
931
932 if(Permission::model()->hasSurveyPermission($this->sid, 'statistics', 'read') && $this->active=='Y' )
933 {
934 $button .= '<a class="btn btn-default" href="'.$sStatUrl.'" role="button" data-toggle="tooltip" title="'.gT('Statistics').'"><span class="glyphicon glyphicon-stats text-success" ></span></a>';
935 }
936
937 if (Permission::model()->hasSurveyPermission($this->sid, 'survey', 'create'))
938 {
939 if($this->active!='Y')
940 {
941 $groupCount = QuestionGroup::model()->countByAttributes(array('sid' => $this->sid, 'language' => $this->language)); //Checked
942 if($groupCount > 0)
943 {
944 $button .= '<a class="btn btn-default" href="'.$sAddquestion.'" role="button" data-toggle="tooltip" title="'.gT('Add new question').'"><span class="icon-add text-success" ></span></a>';
945 }
946 else
947 {
948 $button .= '<a class="btn btn-default" href="'.$sAddGroup.'" role="button" data-toggle="tooltip" title="'.gT('Add new group').'"><span class="icon-add text-success" ></span></a>';
949 }
950 }
951 }
952
953 $previewUrl = Yii::app()->createUrl("survey/index/sid/");
954 $previewUrl .= '/'.$this->sid;
955
956 //$button = '<a class="btn btn-default open-preview" aria-data-url="'.$previewUrl.'" aria-data-language="'.$this->language.'" href="# role="button" ><span class="glyphicon glyphicon-eye-open" ></span></a> ';
957
958 return $button;
959 }
960
961 public function search($id)
962 {
963 $pageSize=Yii::app()->user->getState('pageSize',Yii::app()->params['defaultPageSize']);
964
965 $sort = new CSort();
966 $sort->attributes = array(
967 'survey_id'=>array(
968 'asc'=>'t.sid asc',
969 'desc'=>'t.sid desc',
970
971 ),
972 'title'=>array(
973 'asc'=>'correct_relation_defaultlanguage.surveyls_title asc',
974 'desc'=>'correct_relation_defaultlanguage.surveyls_title desc',
975 ),
976
977 'creation_date'=>array(
978 'asc'=>'t.datecreated asc',
979 'desc'=>'t.datecreated desc',
980 ),
981
982 'owner'=>array(
983 'asc'=>'owner.users_name asc',
984 'desc'=>'owner.users_name desc',
985 ),
986
987 'anonymized_responses'=>array(
988 'asc'=>'t.anonymized asc',
989 'desc'=>'t.anonymized desc',
990 ),
991
992 'running'=>array(
993 'asc'=>'t.active asc, t.expires asc',
994 'desc'=>'t.active desc, t.expires desc',
995 ),
996 'owner_id'=>array(
997 'owner_id'=>$id,
998
999 ),
1000
1001 );
1002 $sort->defaultOrder = array('creation_date' => CSort::SORT_DESC);
1003
1004 $criteria = new CDbCriteria;
1005 $aWithRelations = array('correct_relation_defaultlanguage');
1006
1007 // Search filter
1008 $sid_reference = (Yii::app()->db->getDriverName() == 'pgsql' ?' t.sid::varchar' : 't.sid');
1009 $aWithRelations[] = 'owner';
1010 $criteria->compare($sid_reference, $this->searched_value, true);
1011 $criteria->compare('t.admin', $this->searched_value, true, 'OR');
1012 $criteria->compare('owner.users_name', $this->searched_value, true, 'OR');
1013 $criteria->compare('owner_id', $id, true, 'OR');
1014 $criteria->compare('correct_relation_defaultlanguage.surveyls_title', $this->searched_value, true, 'OR');
1015
1016
1017
1018 // Active filter
1019 if(isset($this->active))
1020 {
1021 if($this->active == 'N' || $this->active == "Y")
1022 {
1023 $criteria->compare("t.active", $this->active, false);
1024 }
1025 else
1026 {
1027 // Time adjust
1028 $sNow = date("Y-m-d H:i:s", strtotime(Yii::app()->getConfig('timeadjust'), strtotime(date("Y-m-d H:i:s"))) );
1029
1030 if($this->active == "E")
1031 {
1032 $criteria->compare("t.active",'Y');
1033 $criteria->addCondition("t.expires <'$sNow'");
1034 }
1035 if($this->active == "S")
1036 {
1037 $criteria->compare("t.active",'Y');
1038 $criteria->addCondition("t.startdate >'$sNow'");
1039 }
1040 if($this->active == "R")
1041 {
1042 $now = new CDbExpression("NOW()");
1043
1044 $criteria->compare("t.active",'Y');
1045 $subCriteria1 = new CDbCriteria;
1046 $subCriteria2 = new CDbCriteria;
1047 $subCriteria1->addCondition($now.' > t.startdate', 'OR');
1048 $subCriteria2->addCondition($now.' < t.expires', 'OR');
1049 $subCriteria1->addCondition('t.expires IS NULL', "OR");
1050 $subCriteria2->addCondition('t.startdate IS NULL', "OR");
1051 $criteria->mergeWith($subCriteria1);
1052 $criteria->mergeWith($subCriteria2);
1053 }
1054 }
1055 }
1056
1057
1058 $criteria->with=$aWithRelations;
1059
1060 // Permission
1061 // Note: reflect Permission::hasPermission
1062 if(!Permission::model()->hasGlobalPermission("surveys",'read'))
1063 {
1064 $criteriaPerm = new CDbCriteria;
1065
1066 // Multiple ON conditions with string values such as 'survey'
1067 $criteriaPerm->mergeWith(array(
1068 'join'=>"LEFT JOIN {{permissions}} AS permissions ON (permissions.entity_id = t.sid AND permissions.permission='survey' AND permissions.entity='survey' AND permissions.uid='".Yii::app()->user->id."') ",
1069 ));
1070 $criteriaPerm->compare('t.owner_id', Yii::app()->user->id, false);
1071 $criteriaPerm->compare('permissions.read_p', '1', false, 'OR');
1072 $criteria->mergeWith($criteriaPerm, 'AND');
1073 }
1074 // $criteria->addCondition("t.blabla == 'blub'");
1075 $dataProvider=new CActiveDataProvider('Survey', array(
1076 'sort'=>$sort,
1077 'criteria'=>$criteria,
1078 'pagination'=>array(
1079 'pageSize'=>$pageSize,
1080 ),
1081 ));
1082
1083 $dataProvider->setTotalItemCount($this->count($criteria));
1084
1085 return $dataProvider;
1086 }
1087
1088 /**
1089 * Transcribe from 3 checkboxes to 1 char for captcha usages
1090 * Uses variables from $_POST
1091 *
1092 * 'A' = All three captcha enabled
1093 * 'B' = All but save and load
1094 * 'C' = All but registration
1095 * 'D' = All but survey access
1096 * 'X' = Only survey access
1097 * 'R' = Only registration
1098 * 'S' = Only save and load
1099 * 'N' = None
1100 *
1101 * @return string One character that corresponds to captcha usage
1102 * @todo Should really be saved as three fields in the database!
1103 */
1104 public static function transcribeCaptchaOptions() {
1105 $surveyaccess = App()->request->getPost('usecaptcha_surveyaccess');
1106 $registration = App()->request->getPost('usecaptcha_registration');
1107 $saveandload = App()->request->getPost('usecaptcha_saveandload');
1108
1109 if ($surveyaccess && $registration && $saveandload)
1110 {
1111 return 'A';
1112 }
1113 elseif ($surveyaccess && $registration)
1114 {
1115 return 'B';
1116 }
1117 elseif ($surveyaccess && $saveandload)
1118 {
1119 return 'C';
1120 }
1121 elseif ($registration && $saveandload)
1122 {
1123 return 'D';
1124 }
1125 elseif ($surveyaccess)
1126 {
1127 return 'X';
1128 }
1129 elseif ($registration)
1130 {
1131 return 'R';
1132 }
1133 elseif ($saveandload)
1134 {
1135 return 'S';
1136 }
1137
1138 return 'N';
1139 }
1140
1141 /**
1142 * Method to make an approximation on how long a survey will last
1143 * Approx is 3 questions each minute.
1144 * @return int
1145 */
1146 public function calculateEstimatedTime ()
1147 {
1148 //@TODO make the time_per_question variable user configureable
1149 $time_per_question = 0.5;
1150 $criteria = new CDbCriteria();
1151 $criteria->addCondition('sid = ' . $this->sid);
1152 $criteria->addCondition('parent_qid = 0');
1153 $criteria->addCondition('language = \'' . $this->language . '\'');
1154 $baseQuestions = Question::model()->count($criteria);
1155 // Note: An array questions with one sub question is fetched as 1 base question + 1 sub question
1156 $criteria = new CDbCriteria();
1157 $criteria->addCondition('sid = ' . $this->sid);
1158 $criteria->addCondition('parent_qid != 0');
1159 $criteria->addCondition('language = \'' . $this->language . '\'');
1160 $subQuestions = Question::model()->count($criteria);
1161 // Subquestions are worth less "time" than base questions
1162 $subQuestions = intval(($subQuestions - $baseQuestions) / 2);
1163 $subQuestions = $subQuestions < 0 ? 0 : $subQuestions;
1164 return ceil(($subQuestions + $baseQuestions)*$time_per_question);
1165 }
1166}