PageRenderTime 119ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 1ms

/application/helpers/common_helper.php

https://bitbucket.org/machaven/limesurvey
PHP | 7809 lines | 5988 code | 657 blank | 1164 comment | 801 complexity | b1dd78b5b367b1d4d436ad4ec9c009dc MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, BSD-3-Clause, GPL-3.0, LGPL-3.0
  1. <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
  2. /*
  3. * LimeSurvey
  4. * Copyright (C) 2007-2011 The LimeSurvey Project Team / Carsten Schmitz
  5. * All rights reserved.
  6. * License: GNU/GPL License v2 or later, see LICENSE.php
  7. * LimeSurvey is free software. This version may have been modified pursuant
  8. * to the GNU General Public License, and as distributed it includes or
  9. * is derivative of works licensed under the GNU General Public License or
  10. * other free or open source software licenses.
  11. * See COPYRIGHT.php for copyright notices and details.
  12. */
  13. Yii::import('application.helpers.sanitize_helper', true);
  14. /**
  15. * Simple function to sort the permissions by title
  16. *
  17. * @param mixed $aPermissionA Permission A to compare
  18. * @param mixed $aPermissionB Permission B to compare
  19. */
  20. function comparePermission($aPermissionA,$aPermissionB)
  21. {
  22. if($aPermissionA['title'] >$aPermissionB['title']) {
  23. return 1;
  24. }
  25. else {
  26. return -1;
  27. }
  28. }
  29. /**
  30. * getQuestionTypeList() Returns list of question types available in LimeSurvey. Edit this if you are adding a new
  31. * question type
  32. *
  33. * @param string $SelectedCode Value of the Question Type (defaults to "T")
  34. * @param string $ReturnType Type of output from this function (defaults to selector)
  35. *
  36. * @return depending on $ReturnType param, returns a straight "array" of question types, or an <option></option> list
  37. *
  38. * Explanation of questiontype array:
  39. *
  40. * description : Question description
  41. * subquestions : 0= Does not support subquestions x=Number of subquestion scales
  42. * answerscales : 0= Does not need answers x=Number of answer scales (usually 1, but e.g. for dual scale question set to 2)
  43. * assessable : 0=Does not support assessment values when editing answerd 1=Support assessment values
  44. */
  45. function getQuestionTypeList($SelectedCode = "T", $ReturnType = "selector")
  46. {
  47. $publicurl = Yii::app()->getConfig('publicurl');
  48. $clang = Yii::app()->lang;
  49. $group['Arrays'] = $clang->gT('Arrays');
  50. $group['MaskQuestions'] = $clang->gT("Mask questions");
  51. $group['SinChoiceQues'] = $clang->gT("Single choice questions");
  52. $group['MulChoiceQues'] = $clang->gT("Multiple choice questions");
  53. $group['TextQuestions'] = $clang->gT("Text questions");
  54. $qtypes = array(
  55. "1" => array('description' => $clang->gT("Array dual scale"),
  56. 'group' => $group['Arrays'],
  57. 'subquestions' => 1,
  58. 'assessable' => 1,
  59. 'hasdefaultvalues' => 0,
  60. 'answerscales' => 2),
  61. "5" => array('description' => $clang->gT("5 Point Choice"),
  62. 'group' => $group['SinChoiceQues'],
  63. 'subquestions' => 0,
  64. 'hasdefaultvalues' => 0,
  65. 'assessable' => 0,
  66. 'answerscales' => 0),
  67. "A" => array('description' => $clang->gT("Array (5 Point Choice)"),
  68. 'group' => $group['Arrays'],
  69. 'subquestions' => 1,
  70. 'hasdefaultvalues' => 0,
  71. 'assessable' => 1,
  72. 'answerscales' => 0),
  73. "B" => array('description' => $clang->gT("Array (10 Point Choice)"),
  74. 'group' => $group['Arrays'],
  75. 'subquestions' => 1,
  76. 'hasdefaultvalues' => 0,
  77. 'assessable' => 1,
  78. 'answerscales' => 0),
  79. "C" => array('description' => $clang->gT("Array (Yes/No/Uncertain)"),
  80. 'group' => $group['Arrays'],
  81. 'subquestions' => 1,
  82. 'hasdefaultvalues' => 0,
  83. 'assessable' => 1,
  84. 'answerscales' => 0),
  85. "D" => array('description' => $clang->gT("Date/Time"),
  86. 'group' => $group['MaskQuestions'],
  87. 'subquestions' => 0,
  88. 'hasdefaultvalues' => 1,
  89. 'assessable' => 0,
  90. 'answerscales' => 0),
  91. "E" => array('description' => $clang->gT("Array (Increase/Same/Decrease)"),
  92. 'group' => $group['Arrays'],
  93. 'subquestions' => 1,
  94. 'hasdefaultvalues' => 0,
  95. 'assessable' => 1,
  96. 'answerscales' => 0),
  97. "F" => array('description' => $clang->gT("Array"),
  98. 'group' => $group['Arrays'],
  99. 'subquestions' => 1,
  100. 'hasdefaultvalues' => 0,
  101. 'assessable' => 1,
  102. 'answerscales' => 1),
  103. "G" => array('description' => $clang->gT("Gender"),
  104. 'group' => $group['MaskQuestions'],
  105. 'subquestions' => 0,
  106. 'hasdefaultvalues' => 0,
  107. 'assessable' => 0,
  108. 'answerscales' => 0),
  109. "H" => array('description' => $clang->gT("Array by column"),
  110. 'group' => $group['Arrays'],
  111. 'hasdefaultvalues' => 0,
  112. 'subquestions' => 1,
  113. 'assessable' => 1,
  114. 'answerscales' => 1),
  115. "I" => array('description' => $clang->gT("Language Switch"),
  116. 'group' => $group['MaskQuestions'],
  117. 'hasdefaultvalues' => 0,
  118. 'subquestions' => 0,
  119. 'assessable' => 0,
  120. 'answerscales' => 0),
  121. "K" => array('description' => $clang->gT("Multiple Numerical Input"),
  122. 'group' => $group['MaskQuestions'],
  123. 'hasdefaultvalues' => 1,
  124. 'subquestions' => 1,
  125. 'assessable' => 1,
  126. 'answerscales' => 0),
  127. "L" => array('description' => $clang->gT("List (Radio)"),
  128. 'group' => $group['SinChoiceQues'],
  129. 'subquestions' => 0,
  130. 'hasdefaultvalues' => 1,
  131. 'assessable' => 1,
  132. 'answerscales' => 1),
  133. "M" => array('description' => $clang->gT("Multiple choice"),
  134. 'group' => $group['MulChoiceQues'],
  135. 'subquestions' => 1,
  136. 'hasdefaultvalues' => 1,
  137. 'assessable' => 1,
  138. 'answerscales' => 0),
  139. "N" => array('description' => $clang->gT("Numerical Input"),
  140. 'group' => $group['MaskQuestions'],
  141. 'subquestions' => 0,
  142. 'hasdefaultvalues' => 1,
  143. 'assessable' => 0,
  144. 'answerscales' => 0),
  145. "O" => array('description' => $clang->gT("List with comment"),
  146. 'group' => $group['SinChoiceQues'],
  147. 'subquestions' => 0,
  148. 'hasdefaultvalues' => 1,
  149. 'assessable' => 1,
  150. 'answerscales' => 1),
  151. "P" => array('description' => $clang->gT("Multiple choice with comments"),
  152. 'group' => $group['MulChoiceQues'],
  153. 'subquestions' => 1,
  154. 'hasdefaultvalues' => 1,
  155. 'assessable' => 1,
  156. 'answerscales' => 0),
  157. "Q" => array('description' => $clang->gT("Multiple Short Text"),
  158. 'group' => $group['TextQuestions'],
  159. 'subquestions' => 1,
  160. 'hasdefaultvalues' => 1,
  161. 'assessable' => 0,
  162. 'answerscales' => 0),
  163. "R" => array('description' => $clang->gT("Ranking"),
  164. 'group' => $group['MaskQuestions'],
  165. 'subquestions' => 0,
  166. 'hasdefaultvalues' => 0,
  167. 'assessable' => 1,
  168. 'answerscales' => 1),
  169. "S" => array('description' => $clang->gT("Short Free Text"),
  170. 'group' => $group['TextQuestions'],
  171. 'subquestions' => 0,
  172. 'hasdefaultvalues' => 1,
  173. 'assessable' => 0,
  174. 'answerscales' => 0),
  175. "T" => array('description' => $clang->gT("Long Free Text"),
  176. 'group' => $group['TextQuestions'],
  177. 'subquestions' => 0,
  178. 'hasdefaultvalues' => 1,
  179. 'assessable' => 0,
  180. 'answerscales' => 0),
  181. "U" => array('description' => $clang->gT("Huge Free Text"),
  182. 'group' => $group['TextQuestions'],
  183. 'subquestions' => 0,
  184. 'hasdefaultvalues' => 1,
  185. 'assessable' => 0,
  186. 'answerscales' => 0),
  187. "X" => array('description' => $clang->gT("Text display"),
  188. 'group' => $group['MaskQuestions'],
  189. 'subquestions' => 0,
  190. 'hasdefaultvalues' => 0,
  191. 'assessable' => 0,
  192. 'answerscales' => 0),
  193. "Y" => array('description' => $clang->gT("Yes/No"),
  194. 'group' => $group['MaskQuestions'],
  195. 'subquestions' => 0,
  196. 'hasdefaultvalues' => 0,
  197. 'assessable' => 0,
  198. 'answerscales' => 0),
  199. "!" => array('description' => $clang->gT("List (Dropdown)"),
  200. 'group' => $group['SinChoiceQues'],
  201. 'subquestions' => 0,
  202. 'hasdefaultvalues' => 1,
  203. 'assessable' => 1,
  204. 'answerscales' => 1),
  205. ":" => array('description' => $clang->gT("Array (Numbers)"),
  206. 'group' => $group['Arrays'],
  207. 'subquestions' => 2,
  208. 'hasdefaultvalues' => 0,
  209. 'assessable' => 1,
  210. 'answerscales' => 0),
  211. ";" => array('description' => $clang->gT("Array (Texts)"),
  212. 'group' => $group['Arrays'],
  213. 'subquestions' => 2,
  214. 'hasdefaultvalues' => 0,
  215. 'assessable' => 0,
  216. 'answerscales' => 0),
  217. "|" => array('description' => $clang->gT("File upload"),
  218. 'group' => $group['MaskQuestions'],
  219. 'subquestions' => 0,
  220. 'hasdefaultvalues' => 0,
  221. 'assessable' => 0,
  222. 'answerscales' => 0),
  223. "*" => array('description' => $clang->gT("Equation"),
  224. 'group' => $group['MaskQuestions'],
  225. 'subquestions' => 0,
  226. 'hasdefaultvalues' => 0,
  227. 'assessable' => 0,
  228. 'answerscales' => 0),
  229. );
  230. asort($qtypes);
  231. if ($ReturnType == "array")
  232. return $qtypes;
  233. if ($ReturnType == "group")
  234. {
  235. foreach ($qtypes as $qkey => $qtype)
  236. {
  237. $newqType[$qtype['group']][$qkey] = $qtype;
  238. }
  239. $qtypeselecter = "";
  240. foreach ($newqType as $group => $members)
  241. {
  242. $qtypeselecter .= '<optgroup label="' . $group . '">';
  243. foreach ($members as $TypeCode => $TypeProperties)
  244. {
  245. $qtypeselecter .= "<option value='$TypeCode'";
  246. if ($SelectedCode == $TypeCode)
  247. {
  248. $qtypeselecter .= " selected='selected'";
  249. }
  250. $qtypeselecter .= ">{$TypeProperties['description']}</option>\n";
  251. }
  252. $qtypeselecter .= '</optgroup>';
  253. }
  254. return $qtypeselecter;
  255. };
  256. $qtypeselecter = "";
  257. foreach ($qtypes as $TypeCode => $TypeProperties)
  258. {
  259. $qtypeselecter .= "<option value='$TypeCode'";
  260. if ($SelectedCode == $TypeCode)
  261. {
  262. $qtypeselecter .= " selected='selected'";
  263. }
  264. $qtypeselecter .= ">{$TypeProperties['description']}</option>\n";
  265. }
  266. return $qtypeselecter;
  267. }
  268. /**
  269. * isStandardTemplate returns true if a template is a standard template
  270. * This function does not check if a template actually exists
  271. *
  272. * @param mixed $sTemplateName template name to look for
  273. * @return bool True if standard template, otherwise false
  274. */
  275. function isStandardTemplate($sTemplateName)
  276. {
  277. return in_array($sTemplateName,array('basic',
  278. 'bluengrey',
  279. 'business_grey',
  280. 'citronade',
  281. 'clear_logo',
  282. 'default',
  283. 'eirenicon',
  284. 'limespired',
  285. 'mint_idea',
  286. 'sherpa',
  287. 'vallendar'));
  288. }
  289. /**
  290. * getSurveyList() Queries the database (survey table) for a list of existing surveys
  291. *
  292. * @param boolean $returnarray if set to true an array instead of an HTML option list is given back
  293. * @return string This string is returned containing <option></option> formatted list of existing surveys
  294. *
  295. */
  296. function getSurveyList($returnarray=false, $surveyid=false)
  297. {
  298. static $cached = null;
  299. $timeadjust = getGlobalSetting('timeadjust');
  300. $clang = new Limesurvey_lang(Yii::app()->session['adminlang']);
  301. if(is_null($cached)) {
  302. if (!hasGlobalPermission('USER_RIGHT_SUPERADMIN'))
  303. $surveyidresult = Survey::model()->permission(Yii::app()->user->getId())->with(array('languagesettings'=>array('condition'=>'surveyls_language=language')))->findAll();
  304. else
  305. $surveyidresult = Survey::model()->with(array('languagesettings'=>array('condition'=>'surveyls_language=language')))->findAll();
  306. $surveynames = array();
  307. foreach ($surveyidresult as $result)
  308. {
  309. $surveynames[] = array_merge($result->attributes, $result->languagesettings[0]->attributes);
  310. }
  311. $cached = $surveynames;
  312. } else {
  313. $surveynames = $cached;
  314. }
  315. $surveyselecter = "";
  316. if ($returnarray===true) return $surveynames;
  317. $activesurveys='';
  318. $inactivesurveys='';
  319. $expiredsurveys='';
  320. if ($surveynames)
  321. {
  322. foreach($surveynames as $sv)
  323. {
  324. $surveylstitle=flattenText($sv['surveyls_title']);
  325. if (strlen($surveylstitle)>45)
  326. {
  327. $surveylstitle = htmlspecialchars(mb_strcut(html_entity_decode($surveylstitle,ENT_QUOTES,'UTF-8'), 0, 45, 'UTF-8'))."...";
  328. }
  329. if($sv['active']!='Y')
  330. {
  331. $inactivesurveys .= "<option ";
  332. if(Yii::app()->user->getId() == $sv['owner_id'])
  333. {
  334. $inactivesurveys .= " style=\"font-weight: bold;\"";
  335. }
  336. if ($sv['sid'] == $surveyid)
  337. {
  338. $inactivesurveys .= " selected='selected'"; $svexist = 1;
  339. }
  340. $inactivesurveys .=" value='{$sv['sid']}'>{$surveylstitle}</option>\n";
  341. } elseif($sv['expires']!='' && $sv['expires'] < dateShift(date("Y-m-d H:i:s"), "Y-m-d H:i:s", $timeadjust))
  342. {
  343. $expiredsurveys .="<option ";
  344. if (Yii::app()->user->getId() == $sv['owner_id'])
  345. {
  346. $expiredsurveys .= " style=\"font-weight: bold;\"";
  347. }
  348. if ($sv['sid'] == $surveyid)
  349. {
  350. $expiredsurveys .= " selected='selected'"; $svexist = 1;
  351. }
  352. $expiredsurveys .=" value='{$sv['sid']}'>{$surveylstitle}</option>\n";
  353. } else
  354. {
  355. $activesurveys .= "<option ";
  356. if(Yii::app()->user->getId() == $sv['owner_id'])
  357. {
  358. $activesurveys .= " style=\"font-weight: bold;\"";
  359. }
  360. if ($sv['sid'] == $surveyid)
  361. {
  362. $activesurveys .= " selected='selected'"; $svexist = 1;
  363. }
  364. $activesurveys .=" value='{$sv['sid']}'>{$surveylstitle}</option>\n";
  365. }
  366. } // End Foreach
  367. }
  368. //Only show each activesurvey group if there are some
  369. if ($activesurveys!='')
  370. {
  371. $surveyselecter .= "<optgroup label='".$clang->gT("Active")."' class='activesurveyselect'>\n";
  372. $surveyselecter .= $activesurveys . "</optgroup>";
  373. }
  374. if ($expiredsurveys!='')
  375. {
  376. $surveyselecter .= "<optgroup label='".$clang->gT("Expired")."' class='expiredsurveyselect'>\n";
  377. $surveyselecter .= $expiredsurveys . "</optgroup>";
  378. }
  379. if ($inactivesurveys!='')
  380. {
  381. $surveyselecter .= "<optgroup label='".$clang->gT("Inactive")."' class='inactivesurveyselect'>\n";
  382. $surveyselecter .= $inactivesurveys . "</optgroup>";
  383. }
  384. if (!isset($svexist))
  385. {
  386. $surveyselecter = "<option selected='selected' value=''>".$clang->gT("Please choose...")."</option>\n".$surveyselecter;
  387. } else
  388. {
  389. $surveyselecter = "<option value=''>".$clang->gT("None")."</option>\n".$surveyselecter;
  390. }
  391. return $surveyselecter;
  392. }
  393. /**
  394. * Returns true if a user has permissions in the particular survey
  395. *
  396. * @param $iSID The survey ID
  397. * @param $sPermission
  398. * @param $sCRUD
  399. * @param $iUID User ID - if not given the one of the current user is used
  400. * @return bool
  401. */
  402. function hasSurveyPermission($iSID, $sPermission, $sCRUD, $iUID=null)
  403. {
  404. if (!in_array($sCRUD,array('create','read','update','delete','import','export'))) return false;
  405. $sCRUD=$sCRUD.'_p';
  406. $thissurvey=getSurveyInfo($iSID);
  407. if (!$thissurvey) return false;
  408. $aSurveyPermissionCache = Yii::app()->getConfig("aSurveyPermissionCache");
  409. if (is_null($iUID))
  410. {
  411. if (!Yii::app()->user->getIsGuest()) $iUID = Yii::app()->session['loginID'];
  412. else return false;
  413. // Some user have acces to whole survey settings
  414. if (Yii::app()->session['USER_RIGHT_SUPERADMIN']==1) return true;
  415. if ($thissurvey && $iUID==$thissurvey['owner_id']) return true;
  416. }
  417. if (!isset($aSurveyPermissionCache[$iSID][$iUID][$sPermission][$sCRUD]))
  418. {
  419. $query = Survey_permissions::model()->findByAttributes(array("sid"=> $iSID,"uid"=> $iUID,"permission"=>$sPermission));
  420. $bPermission = is_null($query) ? array() : $query->attributes;
  421. if (!isset($bPermission[$sCRUD]) || $bPermission[$sCRUD]==0)
  422. {
  423. $bPermission=false;
  424. }
  425. else
  426. {
  427. $bPermission=true;
  428. }
  429. $aSurveyPermissionCache[$iSID][$iUID][$sPermission][$sCRUD]=$bPermission;
  430. }
  431. Yii::app()->setConfig("aSurveyPermissionCache", $aSurveyPermissionCache);
  432. return $aSurveyPermissionCache[$iSID][$iUID][$sPermission][$sCRUD];
  433. }
  434. /**
  435. * Returns true if a user has global permission for a certain action. Available permissions are
  436. *
  437. * USER_RIGHT_CREATE_SURVEY
  438. * USER_RIGHT_CONFIGURATOR
  439. * USER_RIGHT_CREATE_USER
  440. * USER_RIGHT_DELETE_USER
  441. * USER_RIGHT_SUPERADMIN
  442. * USER_RIGHT_MANAGE_TEMPLATE
  443. * USER_RIGHT_MANAGE_LABEL
  444. *
  445. * @param $sPermission
  446. * @return bool
  447. */
  448. function hasGlobalPermission($sPermission)
  449. {
  450. if (!Yii::app()->user->getIsGuest()) $iUID = !Yii::app()->user->getId();
  451. else return false;
  452. if (Yii::app()->session['USER_RIGHT_SUPERADMIN']==1) return true; //Superadmin has access to all
  453. if (Yii::app()->session[$sPermission]==1)
  454. {
  455. return true;
  456. }
  457. else
  458. {
  459. return false;
  460. }
  461. }
  462. function getTemplateList()
  463. {
  464. $usertemplaterootdir=Yii::app()->getConfig("usertemplaterootdir");
  465. $standardtemplaterootdir=Yii::app()->getConfig("standardtemplaterootdir");
  466. if (!$usertemplaterootdir) {die("getTemplateList() no template directory");}
  467. if ($handle = opendir($standardtemplaterootdir))
  468. {
  469. while (false !== ($file = readdir($handle)))
  470. {
  471. if (!is_file("$standardtemplaterootdir/$file") && $file != "." && $file != ".." && $file!=".svn" && isStandardTemplate($file))
  472. {
  473. $list_of_files[$file] = $standardtemplaterootdir.DIRECTORY_SEPARATOR.$file;
  474. }
  475. }
  476. closedir($handle);
  477. }
  478. if ($handle = opendir($usertemplaterootdir))
  479. {
  480. while (false !== ($file = readdir($handle)))
  481. {
  482. if (!is_file("$usertemplaterootdir/$file") && $file != "." && $file != ".." && $file!=".svn")
  483. {
  484. $list_of_files[$file] = $usertemplaterootdir.DIRECTORY_SEPARATOR.$file;
  485. }
  486. }
  487. closedir($handle);
  488. }
  489. ksort($list_of_files);
  490. return $list_of_files;
  491. }
  492. function getAdminThemeList()
  493. {
  494. // $usertemplaterootdir=Yii::app()->getConfig("usertemplaterootdir");
  495. $standardtemplaterootdir=Yii::app()->getConfig("styledir");
  496. // if (!$usertemplaterootdir) {die("getTemplateList() no template directory");}
  497. if ($handle = opendir($standardtemplaterootdir))
  498. {
  499. while (false !== ($file = readdir($handle)))
  500. {
  501. if (!is_file("$standardtemplaterootdir/$file") && $file != "." && $file != ".." && $file!=".svn")
  502. {
  503. $list_of_files[$file] = $standardtemplaterootdir.DIRECTORY_SEPARATOR.$file;
  504. }
  505. }
  506. closedir($handle);
  507. }
  508. /* if ($handle = opendir($usertemplaterootdir))
  509. {
  510. while (false !== ($file = readdir($handle)))
  511. {
  512. if (!is_file("$usertemplaterootdir/$file") && $file != "." && $file != ".." && $file!=".svn")
  513. {
  514. $list_of_files[$file] = $usertemplaterootdir.DIRECTORY_SEPARATOR.$file;
  515. }
  516. }
  517. closedir($handle);
  518. } */
  519. ksort($list_of_files);
  520. return $list_of_files;
  521. }
  522. /**
  523. * getQuestions() queries the database for an list of all questions matching the current survey and group id
  524. *
  525. * @return This string is returned containing <option></option> formatted list of questions in the current survey and group
  526. */
  527. function getQuestions($surveyid,$gid,$selectedqid)
  528. {
  529. $clang = Yii::app()->lang;
  530. $s_lang = Survey::model()->findByPk($surveyid)->language;
  531. $qrows = Questions::model()->findAllByAttributes(array('sid' => $surveyid, 'gid' => $gid, 'language' => $s_lang, 'parent_qid' => 0),array('order'=>'question_order'));
  532. if (!isset($sQuestionselecter)) {$sQuestionselecter="";}
  533. foreach ($qrows as $qrow)
  534. {
  535. $qrow = $qrow->attributes;
  536. $qrow['title'] = strip_tags($qrow['title']);
  537. $link = Yii::app()->getController()->createUrl("/admin/survey/sa/view/surveyid/".$surveyid."/gid/".$gid."/qid/".$qrow['qid']);
  538. $sQuestionselecter .= "<option value='{$link}'";
  539. if ($selectedqid == $qrow['qid'])
  540. {
  541. $sQuestionselecter .= " selected='selected'";
  542. $qexists=true;
  543. }
  544. $sQuestionselecter .=">{$qrow['title']}:";
  545. $sQuestionselecter .= " ";
  546. $question=flattenText($qrow['question']);
  547. if (strlen($question)<35)
  548. {
  549. $sQuestionselecter .= $question;
  550. }
  551. else
  552. {
  553. $sQuestionselecter .= htmlspecialchars(mb_strcut(html_entity_decode($question,ENT_QUOTES,'UTF-8'), 0, 35, 'UTF-8'))."...";
  554. }
  555. $sQuestionselecter .= "</option>\n";
  556. }
  557. if (!isset($qexists))
  558. {
  559. $sQuestionselecter = "<option selected='selected'>".$clang->gT("Please choose...")."</option>\n".$sQuestionselecter;
  560. }
  561. else
  562. {
  563. $link = Yii::app()->getController()->createUrl("/admin/survey/sa/view/surveyid/".$surveyid."/gid/".$gid);
  564. $sQuestionselecter = "<option value='{$link}'>".$clang->gT("None")."</option>\n".$sQuestionselecter;
  565. }
  566. return $sQuestionselecter;
  567. }
  568. /**
  569. * getGidPrevious() returns the Gid of the group prior to the current active group
  570. *
  571. * @param string $surveyid
  572. * @param string $gid
  573. *
  574. * @return The Gid of the previous group
  575. */
  576. function getGidPrevious($surveyid, $gid)
  577. {
  578. $clang = Yii::app()->lang;
  579. if (!$surveyid) {$surveyid=returnGlobal('sid');}
  580. $s_lang = Survey::model()->findByPk($surveyid)->language;
  581. $qresult = Groups::model()->findAllByAttributes(array('sid' => $surveyid, 'language' => $s_lang), array('order'=>'group_order'));
  582. $i = 0;
  583. $iPrev = -1;
  584. foreach ($qresult as $qrow)
  585. {
  586. $qrow = $qrow->attributes;
  587. if ($gid == $qrow['gid']) {$iPrev = $i - 1;}
  588. $i += 1;
  589. }
  590. if ($iPrev >= 0) {$GidPrev = $qresult[$iPrev]->gid;}
  591. else {$GidPrev = "";}
  592. return $GidPrev;
  593. }
  594. /**
  595. * getQidPrevious() returns the Qid of the question prior to the current active question
  596. *
  597. * @param string $surveyid
  598. * @param string $gid
  599. * @param string $qid
  600. *
  601. * @return This Qid of the previous question
  602. */
  603. function getQidPrevious($surveyid, $gid, $qid)
  604. {
  605. $clang = Yii::app()->lang;
  606. $s_lang = Survey::model()->findByPk($surveyid)->language;
  607. $qrows = Questions::model()->findAllByAttributes(array('gid' => $gid, 'sid' => $surveyid, 'language' => $s_lang, 'parent_qid'=>0),array('order'=>'question_order'));
  608. $i = 0;
  609. $iPrev = -1;
  610. if (count($qrows) > 0)
  611. {
  612. foreach ($qrows as $qrow)
  613. {
  614. $qrow = $qrow->attributes;
  615. if ($qid == $qrow['qid']) {$iPrev = $i - 1;}
  616. $i += 1;
  617. }
  618. }
  619. if ($iPrev >= 0) {$QidPrev = $qrows[$iPrev]->qid;}
  620. else {$QidPrev = "";}
  621. return $QidPrev;
  622. }
  623. /**
  624. * getGidNext() returns the Gid of the group next to the current active group
  625. *
  626. * @param string $surveyid
  627. * @param string $gid
  628. *
  629. * @return The Gid of the next group
  630. */
  631. function getGidNext($surveyid, $gid)
  632. {
  633. $clang = Yii::app()->lang;
  634. if (!$surveyid) {$surveyid=returnGlobal('sid');}
  635. $s_lang = Survey::model()->findByPk($surveyid)->language;
  636. //$gquery = "SELECT gid FROM ".db_table_name('groups')." WHERE sid=$surveyid AND language='{$s_lang}' ORDER BY group_order";
  637. $qresult = Groups::model()->findAllByAttributes(array('sid' => $surveyid, 'language' => $s_lang), array('order'=>'group_order'));
  638. $GidNext="";
  639. $i = 0;
  640. $iNext = 1;
  641. foreach ($qresult as $qrow)
  642. {
  643. $qrow = $qrow->attributes;
  644. if ($gid == $qrow['gid']) {$iNext = $i + 1;}
  645. $i += 1;
  646. }
  647. if ($iNext < count($qresult)) {$GidNext = $qresult[$iNext]->gid;}
  648. else {$GidNext = "";}
  649. return $GidNext;
  650. }
  651. /**
  652. * getQidNext() returns the Qid of the question prior to the current active question
  653. *
  654. * @param string $surveyid
  655. * @param string $gid
  656. * @param string $qid
  657. *
  658. * @return This Qid of the previous question
  659. */
  660. function getQidNext($surveyid, $gid, $qid)
  661. {
  662. $clang = Yii::app()->lang;
  663. $s_lang = Survey::model()->findByPk($surveyid)->language;
  664. $qrows = Questions::model()->findAllByAttributes(array('gid' => $gid, 'sid' => $surveyid, 'language' => $s_lang, 'parent_qid' => 0), array('order'=>'question_order'));
  665. $i = 0;
  666. $iNext = 1;
  667. if (count($qrows) > 0)
  668. {
  669. foreach ($qrows as $qrow)
  670. {
  671. if ($qid == $qrow->qid) {$iNext = $i + 1;}
  672. $i += 1;
  673. }
  674. }
  675. if ($iNext < count($qrows)) {$QidNext = $qrows[$iNext]->qid;}
  676. else {$QidNext = "";}
  677. return $QidNext;
  678. }
  679. function convertGETtoPOST($url)
  680. {
  681. $url = preg_replace('/&amp;/i','&',$url);
  682. $stack = explode('?',$url);
  683. $calledscript = array_shift($stack);
  684. $query = array_shift($stack);
  685. $aqueryitems = explode('&',$query);
  686. $arrayParam = Array();
  687. $arrayVal = Array();
  688. foreach ($aqueryitems as $queryitem)
  689. {
  690. $stack = explode ('=', $queryitem);
  691. $paramname = array_shift($stack);
  692. $value = array_shift($stack);
  693. $arrayParam[] = "'".$paramname."'";
  694. $arrayVal[] = substr($value, 0, 9) != "document." ? "'".$value."'" : $value;
  695. }
  696. // $Paramlist = "[" . implode(",",$arrayParam) . "]";
  697. // $Valuelist = "[" . implode(",",$arrayVal) . "]";
  698. $Paramlist = "new Array(" . implode(",",$arrayParam) . ")";
  699. $Valuelist = "new Array(" . implode(",",$arrayVal) . ")";
  700. $callscript = "sendPost('$calledscript','',$Paramlist,$Valuelist);";
  701. return $callscript;
  702. }
  703. /**
  704. * This function calculates how much space is actually used by all files uploaded
  705. * using the File Upload question type
  706. *
  707. * @returns integer Actual space used in MB
  708. */
  709. function calculateTotalFileUploadUsage(){
  710. global $uploaddir;
  711. $sQuery='select sid from {{surveys}}';
  712. $oResult = dbExecuteAssoc($sQuery); //checked
  713. $aRows = $oResult->readAll();
  714. $iTotalSize=0.0;
  715. foreach ($aRows as $aRow)
  716. {
  717. $sFilesPath=$uploaddir.'/surveys/'.$aRow['sid'].'/files';
  718. if (file_exists($sFilesPath))
  719. {
  720. $iTotalSize+=(float)getDirectorySize($sFilesPath);
  721. }
  722. }
  723. return (float)$iTotalSize/1024/1024;
  724. }
  725. function getDirectorySize($directory) {
  726. $size = 0;
  727. foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory)) as $file){
  728. $size+=$file->getSize();
  729. }
  730. return $size;
  731. }
  732. /**
  733. * Gets number of groups inside a particular survey
  734. *
  735. * @param string $surveyid
  736. * @param mixed $lang
  737. */
  738. function getGroupSum($surveyid, $lang)
  739. {
  740. //$condn = "WHERE sid=".$surveyid." AND language='".$lang."'"; //Getting a count of questions for this survey
  741. $condn = array('sid'=>$surveyid,'language'=>$lang);
  742. $sumresult3 = count(Groups::model()->findAllByAttributes($condn)); //Checked)
  743. return $sumresult3 ;
  744. }
  745. /**
  746. * getMaxGroupOrder($surveyid) queries the database for the maximum sortorder of a group and returns the next higher one.
  747. *
  748. * @param mixed $surveyid
  749. */
  750. function getMaxGroupOrder($surveyid)
  751. {
  752. $s_lang = Survey::model()->findByPk($surveyid)->language;
  753. //$max_sql = "SELECT max( group_order ) AS max FROM ".db_table_name('groups')." WHERE sid =$surveyid AND language='{$s_lang}'" ;
  754. $query = Groups::model()->find(array('order' => 'group_order desc'));
  755. $current_max = !is_null($query) ? $query->group_order : '';
  756. if($current_max!="")
  757. {
  758. return ++$current_max ;
  759. }
  760. else return "0" ;
  761. }
  762. /**
  763. * getGroupOrder($surveyid,$gid) queries the database for the sortorder of a group.
  764. *
  765. * @param mixed $surveyid
  766. * @param mixed $gid
  767. * @return mixed
  768. */
  769. function getGroupOrder($surveyid,$gid)
  770. {
  771. $s_lang = Survey::model()->findByPk($surveyid)->language;
  772. //$grporder_sql = "SELECT group_order FROM ".db_table_name('groups')." WHERE sid =$surveyid AND language='{$s_lang}' AND gid=$gid" ;
  773. $grporder_result = Groups::model()->findByAttributes(array('sid' => $surveyid, 'gid' => $gid, 'language' => $s_lang)); //Checked
  774. $grporder_row = $grporder_result->attributes ;
  775. $group_order = $grporder_row['group_order'];
  776. if($group_order=="")
  777. {
  778. return "0" ;
  779. }
  780. else return $group_order ;
  781. }
  782. /**
  783. * getMaxQuestionOrder($gid) queries the database for the maximum sortorder of a question.
  784. *
  785. */
  786. function getMaxQuestionOrder($gid,$surveyid)
  787. {
  788. $gid=sanitize_int($gid);
  789. $s_lang = Survey::model()->findByPk($surveyid)->language;
  790. $max_sql = "SELECT max( question_order ) AS max FROM {{questions}} WHERE gid='$gid' AND language='$s_lang'";
  791. $max_result = Yii::app()->db->createCommand($max_sql)->query(); //Checked
  792. $maxrow = $max_result->read() ;
  793. $current_max = $maxrow['max'];
  794. if($current_max=="")
  795. {
  796. return "0" ;
  797. }
  798. else return $current_max ;
  799. }
  800. /**
  801. * getQuestionClass() returns a class name for a given question type to allow custom styling for each question type.
  802. *
  803. * @param string $input containing unique character representing each question type.
  804. * @return string containing the class name for a given question type.
  805. */
  806. function getQuestionClass($input)
  807. {
  808. switch($input)
  809. { // I think this is a bad solution to adding classes to question
  810. // DIVs but I can't think of a better solution. (eric_t_cruiser)
  811. case 'X': return 'boilerplate'; // BOILERPLATE QUESTION
  812. case '5': return 'choice-5-pt-radio'; // 5 POINT CHOICE radio-buttons
  813. case 'D': return 'date'; // DATE
  814. case 'Z': return 'list-radio-flexible'; // LIST Flexible radio-button
  815. case 'L': return 'list-radio'; // LIST radio-button
  816. case 'W': return 'list-dropdown-flexible'; // LIST drop-down (flexible label)
  817. case '!': return 'list-dropdown'; // List - dropdown
  818. case 'O': return 'list-with-comment'; // LIST radio-button + textarea
  819. case 'R': return 'ranking'; // RANKING STYLE
  820. case 'M': return 'multiple-opt'; // Multiple choice checkbox
  821. case 'I': return 'language'; // Language Question
  822. case 'P': return 'multiple-opt-comments'; // Multiple choice with comments checkbox + text
  823. case 'Q': return 'multiple-short-txt'; // TEXT
  824. case 'K': return 'numeric-multi'; // MULTIPLE NUMERICAL QUESTION
  825. case 'N': return 'numeric'; // NUMERICAL QUESTION TYPE
  826. case 'S': return 'text-short'; // SHORT FREE TEXT
  827. case 'T': return 'text-long'; // LONG FREE TEXT
  828. case 'U': return 'text-huge'; // HUGE FREE TEXT
  829. case 'Y': return 'yes-no'; // YES/NO radio-buttons
  830. case 'G': return 'gender'; // GENDER drop-down list
  831. case 'A': return 'array-5-pt'; // ARRAY (5 POINT CHOICE) radio-buttons
  832. case 'B': return 'array-10-pt'; // ARRAY (10 POINT CHOICE) radio-buttons
  833. case 'C': return 'array-yes-uncertain-no'; // ARRAY (YES/UNCERTAIN/NO) radio-buttons
  834. case 'E': return 'array-increase-same-decrease'; // ARRAY (Increase/Same/Decrease) radio-buttons
  835. case 'F': return 'array-flexible-row'; // ARRAY (Flexible) - Row Format
  836. case 'H': return 'array-flexible-column'; // ARRAY (Flexible) - Column Format
  837. // case '^': return 'slider'; // SLIDER CONTROL
  838. case ':': return 'array-multi-flexi'; // ARRAY (Multi Flexi) 1 to 10
  839. case ";": return 'array-multi-flexi-text';
  840. case "1": return 'array-flexible-duel-scale'; // Array dual scale
  841. case "*": return 'equation'; // Equation
  842. default: return 'generic_question'; // Should have a default fallback
  843. };
  844. };
  845. /**
  846. * setupColumns() defines all the html tags to be wrapped around
  847. * various list type answers.
  848. *
  849. * @param integer $columns - the number of columns, usually supplied by $dcols
  850. * @param integer $answer_count - the number of answers to a question, usually supplied by $anscount
  851. * @param string $wrapperclass - a global class for the wrapper
  852. * @param string $itemclass - a class for the item
  853. * @return array with all the various opening and closing tags to generate a set of columns.
  854. *
  855. * It returns an array with the following items:
  856. * $wrapper['whole-start'] = Opening wrapper for the whole list
  857. * $wrapper['whole-end'] = closing wrapper for the whole list
  858. * $wrapper['col-devide'] = normal column devider
  859. * $wrapper['col-devide-last'] = the last column devider (to allow
  860. * for different styling of the last
  861. * column
  862. * $wrapper['item-start'] = opening wrapper tag for individual
  863. * option
  864. * $wrapper['item-start-other'] = opening wrapper tag for other
  865. * option
  866. * $wrapper['item-start-noanswer'] = opening wrapper tag for no answer
  867. * option
  868. * $wrapper['item-end'] = closing wrapper tag for individual
  869. * option
  870. * $wrapper['maxrows'] = maximum number of rows in each
  871. * column
  872. * $wrapper['cols'] = Number of columns to be inserted
  873. * (and checked against)
  874. *
  875. *
  876. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  877. * Columns are a problem.
  878. * Really there is no perfect solution to columns at the moment.
  879. *
  880. * - Using Tables is problematic semanticly.
  881. * - Using inline or float to create columns, causes the answers
  882. * flows horizontally, not vertically which is not ideal visually.
  883. * - Using CSS3 columns is also a problem because of browser support
  884. * and also because if you have answeres split across two or more
  885. * lines, and those answeres happen to fall at the bottom of a
  886. * column, the answer might be split across columns as well as
  887. * lines.
  888. * - Using nested unordered list with the first level of <LI>s
  889. * floated is the same as using tables and so is bad semantically
  890. * for the same reason tables are bad.
  891. * - Breaking the unordered lists into consecutive floated unordered
  892. * lists is not great semantically but probably not as bad as
  893. * using tables.
  894. *
  895. * Because I haven't been able to decide which option is the least
  896. * bad, I have handed over that responsibility to the admin who sets
  897. * LimeSurvey up on their server.
  898. *
  899. * There are four options:
  900. * 'css' using one of the various CSS only methods for
  901. * rendering columns.
  902. * (Check the CSS file for your chosen template to see
  903. * how columns are defined.)
  904. * 'ul' using multiple floated unordered lists. (DEFAULT)
  905. * 'table' using conventional tables based layout.
  906. * NULL blocks the use of columns
  907. *
  908. * 'ul' is the default because it's the best possible compromise
  909. * between semantic markup and visual layout.
  910. */
  911. function setupColumns($columns, $answer_count,$wrapperclass="",$itemclass="")
  912. {
  913. $column_style = Yii::app()->getConfig('column_style');
  914. if ( !in_array($column_style,array('css','ul','table')) && !is_null($column_style) )
  915. {
  916. $column_style = 'ul';
  917. };
  918. if($columns < 2)
  919. {
  920. $column_style = null;
  921. $columns = 1;
  922. }
  923. if(($columns > $answer_count) && $answer_count>0)
  924. {
  925. $columns = $answer_count;
  926. };
  927. $class_first = ' class="'.$wrapperclass.'"';
  928. if($columns > 1 && !is_null($column_style))
  929. {
  930. if($column_style == 'ul')
  931. {
  932. $ul = '-ul';
  933. }
  934. else
  935. {
  936. $ul = '';
  937. }
  938. $class_first = ' class="'.$wrapperclass.' cols-'.$columns . $ul.' first"';
  939. $class = ' class="'.$wrapperclass.' cols-'.$columns . $ul.'"';
  940. $class_last_ul = ' class="'.$wrapperclass.' cols-'.$columns . $ul.' last"';
  941. $class_last_table = ' class="'.$wrapperclass.' cols-'.$columns.' last"';
  942. }
  943. else
  944. {
  945. $class = ' class="'.$wrapperclass.'"';
  946. $class_last_ul = ' class="'.$wrapperclass.'"';
  947. $class_last_table = ' class="'.$wrapperclass.'"';
  948. };
  949. $wrapper = array(
  950. 'whole-start' => "\n<ul$class_first>\n"
  951. ,'whole-end' => "</ul>\n"
  952. ,'col-devide' => ''
  953. ,'col-devide-last' => ''
  954. ,'item-start' => "\t<li class=\"{$itemclass}\">\n"
  955. ,'item-start-other' => "\t<li class=\"{$itemclass} other other-item\">\n"
  956. ,'item-start-noanswer' => "\t<li class=\"{$itemclass} noanswer-item\">\n"
  957. ,'item-end' => "\t</li>\n"
  958. ,'maxrows' => ceil($answer_count/$columns) //Always rounds up to nearest whole number
  959. ,'cols' => $columns
  960. );
  961. switch($column_style)
  962. {
  963. case 'ul': if($columns > 1)
  964. {
  965. $wrapper['col-devide'] = "\n</ul>\n\n<ul$class>\n";
  966. $wrapper['col-devide-last'] = "\n</ul>\n\n<ul$class_last_ul>\n";
  967. }
  968. break;
  969. case 'table': $table_cols = '';
  970. for($cols = $columns ; $cols > 0 ; --$cols)
  971. {
  972. switch($cols)
  973. {
  974. case $columns: $table_cols .= "\t<col$class_first />\n";
  975. break;
  976. case 1: $table_cols .= "\t<col$class_last_table />\n";
  977. break;
  978. default: $table_cols .= "\t<col$class />\n";
  979. };
  980. };
  981. if($columns > 1)
  982. {
  983. $wrapper['col-devide'] = "\t</ul>\n</td>\n\n<td>\n\t<ul>\n";
  984. $wrapper['col-devide-last'] = "\t</ul>\n</td>\n\n<td class=\"last\">\n\t<ul>\n";
  985. };
  986. $wrapper['whole-start'] = "\n<table$class>\n$table_cols\n\t<tbody>\n<tr>\n<td>\n\t<ul>\n";
  987. $wrapper['whole-end'] = "\t</ul>\n</td>\n</tr>\n\t</tbody>\n</table>\n";
  988. $wrapper['item-start'] = "<li class=\"{$itemclass}\">\n";
  989. $wrapper['item-end'] = "</li class=\"{$itemclass}\">\n";
  990. };
  991. return $wrapper;
  992. };
  993. function alternation($alternate = '' , $type = 'col')
  994. {
  995. /**
  996. * alternation() Returns a class identifyer for alternating between
  997. * two options. Used to style alternate elements differently. creates
  998. * or alternates between the odd string and the even string used in
  999. * as column and row classes for array type questions.
  1000. *
  1001. * @param string $alternate = '' (empty) (default) , 'array2' , 'array1' , 'odd' , 'even'
  1002. * @param string $type = 'col' (default) or 'row'
  1003. *
  1004. * @return string representing either the first alternation or the opposite alternation to the one supplied..
  1005. */
  1006. /*
  1007. // The following allows type to be left blank for row in subsequent
  1008. // function calls.
  1009. // It has been left out because 'row' must be defined the first time
  1010. // alternation() is called. Since it is only ever written once for each
  1011. // while statement within a function, 'row' is always defined.
  1012. if(!empty($alternate) && $type != 'row')
  1013. { if($alternate == ('array2' || 'array1'))
  1014. {
  1015. $type = 'row';
  1016. };
  1017. };
  1018. // It has been left in case it becomes useful but probably should be
  1019. // removed.
  1020. */
  1021. if($type == 'row')
  1022. {
  1023. $odd = 'array2'; // should be row_odd
  1024. $even = 'array1'; // should be row_even
  1025. }
  1026. else
  1027. {
  1028. $odd = 'odd'; // should be col_odd
  1029. $even = 'even'; // should be col_even
  1030. };
  1031. if($alternate == $odd)
  1032. {
  1033. $alternate = $even;
  1034. }
  1035. else
  1036. {
  1037. $alternate = $odd;
  1038. };
  1039. return $alternate;
  1040. }
  1041. /**
  1042. * longestString() returns the length of the longest string past to it.
  1043. * @peram string $new_string
  1044. * @peram integer $longest_length length of the (previously) longest string passed to it.
  1045. * @return integer representing the length of the longest string passed (updated if $new_string was longer than $longest_length)
  1046. *
  1047. * usage should look like this: $longest_length = longestString( $new_string , $longest_length );
  1048. *
  1049. */
  1050. function longestString( $new_string , $longest_length )
  1051. {
  1052. if($longest_length < strlen(trim(strip_tags($new_string))))
  1053. {
  1054. $longest_length = strlen(trim(strip_tags($new_string)));
  1055. };
  1056. return $longest_length;
  1057. };
  1058. /**
  1059. * getNotificationList() returns different options for notifications
  1060. *
  1061. * @param string $notificationcode - the currently selected one
  1062. *
  1063. * @return This string is returned containing <option></option> formatted list of notification methods for current survey
  1064. */
  1065. function getNotificationList($notificationcode)
  1066. {
  1067. $clang = Yii::app()->lang;
  1068. $ntypes = array(
  1069. "0"=>$clang->gT("No email notification"),
  1070. "1"=>$clang->gT("Basic email notification"),
  1071. "2"=>$clang->gT("Detailed email notification with result codes")
  1072. );
  1073. if (!isset($ntypeselector)) {$ntypeselector="";}
  1074. foreach($ntypes as $ntcode=>$ntdescription)
  1075. {
  1076. $ntypeselector .= "<option value='$ntcode'";
  1077. if ($notificationcode == $ntcode) {$ntypeselector .= " selected='selected'";}
  1078. $ntypeselector .= ">$ntdescription</option>\n";
  1079. }
  1080. return $ntypeselector;
  1081. }
  1082. /**
  1083. * getGroupList() queries the database for a list of all groups matching the current survey sid
  1084. *
  1085. *
  1086. * @param string $gid - the currently selected gid/group
  1087. *
  1088. * @return This string is returned containing <option></option> formatted list of groups to current survey
  1089. */
  1090. function getGroupList($gid,$surveyid)
  1091. {
  1092. $clang = Yii::app()->lang;
  1093. $groupselecter="";
  1094. $gid=sanitize_int($gid);
  1095. $surveyid=sanitize_int($surveyid);
  1096. if (!$surveyid) {$surveyid=returnGlobal('sid');}
  1097. $s_lang = Survey::model()->findByPk($surveyid)->language;
  1098. $gidquery = "SELECT gid, group_name FROM {{groups}} WHERE sid='{$surveyid}' AND language='{$s_lang}' ORDER BY group_order";
  1099. $gidresult = Yii::app()->db->createCommand($gidquery)->query(); //Checked
  1100. foreach ($gidresult->readAll() as $gv)
  1101. {
  1102. $groupselecter .= "<option";
  1103. if ($gv['gid'] == $gid) {$groupselecter .= " selected='selected'"; $gvexist = 1;}
  1104. $groupselecter .= " value='".Yii::app()->getConfig('scriptname')."?sid=$surveyid&amp;gid=".$gv['gid']."'>".htmlspecialchars($gv['group_name'])."</option>\n";
  1105. }
  1106. if ($groupselecter)
  1107. {
  1108. if (!isset($gvexist)) {$groupselecter = "<option selected='selected'>".$clang->gT("Please choose...")."</option>\n".$groupselecter;}
  1109. else {$groupselecter .= "<option value='".Yii::app()->getConfig('scriptname')."?sid=$surveyid&amp;gid='>".$clang->gT("None")."</option>\n";}
  1110. }
  1111. return $groupselecter;
  1112. }
  1113. function getGroupList3($gid,$surveyid)
  1114. {
  1115. //$clang = Yii::app()->lang;
  1116. $gid=sanitize_int($gid);
  1117. $surveyid=sanitize_int($surveyid);
  1118. if (!$surveyid) {$surveyid=returnGlobal('sid');}
  1119. $groupselecter = "";
  1120. $s_lang = Survey::model()->findByPk($surveyid)->language;
  1121. //$gidquery = "SELECT gid, group_name FROM ".db_table_name('groups')." WHERE sid=$surveyid AND language='{$s_lang}' ORDER BY group_order";
  1122. $gidresult = Groups::model()->findAllByAttributes(array('sid' => $surveyid, 'language' => $s_lang), array('order'=>'group_order'));
  1123. foreach ($gidresult as $gv)
  1124. {
  1125. $gv = $gv->attributes;
  1126. $groupselecter .= "<option";
  1127. if ($gv['gid'] == $gid) {$groupselecter .= " selected='selected'"; }
  1128. $groupselecter .= " value='".$gv['gid']."'>".htmlspecialchars($gv['group_name'])."</option>\n";
  1129. }
  1130. return $groupselecter;
  1131. }
  1132. /**
  1133. * put your comment there...
  1134. *
  1135. * @param mixed $gid
  1136. * @param mixed $language
  1137. */
  1138. function getGroupListLang($gid, $language, $surveyid)
  1139. {
  1140. $clang = Yii::app()->lang;
  1141. $groupselecter="";
  1142. if (!$surveyid) {$surveyid=returnGlobal('sid');}
  1143. $gidresult = Groups::model()->findAll(array('condition'=>'sid=:surveyid AND language=:language',
  1144. 'order'=>'group_order',
  1145. 'params'=>array(':surveyid'=>$surveyid,':language'=>$language))); //Checked)
  1146. foreach ($gidresult as $gv)
  1147. {
  1148. $gv = $gv->attributes;
  1149. $groupselecter .= "<option";
  1150. if ($gv['gid'] == $gid) {$groupselecter .= " selected='selected'"; $gvexist = 1;}
  1151. $link = Yii::app()->getController()->createUrl("/admin/survey/sa/view/surveyid/".$surveyid."/gid/".$gv['gid']);
  1152. $groupselecter .= " value='{$link}'>";
  1153. if (strip_tags($gv['group_name']))
  1154. {
  1155. $groupselecter .= htmlspecialchars(strip_tags($gv['group_name']));
  1156. } else {
  1157. $groupselecter .= htmlspecialchars($gv['group_name']);
  1158. }
  1159. $groupselecter .= "</option>\n";
  1160. }
  1161. if ($groupselecter)
  1162. {
  1163. $link = Yii::app()->getController()->createUrl("/admin/survey/sa/view/surveyid/".$surveyid);
  1164. if (!isset($gvexist)) {$groupselecter = "<option selected='selected'>".$clang->gT("Please choose...")."</option>\n".$groupselecter;}
  1165. else {$groupselecter .= "<option value='{$link}'>".$clang->gT("None")."</option>\n";}
  1166. }
  1167. return $groupselecter;
  1168. }
  1169. function getUserList($outputformat='fullinfoarray')
  1170. {
  1171. $clang = Yii::app()->lang;
  1172. if (!empty(Yii::app()->session['loginID']))
  1173. {
  1174. $myuid=sanitize_int(Yii::app()->session['loginID']);
  1175. }
  1176. $usercontrolSameGroupPolicy = Yii::app()->getConfig('usercontrolSameGroupPolicy');
  1177. if (Yii::app()->session['USER_RIGHT_SUPERADMIN'] != 1 && isset($usercontrolSameGroupPolicy) &&
  1178. $usercontrolSameGroupPolicy == true)
  1179. {
  1180. if (isset($myuid))
  1181. {
  1182. $sDatabaseType = Yii::app()->db->getDriverName();
  1183. if ($sDatabaseType=='mssql' || $sDatabaseType=="sqlsrv")
  1184. {
  1185. $sSelectFields = 'users_name,uid,email,full_name,parent_id,create_survey,participant_panel,configurator,create_user,delete_user,superadmin,manage_template,manage_label,CAST(password as varchar) as password';
  1186. }
  1187. else
  1188. {
  1189. $sSelectFields = 'users_name,uid,email,full_name,parent_id,create_survey,participant_panel,configurator,create_user,delete_user,superadmin,manage_template,manage_label,password';
  1190. }
  1191. // List users from same group as me + all my childs
  1192. // a subselect is used here because MSSQL does not like to group by text
  1193. // also Postgres does like this one better
  1194. $uquery = " SELECT {$sSelectFields} from {{users}} where uid in (
  1195. SELECT uid from {{user_in_groups}} where ugid in (
  1196. SELECT ugid from {{user_in_groups}} where uid={$myuid}
  1197. )
  1198. )
  1199. UNION
  1200. SELECT {$sSelectFields} from {{users}} v where v.parent_id={$myuid}
  1201. UNION
  1202. SELECT {$sSelectFields} from {{users}} v where uid={$myuid}";
  1203. }
  1204. else
  1205. {
  1206. return array(); // Or die maybe
  1207. }
  1208. }
  1209. else
  1210. {
  1211. $uquery = "SELECT * FROM {{users}} ORDER BY uid";
  1212. }
  1213. $uresult = Yii::app()->db->createCommand($uquery)->query()->readAll(); //Checked
  1214. if (count($uresult)==0)
  1215. //user is not in a group and usercontrolSameGroupPolicy is activated - at least show his own userinfo
  1216. {
  1217. $uquery = "SELECT u.* FROM {{users}} AS u WHERE u.uid=".$myuid;
  1218. $uresult = Yii::app()->db->createCommand($uquery)->query()->readAll();//Checked
  1219. }
  1220. $userlist = array();
  1221. $userlist[0] = "Reserved for logged in user";
  1222. //while ($srow = $uresult->readAll())
  1223. foreach ($uresult as $srow)
  1224. {
  1225. if ($outputformat != 'onlyuidarray')
  1226. {
  1227. if ($srow['uid'] != Yii::app()->session['loginID'])
  1228. {
  1229. $userlist[] = array("user"=>$srow['users_name'], "uid"=>$srow['uid'], "email"=>$srow['email'], "password"=>$srow['password'], "full_name"=>$srow['full_name'], "parent_id"=>$srow['parent_id'], "create_survey"=>$srow['create_survey'], "participant_panel"=>$srow['participant_panel'], "configurator"=>$srow['configurator'], "create_user"=>$srow['create_user'], "delete_user"=>$srow['delete_user'], "superadmin"=>$srow['superadmin'], "manage_template"=>$srow['manage_template'], "manage_label"=>$srow['manage_label']); //added by Dennis modified by Moses
  1230. }
  1231. else
  1232. {
  1233. $userlist[0] = array("user"=>$srow['users_name'], "uid"=>$srow['uid'], "email"=>$srow['email'], "password"=>$srow['password'], "full_name"=>$srow['full_name'], "parent_id"=>$srow['parent_id'], "create_survey"=>$srow['create_survey'],"participant_panel"=>$srow['participant_panel'], "configurator"=>$srow['configurator'], "create_user"=>$srow['create_user'], "delete_user"=>$srow['delete_user'], "superadmin"=>$srow['superadmin'], "manage_template"=>$srow['manage_template'], "manage_label"=>$srow['manage_label']);
  1234. }
  1235. }
  1236. else
  1237. {
  1238. if ($srow['uid'] != Yii::app()->session['loginID'])
  1239. {
  1240. $userlist[] = $srow['uid'];
  1241. }
  1242. else
  1243. {
  1244. $userlist[0] = $srow['uid'];
  1245. }
  1246. }
  1247. }
  1248. return $userlist;
  1249. }
  1250. /**
  1251. * Gets all survey infos in one big array including the language specific settings
  1252. *
  1253. * @param string $surveyid The survey ID
  1254. * @param string $languagecode The language code - if not given the base language of the particular survey is used
  1255. * @return array Returns array with survey info or false, if survey does not exist
  1256. */
  1257. function getSurveyInfo($surveyid, $languagecode='')
  1258. {
  1259. static $staticSurveyInfo = array();// Use some static
  1260. $surveyid=sanitize_int($surveyid);
  1261. $languagecode=sanitize_languagecode($languagecode);
  1262. $thissurvey=false;
  1263. // Do job only if this survey exist
  1264. if(!Survey::model()->findByPk($surveyid))
  1265. {
  1266. return false;
  1267. }
  1268. // if no language code is set then get the base language one
  1269. if ((!isset($languagecode) || $languagecode==''))
  1270. {
  1271. $languagecode=Survey::model()->findByPk($surveyid)->language;
  1272. }
  1273. if(isset($staticSurveyInfo[$surveyid][$languagecode]) )
  1274. {
  1275. $thissurvey=$staticSurveyInfo[$surveyid][$languagecode];
  1276. }
  1277. else
  1278. {
  1279. $result = Surveys_languagesettings::model()->with('survey')->findByPk(array('surveyls_survey_id' => $surveyid, 'surveyls_language' => $languagecode));
  1280. if (is_null($result)) {
  1281. // When additional language was added, but not saved it does not exists
  1282. // We should revert to the base language then
  1283. $languagecode=Survey::model()->findByPk($surveyid)->language;
  1284. $result = Surveys_languagesettings::model()->with('survey')->findByPk(array('surveyls_survey_id' => $surveyid, 'surveyls_language' => $languagecode));
  1285. }
  1286. if($result)
  1287. {
  1288. $thissurvey=array_merge($result->survey->attributes,$result->attributes);
  1289. $thissurvey['name']=$thissurvey['surveyls_title'];
  1290. $thissurvey['description']=$thissurvey['surveyls_description'];
  1291. $thissurvey['welcome']=$thissurvey['surveyls_welcometext'];
  1292. $thissurvey['templatedir']=$thissurvey['template'];
  1293. $thissurvey['adminname']=$thissurvey['admin'];
  1294. $thissurvey['tablename']='{{survey_'.$thissurvey['sid'] . '}}';
  1295. $thissurvey['urldescrip']=$thissurvey['surveyls_urldescription'];
  1296. $thissurvey['url']=$thissurvey['surveyls_url'];
  1297. $thissurvey['expiry']=$thissurvey['expires'];
  1298. $thissurvey['email_invite_subj']=$thissurvey['surveyls_email_invite_subj'];
  1299. $thissurvey['email_invite']=$thissurvey['surveyls_email_invite'];
  1300. $thissurvey['email_remind_subj']=$thissurvey['surveyls_email_remind_subj'];
  1301. $thissurvey['email_remind']=$thissurvey['surveyls_email_remind'];
  1302. $thissurvey['email_confirm_subj']=$thissurvey['surveyls_email_confirm_subj'];
  1303. $thissurvey['email_confirm']=$thissurvey['surveyls_email_confirm'];
  1304. $thissurvey['email_register_subj']=$thissurvey['surveyls_email_register_subj'];
  1305. $thissurvey['email_register']=$thissurvey['surveyls_email_register'];
  1306. $thissurvey['attributedescriptions'] = $result->survey->tokenAttributes;
  1307. $thissurvey['attributecaptions'] = $result->attributeCaptions;
  1308. if (!isset($thissurvey['adminname'])) {$thissurvey['adminname']=Yii::app()->getConfig('siteadminemail');}
  1309. if (!isset($thissurvey['adminemail'])) {$thissurvey['adminemail']=Yii::app()->getConfig('siteadminname');}
  1310. if (!isset($thissurvey['urldescrip']) || $thissurvey['urldescrip'] == '' ) {$thissurvey['urldescrip']=$thissurvey['surveyls_url'];}
  1311. $staticSurveyInfo[$surveyid][$languagecode]=$thissurvey;
  1312. }
  1313. }
  1314. return $thissurvey;
  1315. }
  1316. /**
  1317. * Returns the default email template texts as array
  1318. *
  1319. * @param mixed $oLanguage Required language translationb object
  1320. * @param string $mode Escape mode for the translation function
  1321. * @return array
  1322. */
  1323. function templateDefaultTexts($oLanguage, $mode='html'){
  1324. return array(
  1325. 'admin_detailed_notification_subject'=>$oLanguage->gT("Response submission for survey {SURVEYNAME} with results",$mode),
  1326. 'admin_detailed_notification'=>$oLanguage->gT("Hello,\n\nA new response was submitted for your survey '{SURVEYNAME}'.\n\nClick the following link to reload the survey:\n{RELOADURL}\n\nClick the following link to see the individual response:\n{VIEWRESPONSEURL}\n\nClick the following link to edit the individual response:\n{EDITRESPONSEURL}\n\nView statistics by clicking here:\n{STATISTICSURL}\n\n\nThe following answers were given by the participant:\n{ANSWERTABLE}",$mode),
  1327. 'admin_detailed_notification_css'=>'<style type="text/css">
  1328. .printouttable {
  1329. margin:1em auto;
  1330. }
  1331. .printouttable th {
  1332. text-align: center;
  1333. }
  1334. .printouttable td {
  1335. border-color: #ddf #ddf #ddf #ddf;
  1336. border-style: solid;
  1337. border-width: 1px;
  1338. padding:0.1em 1em 0.1em 0.5em;
  1339. }
  1340. .printouttable td:first-child {
  1341. font-weight: 700;
  1342. text-align: right;
  1343. padding-right: 5px;
  1344. padding-left: 5px;
  1345. }
  1346. .printouttable .printanswersquestion td{
  1347. background-color:#F7F8FF;
  1348. }
  1349. .printouttable .printanswersquestionhead td{
  1350. text-align: left;
  1351. background-color:#ddf;
  1352. }
  1353. .printouttable .printanswersgroup td{
  1354. text-align: center;
  1355. font-weight:bold;
  1356. padding-top:1em;
  1357. }
  1358. </style>',
  1359. 'admin_notification_subject'=>$oLanguage->gT("Response submission for survey {SURVEYNAME}",$mode),
  1360. 'admin_notification'=>$oLanguage->gT("Hello,\n\nA new response was submitted for your survey '{SURVEYNAME}'.\n\nClick the following link to reload the survey:\n{RELOADURL}\n\nClick the following link to see the individual response:\n{VIEWRESPONSEURL}\n\nClick the following link to edit the individual response:\n{EDITRESPONSEURL}\n\nView statistics by clicking here:\n{STATISTICSURL}",$mode),
  1361. 'confirmation_subject'=>$oLanguage->gT("Confirmation of your participation in our survey"),
  1362. 'confirmation'=>$oLanguage->gT("Dear {FIRSTNAME},\n\nthis email is to confirm that you have completed the survey titled {SURVEYNAME} and your response has been saved. Thank you for participating.\n\nIf you have any further questions about this email, please contact {ADMINNAME} on {ADMINEMAIL}.\n\nSincerely,\n\n{ADMINNAME}",$mode),
  1363. 'invitation_subject'=>$oLanguage->gT("Invitation to participate in a survey",$mode),
  1364. 'invitation'=>$oLanguage->gT("Dear {FIRSTNAME},\n\nyou have been invited to participate in a survey.\n\nThe survey is titled:\n\"{SURVEYNAME}\"\n\n\"{SURVEYDESCRIPTION}\"\n\nTo participate, please click on the link below.\n\nSincerely,\n\n{ADMINNAME} ({ADMINEMAIL})\n\n----------------------------------------------\nClick here to do the survey:\n{SURVEYURL}",$mode)."\n\n".$oLanguage->gT("If you do not want to participate in this survey and don't want to receive any more invitations please click the following link:\n{OPTOUTURL}",$mode)."\n\n".$oLanguage->gT("If you are blacklisted but want to participate in this survey and want to receive invitations please click the following link:\n{OPTINURL}",$mode),
  1365. 'reminder_subject'=>$oLanguage->gT("Reminder to participate in a survey",$mode),
  1366. 'reminder'=>$oLanguage->gT("Dear {FIRSTNAME},\n\nRecently we invited you to participate in a survey.\n\nWe note that you have not yet completed the survey, and wish to remind you that the survey is still available should you wish to take part.\n\nThe survey is titled:\n\"{SURVEYNAME}\"\n\n\"{SURVEYDESCRIPTION}\"\n\nTo participate, please click on the link below.\n\nSincerely,\n\n{ADMINNAME} ({ADMINEMAIL})\n\n----------------------------------------------\nClick here to do the survey:\n{SURVEYURL}",$mode)."\n\n".$oLanguage->gT("If you do not want to participate in this survey and don't want to receive any more invitations please click the following link:\n{OPTOUTURL}",$mode),
  1367. 'registration_subject'=>$oLanguage->gT("Survey registration confirmation",$mode),
  1368. 'registration'=>$oLanguage->gT("Dear {FIRSTNAME},\n\nYou, or someone using your email address, have registered to participate in an online survey titled {SURVEYNAME}.\n\nTo complete this survey, click on the following URL:\n\n{SURVEYURL}\n\nIf you have any questions about this survey, or if you did not register to participate and believe this email is in error, please contact {ADMINNAME} at {ADMINEMAIL}.",$mode)
  1369. );
  1370. }
  1371. /**
  1372. * Compares two elements from an array (passed by the usort function)
  1373. * and returns -1, 0 or 1 depending on the result of the comparison of
  1374. * the sort order of the group_order and question_order field
  1375. *
  1376. * @param mixed $a
  1377. * @param mixed $b
  1378. * @return int
  1379. */
  1380. function groupOrderThenQuestionOrder($a, $b)
  1381. {
  1382. if (isset($a['group_order']) && isset($b['group_order']))
  1383. {
  1384. $GroupResult = strnatcasecmp($a['group_order'], $b['group_order']);
  1385. }
  1386. else
  1387. {
  1388. $GroupResult = "";
  1389. }
  1390. if ($GroupResult == 0)
  1391. {
  1392. $TitleResult = strnatcasecmp($a["question_order"], $b["question_order"]);
  1393. return $TitleResult;
  1394. }
  1395. return $GroupResult;
  1396. }
  1397. function fixSortOrderAnswers($qid,$surveyid=null) //Function rewrites the sortorder for a group of answers
  1398. {
  1399. $qid=sanitize_int($qid);
  1400. $baselang = Survey::model()->findByPk($surveyid)->language;
  1401. Answers::model()->updateSortOrder($qid,$baselang);
  1402. }
  1403. /**
  1404. * This function rewrites the sortorder for questions inside the named group
  1405. * REMOVED the 2012-08-08 : replaced by Questions::model()->updateQuestionOrder
  1406. * @param integer $groupid the group id
  1407. * @param integer $surveyid the survey id
  1408. */
  1409. /**
  1410. function fixSortOrderQuestions($groupid, $surveyid) //Function rewrites the sortorder for questions
  1411. {
  1412. $gid = sanitize_int($groupid);
  1413. $surveyid = sanitize_int($surveyid);
  1414. $baselang = Survey::model()->findByPk($surveyid)->language;
  1415. $questions = Questions::model()->findAllByAttributes(array('gid' => $gid, 'sid' => $surveyid, 'language' => $baselang));
  1416. $p = 0;
  1417. foreach ($questions as $question)
  1418. {
  1419. $question->question_order = $p;
  1420. $question->save();
  1421. $p++;
  1422. }
  1423. }
  1424. */
  1425. function shiftOrderQuestions($sid,$gid,$shiftvalue) //Function shifts the sortorder for questions
  1426. {
  1427. $sid=sanitize_int($sid);
  1428. $gid=sanitize_int($gid);
  1429. $shiftvalue=sanitize_int($shiftvalue);
  1430. $baselang = Survey::model()->findByPk($sid)->language;
  1431. Questions::model()->updateQuestionOrder($gid,$baselang,$shiftvalue);
  1432. }
  1433. function fixSortOrderGroups($surveyid) //Function rewrites the sortorder for groups
  1434. {
  1435. $baselang = Survey::model()->findByPk($surveyid)->language;
  1436. Groups::model()->updateGroupOrder($surveyid,$baselang);
  1437. }
  1438. function fixMovedQuestionConditions($qid,$oldgid,$newgid) //Function rewrites the cfieldname for a question after group change
  1439. {
  1440. $surveyid = Yii::app()->getConfig('sid');
  1441. $qid=sanitize_int($qid);
  1442. $oldgid=sanitize_int($oldgid);
  1443. $newgid=sanitize_int($newgid);
  1444. Conditions::model()->updateCFieldName($surveyid,$qid,$oldgid,$newgid);
  1445. // TMSW Conditions->Relevance: Call LEM->ConvertConditionsToRelevance() when done
  1446. }
  1447. /**
  1448. * This function returns POST/REQUEST vars, for some vars like SID and others they are also sanitized
  1449. *
  1450. * @param mixed $stringname
  1451. * @param mixed $urlParam
  1452. */
  1453. function returnGlobal($stringname)
  1454. {
  1455. if ($stringname=='sid') // don't read SID from a Cookie
  1456. {
  1457. if (isset($_GET[$stringname])) $urlParam = $_GET[$stringname];
  1458. if (isset($_POST[$stringname])) $urlParam = $_POST[$stringname];
  1459. }
  1460. elseif (isset($_REQUEST[$stringname]))
  1461. {
  1462. $urlParam = $_REQUEST[$stringname];
  1463. }
  1464. if (isset($urlParam))
  1465. {
  1466. if ($stringname == 'sid' || $stringname == "gid" || $stringname == "oldqid" ||
  1467. $stringname == "qid" || $stringname == "tid" ||
  1468. $stringname == "lid" || $stringname == "ugid"||
  1469. $stringname == "thisstep" || $stringname == "scenario" ||
  1470. $stringname == "cqid" || $stringname == "cid" ||
  1471. $stringname == "qaid" || $stringname == "scid" ||
  1472. $stringname == "loadsecurity")
  1473. {
  1474. return sanitize_int($urlParam);
  1475. }
  1476. elseif ($stringname =="lang" || $stringname =="adminlang")
  1477. {
  1478. return sanitize_languagecode($urlParam);
  1479. }
  1480. elseif ($stringname =="htmleditormode" ||
  1481. $stringname =="subaction" ||
  1482. $stringname =="questionselectormode" ||
  1483. $stringname =="templateeditormode"
  1484. )
  1485. {
  1486. return sanitize_paranoid_string($urlParam);
  1487. }
  1488. elseif ( $stringname =="cquestions")
  1489. {
  1490. return sanitize_cquestions($urlParam);
  1491. }
  1492. return $urlParam;
  1493. }
  1494. else
  1495. {
  1496. return NULL;
  1497. }
  1498. }
  1499. function sendCacheHeaders()
  1500. {
  1501. global $embedded;
  1502. if ( $embedded ) return;
  1503. if (!headers_sent())
  1504. {
  1505. header('P3P:CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"'); // this line lets IE7 run LimeSurvey in an iframe
  1506. header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); // Date in the past
  1507. header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); // always modified
  1508. header("Cache-Control: no-store, no-cache, must-revalidate"); // HTTP/1.1
  1509. header("Cache-Control: post-check=0, pre-check=0", false);
  1510. header("Pragma: no-cache");
  1511. header('Content-Type: text/html; charset=utf-8');
  1512. }
  1513. }
  1514. function getSIDGIDQIDAIDType($fieldcode)
  1515. {
  1516. // use simple parsing to get {sid}, {gid}
  1517. // and what may be {qid} or {qid}{aid} combination
  1518. list($fsid, $fgid, $fqid) = explode('X', $fieldcode);
  1519. $fsid=sanitize_int($fsid);
  1520. $fgid=sanitize_int($fgid);
  1521. if (!$fqid) {$fqid=0;}
  1522. $fqid=sanitize_int($fqid);
  1523. // try a true parsing of fieldcode (can separate qid from aid)
  1524. // but fails for type M and type P multiple choice
  1525. // questions because the SESSION fieldcode is combined
  1526. // and we want here to pass only the sidXgidXqid for type M and P
  1527. $fields=arraySearchByKey($fieldcode, createFieldMap($fsid,'full',false,false,getBaseLanguageFromSurveyID($fsid)), "fieldname", 1);
  1528. if (count($fields) != 0)
  1529. {
  1530. $aRef['sid']=$fields['sid'];
  1531. $aRef['gid']=$fields['gid'];
  1532. $aRef['qid']=$fields['qid'];
  1533. $aRef['aid']=$fields['aid'];
  1534. $aRef['type']=$fields['type'];
  1535. }
  1536. else
  1537. {
  1538. // either the fielcode doesn't match a question
  1539. // or it is a type M or P question
  1540. $aRef['sid']=$fsid;
  1541. $aRef['gid']=$fgid;
  1542. $aRef['qid']=sanitize_int($fqid);
  1543. $s_lang = Survey::model()->findByPk($fsid)->language;
  1544. $fieldtoselect = array('type');
  1545. $condition = "qid = ".$fqid." AND language='".$s_lang."'";
  1546. $result = Questions::model()->findAllByAttributes(array('qid' => $fqid, 'language' => $s_lang));
  1547. if ( count($result) == 0 )
  1548. { // question doesn't exist
  1549. return array();
  1550. }
  1551. else
  1552. { // certainly is type M or P
  1553. foreach ($result as $row)
  1554. {
  1555. $aRef['type']=$row['type'];
  1556. }
  1557. }
  1558. }
  1559. //return array('sid'=>$fsid, "gid"=>$fgid, "qid"=>$fqid);
  1560. return $aRef;
  1561. }
  1562. /**
  1563. * @param type $iSurveyID The Survey ID
  1564. * @param type $sFieldCode Field code of the particular field
  1565. * @param type $sValue The stored response value
  1566. * @param object $oLanguage Initialized limesurvey_lang object for the resulting response data
  1567. * @return string
  1568. */
  1569. function getExtendedAnswer($iSurveyID, $sFieldCode, $sValue, $oLanguage)
  1570. {
  1571. if (is_null($sValue) || $sValue=='') return '';
  1572. $sLanguage = $oLanguage->langcode;
  1573. //Fieldcode used to determine question, $sValue used to match against answer code
  1574. //Returns NULL if question type does not suit
  1575. if (strpos($sFieldCode, "{$iSurveyID}X")===0) //Only check if it looks like a real fieldcode
  1576. {
  1577. $fieldmap = createFieldMap($iSurveyID,'short',false,false,$sLanguage);
  1578. if (isset($fieldmap[$sFieldCode]))
  1579. $fields = $fieldmap[$sFieldCode];
  1580. else
  1581. return false;
  1582. //Find out the question type
  1583. $this_type = $fields['type'];
  1584. switch($this_type)
  1585. {
  1586. case 'D':
  1587. if (trim($sValue)!='')
  1588. {
  1589. $qidattributes = getQuestionAttributeValues($fields['qid']);
  1590. $dateformatdetails = getDateFormatDataForQID($qidattributes, $iSurveyID);
  1591. $sValue=convertDateTimeFormat($sValue,"Y-m-d H:i:s",$dateformatdetails['phpdate']);
  1592. }
  1593. break;
  1594. case 'N':
  1595. if (trim($sValue)!='')
  1596. {
  1597. if(strpos($sValue,".")!==false)
  1598. {
  1599. $sValue=rtrim(rtrim($sValue,"0"),".");
  1600. }
  1601. $qidattributes = getQuestionAttributeValues($fields['qid']);
  1602. if($qidattributes['num_value_int_only'])
  1603. {
  1604. $sValue=number_format($sValue, 0, '', '');
  1605. }
  1606. }
  1607. break;
  1608. case "L":
  1609. case "!":
  1610. case "O":
  1611. case "^":
  1612. case "I":
  1613. case "R":
  1614. $result = Answers::model()->getAnswerFromCode($fields['qid'],$sValue,$sLanguage);
  1615. foreach($result as $row)
  1616. {
  1617. $this_answer=$row['answer'];
  1618. } // while
  1619. if ($sValue == "-oth-")
  1620. {
  1621. $this_answer=$oLanguage->gT("Other");
  1622. }
  1623. break;
  1624. case "M":
  1625. case "J":
  1626. case "P":
  1627. switch($sValue)
  1628. {
  1629. case "Y": $this_answer=$oLanguage->gT("Yes"); break;
  1630. }
  1631. break;
  1632. case "Y":
  1633. switch($sValue)
  1634. {
  1635. case "Y": $this_answer=$oLanguage->gT("Yes"); break;
  1636. case "N": $this_answer=$oLanguage->gT("No"); break;
  1637. default: $this_answer=$oLanguage->gT("No answer");
  1638. }
  1639. break;
  1640. case "G":
  1641. switch($sValue)
  1642. {
  1643. case "M": $this_answer=$oLanguage->gT("Male"); break;
  1644. case "F": $this_answer=$oLanguage->gT("Female"); break;
  1645. default: $this_answer=$oLanguage->gT("No answer");
  1646. }
  1647. break;
  1648. case "C":
  1649. switch($sValue)
  1650. {
  1651. case "Y": $this_answer=$oLanguage->gT("Yes"); break;
  1652. case "N": $this_answer=$oLanguage->gT("No"); break;
  1653. case "U": $this_answer=$oLanguage->gT("Uncertain"); break;
  1654. }
  1655. break;
  1656. case "E":
  1657. switch($sValue)
  1658. {
  1659. case "I": $this_answer=$oLanguage->gT("Increase"); break;
  1660. case "D": $this_answer=$oLanguage->gT("Decrease"); break;
  1661. case "S": $this_answer=$oLanguage->gT("Same"); break;
  1662. }
  1663. break;
  1664. case "F":
  1665. case "H":
  1666. case "1":
  1667. $aConditions=array('qid' => $fields['qid'], 'code' => $sValue, 'language' => $sLanguage);
  1668. if (isset($fields['scale_id']))
  1669. {
  1670. $iScaleID=$fields['scale_id'];
  1671. }
  1672. else
  1673. {
  1674. $iScaleID=0;
  1675. }
  1676. $result = Answers::model()->getAnswerFromCode($fields['qid'],$sValue,$sLanguage,$iScaleID);
  1677. foreach($result as $row)
  1678. {
  1679. $this_answer=$row['answer'];
  1680. } // while
  1681. $this_answer=$row['answer'];
  1682. if ($sValue == "-oth-")
  1683. {
  1684. $this_answer=$oLanguage->gT("Other");
  1685. }
  1686. break;
  1687. case "|": //File upload
  1688. if (substr($sFieldCode, -9) == 'filecount') {
  1689. $this_answer = $oLanguage->gT("File count");
  1690. } else {
  1691. //Show the filename, size, title and comment -- no link!
  1692. $files = json_decode($sValue);
  1693. $sValue = '';
  1694. if (is_array($files)) {
  1695. foreach ($files as $file) {
  1696. $sValue .= $file->name .
  1697. ' (' . $file->size . 'KB) ' .
  1698. strip_tags($file->title) .
  1699. ' - ' . strip_tags($file->comment) . "<br/>";
  1700. }
  1701. }
  1702. }
  1703. break;
  1704. default:
  1705. ;
  1706. } // switch
  1707. }
  1708. switch($sFieldCode)
  1709. {
  1710. case 'submitdate':
  1711. if (trim($sValue)!='')
  1712. {
  1713. $dateformatdetails = getDateFormatDataForQID(array('date_format'=>''), $iSurveyID);
  1714. $sValue=convertDateTimeFormat($sValue,"Y-m-d H:i:s",$dateformatdetails['phpdate'].' H:i:s');
  1715. }
  1716. break;
  1717. }
  1718. if (isset($this_answer))
  1719. {
  1720. return $this_answer." [$sValue]";
  1721. }
  1722. else
  1723. {
  1724. return $sValue;
  1725. }
  1726. }
  1727. /*function validateEmailAddress($email)
  1728. {
  1729. // Create the syntactical validation regular expression
  1730. // Validate the syntax
  1731. // see http://data.iana.org/TLD/tlds-alpha-by-domain.txt
  1732. $maxrootdomainlength = 6;
  1733. return ( ! preg_match("/^[_a-zA-Z0-9-]+(\.[_a-zA-Z0-9-]+)*@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.(([0-9]{1,3})|([a-zA-Z]{2,".$maxrootdomainlength."}))$/ix", $email)) ? FALSE : TRUE;
  1734. }*/
  1735. function validateEmailAddress($email){
  1736. $no_ws_ctl = "[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]";
  1737. $alpha = "[\\x41-\\x5a\\x61-\\x7a]";
  1738. $digit = "[\\x30-\\x39]";
  1739. $cr = "\\x0d";
  1740. $lf = "\\x0a";
  1741. $crlf = "(?:$cr$lf)";
  1742. $obs_char = "[\\x00-\\x09\\x0b\\x0c\\x0e-\\x7f]";
  1743. $obs_text = "(?:$lf*$cr*(?:$obs_char$lf*$cr*)*)";
  1744. $text = "(?:[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f]|$obs_text)";
  1745. $text = "(?:$lf*$cr*$obs_char$lf*$cr*)";
  1746. $obs_qp = "(?:\\x5c[\\x00-\\x7f])";
  1747. $quoted_pair = "(?:\\x5c$text|$obs_qp)";
  1748. $wsp = "[\\x20\\x09]";
  1749. $obs_fws = "(?:$wsp+(?:$crlf$wsp+)*)";
  1750. $fws = "(?:(?:(?:$wsp*$crlf)?$wsp+)|$obs_fws)";
  1751. $ctext = "(?:$no_ws_ctl|[\\x21-\\x27\\x2A-\\x5b\\x5d-\\x7e])";
  1752. $ccontent = "(?:$ctext|$quoted_pair)";
  1753. $comment = "(?:\\x28(?:$fws?$ccontent)*$fws?\\x29)";
  1754. $cfws = "(?:(?:$fws?$comment)*(?:$fws?$comment|$fws))";
  1755. $outer_ccontent_dull = "(?:$fws?$ctext|$quoted_pair)";
  1756. $outer_ccontent_nest = "(?:$fws?$comment)";
  1757. $outer_comment = "(?:\\x28$outer_ccontent_dull*(?:$outer_ccontent_nest$outer_ccontent_dull*)+$fws?\\x29)";
  1758. $atext = "(?:$alpha|$digit|[\\x21\\x23-\\x27\\x2a\\x2b\\x2d\\x2f\\x3d\\x3f\\x5e\\x5f\\x60\\x7b-\\x7e])";
  1759. $atext_domain = "(?:$alpha|$digit|[\\x2b\\x2d\\x5f])";
  1760. $atom = "(?:$cfws?(?:$atext)+$cfws?)";
  1761. $atom_domain = "(?:$cfws?(?:$atext_domain)+$cfws?)";
  1762. $qtext = "(?:$no_ws_ctl|[\\x21\\x23-\\x5b\\x5d-\\x7e])";
  1763. $qcontent = "(?:$qtext|$quoted_pair)";
  1764. $quoted_string = "(?:$cfws?\\x22(?:$fws?$qcontent)*$fws?\\x22$cfws?)";
  1765. $quoted_string = "(?:$cfws?\\x22(?:$fws?$qcontent)+$fws?\\x22$cfws?)";
  1766. $word = "(?:$atom|$quoted_string)";
  1767. $obs_local_part = "(?:$word(?:\\x2e$word)*)";
  1768. $obs_domain = "(?:$atom_domain(?:\\x2e$atom_domain)*)";
  1769. $dot_atom_text = "(?:$atext+(?:\\x2e$atext+)*)";
  1770. $dot_atom_text_domain = "(?:$atext_domain+(?:\\x2e$atext_domain+)*)";
  1771. $dot_atom = "(?:$cfws?$dot_atom_text$cfws?)";
  1772. $dot_atom_domain = "(?:$cfws?$dot_atom_text_domain$cfws?)";
  1773. $dtext = "(?:$no_ws_ctl|[\\x21-\\x5a\\x5e-\\x7e])";
  1774. $dcontent = "(?:$dtext|$quoted_pair)";
  1775. $domain_literal = "(?:$cfws?\\x5b(?:$fws?$dcontent)*$fws?\\x5d$cfws?)";
  1776. $local_part = "(($dot_atom)|($quoted_string)|($obs_local_part))";
  1777. $domain = "(($dot_atom_domain)|($domain_literal)|($obs_domain))";
  1778. $addr_spec = "$local_part\\x40$domain";
  1779. if (strlen($email) > 256) return FALSE;
  1780. $email = stripComments($outer_comment, $email, "(x)");
  1781. if (!preg_match("!^$addr_spec$!", $email, $m)){
  1782. return FALSE;
  1783. }
  1784. $bits = array(
  1785. 'local' => isset($m[1]) ? $m[1] : '',
  1786. 'local-atom' => isset($m[2]) ? $m[2] : '',
  1787. 'local-quoted' => isset($m[3]) ? $m[3] : '',
  1788. 'local-obs' => isset($m[4]) ? $m[4] : '',
  1789. 'domain' => isset($m[5]) ? $m[5] : '',
  1790. 'domain-atom' => isset($m[6]) ? $m[6] : '',
  1791. 'domain-literal' => isset($m[7]) ? $m[7] : '',
  1792. 'domain-obs' => isset($m[8]) ? $m[8] : '',
  1793. );
  1794. $bits['local'] = stripComments($comment, $bits['local']);
  1795. $bits['domain'] = stripComments($comment, $bits['domain']);
  1796. if (strlen($bits['local']) > 64) return FALSE;
  1797. if (strlen($bits['domain']) > 255) return FALSE;
  1798. if (strlen($bits['domain-literal'])){
  1799. $Snum = "(\d{1,3})";
  1800. $IPv4_address_literal = "$Snum\.$Snum\.$Snum\.$Snum";
  1801. $IPv6_hex = "(?:[0-9a-fA-F]{1,4})";
  1802. $IPv6_full = "IPv6\:$IPv6_hex(:?\:$IPv6_hex){7}";
  1803. $IPv6_comp_part = "(?:$IPv6_hex(?:\:$IPv6_hex){0,5})?";
  1804. $IPv6_comp = "IPv6\:($IPv6_comp_part\:\:$IPv6_comp_part)";
  1805. $IPv6v4_full = "IPv6\:$IPv6_hex(?:\:$IPv6_hex){5}\:$IPv4_address_literal";
  1806. $IPv6v4_comp_part = "$IPv6_hex(?:\:$IPv6_hex){0,3}";
  1807. $IPv6v4_comp = "IPv6\:((?:$IPv6v4_comp_part)?\:\:(?:$IPv6v4_comp_part\:)?)$IPv4_address_literal";
  1808. if (preg_match("!^\[$IPv4_address_literal\]$!", $bits['domain'], $m)){
  1809. if (intval($m[1]) > 255) return FALSE;
  1810. if (intval($m[2]) > 255) return FALSE;
  1811. if (intval($m[3]) > 255) return FALSE;
  1812. if (intval($m[4]) > 255) return FALSE;
  1813. }else{
  1814. while (1){
  1815. if (preg_match("!^\[$IPv6_full\]$!", $bits['domain'])){
  1816. break;
  1817. }
  1818. if (preg_match("!^\[$IPv6_comp\]$!", $bits['domain'], $m)){
  1819. list($a, $b) = explode('::', $m[1]);
  1820. $folded = (strlen($a) && strlen($b)) ? "$a:$b" : "$a$b";
  1821. $groups = explode(':', $folded);
  1822. if (count($groups) > 6) return FALSE;
  1823. break;
  1824. }
  1825. if (preg_match("!^\[$IPv6v4_full\]$!", $bits['domain'], $m)){
  1826. if (intval($m[1]) > 255) return FALSE;
  1827. if (intval($m[2]) > 255) return FALSE;
  1828. if (intval($m[3]) > 255) return FALSE;
  1829. if (intval($m[4]) > 255) return FALSE;
  1830. break;
  1831. }
  1832. if (preg_match("!^\[$IPv6v4_comp\]$!", $bits['domain'], $m)){
  1833. list($a, $b) = explode('::', $m[1]);
  1834. $b = substr($b, 0, -1); # remove the trailing colon before the IPv4 address
  1835. $folded = (strlen($a) && strlen($b)) ? "$a:$b" : "$a$b";
  1836. $groups = explode(':', $folded);
  1837. if (count($groups) > 4) return FALSE;
  1838. break;
  1839. }
  1840. return FALSE;
  1841. }
  1842. }
  1843. }else{
  1844. $labels = explode('.', $bits['domain']);
  1845. if (count($labels) == 1) return FALSE;
  1846. foreach ($labels as $label){
  1847. if (strlen($label) > 63) return FALSE;
  1848. if (substr($label, 0, 1) == '-') return FALSE;
  1849. if (substr($label, -1) == '-') return FALSE;
  1850. }
  1851. if (preg_match('!^[0-9]+$!', array_pop($labels))) return FALSE;
  1852. }
  1853. return TRUE;
  1854. }
  1855. ##################################################################################
  1856. function stripComments($comment, $email, $replace=''){
  1857. while (1){
  1858. $new = preg_replace("!$comment!", $replace, $email);
  1859. if (strlen($new) == strlen($email)){
  1860. return $email;
  1861. }
  1862. $email = $new;
  1863. }
  1864. }
  1865. function validateTemplateDir($sTemplateName)
  1866. {
  1867. $usertemplaterootdir = Yii::app()->getConfig('usertemplaterootdir');
  1868. $standardtemplaterootdir = Yii::app()->getConfig('standardtemplaterootdir');
  1869. $sDefaultTemplate = Yii::app()->getConfig('defaulttemplate');
  1870. if (is_dir("$usertemplaterootdir/{$sTemplateName}/"))
  1871. {
  1872. return $sTemplateName;
  1873. }
  1874. elseif (is_dir("$standardtemplaterootdir/{$sTemplateName}/"))
  1875. {
  1876. return $sTemplateName;
  1877. }
  1878. elseif (is_dir("$standardtemplaterootdir/{$sDefaultTemplate}/"))
  1879. {
  1880. return $sDefaultTemplate;
  1881. }
  1882. elseif (is_dir("$usertemplaterootdir/{$sDefaultTemplate}/"))
  1883. {
  1884. return $sDefaultTemplate;
  1885. }
  1886. else
  1887. {
  1888. return 'default';
  1889. }
  1890. }
  1891. /**
  1892. *This functions generates a a summary containing the SGQA for questions of a survey, enriched with options per question
  1893. * It can be used for the generation of statistics. Derived from Statistics_userController
  1894. * @param int $iSurveyID Id of the Survey in question
  1895. * @param array $aFilters an array which is the result of a query in Questions model
  1896. * @param string $sLanguage
  1897. * @return array The summary
  1898. */
  1899. function createCompleteSGQA($iSurveyID,$aFilters,$sLanguage) {
  1900. foreach ($aFilters as $flt)
  1901. {
  1902. Yii::app()->loadHelper("surveytranslator");
  1903. $myfield = "{$iSurveyID}X{$flt['gid']}X{$flt['qid']}";
  1904. $oSurvey = Survey::model()->findByPk($iSurveyID);
  1905. $aAdditionalLanguages = array_filter(explode(" ", $oSurvey->additional_languages));
  1906. if (is_null($sLanguage)|| !in_array($sLanguage,$aAdditionalLanguages))
  1907. $sLanguage = $oSurvey->language;
  1908. switch ($flt['type'])
  1909. {
  1910. case "K": // Multiple Numerical
  1911. case "Q": // Multiple Short Text
  1912. //get answers
  1913. $result = Questions::model()->getQuestionsForStatistics('title as code, question as answer', "parent_qid=$flt[qid] AND language = '{$sLanguage}'", 'question_order');
  1914. //go through all the (multiple) answers
  1915. foreach($result as $row)
  1916. {
  1917. $myfield2=$flt['type'].$myfield.reset($row);
  1918. $allfields[] = $myfield2;
  1919. }
  1920. break;
  1921. case "A": // ARRAY OF 5 POINT CHOICE QUESTIONS
  1922. case "B": // ARRAY OF 10 POINT CHOICE QUESTIONS
  1923. case "C": // ARRAY OF YES\No\$clang->gT("Uncertain") QUESTIONS
  1924. case "E": // ARRAY OF Increase/Same/Decrease QUESTIONS
  1925. case "F": // FlEXIBLE ARRAY
  1926. case "H": // ARRAY (By Column)
  1927. //get answers
  1928. $result = Questions::model()->getQuestionsForStatistics('title, question', "parent_qid=$flt[qid] AND language = '{$sLanguage}'", 'question_order');
  1929. //go through all the (multiple) answers
  1930. foreach($result as $row)
  1931. {
  1932. $myfield2 = $myfield.reset($row);
  1933. $allfields[]=$myfield2;
  1934. }
  1935. break;
  1936. // all "free text" types (T, U, S) get the same prefix ("T")
  1937. case "T": // Long free text
  1938. case "U": // Huge free text
  1939. case "S": // Short free text
  1940. $myfield="T$myfield";
  1941. $allfields[] = $myfield;
  1942. break;
  1943. case ";": //ARRAY (Multi Flex) (Text)
  1944. case ":": //ARRAY (Multi Flex) (Numbers)
  1945. $result = Questions::model()->getQuestionsForStatistics('title, question', "parent_qid=$flt[qid] AND language = '{$sLanguage}' AND scale_id = 0", 'question_order');
  1946. foreach($result as $row)
  1947. {
  1948. $fresult = Questions::model()->getQuestionsForStatistics('title, question', "parent_qid=$flt[qid] AND language = '{$sLanguage}' AND scale_id = 1", 'question_order');
  1949. foreach($fresult as $frow)
  1950. {
  1951. $myfield2 = $myfield . reset($row) . "_" . $frow['title'];
  1952. $allfields[]=$myfield2;
  1953. }
  1954. }
  1955. break;
  1956. case "R": //RANKING
  1957. //get some answers
  1958. $result = Answers::model()->getQuestionsForStatistics('code, answer', "qid=$flt[qid] AND language = '{$sLanguage}'", 'sortorder, answer');
  1959. //get number of answers
  1960. //loop through all answers. if there are 3 items to rate there will be 3 statistics
  1961. $i=0;
  1962. foreach($result as $row)
  1963. {
  1964. $i++;
  1965. $myfield2 = "R" . $myfield . $i . "-" . strlen($i);
  1966. $allfields[]=$myfield2;
  1967. }
  1968. break;
  1969. //Boilerplate questions are only used to put some text between other questions -> no analysis needed
  1970. case "X": //This is a boilerplate question and it has no business in this script
  1971. break;
  1972. case "1": // MULTI SCALE
  1973. //get answers
  1974. $result = Questions::model()->getQuestionsForStatistics('title, question', "parent_qid=$flt[qid] AND language = '{$sLanguage}'", 'question_order');
  1975. //loop through answers
  1976. foreach($result as $row)
  1977. {
  1978. //----------------- LABEL 1 ---------------------
  1979. $myfield2 = $myfield . reset($row)."#0";
  1980. $allfields[]=$myfield2;
  1981. //----------------- LABEL 2 ---------------------
  1982. $myfield2 = $myfield . reset($row)."#1";
  1983. $allfields[]=$myfield2;
  1984. } //end WHILE -> loop through all answers
  1985. break;
  1986. case "P": //P - Multiple choice with comments
  1987. case "M": //M - Multiple choice
  1988. case "N": //N - Numerical input
  1989. case "D": //D - Date
  1990. $myfield2 = $flt['type'].$myfield;
  1991. $allfields[]=$myfield2;
  1992. break;
  1993. default: //Default settings
  1994. $allfields[] = $myfield;
  1995. break;
  1996. } //end switch
  1997. }
  1998. return $allfields;
  1999. }
  2000. /**
  2001. * This function generates an array containing the fieldcode, and matching data in the same order as the activate script
  2002. *
  2003. * @param string $surveyid The Survey ID
  2004. * @param mixed $style 'short' (default) or 'full' - full creates extra information like default values
  2005. * @param mixed $force_refresh - Forces to really refresh the array, not just take the session copy
  2006. * @param int $questionid Limit to a certain qid only (for question preview) - default is false
  2007. * @param string $sQuestionLanguage The language to use
  2008. * @return array
  2009. */
  2010. function createFieldMap($surveyid, $style='short', $force_refresh=false, $questionid=false, $sLanguage) {
  2011. global $aDuplicateQIDs;
  2012. $sLanguage = sanitize_languagecode($sLanguage);
  2013. $surveyid = sanitize_int($surveyid);
  2014. $clang = new Limesurvey_lang($sLanguage);
  2015. //checks to see if fieldmap has already been built for this page.
  2016. if (isset(Yii::app()->session['fieldmap-' . $surveyid . $sLanguage]) && !$force_refresh && $questionid == false) {
  2017. return Yii::app()->session['fieldmap-' . $surveyid . $sLanguage];
  2018. }
  2019. $fieldmap["id"]=array("fieldname"=>"id", 'sid'=>$surveyid, 'type'=>"id", "gid"=>"", "qid"=>"", "aid"=>"");
  2020. if ($style == "full")
  2021. {
  2022. $fieldmap["id"]['title']="";
  2023. $fieldmap["id"]['question']=$clang->gT("Response ID");
  2024. $fieldmap["id"]['group_name']="";
  2025. }
  2026. $fieldmap["submitdate"]=array("fieldname"=>"submitdate", 'type'=>"submitdate", 'sid'=>$surveyid, "gid"=>"", "qid"=>"", "aid"=>"");
  2027. if ($style == "full")
  2028. {
  2029. $fieldmap["submitdate"]['title']="";
  2030. $fieldmap["submitdate"]['question']=$clang->gT("Date submitted");
  2031. $fieldmap["submitdate"]['group_name']="";
  2032. }
  2033. $fieldmap["lastpage"]=array("fieldname"=>"lastpage", 'sid'=>$surveyid, 'type'=>"lastpage", "gid"=>"", "qid"=>"", "aid"=>"");
  2034. if ($style == "full")
  2035. {
  2036. $fieldmap["lastpage"]['title']="";
  2037. $fieldmap["lastpage"]['question']=$clang->gT("Last page");
  2038. $fieldmap["lastpage"]['group_name']="";
  2039. }
  2040. $fieldmap["startlanguage"]=array("fieldname"=>"startlanguage", 'sid'=>$surveyid, 'type'=>"startlanguage", "gid"=>"", "qid"=>"", "aid"=>"");
  2041. if ($style == "full")
  2042. {
  2043. $fieldmap["startlanguage"]['title']="";
  2044. $fieldmap["startlanguage"]['question']=$clang->gT("Start language");
  2045. $fieldmap["startlanguage"]['group_name']="";
  2046. }
  2047. // Select which question IDs have default values
  2048. $_aDefaultValues = Defaultvalues::model()->with(array('question' => array('condition' => 'question.sid=' . $surveyid)))->findAll();
  2049. $aDefaultValues = array();
  2050. foreach ($_aDefaultValues as $k => $v)
  2051. $aDefaultValues[] = $v->qid;
  2052. //Check for any additional fields for this survey and create necessary fields (token and datestamp and ipaddr)
  2053. $prow = Survey::model()->findByPk($surveyid)->getAttributes(); //Checked
  2054. if ($prow['anonymized'] == "N" && Survey::model()->hasTokens($surveyid)) {
  2055. $fieldmap["token"]=array("fieldname"=>"token", 'sid'=>$surveyid, 'type'=>"token", "gid"=>"", "qid"=>"", "aid"=>"");
  2056. if ($style == "full")
  2057. {
  2058. $fieldmap["token"]['title']="";
  2059. $fieldmap["token"]['question']=$clang->gT("Token");
  2060. $fieldmap["token"]['group_name']="";
  2061. }
  2062. }
  2063. if ($prow['datestamp'] == "Y")
  2064. {
  2065. $fieldmap["startdate"]=array("fieldname"=>"startdate",
  2066. 'type'=>"startdate",
  2067. 'sid'=>$surveyid,
  2068. "gid"=>"",
  2069. "qid"=>"",
  2070. "aid"=>"");
  2071. if ($style == "full")
  2072. {
  2073. $fieldmap["startdate"]['title']="";
  2074. $fieldmap["startdate"]['question']=$clang->gT("Date started");
  2075. $fieldmap["startdate"]['group_name']="";
  2076. }
  2077. $fieldmap["datestamp"]=array("fieldname"=>"datestamp",
  2078. 'type'=>"datestamp",
  2079. 'sid'=>$surveyid,
  2080. "gid"=>"",
  2081. "qid"=>"",
  2082. "aid"=>"");
  2083. if ($style == "full")
  2084. {
  2085. $fieldmap["datestamp"]['title']="";
  2086. $fieldmap["datestamp"]['question']=$clang->gT("Date last action");
  2087. $fieldmap["datestamp"]['group_name']="";
  2088. }
  2089. }
  2090. if ($prow['ipaddr'] == "Y")
  2091. {
  2092. $fieldmap["ipaddr"]=array("fieldname"=>"ipaddr",
  2093. 'type'=>"ipaddress",
  2094. 'sid'=>$surveyid,
  2095. "gid"=>"",
  2096. "qid"=>"",
  2097. "aid"=>"");
  2098. if ($style == "full")
  2099. {
  2100. $fieldmap["ipaddr"]['title']="";
  2101. $fieldmap["ipaddr"]['question']=$clang->gT("IP address");
  2102. $fieldmap["ipaddr"]['group_name']="";
  2103. }
  2104. }
  2105. // Add 'refurl' to fieldmap.
  2106. if ($prow['refurl'] == "Y")
  2107. {
  2108. $fieldmap["refurl"]=array("fieldname"=>"refurl", 'type'=>"url", 'sid'=>$surveyid, "gid"=>"", "qid"=>"", "aid"=>"");
  2109. if ($style == "full")
  2110. {
  2111. $fieldmap["refurl"]['title']="";
  2112. $fieldmap["refurl"]['question']=$clang->gT("Referrer URL");
  2113. $fieldmap["refurl"]['group_name']="";
  2114. }
  2115. }
  2116. // Collect all default values once so don't need separate query for each question with defaults
  2117. // First collect language specific defaults
  2118. $defaultsQuery = "SELECT a.qid, a.sqid, a.scale_id, a.specialtype, a.defaultvalue"
  2119. . " FROM {{defaultvalues}} as a, {{questions}} as b"
  2120. . " WHERE a.qid = b.qid"
  2121. . " AND a.language = b.language"
  2122. . " AND a.language = '{$sLanguage}'"
  2123. . " AND b.same_default=0"
  2124. . " AND b.sid = ".$surveyid;
  2125. $defaultResults = Yii::app()->db->createCommand($defaultsQuery)->queryAll();
  2126. $defaultValues = array(); // indexed by question then subquestion
  2127. foreach($defaultResults as $dv)
  2128. {
  2129. if ($dv['specialtype'] != '') {
  2130. $sq = $dv['specialtype'];
  2131. }
  2132. else {
  2133. $sq = $dv['sqid'];
  2134. }
  2135. $defaultValues[$dv['qid'].'~'.$sq] = $dv['defaultvalue'];
  2136. }
  2137. // Now overwrite language-specific defaults (if any) base language values for each question that uses same_defaults=1
  2138. $baseLanguage = getBaseLanguageFromSurveyID($surveyid);
  2139. $defaultsQuery = "SELECT a.qid, a.sqid, a.scale_id, a.specialtype, a.defaultvalue"
  2140. . " FROM {{defaultvalues}} as a, {{questions}} as b"
  2141. . " WHERE a.qid = b.qid"
  2142. . " AND a.language = b.language"
  2143. . " AND a.language = '{$baseLanguage}'"
  2144. . " AND b.same_default=1"
  2145. . " AND b.sid = ".$surveyid;
  2146. $defaultResults = Yii::app()->db->createCommand($defaultsQuery)->queryAll();
  2147. foreach($defaultResults as $dv)
  2148. {
  2149. if ($dv['specialtype'] != '') {
  2150. $sq = $dv['specialtype'];
  2151. }
  2152. else {
  2153. $sq = $dv['sqid'];
  2154. }
  2155. $defaultValues[$dv['qid'].'~'.$sq] = $dv['defaultvalue'];
  2156. }
  2157. $qtypes=getQuestionTypeList('','array');
  2158. $aquery = "SELECT * "
  2159. ." FROM {{questions}} as questions, {{groups}} as groups"
  2160. ." WHERE questions.gid=groups.gid AND "
  2161. ." questions.sid=$surveyid AND "
  2162. ." questions.language='{$sLanguage}' AND "
  2163. ." questions.parent_qid=0 AND "
  2164. ." groups.language='{$sLanguage}' ";
  2165. if ($questionid!==false)
  2166. {
  2167. $aquery.=" and questions.qid={$questionid} ";
  2168. }
  2169. $aquery.=" ORDER BY group_order, question_order";
  2170. $aresult = Yii::app()->db->createCommand($aquery)->queryAll();
  2171. $questionSeq=-1; // this is incremental question sequence across all groups
  2172. $groupSeq=-1;
  2173. $_groupOrder=-1;
  2174. foreach ($aresult as $arow) //With each question, create the appropriate field(s))
  2175. {
  2176. ++$questionSeq;
  2177. // fix fact taht group_order may have gaps
  2178. if ($_groupOrder != $arow['group_order']) {
  2179. $_groupOrder = $arow['group_order'];
  2180. ++$groupSeq;
  2181. }
  2182. // Conditions indicators are obsolete with EM. However, they are so tightly coupled into LS code that easider to just set values to 'N' for now and refactor later.
  2183. $conditions = 'N';
  2184. $usedinconditions = 'N';
  2185. // Field identifier
  2186. // GXQXSXA
  2187. // G=Group Q=Question S=Subquestion A=Answer Option
  2188. // If S or A don't exist then set it to 0
  2189. // Implicit (subqestion intermal to a question type ) or explicit qubquestions/answer count starts at 1
  2190. // Types "L", "!", "O", "D", "G", "N", "X", "Y", "5", "S", "T", "U"
  2191. $fieldname="{$arow['sid']}X{$arow['gid']}X{$arow['qid']}";
  2192. if ($qtypes[$arow['type']]['subquestions']==0 && $arow['type'] != "R" && $arow['type'] != "|")
  2193. {
  2194. if (isset($fieldmap[$fieldname])) $aDuplicateQIDs[$arow['qid']]=array('fieldname'=>$fieldname,'question'=>$arow['question'],'gid'=>$arow['gid']);
  2195. $fieldmap[$fieldname]=array("fieldname"=>$fieldname, 'type'=>"{$arow['type']}", 'sid'=>$surveyid, "gid"=>$arow['gid'], "qid"=>$arow['qid'], "aid"=>"");
  2196. if ($style == "full")
  2197. {
  2198. $fieldmap[$fieldname]['title']=$arow['title'];
  2199. $fieldmap[$fieldname]['question']=$arow['question'];
  2200. $fieldmap[$fieldname]['group_name']=$arow['group_name'];
  2201. $fieldmap[$fieldname]['mandatory']=$arow['mandatory'];
  2202. $fieldmap[$fieldname]['hasconditions']=$conditions;
  2203. $fieldmap[$fieldname]['usedinconditions']=$usedinconditions;
  2204. $fieldmap[$fieldname]['questionSeq']=$questionSeq;
  2205. $fieldmap[$fieldname]['groupSeq']=$groupSeq;
  2206. if (isset($defaultValues[$arow['qid'].'~0'])) {
  2207. $fieldmap[$fieldname]['defaultvalue'] = $defaultValues[$arow['qid'].'~0'];
  2208. }
  2209. }
  2210. switch($arow['type'])
  2211. {
  2212. case "L": //RADIO LIST
  2213. case "!": //DROPDOWN LIST
  2214. if ($arow['other'] == "Y")
  2215. {
  2216. $fieldname="{$arow['sid']}X{$arow['gid']}X{$arow['qid']}other";
  2217. if (isset($fieldmap[$fieldname])) $aDuplicateQIDs[$arow['qid']]=array('fieldname'=>$fieldname,'question'=>$arow['question'],'gid'=>$arow['gid']);
  2218. $fieldmap[$fieldname]=array("fieldname"=>$fieldname,
  2219. 'type'=>$arow['type'],
  2220. 'sid'=>$surveyid,
  2221. "gid"=>$arow['gid'],
  2222. "qid"=>$arow['qid'],
  2223. "aid"=>"other");
  2224. // dgk bug fix line above. aid should be set to "other" for export to append to the field name in the header line.
  2225. if ($style == "full")
  2226. {
  2227. $fieldmap[$fieldname]['title']=$arow['title'];
  2228. $fieldmap[$fieldname]['question']=$arow['question'];
  2229. $fieldmap[$fieldname]['subquestion']=$clang->gT("Other");
  2230. $fieldmap[$fieldname]['group_name']=$arow['group_name'];
  2231. $fieldmap[$fieldname]['mandatory']=$arow['mandatory'];
  2232. $fieldmap[$fieldname]['hasconditions']=$conditions;
  2233. $fieldmap[$fieldname]['usedinconditions']=$usedinconditions;
  2234. $fieldmap[$fieldname]['questionSeq']=$questionSeq;
  2235. $fieldmap[$fieldname]['groupSeq']=$groupSeq;
  2236. if (isset($defaultValues[$arow['qid'].'~other'])) {
  2237. $fieldmap[$fieldname]['defaultvalue'] = $defaultValues[$arow['qid'].'~other'];
  2238. }
  2239. }
  2240. }
  2241. break;
  2242. case "O": //DROPDOWN LIST WITH COMMENT
  2243. $fieldname="{$arow['sid']}X{$arow['gid']}X{$arow['qid']}comment";
  2244. if (isset($fieldmap[$fieldname])) $aDuplicateQIDs[$arow['qid']]=array('fieldname'=>$fieldname,'question'=>$arow['question'],'gid'=>$arow['gid']);
  2245. $fieldmap[$fieldname]=array("fieldname"=>$fieldname,
  2246. 'type'=>$arow['type'],
  2247. 'sid'=>$surveyid,
  2248. "gid"=>$arow['gid'],
  2249. "qid"=>$arow['qid'],
  2250. "aid"=>"comment");
  2251. // dgk bug fix line below. aid should be set to "comment" for export to append to the field name in the header line. Also needed set the type element correctly.
  2252. if ($style == "full")
  2253. {
  2254. $fieldmap[$fieldname]['title']=$arow['title'];
  2255. $fieldmap[$fieldname]['question']=$arow['question'];
  2256. $fieldmap[$fieldname]['subquestion']=$clang->gT("Comment");
  2257. $fieldmap[$fieldname]['group_name']=$arow['group_name'];
  2258. $fieldmap[$fieldname]['mandatory']=$arow['mandatory'];
  2259. $fieldmap[$fieldname]['hasconditions']=$conditions;
  2260. $fieldmap[$fieldname]['usedinconditions']=$usedinconditions;
  2261. $fieldmap[$fieldname]['questionSeq']=$questionSeq;
  2262. $fieldmap[$fieldname]['groupSeq']=$groupSeq;
  2263. }
  2264. break;
  2265. }
  2266. }
  2267. // For Multi flexi question types
  2268. elseif ($qtypes[$arow['type']]['subquestions']==2 && $qtypes[$arow['type']]['answerscales']==0)
  2269. {
  2270. //MULTI FLEXI
  2271. $abrows = getSubQuestions($surveyid,$arow['qid'],$sLanguage);
  2272. //Now first process scale=1
  2273. $answerset=array();
  2274. $answerList = array();
  2275. foreach ($abrows as $key=>$abrow)
  2276. {
  2277. if($abrow['scale_id']==1) {
  2278. $answerset[]=$abrow;
  2279. $answerList[] = array(
  2280. 'code'=>$abrow['title'],
  2281. 'answer'=>$abrow['question'],
  2282. );
  2283. unset($abrows[$key]);
  2284. }
  2285. }
  2286. reset($abrows);
  2287. foreach ($abrows as $abrow)
  2288. {
  2289. foreach($answerset as $answer)
  2290. {
  2291. $fieldname="{$arow['sid']}X{$arow['gid']}X{$arow['qid']}{$abrow['title']}_{$answer['title']}";
  2292. if (isset($fieldmap[$fieldname])) $aDuplicateQIDs[$arow['qid']]=array('fieldname'=>$fieldname,'question'=>$arow['question'],'gid'=>$arow['gid']);
  2293. $fieldmap[$fieldname]=array("fieldname"=>$fieldname,
  2294. 'type'=>$arow['type'],
  2295. 'sid'=>$surveyid,
  2296. "gid"=>$arow['gid'],
  2297. "qid"=>$arow['qid'],
  2298. "aid"=>$abrow['title']."_".$answer['title'],
  2299. "sqid"=>$abrow['qid']);
  2300. if ($abrow['other']=="Y") {$alsoother="Y";}
  2301. if ($style == "full")
  2302. {
  2303. $fieldmap[$fieldname]['title']=$arow['title'];
  2304. $fieldmap[$fieldname]['question']=$arow['question'];
  2305. $fieldmap[$fieldname]['subquestion1']=$abrow['question'];
  2306. $fieldmap[$fieldname]['subquestion2']=$answer['question'];
  2307. $fieldmap[$fieldname]['group_name']=$arow['group_name'];
  2308. $fieldmap[$fieldname]['mandatory']=$arow['mandatory'];
  2309. $fieldmap[$fieldname]['hasconditions']=$conditions;
  2310. $fieldmap[$fieldname]['usedinconditions']=$usedinconditions;
  2311. $fieldmap[$fieldname]['questionSeq']=$questionSeq;
  2312. $fieldmap[$fieldname]['groupSeq']=$groupSeq;
  2313. $fieldmap[$fieldname]['preg']=$arow['preg'];
  2314. $fieldmap[$fieldname]['answerList']=$answerList;
  2315. }
  2316. }
  2317. }
  2318. unset($answerset);
  2319. }
  2320. elseif ($arow['type'] == "1")
  2321. {
  2322. $abrows = getSubQuestions($surveyid,$arow['qid'],$sLanguage);
  2323. foreach ($abrows as $abrow)
  2324. {
  2325. $fieldname="{$arow['sid']}X{$arow['gid']}X{$arow['qid']}{$abrow['title']}#0";
  2326. if (isset($fieldmap[$fieldname])) $aDuplicateQIDs[$arow['qid']]=array('fieldname'=>$fieldname,'question'=>$arow['question'],'gid'=>$arow['gid']);
  2327. $fieldmap[$fieldname]=array("fieldname"=>$fieldname, 'type'=>$arow['type'], 'sid'=>$surveyid, "gid"=>$arow['gid'], "qid"=>$arow['qid'], "aid"=>$abrow['title'], "scale_id"=>0);
  2328. if ($style == "full")
  2329. {
  2330. $fieldmap[$fieldname]['title']=$arow['title'];
  2331. $fieldmap[$fieldname]['question']=$arow['question'];
  2332. $fieldmap[$fieldname]['subquestion']=$abrow['question'];
  2333. $fieldmap[$fieldname]['group_name']=$arow['group_name'];
  2334. $fieldmap[$fieldname]['scale']=$clang->gT('Scale 1');
  2335. $fieldmap[$fieldname]['mandatory']=$arow['mandatory'];
  2336. $fieldmap[$fieldname]['hasconditions']=$conditions;
  2337. $fieldmap[$fieldname]['usedinconditions']=$usedinconditions;
  2338. $fieldmap[$fieldname]['questionSeq']=$questionSeq;
  2339. $fieldmap[$fieldname]['groupSeq']=$groupSeq;
  2340. }
  2341. $fieldname="{$arow['sid']}X{$arow['gid']}X{$arow['qid']}{$abrow['title']}#1";
  2342. if (isset($fieldmap[$fieldname])) $aDuplicateQIDs[$arow['qid']]=array('fieldname'=>$fieldname,'question'=>$arow['question'],'gid'=>$arow['gid']);
  2343. $fieldmap[$fieldname]=array("fieldname"=>$fieldname, 'type'=>$arow['type'], 'sid'=>$surveyid, "gid"=>$arow['gid'], "qid"=>$arow['qid'], "aid"=>$abrow['title'], "scale_id"=>1);
  2344. if ($style == "full")
  2345. {
  2346. $fieldmap[$fieldname]['title']=$arow['title'];
  2347. $fieldmap[$fieldname]['question']=$arow['question'];
  2348. $fieldmap[$fieldname]['subquestion']=$abrow['question'];
  2349. $fieldmap[$fieldname]['group_name']=$arow['group_name'];
  2350. $fieldmap[$fieldname]['scale']=$clang->gT('Scale 2');
  2351. $fieldmap[$fieldname]['mandatory']=$arow['mandatory'];
  2352. $fieldmap[$fieldname]['hasconditions']=$conditions;
  2353. $fieldmap[$fieldname]['usedinconditions']=$usedinconditions;
  2354. $fieldmap[$fieldname]['questionSeq']=$questionSeq;
  2355. $fieldmap[$fieldname]['groupSeq']=$groupSeq;
  2356. }
  2357. }
  2358. }
  2359. elseif ($arow['type'] == "R")
  2360. {
  2361. //MULTI ENTRY
  2362. $data = Answers::model()->findAllByAttributes(array('qid' => $arow['qid'], 'language' => $sLanguage));
  2363. $data = count($data);
  2364. $slots=$data;
  2365. for ($i=1; $i<=$slots; $i++)
  2366. {
  2367. $fieldname="{$arow['sid']}X{$arow['gid']}X{$arow['qid']}$i";
  2368. if (isset($fieldmap[$fieldname])) $aDuplicateQIDs[$arow['qid']]=array('fieldname'=>$fieldname,'question'=>$arow['question'],'gid'=>$arow['gid']);
  2369. $fieldmap[$fieldname]=array("fieldname"=>$fieldname, 'type'=>$arow['type'], 'sid'=>$surveyid, "gid"=>$arow['gid'], "qid"=>$arow['qid'], "aid"=>$i);
  2370. if ($style == "full")
  2371. {
  2372. $fieldmap[$fieldname]['title']=$arow['title'];
  2373. $fieldmap[$fieldname]['question']=$arow['question'];
  2374. $fieldmap[$fieldname]['subquestion']=sprintf($clang->gT('Rank %s'),$i);
  2375. $fieldmap[$fieldname]['group_name']=$arow['group_name'];
  2376. $fieldmap[$fieldname]['mandatory']=$arow['mandatory'];
  2377. $fieldmap[$fieldname]['hasconditions']=$conditions;
  2378. $fieldmap[$fieldname]['usedinconditions']=$usedinconditions;
  2379. $fieldmap[$fieldname]['questionSeq']=$questionSeq;
  2380. $fieldmap[$fieldname]['groupSeq']=$groupSeq;
  2381. }
  2382. }
  2383. }
  2384. elseif ($arow['type'] == "|")
  2385. {
  2386. $qidattributes= getQuestionAttributeValues($arow['qid']);
  2387. $fieldname="{$arow['sid']}X{$arow['gid']}X{$arow['qid']}";
  2388. $fieldmap[$fieldname]=array("fieldname"=>$fieldname,
  2389. 'type'=>$arow['type'],
  2390. 'sid'=>$surveyid,
  2391. "gid"=>$arow['gid'],
  2392. "qid"=>$arow['qid'],
  2393. "aid"=>''
  2394. );
  2395. if ($style == "full")
  2396. {
  2397. $fieldmap[$fieldname]['title']=$arow['title'];
  2398. $fieldmap[$fieldname]['question']=$arow['question'];
  2399. $fieldmap[$fieldname]['max_files']=$qidattributes['max_num_of_files'];
  2400. $fieldmap[$fieldname]['group_name']=$arow['group_name'];
  2401. $fieldmap[$fieldname]['mandatory']=$arow['mandatory'];
  2402. $fieldmap[$fieldname]['hasconditions']=$conditions;
  2403. $fieldmap[$fieldname]['usedinconditions']=$usedinconditions;
  2404. $fieldmap[$fieldname]['questionSeq']=$questionSeq;
  2405. $fieldmap[$fieldname]['groupSeq']=$groupSeq;
  2406. }
  2407. $fieldname="{$arow['sid']}X{$arow['gid']}X{$arow['qid']}"."_filecount";
  2408. $fieldmap[$fieldname]=array("fieldname"=>$fieldname,
  2409. 'type'=>$arow['type'],
  2410. 'sid'=>$surveyid,
  2411. "gid"=>$arow['gid'],
  2412. "qid"=>$arow['qid'],
  2413. "aid"=>"filecount"
  2414. );
  2415. if ($style == "full")
  2416. {
  2417. $fieldmap[$fieldname]['title']=$arow['title'];
  2418. $fieldmap[$fieldname]['question']="filecount - ".$arow['question'];
  2419. $fieldmap[$fieldname]['group_name']=$arow['group_name'];
  2420. $fieldmap[$fieldname]['mandatory']=$arow['mandatory'];
  2421. $fieldmap[$fieldname]['hasconditions']=$conditions;
  2422. $fieldmap[$fieldname]['usedinconditions']=$usedinconditions;
  2423. $fieldmap[$fieldname]['questionSeq']=$questionSeq;
  2424. $fieldmap[$fieldname]['groupSeq']=$groupSeq;
  2425. }
  2426. }
  2427. else // Question types with subquestions and one answer per subquestion (M/A/B/C/E/F/H/P)
  2428. {
  2429. //MULTI ENTRY
  2430. $abrows = getSubQuestions($surveyid,$arow['qid'],$sLanguage);
  2431. foreach ($abrows as $abrow)
  2432. {
  2433. $fieldname="{$arow['sid']}X{$arow['gid']}X{$arow['qid']}{$abrow['title']}";
  2434. if (isset($fieldmap[$fieldname])) $aDuplicateQIDs[$arow['qid']]=array('fieldname'=>$fieldname,'question'=>$arow['question'],'gid'=>$arow['gid']);
  2435. $fieldmap[$fieldname]=array("fieldname"=>$fieldname,
  2436. 'type'=>$arow['type'],
  2437. 'sid'=>$surveyid,
  2438. 'gid'=>$arow['gid'],
  2439. 'qid'=>$arow['qid'],
  2440. 'aid'=>$abrow['title'],
  2441. 'sqid'=>$abrow['qid']);
  2442. if ($style == "full")
  2443. {
  2444. $fieldmap[$fieldname]['title']=$arow['title'];
  2445. $fieldmap[$fieldname]['question']=$arow['question'];
  2446. $fieldmap[$fieldname]['subquestion']=$abrow['question'];
  2447. $fieldmap[$fieldname]['group_name']=$arow['group_name'];
  2448. $fieldmap[$fieldname]['mandatory']=$arow['mandatory'];
  2449. $fieldmap[$fieldname]['hasconditions']=$conditions;
  2450. $fieldmap[$fieldname]['usedinconditions']=$usedinconditions;
  2451. $fieldmap[$fieldname]['questionSeq']=$questionSeq;
  2452. $fieldmap[$fieldname]['groupSeq']=$groupSeq;
  2453. $fieldmap[$fieldname]['preg']=$arow['preg'];
  2454. if (isset($defaultValues[$arow['qid'].'~'.$abrow['qid']])) {
  2455. $fieldmap[$fieldname]['defaultvalue'] = $defaultValues[$arow['qid'].'~'.$abrow['qid']];
  2456. }
  2457. }
  2458. if ($arow['type'] == "P")
  2459. {
  2460. $fieldname="{$arow['sid']}X{$arow['gid']}X{$arow['qid']}{$abrow['title']}comment";
  2461. if (isset($fieldmap[$fieldname])) $aDuplicateQIDs[$arow['qid']]=array('fieldname'=>$fieldname,'question'=>$arow['question'],'gid'=>$arow['gid']);
  2462. $fieldmap[$fieldname]=array("fieldname"=>$fieldname, 'type'=>$arow['type'], 'sid'=>$surveyid, "gid"=>$arow['gid'], "qid"=>$arow['qid'], "aid"=>$abrow['title']."comment");
  2463. if ($style == "full")
  2464. {
  2465. $fieldmap[$fieldname]['title']=$arow['title'];
  2466. $fieldmap[$fieldname]['question']=$arow['question'];
  2467. $fieldmap[$fieldname]['subquestion']=$clang->gT('Comment');
  2468. $fieldmap[$fieldname]['group_name']=$arow['group_name'];
  2469. $fieldmap[$fieldname]['mandatory']=$arow['mandatory'];
  2470. $fieldmap[$fieldname]['hasconditions']=$conditions;
  2471. $fieldmap[$fieldname]['usedinconditions']=$usedinconditions;
  2472. $fieldmap[$fieldname]['questionSeq']=$questionSeq;
  2473. $fieldmap[$fieldname]['groupSeq']=$groupSeq;
  2474. }
  2475. }
  2476. }
  2477. if ($arow['other']=="Y" && ($arow['type']=="M" || $arow['type']=="P"))
  2478. {
  2479. $fieldname="{$arow['sid']}X{$arow['gid']}X{$arow['qid']}other";
  2480. if (isset($fieldmap[$fieldname])) $aDuplicateQIDs[$arow['qid']]=array('fieldname'=>$fieldname,'question'=>$arow['question'],'gid'=>$arow['gid']);
  2481. $fieldmap[$fieldname]=array("fieldname"=>$fieldname, 'type'=>$arow['type'], 'sid'=>$surveyid, "gid"=>$arow['gid'], "qid"=>$arow['qid'], "aid"=>"other");
  2482. if ($style == "full")
  2483. {
  2484. $fieldmap[$fieldname]['title']=$arow['title'];
  2485. $fieldmap[$fieldname]['question']=$arow['question'];
  2486. $fieldmap[$fieldname]['subquestion']=$clang->gT('Other');
  2487. $fieldmap[$fieldname]['group_name']=$arow['group_name'];
  2488. $fieldmap[$fieldname]['mandatory']=$arow['mandatory'];
  2489. $fieldmap[$fieldname]['hasconditions']=$conditions;
  2490. $fieldmap[$fieldname]['usedinconditions']=$usedinconditions;
  2491. $fieldmap[$fieldname]['questionSeq']=$questionSeq;
  2492. $fieldmap[$fieldname]['groupSeq']=$groupSeq;
  2493. $fieldmap[$fieldname]['other']=$arow['other'];
  2494. }
  2495. if ($arow['type']=="P")
  2496. {
  2497. $fieldname="{$arow['sid']}X{$arow['gid']}X{$arow['qid']}othercomment";
  2498. if (isset($fieldmap[$fieldname])) $aDuplicateQIDs[$arow['qid']]=array('fieldname'=>$fieldname,'question'=>$arow['question'],'gid'=>$arow['gid']);
  2499. $fieldmap[$fieldname]=array("fieldname"=>$fieldname, 'type'=>$arow['type'], 'sid'=>$surveyid, "gid"=>$arow['gid'], "qid"=>$arow['qid'], "aid"=>"othercomment");
  2500. if ($style == "full")
  2501. {
  2502. $fieldmap[$fieldname]['title']=$arow['title'];
  2503. $fieldmap[$fieldname]['question']=$arow['question'];
  2504. $fieldmap[$fieldname]['subquestion']=$clang->gT('Other comment');
  2505. $fieldmap[$fieldname]['group_name']=$arow['group_name'];
  2506. $fieldmap[$fieldname]['mandatory']=$arow['mandatory'];
  2507. $fieldmap[$fieldname]['hasconditions']=$conditions;
  2508. $fieldmap[$fieldname]['usedinconditions']=$usedinconditions;
  2509. $fieldmap[$fieldname]['questionSeq']=$questionSeq;
  2510. $fieldmap[$fieldname]['groupSeq']=$groupSeq;
  2511. $fieldmap[$fieldname]['other']=$arow['other'];
  2512. }
  2513. }
  2514. }
  2515. }
  2516. if (isset($fieldmap[$fieldname]))
  2517. {
  2518. $fieldmap[$fieldname]['relevance']=$arow['relevance'];
  2519. $fieldmap[$fieldname]['grelevance']=$arow['grelevance'];
  2520. $fieldmap[$fieldname]['questionSeq']=$questionSeq;
  2521. $fieldmap[$fieldname]['groupSeq']=$groupSeq;
  2522. $fieldmap[$fieldname]['preg']=$arow['preg'];
  2523. $fieldmap[$fieldname]['other']=$arow['other'];
  2524. $fieldmap[$fieldname]['help']=$arow['help'];
  2525. }
  2526. else
  2527. {
  2528. --$questionSeq; // didn't generate a valid $fieldmap entry, so decrement the question counter to ensure they are sequential
  2529. }
  2530. }
  2531. if (isset($fieldmap)) {
  2532. if ($questionid == false)
  2533. {
  2534. // If the fieldmap was randomized, the master will contain the proper order. Copy that fieldmap with the new language settings.
  2535. if (isset(Yii::app()->session['survey_'.$surveyid]['fieldmap-' . $surveyid . '-randMaster']))
  2536. {
  2537. $masterFieldmap = Yii::app()->session['survey_'.$surveyid]['fieldmap-' . $surveyid . '-randMaster'];
  2538. $mfieldmap = Yii::app()->session['survey_'.$surveyid][$masterFieldmap];
  2539. foreach ($mfieldmap as $fieldname => $mf)
  2540. {
  2541. if (isset($fieldmap[$fieldname]))
  2542. {
  2543. // This array holds the keys of translatable attributes
  2544. $translatable = array_flip(array('question', 'subquestion', 'subquestion1', 'subquestion2', 'group_name', 'answerList', 'defaultValue', 'help'));
  2545. // We take all translatable attributes from the new fieldmap
  2546. $newText = array_intersect_key($fieldmap[$fieldname], $translatable);
  2547. // And merge them with the other values from the random fieldmap like questionSeq, groupSeq etc.
  2548. $mf = $newText + $mf;
  2549. }
  2550. $mfieldmap[$fieldname] = $mf;
  2551. }
  2552. $fieldmap = $mfieldmap;
  2553. }
  2554. Yii::app()->session['fieldmap-' . $surveyid . $sLanguage]=$fieldmap;
  2555. }
  2556. return $fieldmap;
  2557. }
  2558. }
  2559. /**
  2560. * Returns true if the given survey has a File Upload Question Type
  2561. * @param $surveyid The survey ID
  2562. * @return bool
  2563. */
  2564. function hasFileUploadQuestion($surveyid) {
  2565. $fieldmap = createFieldMap($surveyid,'short',false,false,getBaseLanguageFromSurveyID($surveyid));
  2566. foreach ($fieldmap as $field) {
  2567. if (isset($field['type']) && $field['type'] === '|') return true;
  2568. }
  2569. }
  2570. /**
  2571. * This function generates an array containing the fieldcode, and matching data in the same order as the activate script
  2572. *
  2573. * @param string $surveyid The Survey ID
  2574. * @param mixed $style 'short' (default) or 'full' - full creates extra information like default values
  2575. * @param mixed $force_refresh - Forces to really refresh the array, not just take the session copy
  2576. * @param int $questionid Limit to a certain qid only (for question preview) - default is false
  2577. * @param string $sQuestionLanguage The language to use
  2578. * @return array
  2579. */
  2580. function createTimingsFieldMap($surveyid, $style='full', $force_refresh=false, $questionid=false, $sQuestionLanguage=null) {
  2581. global $aDuplicateQIDs;
  2582. static $timingsFieldMap;
  2583. $sLanguage = sanitize_languagecode($sQuestionLanguage);
  2584. $surveyid = sanitize_int($surveyid);
  2585. $clang = new Limesurvey_lang($sLanguage);
  2586. //checks to see if fieldmap has already been built for this page.
  2587. if (isset($timingsFieldMap[$surveyid][$style][$clang->langcode]) && $force_refresh==false) {
  2588. return $timingsFieldMap[$surveyid][$style][$clang->langcode];
  2589. }
  2590. //do something
  2591. $fields = createFieldMap($surveyid, $style, $force_refresh, $questionid, $sQuestionLanguage);
  2592. $fieldmap['interviewtime']=array('fieldname'=>'interviewtime','type'=>'interview_time','sid'=>$surveyid, 'gid'=>'', 'qid'=>'', 'aid'=>'', 'question'=>$clang->gT('Total time'), 'title'=>'interviewtime');
  2593. foreach ($fields as $field) {
  2594. if (!empty($field['gid'])) {
  2595. // field for time spent on page
  2596. $fieldname="{$field['sid']}X{$field['gid']}time";
  2597. if (!isset($fieldmap[$fieldname]))
  2598. {
  2599. $fieldmap[$fieldname]=array("fieldname"=>$fieldname, 'type'=>"page_time", 'sid'=>$surveyid, "gid"=>$field['gid'], "group_name"=>$field['group_name'], "qid"=>'', 'aid'=>'', 'title'=>'groupTime'.$field['gid'], 'question'=>$clang->gT('Group time').": ".$field['group_name']);
  2600. }
  2601. // field for time spent on answering a question
  2602. $fieldname="{$field['sid']}X{$field['gid']}X{$field['qid']}time";
  2603. if (!isset($fieldmap[$fieldname]))
  2604. {
  2605. $fieldmap[$fieldname]=array("fieldname"=>$fieldname, 'type'=>"answer_time", 'sid'=>$surveyid, "gid"=>$field['gid'], "group_name"=>$field['group_name'], "qid"=>$field['qid'], 'aid'=>'', "title"=>$field['title'].'Time', "question"=>$clang->gT('Question time').": ".$field['title']);
  2606. }
  2607. }
  2608. }
  2609. $timingsFieldMap[$surveyid][$style][$clang->langcode] = $fieldmap;
  2610. return $timingsFieldMap[$surveyid][$style][$clang->langcode];
  2611. }
  2612. /**
  2613. * put your comment there...
  2614. *
  2615. * @param mixed $needle
  2616. * @param mixed $haystack
  2617. * @param mixed $keyname
  2618. * @param mixed $maxanswers
  2619. */
  2620. function arraySearchByKey($needle, $haystack, $keyname, $maxanswers="") {
  2621. $output=array();
  2622. foreach($haystack as $hay) {
  2623. if (array_key_exists($keyname, $hay)) {
  2624. if ($hay[$keyname] == $needle) {
  2625. if ($maxanswers == 1) {
  2626. return $hay;
  2627. } else {
  2628. $output[]=$hay;
  2629. }
  2630. }
  2631. }
  2632. }
  2633. return $output;
  2634. }
  2635. /**
  2636. * set the rights of a user and his children
  2637. *
  2638. * @param int $uid the user id
  2639. * @param mixed $rights rights array
  2640. */
  2641. function setUserRights($uid, $rights)
  2642. {
  2643. $uid=sanitize_int($uid);
  2644. $updates = "create_survey=".$rights['create_survey']
  2645. . ", create_user=".$rights['create_user']
  2646. . ", participant_panel=".$rights['participant_panel']
  2647. . ", delete_user=".$rights['delete_user']
  2648. . ", superadmin=".$rights['superadmin']
  2649. . ", configurator=".$rights['configurator']
  2650. . ", manage_template=".$rights['manage_template']
  2651. . ", manage_label=".$rights['manage_label'];
  2652. $uquery = "UPDATE {{users}} SET ".$updates." WHERE uid = ".$uid;
  2653. return dbSelectLimitAssoc($uquery); //Checked
  2654. }
  2655. /**
  2656. * This function returns a count of the number of saved responses to a survey
  2657. *
  2658. * @param mixed $surveyid Survey ID
  2659. */
  2660. function getSavedCount($surveyid)
  2661. {
  2662. $surveyid=(int)$surveyid;
  2663. return Saved_control::model()->getCountOfAll($surveyid);
  2664. }
  2665. /**
  2666. * Returns the base language from a survey id
  2667. *
  2668. * @deprecated Use Survey::model()->findByPk($surveyid)->language
  2669. * @param int $surveyid
  2670. * @return string
  2671. */
  2672. function getBaseLanguageFromSurveyID($surveyid)
  2673. {
  2674. return Survey::model()->findByPk($surveyid)->language;
  2675. }
  2676. function buildLabelSetCheckSumArray()
  2677. {
  2678. // BUILD CHECKSUMS FOR ALL EXISTING LABEL SETS
  2679. /**$query = "SELECT lid
  2680. FROM ".db_table_name('labelsets')."
  2681. ORDER BY lid"; */
  2682. $result = Labelsets::model()->getLID();//($query) or safeDie("safe_died collecting labelset ids<br />$query<br />"); //Checked)
  2683. $csarray=array();
  2684. foreach($result as $row)
  2685. {
  2686. $thisset="";
  2687. $query2 = "SELECT code, title, sortorder, language, assessment_value
  2688. FROM {{labels}}
  2689. WHERE lid={$row['lid']}
  2690. ORDER BY language, sortorder, code";
  2691. $result2 = Yii::app()->db->createCommand($query2)->query();
  2692. foreach ($result2->readAll() as $row2)
  2693. {
  2694. $thisset .= implode('.', $row2);
  2695. } // while
  2696. $csarray[$row['lid']]=dechex(crc32($thisset)*1);
  2697. }
  2698. return $csarray;
  2699. }
  2700. /**
  2701. * Returns a flat array with all question attributes for the question only (and the qid we gave it)!
  2702. * @param $iQID The question ID
  2703. * @return array$bOrderByNative=>value, attribute=>value} or false if the question ID does not exist (anymore)
  2704. */
  2705. function getQuestionAttributeValues($iQID)
  2706. {
  2707. static $cache = array();
  2708. static $availableattributesarr = null;
  2709. $iQID = sanitize_int($iQID);
  2710. if (isset($cache[$iQID])) {
  2711. return $cache[$iQID];
  2712. }
  2713. $row = Questions::model()->findByAttributes(array('qid' => $iQID)); //, 'parent_qid' => 0), array('group' => 'type')
  2714. if (empty($row)) // Question was deleted while running the survey
  2715. {
  2716. $cache[$iQID] = false;
  2717. return false;
  2718. }
  2719. else
  2720. {
  2721. $row = $row->getAttributes();
  2722. }
  2723. $type = $row['type'];
  2724. $surveyid = $row['sid'];
  2725. $aLanguages = array_merge((array)Survey::model()->findByPk($surveyid)->language, Survey::model()->findByPk($surveyid)->additionalLanguages);
  2726. //Now read available attributes, make sure we do this only once per request to save
  2727. //processing cycles and memory
  2728. if (is_null($availableattributesarr)) $availableattributesarr = questionAttributes();
  2729. if (isset($availableattributesarr[$type]))
  2730. {
  2731. $aAvailableAttributes = $availableattributesarr[$type];
  2732. }
  2733. else
  2734. {
  2735. $cache[$iQID] = array();
  2736. return array();
  2737. }
  2738. $aResultAttributes = array();
  2739. foreach($aAvailableAttributes as $attribute){
  2740. if ($attribute['i18n'])
  2741. {
  2742. foreach ($aLanguages as $sLanguage)
  2743. {
  2744. $aResultAttributes[$attribute['name']][$sLanguage]=$attribute['default'];
  2745. }
  2746. }
  2747. else
  2748. {
  2749. $aResultAttributes[$attribute['name']]=$attribute['default'];
  2750. }
  2751. }
  2752. $result = Question_attributes::model()->findAllByAttributes(array('qid' => $iQID));
  2753. foreach ($result as $row)
  2754. {
  2755. $row = $row->attributes;
  2756. if (!isset($aAvailableAttributes[$row['attribute']]))
  2757. {
  2758. continue; // Sort out attributes not belonging to this question
  2759. }
  2760. if (!($aAvailableAttributes[$row['attribute']]['i18n']))
  2761. {
  2762. $aResultAttributes[$row['attribute']]=$row['value'];
  2763. }
  2764. elseif(!empty($row['language']))
  2765. {
  2766. $aResultAttributes[$row['attribute']][$row['language']]=$row['value'];
  2767. }
  2768. }
  2769. $cache[$iQID] = $aResultAttributes;
  2770. return $aResultAttributes;
  2771. }
  2772. /**
  2773. *
  2774. * Returns the questionAttribtue value set or '' if not set
  2775. * @author: lemeur
  2776. * @param $questionAttributeArray
  2777. * @param $attributeName
  2778. * @param $language string Optional: The language if the particualr attributes is localizable
  2779. * @return string
  2780. */
  2781. function getQuestionAttributeValue($questionAttributeArray, $attributeName, $language='')
  2782. {
  2783. if ($language=='' && isset($questionAttributeArray[$attributeName]))
  2784. {
  2785. return $questionAttributeArray[$attributeName];
  2786. }
  2787. elseif ($language!='' && isset($questionAttributeArray[$attributeName][$language]))
  2788. {
  2789. return $questionAttributeArray[$attributeName][$language];
  2790. }
  2791. else
  2792. {
  2793. return '';
  2794. }
  2795. }
  2796. /**
  2797. * Returns array of question type chars with attributes
  2798. *
  2799. * @param mixed $returnByName If set to true the array will be by attribute name
  2800. */
  2801. function questionAttributes($returnByName=false)
  2802. {
  2803. $clang = Yii::app()->lang;
  2804. //For each question attribute include a key:
  2805. // name - the display name
  2806. // types - a string with one character representing each question typy to which the attribute applies
  2807. // help - a short explanation
  2808. // If you insert a new attribute please do it in correct alphabetical order!
  2809. $qattributes["alphasort"]=array(
  2810. "types"=>"!LOWZ",
  2811. 'category'=>$clang->gT('Display'),
  2812. 'sortorder'=>100,
  2813. 'inputtype'=>'singleselect',
  2814. 'options'=>array(0=>$clang->gT('No'),
  2815. 1=>$clang->gT('Yes')),
  2816. 'default'=>0,
  2817. "help"=>$clang->gT("Sort the answer options alphabetically"),
  2818. "caption"=>$clang->gT('Sort answers alphabetically'));
  2819. $qattributes["answer_width"]=array(
  2820. "types"=>"ABCEF1:;",
  2821. 'category'=>$clang->gT('Display'),
  2822. 'sortorder'=>100,
  2823. 'inputtype'=>'integer',
  2824. 'min'=>'1',
  2825. 'max'=>'100',
  2826. "help"=>$clang->gT('Set the percentage width of the (sub-)question column (1-100)'),
  2827. "caption"=>$clang->gT('(Sub-)question width'));
  2828. $qattributes["repeat_headings"]=array(
  2829. "types"=>"F:1;",
  2830. 'category'=>$clang->gT('Display'),
  2831. 'sortorder'=>100,
  2832. 'inputtype'=>'integer',
  2833. 'default'=>'',
  2834. "help"=>$clang->gT('Repeat headings every X subquestions (Set to 0 to deactivate heading repeat, deactivate minimum repeat headings from config).'),
  2835. "caption"=>$clang->gT('Repeat headers'));
  2836. $qattributes["array_filter"]=array(
  2837. "types"=>"1ABCEF:;MPLKQR",
  2838. 'category'=>$clang->gT('Logic'),
  2839. 'sortorder'=>100,
  2840. 'inputtype'=>'text',
  2841. "help"=>$clang->gT("Enter the code(s) of Multiple choice question(s) (separated by semicolons) to only show the matching answer options in this question."),
  2842. "caption"=>$clang->gT('Array filter'));
  2843. $qattributes["array_filter_exclude"]=array(
  2844. "types"=>"1ABCEF:;MPLKQR",
  2845. 'category'=>$clang->gT('Logic'),
  2846. 'sortorder'=>100,
  2847. 'inputtype'=>'text',
  2848. "help"=>$clang->gT("Enter the code(s) of Multiple choice question(s) (separated by semicolons) to exclude the matching answer options in this question."),
  2849. "caption"=>$clang->gT('Array filter exclusion'));
  2850. $qattributes["array_filter_style"]=array(
  2851. "types"=>"1ABCEF:;MPLKQR",
  2852. 'category'=>$clang->gT('Logic'),
  2853. 'sortorder'=>100,
  2854. 'inputtype'=>'singleselect',
  2855. 'options'=>array(0=>$clang->gT('Hidden'),
  2856. 1=>$clang->gT('Disabled')),
  2857. 'default'=>0,
  2858. "help"=>$clang->gT("Specify how array-filtered sub-questions should be displayed"),
  2859. "caption"=>$clang->gT('Array filter style'));
  2860. $qattributes["assessment_value"]=array(
  2861. "types"=>"MP",
  2862. 'category'=>$clang->gT('Logic'),
  2863. 'sortorder'=>100,
  2864. 'default'=>'1',
  2865. 'inputtype'=>'integer',
  2866. "help"=>$clang->gT("If one of the subquestions is marked then for each marked subquestion this value is added as assessment."),
  2867. "caption"=>$clang->gT('Assessment value'));
  2868. $qattributes["category_separator"]=array(
  2869. "types"=>"!",
  2870. 'category'=>$clang->gT('Display'),
  2871. 'sortorder'=>100,
  2872. 'inputtype'=>'text',
  2873. "help"=>$clang->gT('Category separator'),
  2874. "caption"=>$clang->gT('Category separator'));
  2875. $qattributes["code_filter"]=array(
  2876. "types"=>"WZ",
  2877. 'category'=>$clang->gT('Logic'),
  2878. 'sortorder'=>100,
  2879. 'inputtype'=>'text',
  2880. "help"=>$clang->gT('Filter the available answers by this value'),
  2881. "caption"=>$clang->gT('Code filter'));
  2882. $qattributes["display_columns"]=array(
  2883. "types"=>"LM",
  2884. 'category'=>$clang->gT('Display'),
  2885. 'sortorder'=>100,
  2886. 'inputtype'=>'integer',
  2887. 'default'=>'1',
  2888. 'min'=>'1',
  2889. 'max'=>'100',
  2890. "help"=>$clang->gT('The answer options will be distributed across the number of columns set here'),
  2891. "caption"=>$clang->gT('Display columns'));
  2892. $qattributes["display_rows"]=array(
  2893. "types"=>"QSTU",
  2894. 'category'=>$clang->gT('Display'),
  2895. 'sortorder'=>100,
  2896. 'inputtype'=>'text',
  2897. "help"=>$clang->gT('How many rows to display'),
  2898. "caption"=>$clang->gT('Display rows'));
  2899. $qattributes["dropdown_dates"]=array(
  2900. "types"=>"D",
  2901. 'category'=>$clang->gT('Display'),
  2902. 'sortorder'=>100,
  2903. 'inputtype'=>'singleselect',
  2904. 'options'=>array(0=>$clang->gT('No'),
  2905. 1=>$clang->gT('Yes')),
  2906. 'default'=>0,
  2907. "help"=>$clang->gT('Use accessible dropdown boxes instead of calendar popup'),
  2908. "caption"=>$clang->gT('Display dropdown boxes'));
  2909. $qattributes["dropdown_dates_year_min"]=array(
  2910. "types"=>"D",
  2911. 'category'=>$clang->gT('Display'),
  2912. 'sortorder'=>110,
  2913. 'inputtype'=>'text',
  2914. "help"=>$clang->gT('Minimum year value in calendar'),
  2915. "caption"=>$clang->gT('Minimum year'));
  2916. $qattributes["dropdown_dates_year_max"]=array(
  2917. "types"=>"D",
  2918. 'category'=>$clang->gT('Display'),
  2919. 'sortorder'=>111,
  2920. 'inputtype'=>'text',
  2921. "help"=>$clang->gT('Maximum year value for calendar'),
  2922. "caption"=>$clang->gT('Maximum year'));
  2923. $qattributes["dropdown_prepostfix"]=array(
  2924. "types"=>"1",
  2925. 'category'=>$clang->gT('Display'),
  2926. 'sortorder'=>112,
  2927. 'inputtype'=>'text',
  2928. 'i18n'=>true,
  2929. "help"=>$clang->gT('Prefix|Suffix for dropdown lists'),
  2930. "caption"=>$clang->gT('Dropdown prefix/suffix'));
  2931. $qattributes["dropdown_separators"]=array(
  2932. "types"=>"1",
  2933. 'category'=>$clang->gT('Display'),
  2934. 'sortorder'=>120,
  2935. 'inputtype'=>'text',
  2936. "help"=>$clang->gT('Post-Answer-Separator|Inter-Dropdownlist-Separator for dropdown lists'),
  2937. "caption"=>$clang->gT('Dropdown separator'));
  2938. $qattributes["dualscale_headerA"]=array(
  2939. "types"=>"1",
  2940. 'category'=>$clang->gT('Display'),
  2941. 'sortorder'=>110,
  2942. 'inputtype'=>'text',
  2943. 'i18n'=>true,
  2944. "help"=>$clang->gT('Enter a header text for the first scale'),
  2945. "caption"=>$clang->gT('Header for first scale'));
  2946. $qattributes["dualscale_headerB"]=array(
  2947. "types"=>"1",
  2948. 'category'=>$clang->gT('Display'),
  2949. 'sortorder'=>111,
  2950. 'inputtype'=>'text',
  2951. 'i18n'=>true,
  2952. "help"=>$clang->gT('Enter a header text for the second scale'),
  2953. "caption"=>$clang->gT('Header for second scale'));
  2954. $qattributes["equals_num_value"]=array(
  2955. "types"=>"K",
  2956. 'category'=>$clang->gT('Input'),
  2957. 'sortorder'=>100,
  2958. 'inputtype'=>'text',
  2959. "help"=>$clang->gT('Multiple numeric inputs sum must equal this value'),
  2960. "caption"=>$clang->gT('Equals sum value'));
  2961. $qattributes["em_validation_q"]=array(
  2962. "types"=>":;ABCEFKMNPQRSTU",
  2963. 'category'=>$clang->gT('Logic'),
  2964. 'sortorder'=>200,
  2965. 'inputtype'=>'textarea',
  2966. "help"=>$clang->gT('Enter a boolean equation to validate the whole question.'),
  2967. "caption"=>$clang->gT('Question validation equation'));
  2968. $qattributes["em_validation_q_tip"]=array(
  2969. "types"=>":;ABCEFKMNPQRSTU",
  2970. 'category'=>$clang->gT('Logic'),
  2971. 'sortorder'=>210,
  2972. 'inputtype'=>'textarea',
  2973. "help"=>$clang->gT('This is a hint text that will be shown to the participant describing the question validation equation.'),
  2974. "caption"=>$clang->gT('Question validation tip'));
  2975. $qattributes["em_validation_sq"]=array(
  2976. "types"=>";:KQSTUN",
  2977. 'category'=>$clang->gT('Logic'),
  2978. 'sortorder'=>220,
  2979. 'inputtype'=>'textarea',
  2980. "help"=>$clang->gT('Enter a boolean equation to validate each sub-question.'),
  2981. "caption"=>$clang->gT('Sub-question validation equation'));
  2982. $qattributes["em_validation_sq_tip"]=array(
  2983. "types"=>";:KQSTUN",
  2984. 'category'=>$clang->gT('Logic'),
  2985. 'sortorder'=>230,
  2986. 'inputtype'=>'textarea',
  2987. "help"=>$clang->gT('This is a tip shown to the participant describing the sub-question validation equation.'),
  2988. "caption"=>$clang->gT('Sub-question validation tip'));
  2989. $qattributes["exclude_all_others"]=array(
  2990. "types"=>":ABCEFMPKQ",
  2991. 'category'=>$clang->gT('Logic'),
  2992. 'sortorder'=>130,
  2993. 'inputtype'=>'text',
  2994. "help"=>$clang->gT('Excludes all other options if a certain answer is selected - just enter the answer code(s) seperated with a semikolon.'),
  2995. "caption"=>$clang->gT('Exclusive option'));
  2996. $qattributes["exclude_all_others_auto"]=array(
  2997. "types"=>"MP",
  2998. 'category'=>$clang->gT('Logic'),
  2999. 'sortorder'=>131,
  3000. 'inputtype'=>'singleselect',
  3001. 'options'=>array(0=>$clang->gT('No'),
  3002. 1=>$clang->gT('Yes')),
  3003. 'default'=>0,
  3004. "help"=>$clang->gT('If the participant marks all options, uncheck all and check the option set in the "Exclusive option" setting'),
  3005. "caption"=>$clang->gT('Auto-check exclusive option if all others are checked'));
  3006. // Map Options
  3007. $qattributes["location_city"]=array(
  3008. "types"=>"S",
  3009. 'readonly_when_active'=>true,
  3010. 'category'=>$clang->gT('Location'),
  3011. 'sortorder'=>100,
  3012. 'inputtype'=>'singleselect',
  3013. 'default'=>0,
  3014. 'options'=>array(0=>$clang->gT('Yes'),
  3015. 1=>$clang->gT('No')),
  3016. "help"=>$clang->gT("Store the city?"),
  3017. "caption"=>$clang->gT("Save city"));
  3018. $qattributes["location_state"]=array(
  3019. "types"=>"S",
  3020. 'readonly_when_active'=>true,
  3021. 'category'=>$clang->gT('Location'),
  3022. 'sortorder'=>100,
  3023. 'default'=>0,
  3024. 'inputtype'=>'singleselect',
  3025. 'options'=>array(0=>$clang->gT('Yes'),
  3026. 1=>$clang->gT('No')),
  3027. "help"=>$clang->gT("Store the state?"),
  3028. "caption"=>$clang->gT("Save state"));
  3029. $qattributes["location_postal"]=array(
  3030. "types"=>"S",
  3031. 'readonly_when_active'=>true,
  3032. 'category'=>$clang->gT('Location'),
  3033. 'sortorder'=>100,
  3034. 'inputtype'=>'singleselect',
  3035. 'default'=>0,
  3036. 'options'=>array(0=>$clang->gT('Yes'),
  3037. 1=>$clang->gT('No')),
  3038. "help"=>$clang->gT("Store the postal code?"),
  3039. "caption"=>$clang->gT("Save postal code"));
  3040. $qattributes["location_country"]=array(
  3041. "types"=>"S",
  3042. 'readonly_when_active'=>true,
  3043. 'category'=>$clang->gT('Location'),
  3044. 'sortorder'=>100,
  3045. 'inputtype'=>'singleselect',
  3046. 'default'=>0,
  3047. 'options'=>array(0=>$clang->gT('Yes'),
  3048. 1=>$clang->gT('No')),
  3049. "help"=>$clang->gT("Store the country?"),
  3050. "caption"=>$clang->gT("Save country"));
  3051. $qattributes["statistics_showmap"]=array(
  3052. "types"=>"S",
  3053. 'category'=>$clang->gT('Statistics'),
  3054. 'inputtype'=>'singleselect',
  3055. 'sortorder'=>100,
  3056. 'options'=>array(1=>$clang->gT('Yes'), 0=>$clang->gT('No')),
  3057. 'help'=>$clang->gT("Show a map in the statistics?"),
  3058. 'caption'=>$clang->gT("Display map"),
  3059. 'default'=>1
  3060. );
  3061. $qattributes["statistics_showgraph"]=array(
  3062. 'types'=>'15ABCDEFGHIKLMNOPQRSTUWXYZ!:;|*',
  3063. 'category'=>$clang->gT('Statistics'),
  3064. 'inputtype'=>'singleselect',
  3065. 'sortorder'=>101,
  3066. 'options'=>array(1=>$clang->gT('Yes'), 0=>$clang->gT('No')),
  3067. 'help'=>$clang->gT("Display a chart in the statistics?"),
  3068. 'caption'=>$clang->gT("Display chart"),
  3069. 'default'=>1
  3070. );
  3071. $qattributes["statistics_graphtype"]=array(
  3072. "types"=>'15ABCDEFGHIKLNOQRSTUWXYZ!:;|*',
  3073. 'category'=>$clang->gT('Statistics'),
  3074. 'inputtype'=>'singleselect',
  3075. 'sortorder'=>102,
  3076. 'options'=>array(0=>$clang->gT('Bar chart'), 1=>$clang->gT('Pie chart')),
  3077. 'help'=>$clang->gT("Select the type of chart to be displayed"),
  3078. 'caption'=>$clang->gT("Chart type"),
  3079. 'default'=>0
  3080. );
  3081. $qattributes["location_mapservice"]=array(
  3082. "types"=>"S",
  3083. 'category'=>$clang->gT('Location'),
  3084. 'sortorder'=>90,
  3085. 'inputtype'=>'singleselect',
  3086. 'options'=>array(0=>$clang->gT('Off'),
  3087. 1=>$clang->gT('Google Maps')),
  3088. 'default' => 0,
  3089. "help"=>$clang->gT("Activate this to show a map above the input field where the user can select a location"),
  3090. "caption"=>$clang->gT("Use mapping service"));
  3091. $qattributes["location_mapwidth"]=array(
  3092. "types"=>"S",
  3093. 'category'=>$clang->gT('Location'),
  3094. 'sortorder'=>102,
  3095. 'inputtype'=>'text',
  3096. 'default'=>'500',
  3097. "help"=>$clang->gT("Width of the map in pixel"),
  3098. "caption"=>$clang->gT("Map width"));
  3099. $qattributes["location_mapheight"]=array(
  3100. "types"=>"S",
  3101. 'category'=>$clang->gT('Location'),
  3102. 'sortorder'=>103,
  3103. 'inputtype'=>'text',
  3104. 'default'=>'300',
  3105. "help"=>$clang->gT("Height of the map in pixel"),
  3106. "caption"=>$clang->gT("Map height"));
  3107. $qattributes["location_nodefaultfromip"]=array(
  3108. "types"=>"S",
  3109. 'category'=>$clang->gT('Location'),
  3110. 'sortorder'=>91,
  3111. 'inputtype'=>'singleselect',
  3112. 'options'=>array(0=>$clang->gT('Yes'),
  3113. 1=>$clang->gT('No')),
  3114. 'default' => 0,
  3115. "help"=>$clang->gT("Get the default location using the user's IP address?"),
  3116. "caption"=>$clang->gT("IP as default location"));
  3117. $qattributes["location_defaultcoordinates"]=array(
  3118. "types"=>"S",
  3119. 'category'=>$clang->gT('Location'),
  3120. 'sortorder'=>101,
  3121. 'inputtype'=>'text',
  3122. "help"=>$clang->gT('Default coordinates of the map when the page first loads. Format: latitude [space] longtitude'),
  3123. "caption"=>$clang->gT('Default position'));
  3124. $qattributes["location_mapzoom"]=array(
  3125. "types"=>"S",
  3126. 'category'=>$clang->gT('Location'),
  3127. 'sortorder'=>101,
  3128. 'inputtype'=>'text',
  3129. 'default'=>'11',
  3130. "help"=>$clang->gT("Map zoom level"),
  3131. "caption"=>$clang->gT("Zoom level"));
  3132. // End Map Options
  3133. $qattributes["hide_tip"]=array(
  3134. "types"=>"15ABCDEFGHIKLMNOPQRSTUXY!:;|",
  3135. 'category'=>$clang->gT('Display'),
  3136. 'sortorder'=>100,
  3137. 'inputtype'=>'singleselect',
  3138. 'options'=>array(0=>$clang->gT('No'),
  3139. 1=>$clang->gT('Yes')),
  3140. 'default'=>0,
  3141. "help"=>$clang->gT('Hide the tip that is normally shown with a question'),
  3142. "caption"=>$clang->gT('Hide tip'));
  3143. $qattributes['hidden']=array(
  3144. 'types'=>'15ABCDEFGHIKLMNOPQRSTUWXYZ!:;|*',
  3145. 'category'=>$clang->gT('Display'),
  3146. 'sortorder'=>101,
  3147. 'inputtype'=>'singleselect',
  3148. 'options'=>array(0=>$clang->gT('No'),
  3149. 1=>$clang->gT('Yes')),
  3150. 'default'=>0,
  3151. 'help'=>$clang->gT('Hide this question at any time. This is useful for including data using answer prefilling.'),
  3152. 'caption'=>$clang->gT('Always hide this question'));
  3153. $qattributes["max_answers"]=array(
  3154. "types"=>"MPR1:;ABCEFKQ",
  3155. 'category'=>$clang->gT('Logic'),
  3156. 'sortorder'=>11,
  3157. 'inputtype'=>'integer',
  3158. "help"=>$clang->gT('Limit the number of possible answers'),
  3159. "caption"=>$clang->gT('Maximum answers'));
  3160. $qattributes["max_num_value"]=array(
  3161. "types"=>"K",
  3162. 'category'=>$clang->gT('Input'),
  3163. 'sortorder'=>100,
  3164. 'inputtype'=>'text',
  3165. "help"=>$clang->gT('Maximum sum value of multiple numeric input'),
  3166. "caption"=>$clang->gT('Maximum sum value'));
  3167. $qattributes["max_num_value_n"]=array(
  3168. "types"=>"NK",
  3169. 'category'=>$clang->gT('Input'),
  3170. 'sortorder'=>110,
  3171. 'inputtype'=>'integer',
  3172. "help"=>$clang->gT('Maximum value of the numeric input'),
  3173. "caption"=>$clang->gT('Maximum value'));
  3174. // $qattributes["max_num_value_sgqa"]=array(
  3175. // "types"=>"K",
  3176. // 'category'=>$clang->gT('Logic'),
  3177. // 'sortorder'=>100,
  3178. // 'inputtype'=>'text',
  3179. // "help"=>$clang->gT('Enter the SGQA identifier to use the total of a previous question as the maximum for this question'),
  3180. // "caption"=>$clang->gT('Max value from SGQA'));
  3181. $qattributes["maximum_chars"]=array(
  3182. "types"=>"STUNQK:;",
  3183. 'category'=>$clang->gT('Input'),
  3184. 'sortorder'=>100,
  3185. 'inputtype'=>'text',
  3186. "help"=>$clang->gT('Maximum characters allowed'),
  3187. "caption"=>$clang->gT('Maximum characters'));
  3188. $qattributes["min_answers"]=array(
  3189. "types"=>"MPR1:;ABCEFKQ",
  3190. 'category'=>$clang->gT('Logic'),
  3191. 'sortorder'=>10,
  3192. 'inputtype'=>'integer',
  3193. "help"=>$clang->gT('Ensure a minimum number of possible answers (0=No limit)'),
  3194. "caption"=>$clang->gT('Minimum answers'));
  3195. $qattributes["min_num_value"]=array(
  3196. "types"=>"K",
  3197. 'category'=>$clang->gT('Input'),
  3198. 'sortorder'=>100,
  3199. 'inputtype'=>'text',
  3200. "help"=>$clang->gT('The sum of the multiple numeric inputs must be greater than this value'),
  3201. "caption"=>$clang->gT('Minimum sum value'));
  3202. $qattributes["min_num_value_n"]=array(
  3203. "types"=>"NK",
  3204. 'category'=>$clang->gT('Input'),
  3205. 'sortorder'=>100,
  3206. 'inputtype'=>'integer',
  3207. "help"=>$clang->gT('Minimum value of the numeric input'),
  3208. "caption"=>$clang->gT('Minimum value'));
  3209. // $qattributes["min_num_value_sgqa"]=array(
  3210. // "types"=>"K",
  3211. // 'category'=>$clang->gT('Logic'),
  3212. // 'sortorder'=>100,
  3213. // 'inputtype'=>'text',
  3214. // "help"=>$clang->gT('Enter the SGQA identifier to use the total of a previous question as the minimum for this question'),
  3215. // "caption"=>$clang->gT('Minimum value from SGQA'));
  3216. $qattributes["multiflexible_max"]=array(
  3217. "types"=>":",
  3218. 'category'=>$clang->gT('Display'),
  3219. 'sortorder'=>112,
  3220. 'inputtype'=>'text',
  3221. "help"=>$clang->gT('Maximum value for array(mult-flexible) question type'),
  3222. "caption"=>$clang->gT('Maximum value'));
  3223. $qattributes["multiflexible_min"]=array(
  3224. "types"=>":",
  3225. 'category'=>$clang->gT('Display'),
  3226. 'sortorder'=>110,
  3227. 'inputtype'=>'text',
  3228. "help"=>$clang->gT('Minimum value for array(multi-flexible) question type'),
  3229. "caption"=>$clang->gT('Minimum value'));
  3230. $qattributes["multiflexible_step"]=array(
  3231. "types"=>":",
  3232. 'category'=>$clang->gT('Display'),
  3233. 'sortorder'=>111,
  3234. 'inputtype'=>'text',
  3235. "help"=>$clang->gT('Step value'),
  3236. "caption"=>$clang->gT('Step value'));
  3237. $qattributes["multiflexible_checkbox"]=array(
  3238. "types"=>":",
  3239. 'category'=>$clang->gT('Display'),
  3240. 'sortorder'=>100,
  3241. 'inputtype'=>'singleselect',
  3242. 'options'=>array(0=>$clang->gT('No'),
  3243. 1=>$clang->gT('Yes')),
  3244. 'default'=>0,
  3245. "help"=>$clang->gT('Use checkbox layout'),
  3246. "caption"=>$clang->gT('Checkbox layout'));
  3247. $qattributes["reverse"]=array(
  3248. "types"=>"D:",
  3249. 'category'=>$clang->gT('Display'),
  3250. 'sortorder'=>100,
  3251. 'inputtype'=>'singleselect',
  3252. 'options'=>array(0=>$clang->gT('No'),
  3253. 1=>$clang->gT('Yes')),
  3254. 'default'=>0,
  3255. "help"=>$clang->gT('Present answer options in reverse order'),
  3256. "caption"=>$clang->gT('Reverse answer order'));
  3257. // $qattributes["num_value_equals_sgqa"]=array(
  3258. // "types"=>"K",
  3259. // 'category'=>$clang->gT('Logic'),
  3260. // 'sortorder'=>100,
  3261. // 'inputtype'=>'text',
  3262. // "help"=>$clang->gT('SGQA identifier to use total of previous question as total for this question'),
  3263. // "caption"=>$clang->gT('Value equals SGQA'));
  3264. $qattributes["num_value_int_only"]=array(
  3265. "types"=>"N",
  3266. 'category'=>$clang->gT('Input'),
  3267. 'sortorder'=>100,
  3268. 'inputtype'=>'singleselect',
  3269. 'options'=>array(
  3270. 0=>$clang->gT('No'),
  3271. 1=>$clang->gT('Yes')),
  3272. 'default'=>0,
  3273. "help"=>$clang->gT('Restrict input to integer values'),
  3274. "caption"=>$clang->gT('Integer only'));
  3275. $qattributes["numbers_only"]=array(
  3276. "types"=>"Q;S*",
  3277. 'category'=>$clang->gT('Other'),
  3278. 'sortorder'=>150,
  3279. 'inputtype'=>'singleselect',
  3280. 'options'=>array(
  3281. 0=>$clang->gT('No'),
  3282. 1=>$clang->gT('Yes')
  3283. ),
  3284. 'default'=>0,
  3285. "help"=>$clang->gT('Allow only numerical input'),
  3286. "caption"=>$clang->gT('Numbers only')
  3287. );
  3288. $qattributes['show_totals'] = array(
  3289. 'types' => ';',
  3290. 'category' => $clang->gT('Other'),
  3291. 'sortorder' => 151,
  3292. 'inputtype' => 'singleselect',
  3293. 'options' => array(
  3294. 'X' => $clang->gT('Off'),
  3295. 'R' => $clang->gT('Rows'),
  3296. 'C' => $clang->gT('Columns'),
  3297. 'B' => $clang->gT('Both rows and columns')
  3298. ),
  3299. 'default' => 'X',
  3300. 'help' => $clang->gT('Show totals for either rows, columns or both rows and columns'),
  3301. 'caption' => $clang->gT('Show totals for')
  3302. );
  3303. $qattributes['show_grand_total'] = array(
  3304. 'types' => ';',
  3305. 'category' => $clang->gT('Other'),
  3306. 'sortorder' => 152,
  3307. 'inputtype' => 'singleselect',
  3308. 'options' => array(
  3309. 0 => $clang->gT('No'),
  3310. 1 => $clang->gT('Yes')
  3311. ),
  3312. 'default' => 0,
  3313. 'help' => $clang->gT('Show grand total for either columns or rows'),
  3314. 'caption' => $clang->gT('Show grand total')
  3315. );
  3316. $qattributes["input_boxes"]=array(
  3317. "types"=>":",
  3318. 'category'=>$clang->gT('Display'),
  3319. 'sortorder'=>100,
  3320. 'inputtype'=>'singleselect',
  3321. 'options'=>array(0=>$clang->gT('No'),
  3322. 1=>$clang->gT('Yes')),
  3323. 'default'=>0,
  3324. "help"=>$clang->gT("Present as text input boxes instead of dropdown lists"),
  3325. "caption"=>$clang->gT("Text inputs"));
  3326. $qattributes["other_comment_mandatory"]=array(
  3327. "types"=>"PLW!Z",
  3328. 'category'=>$clang->gT('Logic'),
  3329. 'sortorder'=>100,
  3330. 'inputtype'=>'singleselect',
  3331. 'options'=>array(0=>$clang->gT('No'),
  3332. 1=>$clang->gT('Yes')),
  3333. 'default'=>0,
  3334. "help"=>$clang->gT("Make the 'Other:' comment field mandatory when the 'Other:' option is active"),
  3335. "caption"=>$clang->gT("'Other:' comment mandatory"));
  3336. $qattributes["other_numbers_only"]=array(
  3337. "types"=>"LMP",
  3338. 'category'=>$clang->gT('Logic'),
  3339. 'sortorder'=>100,
  3340. 'inputtype'=>'singleselect',
  3341. 'options'=>array(0=>$clang->gT('No'),
  3342. 1=>$clang->gT('Yes')),
  3343. 'default'=>0,
  3344. "help"=>$clang->gT("Allow only numerical input for 'Other' text"),
  3345. "caption"=>$clang->gT("Numbers only for 'Other'"));
  3346. $qattributes["other_replace_text"]=array(
  3347. "types"=>"LMPWZ!",
  3348. 'category'=>$clang->gT('Display'),
  3349. 'sortorder'=>100,
  3350. 'inputtype'=>'text',
  3351. 'i18n'=>true,
  3352. "help"=>$clang->gT("Replaces the label of the 'Other:' answer option with a custom text"),
  3353. "caption"=>$clang->gT("Label for 'Other:' option"));
  3354. $qattributes["page_break"]=array(
  3355. "types"=>"15ABCDEFGHKLMNOPQRSTUWXYZ!:;|*",
  3356. 'category'=>$clang->gT('Other'),
  3357. 'sortorder'=>100,
  3358. 'inputtype'=>'singleselect',
  3359. 'options'=>array(0=>$clang->gT('No'),
  3360. 1=>$clang->gT('Yes')),
  3361. 'default'=>0,
  3362. "help"=>$clang->gT('Insert a page break before this question in printable view by setting this to Yes.'),
  3363. "caption"=>$clang->gT('Insert page break in printable view'));
  3364. $qattributes["prefix"]=array(
  3365. "types"=>"KNQS",
  3366. 'category'=>$clang->gT('Display'),
  3367. 'sortorder'=>10,
  3368. 'inputtype'=>'text',
  3369. 'i18n'=>true,
  3370. "help"=>$clang->gT('Add a prefix to the answer field'),
  3371. "caption"=>$clang->gT('Answer prefix'));
  3372. $qattributes["printable_help"]=array(
  3373. "types"=>"15ABCEFGHKLMNOPRWYZ!:*",
  3374. 'category'=>$clang->gT('Display'),
  3375. 'sortorder'=>201,
  3376. "inputtype"=>"text",
  3377. 'i18n'=>true,
  3378. 'default'=>"",
  3379. "help"=>$clang->gT('In the printable version replace the relevance equation with this explanation text.'),
  3380. "caption"=>$clang->gT("Relevance help for printable survey"));
  3381. $qattributes["public_statistics"]=array(
  3382. "types"=>"15ABCEFGHKLMNOPRWYZ!:*",
  3383. 'category'=>$clang->gT('Statistics'),
  3384. 'sortorder'=>80,
  3385. 'inputtype'=>'singleselect',
  3386. 'options'=>array(0=>$clang->gT('No'),
  3387. 1=>$clang->gT('Yes')),
  3388. 'default'=>0,
  3389. "help"=>$clang->gT('Show statistics of this question in the public statistics page'),
  3390. "caption"=>$clang->gT('Show in public statistics'));
  3391. $qattributes["random_order"]=array(
  3392. "types"=>"!ABCEFHKLMOPQRWZ1:;",
  3393. 'category'=>$clang->gT('Display'),
  3394. 'sortorder'=>100,
  3395. 'inputtype'=>'singleselect',
  3396. 'options'=>array(0=>$clang->gT('Off'),
  3397. 1=>$clang->gT('Randomize on each page load')
  3398. //,2=>$clang->gT('Randomize once on survey start') //Mdekker: commented out as code to handle this was removed in refactoring
  3399. ),
  3400. 'default'=>0,
  3401. "help"=>$clang->gT('Present answers in random order'),
  3402. "caption"=>$clang->gT('Random answer order'));
  3403. /*
  3404. $qattributes['relevance']=array(
  3405. 'types'=>'15ABCDEFGHIKLMNOPQRSTUWXYZ!:;|*',
  3406. 'category'=>$clang->gT('Display'),
  3407. 'sortorder'=>1,
  3408. 'inputtype'=>'text',
  3409. 'default'=>'1',
  3410. 'help'=>$clang->gT('The relevance equation determines whether a question should be shown (if true) or hiddden and marked as Not Applicable (if false).'
  3411. . ' The relevance equation can be as complex as you like, using any combination of mathematical operators, nested parentheses,'
  3412. . ' any variable or token that has already been set, and any of more than 50 functions. It is parsed by the ExpressionManager.'),
  3413. 'caption'=>$clang->gT('Relevance equation'));
  3414. */
  3415. $qattributes["showpopups"]=array(
  3416. "types"=>"R",
  3417. 'category'=>$clang->gT('Display'),
  3418. 'sortorder'=>110,
  3419. 'inputtype'=>'singleselect',
  3420. 'options'=>array(0=>$clang->gT('No'),
  3421. 1=>$clang->gT('Yes')),
  3422. 'default'=>1,
  3423. "caption"=>$clang->gT('Show javascript alert'),
  3424. "help"=>$clang->gT('Show an alert if answers exceeds the number of max answers'));
  3425. $qattributes["samechoiceheight"]=array(
  3426. "types"=>"R",
  3427. 'category'=>$clang->gT('Display'),
  3428. 'sortorder'=>120,
  3429. 'inputtype'=>'singleselect',
  3430. 'options'=>array(0=>$clang->gT('No'),
  3431. 1=>$clang->gT('Yes')),
  3432. 'default'=>1,
  3433. "caption"=>$clang->gT('Same height for all choice'),
  3434. "help"=>$clang->gT('Force each choice to have the same height'));
  3435. $qattributes["samelistheight"]=array(
  3436. "types"=>"R",
  3437. 'category'=>$clang->gT('Display'),
  3438. 'sortorder'=>121,
  3439. 'inputtype'=>'singleselect',
  3440. 'options'=>array(0=>$clang->gT('No'),
  3441. 1=>$clang->gT('Yes')),
  3442. 'default'=>1,
  3443. "caption"=>$clang->gT('Same height for lists'),
  3444. "help"=>$clang->gT('Force the choice list and the rank list to have the same height'));
  3445. $qattributes["parent_order"]=array(
  3446. "types"=>":",
  3447. 'category'=>$clang->gT('Display'),
  3448. 'sortorder'=>100,
  3449. 'inputtype'=>'text',
  3450. "caption"=>$clang->gT('Get order from previous question'),
  3451. "help"=>$clang->gT('Enter question ID to get subquestion order from a previous question'));
  3452. $qattributes["slider_layout"]=array(
  3453. "types"=>"K",
  3454. 'category'=>$clang->gT('Slider'),
  3455. 'sortorder'=>1,
  3456. 'inputtype'=>'singleselect',
  3457. 'options'=>array(0=>$clang->gT('No'),
  3458. 1=>$clang->gT('Yes')),
  3459. 'default'=>0,
  3460. "help"=>$clang->gT('Use slider layout'),
  3461. "caption"=>$clang->gT('Use slider layout'));
  3462. $qattributes["slider_min"]=array(
  3463. "types"=>"K",
  3464. 'category'=>$clang->gT('Slider'),
  3465. 'sortorder'=>100,
  3466. 'inputtype'=>'text',
  3467. "help"=>$clang->gT('Slider minimum value'),
  3468. "caption"=>$clang->gT('Slider minimum value'));
  3469. $qattributes["slider_max"]=array(
  3470. "types"=>"K",
  3471. 'category'=>$clang->gT('Slider'),
  3472. 'sortorder'=>100,
  3473. 'inputtype'=>'text',
  3474. "help"=>$clang->gT('Slider maximum value'),
  3475. "caption"=>$clang->gT('Slider maximum value'));
  3476. $qattributes["slider_accuracy"]=array(
  3477. "types"=>"K",
  3478. 'category'=>$clang->gT('Slider'),
  3479. 'sortorder'=>100,
  3480. 'inputtype'=>'text',
  3481. "help"=>$clang->gT('Slider accuracy'),
  3482. "caption"=>$clang->gT('Slider accuracy'));
  3483. $qattributes["slider_default"]=array(
  3484. "types"=>"K",
  3485. 'category'=>$clang->gT('Slider'),
  3486. 'sortorder'=>100,
  3487. 'inputtype'=>'text',
  3488. "help"=>$clang->gT('Slider initial value'),
  3489. "caption"=>$clang->gT('Slider initial value'));
  3490. $qattributes["slider_middlestart"]=array(
  3491. "types"=>"K",
  3492. 'category'=>$clang->gT('Slider'),
  3493. 'sortorder'=>10,
  3494. 'inputtype'=>'singleselect',
  3495. 'options'=>array(0=>$clang->gT('No'),
  3496. 1=>$clang->gT('Yes')),
  3497. 'default'=>0,
  3498. "help"=>$clang->gT('The handle is displayed at the middle of the slider (this will not set the initial value)'),
  3499. "caption"=>$clang->gT('Slider starts at the middle position'));
  3500. $qattributes["slider_rating"]=array(
  3501. "types"=>"5",
  3502. 'category'=>$clang->gT('Display'),
  3503. 'sortorder'=>90,
  3504. 'inputtype'=>'singleselect',
  3505. 'options'=>array(
  3506. 0=>$clang->gT('No'),
  3507. 1=>$clang->gT('Yes - stars'),
  3508. 2=>$clang->gT('Yes - slider with emoticon'),
  3509. ),
  3510. 'default'=>0,
  3511. "help"=>$clang->gT('Use slider layout'),
  3512. "caption"=>$clang->gT('Use slider layout'));
  3513. $qattributes["slider_showminmax"]=array(
  3514. "types"=>"K",
  3515. 'category'=>$clang->gT('Slider'),
  3516. 'sortorder'=>100,
  3517. 'inputtype'=>'singleselect',
  3518. 'options'=>array(0=>$clang->gT('No'),
  3519. 1=>$clang->gT('Yes')),
  3520. 'default'=>0,
  3521. "help"=>$clang->gT('Display min and max value under the slider'),
  3522. "caption"=>$clang->gT('Display slider min and max value'));
  3523. $qattributes["slider_separator"]=array(
  3524. "types"=>"K",
  3525. 'category'=>$clang->gT('Slider'),
  3526. 'sortorder'=>100,
  3527. 'inputtype'=>'text',
  3528. "help"=>$clang->gT('Answer|Left-slider-text|Right-slider-text separator character'),
  3529. "caption"=>$clang->gT('Slider left/right text separator'));
  3530. $qattributes["suffix"]=array(
  3531. "types"=>"KNQS",
  3532. 'category'=>$clang->gT('Display'),
  3533. 'sortorder'=>11,
  3534. 'inputtype'=>'text',
  3535. 'i18n'=>true,
  3536. "help"=>$clang->gT('Add a suffix to the answer field'),
  3537. "caption"=>$clang->gT('Answer suffix'));
  3538. $qattributes["text_input_width"]=array(
  3539. "types"=>"KNSTUQ;",
  3540. 'category'=>$clang->gT('Display'),
  3541. 'sortorder'=>100,
  3542. 'inputtype'=>'text',
  3543. "help"=>$clang->gT('Width of text input box'),
  3544. "caption"=>$clang->gT('Input box width'));
  3545. $qattributes["use_dropdown"]=array(
  3546. "types"=>"1FO",
  3547. 'category'=>$clang->gT('Display'),
  3548. 'sortorder'=>112,
  3549. 'inputtype'=>'singleselect',
  3550. 'options'=>array(0=>$clang->gT('No'),
  3551. 1=>$clang->gT('Yes')),
  3552. 'default'=>0,
  3553. "help"=>$clang->gT('Present dropdown control(s) instead of list of radio buttons'),
  3554. "caption"=>$clang->gT('Use dropdown presentation'));
  3555. $qattributes["dropdown_size"]=array(
  3556. "types"=>"!", // TODO add these later? "1F",
  3557. 'category'=>$clang->gT('Display'),
  3558. 'sortorder'=>200,
  3559. 'inputtype'=>'text',
  3560. 'default'=>0,
  3561. "help"=>$clang->gT('For list dropdown boxes, show up to this many rows'),
  3562. "caption"=>$clang->gT('Height of dropdown'));
  3563. $qattributes["dropdown_prefix"]=array(
  3564. "types"=>"!", // TODO add these later? "1F",
  3565. 'category'=>$clang->gT('Display'),
  3566. 'sortorder'=>201,
  3567. 'inputtype'=>'singleselect',
  3568. 'options'=>array(0=>$clang->gT('None'),
  3569. 1=>$clang->gT('Order - like 3)'),
  3570. ),
  3571. 'default'=>0,
  3572. "help"=>$clang->gT('Accelerator keys for list items'),
  3573. "caption"=>$clang->gT('Prefix for list items'));
  3574. $qattributes["scale_export"]=array(
  3575. "types"=>"CEFGHLMOPWYZ1!:*",
  3576. 'category'=>$clang->gT('Other'),
  3577. 'sortorder'=>100,
  3578. 'inputtype'=>'singleselect',
  3579. 'options'=>array(0=>$clang->gT('Default'),
  3580. 1=>$clang->gT('Nominal'),
  3581. 2=>$clang->gT('Ordinal'),
  3582. 3=>$clang->gT('Scale')),
  3583. 'default'=>0,
  3584. "help"=>$clang->gT("Set a specific SPSS export scale type for this question"),
  3585. "caption"=>$clang->gT('SPSS export scale type'));
  3586. $qattributes["choice_title"]=array(
  3587. "types"=>"R",
  3588. 'category'=>$clang->gT('Other'),
  3589. 'sortorder'=>200,
  3590. "inputtype"=>"text",
  3591. 'i18n'=>true,
  3592. 'default'=>"",
  3593. "help"=>sprintf($clang->gT("Replace choice header (default: \"%s\")",'js'),$clang->gT("Your Choices")),
  3594. "caption"=>$clang->gT("Choice header"));
  3595. $qattributes["rank_title"]=array(
  3596. "types"=>"R",
  3597. 'category'=>$clang->gT('Other'),
  3598. 'sortorder'=>201,
  3599. "inputtype"=>"text",
  3600. 'i18n'=>true,
  3601. 'default'=>"",
  3602. "help"=>sprintf($clang->gT("Replace rank header (default: \"%s\")",'js'),$clang->gT("Your Ranking")),
  3603. "caption"=>$clang->gT("Rank header"));
  3604. //Timer attributes
  3605. $qattributes["time_limit"]=array(
  3606. "types"=>"STUXL!",
  3607. 'category'=>$clang->gT('Timer'),
  3608. 'sortorder'=>90,
  3609. "inputtype"=>"integer",
  3610. "help"=>$clang->gT("Limit time to answer question (in seconds)"),
  3611. "caption"=>$clang->gT("Time limit"));
  3612. $qattributes["time_limit_action"]=array(
  3613. "types"=>"STUXL!",
  3614. 'category'=>$clang->gT('Timer'),
  3615. 'sortorder'=>92,
  3616. 'inputtype'=>'singleselect',
  3617. 'options'=>array(1=>$clang->gT('Warn and move on'),
  3618. 2=>$clang->gT('Move on without warning'),
  3619. 3=>$clang->gT('Disable only')),
  3620. "default" => 1,
  3621. "help"=>$clang->gT("Action to perform when time limit is up"),
  3622. "caption"=>$clang->gT("Time limit action"));
  3623. $qattributes["time_limit_disable_next"]=array(
  3624. "types"=>"STUXL!",
  3625. 'category'=>$clang->gT('Timer'),
  3626. 'sortorder'=>94,
  3627. "inputtype"=>"singleselect",
  3628. 'default'=>0,
  3629. 'options'=>array(0=>$clang->gT('No'),
  3630. 1=>$clang->gT('Yes')),
  3631. "help"=>$clang->gT("Disable the next button until time limit expires"),
  3632. "caption"=>$clang->gT("Time limit disable next"));
  3633. $qattributes["time_limit_disable_prev"]=array(
  3634. "types"=>"STUXL!",
  3635. 'category'=>$clang->gT('Timer'),
  3636. 'sortorder'=>96,
  3637. "inputtype"=>"singleselect",
  3638. 'options'=>array(0=>$clang->gT('No'),
  3639. 1=>$clang->gT('Yes')),
  3640. 'default'=>0,
  3641. "help"=>$clang->gT("Disable the prev button until the time limit expires"),
  3642. "caption"=>$clang->gT("Time limit disable prev"));
  3643. $qattributes["time_limit_countdown_message"]=array(
  3644. "types"=>"STUXL!",
  3645. 'category'=>$clang->gT('Timer'),
  3646. 'sortorder'=>98,
  3647. "inputtype"=>"textarea",
  3648. 'i18n'=>true,
  3649. "help"=>$clang->gT("The text message that displays in the countdown timer during the countdown"),
  3650. "caption"=>$clang->gT("Time limit countdown message"));
  3651. $qattributes["time_limit_timer_style"]=array(
  3652. "types"=>"STUXL!",
  3653. 'category'=>$clang->gT('Timer'),
  3654. 'sortorder'=>100,
  3655. "inputtype"=>"textarea",
  3656. "help"=>$clang->gT("CSS Style for the message that displays in the countdown timer during the countdown"),
  3657. "caption"=>$clang->gT("Time limit timer CSS style"));
  3658. $qattributes["time_limit_message_delay"]=array(
  3659. "types"=>"STUXL!",
  3660. 'category'=>$clang->gT('Timer'),
  3661. 'sortorder'=>102,
  3662. "inputtype"=>"integer",
  3663. "help"=>$clang->gT("Display the 'time limit expiry message' for this many seconds before performing the 'time limit action' (defaults to 1 second if left blank)"),
  3664. "caption"=>$clang->gT("Time limit expiry message display time"));
  3665. $qattributes["time_limit_message"]=array(
  3666. "types"=>"STUXL!",
  3667. 'category'=>$clang->gT('Timer'),
  3668. 'sortorder'=>104,
  3669. "inputtype"=>"textarea",
  3670. 'i18n'=>true,
  3671. "help"=>$clang->gT("The message to display when the time limit has expired (a default message will display if this setting is left blank)"),
  3672. "caption"=>$clang->gT("Time limit expiry message"));
  3673. $qattributes["time_limit_message_style"]=array(
  3674. "types"=>"STUXL!",
  3675. 'category'=>$clang->gT('Timer'),
  3676. 'sortorder'=>106,
  3677. "inputtype"=>"textarea",
  3678. "help"=>$clang->gT("CSS style for the 'time limit expiry message'"),
  3679. "caption"=>$clang->gT("Time limit message CSS style"));
  3680. $qattributes["time_limit_warning"]=array(
  3681. "types"=>"STUXL!",
  3682. 'category'=>$clang->gT('Timer'),
  3683. 'sortorder'=>108,
  3684. "inputtype"=>"integer",
  3685. "help"=>$clang->gT("Display a 'time limit warning' when there are this many seconds remaining in the countdown (warning will not display if left blank)"),
  3686. "caption"=>$clang->gT("1st time limit warning message timer"));
  3687. $qattributes["time_limit_warning_display_time"]=array(
  3688. "types"=>"STUXL!",
  3689. 'category'=>$clang->gT('Timer'),
  3690. 'sortorder'=>110,
  3691. "inputtype"=>"integer",
  3692. "help"=>$clang->gT("The 'time limit warning' will stay visible for this many seconds (will not turn off if this setting is left blank)"),
  3693. "caption"=>$clang->gT("1st time limit warning message display time"));
  3694. $qattributes["time_limit_warning_message"]=array(
  3695. "types"=>"STUXL!",
  3696. 'category'=>$clang->gT('Timer'),
  3697. 'sortorder'=>112,
  3698. "inputtype"=>"textarea",
  3699. 'i18n'=>true,
  3700. "help"=>$clang->gT("The message to display as a 'time limit warning' (a default warning will display if this is left blank)"),
  3701. "caption"=>$clang->gT("1st time limit warning message"));
  3702. $qattributes["time_limit_warning_style"]=array(
  3703. "types"=>"STUXL!",
  3704. 'category'=>$clang->gT('Timer'),
  3705. 'sortorder'=>114,
  3706. "inputtype"=>"textarea",
  3707. "help"=>$clang->gT("CSS style used when the 'time limit warning' message is displayed"),
  3708. "caption"=>$clang->gT("1st time limit warning CSS style"));
  3709. $qattributes["time_limit_warning_2"]=array(
  3710. "types"=>"STUXL!",
  3711. 'category'=>$clang->gT('Timer'),
  3712. 'sortorder'=>116,
  3713. "inputtype"=>"integer",
  3714. "help"=>$clang->gT("Display the 2nd 'time limit warning' when there are this many seconds remaining in the countdown (warning will not display if left blank)"),
  3715. "caption"=>$clang->gT("2nd time limit warning message timer"));
  3716. $qattributes["time_limit_warning_2_display_time"]=array(
  3717. "types"=>"STUXL!",
  3718. 'category'=>$clang->gT('Timer'),
  3719. 'sortorder'=>118,
  3720. "inputtype"=>"integer",
  3721. "help"=>$clang->gT("The 2nd 'time limit warning' will stay visible for this many seconds (will not turn off if this setting is left blank)"),
  3722. "caption"=>$clang->gT("2nd time limit warning message display time"));
  3723. $qattributes["time_limit_warning_2_message"]=array(
  3724. "types"=>"STUXL!",
  3725. 'category'=>$clang->gT('Timer'),
  3726. 'sortorder'=>120,
  3727. "inputtype"=>"textarea",
  3728. 'i18n'=>true,
  3729. "help"=>$clang->gT("The 2nd message to display as a 'time limit warning' (a default warning will display if this is left blank)"),
  3730. "caption"=>$clang->gT("2nd time limit warning message"));
  3731. $qattributes["time_limit_warning_2_style"]=array(
  3732. "types"=>"STUXL!",
  3733. 'category'=>$clang->gT('Timer'),
  3734. 'sortorder'=>122,
  3735. "inputtype"=>"textarea",
  3736. "help"=>$clang->gT("CSS style used when the 2nd 'time limit warning' message is displayed"),
  3737. "caption"=>$clang->gT("2nd time limit warning CSS style"));
  3738. $qattributes["date_format"]=array(
  3739. "types"=>"D",
  3740. 'category'=>$clang->gT('Input'),
  3741. 'sortorder'=>100,
  3742. "inputtype"=>"text",
  3743. "help"=>$clang->gT("Specify a custom date/time format (the <i>d/dd m/mm yy/yyyy H/HH M/MM</i> formats and \"-./: \" characters are allowed for day/month/year/hour/minutes without or with leading zero respectively. Defaults to survey's date format"),
  3744. "caption"=>$clang->gT("Date/Time format"));
  3745. $qattributes["dropdown_dates_minute_step"]=array(
  3746. "types"=>"D",
  3747. 'category'=>$clang->gT('Input'),
  3748. 'sortorder'=>100,
  3749. "inputtype"=>"integer",
  3750. 'default'=>1,
  3751. "help"=>$clang->gT("Minute step interval when using select boxes"),
  3752. "caption"=>$clang->gT("Minute step interval"));
  3753. $qattributes["dropdown_dates_month_style"]=array(
  3754. "types"=>"D",
  3755. 'category'=>$clang->gT('Display'),
  3756. 'sortorder'=>100,
  3757. "inputtype"=>"singleselect",
  3758. 'options'=>array(0=>$clang->gT('Short names'),
  3759. 1=>$clang->gT('Full names'),
  3760. 2=>$clang->gT('Numbers')),
  3761. 'default'=>0,
  3762. "help"=>$clang->gT("Change the display style of the month when using select boxes"),
  3763. "caption"=>$clang->gT("Month display style"));
  3764. $qattributes["show_title"]=array(
  3765. "types"=>"|",
  3766. 'category'=>$clang->gT('File metadata'),
  3767. 'sortorder'=>124,
  3768. "inputtype"=>"singleselect",
  3769. 'options'=>array(0=>$clang->gT('No'),
  3770. 1=>$clang->gT('Yes')),
  3771. 'default'=>1,
  3772. "help"=>$clang->gT("Is the participant required to give a title to the uploaded file?"),
  3773. "caption"=>$clang->gT("Show title"));
  3774. $qattributes["show_comment"]=array(
  3775. "types"=>"|",
  3776. 'category'=>$clang->gT('File metadata'),
  3777. 'sortorder'=>126,
  3778. "inputtype"=>"singleselect",
  3779. 'options'=>array(0=>$clang->gT('No'),
  3780. 1=>$clang->gT('Yes')),
  3781. 'default'=>1,
  3782. "help"=>$clang->gT("Is the participant required to give a comment to the uploaded file?"),
  3783. "caption"=>$clang->gT("Show comment"));
  3784. $qattributes["max_filesize"]=array(
  3785. "types"=>"|",
  3786. 'category'=>$clang->gT('Other'),
  3787. 'sortorder'=>128,
  3788. "inputtype"=>"integer",
  3789. 'default'=>10240,
  3790. "help"=>$clang->gT("The participant cannot upload a single file larger than this size"),
  3791. "caption"=>$clang->gT("Maximum file size allowed (in KB)"));
  3792. $qattributes["max_num_of_files"]=array(
  3793. "types"=>"|",
  3794. 'category'=>$clang->gT('Other'),
  3795. 'sortorder'=>130,
  3796. "inputtype"=>"text",
  3797. 'default'=>'1',
  3798. "help"=>$clang->gT("Maximum number of files that the participant can upload for this question"),
  3799. "caption"=>$clang->gT("Max number of files"));
  3800. $qattributes["min_num_of_files"]=array(
  3801. "types"=>"|",
  3802. 'category'=>$clang->gT('Other'),
  3803. 'sortorder'=>132,
  3804. "inputtype"=>"text",
  3805. 'default'=>'0',
  3806. "help"=>$clang->gT("Minimum number of files that the participant must upload for this question"),
  3807. "caption"=>$clang->gT("Min number of files"));
  3808. $qattributes["allowed_filetypes"]=array(
  3809. "types"=>"|",
  3810. 'category'=>$clang->gT('Other'),
  3811. 'sortorder'=>134,
  3812. "inputtype"=>"text",
  3813. 'default'=>"png, gif, doc, odt",
  3814. "help"=>$clang->gT("Allowed file types in comma separated format. e.g. pdf,doc,odt"),
  3815. "caption"=>$clang->gT("Allowed file types"));
  3816. $qattributes["random_group"]=array(
  3817. "types"=>"15ABCDEFGHIKLMNOPQRSTUWXYZ!:;|",
  3818. 'category'=>$clang->gT('Logic'),
  3819. 'sortorder'=>100,
  3820. 'inputtype'=>'text',
  3821. "help"=>$clang->gT("Place questions into a specified randomization group, all questions included in the specified group will appear in a random order"),
  3822. "caption"=>$clang->gT("Randomization group name"));
  3823. // This is added to support historical behavior. Early versions of 1.92 used a value of "No", so if there was a min_sum_value or equals_sum_value, the question was not valid
  3824. // unless those criteria were met. In later releases of 1.92, the default was changed so that missing values were allowed even if those attributes were set
  3825. // This attribute lets authors control whether missing values should be allowed in those cases without needing to set min_answers
  3826. // Existing surveys will use the old behavior, but if the author edits the question, the default will be the new behavior.
  3827. $qattributes["value_range_allows_missing"]=array(
  3828. "types"=>"K",
  3829. 'category'=>$clang->gT('Input'),
  3830. 'sortorder'=>100,
  3831. "inputtype"=>"singleselect",
  3832. 'options'=>array(0=>$clang->gT('No'),
  3833. 1=>$clang->gT('Yes')),
  3834. 'default'=>1,
  3835. "help"=>$clang->gT("Is no answer (missing) allowed when either 'Equals sum value' or 'Minimum sum value' are set?"),
  3836. "caption"=>$clang->gT("Value range allows missing"));
  3837. //This builds a more useful array (don't modify)
  3838. if ($returnByName==false)
  3839. {
  3840. foreach($qattributes as $qname=>$qvalue)
  3841. {
  3842. for ($i=0; $i<=strlen($qvalue['types'])-1; $i++)
  3843. {
  3844. $qat[substr($qvalue['types'], $i, 1)][$qname]=array("name"=>$qname,
  3845. "inputtype"=>$qvalue['inputtype'],
  3846. "category"=>$qvalue['category'],
  3847. "sortorder"=>$qvalue['sortorder'],
  3848. "i18n"=>isset($qvalue['i18n'])?$qvalue['i18n']:false,
  3849. "readonly"=>isset($qvalue['readonly_when_active'])?$qvalue['readonly_when_active']:false,
  3850. "options"=>isset($qvalue['options'])?$qvalue['options']:'',
  3851. "default"=>isset($qvalue['default'])?$qvalue['default']:'',
  3852. "help"=>$qvalue['help'],
  3853. "caption"=>$qvalue['caption']);
  3854. }
  3855. }
  3856. return $qat;
  3857. }
  3858. else {
  3859. return $qattributes;
  3860. }
  3861. }
  3862. function categorySort($a, $b)
  3863. {
  3864. $result=strnatcasecmp($a['category'], $b['category']);
  3865. if ($result==0)
  3866. {
  3867. $result=$a['sortorder']-$b['sortorder'];
  3868. }
  3869. return $result;
  3870. }
  3871. // make sure the given string (which comes from a POST or GET variable)
  3872. // is safe to use in MySQL. This does nothing if gpc_magic_quotes is on.
  3873. function autoEscape($str) {
  3874. if (!get_magic_quotes_gpc()) {
  3875. return addslashes ($str);
  3876. }
  3877. return $str;
  3878. }
  3879. // the opposite of the above: takes a POST or GET variable which may or
  3880. // may not have been 'auto-quoted', and return the *unquoted* version.
  3881. // this is useful when the value is destined for a web page (eg) not
  3882. // a SQL query.
  3883. function autoUnescape($str) {
  3884. if (!isset($str)) {return null;};
  3885. if (!get_magic_quotes_gpc())
  3886. return $str;
  3887. return stripslashes($str);
  3888. }
  3889. // make a string safe to include in an HTML 'value' attribute.
  3890. function HTMLEscape($str) {
  3891. // escape newline characters, too, in case we put a value from
  3892. // a TEXTAREA into an <input type="hidden"> value attribute.
  3893. return str_replace(array("\x0A","\x0D"),array("&#10;","&#13;"),
  3894. htmlspecialchars( $str, ENT_QUOTES ));
  3895. }
  3896. /**
  3897. * Escapes a text value for db
  3898. *
  3899. * @param string $value
  3900. * @return string
  3901. */
  3902. function dbQuoteAll($value)
  3903. {
  3904. return Yii::app()->db->quoteValue($value);
  3905. }
  3906. // make a string safe to include in a JavaScript String parameter.
  3907. function javascriptEscape($str, $strip_tags=false, $htmldecode=false) {
  3908. $new_str ='';
  3909. if ($htmldecode==true) {
  3910. $str=html_entity_decode($str,ENT_QUOTES,'UTF-8');
  3911. }
  3912. if ($strip_tags==true)
  3913. {
  3914. $str=strip_tags($str);
  3915. }
  3916. return str_replace(array('\'','"', "\n", "\r"),
  3917. array("\\'",'\u0022', "\\n",'\r'),
  3918. $str);
  3919. }
  3920. /**
  3921. * This function mails a text $body to the recipient $to.
  3922. * You can use more than one recipient when using a semikolon separated string with recipients.
  3923. *
  3924. * @param string $body Body text of the email in plain text or HTML
  3925. * @param mixed $subject Email subject
  3926. * @param mixed $to Array with several email addresses or single string with one email address
  3927. * @param mixed $from
  3928. * @param mixed $sitename
  3929. * @param mixed $ishtml
  3930. * @param mixed $bouncemail
  3931. * @param mixed $attachment
  3932. * @return bool If successful returns true
  3933. */
  3934. function SendEmailMessage($body, $subject, $to, $from, $sitename, $ishtml=false, $bouncemail=null, $attachment=null, $customheaders="")
  3935. {
  3936. global $maildebug, $maildebugbody;
  3937. $clang = Yii::app()->lang;
  3938. $emailmethod = Yii::app()->getConfig('emailmethod');
  3939. $emailsmtphost = Yii::app()->getConfig("emailsmtphost");
  3940. $emailsmtpuser = Yii::app()->getConfig("emailsmtpuser");
  3941. $emailsmtppassword = Yii::app()->getConfig("emailsmtppassword");
  3942. $emailsmtpdebug = Yii::app()->getConfig("emailsmtpdebug");
  3943. $emailsmtpssl = Yii::app()->getConfig("emailsmtpssl");
  3944. $defaultlang = Yii::app()->getConfig("defaultlang");
  3945. $emailcharset = Yii::app()->getConfig("emailcharset");
  3946. if ($emailcharset!='utf-8')
  3947. {
  3948. $body=mb_convert_encoding($body,$emailcharset,'utf-8');
  3949. $subject=mb_convert_encoding($subject,$emailcharset,'utf-8');
  3950. $sitename=mb_convert_encoding($sitename,$emailcharset,'utf-8');
  3951. }
  3952. if (!is_array($to)){
  3953. $to=array($to);
  3954. }
  3955. if (!is_array($customheaders) && $customheaders == '')
  3956. {
  3957. $customheaders=array();
  3958. }
  3959. if (Yii::app()->getConfig('demoMode'))
  3960. {
  3961. $maildebug=$clang->gT('Email was not sent because demo-mode is activated.');
  3962. $maildebugbody='';
  3963. return false;
  3964. }
  3965. if (is_null($bouncemail) )
  3966. {
  3967. $sender=$from;
  3968. }
  3969. else
  3970. {
  3971. $sender=$bouncemail;
  3972. }
  3973. require_once(APPPATH.'/third_party/phpmailer/class.phpmailer.php');
  3974. $mail = new PHPMailer;
  3975. if (!$mail->SetLanguage($defaultlang,APPPATH.'/third_party/phpmailer/language/'))
  3976. {
  3977. $mail->SetLanguage('en',APPPATH.'/third_party/phpmailer/language/');
  3978. }
  3979. $mail->CharSet = $emailcharset;
  3980. if (isset($emailsmtpssl) && trim($emailsmtpssl)!=='' && $emailsmtpssl!==0) {
  3981. if ($emailsmtpssl===1) {$mail->SMTPSecure = "ssl";}
  3982. else {$mail->SMTPSecure = $emailsmtpssl;}
  3983. }
  3984. $fromname='';
  3985. $fromemail=$from;
  3986. if (strpos($from,'<'))
  3987. {
  3988. $fromemail=substr($from,strpos($from,'<')+1,strpos($from,'>')-1-strpos($from,'<'));
  3989. $fromname=trim(substr($from,0, strpos($from,'<')-1));
  3990. }
  3991. $sendername='';
  3992. $senderemail=$sender;
  3993. if (strpos($sender,'<'))
  3994. {
  3995. $senderemail=substr($sender,strpos($sender,'<')+1,strpos($sender,'>')-1-strpos($sender,'<'));
  3996. $sendername=trim(substr($sender,0, strpos($sender,'<')-1));
  3997. }
  3998. switch ($emailmethod) {
  3999. case "qmail":
  4000. $mail->IsQmail();
  4001. break;
  4002. case "smtp":
  4003. $mail->IsSMTP();
  4004. if ($emailsmtpdebug>0)
  4005. {
  4006. $mail->SMTPDebug = $emailsmtpdebug;
  4007. }
  4008. if (strpos($emailsmtphost,':')>0)
  4009. {
  4010. $mail->Host = substr($emailsmtphost,0,strpos($emailsmtphost,':'));
  4011. $mail->Port = substr($emailsmtphost,strpos($emailsmtphost,':')+1);
  4012. }
  4013. else {
  4014. $mail->Host = $emailsmtphost;
  4015. }
  4016. $mail->Username =$emailsmtpuser;
  4017. $mail->Password =$emailsmtppassword;
  4018. if (trim($emailsmtpuser)!="")
  4019. {
  4020. $mail->SMTPAuth = true;
  4021. }
  4022. break;
  4023. case "sendmail":
  4024. $mail->IsSendmail();
  4025. break;
  4026. default:
  4027. //Set to the default value to rule out incorrect settings.
  4028. $emailmethod="mail";
  4029. $mail->IsMail();
  4030. }
  4031. $mail->SetFrom($fromemail, $fromname);
  4032. $mail->Sender = $senderemail; // Sets Return-Path for error notifications
  4033. foreach ($to as $singletoemail)
  4034. {
  4035. if (strpos($singletoemail, '<') )
  4036. {
  4037. $toemail=substr($singletoemail,strpos($singletoemail,'<')+1,strpos($singletoemail,'>')-1-strpos($singletoemail,'<'));
  4038. $toname=trim(substr($singletoemail,0, strpos($singletoemail,'<')-1));
  4039. $mail->AddAddress($toemail,$toname);
  4040. }
  4041. else
  4042. {
  4043. $mail->AddAddress($singletoemail);
  4044. }
  4045. }
  4046. if (is_array($customheaders))
  4047. {
  4048. foreach ($customheaders as $key=>$val) {
  4049. $mail->AddCustomHeader($val);
  4050. }
  4051. }
  4052. $mail->AddCustomHeader("X-Surveymailer: $sitename Emailer (LimeSurvey.sourceforge.net)");
  4053. if (get_magic_quotes_gpc() != "0") {$body = stripcslashes($body);}
  4054. if ($ishtml) {
  4055. $mail->IsHTML(true);
  4056. $mail->Body = $body;
  4057. $mail->AltBody = strip_tags(breakToNewline(html_entity_decode($body,ENT_QUOTES,$emailcharset)));
  4058. } else
  4059. {
  4060. $mail->IsHTML(false);
  4061. $mail->Body = $body;
  4062. }
  4063. // add the attachment if there is one
  4064. if(!is_null($attachment))
  4065. $mail->AddAttachment($attachment);
  4066. if (trim($subject)!='') {$mail->Subject = "=?$emailcharset?B?" . base64_encode($subject) . "?=";}
  4067. if ($emailsmtpdebug>0) {
  4068. ob_start();
  4069. }
  4070. $sent=$mail->Send();
  4071. $maildebug=$mail->ErrorInfo;
  4072. if ($emailsmtpdebug>0) {
  4073. $maildebug .= '<li>'.$clang->gT('SMTP debug output:').'</li><pre>'.strip_tags(ob_get_contents()).'</pre>';
  4074. ob_end_clean();
  4075. }
  4076. $maildebugbody=$mail->Body;
  4077. //if(!$sent) var_dump($maildebug);
  4078. return $sent;
  4079. }
  4080. /**
  4081. * This functions removes all HTML tags, Javascript, CRs, linefeeds and other strange chars from a given text
  4082. *
  4083. * @param string $sTextToFlatten Text you want to clean
  4084. * @param boolan $keepSpan set to true for keep span, used for expression manager. Default: false
  4085. * @param boolan $bDecodeHTMLEntities If set to true then all HTML entities will be decoded to the specified charset. Default: false
  4086. * @param string $sCharset Charset to decode to if $decodeHTMLEntities is set to true. Default: UTF-8
  4087. * @param string $bStripNewLines strip new lines if true, if false replace all new line by \r\n. Default: true
  4088. *
  4089. * @return string Cleaned text
  4090. */
  4091. function flattenText($sTextToFlatten, $keepSpan=false, $bDecodeHTMLEntities=false, $sCharset='UTF-8', $bStripNewLines=true)
  4092. {
  4093. $sNicetext = stripJavaScript($sTextToFlatten);
  4094. // When stripping tags, add a space before closing tags so that strings with embedded HTML tables don't get concatenated
  4095. $sNicetext = str_replace(array('</td','</th'),array(' </td',' </th'), $sNicetext);
  4096. if ($keepSpan) {
  4097. // Keep <span> so can show EM syntax-highlighting; add space before tags so that word-wrapping not destroyed when remove tags.
  4098. $sNicetext = strip_tags($sNicetext,'<span><table><tr><td><th>');
  4099. }
  4100. else {
  4101. $sNicetext = strip_tags($sNicetext);
  4102. }
  4103. // ~\R~u : see "What \R matches" and "Newline sequences" in http://www.pcre.org/pcre.txt
  4104. if ($bStripNewLines ){ // strip new lines
  4105. $sNicetext = preg_replace(array('~\R~u'),array(' '), $sNicetext);
  4106. }
  4107. else // unify newlines to \r\n
  4108. {
  4109. $sNicetext = preg_replace(array('~\R~u'), array("\r\n"), $sNicetext);
  4110. }
  4111. if ($bDecodeHTMLEntities==true)
  4112. {
  4113. $sNicetext = str_replace('&nbsp;',' ', $sNicetext); // html_entity_decode does not convert &nbsp; to spaces
  4114. $sNicetext = html_entity_decode($sNicetext, ENT_QUOTES, $sCharset);
  4115. }
  4116. $sNicetext = trim($sNicetext);
  4117. return $sNicetext;
  4118. }
  4119. /**
  4120. * getArrayFilterExcludesCascadesForGroup() queries the database and produces a list of array_filter_exclude questions and targets with in the same group
  4121. * @return returns a keyed nested array, keyed by the qid of the question, containing cascade information
  4122. */
  4123. function getArrayFilterExcludesCascadesForGroup($surveyid, $gid="", $output="qid")
  4124. {
  4125. $surveyid=sanitize_int($surveyid);
  4126. $gid=sanitize_int($gid);
  4127. $cascaded=array();
  4128. $sources=array();
  4129. $qidtotitle=array();
  4130. $fieldmap = createFieldMap($surveyid,'full',false,false,getBaseLanguageFromSurveyID($surveyid));
  4131. if($gid != "") {
  4132. $qrows = arraySearchByKey($gid, $fieldmap, 'gid');
  4133. } else {
  4134. $qrows = $fieldmap;
  4135. }
  4136. $grows = array(); //Create an empty array in case query not return any rows
  4137. // Store each result as an array with in the $grows array
  4138. foreach ($qrows as $qrow) {
  4139. if (isset($qrow['gid']) && !empty($qrow['gid'])) {
  4140. $grows[$qrow['qid']] = array('qid' => $qrow['qid'],'type' => $qrow['type'], 'mandatory' => $qrow['mandatory'], 'title' => $qrow['title'], 'gid' => $qrow['gid']);
  4141. }
  4142. }
  4143. $attrmach = array(); // Stores Matches of filters that have their values as questions within current group
  4144. foreach ($grows as $qrow) // Cycle through questions to see if any have list_filter attributes
  4145. {
  4146. $qidtotitle[$qrow['qid']]=$qrow['title'];
  4147. $qresult = getQuestionAttributeValues($qrow['qid'],$qrow['type']);
  4148. if (isset($qresult['array_filter_exclude'])) // We Found a array_filter attribute
  4149. {
  4150. $val = $qresult['array_filter_exclude']; // Get the Value of the Attribute ( should be a previous question's title in same group )
  4151. foreach ($grows as $avalue) // Cycle through all the other questions in this group until we find the source question for this array_filter
  4152. {
  4153. if ($avalue['title'] == $val)
  4154. {
  4155. /* This question ($avalue) is the question that provides the source information we use
  4156. * to determine which answers show up in the question we're looking at, which is $qrow['qid']
  4157. * So, in other words, we're currently working on question $qrow['qid'], trying to find out more
  4158. * information about question $avalue['qid'], because that's the source */
  4159. $sources[$qrow['qid']]=$avalue['qid']; /* This question ($qrow['qid']) relies on answers in $avalue['qid'] */
  4160. if(isset($cascades)) {unset($cascades);}
  4161. $cascades=array(); /* Create an empty array */
  4162. /* At this stage, we know for sure that this question relies on one other question for the filter */
  4163. /* But this function wants to send back information about questions that rely on multiple other questions for the filter */
  4164. /* So we don't want to do anything yet */
  4165. /* What we need to do now, is check whether the question this one relies on, also relies on another */
  4166. /* The question we are now checking is $avalue['qid'] */
  4167. $keepgoing=1;
  4168. $questiontocheck=$avalue['qid'];
  4169. /* If there is a key in the $sources array that is equal to $avalue['qid'] then we want to add that
  4170. * to the $cascades array */
  4171. while($keepgoing > 0)
  4172. {
  4173. if(!empty($sources[$questiontocheck]))
  4174. {
  4175. $cascades[] = $sources[$questiontocheck];
  4176. /* Now we need to move down the chain */
  4177. /* We want to check the $sources[$questiontocheck] question */
  4178. $questiontocheck=$sources[$questiontocheck];
  4179. } else {
  4180. /* Since it was empty, there must not be any more questions down the cascade */
  4181. $keepgoing=0;
  4182. }
  4183. }
  4184. /* Now add all that info */
  4185. if(count($cascades) > 0) {
  4186. $cascaded[$qrow['qid']]=$cascades;
  4187. }
  4188. }
  4189. }
  4190. }
  4191. }
  4192. $cascade2=array();
  4193. if($output == "title")
  4194. {
  4195. foreach($cascaded as $key=>$cascade) {
  4196. foreach($cascade as $item)
  4197. {
  4198. $cascade2[$key][]=$qidtotitle[$item];
  4199. }
  4200. }
  4201. $cascaded=$cascade2;
  4202. }
  4203. return $cascaded;
  4204. }
  4205. /**
  4206. * getArrayFiltersForQuestion($qid) finds out if a question has an array_filter attribute and what codes where selected on target question
  4207. * @return returns an array of codes that were selected else returns false
  4208. */
  4209. function getArrayFiltersForQuestion($qid)
  4210. {
  4211. static $cache = array();
  4212. // TODO: Check list_filter values to make sure questions are previous?
  4213. $qid=sanitize_int($qid);
  4214. if (isset($cache[$qid])) return $cache[$qid];
  4215. $attributes = getQuestionAttributeValues($qid);
  4216. if (isset($attributes['array_filter']) && Yii::app()->session['fieldarray']) {
  4217. $val = $attributes['array_filter']; // Get the Value of the Attribute ( should be a previous question's title in same group )
  4218. foreach (Yii::app()->session['fieldarray'] as $fields)
  4219. {
  4220. if ($fields[2] == $val)
  4221. {
  4222. // we found the target question, now we need to know what the answers where, we know its a multi!
  4223. $fields[0]=sanitize_int($fields[0]);
  4224. //$query = "SELECT title FROM ".db_table_name('questions')." where parent_qid='{$fields[0]}' AND language='".Yii::app()->session[$surveyid]['s_lang']."' order by question_order";
  4225. $qresult=Questions::model()->findAllByAttributes(array("parent_qid"=> $fields[0], "language"=> Yii::app()->session[$surveyid]['s_lang']), array('order' => "question_order"));
  4226. $selected = array();
  4227. //while ($code = $qresult->fetchRow())
  4228. foreach ($qresult->readAll() as $code)
  4229. {
  4230. if (Yii::app()->session[$fields[1].$code['title']] == "Y"
  4231. || Yii::app()->session[$fields[1]] == $code['title']) array_push($selected,$code['title']);
  4232. }
  4233. //Now we also need to find out if (a) the question had "other" enabled, and (b) if that was selected
  4234. //$query = "SELECT other FROM ".db_table_name('questions')." where qid='{$fields[0]}'";
  4235. $qresult=Questions::model()->findAllByAttributes(array("qid"=>$fields[0]));
  4236. foreach ($qresult->readAll() as $row) {$other=$row['other'];}
  4237. if($other == "Y")
  4238. {
  4239. if(Yii::app()->session[$fields[1].'other'] && Yii::app()->session[$fields[1].'other'] !="") {array_push($selected, "other");}
  4240. }
  4241. $cache[$qid] = $selected;
  4242. return $cache[$qid];
  4243. }
  4244. }
  4245. $cache[$qid] = false;
  4246. return $cache[$qid];
  4247. }
  4248. $cache[$qid] = false;
  4249. return $cache[$qid];
  4250. }
  4251. /**
  4252. * getGroupsByQuestion($surveyid)
  4253. * @return returns a keyed array of groups to questions ie: array([1]=>[2]) question qid 1, is in group gid 2.
  4254. */
  4255. function getGroupsByQuestion($surveyid) {
  4256. $output=array();
  4257. $surveyid=sanitize_int($surveyid);
  4258. $result=Questions::model()->findAllByAttributes(array("sid"=>$surveyid));
  4259. foreach ($qresult->readAll() as $val)
  4260. {
  4261. $output[$val['qid']]=$val['gid'];
  4262. }
  4263. return $output;
  4264. }
  4265. /**
  4266. * getArrayFilterExcludesForQuestion($qid) finds out if a question has an array_filter_exclude attribute and what codes where selected on target question
  4267. * @return returns an array of codes that were selected else returns false
  4268. */
  4269. function getArrayFilterExcludesForQuestion($qid)
  4270. {
  4271. static $cascadesCache = array();
  4272. static $cache = array();
  4273. // TODO: Check list_filter values to make sure questions are previous?
  4274. // $surveyid = Yii::app()->getConfig('sid');
  4275. $surveyid=returnGlobal('sid');
  4276. $qid=sanitize_int($qid);
  4277. if (isset($cache[$qid])) return $cache[$qid];
  4278. $attributes = getQuestionAttributeValues($qid);
  4279. $excludevals=array();
  4280. if (isset($attributes['array_filter_exclude'])) // We Found a array_filter_exclude attribute
  4281. {
  4282. $selected=array();
  4283. $excludevals[] = $attributes['array_filter_exclude']; // Get the Value of the Attribute ( should be a previous question's title in same group )
  4284. /* Find any cascades and place them in the $excludevals array*/
  4285. if (!isset($cascadesCache[$surveyid])) {
  4286. $cascadesCache[$surveyid] = getArrayFilterExcludesCascadesForGroup($surveyid, "", "title");
  4287. }
  4288. $array_filterXqs_cascades = $cascadesCache[$surveyid];
  4289. if(isset($array_filterXqs_cascades[$qid]))
  4290. {
  4291. foreach($array_filterXqs_cascades[$qid] as $afc)
  4292. {
  4293. $excludevals[]=array("value"=>$afc);
  4294. }
  4295. }
  4296. /* For each $val (question title) that applies to this, check what values exist and add them to the $selected array */
  4297. foreach ($excludevals as $val)
  4298. {
  4299. foreach (Yii::app()->session['fieldarray'] as $fields) //iterate through every question in the survey
  4300. {
  4301. if ($fields[2] == $val)
  4302. {
  4303. // we found the target question, now we need to know what the answers were!
  4304. $fields[0]=sanitize_int($fields[0]);
  4305. $query = "SELECT title FROM {{questions}} where parent_qid='{$fields[0]}' AND language='".Yii::app()->session[$surveyid]['s_lang']."' order by question_order";
  4306. $qresult = dbExecuteAssoc($query); //Checked
  4307. foreach ($qresult->readAll() as $code)
  4308. {
  4309. if (isset(Yii::app()->session[$fields[1]]))
  4310. if ((isset(Yii::app()->session[$fields[1].$code['title']]) && Yii::app()->session[$fields[1].$code['title']] == "Y")
  4311. || Yii::app()->session[$fields[1]] == $code['title'])
  4312. array_push($selected,$code['title']);
  4313. }
  4314. //Now we also need to find out if (a) the question had "other" enabled, and (b) if that was selected
  4315. $query = "SELECT other FROM {{questions}} where qid='{$fields[0]}'";
  4316. $qresult = dbExecuteAssoc($query);
  4317. foreach ($qresult->readAll() as $row) {$other=$row['other'];}
  4318. if($other == "Y")
  4319. {
  4320. if(Yii::app()->session[$fields[1].'other'] != "") {array_push($selected, "other");}
  4321. }
  4322. }
  4323. }
  4324. }
  4325. if(count($selected) > 0)
  4326. {
  4327. $cache[$qid] = $selected;
  4328. return $cache[$qid];
  4329. } else {
  4330. $cache[$qid] = false;
  4331. return $cache[$qid];
  4332. }
  4333. }
  4334. $cache[$qid] = false;
  4335. return $cache[$qid];
  4336. }
  4337. function CSVEscape($str)
  4338. {
  4339. $str= str_replace('\n','\%n',$str);
  4340. return '"' . str_replace('"','""', $str) . '"';
  4341. }
  4342. function convertCSVRowToArray($string, $seperator, $quotechar)
  4343. {
  4344. $fields=preg_split('/' . $seperator . '(?=([^"]*"[^"]*")*(?![^"]*"))/',trim($string));
  4345. $fields=array_map('CSVUnquote',$fields);
  4346. return $fields;
  4347. }
  4348. function createPassword()
  4349. {
  4350. $pwchars = "abcdefhjmnpqrstuvwxyz23456789";
  4351. $password_length = 12;
  4352. $passwd = '';
  4353. for ($i=0; $i<$password_length; $i++)
  4354. {
  4355. $passwd .= $pwchars[(int)floor(rand(0,strlen($pwchars)-1))];
  4356. }
  4357. return $passwd;
  4358. }
  4359. function languageDropdown($surveyid,$selected)
  4360. {
  4361. $homeurl = Yii::app()->getConfig('homeurl');
  4362. $slangs = Survey::model()->findByPk($surveyid)->additionalLanguages;
  4363. $baselang = Survey::model()->findByPk($surveyid)->language;
  4364. array_unshift($slangs,$baselang);
  4365. $html = "<select class='listboxquestions' name='langselect' onchange=\"window.open(this.options[this.selectedIndex].value, '_top')\">\n";
  4366. foreach ($slangs as $lang)
  4367. {
  4368. $link = Yii::app()->homeUrl.("/admin/dataentry/sa/view/surveyid/".$surveyid."/lang/".$lang);
  4369. if ($lang == $selected) $html .= "\t<option value='{$link}' selected='selected'>".getLanguageNameFromCode($lang,false)."</option>\n";
  4370. if ($lang != $selected) $html .= "\t<option value='{$link}'>".getLanguageNameFromCode($lang,false)."</option>\n";
  4371. }
  4372. $html .= "</select>";
  4373. return $html;
  4374. }
  4375. function languageDropdownClean($surveyid,$selected)
  4376. {
  4377. $slangs = Survey::model()->findByPk($surveyid)->additionalLanguages;
  4378. $baselang = Survey::model()->findByPk($surveyid)->language;
  4379. array_unshift($slangs,$baselang);
  4380. $html = "<select class='listboxquestions' id='language' name='language'>\n";
  4381. foreach ($slangs as $lang)
  4382. {
  4383. if ($lang == $selected) $html .= "\t<option value='$lang' selected='selected'>".getLanguageNameFromCode($lang,false)."</option>\n";
  4384. if ($lang != $selected) $html .= "\t<option value='$lang'>".getLanguageNameFromCode($lang,false)."</option>\n";
  4385. }
  4386. $html .= "</select>";
  4387. return $html;
  4388. }
  4389. /**
  4390. * This function removes a directory recursively
  4391. *
  4392. * @param mixed $dirname
  4393. * @return bool
  4394. */
  4395. function rmdirr($dirname)
  4396. {
  4397. // Sanity check
  4398. if (!file_exists($dirname)) {
  4399. return false;
  4400. }
  4401. // Simple delete for a file
  4402. if (is_file($dirname) || is_link($dirname)) {
  4403. return @unlink($dirname);
  4404. }
  4405. // Loop through the folder
  4406. $dir = dir($dirname);
  4407. while (false !== $entry = $dir->read()) {
  4408. // Skip pointers
  4409. if ($entry == '.' || $entry == '..') {
  4410. continue;
  4411. }
  4412. // Recurse
  4413. rmdirr($dirname . DIRECTORY_SEPARATOR . $entry);
  4414. }
  4415. // Clean up
  4416. $dir->close();
  4417. return @rmdir($dirname);
  4418. }
  4419. /**
  4420. * This function removes surrounding and masking quotes from the CSV field
  4421. *
  4422. * @param mixed $field
  4423. * @return mixed
  4424. */
  4425. function CSVUnquote($field)
  4426. {
  4427. //print $field.":";
  4428. $field = preg_replace ("/^\040*\"/", "", $field);
  4429. $field = preg_replace ("/\"\040*$/", "", $field);
  4430. $field= str_replace('""','"',$field);
  4431. //print $field."\n";
  4432. return $field;
  4433. }
  4434. /**
  4435. * This function return actual completion state
  4436. *
  4437. * @return string (complete|incomplete|all) or false
  4438. */
  4439. function incompleteAnsFilterState()
  4440. {
  4441. $letsfilter='';
  4442. $letsfilter = returnGlobal('completionstate'); //read get/post completionstate
  4443. // first let's initialize the incompleteanswers session variable
  4444. if ($letsfilter != '')
  4445. { // use the read value if not empty
  4446. Yii::app()->session['incompleteanswers'] = $letsfilter;
  4447. }
  4448. elseif (empty(Yii::app()->session['incompleteanswers']))
  4449. { // sets default variable value from config file
  4450. Yii::app()->session['incompleteanswers'] = Yii::app()->getConfig('filterout_incomplete_answers');
  4451. }
  4452. if (Yii::app()->session['incompleteanswers']=='complete' || Yii::app()->session['incompleteanswers']=='all' || Yii::app()->session['incompleteanswers']=='incomplete') {
  4453. return Yii::app()->session['incompleteanswers'];
  4454. }
  4455. else
  4456. { // last resort is to prevent filtering
  4457. return false;
  4458. }
  4459. }
  4460. /**
  4461. * isCaptchaEnabled($screen, $usecaptchamode)
  4462. * @param string $screen - the screen name for which to test captcha activation
  4463. *
  4464. * @return boolean - returns true if captcha must be enabled
  4465. **/
  4466. function isCaptchaEnabled($screen, $captchamode='')
  4467. {
  4468. switch($screen)
  4469. {
  4470. case 'registrationscreen':
  4471. if ($captchamode == 'A' ||
  4472. $captchamode == 'B' ||
  4473. $captchamode == 'D' ||
  4474. $captchamode == 'R')
  4475. {
  4476. return true;
  4477. }
  4478. else
  4479. {
  4480. return false;
  4481. }
  4482. break;
  4483. case 'surveyaccessscreen':
  4484. if ($captchamode == 'A' ||
  4485. $captchamode == 'B' ||
  4486. $captchamode == 'C' ||
  4487. $captchamode == 'X')
  4488. {
  4489. return true;
  4490. }
  4491. else
  4492. {
  4493. return false;
  4494. }
  4495. break;
  4496. case 'saveandloadscreen':
  4497. if ($captchamode == 'A' ||
  4498. $captchamode == 'C' ||
  4499. $captchamode == 'D' ||
  4500. $captchamode == 'S')
  4501. {
  4502. return true;
  4503. }
  4504. else
  4505. {
  4506. return false;
  4507. }
  4508. return true;
  4509. break;
  4510. default:
  4511. return true;
  4512. break;
  4513. }
  4514. }
  4515. /**
  4516. * used for import[survey|questions|groups]
  4517. *
  4518. * @param mixed $string
  4519. * @return mixed
  4520. */
  4521. function convertCSVReturnToReturn($string)
  4522. {
  4523. $string= str_replace('\n', "\n", $string);
  4524. return str_replace('\%n', '\n', $string);
  4525. }
  4526. /**
  4527. * Check if a table does exist in the database
  4528. *
  4529. * @param string $sTableName Table name to check for (without dbprefix!))
  4530. * @return boolean True or false if table exists or not
  4531. */
  4532. function tableExists($sTableName)
  4533. {
  4534. $sTableName=Yii::app()->db->tablePrefix.str_replace(array('{','}'),array('',''),$sTableName);
  4535. return in_array($sTableName,Yii::app()->db->schema->getTableNames());
  4536. }
  4537. // Returns false if the survey is anonymous,
  4538. // and a token table exists: in this case the completed field of a token
  4539. // will contain 'Y' instead of the submitted date to ensure privacy
  4540. // Returns true otherwise
  4541. function isTokenCompletedDatestamped($thesurvey)
  4542. {
  4543. if ($thesurvey['anonymized'] == 'Y' && tableExists('tokens_'.$thesurvey['sid']))
  4544. {
  4545. return false;
  4546. }
  4547. else
  4548. {
  4549. return true;
  4550. }
  4551. }
  4552. /**
  4553. * example usage
  4554. * $date = "2006-12-31 21:00";
  4555. * $shift "+6 hours"; // could be days, weeks... see function strtotime() for usage
  4556. *
  4557. * echo sql_date_shift($date, "Y-m-d H:i:s", $shift);
  4558. *
  4559. * will output: 2007-01-01 03:00:00
  4560. *
  4561. * @param mixed $date
  4562. * @param mixed $dformat
  4563. * @param mixed $shift
  4564. * @return string
  4565. */
  4566. function dateShift($date, $dformat, $shift)
  4567. {
  4568. return date($dformat, strtotime($shift, strtotime($date)));
  4569. }
  4570. // getBounceEmail: returns email used to receive error notifications
  4571. function getBounceEmail($surveyid)
  4572. {
  4573. $surveyInfo=getSurveyInfo($surveyid);
  4574. if ($surveyInfo['bounce_email'] == '')
  4575. {
  4576. return null; // will be converted to from in MailText
  4577. }
  4578. else
  4579. {
  4580. return $surveyInfo['bounce_email'];
  4581. }
  4582. }
  4583. // getEmailFormat: returns email format for the survey
  4584. // returns 'text' or 'html'
  4585. function getEmailFormat($surveyid)
  4586. {
  4587. $surveyInfo=getSurveyInfo($surveyid);
  4588. if ($surveyInfo['htmlemail'] == 'Y')
  4589. {
  4590. return 'html';
  4591. }
  4592. else
  4593. {
  4594. return 'text';
  4595. }
  4596. }
  4597. // Check if user has manage rights for a template
  4598. function hasTemplateManageRights($userid, $templatefolder) {
  4599. $userid=sanitize_int($userid);
  4600. $templatefolder=sanitize_paranoid_string($templatefolder);
  4601. $criteria = new CDbCriteria;
  4602. $criteria->addColumnCondition(array('uid' => $userid));
  4603. $criteria->addSearchCondition('folder', $templatefolder);
  4604. $query=Templates_rights::model()->find($criteria);
  4605. //if ($result->RecordCount() == 0) return false;
  4606. if (is_null($query)) return false;
  4607. $row = $query;
  4608. //$row = $result->FetchRow();
  4609. return $row["use"];
  4610. }
  4611. /**
  4612. * This function creates an incrementing answer code based on the previous source-code
  4613. *
  4614. * @param mixed $sourcecode The previous answer code
  4615. */
  4616. function getNextCode($sourcecode)
  4617. {
  4618. $i=1;
  4619. $found=true;
  4620. $foundnumber=-1;
  4621. while ($i<=strlen($sourcecode) && $found)
  4622. {
  4623. $found=is_numeric(substr($sourcecode,-$i));
  4624. if ($found)
  4625. {
  4626. $foundnumber=substr($sourcecode,-$i);
  4627. $i++;
  4628. }
  4629. }
  4630. if ($foundnumber==-1)
  4631. {
  4632. return($sourcecode);
  4633. }
  4634. else
  4635. {
  4636. $foundnumber++;
  4637. $result=substr($sourcecode,0,strlen($sourcecode)-strlen($foundnumber)).$foundnumber;
  4638. return($result);
  4639. }
  4640. }
  4641. /**
  4642. * Translate links which are in any answer/question/survey/email template/label set to their new counterpart
  4643. *
  4644. * @param mixed $sType 'survey' or 'label'
  4645. * @param mixed $iOldSurveyID
  4646. * @param mixed $iNewSurveyID
  4647. * @param mixed $sString
  4648. * @return string
  4649. */
  4650. function translateLinks($sType, $iOldSurveyID, $iNewSurveyID, $sString)
  4651. {
  4652. if ($sType == 'survey')
  4653. {
  4654. $sPattern = "([^'\"]*)/upload/surveys/{$iOldSurveyID}/";
  4655. $sReplace = Yii::app()->getConfig("publicurl")."upload/surveys/{$iNewSurveyID}/";
  4656. return preg_replace('#'.$sPattern.'#', $sReplace, $sString);
  4657. }
  4658. elseif ($sType == 'label')
  4659. {
  4660. $pattern = "([^'\"]*)/upload/labels/{$iOldSurveyID}/";
  4661. $replace = Yii::app()->getConfig("publicurl")."upload/labels/{$iNewSurveyID}/";
  4662. return preg_replace('#'.$pattern.'#', $replace, $sString);
  4663. }
  4664. else // unkown type
  4665. {
  4666. return $sString;
  4667. }
  4668. }
  4669. /**
  4670. * This function creates the old fieldnames for survey import
  4671. *
  4672. * @param mixed $iOldSID The old survey id
  4673. * @param mixed $iNewSID The new survey id
  4674. * @param array $aGIDReplacements An array with group ids (oldgid=>newgid)
  4675. * @param array $aQIDReplacements An array with question ids (oldqid=>newqid)
  4676. */
  4677. function reverseTranslateFieldNames($iOldSID,$iNewSID,$aGIDReplacements,$aQIDReplacements)
  4678. {
  4679. $aGIDReplacements=array_flip($aGIDReplacements);
  4680. $aQIDReplacements=array_flip($aQIDReplacements);
  4681. if ($iOldSID==$iNewSID) {
  4682. $forceRefresh=true; // otherwise grabs the cached copy and throws undefined index exceptions
  4683. }
  4684. else {
  4685. $forceRefresh=false;
  4686. }
  4687. $aFieldMap = createFieldMap($iNewSID,'short',$forceRefresh,false,getBaseLanguageFromSurveyID($iNewSID));
  4688. $aFieldMappings=array();
  4689. foreach ($aFieldMap as $sFieldname=>$aFieldinfo)
  4690. {
  4691. if ($aFieldinfo['qid']!=null)
  4692. {
  4693. $aFieldMappings[$sFieldname]=$iOldSID.'X'.$aGIDReplacements[$aFieldinfo['gid']].'X'.$aQIDReplacements[$aFieldinfo['qid']].$aFieldinfo['aid'];
  4694. if ($aFieldinfo['type']=='1')
  4695. {
  4696. $aFieldMappings[$sFieldname]=$aFieldMappings[$sFieldname].'#'.$aFieldinfo['scale_id'];
  4697. }
  4698. // now also add a shortened field mapping which is needed for certain kind of condition mappings
  4699. $aFieldMappings[$iNewSID.'X'.$aFieldinfo['gid'].'X'.$aFieldinfo['qid']]=$iOldSID.'X'.$aGIDReplacements[$aFieldinfo['gid']].'X'.$aQIDReplacements[$aFieldinfo['qid']];
  4700. // Shortened field mapping for timings table
  4701. $aFieldMappings[$iNewSID.'X'.$aFieldinfo['gid']]=$iOldSID.'X'.$aGIDReplacements[$aFieldinfo['gid']];
  4702. }
  4703. }
  4704. return array_flip($aFieldMappings);
  4705. }
  4706. /**
  4707. * put your comment there...
  4708. *
  4709. * @param mixed $id
  4710. * @param mixed $type
  4711. */
  4712. function hasResources($id,$type='survey')
  4713. {
  4714. $dirname = Yii::app()->getConfig("uploaddir");
  4715. if ($type == 'survey')
  4716. {
  4717. $dirname .= "/surveys/$id";
  4718. }
  4719. elseif ($type == 'label')
  4720. {
  4721. $dirname .= "/labels/$id";
  4722. }
  4723. else
  4724. {
  4725. return false;
  4726. }
  4727. if (is_dir($dirname) && $dh=opendir($dirname))
  4728. {
  4729. while(($entry = readdir($dh)) !== false)
  4730. {
  4731. if($entry !== '.' && $entry !== '..')
  4732. {
  4733. return true;
  4734. break;
  4735. }
  4736. }
  4737. closedir($dh);
  4738. }
  4739. else
  4740. {
  4741. return false;
  4742. }
  4743. return false;
  4744. }
  4745. /**
  4746. * Creates a random sequence of characters
  4747. *
  4748. * @param mixed $length Length of resulting string
  4749. * @param string $pattern To define which characters should be in the resulting string
  4750. */
  4751. function randomChars($length,$pattern="23456789abcdefghijkmnpqrstuvwxyz")
  4752. {
  4753. $patternlength = strlen($pattern)-1;
  4754. for($i=0;$i<$length;$i++)
  4755. {
  4756. if(isset($key))
  4757. $key .= $pattern{rand(0,$patternlength)};
  4758. else
  4759. $key = $pattern{rand(0,$patternlength)};
  4760. }
  4761. return $key;
  4762. }
  4763. /**
  4764. * used to translate simple text to html (replacing \n with <br />
  4765. *
  4766. * @param mixed $mytext
  4767. * @param mixed $ishtml
  4768. * @return mixed
  4769. */
  4770. function conditionalNewlineToBreak($mytext,$ishtml,$encoded='')
  4771. {
  4772. if ($ishtml === true)
  4773. {
  4774. // $mytext has been processed by clang->gT with html mode
  4775. // and thus \n has already been translated to &#10;
  4776. if ($encoded == '')
  4777. {
  4778. $mytext=str_replace('&#10;', '<br />',$mytext);
  4779. }
  4780. return str_replace("\n", '<br />',$mytext);
  4781. }
  4782. else
  4783. {
  4784. return $mytext;
  4785. }
  4786. }
  4787. function breakToNewline( $data ) {
  4788. return preg_replace( '!<br.*>!iU', "\n", $data );
  4789. }
  4790. function safeDie($text)
  4791. {
  4792. //Only allowed tag: <br />
  4793. $textarray=explode('<br />',$text);
  4794. $textarray=array_map('htmlspecialchars',$textarray);
  4795. die(implode( '<br />',$textarray));
  4796. }
  4797. function fixCKeditorText($str)
  4798. {
  4799. $str = str_replace('<br type="_moz" />','',$str);
  4800. if ($str == "<br />" || $str == " " || $str == "&nbsp;")
  4801. {
  4802. $str = "";
  4803. }
  4804. if (preg_match("/^[\s]+$/",$str))
  4805. {
  4806. $str='';
  4807. }
  4808. if ($str == "\n")
  4809. {
  4810. $str = "";
  4811. }
  4812. if (trim($str) == "&nbsp;" || trim($str)=='')
  4813. { // chrome adds a single &nbsp; element to empty fckeditor fields
  4814. $str = "";
  4815. }
  4816. return $str;
  4817. }
  4818. /**
  4819. * This is a helper function for getAttributeFieldNames
  4820. *
  4821. * @param mixed $fieldname
  4822. */
  4823. function filterForAttributes ($fieldname)
  4824. {
  4825. if (strpos($fieldname,'attribute_')===false) return false; else return true;
  4826. }
  4827. /**
  4828. * Retrieves the attribute field names from the related token table
  4829. *
  4830. * @param mixed $iSurveyID The survey ID
  4831. * @return array The fieldnames
  4832. */
  4833. function GetAttributeFieldNames($iSurveyID)
  4834. {
  4835. if (!tableExists("{{tokens_{$iSurveyID}}}") || !$table = Yii::app()->db->schema->getTable('{{tokens_'.$iSurveyID.'}}'))
  4836. return Array();
  4837. return array_filter(array_keys($table->columns), 'filterForAttributes');
  4838. }
  4839. /**
  4840. * Returns the full list of attribute token fields including the properties for each field
  4841. * Use this instead of plain Survey::model()->findByPk($iSurveyID)->tokenAttributes calls because Survey::model()->findByPk($iSurveyID)->tokenAttributes may contain old descriptions where the fields does not physically exist
  4842. *
  4843. * @param integer $iSurveyID The Survey ID
  4844. */
  4845. function GetParticipantAttributes($iSurveyID)
  4846. {
  4847. if (!tableExists("{{tokens_{$iSurveyID}}}") || !$table = Yii::app()->db->schema->getTable('{{tokens_'.$iSurveyID.'}}'))
  4848. return Array();
  4849. $aFields= array_filter(array_keys($table->columns), 'filterForAttributes');
  4850. $aTokenAttributes=Survey::model()->findByPk($iSurveyID)->tokenAttributes;
  4851. if (count($aFields)==0) return array();
  4852. return array_intersect_key($aTokenAttributes,array_flip($aFields));
  4853. }
  4854. /**
  4855. * Retrieves the token field names usable for conditions from the related token table
  4856. *
  4857. * @param mixed $surveyid The survey ID
  4858. * @return array The fieldnames
  4859. */
  4860. function getTokenConditionsFieldNames($surveyid)
  4861. {
  4862. $extra_attrs=getAttributeFieldNames($surveyid);
  4863. $basic_attrs=Array('firstname','lastname','email','token','language','sent','remindersent','remindercount');
  4864. return array_merge($basic_attrs,$extra_attrs);
  4865. }
  4866. /**
  4867. * Retrieves the attribute names from the related token table
  4868. *
  4869. * @param mixed $surveyid The survey ID
  4870. * @param boolean $bOnlyAttributes Set this to true if you only want the fieldnames of the additional attribue fields - defaults to false
  4871. * @return array The fieldnames as key and names as value in an Array
  4872. */
  4873. function getTokenFieldsAndNames($surveyid, $bOnlyAttributes = false)
  4874. {
  4875. $clang = Yii::app()->lang;
  4876. $aBasicTokenFields=array('firstname'=>array(
  4877. 'description'=>$clang->gT('First name'),
  4878. 'mandatory'=>'N',
  4879. 'showregister'=>'Y'
  4880. ),
  4881. 'lastname'=>array(
  4882. 'description'=>$clang->gT('Last name'),
  4883. 'mandatory'=>'N',
  4884. 'showregister'=>'Y'
  4885. ),
  4886. 'email'=>array(
  4887. 'description'=>$clang->gT('Email address'),
  4888. 'mandatory'=>'N',
  4889. 'showregister'=>'Y'
  4890. ),
  4891. 'token'=>array(
  4892. 'description'=>$clang->gT('Token'),
  4893. 'mandatory'=>'N',
  4894. 'showregister'=>'Y'
  4895. ),
  4896. 'language'=>array(
  4897. 'description'=>$clang->gT('Language code'),
  4898. 'mandatory'=>'N',
  4899. 'showregister'=>'Y'
  4900. ),
  4901. 'sent'=>array(
  4902. 'description'=>$clang->gT('Invitation sent date'),
  4903. 'mandatory'=>'N',
  4904. 'showregister'=>'Y'
  4905. ),
  4906. 'remindersent'=>array(
  4907. 'description'=>$clang->gT('Last reminder sent date'),
  4908. 'mandatory'=>'N',
  4909. 'showregister'=>'Y'
  4910. ),
  4911. 'remindercount'=>array(
  4912. 'description'=>$clang->gT('Total numbers of sent reminders'),
  4913. 'mandatory'=>'N',
  4914. 'showregister'=>'Y'
  4915. ),
  4916. 'usesleft'=>array(
  4917. 'description'=>$clang->gT('Uses left'),
  4918. 'mandatory'=>'N',
  4919. 'showregister'=>'Y'
  4920. ),
  4921. );
  4922. $aExtraTokenFields=getAttributeFieldNames($surveyid);
  4923. $aSavedExtraTokenFields = Survey::model()->findByPk($surveyid)->tokenAttributes;
  4924. // Drop all fields that are in the saved field description but not in the table definition
  4925. $aSavedExtraTokenFields=array_intersect_key($aSavedExtraTokenFields,array_flip($aExtraTokenFields));
  4926. // Now add all fields that are in the table but not in the field description
  4927. foreach ($aExtraTokenFields as $sField)
  4928. {
  4929. if (!isset($aSavedExtraTokenFields[$sField]))
  4930. {
  4931. $aSavedExtraTokenFields[$sField]=array(
  4932. 'description'=>$sField,
  4933. 'mandatory'=>'N',
  4934. 'showregister'=>'N'
  4935. );
  4936. }
  4937. }
  4938. if ($bOnlyAttributes)
  4939. {
  4940. return $aSavedExtraTokenFields;
  4941. }
  4942. else
  4943. {
  4944. return array_merge($aBasicTokenFields,$aSavedExtraTokenFields);
  4945. }
  4946. }
  4947. /**
  4948. * Retrieves the token attribute value from the related token table
  4949. *
  4950. * @param mixed $surveyid The survey ID
  4951. * @param mixed $attrName The token-attribute field name
  4952. * @param mixed $token The token code
  4953. * @return string The token attribute value (or null on error)
  4954. */
  4955. function getAttributeValue($surveyid,$attrName,$token)
  4956. {
  4957. $attrName=strtolower($attrName);
  4958. if (!tableExists('tokens_'.$surveyid) || !in_array($attrName,getTokenConditionsFieldNames($surveyid)))
  4959. {
  4960. return null;
  4961. }
  4962. $surveyid=sanitize_int($surveyid);
  4963. Tokens_dynamic::sid($surveyid);
  4964. $query=Tokens_dynamic::model()->find(array("token"=>$token));
  4965. $count=$query->count(); // OK - AR count
  4966. if ($count != 1)
  4967. {
  4968. return null;
  4969. }
  4970. else
  4971. {
  4972. return $row->$attrName;//[0]
  4973. }
  4974. }
  4975. /**
  4976. * This function strips any content between and including <javascript> tags
  4977. *
  4978. * @param string $sContent String to clean
  4979. * @return string Cleaned string
  4980. */
  4981. function stripJavaScript($sContent){
  4982. $text = preg_replace('@<script[^>]*?>.*?</script>@si', '', $sContent);
  4983. return $text;
  4984. }
  4985. /**
  4986. * This function converts emebedded Javascript to Text
  4987. *
  4988. * @param string $sContent String to clean
  4989. * @return string Cleaned string
  4990. */
  4991. function showJavaScript($sContent){
  4992. $text = preg_replace_callback ('@<script[^>]*?>.*?</script>@si', create_function(
  4993. // single quotes are essential here,
  4994. // or alternative escape all $ as \$
  4995. '$matches',
  4996. 'return htmlspecialchars($matches[0]);'
  4997. ), $sContent);
  4998. return $text;
  4999. }
  5000. /**
  5001. * This function cleans files from the temporary directory being older than 1 day
  5002. * @todo Make the days configurable
  5003. */
  5004. function cleanTempDirectory()
  5005. {
  5006. $dir = Yii::app()->getConfig('tempdir').DIRECTORY_SEPARATOR;
  5007. $dp = opendir($dir) or show_error('Could not open temporary directory');
  5008. while ($file = readdir($dp)) {
  5009. if (is_file($dir.$file) && (filemtime($dir.$file)) < (strtotime('-1 days')) && $file!='index.html' && $file!='.gitignore' && $file!='readme.txt') {
  5010. @unlink($dir.$file);
  5011. }
  5012. }
  5013. $dir= Yii::app()->getConfig('tempdir').DIRECTORY_SEPARATOR.'upload'.DIRECTORY_SEPARATOR;
  5014. $dp = opendir($dir) or die ('Could not open temporary upload directory');
  5015. while ($file = readdir($dp)) {
  5016. if (is_file($dir.$file) && (filemtime($dir.$file)) < (strtotime('-1 days')) && $file!='index.html' && $file!='.gitignore' && $file!='readme.txt') {
  5017. @unlink($dir.$file);
  5018. }
  5019. }
  5020. closedir($dp);
  5021. }
  5022. function useFirebug()
  5023. {
  5024. if(FIREBUG == true)
  5025. {
  5026. return '<script type="text/javascript" src="http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js"></script>';
  5027. };
  5028. };
  5029. /**
  5030. * This is a convenience function for the coversion of datetime values
  5031. *
  5032. * @param mixed $value
  5033. * @param mixed $fromdateformat
  5034. * @param mixed $todateformat
  5035. * @return string
  5036. */
  5037. function convertDateTimeFormat($value, $fromdateformat, $todateformat)
  5038. {
  5039. Yii::import('application.libraries.Date_Time_Converter', true);
  5040. $date = new Date_Time_Converter($value, $fromdateformat);
  5041. return $date->convert($todateformat);
  5042. }
  5043. /**
  5044. * This function removes the UTF-8 Byte Order Mark from a string
  5045. *
  5046. * @param string $str
  5047. * @return string
  5048. */
  5049. function removeBOM($str=""){
  5050. if(substr($str, 0,3) == pack("CCC",0xef,0xbb,0xbf)) {
  5051. $str=substr($str, 3);
  5052. }
  5053. return $str;
  5054. }
  5055. /**
  5056. * This function requests the latest update information from the LimeSurvey.org website
  5057. *
  5058. * @returns array Contains update information or false if the request failed for some reason
  5059. */
  5060. /**********************************************/
  5061. /* This function needs ported still. */
  5062. /**********************************************/
  5063. function getUpdateInfo()
  5064. {
  5065. if (getGlobalSetting('SessionName')=='')
  5066. {
  5067. setGlobalSetting('SessionName',randomChars(64,'ABCDEFGHIJKLMNOPQRSTUVWXYZ!"$%&/()=?`+*~#",;.:abcdefghijklmnopqrstuvwxyz123456789'));
  5068. }
  5069. Yii::import('application.libraries.admin.http.httpRequestIt');
  5070. $http=new httpRequestIt;
  5071. $http->timeout=0;
  5072. $http->data_timeout=0;
  5073. $http->user_agent="Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)";
  5074. $http->GetRequestArguments("http://update.limesurvey.org?build=".Yii::app()->getConfig("buildnumber").'&id='.md5(getGlobalSetting('SessionName')),$arguments);
  5075. $updateinfo=false;
  5076. $error=$http->Open($arguments);
  5077. $error=$http->SendRequest($arguments);
  5078. $http->ReadReplyHeaders($headers);
  5079. if($error=="") {
  5080. $body=''; $full_body='';
  5081. for(;;){
  5082. $error = $http->ReadReplyBody($body,10000);
  5083. if($error != "" || strlen($body)==0) break;
  5084. $full_body .= $body;
  5085. }
  5086. $updateinfo=json_decode($full_body,true);
  5087. if ($http->response_status!='200')
  5088. {
  5089. $updateinfo['errorcode']=$http->response_status;
  5090. $updateinfo['errorhtml']=$full_body;
  5091. }
  5092. }
  5093. else
  5094. {
  5095. $updateinfo['errorcode']=$error;
  5096. $updateinfo['errorhtml']=$error;
  5097. }
  5098. unset( $http );
  5099. return $updateinfo;
  5100. }
  5101. /**
  5102. * This function updates the actual global variables if an update is available after using getUpdateInfo
  5103. * @return Array with update or error information
  5104. */
  5105. function updateCheck()
  5106. {
  5107. $updateinfo=getUpdateInfo();
  5108. if (isset($updateinfo['Targetversion']['build']) && (int)$updateinfo['Targetversion']['build']>(int)Yii::app()->getConfig('buildnumber') && trim(Yii::app()->getConfig('buildnumber'))!='')
  5109. {
  5110. setGlobalSetting('updateavailable',1);
  5111. setGlobalSetting('updatebuild',$updateinfo['Targetversion']['build']);
  5112. setGlobalSetting('updateversion',$updateinfo['Targetversion']['versionnumber']);
  5113. }
  5114. else
  5115. {
  5116. setGlobalSetting('updateavailable',0);
  5117. }
  5118. setGlobalSetting('updatelastcheck',date('Y-m-d H:i:s'));
  5119. return $updateinfo;
  5120. }
  5121. /**
  5122. * Return the goodchars to be used when filtering input for numbers.
  5123. *
  5124. * @param $lang string language used, for localisation
  5125. * @param $integer bool use only integer
  5126. * @param $negative bool allow negative values
  5127. */
  5128. function getNumericalFormat($lang = 'en', $integer = false, $negative = true) {
  5129. $goodchars = "0123456789";
  5130. if ($integer === false) $goodchars .= "."; //Todo, add localisation
  5131. if ($negative === true) $goodchars .= "-"; //Todo, check databases
  5132. return $goodchars;
  5133. }
  5134. /**
  5135. * Return array with token attribute.
  5136. *
  5137. * @param $surveyid int the surveyid
  5138. * @param $token string token code
  5139. *
  5140. * @return Array of token data
  5141. */
  5142. function getTokenData($surveyid, $token)
  5143. {
  5144. $thistoken = Tokens_dynamic::model($surveyid)->find('token = :token',array(':token' => $token));
  5145. $thistokenarray=array(); // so has default value
  5146. if($thistoken)
  5147. {
  5148. $thistokenarray =$thistoken->attributes;
  5149. }// Did we fill with empty string if not exist ?
  5150. return $thistokenarray;
  5151. }
  5152. /**
  5153. * This function returns the complete directory path to a given template name
  5154. *
  5155. * @param mixed $sTemplateName
  5156. */
  5157. function getTemplatePath($sTemplateName = false)
  5158. {
  5159. if (!$sTemplateName)
  5160. {
  5161. $sTemplateName=Yii::app()->getConfig('defaulttemplate'); // if $sTemplateName is NULL or false or ""
  5162. }
  5163. if (isStandardTemplate($sTemplateName))
  5164. {
  5165. return Yii::app()->getConfig("standardtemplaterootdir").DIRECTORY_SEPARATOR.$sTemplateName;
  5166. }
  5167. else
  5168. {
  5169. if (is_dir(Yii::app()->getConfig("usertemplaterootdir").DIRECTORY_SEPARATOR.$sTemplateName))
  5170. {
  5171. return Yii::app()->getConfig("usertemplaterootdir").DIRECTORY_SEPARATOR.$sTemplateName;
  5172. }
  5173. elseif (isStandardTemplate(Yii::app()->getConfig('defaulttemplate')))
  5174. {
  5175. return Yii::app()->getConfig("standardtemplaterootdir").DIRECTORY_SEPARATOR.$sTemplateName;
  5176. }
  5177. elseif (file_exists(Yii::app()->getConfig("usertemplaterootdir").DIRECTORY_SEPARATOR.Yii::app()->getConfig('defaulttemplate')))
  5178. {
  5179. return Yii::app()->getConfig("usertemplaterootdir").DIRECTORY_SEPARATOR.Yii::app()->getConfig('defaulttemplate');
  5180. }
  5181. else
  5182. {
  5183. return Yii::app()->getConfig("standardtemplaterootdir").DIRECTORY_SEPARATOR.'default';
  5184. }
  5185. }
  5186. }
  5187. /**
  5188. * This function returns the complete URL path to a given template name
  5189. *
  5190. * @param mixed $sTemplateName
  5191. */
  5192. function getTemplateURL($sTemplateName)
  5193. {
  5194. if (isStandardTemplate($sTemplateName))
  5195. {
  5196. return Yii::app()->getConfig("standardtemplaterooturl").'/'.$sTemplateName;
  5197. }
  5198. else
  5199. {
  5200. if (file_exists(Yii::app()->getConfig("usertemplaterootdir").'/'.$sTemplateName))
  5201. {
  5202. return Yii::app()->getConfig("usertemplaterooturl").'/'.$sTemplateName;
  5203. }
  5204. elseif (file_exists(Yii::app()->getConfig("usertemplaterootdir").'/'.Yii::app()->getConfig('defaulttemplate')))
  5205. {
  5206. return Yii::app()->getConfig("usertemplaterooturl").'/'.Yii::app()->getConfig('defaulttemplate');
  5207. }
  5208. elseif (file_exists(Yii::app()->getConfig("standardtemplaterootdir").'/'.Yii::app()->getConfig('defaulttemplate')))
  5209. {
  5210. return Yii::app()->getConfig("standardtemplaterooturl").'/'.Yii::app()->getConfig('defaulttemplate');
  5211. }
  5212. else
  5213. {
  5214. return Yii::app()->getConfig("standardtemplaterooturl").'/default';
  5215. }
  5216. }
  5217. }
  5218. /**
  5219. * Return an array of subquestions for a given sid/qid
  5220. *
  5221. * @param int $sid
  5222. * @param int $qid
  5223. * @param $sLanguage Language of the subquestion text
  5224. */
  5225. function getSubQuestions($sid, $qid, $sLanguage) {
  5226. static $subquestions;
  5227. if (!isset($subquestions[$sid]))
  5228. {
  5229. $subquestions[$sid]=array();
  5230. }
  5231. if (!isset($subquestions[$sid][$sLanguage])) {
  5232. $query = "SELECT sq.*, q.other FROM {{questions}} as sq, {{questions}} as q"
  5233. ." WHERE sq.parent_qid=q.qid AND q.sid=".$sid
  5234. ." AND sq.language='".$sLanguage. "' "
  5235. ." AND q.language='".$sLanguage. "' "
  5236. ." ORDER BY sq.parent_qid, q.question_order,sq.scale_id , sq.question_order";
  5237. $query = Yii::app()->db->createCommand($query)->query();
  5238. $resultset=array();
  5239. //while ($row=$result->FetchRow())
  5240. foreach ($query->readAll() as $row)
  5241. {
  5242. $resultset[$row['parent_qid']][] = $row;
  5243. }
  5244. $subquestions[$sid][$sLanguage] = $resultset;
  5245. }
  5246. if (isset($subquestions[$sid][$sLanguage][$qid])) return $subquestions[$sid][$sLanguage][$qid];
  5247. return array();
  5248. }
  5249. /**
  5250. * Wrapper function to retrieve an xmlwriter object and do error handling if it is not compiled
  5251. * into PHP
  5252. */
  5253. function getXMLWriter() {
  5254. if (!extension_loaded('xmlwriter')) {
  5255. safeDie('XMLWriter class not compiled into PHP, please contact your system administrator');
  5256. } else {
  5257. $xmlwriter = new XMLWriter();
  5258. }
  5259. return $xmlwriter;
  5260. }
  5261. /**
  5262. * Returns true when a token can not be used (either doesn't exist, has less then one usage left )
  5263. *
  5264. * @param mixed $tid Token
  5265. */
  5266. function usedTokens($token, $surveyid)
  5267. {
  5268. $utresult = true;
  5269. $query=Tokens_dynamic::model($surveyid)->findAllByAttributes(array("token"=>$token));
  5270. if (count($query) > 0) {
  5271. $row = $query[0];
  5272. if ($row->usesleft > 0) $utresult = false;
  5273. }
  5274. return $utresult;
  5275. }
  5276. /**
  5277. * SSLRedirect() generates a redirect URL for the appropriate SSL mode then applies it.
  5278. * (Was redirect() before CodeIgniter port.)
  5279. *
  5280. * @param $enforceSSLMode string 's' or '' (empty).
  5281. */
  5282. function SSLRedirect($enforceSSLMode)
  5283. {
  5284. $url = 'http'.$enforceSSLMode.'://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
  5285. if (!headers_sent())
  5286. { // If headers not sent yet... then do php redirect
  5287. //ob_clean();
  5288. header('Location: '.$url);
  5289. //ob_flush();
  5290. exit;
  5291. };
  5292. };
  5293. /**
  5294. * enforceSSLMode() $force_ssl is on or off, it checks if the current
  5295. * request is to HTTPS (or not). If $force_ssl is on, and the
  5296. * request is not to HTTPS, it redirects the request to the HTTPS
  5297. * version of the URL, if the request is to HTTPS, it rewrites all
  5298. * the URL variables so they also point to HTTPS.
  5299. */
  5300. function enforceSSLMode()
  5301. {
  5302. $bSSLActive = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != "off");
  5303. if (Yii::app()->getConfig('ssl_emergency_override') !== true )
  5304. {
  5305. $force_ssl = strtolower(getGlobalSetting('force_ssl'));
  5306. }
  5307. else
  5308. {
  5309. $force_ssl = 'off';
  5310. };
  5311. if( $force_ssl == 'on' && !$bSSLActive )
  5312. {
  5313. SSLRedirect('s');
  5314. }
  5315. if( $force_ssl == 'off' && $bSSLActive)
  5316. {
  5317. SSLRedirect('');
  5318. };
  5319. };
  5320. /**
  5321. * Returns the number of answers matching the quota
  5322. *
  5323. * @param int $iSurveyId - Survey identification number
  5324. * @param int $quotaid - quota id for which you want to compute the completed field
  5325. * @return mixed - Integer of matching entries in the result DB or 'N/A'
  5326. */
  5327. function getQuotaCompletedCount($iSurveyId, $quotaid)
  5328. {
  5329. $result = "N/A";
  5330. $quota_info = getQuotaInformation($iSurveyId, Survey::model()->findByPk($iSurveyId)->language, $quotaid);
  5331. $quota = $quota_info[0];
  5332. if (Yii::app()->db->schema->getTable('{{survey_' . $iSurveyId . '}}') &&
  5333. count($quota['members']) > 0)
  5334. {
  5335. // Keep a list of fields for easy reference
  5336. $fields_list = array();
  5337. // Construct an array of value for each $quota['members']['fieldnames']
  5338. $fields_query = array();
  5339. foreach ($quota['members'] as $member)
  5340. {
  5341. $criteria = new CDbCriteria;
  5342. foreach ($member['fieldnames'] as $fieldname)
  5343. {
  5344. if (!in_array($fieldname, $fields_list))
  5345. $fields_list[] = $fieldname;
  5346. $criteria->addColumnCondition(array($fieldname => $member['value']), 'OR');
  5347. }
  5348. $fields_query[$fieldname] = $criteria;
  5349. }
  5350. $criteria = new CDbCriteria;
  5351. foreach ($fields_list as $fieldname)
  5352. $criteria->mergeWith($fields_query[$fieldname]);
  5353. $result = Survey_dynamic::model($iSurveyId)->count($criteria);
  5354. }
  5355. return $result;
  5356. }
  5357. /**
  5358. * Creates an array with details on a particular response for display purposes
  5359. * Used in Print answers, Detailed response view and Detailed admin notification email
  5360. *
  5361. * @param mixed $iSurveyID
  5362. * @param mixed $iResponseID
  5363. * @param mixed $sLanguageCode
  5364. * @param boolean $bHonorConditions Apply conditions
  5365. */
  5366. function getFullResponseTable($iSurveyID, $iResponseID, $sLanguageCode, $bHonorConditions=false)
  5367. {
  5368. $aFieldMap = createFieldMap($iSurveyID,'full',false,false,$sLanguageCode);
  5369. $oLanguage = new Limesurvey_lang($sLanguageCode);
  5370. //Get response data
  5371. $idrow = Survey_dynamic::model($iSurveyID)->findByAttributes(array('id'=>$iResponseID));
  5372. // Create array of non-null values - those are the relevant ones
  5373. $aRelevantFields = array();
  5374. foreach ($aFieldMap as $sKey=>$fname)
  5375. {
  5376. if (LimeExpressionManager::QuestionIsRelevant($fname['qid']))
  5377. {
  5378. $aRelevantFields[$sKey]=$fname;
  5379. }
  5380. }
  5381. $aResultTable=array();
  5382. $oldgid = 0;
  5383. $oldqid = 0;
  5384. foreach ($aRelevantFields as $sKey=>$fname)
  5385. {
  5386. if (!empty($fname['qid']))
  5387. {
  5388. $attributes = getQuestionAttributeValues($fname['qid']);
  5389. if (getQuestionAttributeValue($attributes, 'hidden') == 1)
  5390. {
  5391. continue;
  5392. }
  5393. }
  5394. $question = $fname['question'];
  5395. $subquestion='';
  5396. if (isset($fname['gid']) && !empty($fname['gid'])) {
  5397. //Check to see if gid is the same as before. if not show group name
  5398. if ($oldgid !== $fname['gid'])
  5399. {
  5400. $oldgid = $fname['gid'];
  5401. if (LimeExpressionManager::GroupIsRelevant($fname['gid'])) {
  5402. $aResultTable['gid_'.$fname['gid']]=array($fname['group_name']);
  5403. }
  5404. }
  5405. }
  5406. if (!empty($fname['qid']))
  5407. {
  5408. if ($oldqid !== $fname['qid'])
  5409. {
  5410. $oldqid = $fname['qid'];
  5411. if (isset($fname['subquestion']) || isset($fname['subquestion1']) || isset($fname['subquestion2']))
  5412. {
  5413. $aResultTable['qid_'.$fname['sid'].'X'.$fname['gid'].'X'.$fname['qid']]=array($fname['question'],'','');
  5414. }
  5415. else
  5416. {
  5417. $answer = getExtendedAnswer($iSurveyID,$fname['fieldname'], $idrow[$fname['fieldname']],$oLanguage);
  5418. $aResultTable[$fname['fieldname']]=array($question,'',$answer);
  5419. continue;
  5420. }
  5421. }
  5422. }
  5423. else
  5424. {
  5425. $answer=getExtendedAnswer($iSurveyID,$fname['fieldname'], $idrow[$fname['fieldname']],$oLanguage);
  5426. $aResultTable[$fname['fieldname']]=array($question,'',$answer);
  5427. continue;
  5428. }
  5429. if (isset($fname['subquestion']))
  5430. $subquestion = "{$fname['subquestion']}";
  5431. if (isset($fname['subquestion1']))
  5432. $subquestion = "{$fname['subquestion1']}";
  5433. if (isset($fname['subquestion2']))
  5434. $subquestion .= "[{$fname['subquestion2']}]";
  5435. $answer = getExtendedAnswer($iSurveyID,$fname['fieldname'], $idrow[$fname['fieldname']],$oLanguage);
  5436. $aResultTable[$fname['fieldname']]=array('',$subquestion,$answer);
  5437. }
  5438. return $aResultTable;
  5439. }
  5440. /**
  5441. * Check if $str is an integer, or string representation of an integer
  5442. *
  5443. * @param mixed $mStr
  5444. */
  5445. function isNumericInt($mStr)
  5446. {
  5447. if(is_int($mStr))
  5448. return true;
  5449. elseif(is_string($mStr))
  5450. return preg_match("/^[0-9]+$/", $mStr);
  5451. return false;
  5452. }
  5453. /**
  5454. * Include Keypad headers
  5455. */
  5456. function includeKeypad()
  5457. {
  5458. $clang = Yii::app()->lang;
  5459. header_includes(Yii::app()->getConfig('generalscripts').'jquery/jquery.keypad.min.js');
  5460. if ($clang->langcode != 'en')
  5461. {
  5462. header_includes(Yii::app()->getConfig('generalscripts').'jquery/locale/jquery.ui.keypad-'.$clang->langcode.'.js');
  5463. }
  5464. header_includes('jquery.keypad.alt.css','css');
  5465. }
  5466. /**
  5467. * getQuotaInformation() returns quota information for the current survey
  5468. * @param string $surveyid - Survey identification number
  5469. * @param string $quotaid - Optional quotaid that restricts the result to a given quota
  5470. * @return array - nested array, Quotas->Members->Fields
  5471. */
  5472. function getQuotaInformation($surveyid,$language,$iQuotaID='all')
  5473. {
  5474. global $clienttoken;
  5475. $baselang = Survey::model()->findByPk($surveyid)->language;
  5476. $aAttributes=array('sid' => $surveyid);
  5477. if ($iQuotaID != 'all')
  5478. {
  5479. $aAttributes['id'] = $iQuotaID;
  5480. }
  5481. $result = Quota::model()->with(array('languagesettings' => array('condition' => "quotals_language='$language'")))->findAllByAttributes($aAttributes);
  5482. $quota_info = array();
  5483. $x=0;
  5484. $surveyinfo=getSurveyInfo($surveyid);
  5485. // Check all quotas for the current survey
  5486. //if ($result->RecordCount() > 0)
  5487. if (count($result) > 0)
  5488. {
  5489. //while ($survey_quotas = $result->FetchRow())
  5490. foreach ($result as $_survey_quotas)
  5491. {
  5492. $survey_quotas = $_survey_quotas->attributes;
  5493. // !!! Doubting this
  5494. foreach ($_survey_quotas->languagesettings[0] as $k => $v)
  5495. $survey_quotas[$k] = $v;
  5496. array_push($quota_info,array('Name' => $survey_quotas['name'],
  5497. 'Limit' => $survey_quotas['qlimit'],
  5498. 'Action' => $survey_quotas['action'],
  5499. 'Message' => $survey_quotas['quotals_message'],
  5500. 'Url' => $survey_quotas['quotals_url'],
  5501. 'UrlDescrip' => $survey_quotas['quotals_urldescrip'],
  5502. 'AutoloadUrl' => $survey_quotas['autoload_url']));
  5503. $result_qe = Quota_members::model()->findAllByAttributes(array('quota_id'=>$survey_quotas['id']));
  5504. $quota_info[$x]['members'] = array();
  5505. if (count($result_qe) > 0)
  5506. {
  5507. foreach ($result_qe as $quota_entry)
  5508. {
  5509. $quota_entry = $quota_entry->attributes;
  5510. $result_quest=Questions::model()->findByAttributes(array('qid'=>$quota_entry['qid'], 'language'=>$baselang));
  5511. $qtype=$result_quest->attributes;
  5512. $fieldnames = "0";
  5513. if ($qtype['type'] == "I" || $qtype['type'] == "G" || $qtype['type'] == "Y")
  5514. {
  5515. $fieldnames=array(0 => $surveyid.'X'.$qtype['gid'].'X'.$quota_entry['qid']);
  5516. $value = $quota_entry['code'];
  5517. }
  5518. if($qtype['type'] == "L" || $qtype['type'] == "O" || $qtype['type'] =="!")
  5519. {
  5520. $fieldnames=array(0 => $surveyid.'X'.$qtype['gid'].'X'.$quota_entry['qid']);
  5521. $value = $quota_entry['code'];
  5522. }
  5523. if($qtype['type'] == "M")
  5524. {
  5525. $fieldnames=array(0 => $surveyid.'X'.$qtype['gid'].'X'.$quota_entry['qid'].$quota_entry['code']);
  5526. $value = "Y";
  5527. }
  5528. if($qtype['type'] == "A" || $qtype['type'] == "B")
  5529. {
  5530. $temp = explode('-',$quota_entry['code']);
  5531. $fieldnames=array(0 => $surveyid.'X'.$qtype['gid'].'X'.$quota_entry['qid'].$temp[0]);
  5532. $value = $temp[1];
  5533. }
  5534. array_push($quota_info[$x]['members'],array('Title' => $qtype['title'],
  5535. 'type' => $qtype['type'],
  5536. 'code' => $quota_entry['code'],
  5537. 'value' => $value,
  5538. 'qid' => $quota_entry['qid'],
  5539. 'fieldnames' => $fieldnames));
  5540. }
  5541. }
  5542. $x++;
  5543. }
  5544. }
  5545. return $quota_info;
  5546. }
  5547. /**
  5548. * This function replaces the old insertans tags with new ones across a survey
  5549. *
  5550. * @param string $newsid Old SID
  5551. * @param string $oldsid New SID
  5552. * @param mixed $fieldnames Array array('oldfieldname'=>'newfieldname')
  5553. */
  5554. function translateInsertansTags($newsid,$oldsid,$fieldnames)
  5555. {
  5556. uksort($fieldnames, create_function('$a,$b', 'return strlen($a) < strlen($b);'));
  5557. Yii::app()->loadHelper('database');
  5558. $newsid=sanitize_int($newsid);
  5559. $oldsid=sanitize_int($oldsid);
  5560. # translate 'surveyls_urldescription' and 'surveyls_url' INSERTANS tags in surveyls
  5561. $sql = "SELECT surveyls_survey_id, surveyls_language, surveyls_urldescription, surveyls_url from {{surveys_languagesettings}}
  5562. WHERE surveyls_survey_id=".$newsid." AND (surveyls_urldescription LIKE '%{$oldsid}X%' OR surveyls_url LIKE '%{$oldsid}X%')";
  5563. $result = dbExecuteAssoc($sql) or show_error("Can't read groups table in transInsertAns "); // Checked
  5564. //while ($qentry = $res->FetchRow())
  5565. foreach ($result->readAll() as $qentry)
  5566. {
  5567. $urldescription = $qentry['surveyls_urldescription'];
  5568. $endurl = $qentry['surveyls_url'];
  5569. $language = $qentry['surveyls_language'];
  5570. foreach ($fieldnames as $sOldFieldname=>$sNewFieldname)
  5571. {
  5572. $pattern = $sOldFieldname;
  5573. $replacement = $sNewFieldname;
  5574. $urldescription=preg_replace('/'.$pattern.'/', $replacement, $urldescription);
  5575. $endurl=preg_replace('/'.$pattern.'/', $replacement, $endurl);
  5576. }
  5577. if (strcmp($urldescription,$qentry['surveyls_urldescription']) !=0 ||
  5578. (strcmp($endurl,$qentry['surveyls_url']) !=0))
  5579. {
  5580. // Update Field
  5581. $data = array(
  5582. 'surveyls_urldescription' => $urldescription,
  5583. 'surveyls_url' => $endurl
  5584. );
  5585. $where = array(
  5586. 'surveyls_survey_id' => $newsid,
  5587. 'surveyls_language' => $language
  5588. );
  5589. Surveys_languagesettings::model()->updateRecord($data,$where);
  5590. } // Enf if modified
  5591. } // end while qentry
  5592. # translate 'quotals_urldescrip' and 'quotals_url' INSERTANS tags in quota_languagesettings
  5593. $sql = "SELECT quotals_id, quotals_urldescrip, quotals_url from {{quota_languagesettings}} qls, {{quota}} q
  5594. WHERE sid=".$newsid." AND q.id=qls.quotals_quota_id AND (quotals_urldescrip LIKE '%{$oldsid}X%' OR quotals_url LIKE '%{$oldsid}X%')";
  5595. $result = dbExecuteAssoc($sql) or safeDie("Can't read quota table in transInsertAns"); // Checked
  5596. foreach ($result->readAll() as $qentry)
  5597. {
  5598. $urldescription = $qentry['quotals_urldescrip'];
  5599. $endurl = $qentry['quotals_url'];
  5600. foreach ($fieldnames as $sOldFieldname=>$sNewFieldname)
  5601. {
  5602. $pattern = $sOldFieldname;
  5603. $replacement = $sNewFieldname;
  5604. $urldescription=preg_replace('/'.$pattern.'/', $replacement, $urldescription);
  5605. $endurl=preg_replace('/'.$pattern.'/', $replacement, $endurl);
  5606. }
  5607. if (strcmp($urldescription,$qentry['quotals_urldescrip']) !=0 || (strcmp($endurl,$qentry['quotals_url']) !=0))
  5608. {
  5609. // Update Field
  5610. $sqlupdate = "UPDATE {{quota_languagesettings}} SET quotals_urldescrip='".$urldescription."', quotals_url='".$endurl."' WHERE quotals_id={$qentry['quotals_id']}";
  5611. $updateres=dbExecuteAssoc($sqlupdate) or safeDie ("Couldn't update INSERTANS in quota_languagesettings<br />$sqlupdate<br />"); //Checked
  5612. } // Enf if modified
  5613. } // end while qentry
  5614. # translate 'description' INSERTANS tags in groups
  5615. $sql = "SELECT gid, language, group_name, description from {{groups}}
  5616. WHERE sid=".$newsid." AND description LIKE '%{$oldsid}X%' OR group_name LIKE '%{$oldsid}X%'";
  5617. $res = dbExecuteAssoc($sql) or show_error("Can't read groups table in transInsertAns"); // Checked
  5618. //while ($qentry = $res->FetchRow())
  5619. foreach ($res->readAll() as $qentry)
  5620. {
  5621. $gpname = $qentry['group_name'];
  5622. $description = $qentry['description'];
  5623. $gid = $qentry['gid'];
  5624. $language = $qentry['language'];
  5625. foreach ($fieldnames as $sOldFieldname=>$sNewFieldname)
  5626. {
  5627. $pattern = $sOldFieldname;
  5628. $replacement = $sNewFieldname;
  5629. $gpname = preg_replace('/'.$pattern.'/', $replacement, $gpname);
  5630. $description=preg_replace('/'.$pattern.'/', $replacement, $description);
  5631. }
  5632. if (strcmp($description,$qentry['description']) !=0 ||
  5633. strcmp($gpname,$qentry['group_name']) !=0)
  5634. {
  5635. // Update Fields
  5636. $data = array(
  5637. 'description' => $description,
  5638. 'group_name' => $gpname
  5639. );
  5640. $where = array(
  5641. 'gid' => $gid,
  5642. 'language' => $language
  5643. );
  5644. Groups::model()->update($data,$where);
  5645. } // Enf if modified
  5646. } // end while qentry
  5647. # translate 'question' and 'help' INSERTANS tags in questions
  5648. $sql = "SELECT qid, language, question, help from {{questions}}
  5649. WHERE sid=".$newsid." AND (question LIKE '%{$oldsid}X%' OR help LIKE '%{$oldsid}X%')";
  5650. $result = dbExecuteAssoc($sql) or die("Can't read question table in transInsertAns "); // Checked
  5651. //while ($qentry = $res->FetchRow())
  5652. $aResultData=$result->readAll() ;
  5653. foreach ($aResultData as $qentry)
  5654. {
  5655. $question = $qentry['question'];
  5656. $help = $qentry['help'];
  5657. $qid = $qentry['qid'];
  5658. $language = $qentry['language'];
  5659. foreach ($fieldnames as $sOldFieldname=>$sNewFieldname)
  5660. {
  5661. $pattern = $sOldFieldname;
  5662. $replacement = $sNewFieldname;
  5663. $question=preg_replace('/'.$pattern.'/', $replacement, $question);
  5664. $help=preg_replace('/'.$pattern.'/', $replacement, $help);
  5665. }
  5666. if (strcmp($question,$qentry['question']) !=0 ||
  5667. strcmp($help,$qentry['help']) !=0)
  5668. {
  5669. // Update Field
  5670. $data = array(
  5671. 'question' => $question,
  5672. 'help' => $help
  5673. );
  5674. $where = array(
  5675. 'qid' => $qid,
  5676. 'language' => $language
  5677. );
  5678. Questions::model()->updateByPk($where,$data);
  5679. } // Enf if modified
  5680. } // end while qentry
  5681. # translate 'answer' INSERTANS tags in answers
  5682. $result=Answers::model()->oldNewInsertansTags($newsid,$oldsid);
  5683. //while ($qentry = $res->FetchRow())
  5684. foreach ($result as $qentry)
  5685. {
  5686. $answer = $qentry['answer'];
  5687. $code = $qentry['code'];
  5688. $qid = $qentry['qid'];
  5689. $language = $qentry['language'];
  5690. foreach ($fieldnames as $sOldFieldname=>$sNewFieldname)
  5691. {
  5692. $pattern = $sOldFieldname;
  5693. $replacement = $sNewFieldname;
  5694. $answer=preg_replace('/'.$pattern.'/', $replacement, $answer);
  5695. }
  5696. if (strcmp($answer,$qentry['answer']) !=0)
  5697. {
  5698. // Update Field
  5699. $data = array(
  5700. 'answer' => $answer,
  5701. 'qid' => $qid
  5702. );
  5703. $where = array(
  5704. 'code' => $code,
  5705. 'language' => $language
  5706. );
  5707. Answers::model()->update($data,$where);
  5708. } // Enf if modified
  5709. } // end while qentry
  5710. }
  5711. /**
  5712. * This function is a replacement of accessDenied.php which return appropriate error message which is then displayed.
  5713. *
  5714. * @params string $action - action for which acces denied error message is to be returned
  5715. * @params string sid - survey id
  5716. * @return $accesssummary - proper access denied error message
  5717. */
  5718. function accessDenied($action,$sid='')
  5719. {
  5720. $clang = Yii::app()->lang;
  5721. if (Yii::app()->session['loginID'])
  5722. {
  5723. $ugid = Yii::app()->getConfig('ugid');
  5724. $accesssummary = "<p><strong>".$clang->gT("Access denied!")."</strong><br />\n";
  5725. $scriptname = Yii::app()->getConfig('scriptname');
  5726. //$action=returnGlobal('action');
  5727. if ( $action == "dumpdb" )
  5728. {
  5729. $accesssummary .= "<p>".$clang->gT("You are not allowed dump the database!")."<br />";
  5730. $accesssummary .= "<a href='$scriptname'>".$clang->gT("Continue")."</a><br />&nbsp;\n";
  5731. }
  5732. elseif($action == "dumplabel")
  5733. {
  5734. $accesssummary .= "<p>".$clang->gT("You are not allowed export a label set!")."<br />";
  5735. $accesssummary .= "<a href='$scriptname'>".$clang->gT("Continue")."</a><br />&nbsp;\n";
  5736. }
  5737. elseif($action == "edituser")
  5738. {
  5739. $accesssummary .= "<p>".$clang->gT("You are not allowed to change user data!");
  5740. $accesssummary .= "<br /><br /><a href='$scriptname?action=editusers'>".$clang->gT("Continue")."</a><br />&nbsp;\n";
  5741. }
  5742. elseif($action == "newsurvey")
  5743. {
  5744. $accesssummary .= "<p>".$clang->gT("You are not allowed to create new surveys!")."<br />";
  5745. $accesssummary .= "<a href='$scriptname'>".$clang->gT("Continue")."</a><br />&nbsp;\n";
  5746. }
  5747. elseif($action == "deletesurvey")
  5748. {
  5749. $accesssummary .= "<p>".$clang->gT("You are not allowed to delete this survey!")."<br />";
  5750. $accesssummary .= "<a href='$scriptname?sid={$sid}'>".$clang->gT("Continue")."</a><br />&nbsp;\n";
  5751. }
  5752. elseif($action == "addquestion")
  5753. {
  5754. $accesssummary .= "<p>".$clang->gT("You are not allowed to add new questions for this survey!")."<br />";
  5755. $accesssummary .= "<a href='$scriptname?sid={$sid}'>".$clang->gT("Continue")."</a><br />&nbsp;\n";
  5756. }
  5757. elseif($action == "activate")
  5758. {
  5759. $accesssummary .= "<p>".$clang->gT("You are not allowed to activate this survey!")."<br />";
  5760. $accesssummary .= "<a href='$scriptname?sid={$sid}'>".$clang->gT("Continue")."</a><br />&nbsp;\n";
  5761. }
  5762. elseif($action == "deactivate")
  5763. {
  5764. $accesssummary .= "<p>".$clang->gT("You are not allowed to stop this survey!")."<br />";
  5765. $accesssummary .= "<a href='$scriptname?sid={$sid}'>".$clang->gT("Continue")."</a><br />&nbsp;\n";
  5766. }
  5767. elseif($action == "addgroup")
  5768. {
  5769. $accesssummary .= "<p>".$clang->gT("You are not allowed to add a group to this survey!")."<br />";
  5770. $accesssummary .= "<a href='$scriptname?sid={$sid}'>".$clang->gT("Continue")."</a><br />&nbsp;\n";
  5771. }
  5772. elseif($action == "ordergroups")
  5773. {
  5774. $link = Yii::app()->getController()->createUrl("/admin/survey/sa/view/surveyid/$sid");
  5775. $accesssummary .= "<p>".$clang->gT("You are not allowed to order groups in this survey!")."<br />";
  5776. $accesssummary .= "<a href='$link'>".$clang->gT("Continue")."</a><br />&nbsp;\n";
  5777. }
  5778. elseif($action == "editsurvey")
  5779. {
  5780. $link = Yii::app()->getController()->createUrl("/admin/survey/sa/view/surveyid/$sid");
  5781. $accesssummary .= "<p>".$clang->gT("You are not allowed to edit this survey!")."</p>";
  5782. $accesssummary .= "<a href='$link'>".$clang->gT("Continue")."</a><br />&nbsp;\n";
  5783. }
  5784. elseif($action == "editgroup")
  5785. {
  5786. $accesssummary .= "<p>".$clang->gT("You are not allowed to edit groups in this survey!")."</p>";
  5787. $accesssummary .= "<a href='$scriptname?sid={$sid}'>".$clang->gT("Continue")."</a><br />&nbsp;\n";
  5788. }
  5789. elseif($action == "browse_response" || $action == "listcolumn" || $action == "vvexport" || $action == "vvimport")
  5790. {
  5791. $accesssummary .= "<p>".$clang->gT("You are not allowed to browse responses!")."</p>";
  5792. $accesssummary .= "<a href='$scriptname?sid={$sid}'>".$clang->gT("Continue")."</a><br />&nbsp;\n";
  5793. }
  5794. elseif($action == "assessment")
  5795. {
  5796. $accesssummary .= "<p>".$clang->gT("You are not allowed to set assessment rules!")."</p>";
  5797. $accesssummary .= "<a href='$scriptname?sid={$sid}'>".$clang->gT("Continue")."</a><br />&nbsp;\n";
  5798. }
  5799. elseif($action == "delusergroup")
  5800. {
  5801. $accesssummary .= "<p>".$clang->gT("You are not allowed to delete this group!")."</p>";
  5802. $accesssummary .= "<a href='$scriptname?action=editusergroups'>".$clang->gT("Continue")."</a><br />&nbsp;\n";
  5803. }
  5804. elseif($action == "importsurvey")
  5805. {
  5806. $accesssummary .= "<p>".$clang->gT("You are not allowed to import a survey!")."</p>";
  5807. $accesssummary .= "<a href='$scriptname?sid={$sid}'>".$clang->gT("Continue")."</a><br />&nbsp;\n";
  5808. }
  5809. elseif($action == "importgroup")
  5810. {
  5811. $accesssummary .= "<p>".$clang->gT("You are not allowed to import a group!")."</p>";
  5812. $accesssummary .= "<a href='$scriptname?sid={$sid}'>".$clang->gT("Continue")."</a><br />&nbsp;\n";
  5813. }
  5814. elseif($action == "importquestion")
  5815. {
  5816. $accesssummary .= "<p>".$clang->gT("You are not allowed to to import a question!")."</p>";
  5817. $accesssummary .= "<a href='$scriptname?sid={$sid}'>".$clang->gT("Continue")."</a><br />&nbsp;\n";
  5818. }
  5819. elseif($action == "CSRFwarn") //won't be used.
  5820. {
  5821. $sURLID='';
  5822. if (isset($sid)) {
  5823. $sURLID="?sid={$sid}";
  5824. }
  5825. $accesssummary .= "<p><span color='errortitle'>".$clang->gT("Security alert")."</span>: ".$clang->gT("Someone may be trying to use your LimeSurvey session (CSRF attack suspected). If you just clicked on a malicious link, please report this to your system administrator.").'<br>'.$clang->gT('Also this problem can occur when you are working/editing in LimeSurvey in several browser windows/tabs at the same time.')."</p>";
  5826. $accesssummary .= "<a href='{$scriptname}{$sURLID}'>".$clang->gT("Continue")."</a><br />&nbsp;\n";
  5827. }
  5828. elseif($action == "FakeGET")
  5829. {
  5830. $accesssummary .= "<p><span class='errortitle'>".$clang->gT("Security alert")."</span>: ".$clang->gT("Someone may be trying to use your LimeSurvey session (CSRF attack suspected). If you just clicked on a malicious link, please report this to your system administrator.").'<br>'.$clang->gT('Also this problem can occur when you are working/editing in LimeSurvey in several browser windows/tabs at the same time.')."</p>";
  5831. $accesssummary .= "<a href='$scriptname?sid={$sid}'>".$clang->gT("Continue")."</a><br />&nbsp;\n";
  5832. }
  5833. else
  5834. {
  5835. $accesssummary .= "<br />".$clang->gT("You are not allowed to perform this operation!")."<br />\n";
  5836. if(!empty($sid))
  5837. {
  5838. $accesssummary .= "<br /><br /><a href='$scriptname?sid=$sid>".$clang->gT("Continue")."</a><br />&nbsp;\n";
  5839. }
  5840. elseif(!empty($ugid))
  5841. {
  5842. $accesssummary .= "<br /><br /><a href='$scriptname?action=editusergroups&ugid={$ugid}'>".$clang->gT("Continue")."</a><br />&nbsp;\n";
  5843. }
  5844. else
  5845. {
  5846. $accesssummary .= "<br /><br /><a href='$scriptname'>".$clang->gT("Continue")."</a><br />&nbsp;\n";
  5847. }
  5848. }
  5849. return $accesssummary;
  5850. }
  5851. }
  5852. /**
  5853. * cleanLanguagesFromSurvey() removes any languages from survey tables that are not in the passed list
  5854. * @param string $sid - the currently selected survey
  5855. * @param string $availlangs - space seperated list of additional languages in survey
  5856. * @return bool - always returns true
  5857. */
  5858. function cleanLanguagesFromSurvey($sid, $availlangs)
  5859. {
  5860. Yii::app()->loadHelper('database');
  5861. //$clang = Yii::app()->lang;
  5862. $sid=sanitize_int($sid);
  5863. $baselang = Survey::model()->findByPk($sid)->language;
  5864. if (!empty($availlangs) && $availlangs != " ")
  5865. {
  5866. $availlangs=sanitize_languagecodeS($availlangs);
  5867. $langs = explode(" ",$availlangs);
  5868. if($langs[count($langs)-1] == "") array_pop($langs);
  5869. }
  5870. $sqllang = "language <> '".$baselang."' ";
  5871. if (!empty($availlangs) && $availlangs != " ")
  5872. {
  5873. foreach ($langs as $lang)
  5874. {
  5875. $sqllang .= "AND language <> '".$lang."' ";
  5876. }
  5877. }
  5878. // Remove From Answers Table
  5879. $query = "SELECT qid FROM {{questions}} WHERE sid='{$sid}' AND $sqllang";
  5880. $qidresult = dbExecuteAssoc($query);
  5881. foreach ($qidresult->readAll() as $qrow)
  5882. {
  5883. $myqid = $qrow['qid'];
  5884. $query = "DELETE FROM {{answers}} WHERE qid='$myqid' AND $sqllang";
  5885. dbExecuteAssoc($query);
  5886. }
  5887. // Remove From Questions Table
  5888. $query = "DELETE FROM {{questions}} WHERE sid='{$sid}' AND $sqllang";
  5889. dbExecuteAssoc($query);
  5890. // Remove From Groups Table
  5891. $query = "DELETE FROM {{groups}} WHERE sid='{$sid}' AND $sqllang";
  5892. dbExecuteAssoc($query);
  5893. return true;
  5894. }
  5895. /**
  5896. * fixLanguageConsistency() fixes missing groups, questions, answers, quotas & assessments for languages on a survey
  5897. * @param string $sid - the currently selected survey
  5898. * @param string $availlangs - space seperated list of additional languages in survey - if empty all additional languages of a survey are checked against the base language
  5899. * @return bool - always returns true
  5900. */
  5901. function fixLanguageConsistency($sid, $availlangs='')
  5902. {
  5903. $sid=sanitize_int($sid);
  5904. $clang = Yii::app()->lang;
  5905. if (trim($availlangs)!='')
  5906. {
  5907. $availlangs=sanitize_languagecodeS($availlangs);
  5908. $langs = explode(" ",$availlangs);
  5909. if($langs[count($langs)-1] == "") array_pop($langs);
  5910. } else {
  5911. $langs=Survey::model()->findByPk($sid)->additionalLanguages;
  5912. }
  5913. $baselang = Survey::model()->findByPk($sid)->language;
  5914. $query = "SELECT * FROM {{groups}} WHERE sid='{$sid}' AND language='{$baselang}' ORDER BY group_order";
  5915. $result = Yii::app()->db->createCommand($query)->query();
  5916. foreach($result->readAll() as $group)
  5917. {
  5918. foreach ($langs as $lang)
  5919. {
  5920. $query = "SELECT count(gid) FROM {{groups}} WHERE sid='{$sid}' AND gid='{$group['gid']}' AND language='{$lang}'";
  5921. $gresult = Yii::app()->db->createCommand($query)->queryScalar();
  5922. if ($gresult < 1)
  5923. {
  5924. $data = array(
  5925. 'gid' => $group['gid'],
  5926. 'sid' => $group['sid'],
  5927. 'group_name' => $group['group_name'],
  5928. 'group_order' => $group['group_order'],
  5929. 'description' => $group['description'],
  5930. 'randomization_group' => $group['randomization_group'],
  5931. 'grelevance' => $group['grelevance'],
  5932. 'language' => $lang
  5933. );
  5934. switchMSSQLIdentityInsert('groups',true);
  5935. Yii::app()->db->createCommand()->insert('{{groups}}', $data);
  5936. switchMSSQLIdentityInsert('groups',false);
  5937. }
  5938. }
  5939. reset($langs);
  5940. }
  5941. $quests = array();
  5942. $query = "SELECT * FROM {{questions}} WHERE sid='{$sid}' AND language='{$baselang}' ORDER BY question_order";
  5943. $result = Yii::app()->db->createCommand($query)->query()->readAll();
  5944. if (count($result) > 0)
  5945. {
  5946. foreach($result as $question)
  5947. {
  5948. array_push($quests,$question['qid']);
  5949. foreach ($langs as $lang)
  5950. {
  5951. $query = "SELECT count(qid) FROM {{questions}} WHERE sid='{$sid}' AND qid='{$question['qid']}' AND language='{$lang}' AND scale_id={$question['scale_id']}";
  5952. $gresult = Yii::app()->db->createCommand($query)->queryScalar();
  5953. if ($gresult < 1)
  5954. {
  5955. switchMSSQLIdentityInsert('questions',true);
  5956. $data = array(
  5957. 'qid' => $question['qid'],
  5958. 'sid' => $question['sid'],
  5959. 'gid' => $question['gid'],
  5960. 'type' => $question['type'],
  5961. 'title' => $question['title'],
  5962. 'question' => $question['question'],
  5963. 'preg' => $question['preg'],
  5964. 'help' => $question['help'],
  5965. 'other' => $question['other'],
  5966. 'mandatory' => $question['mandatory'],
  5967. 'question_order' => $question['question_order'],
  5968. 'language' => $lang,
  5969. 'scale_id' => $question['scale_id'],
  5970. 'parent_qid' => $question['parent_qid'],
  5971. 'relevance' => $question['relevance']
  5972. );
  5973. Yii::app()->db->createCommand()->insert('{{questions}}', $data);
  5974. }
  5975. }
  5976. reset($langs);
  5977. }
  5978. $sqlans = "";
  5979. foreach ($quests as $quest)
  5980. {
  5981. $sqlans .= " OR qid = '".$quest."' ";
  5982. }
  5983. $query = "SELECT * FROM {{answers}} WHERE language='{$baselang}' and (".trim($sqlans,' OR').") ORDER BY qid, code";
  5984. $result = Yii::app()->db->createCommand($query)->query();
  5985. foreach($result->readAll() as $answer)
  5986. {
  5987. foreach ($langs as $lang)
  5988. {
  5989. $query = "SELECT count(qid) FROM {{answers}} WHERE code='{$answer['code']}' AND qid='{$answer['qid']}' AND language='{$lang}' AND scale_id={$answer['scale_id']}";
  5990. $gresult = Yii::app()->db->createCommand($query)->queryScalar();
  5991. if ($gresult < 1)
  5992. {
  5993. $data = array(
  5994. 'qid' => $answer['qid'],
  5995. 'code' => $answer['code'],
  5996. 'answer' => $answer['answer'],
  5997. 'scale_id' => $answer['scale_id'],
  5998. 'sortorder' => $answer['sortorder'],
  5999. 'language' => $lang,
  6000. 'assessment_value' => $answer['assessment_value']
  6001. );
  6002. Yii::app()->db->createCommand()->insert('{{answers}}', $data);
  6003. }
  6004. }
  6005. reset($langs);
  6006. }
  6007. }
  6008. $query = "SELECT * FROM {{assessments}} WHERE sid='{$sid}' AND language='{$baselang}'";
  6009. $result = Yii::app()->db->createCommand($query)->query();
  6010. foreach($result->readAll() as $assessment)
  6011. {
  6012. foreach ($langs as $lang)
  6013. {
  6014. $query = "SELECT count(id) FROM {{assessments}} WHERE sid='{$sid}' AND id='{$assessment['id']}' AND language='{$lang}'";
  6015. $gresult = Yii::app()->db->createCommand($query)->queryScalar();
  6016. if ($gresult < 1)
  6017. {
  6018. $data = array(
  6019. 'id' => $assessment['id'],
  6020. 'sid' => $assessment['sid'],
  6021. 'scope' => $assessment['scope'],
  6022. 'gid' => $assessment['gid'],
  6023. 'name' => $assessment['name'],
  6024. 'minimum' => $assessment['minimum'],
  6025. 'maximum' => $assessment['maximum'],
  6026. 'message' => $assessment['message'],
  6027. 'language' => $lang
  6028. );
  6029. Yii::app()->db->createCommand()->insert('{{assessments}}', $data);
  6030. }
  6031. }
  6032. reset($langs);
  6033. }
  6034. $query = "SELECT * FROM {{quota_languagesettings}} join {{quota}} q on quotals_quota_id=q.id WHERE q.sid='{$sid}' AND quotals_language='{$baselang}'";
  6035. $result = Yii::app()->db->createCommand($query)->query();
  6036. foreach($result->readAll() as $qls)
  6037. {
  6038. foreach ($langs as $lang)
  6039. {
  6040. $query = "SELECT count(quotals_id) FROM {{quota_languagesettings}} WHERE quotals_quota_id='{$qls['quotals_quota_id']}' AND quotals_language='{$lang}'";
  6041. $gresult = Yii::app()->db->createCommand($query)->queryScalar();
  6042. if ($gresult < 1)
  6043. {
  6044. $data = array(
  6045. 'quotals_quota_id' => $qls['quotals_quota_id'],
  6046. 'quotals_name' => $qls['quotals_name'],
  6047. 'quotals_message' => $qls['quotals_message'],
  6048. 'quotals_url' => $qls['quotals_url'],
  6049. 'quotals_urldescrip' => $qls['quotals_urldescrip'],
  6050. 'quotals_language' => $lang
  6051. );
  6052. Yii::app()->db->createCommand()->insert('{{quota_languagesettings}}', $data);
  6053. }
  6054. }
  6055. reset($langs);
  6056. }
  6057. return true;
  6058. }
  6059. /**
  6060. * This function switches identity insert on/off for the MSSQL database
  6061. *
  6062. * @param string $table table name (without prefix)
  6063. * @param mixed $state Set to true to activate ID insert, or false to deactivate
  6064. */
  6065. function switchMSSQLIdentityInsert($table,$state)
  6066. {
  6067. if (in_array(Yii::app()->db->getDriverName(), array('mssql', 'sqlsrv')))
  6068. {
  6069. if ($state == true)
  6070. {
  6071. // This needs to be done directly on the PDO object because when using CdbCommand or similar it won't have any effect
  6072. Yii::app()->db->pdoInstance->exec('SET IDENTITY_INSERT '.Yii::app()->db->tablePrefix.$table.' ON');
  6073. }
  6074. else
  6075. {
  6076. // This needs to be done directly on the PDO object because when using CdbCommand or similar it won't have any effect
  6077. Yii::app()->db->pdoInstance->exec('SET IDENTITY_INSERT '.Yii::app()->db->tablePrefix.$table.' OFF');
  6078. }
  6079. }
  6080. }
  6081. /**
  6082. * Retrieves the last Insert ID realiable for cross-DB applications
  6083. *
  6084. * @param string $sTableName Needed for Postgres and MSSQL
  6085. */
  6086. function getLastInsertID($sTableName)
  6087. {
  6088. $sDBDriver=Yii::app()->db->getDriverName();
  6089. if ($sDBDriver=='mysql' || $sDBDriver=='mysqli')
  6090. {
  6091. return Yii::app()->db->getLastInsertID();
  6092. }
  6093. else
  6094. {
  6095. return Yii::app()->db->getCommandBuilder()->getLastInsertID($sTableName);
  6096. }
  6097. }
  6098. // TMSW Conditions->Relevance: This function is not needed? Optionally replace this with call to EM to get similar info
  6099. /**
  6100. * getGroupDepsForConditions() get Dependencies between groups caused by conditions
  6101. * @param string $sid - the currently selected survey
  6102. * @param string $depgid - (optionnal) get only the dependencies applying to the group with gid depgid
  6103. * @param string $targgid - (optionnal) get only the dependencies for groups dependents on group targgid
  6104. * @param string $index-by - (optionnal) "by-depgid" for result indexed with $res[$depgid][$targgid]
  6105. * "by-targgid" for result indexed with $res[$targgid][$depgid]
  6106. * @return array - returns an array describing the conditions or NULL if no dependecy is found
  6107. *
  6108. * Example outupt assumin $index-by="by-depgid":
  6109. *Array
  6110. *(
  6111. * [125] => Array // Group Id 125 is dependent on
  6112. * (
  6113. * [123] => Array // Group Id 123
  6114. * (
  6115. * [depgpname] => G3 // GID-125 has name G3
  6116. * [targetgpname] => G1 // GID-123 has name G1
  6117. * [conditions] => Array
  6118. * (
  6119. * [189] => Array // Because Question Id 189
  6120. * (
  6121. * [0] => 9 // Have condition 9 set
  6122. * [1] => 10 // and condition 10 set
  6123. * [2] => 14 // and condition 14 set
  6124. * )
  6125. *
  6126. * )
  6127. *
  6128. * )
  6129. *
  6130. * [124] => Array // GID 125 is also dependent on GID 124
  6131. * (
  6132. * [depgpname] => G3
  6133. * [targetgpname] => G2
  6134. * [conditions] => Array
  6135. * (
  6136. * [189] => Array // Because Question Id 189 have conditions set
  6137. * (
  6138. * [0] => 11
  6139. * )
  6140. *
  6141. * [215] => Array // And because Question Id 215 have conditions set
  6142. * (
  6143. * [0] => 12
  6144. * )
  6145. *
  6146. * )
  6147. *
  6148. * )
  6149. *
  6150. * )
  6151. *
  6152. *)
  6153. *
  6154. * Usage example:
  6155. * * Get all group dependencies for SID $sid indexed by depgid:
  6156. * $result=getGroupDepsForConditions($sid);
  6157. * * Get all group dependencies for GID $gid in survey $sid indexed by depgid:
  6158. * $result=getGroupDepsForConditions($sid,$gid);
  6159. * * Get all group dependents on group $gid in survey $sid indexed by targgid:
  6160. * $result=getGroupDepsForConditions($sid,"all",$gid,"by-targgid");
  6161. */
  6162. function getGroupDepsForConditions($sid,$depgid="all",$targgid="all",$indexby="by-depgid")
  6163. {
  6164. $sid=sanitize_int($sid);
  6165. $condarray = Array();
  6166. $sqldepgid="";
  6167. $sqltarggid="";
  6168. if ($depgid != "all") { $depgid = sanitize_int($depgid); $sqldepgid="AND tq.gid=$depgid";}
  6169. if ($targgid != "all") {$targgid = sanitize_int($targgid); $sqltarggid="AND tq2.gid=$targgid";}
  6170. $baselang = Survey::model()->findByPk($sid)->language;
  6171. $condquery = "SELECT tg.gid as depgid, tg.group_name as depgpname, "
  6172. . "tg2.gid as targgid, tg2.group_name as targgpname, tq.qid as depqid, tc.cid FROM "
  6173. . "{{conditions}} AS tc, "
  6174. . "{{questions}} AS tq, "
  6175. . "{{questions}} AS tq2, "
  6176. . "{{groups}} AS tg ,"
  6177. . "{{groups}} AS tg2 "
  6178. . "WHERE tq.language='{$baselang}' AND tq2.language='{$baselang}' AND tg.language='{$baselang}' AND tg2.language='{$baselang}' AND tc.qid = tq.qid AND tq.sid=$sid "
  6179. . "AND tq.gid = tg.gid AND tg2.gid = tq2.gid "
  6180. . "AND tq2.qid=tc.cqid AND tq.gid != tg2.gid $sqldepgid $sqltarggid";
  6181. $condresult = Yii::app()->db->createCommand($condquery)->query()->readAll();
  6182. if (count($condresult) > 0) {
  6183. foreach ($condresult as $condrow)
  6184. {
  6185. switch ($indexby)
  6186. {
  6187. case "by-depgid":
  6188. $depgid=$condrow['depgid'];
  6189. $targetgid=$condrow['targgid'];
  6190. $depqid=$condrow['depqid'];
  6191. $cid=$condrow['cid'];
  6192. $condarray[$depgid][$targetgid]['depgpname'] = $condrow['depgpname'];
  6193. $condarray[$depgid][$targetgid]['targetgpname'] = $condrow['targgpname'];
  6194. $condarray[$depgid][$targetgid]['conditions'][$depqid][]=$cid;
  6195. break;
  6196. case "by-targgid":
  6197. $depgid=$condrow['depgid'];
  6198. $targetgid=$condrow['targgid'];
  6199. $depqid=$condrow['depqid'];
  6200. $cid=$condrow['cid'];
  6201. $condarray[$targetgid][$depgid]['depgpname'] = $condrow['depgpname'];
  6202. $condarray[$targetgid][$depgid]['targetgpname'] = $condrow['targgpname'];
  6203. $condarray[$targetgid][$depgid]['conditions'][$depqid][] = $cid;
  6204. break;
  6205. }
  6206. }
  6207. return $condarray;
  6208. }
  6209. return null;
  6210. }
  6211. // TMSW Conditions->Relevance: This function is not needed? Optionally replace this with call to EM to get similar info
  6212. /**
  6213. * getQuestDepsForConditions() get Dependencies between groups caused by conditions
  6214. * @param string $sid - the currently selected survey
  6215. * @param string $gid - (optionnal) only search dependecies inside the Group Id $gid
  6216. * @param string $depqid - (optionnal) get only the dependencies applying to the question with qid depqid
  6217. * @param string $targqid - (optionnal) get only the dependencies for questions dependents on question Id targqid
  6218. * @param string $index-by - (optionnal) "by-depqid" for result indexed with $res[$depqid][$targqid]
  6219. * "by-targqid" for result indexed with $res[$targqid][$depqid]
  6220. * @return array - returns an array describing the conditions or NULL if no dependecy is found
  6221. *
  6222. * Example outupt assumin $index-by="by-depqid":
  6223. *Array
  6224. *(
  6225. * [184] => Array // Question Id 184
  6226. * (
  6227. * [183] => Array // Depends on Question Id 183
  6228. * (
  6229. * [0] => 5 // Because of condition Id 5
  6230. * )
  6231. *
  6232. * )
  6233. *
  6234. *)
  6235. *
  6236. * Usage example:
  6237. * * Get all questions dependencies for Survey $sid and group $gid indexed by depqid:
  6238. * $result=getQuestDepsForConditions($sid,$gid);
  6239. * * Get all questions dependencies for question $qid in survey/group $sid/$gid indexed by depqid:
  6240. * $result=getGroupDepsForConditions($sid,$gid,$qid);
  6241. * * Get all questions dependents on question $qid in survey/group $sid/$gid indexed by targqid:
  6242. * $result=getGroupDepsForConditions($sid,$gid,"all",$qid,"by-targgid");
  6243. */
  6244. function getQuestDepsForConditions($sid,$gid="all",$depqid="all",$targqid="all",$indexby="by-depqid", $searchscope="samegroup")
  6245. {
  6246. $clang = Yii::app()->lang;
  6247. $condarray = Array();
  6248. $baselang = Survey::model()->findByPk($sid)->language;
  6249. $sqlgid="";
  6250. $sqldepqid="";
  6251. $sqltargqid="";
  6252. $sqlsearchscope="";
  6253. if ($gid != "all") {$gid = sanitize_int($gid); $sqlgid="AND tq.gid=$gid";}
  6254. if ($depqid != "all") {$depqid = sanitize_int($depqid); $sqldepqid="AND tq.qid=$depqid";}
  6255. if ($targqid != "all") {$targqid = sanitize_int($targqid); $sqltargqid="AND tq2.qid=$targqid";}
  6256. if ($searchscope == "samegroup") {$sqlsearchscope="AND tq2.gid=tq.gid";}
  6257. $condquery = "SELECT tq.qid as depqid, tq2.qid as targqid, tc.cid
  6258. FROM {{conditions}} AS tc, {{questions}} AS tq, {{questions}} AS tq2
  6259. WHERE tq.language='{$baselang}' AND tq2.language='{$baselang}' AND tc.qid = tq.qid AND tq.sid='$sid'
  6260. AND tq2.qid=tc.cqid $sqlsearchscope $sqlgid $sqldepqid $sqltargqid";
  6261. $condresult=Yii::app()->db->createCommand($condquery)->query()->readAll();
  6262. if (count($condresult) > 0) {
  6263. foreach ($condresult as $condrow)
  6264. {
  6265. $depqid=$condrow['depqid'];
  6266. $targetqid=$condrow['targqid'];
  6267. $condid=$condrow['cid'];
  6268. switch ($indexby)
  6269. {
  6270. case "by-depqid":
  6271. $condarray[$depqid][$targetqid][] = $condid;
  6272. break;
  6273. case "by-targqid":
  6274. $condarray[$targetqid][$depqid][] = $condid;
  6275. break;
  6276. }
  6277. }
  6278. return $condarray;
  6279. }
  6280. return null;
  6281. }
  6282. // TMSW Conditions->Relevance: This function is not needed - could replace with a message from EM output.
  6283. /**
  6284. * checkMoveQuestionConstraintsForConditions()
  6285. * @param string $sid - the currently selected survey
  6286. * @param string $qid - qid of the question you want to check possible moves
  6287. * @param string $newgid - (optionnal) get only constraints when trying to move to this particular GroupId
  6288. * otherwise, get all moves constraints for this question
  6289. *
  6290. * @return array - returns an array describing the conditions
  6291. * Array
  6292. * (
  6293. * ['notAbove'] = null | Array
  6294. * (
  6295. * Array ( gid1, group_order1, qid1, cid1 )
  6296. * )
  6297. * ['notBelow'] = null | Array
  6298. * (
  6299. * Array ( gid2, group_order2, qid2, cid2 )
  6300. * )
  6301. * )
  6302. *
  6303. * This should be read as:
  6304. * - this question can't be move above group gid1 in position group_order1 because of the condition cid1 on question qid1
  6305. * - this question can't be move below group gid2 in position group_order2 because of the condition cid2 on question qid2
  6306. *
  6307. */
  6308. function checkMoveQuestionConstraintsForConditions($sid,$qid,$newgid="all")
  6309. {
  6310. $clang = Yii::app()->lang;
  6311. $resarray=Array();
  6312. $resarray['notAbove']=null; // defaults to no constraint
  6313. $resarray['notBelow']=null; // defaults to no constraint
  6314. $sid=sanitize_int($sid);
  6315. $qid=sanitize_int($qid);
  6316. if ($newgid != "all")
  6317. {
  6318. $newgid=sanitize_int($newgid);
  6319. $newgorder=getGroupOrder($sid,$newgid);
  6320. }
  6321. else
  6322. {
  6323. $neworder=""; // Not used in this case
  6324. }
  6325. $baselang = Survey::model()->findByPk($sid)->language;
  6326. // First look for 'my dependencies': questions on which I have set conditions
  6327. $condquery = "SELECT tq.qid as depqid, tq.gid as depgid, tg.group_order as depgorder, "
  6328. . "tq2.qid as targqid, tq2.gid as targgid, tg2.group_order as targgorder, "
  6329. . "tc.cid FROM "
  6330. . "{{conditions}} AS tc, "
  6331. . "{{questions}} AS tq, "
  6332. . "{{questions}} AS tq2, "
  6333. . "{{groups}} AS tg, "
  6334. . "{{groups}} AS tg2 "
  6335. . "WHERE tq.language='{$baselang}' AND tq2.language='{$baselang}' AND tc.qid = tq.qid AND tq.sid=$sid "
  6336. . "AND tq2.qid=tc.cqid AND tg.gid=tq.gid AND tg2.gid=tq2.gid AND tq.qid=$qid ORDER BY tg2.group_order DESC";
  6337. $condresult=Yii::app()->db->createCommand($condquery)->query();
  6338. foreach ($condresult->readAll() as $condrow )
  6339. {
  6340. // This Question can go up to the minimum GID on the 1st row
  6341. $depqid=$condrow['depqid'];
  6342. $depgid=$condrow['depgid'];
  6343. $depgorder=$condrow['depgorder'];
  6344. $targetqid=$condrow['targqid'];
  6345. $targetgid=$condrow['targgid'];
  6346. $targetgorder=$condrow['targgorder'];
  6347. $condid=$condrow['cid'];
  6348. //echo "This question can't go above to GID=$targetgid/order=$targetgorder because of CID=$condid";
  6349. if ($newgid != "all")
  6350. { // Get only constraints when trying to move to this group
  6351. if ($newgorder < $targetgorder)
  6352. {
  6353. $resarray['notAbove'][]=Array($targetgid,$targetgorder,$depqid,$condid);
  6354. }
  6355. }
  6356. else
  6357. { // get all moves constraints
  6358. $resarray['notAbove'][]=Array($targetgid,$targetgorder,$depqid,$condid);
  6359. }
  6360. }
  6361. // Secondly look for 'questions dependent on me': questions that have conditions on my answers
  6362. $condquery = "SELECT tq.qid as depqid, tq.gid as depgid, tg.group_order as depgorder, "
  6363. . "tq2.qid as targqid, tq2.gid as targgid, tg2.group_order as targgorder, "
  6364. . "tc.cid FROM {{conditions}} AS tc, "
  6365. . "{{questions}} AS tq, "
  6366. . "{{questions}} AS tq2, "
  6367. . "{{groups}} AS tg, "
  6368. . "{{groups}} AS tg2 "
  6369. . "WHERE tq.language='{$baselang}' AND tq2.language='{$baselang}' AND tc.qid = tq.qid AND tq.sid=$sid "
  6370. . "AND tq2.qid=tc.cqid AND tg.gid=tq.gid AND tg2.gid=tq2.gid AND tq2.qid=$qid ORDER BY tg.group_order";
  6371. $condresult=Yii::app()->db->createCommand($condquery)->query();
  6372. foreach ($condresult->readAll() as $condrow)
  6373. {
  6374. // This Question can go down to the maximum GID on the 1st row
  6375. $depqid=$condrow['depqid'];
  6376. $depgid=$condrow['depgid'];
  6377. $depgorder=$condrow['depgorder'];
  6378. $targetqid=$condrow['targqid'];
  6379. $targetgid=$condrow['targgid'];
  6380. $targetgorder=$condrow['targgorder'];
  6381. $condid=$condrow['cid'];
  6382. //echo "This question can't go below to GID=$depgid/order=$depgorder because of CID=$condid";
  6383. if ($newgid != "all")
  6384. { // Get only constraints when trying to move to this group
  6385. if ($newgorder > $depgorder)
  6386. {
  6387. $resarray['notBelow'][]=Array($depgid,$depgorder,$depqid,$condid);
  6388. }
  6389. }
  6390. else
  6391. { // get all moves constraints
  6392. $resarray['notBelow'][]=Array($depgid,$depgorder,$depqid,$condid);
  6393. }
  6394. }
  6395. return $resarray;
  6396. }
  6397. function getUserGroupList($ugid=NULL,$outputformat='optionlist')
  6398. {
  6399. $clang = Yii::app()->lang;
  6400. //$squery = "SELECT ugid, name FROM ".db_table_name('user_groups') ." WHERE owner_id = {Yii::app()->session['loginID']} ORDER BY name";
  6401. $sQuery = "SELECT distinct a.ugid, a.name, a.owner_id FROM {{user_groups}} AS a LEFT JOIN {{user_in_groups}} AS b ON a.ugid = b.ugid WHERE 1=1 ";
  6402. if (!hasGlobalPermission('USER_RIGHT_SUPERADMIN'))
  6403. {
  6404. $sQuery .="AND uid = ".Yii::app()->session['loginID'];
  6405. }
  6406. $sQuery .= " ORDER BY name";
  6407. $sresult = Yii::app()->db->createCommand($sQuery)->query(); //Checked
  6408. if (!$sresult) {return "Database Error";}
  6409. $selecter = "";
  6410. foreach ($sresult->readAll() as $row)
  6411. {
  6412. $groupnames[] = $row;
  6413. }
  6414. //$groupnames = $sresult->GetRows();
  6415. $simplegidarray=array();
  6416. if (isset($groupnames))
  6417. {
  6418. foreach($groupnames as $gn)
  6419. {
  6420. $selecter .= "<option ";
  6421. if(Yii::app()->session['loginID'] == $gn['owner_id']) {$selecter .= " style=\"font-weight: bold;\"";}
  6422. //if (isset($_GET['ugid']) && $gn['ugid'] == $_GET['ugid']) {$selecter .= " selected='selected'"; $svexist = 1;}
  6423. if ($gn['ugid'] == $ugid) {$selecter .= " selected='selected'"; $svexist = 1;}
  6424. $link = Yii::app()->getController()->createUrl("/admin/usergroups/sa/view/ugid/".$gn['ugid']);
  6425. $selecter .=" value='{$link}'>{$gn['name']}</option>\n";
  6426. $simplegidarray[] = $gn['ugid'];
  6427. }
  6428. }
  6429. if (!isset($svexist)) {$selecter = "<option value='-1' selected='selected'>".$clang->gT("Please choose...")."</option>\n".$selecter;}
  6430. //else {$selecter = "<option value='-1'>".$clang->gT("None")."</option>\n".$selecter;}
  6431. if ($outputformat == 'simplegidarray')
  6432. {
  6433. return $simplegidarray;
  6434. }
  6435. else
  6436. {
  6437. return $selecter;
  6438. }
  6439. }
  6440. function getGroupUserList($ugid)
  6441. {
  6442. Yii::app()->loadHelper('database');
  6443. $clang = Yii::app()->lang;
  6444. $ugid=sanitize_int($ugid);
  6445. $surveyidquery = "SELECT a.uid, a.users_name FROM {{users}} AS a LEFT JOIN (SELECT uid AS id FROM {{user_in_groups}} WHERE ugid = {$ugid}) AS b ON a.uid = b.id WHERE id IS NULL ORDER BY a.users_name";
  6446. $surveyidresult = dbExecuteAssoc($surveyidquery); //Checked
  6447. if (!$surveyidresult) {return "Database Error";}
  6448. $surveyselecter = "";
  6449. foreach ($surveyidresult->readAll() as $row)
  6450. {
  6451. $surveynames[] = $row;
  6452. }
  6453. //$surveynames = $surveyidresult->GetRows();
  6454. if (isset($surveynames))
  6455. {
  6456. foreach($surveynames as $sv)
  6457. {
  6458. $surveyselecter .= "<option";
  6459. $surveyselecter .=" value='{$sv['uid']}'>{$sv['users_name']}</option>\n";
  6460. }
  6461. }
  6462. $surveyselecter = "<option value='-1' selected='selected'>".$clang->gT("Please choose...")."</option>\n".$surveyselecter;
  6463. return $surveyselecter;
  6464. }
  6465. /**
  6466. * Run an arbitrary sequence of semicolon-delimited SQL commands
  6467. *
  6468. * Assumes that the input text (file or string) consists of
  6469. * a number of SQL statements ENDING WITH SEMICOLONS. The
  6470. * semicolons MUST be the last character in a line.
  6471. * Lines that are blank or that start with "#" or "--" (postgres) are ignored.
  6472. * Only tested with mysql dump files (mysqldump -p -d limesurvey)
  6473. * Function kindly borrowed by Moodle
  6474. * @param string $sqlfile The path where a file with sql commands can be found on the server.
  6475. * @param string $sqlstring If no path is supplied then a string with semicolon delimited sql
  6476. * commands can be supplied in this argument.
  6477. * @return bool Returns true if database was modified successfully.
  6478. */
  6479. function modifyDatabase($sqlfile='', $sqlstring='')
  6480. {
  6481. Yii::app()->loadHelper('database');
  6482. $clang = Yii::app()->lang;
  6483. global $siteadminemail;
  6484. global $siteadminname;
  6485. global $codeString;
  6486. global $modifyoutput;
  6487. $success = true; // Let's be optimistic
  6488. $modifyoutput='';
  6489. if (!empty($sqlfile)) {
  6490. if (!is_readable($sqlfile)) {
  6491. $success = false;
  6492. echo '<p>Tried to modify database, but "'. $sqlfile .'" doesn\'t exist!</p>';
  6493. return $success;
  6494. } else {
  6495. $lines = file($sqlfile);
  6496. }
  6497. } else {
  6498. $sqlstring = trim($sqlstring);
  6499. if ($sqlstring{strlen($sqlstring)-1} != ";") {
  6500. $sqlstring .= ";"; // add it in if it's not there.
  6501. }
  6502. $lines[] = $sqlstring;
  6503. }
  6504. $command = '';
  6505. foreach ($lines as $line) {
  6506. $line = rtrim($line);
  6507. $length = strlen($line);
  6508. if ($length and $line[0] <> '#' and substr($line,0,2) <> '--') {
  6509. if (substr($line, $length-1, 1) == ';') {
  6510. $line = substr($line, 0, $length-1); // strip ;
  6511. $command .= $line;
  6512. $command = str_replace('prefix_', Yii::app()->db->tablePrefix, $command); // Table prefixes
  6513. $command = str_replace('$defaultuser', Yii::app()->getConfig('defaultuser'), $command);
  6514. $command = str_replace('$defaultpass', hash('sha256',Yii::app()->getConfig('defaultpass')), $command);
  6515. $command = str_replace('$siteadminname', $siteadminname, $command);
  6516. $command = str_replace('$siteadminemail', $siteadminemail, $command);
  6517. $command = str_replace('$defaultlang', Yii::app()->getConfig('defaultlang'), $command);
  6518. $command = str_replace('$databasetabletype', Yii::app()->db->getDriverName(), $command);
  6519. try
  6520. { Yii::app()->db->createCommand($command)->query(); //Checked
  6521. $command=htmlspecialchars($command);
  6522. $modifyoutput .=". ";
  6523. }
  6524. catch(CDbException $e)
  6525. {
  6526. $command=htmlspecialchars($command);
  6527. $modifyoutput .="<br />".sprintf($clang->gT("SQL command failed: %s"),"<span style='font-size:10px;'>".$command."</span>","<span style='color:#ee0000;font-size:10px;'></span><br/>");
  6528. $success = false;
  6529. }
  6530. $command = '';
  6531. } else {
  6532. $command .= $line;
  6533. }
  6534. }
  6535. }
  6536. return $success;
  6537. }
  6538. /**
  6539. * Returns labelsets for given language(s), or for all if null
  6540. *
  6541. * @param string $languages
  6542. * @return array
  6543. */
  6544. function getLabelSets($languages = null)
  6545. {
  6546. $clang = Yii::app()->lang;
  6547. $languagesarray = array();
  6548. if ($languages)
  6549. {
  6550. $languages=sanitize_languagecodeS($languages);
  6551. $languagesarray=explode(' ',trim($languages));
  6552. }
  6553. $criteria = new CDbCriteria;
  6554. foreach ($languagesarray as $k => $item)
  6555. {
  6556. $criteria->params[':lang_like1_' . $k] = "% $item %";
  6557. $criteria->params[':lang_' . $k] = $item;
  6558. $criteria->params[':lang_like2_' . $k] = "% $item";
  6559. $criteria->params[':lang_like3_' . $k] = "$item %";
  6560. $criteria->addCondition("
  6561. ((languages like :lang_like1_$k) or
  6562. (languages = :lang_$k) or
  6563. (languages like :lang_like2_$k) or
  6564. (languages like :lang_like3_$k))");
  6565. }
  6566. $result = Labelsets::model()->findAll($criteria);
  6567. $labelsets=array();
  6568. foreach ($result as $row)
  6569. $labelsets[] = array($row->lid, $row->label_name);
  6570. return $labelsets;
  6571. }
  6572. function getHeader($meta = false)
  6573. {
  6574. global $embedded,$surveyid ;
  6575. Yii::app()->loadHelper('surveytranslator');
  6576. // Set Langage // TODO remove one of the Yii::app()->session see bug #5901
  6577. if (Yii::app()->session['s_lang'] )
  6578. {
  6579. $languagecode = Yii::app()->session['s_lang'];
  6580. }
  6581. elseif (Yii::app()->session['survey_'.$surveyid]['s_lang'] )
  6582. {
  6583. $languagecode = Yii::app()->session['survey_'.$surveyid]['s_lang'];
  6584. }
  6585. elseif (isset($surveyid) && $surveyid && Survey::model()->findByPk($surveyid))
  6586. {
  6587. $languagecode=Survey::model()->findByPk($surveyid)->language;
  6588. }
  6589. else
  6590. {
  6591. $languagecode = Yii::app()->getConfig('defaultlang');
  6592. }
  6593. $header= "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
  6594. . "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"{$languagecode}\" lang=\"{$languagecode}\"";
  6595. if (getLanguageRTL($languagecode))
  6596. {
  6597. $header.=" dir=\"rtl\" ";
  6598. }
  6599. $header.= ">\n\t<head>\n";
  6600. if ($meta)
  6601. $header .= $meta;
  6602. if ( !$embedded )
  6603. {
  6604. return $header;
  6605. }
  6606. global $embedded_headerfunc;
  6607. if ( function_exists( $embedded_headerfunc ) )
  6608. return $embedded_headerfunc($header);
  6609. }
  6610. function doHeader()
  6611. {
  6612. echo getHeader();
  6613. }
  6614. /**
  6615. * This function returns the header for the printable survey
  6616. * @return String
  6617. *
  6618. */
  6619. function getPrintableHeader()
  6620. {
  6621. global $rooturl,$homeurl;
  6622. $headelements = '
  6623. <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
  6624. <script type="text/javascript" src="'.Yii::app()->getConfig('generalscripts').'jquery/jquery.js"></script>
  6625. <script type="text/javascript" src="'.Yii::app()->getConfig('adminscripts').'printablesurvey.js"></script>
  6626. ';
  6627. return $headelements;
  6628. }
  6629. // This function returns the Footer as result string
  6630. // If you want to echo the Footer use doFooter() !
  6631. function getFooter()
  6632. {
  6633. global $embedded;
  6634. if ( !$embedded )
  6635. {
  6636. return "\n\n\t</body>\n</html>\n";
  6637. }
  6638. global $embedded_footerfunc;
  6639. if ( function_exists( $embedded_footerfunc ) )
  6640. return $embedded_footerfunc();
  6641. }
  6642. function doFooter()
  6643. {
  6644. echo getFooter();
  6645. }
  6646. function getDBTableUsage($surveyid){
  6647. Yii::app()->loadHelper('admin/activate');
  6648. $arrCols = activateSurvey($surveyid,$surveyid,'admin.php',true);
  6649. $length = 1;
  6650. foreach ($arrCols['fields'] as $col){
  6651. switch ($col[0]){
  6652. case 'C':
  6653. $length = $length + ($col[1]*3) + 1;
  6654. break;
  6655. case 'X':
  6656. case 'B':
  6657. $length = $length + 12;
  6658. break;
  6659. case 'D':
  6660. $length = $length + 3;
  6661. break;
  6662. case 'T':
  6663. case 'TS':
  6664. case 'N':
  6665. $length = $length + 8;
  6666. break;
  6667. case 'L':
  6668. $legth++;
  6669. break;
  6670. case 'I':
  6671. case 'I4':
  6672. case 'F':
  6673. $length = $length + 4;
  6674. break;
  6675. case 'I1':
  6676. $length = $length + 1;
  6677. break;
  6678. case 'I2':
  6679. $length = $length + 2;
  6680. break;
  6681. case 'I8':
  6682. $length = $length + 8;
  6683. break;
  6684. }
  6685. }
  6686. if ($arrCols['dbtype'] == 'mysql' || $arrCols['dbtype'] == 'mysqli'){
  6687. if ($arrCols['dbengine']=='myISAM'){
  6688. $hard_limit = 4096;
  6689. }
  6690. elseif ($arrCols['dbengine'] == "InnoDB"){
  6691. $hard_limit = 1000;
  6692. }
  6693. else{
  6694. return false;
  6695. }
  6696. $size_limit = 65535;
  6697. }
  6698. elseif ($arrCols['dbtype'] == 'postgre'){
  6699. $hard_limit = 1600;
  6700. $size_limit = 0;
  6701. }
  6702. elseif ($arrCols['dbtype'] == 'mssql'){
  6703. $hard_limit = 1024;
  6704. $size_limit = 0;
  6705. }
  6706. else{
  6707. return false;
  6708. }
  6709. $columns_used = count($arrCols['fields']);
  6710. return (array( 'dbtype'=>$arrCols['dbtype'], 'column'=>array($columns_used,$hard_limit) , 'size' => array($length, $size_limit) ));
  6711. }
  6712. /**
  6713. * Checks that each object from an array of CSV data [question-rows,answer-rows,labelsets-row] supports at least a given language
  6714. *
  6715. * @param mixed $csvarray array with a line of csv data per row
  6716. * @param mixed $idkeysarray array of integers giving the csv-row numbers of the object keys
  6717. * @param mixed $langfieldnum integer giving the csv-row number of the language(s) filed
  6718. * ==> the language field can be a single language code or a
  6719. * space separated language code list
  6720. * @param mixed $langcode the language code to be tested
  6721. * @param mixed $hasheader if we should strip off the first line (if it contains headers)
  6722. */
  6723. function doesImportArraySupportLanguage($csvarray,$idkeysarray,$langfieldnum,$langcode, $hasheader = false)
  6724. {
  6725. // An array with one row per object id and langsupport status as value
  6726. $objlangsupportarray=Array();
  6727. if ($hasheader === true )
  6728. { // stripping first row to skip headers if any
  6729. array_shift($csvarray);
  6730. }
  6731. foreach ($csvarray as $csvrow)
  6732. {
  6733. $rowcontents = convertCSVRowToArray($csvrow,',','"');
  6734. $rowid = "";
  6735. foreach ($idkeysarray as $idfieldnum)
  6736. {
  6737. $rowid .= $rowcontents[$idfieldnum]."-";
  6738. }
  6739. $rowlangarray = explode (" ", @$rowcontents[$langfieldnum]);
  6740. if (!isset($objlangsupportarray[$rowid]))
  6741. {
  6742. if (array_search($langcode,$rowlangarray)!== false)
  6743. {
  6744. $objlangsupportarray[$rowid] = "true";
  6745. }
  6746. else
  6747. {
  6748. $objlangsupportarray[$rowid] = "false";
  6749. }
  6750. }
  6751. else
  6752. {
  6753. if ($objlangsupportarray[$rowid] == "false" &&
  6754. array_search($langcode,$rowlangarray) !== false)
  6755. {
  6756. $objlangsupportarray[$rowid] = "true";
  6757. }
  6758. }
  6759. } // end foreach rown
  6760. // If any of the object doesn't support the given language, return false
  6761. if (array_search("false",$objlangsupportarray) === false)
  6762. {
  6763. return true;
  6764. }
  6765. else
  6766. {
  6767. return false;
  6768. }
  6769. }
  6770. /**
  6771. * Retrieve a HTML <OPTION> list of survey admin users
  6772. *
  6773. * @param mixed $bIncludeOwner If the survey owner should be included
  6774. * @param mixed $bIncludeSuperAdmins If Super admins should be included
  6775. * @param int surveyid
  6776. * @return string
  6777. */
  6778. function getSurveyUserList($bIncludeOwner=true, $bIncludeSuperAdmins=true,$surveyid)
  6779. {
  6780. $clang = Yii::app()->lang;
  6781. $surveyid=sanitize_int($surveyid);
  6782. $sSurveyIDQuery = "SELECT a.uid, a.users_name, a.full_name FROM {{users}} AS a
  6783. LEFT OUTER JOIN (SELECT uid AS id FROM {{survey_permissions}} WHERE sid = {$surveyid}) AS b ON a.uid = b.id
  6784. WHERE id IS NULL ";
  6785. if (!$bIncludeSuperAdmins)
  6786. {
  6787. $sSurveyIDQuery.='and superadmin=0 ';
  6788. }
  6789. $sSurveyIDQuery.= 'ORDER BY a.users_name';
  6790. $oSurveyIDResult = Yii::app()->db->createCommand($sSurveyIDQuery)->query(); //Checked
  6791. $aSurveyIDResult = $oSurveyIDResult->readAll();
  6792. $surveyselecter = "";
  6793. if (Yii::app()->getConfig('usercontrolSameGroupPolicy') == true)
  6794. {
  6795. $authorizedUsersList = getUserList('onlyuidarray');
  6796. }
  6797. foreach($aSurveyIDResult as $sv)
  6798. {
  6799. if (Yii::app()->getConfig('usercontrolSameGroupPolicy') == false ||
  6800. in_array($sv['uid'],$authorizedUsersList))
  6801. {
  6802. $surveyselecter .= "<option";
  6803. $surveyselecter .=" value='{$sv['uid']}'>{$sv['users_name']} {$sv['full_name']}</option>\n";
  6804. }
  6805. }
  6806. if (!isset($svexist)) {$surveyselecter = "<option value='-1' selected='selected'>".$clang->gT("Please choose...")."</option>\n".$surveyselecter;}
  6807. else {$surveyselecter = "<option value='-1'>".$clang->gT("None")."</option>\n".$surveyselecter;}
  6808. return $surveyselecter;
  6809. }
  6810. function getSurveyUserGroupList($outputformat='htmloptions',$surveyid)
  6811. {
  6812. $clang = Yii::app()->lang;
  6813. $surveyid=sanitize_int($surveyid);
  6814. $surveyidquery = "SELECT a.ugid, a.name, MAX(d.ugid) AS da
  6815. FROM {{user_groups}} AS a
  6816. LEFT JOIN (
  6817. SELECT b.ugid
  6818. FROM {{user_in_groups}} AS b
  6819. LEFT JOIN (SELECT * FROM {{survey_permissions}}
  6820. WHERE sid = {$surveyid}) AS c ON b.uid = c.uid WHERE c.uid IS NULL
  6821. ) AS d ON a.ugid = d.ugid GROUP BY a.ugid, a.name HAVING MAX(d.ugid) IS NOT NULL";
  6822. $surveyidresult = Yii::app()->db->createCommand($surveyidquery)->query(); //Checked
  6823. $aResult=$surveyidresult->readAll();
  6824. $surveyselecter = "";
  6825. if (Yii::app()->getConfig('usercontrolSameGroupPolicy') == true)
  6826. {
  6827. $authorizedGroupsList=getUserGroupList(NULL, 'simplegidarray');
  6828. }
  6829. foreach($aResult as $sv)
  6830. {
  6831. if (Yii::app()->getConfig('usercontrolSameGroupPolicy') == false ||
  6832. in_array($sv['ugid'],$authorizedGroupsList))
  6833. {
  6834. $surveyselecter .= "<option";
  6835. $surveyselecter .=" value='{$sv['ugid']}'>{$sv['name']}</option>\n";
  6836. $simpleugidarray[] = $sv['ugid'];
  6837. }
  6838. }
  6839. if (!isset($svexist)) {$surveyselecter = "<option value='-1' selected='selected'>".$clang->gT("Please choose...")."</option>\n".$surveyselecter;}
  6840. else {$surveyselecter = "<option value='-1'>".$clang->gT("None")."</option>\n".$surveyselecter;}
  6841. if ($outputformat == 'simpleugidarray')
  6842. {
  6843. return $simpleugidarray;
  6844. }
  6845. else
  6846. {
  6847. return $surveyselecter;
  6848. }
  6849. }
  6850. /*
  6851. * Emit the standard (last) onsubmit handler for the survey.
  6852. *
  6853. * This code in injected in the three questionnaire modes right after the <form> element,
  6854. * before the individual questions emit their own onsubmit replacement code.
  6855. */
  6856. function sDefaultSubmitHandler()
  6857. {
  6858. return <<<EOS
  6859. <script type='text/javascript'>
  6860. <!--
  6861. // register the standard (last) onsubmit handler *first*
  6862. document.limesurvey.onsubmit = std_onsubmit_handler;
  6863. -->
  6864. </script>
  6865. EOS;
  6866. }
  6867. /**
  6868. * This function fixes the group ID and type on all subquestions
  6869. *
  6870. */
  6871. function fixSubquestions()
  6872. {
  6873. $surveyidresult=Yii::app()->db->createCommand("select sq.qid, sq.parent_qid, sq.gid as sqgid, q.gid, sq.type as sqtype, q.type
  6874. from {{questions}} sq JOIN {{questions}} q on sq.parent_qid=q.qid
  6875. where sq.parent_qid>0 and (sq.gid!=q.gid or sq.type!=q.type)")->query();
  6876. foreach($surveyidresult->readAll() as $sv)
  6877. {
  6878. Yii::app()->db->createCommand("update {{questions}} set type='{$sv['type']}', gid={$sv['gid']} where qid={$sv['qid']}")->query();
  6879. }
  6880. }
  6881. /**
  6882. * Must use ls_json_encode to json_encode content, otherwise LimeExpressionManager will think that the associative arrays are expressions and try to parse them.
  6883. */
  6884. function ls_json_encode($content)
  6885. {
  6886. $ans = json_encode($content);
  6887. $ans = str_replace(array('{','}'),array('{ ',' }'), $ans);
  6888. return $ans;
  6889. }
  6890. /**
  6891. * Decode a json string, sometimes needs stripslashes
  6892. *
  6893. * @param type $jsonString
  6894. * @return type
  6895. */
  6896. function json_decode_ls($jsonString)
  6897. {
  6898. $decoded = json_decode($jsonString, true);
  6899. if (is_null($decoded) && !empty($jsonString))
  6900. {
  6901. // probably we need stipslahes
  6902. $decoded = json_decode(stripslashes($jsonString), true);
  6903. }
  6904. return $decoded;
  6905. }
  6906. /**
  6907. * Swaps two positions in an array
  6908. *
  6909. * @param mixed $key1
  6910. * @param mixed $key2
  6911. * @param mixed $array
  6912. */
  6913. function arraySwapAssoc($key1, $key2, $array) {
  6914. $newArray = array ();
  6915. foreach ($array as $key => $value) {
  6916. if ($key == $key1) {
  6917. $newArray[$key2] = $array[$key2];
  6918. } elseif ($key == $key2) {
  6919. $newArray[$key1] = $array[$key1];
  6920. } else {
  6921. $newArray[$key] = $value;
  6922. }
  6923. }
  6924. return $newArray;
  6925. }
  6926. /**
  6927. * Ellipsize String
  6928. *
  6929. * This public static function will strip tags from a string, split it at its max_length and ellipsize
  6930. *
  6931. * @param string string to ellipsize
  6932. * @param integer max length of string
  6933. * @param mixed int (1|0) or float, .5, .2, etc for position to split
  6934. * @param string ellipsis ; Default '...'
  6935. * @return string ellipsized string
  6936. */
  6937. function ellipsize($str, $max_length, $position = 1, $ellipsis = '&hellip;')
  6938. {
  6939. // Strip tags
  6940. $str = trim(strip_tags($str));
  6941. // Is the string long enough to ellipsize?
  6942. if (strlen($str) <= $max_length+3)
  6943. {
  6944. return $str;
  6945. }
  6946. $beg = substr($str, 0, floor($max_length * $position));
  6947. $position = ($position > 1) ? 1 : $position;
  6948. if ($position === 1)
  6949. {
  6950. $end = substr($str, 0, -($max_length - strlen($beg)));
  6951. }
  6952. else
  6953. {
  6954. $end = substr($str, -($max_length - strlen($beg)));
  6955. }
  6956. return $beg.$ellipsis.$end;
  6957. }
  6958. /**
  6959. * This function returns the real IP address under all configurations
  6960. *
  6961. */
  6962. function getIPAddress()
  6963. {
  6964. if (!empty($_SERVER['HTTP_CLIENT_IP'])) //check ip from share internet
  6965. {
  6966. return $_SERVER['HTTP_CLIENT_IP'];
  6967. }
  6968. elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) //to check ip is pass from proxy
  6969. {
  6970. return $_SERVER['HTTP_X_FORWARDED_FOR'];
  6971. }
  6972. elseif (!empty($_SERVER['REMOTE_ADDR']))
  6973. {
  6974. return $_SERVER['REMOTE_ADDR'];
  6975. }
  6976. else
  6977. {
  6978. return '127.0.0.1';
  6979. }
  6980. }
  6981. /**
  6982. * This function tries to find out a valid language code for the language of the browser used
  6983. * If it cannot find it it will return the default language from global settings
  6984. *
  6985. */
  6986. function getBrowserLanguage()
  6987. {
  6988. $sLanguage=Yii::app()->getRequest()->getPreferredLanguage();
  6989. Yii::app()->loadHelper("surveytranslator");
  6990. $aLanguages=getLanguageData();
  6991. if (!isset($aLanguages[$sLanguage]))
  6992. {
  6993. $sLanguage=str_replace('_','-',$sLanguage);
  6994. if (!isset($aLanguages[$sLanguage]))
  6995. {
  6996. $sLanguage=substr($sLanguage,0,strpos($sLanguage,'-'));
  6997. if (!isset($aLanguages[$sLanguage]))
  6998. {
  6999. $sLanguage=Yii::app()->getConfig('defaultlang');
  7000. }
  7001. }
  7002. }
  7003. return $sLanguage;
  7004. }
  7005. /**
  7006. * This function add string to css or js header for public surevy
  7007. * @param string string to ellipsize
  7008. * @param string max length of string
  7009. * @return array array of string for js or css to be included
  7010. *
  7011. */
  7012. function header_includes($includes = false, $method = "js" )
  7013. {
  7014. $header_includes = (array) Yii::app()->getConfig("{$method}_header_includes");
  7015. $header_includes[] = $includes;
  7016. $header_includes = array_filter($header_includes);
  7017. $header_includes = array_unique($header_includes);
  7018. Yii::app()->setConfig("{$method}_header_includes", $header_includes);
  7019. return $header_includes;
  7020. }
  7021. // Closing PHP tag intentionally omitted - yes, it is okay