PageRenderTime 55ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/application/controllers/admin/participantsaction.php

https://bitbucket.org/sammousa/valuematchbv-ls2
PHP | 1770 lines | 1332 code | 162 blank | 276 comment | 190 complexity | 5285531018425b7becb137d642f3558a MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, BSD-3-Clause, GPL-3.0, LGPL-3.0

Large files files are truncated, but you can click here to view the full file

  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. * $Id$
  14. */
  15. function subval_sort($a, $subkey, $order)
  16. {
  17. $b = array();
  18. $c = array();
  19. foreach ($a as $k => $v)
  20. {
  21. $b[$k] = strtolower($v[$subkey]);
  22. }
  23. if ($order == "asc")
  24. {
  25. asort($b, SORT_REGULAR);
  26. }
  27. else
  28. {
  29. arsort($b, SORT_REGULAR);
  30. }
  31. foreach ($b as $key => $val)
  32. {
  33. $c[] = $a[$key];
  34. }
  35. return $c;
  36. }
  37. /*
  38. * This is the main controller for Participants Panel
  39. */
  40. class participantsaction extends Survey_Common_Action
  41. {
  42. public function runWithParams($params)
  43. {
  44. if (!hasGlobalPermission('USER_RIGHT_PARTICIPANT_PANEL'))
  45. {
  46. die('No permission');
  47. }
  48. parent::runWithParams($params);
  49. }
  50. /**
  51. * Loads jqGrid for the view
  52. * @param string $sScript Subaction
  53. */
  54. private function _loadjqGrid($sScript = '', $aData = array())
  55. {
  56. $this->getController()->_js_admin_includes(Yii::app()->getConfig('generalscripts') . 'jquery/jqGrid/js/i18n/grid.locale-en.js');
  57. $this->getController()->_js_admin_includes(Yii::app()->getConfig('generalscripts') . 'jquery/jqGrid/js/jquery.jqGrid.min.js');
  58. $this->getController()->_js_admin_includes(Yii::app()->getConfig('generalscripts') . 'jquery/jqGrid/plugins/jquery.searchFilter.js');
  59. $this->getController()->_js_admin_includes(Yii::app()->getConfig('generalscripts') . 'jquery/jqGrid/src/grid.celledit.js');
  60. $this->getController()->_css_admin_includes(Yii::app()->getConfig('generalscripts') . 'jquery/jqGrid/css/ui.jqgrid.css');
  61. $this->getController()->_css_admin_includes(Yii::app()->getConfig('generalscripts') . 'jquery/jqGrid/css/jquery.ui.datepicker.css');
  62. if (!empty($sScript))
  63. {
  64. $this->getController()->_js_admin_includes(Yii::app()->getConfig('adminscripts') . $sScript . '.js');
  65. $this->_renderWrappedTemplate('participants', array('participantsPanel', $sScript), $aData);
  66. }
  67. }
  68. /**
  69. * Renders template(s) wrapped in header and footer
  70. *
  71. * @param string $sAction Current action, the folder to fetch views from
  72. * @param string|array $aViewUrls View url(s)
  73. * @param array $aData Data to be passed on. Optional.
  74. */
  75. protected function _renderWrappedTemplate($sAction = 'participants', $aViewUrls = array(), $aData = array())
  76. {
  77. $aData['display']['menu_bars'] = false;
  78. foreach((array) $aViewUrls as $sViewUrl)
  79. {
  80. $a_ViewUrls[] = $sViewUrl . '_view';
  81. }
  82. parent::_renderWrappedTemplate($sAction, $a_ViewUrls, $aData);
  83. }
  84. /**
  85. * Export to csv using optional search/filter
  86. *
  87. * @param type $search
  88. */
  89. private function csvExport($search = null) {
  90. Yii::app()->loadHelper('export');
  91. $attid = ParticipantAttributeNames::model()->getVisibleAttributes();
  92. //If super admin all the participants will be visible
  93. if (Yii::app()->session['USER_RIGHT_SUPERADMIN'])
  94. {
  95. $iUserID = null;
  96. } else {
  97. $iUserID = Yii::app()->session['loginID'];
  98. }
  99. $query = Participants::model()->getParticipants(0, 0, $attid, null, $search, $iUserID);
  100. if (!$query)
  101. return false;
  102. // Field names in the first row
  103. $fields = array('participant_id', 'firstname', 'lastname', 'email', 'language', 'blacklisted', 'owner_uid');
  104. $outputarray = array(); // The array to be passed to the export helper to be written to a csv file
  105. $outputarray[0] = $fields; //fields written to output array
  106. // If attribute fields are selected, add them to the output
  107. $queryId = Yii::app()->request->getQuery('id');
  108. if (!is_null($queryId) && $queryId != "null") {
  109. $iAttributeId = explode(",", $queryId);
  110. foreach ($iAttributeId as $key => $value)
  111. {
  112. $fields[] = 'a'.$value;
  113. $attributename = ParticipantAttributeNames::model()->getAttributeNames($value);
  114. $outputarray[0][] = $attributename[0]['attribute_name'];
  115. }
  116. }
  117. $fieldKeys = array_flip($fields);
  118. foreach ($query as $field => $aData)
  119. {
  120. $outputarray[] = array_intersect_key($aData, $fieldKeys);
  121. }
  122. CPDBExport($outputarray, "central_" . time());
  123. }
  124. /**
  125. * Returns a string with the number of participants available for export or 0
  126. *
  127. * @param type $search
  128. * @return string|0
  129. */
  130. protected function csvExportCount($search = null)
  131. {
  132. $clang = $this->getController()->lang;
  133. $attid = ParticipantAttributeNames::model()->getVisibleAttributes();
  134. //If super admin all the participants will be visible
  135. if (Yii::app()->session['USER_RIGHT_SUPERADMIN'])
  136. {
  137. $iUserID = null;
  138. } else {
  139. $iUserID = Yii::app()->session['loginID'];
  140. }
  141. $count = Participants::model()->getParticipantsCount($attid, $search, $iUserID);
  142. if ($count > 0) {
  143. return sprintf($clang->gT("Export %s participant(s) to CSV"), $count);
  144. } else {
  145. return $count;
  146. }
  147. }
  148. /**
  149. * Loads the view 'participantsPanel'
  150. */
  151. function index()
  152. {
  153. $iUserID = Yii::app()->session['loginID'];
  154. // if superadmin all the records in the cpdb will be displayed
  155. if (Yii::app()->session['USER_RIGHT_SUPERADMIN'])
  156. {
  157. $iTotalRecords = Participants::model()->count();
  158. }
  159. // if not only the participants on which he has right on (shared and owned)
  160. else
  161. {
  162. $iTotalRecords = Participants::model()->getParticipantsOwnerCount($iUserID);
  163. }
  164. // gets the count of participants, their attributes and other such details
  165. $aData = array(
  166. 'totalrecords' => $iTotalRecords,
  167. 'owned' => Participants::model()->count('owner_uid = ' . $iUserID),
  168. 'shared' => Participants::model()->getParticipantsSharedCount($iUserID),
  169. 'attributecount' => ParticipantAttributeNames::model()->count(),
  170. 'blacklisted' => Participants::model()->count('owner_uid = ' . $iUserID . ' AND blacklisted = \'Y\'')
  171. );
  172. // loads the participant panel and summary view
  173. $this->_renderWrappedTemplate('participants', array('participantsPanel', 'summary'), $aData);
  174. }
  175. /**
  176. * Loads the view 'importCSV'
  177. */
  178. function importCSV()
  179. {
  180. $this->_renderWrappedTemplate('participants', array('participantsPanel', 'importCSV'));
  181. }
  182. /**
  183. * Loads the view 'displayParticipants' which contains the main grid
  184. */
  185. function displayParticipants()
  186. {
  187. $lang = Yii::app()->session['adminlang'];
  188. // loads the survey names to be shown in add to survey
  189. // if user is superadmin, all survey names
  190. $urlSearch=Yii::app()->request->getQuery('searchurl');
  191. $urlSearch=!empty($urlSearch) ? "getParticipantsResults_json/search/$urlSearch" : "getParticipants_json";
  192. //Get list of surveys.
  193. //Should be all surveys owned by user (or all surveys for super admin)
  194. $surveys = Survey::model();
  195. //!!! Is this even possible to execute?
  196. if (empty(Yii::app()->session['USER_RIGHT_SUPERADMIN']))
  197. $surveys->permission(Yii::app()->user->getId());
  198. $aSurveyNames = $surveys->model()->with(array('languagesettings'=>array('condition'=>'surveyls_language=language'), 'owner'))->findAll();
  199. /* Build a list of surveys that have tokens tables */
  200. $tSurveyNames=array();
  201. foreach($aSurveyNames as $row)
  202. {
  203. $row = array_merge($row->attributes, $row->languagesettings[0]->attributes);
  204. $bTokenExists = tableExists('{{tokens_' . $row['sid'] . '}}');
  205. if ($bTokenExists) //If tokens table exists
  206. {
  207. $tSurveyNames[]=$row;
  208. }
  209. }
  210. // data to be passed to view
  211. $aData = array(
  212. 'names' => User::model()->findAll(),
  213. 'attributes' => ParticipantAttributeNames::model()->getVisibleAttributes(),
  214. 'allattributes' => ParticipantAttributeNames::model()->getAllAttributes(),
  215. 'attributeValues' => ParticipantAttributeNames::model()->getAllAttributesValues(),
  216. 'surveynames' => $aSurveyNames,
  217. 'tokensurveynames' => $tSurveyNames,
  218. 'urlsearch' => $urlSearch
  219. );
  220. $this->getController()->_js_admin_includes(Yii::app()->getConfig('generalscripts') . 'jquery/jqGrid/js/i18n/grid.locale-en.js');
  221. $this->getController()->_js_admin_includes(Yii::app()->getConfig('generalscripts') . 'jquery/jqGrid/js/jquery.jqGrid.min.js');
  222. $this->getController()->_css_admin_includes(Yii::app()->getConfig('publicstyleurl') . 'jquery.multiselect.css');
  223. $this->getController()->_css_admin_includes(Yii::app()->getConfig('publicstyleurl') . 'jquery.multiselect.filter.css');
  224. $this->getController()->_css_admin_includes(Yii::app()->getConfig('adminstyleurl') . 'displayParticipants.css');
  225. $this->getController()->_css_admin_includes(Yii::app()->getConfig('generalscripts') . 'jquery/jqGrid/css/ui.jqgrid.css');
  226. $this->getController()->_css_admin_includes(Yii::app()->getConfig('generalscripts') . 'jquery/jqGrid/css/jquery.ui.datepicker.css');
  227. // loads the participant panel view and display participant view
  228. $this->_renderWrappedTemplate('participants', array('participantsPanel', 'displayParticipants'), $aData);
  229. }
  230. /**
  231. * Loads the view 'blacklistControl'
  232. */
  233. function blacklistControl()
  234. {
  235. $aData = array(
  236. 'blacklistallsurveys' => Yii::app()->getConfig('blacklistallsurveys'),
  237. 'blacklistnewsurveys' => Yii::app()->getConfig('blacklistnewsurveys'),
  238. 'blockaddingtosurveys' => Yii::app()->getConfig('blockaddingtosurveys'),
  239. 'hideblacklisted' => Yii::app()->getConfig('hideblacklisted'),
  240. 'deleteblacklisted' => Yii::app()->getConfig('deleteblacklisted'),
  241. 'allowunblacklist' => Yii::app()->getConfig('allowunblacklist')
  242. );
  243. $this->_renderWrappedTemplate('participants', array('participantsPanel', 'blacklist'), $aData);
  244. }
  245. /**
  246. * Loads the view 'userControl'
  247. */
  248. function userControl()
  249. {
  250. $aData = array(
  251. 'userideditable' => Yii::app()->getConfig('userideditable')
  252. );
  253. $this->_renderWrappedTemplate('participants', array('participantsPanel', 'userControl'), $aData);
  254. }
  255. /**
  256. * Loads the view 'sharePanel'
  257. */
  258. function sharePanel()
  259. {
  260. $this->_loadjqGrid('sharePanel');
  261. }
  262. /**
  263. * Sends the shared participant info to the share panel using JSON encoding
  264. * Called after the share panel grid is loaded
  265. * Returns the json depending on the user logged in by checking it from the session
  266. * @return JSON encoded string containg sharing information
  267. */
  268. function getShareInfo_json()
  269. {
  270. $aData = new stdClass();
  271. $aData->page = 1;
  272. // If super administrator all the share info in the links table will be shown
  273. if (Yii::app()->session['USER_RIGHT_SUPERADMIN'])
  274. {
  275. $records = Participants::model()->getParticipantSharedAll();
  276. $aData->records = count($records);
  277. $aData->total = ceil($aData->records / 10);
  278. $i = 0;
  279. foreach ($records as $row)
  280. {
  281. $oShared = User::model()->getName($row['share_uid']); //for conversion of uid to human readable names
  282. $owner = User::model()->getName($row['owner_uid']);
  283. $aData->rows[$i]['id'] = $row['participant_id']."--".$row['share_uid']; //This is the unique combination per record
  284. $aData->rows[$i]['cell'] = array($row['firstname'], $row['lastname'], $row['email'], $oShared[0]['full_name'], $row['share_uid'], $owner[0]['full_name'], $row['date_added'], $row['can_edit']);
  285. $i++;
  286. }
  287. echo ls_json_encode($aData);
  288. }
  289. // otherwise only the shared participants by that user
  290. else
  291. {
  292. $records = Participants::model()->getParticipantShared(Yii::app()->session['loginID']);
  293. $aData->records = count($records);
  294. $aData->total = ceil($aData->records / 10);
  295. $i = 0;
  296. foreach ($records as $row)
  297. {
  298. $sharename = User::model()->getName($row['share_uid']); //for conversion of uid to human readable names
  299. $aData->rows[$i]['id'] = $row['participant_id'];
  300. $aData['rows'][$i]['cell'] = array($row['firstname'], $row['lastname'], $row['email'], $sharename['full_name'], $row['share_uid'], $row['date_added'], $row['can_edit']);
  301. $i++;
  302. }
  303. echo ls_json_encode($aData);
  304. }
  305. }
  306. /**
  307. * Takes the edit call from the share panel, which either edits or deletes the share information
  308. * Basically takes the call on can_edit
  309. */
  310. function editShareInfo()
  311. {
  312. $operation = Yii::app()->request->getPost('oper');
  313. $shareIds = Yii::app()->request->getPost('id');
  314. if ($operation == 'del') // If operation is delete , it will delete, otherwise edit it
  315. {
  316. ParticipantShares::model()->deleteRow($shareIds);
  317. }
  318. else
  319. {
  320. $aData = array(
  321. 'participant_id' => Yii::app()->request->getPost('participant_id'),
  322. 'can_edit' => Yii::app()->request->getPost('can_edit'),
  323. 'share_uid' => Yii::app()->request->getPost('shared_uid')
  324. );
  325. ParticipantShares::model()->updateShare($aData);
  326. }
  327. }
  328. /**
  329. * Loads the view 'attributeControl'
  330. */
  331. function attributeControl()
  332. {
  333. $this->_loadjqGrid('attributeControl');
  334. }
  335. /**
  336. * Sends the attributes info using JSON encoding
  337. * Called after the Attribute management grid is loaded
  338. * @return JSON encoded string containg sharing information
  339. */
  340. function getAttributeInfo_json()
  341. {
  342. $clang = Yii::app()->lang;
  343. $page = Yii::app()->request->getPost('page');
  344. $limit = Yii::app()->request->getPost('rows');
  345. $limit = isset($limit) ? $limit : 50; //Stop division by zero errors
  346. $records = ParticipantAttributeNames::model()->with('participant_attribute_names_lang')->findAll();
  347. $attribute_types = array(
  348. 'DD' => $clang->gT("Drop-down list"),
  349. 'DP' => $clang->gT("Date"),
  350. 'TB' => $clang->gT("Text box")
  351. );
  352. $aData = new stdClass();
  353. $aData->page = $page;
  354. $aData->records = count($records);
  355. $aData->total = ceil(ParticipantAttributeNames::model()->getAttributes(true) / $limit);
  356. $i = 0;
  357. foreach($records as $row) { //Iterate through each attribute
  358. $thisname="";
  359. foreach($row->participant_attribute_names_lang as $names) { //Iterate through each language version of this attribute
  360. if($thisname=="") {$thisname=$names->attribute_name;} //Choose the first item by default
  361. if($names->lang == Yii::app()->session['adminlang']) {$thisname=$names->attribute_name;} //Override the default with the admin language version if found
  362. }
  363. $aData->rows[$i]['id'] = $row->attribute_id;
  364. $aData->rows[$i]['cell'] = array('', $thisname, $attribute_types[$row->attribute_type], $row->visible);
  365. $i++;
  366. }
  367. echo ls_json_encode($aData);
  368. }
  369. /**
  370. * Takes the edit call from the share panel, which either edits or deletes the share information
  371. * Basically takes the call on can_edit
  372. */
  373. function editAttributeInfo()
  374. {
  375. $clang = Yii::app()->lang;
  376. $operation = Yii::app()->request->getPost('oper');
  377. if ($operation == 'del' && Yii::app()->request->getPost('id'))
  378. {
  379. $aAttributeIds = (array) explode(',', Yii::app()->request->getPost('id'));
  380. $aAttributeIds = array_map('trim', $aAttributeIds);
  381. $aAttributeIds = array_map('intval', $aAttributeIds);
  382. foreach ($aAttributeIds as $iAttributeId)
  383. {
  384. ParticipantAttributeNames::model()->delAttribute($iAttributeId);
  385. }
  386. }
  387. elseif ($operation == 'add' && Yii::app()->request->getPost('attribute_name'))
  388. {
  389. $aData = array(
  390. 'attribute_name' => Yii::app()->request->getPost('attribute_name'),
  391. 'attribute_type' => Yii::app()->request->getPost('attribute_type'),
  392. 'visible' => Yii::app()->request->getPost('visible') == 'TRUE' ? 'TRUE' : 'FALSE'
  393. );
  394. echo ParticipantAttributeNames::model()->storeAttribute($aData);
  395. }
  396. elseif ($operation == 'edit' && Yii::app()->request->getPost('id'))
  397. {
  398. $aData = array(
  399. 'attribute_id' => Yii::app()->request->getPost('id'),
  400. 'attribute_name' => Yii::app()->request->getPost('attribute_name'),
  401. 'attribute_type' => Yii::app()->request->getPost('attribute_type'),
  402. 'visible' => Yii::app()->request->getPost('visible') == 'TRUE' ? 'TRUE' : 'FALSE'
  403. );
  404. ParticipantAttributeNames::model()->saveAttribute($aData);
  405. $clang->eT("Attribute display setting updated");
  406. }
  407. }
  408. /**
  409. * Takes the delete call from the display participants and take appropriate action depending on the condition
  410. */
  411. function delParticipant()
  412. {
  413. $selectoption = Yii::app()->request->getPost('selectedoption');
  414. $iParticipantId = Yii::app()->request->getPost('participant_id');
  415. //echo $selectoption." -- ".$iParticipantId."<br />"; die();
  416. // Deletes from participants only
  417. if ($selectoption == 'po')
  418. {
  419. Participants::model()->deleteParticipants($iParticipantId);
  420. }
  421. // Deletes from central and token table
  422. elseif ($selectoption == 'ptt')
  423. {
  424. Participants::model()->deleteParticipantToken($iParticipantId);
  425. }
  426. // Deletes from central , token and assosiated responses as well
  427. elseif ($selectoption == 'ptta')
  428. {
  429. Participants::model()->deleteParticipantTokenAnswer($iParticipantId);
  430. }
  431. }
  432. /**
  433. * Resposible for editing data on the jqGrid
  434. */
  435. function editParticipant()
  436. {
  437. $operation = Yii::app()->request->getPost('oper');
  438. //In case the uid is not editable, then user id is not posted and hence the current user is added in the uid
  439. if (Yii::app()->request->getPost('owner_uid') == '')
  440. {
  441. $oid = Yii::app()->session['loginID'];
  442. }
  443. //otherwise the one which is posted is added
  444. else
  445. {
  446. $oid = Yii::app()->request->getPost('owner_uid');
  447. }
  448. if (Yii::app()->request->getPost('language') == '')
  449. {
  450. $lang = Yii::app()->session['adminlang'];
  451. }
  452. else
  453. {
  454. $lang = Yii::app()->request->getPost('language');
  455. }
  456. // if edit it will update the row
  457. if ($operation == 'edit')
  458. {
  459. $aData = array(
  460. 'participant_id' => Yii::app()->request->getPost('id'),
  461. 'firstname' => Yii::app()->request->getPost('firstname'),
  462. 'lastname' => Yii::app()->request->getPost('lastname'),
  463. 'email' => Yii::app()->request->getPost('email'),
  464. 'language' => Yii::app()->request->getPost('language'),
  465. 'blacklisted' => Yii::app()->request->getPost('blacklisted'),
  466. 'owner_uid' => $oid
  467. );
  468. Participants::model()->updateRow($aData);
  469. }
  470. // if add it will insert a new row
  471. elseif ($operation == 'add')
  472. {
  473. $uuid = $this->gen_uuid();
  474. $aData = array(
  475. 'participant_id' => $uuid,
  476. 'firstname' => Yii::app()->request->getPost('firstname'),
  477. 'lastname' => Yii::app()->request->getPost('lastname'),
  478. 'email' => Yii::app()->request->getPost('email'),
  479. 'language' => Yii::app()->request->getPost('language'),
  480. 'blacklisted' => Yii::app()->request->getPost('blacklisted'),
  481. 'owner_uid' => $oid
  482. );
  483. Participants::model()->insertParticipant($aData);
  484. }
  485. }
  486. /**
  487. * Stores the user control setting to the database
  488. */
  489. function storeUserControlValues()
  490. {
  491. if ($find = Settings_global::model()->findByPk('userideditable'))
  492. {
  493. Settings_global::model()->updateByPk('userideditable', array('stg_value'=>Yii::app()->request->getPost('userideditable')));
  494. }
  495. else
  496. {
  497. $stg = new Settings_global;
  498. $stg ->stg_name='userideditable';
  499. $stg ->stg_value=Yii::app()->request->getPost('userideditable');
  500. $stg->save();
  501. }
  502. Yii::app()->getController()->redirect(Yii::app()->getController()->createUrl('admin/participants/sa/userControl'));
  503. }
  504. /**
  505. * Stores the blacklist setting to the database
  506. */
  507. function storeBlacklistValues()
  508. {
  509. $values = Array('blacklistallsurveys', 'blacklistnewsurveys', 'blockaddingtosurveys', 'hideblacklisted', 'deleteblacklisted', 'allowunblacklist', 'userideditable');
  510. foreach ($values as $value)
  511. {
  512. if ($find = Settings_global::model()->findByPk($value))
  513. {
  514. Settings_global::model()->updateByPk($value, array('stg_value'=>Yii::app()->request->getPost($value)));
  515. }
  516. else
  517. {
  518. $stg = new Settings_global;
  519. $stg ->stg_name=$value;
  520. $stg ->stg_value=Yii::app()->request->getPost($value);
  521. $stg->save();
  522. }
  523. }
  524. Yii::app()->getController()->redirect(Yii::app()->getController()->createUrl('admin/participants/sa/blacklistControl'));
  525. }
  526. /**
  527. * Receives an ajax call containing the participant id in the fourth segment of the url
  528. * Supplies list of survey links - surveys of which this participant is on the tokens table
  529. * URL: [localurl]/limesurvey/admin/participants/getSurveyInfo_json/pid/[participant_id]
  530. * RETURNS: json data containing linked survey information (Survey name, survey id, token_id and date_added)
  531. */
  532. function getSurveyInfo_json()
  533. {
  534. $participantid = Yii::app()->request->getQuery('pid');
  535. $records = Survey_links::model()->findAllByAttributes((array('participant_id' => $participantid)));
  536. $aData = new stdClass();
  537. $aData->page = 1;
  538. $aData->records = count($records);
  539. $aData->total = ceil($aData->records / 10);
  540. $i = 0;
  541. foreach ($records as $row)
  542. {
  543. $oSurvey=Survey::model()->with(array('languagesettings'=>array('condition'=>'surveyls_language=language')))->findByAttributes(array('sid' => $row['survey_id']));
  544. foreach($oSurvey->languagesettings as $oLanguageSetting)
  545. {
  546. $surveyname= $oLanguageSetting->surveyls_title;
  547. }
  548. $surveylink = "";
  549. /* Check permissions of each survey before creating a link*/
  550. if (!hasSurveyPermission($row['survey_id'], 'tokens', 'read'))
  551. {
  552. $surveylink = $row['survey_id'];
  553. } else
  554. {
  555. $surveylink = '<a href=' . Yii::app()->getController()->createUrl("/admin/tokens/sa/browse/surveyid/{$row['survey_id']}") . '>' . $row['survey_id'].'</a>';
  556. }
  557. $aData->rows[$i]['cell'] = array($surveyname, $surveylink, $row['token_id'], $row['date_created'], $row['date_invited'], $row['date_completed']);
  558. $i++;
  559. }
  560. echo ls_json_encode($aData);
  561. }
  562. /**
  563. * Returns the count of the participants in the CSV and show it in the title of the modal box
  564. * This is to give the user the hint to see the number of participants he is exporting
  565. */
  566. function exporttocsvcount()
  567. {
  568. $searchconditionurl = Yii::app()->request->getPost('searchcondition');
  569. $searchcondition = basename($searchconditionurl);
  570. if ($searchcondition != 'getParticipants_json') // if there is a search condition then only the participants that match the search criteria are counted
  571. {
  572. $condition = explode("||", $searchcondition);
  573. $search = Participants::model()->getParticipantsSearchMultipleCondition($condition);
  574. } else {
  575. $search = null;
  576. }
  577. echo $this->csvExportCount($search);
  578. }
  579. /**
  580. * Outputs the count of participants when using the export all button on the top
  581. */
  582. function exporttocsvcountAll()
  583. {
  584. echo $this->csvExportCount();
  585. }
  586. /**
  587. * Responsible to export all the participants in the central table
  588. */
  589. function exporttocsvAll()
  590. {
  591. $this->csvExport(); // no search
  592. }
  593. /**
  594. * Similar to export to all message where it counts the number to participants to be copied
  595. * and echo them to be displayed in modal box header
  596. */
  597. function getaddtosurveymsg()
  598. {
  599. $searchcondition = basename(Yii::app()->request->getPost('searchcondition'));
  600. // If there is a search condition in the url of the jqGrid
  601. if ($searchcondition != 'getParticipants_json')
  602. {
  603. $participantid = "";
  604. $condition = explode("||", $searchcondition);
  605. $query = Participants::model()->getParticipantsSearchMultiple($condition, 0, 0);
  606. printf( $this->getController()->lang->gT("%s participant(s) are to be copied "), count($query));
  607. }
  608. // if there is no search condition the participants will be counted on the basis of who is logged in
  609. else
  610. {
  611. if (Yii::app()->session['USER_RIGHT_SUPERADMIN']) //If super admin all the participants will be visible
  612. {
  613. $count = Participants::model()->getParticipantsCountWithoutLimit();
  614. }
  615. else
  616. {
  617. $query = Participants::model()->getParticipantsOwner(Yii::app()->session['loginID']);
  618. $count = count($query);
  619. }
  620. printf($this->getController()->lang->gT("%s participant(s) are to be copied "), $count);
  621. }
  622. }
  623. /**
  624. * Gets the ids of participants to be copied to the individual survey
  625. */
  626. function getSearchIDs()
  627. {
  628. $searchcondition = basename(Yii::app()->request->getPost('searchcondition')); // get the search condition from the URL
  629. /* a search contains posted data inside $_POST['searchcondition'].
  630. * Each seperate query is made up of 3 fields, seperated by double-pipes ("|")
  631. * EG: fname||eq||jason||lname||ct||c
  632. *
  633. */
  634. if ($searchcondition != 'getParticipants_json') // if there is a search condition present
  635. {
  636. $participantid = "";
  637. $condition = explode("||", $searchcondition); // explode the condition to the array
  638. $query = Participants::model()->getParticipantsSearchMultiple($condition, 0, 0);
  639. foreach ($query as $key => $value)
  640. {
  641. if (Yii::app()->session['USER_RIGHT_SUPERADMIN'])
  642. {
  643. $participantid .= "," . $value['participant_id']; // combine the participant id's in an string
  644. } else
  645. {
  646. if(Participants::model()->is_owner($value['participant_id']))
  647. {
  648. $participantid .= "," . $value['participant_id']; // combine the participant id's in an string
  649. }
  650. }
  651. }
  652. echo $participantid; //echo the participant id's
  653. }
  654. else// if no search condition
  655. {
  656. $participantid = ""; // initiallise the participant id to blank
  657. if (Yii::app()->session['USER_RIGHT_SUPERADMIN']) //If super admin all the participants will be visible
  658. {
  659. $query = Participants::model()->getParticipantsWithoutLimit(); // get all the participant id if it is a super admin
  660. }
  661. else // get participants on which the user has right on
  662. {
  663. $query = Participants::model()->getParticipantsOwner(Yii::app()->session['loginID']);
  664. }
  665. foreach ($query as $key => $value)
  666. {
  667. $participantid = $participantid . "," . $value['participant_id']; // combine the participant id's in an string
  668. }
  669. echo $participantid; //echo the participant id's
  670. }
  671. }
  672. /**
  673. * Responsible for reading the CSV file line by line, check for duplicate participants
  674. * invalid participants and invalid attributes and copy them to the central table
  675. * Also responsible for creation of new attribute and mapping of old attribute to attribute in csv
  676. */
  677. function exporttocsv()
  678. {
  679. $searchconditionurl = Yii::app()->request->getPost('searchcondition');
  680. $searchcondition = basename($searchconditionurl);
  681. if ($searchcondition != 'getParticipants_json') // if there is a search condition then only the participants that match the search criteria are counted
  682. {
  683. $condition = explode("||", $searchcondition);
  684. $search = Participants::model()->getParticipantsSearchMultipleCondition($condition);
  685. } else {
  686. $search = null;
  687. }
  688. $this->csvExport($search);
  689. }
  690. /**
  691. * Equal to getParticipants_json() but now with a search
  692. */
  693. function getParticipantsResults_json()
  694. {
  695. $searchcondition = Yii::app()->request->getpost('searchcondition');
  696. $finalcondition = array();
  697. $condition = explode("||", $searchcondition);
  698. $search = Participants::model()->getParticipantsSearchMultipleCondition($condition);
  699. return $this->getParticipants_json($search);
  700. }
  701. /*
  702. * Sends the data in JSON format extracted from the database to be displayed using the jqGrid
  703. */
  704. function getParticipants_json($search = null)
  705. {
  706. $page = Yii::app()->request->getPost('page');
  707. $limit = Yii::app()->request->getPost('rows');
  708. $limit = isset($limit) ? $limit : 50; //Stop division by zero errors
  709. $attid = ParticipantAttributeNames::model()->getVisibleAttributes();
  710. $participantfields = array('participant_id', 'can_edit', 'firstname', 'lastname', 'email', 'blacklisted', 'survey', 'language', 'owner_uid');
  711. foreach ($attid as $key => $value)
  712. {
  713. array_push($participantfields, $value['attribute_id']);
  714. }
  715. $sidx = Yii::app()->request->getPost('sidx');
  716. $sidx = !empty($sidx) ? $sidx : "lastname";
  717. $sord = Yii::app()->request->getPost('sord');
  718. $sord = !empty($sord) ? $sord : "asc";
  719. $order = $sidx. " ". $sord;
  720. $aData = new stdClass;
  721. //If super admin all the participants will be visible
  722. if (Yii::app()->session['USER_RIGHT_SUPERADMIN'])
  723. {
  724. $iUserID = null;
  725. } else {
  726. $iUserID = Yii::app()->session['loginID'];
  727. }
  728. $aData->records = Participants::model()->getParticipantsCount($attid, $search, $iUserID);
  729. $aData->total = ceil($aData->records / $limit);
  730. if ($page>$aData->total) {
  731. $page = $aData->total;
  732. }
  733. $aData->page = $page;
  734. $records = Participants::model()->getParticipants($page, $limit,$attid, $order, $search, $iUserID);
  735. $aRowToAdd=array();
  736. foreach ($records as $key => $row)
  737. {
  738. if (array_key_exists('can_edit', $row)) {
  739. $sCanEdit = $row['can_edit'];
  740. if (is_null($sCanEdit)) {
  741. $sCanEdit = 'true';
  742. }
  743. } else {
  744. // Super admin
  745. $sCanEdit = "true";
  746. }
  747. if (trim($row['ownername'])=='') {
  748. $row['ownername']=$row['username'];
  749. }
  750. $aRowToAdd['cell'] = array($row['participant_id'], $sCanEdit, $row['firstname'], $row['lastname'], $row['email'], $row['blacklisted'], $row['survey'], $row['language'], $row['ownername']);
  751. $aRowToAdd['id'] = $row['participant_id'];
  752. unset($row['participant_id'], $row['firstname'], $row['lastname'], $row['email'], $row['blacklisted'], $row['language'],$row['ownername'],$row['owner_uid'], $row['can_edit'], $row['survey'], $row['username']);
  753. foreach($row as $key=>$attvalue)
  754. {
  755. $aRowToAdd['cell'][] = $attvalue;
  756. }
  757. $aData->rows[] = $aRowToAdd;
  758. }
  759. echo ls_json_encode($aData);
  760. }
  761. /*
  762. * Fetches the attributes of a participant to be displayed in the attribute subgrid
  763. */
  764. function getAttribute_json()
  765. {
  766. $iParticipantId = Yii::app()->request->getQuery('pid');
  767. $records = ParticipantAttributeNames::model()->getParticipantVisibleAttribute($iParticipantId);
  768. //$getallattributes = ParticipantAttributeNames::model()->with('participant_attribute_names_lang')->findAll();
  769. $records = subval_sort($records, "attribute_name", "asc");
  770. $i = 0;
  771. $doneattributes = array(); //If the user has any actual attribute values, they'll be stored here
  772. /* Iterate through each attribute owned by this user */
  773. foreach ($records as $row)
  774. {
  775. $outputs[$i] = array("", $row['participant_id']."_".$row['attribute_id'], $row['attribute_type'], $row['attribute_id'], $row['attribute_name'], $row['value']);
  776. /* Collect allowed values for a DropDown attribute */
  777. if ($row['attribute_type'] == "DD")
  778. {
  779. $attvalues = ParticipantAttributeNames::model()->getAttributesValues($row['attribute_id']);
  780. if (!empty($attvalues))
  781. {
  782. $attval = "";
  783. foreach ($attvalues as $val)
  784. {
  785. $attval .= $val['value'] . ":" . $val['value'];
  786. $attval .= ";";
  787. }
  788. $attval = substr($attval, 0, -1);
  789. array_push($outputs[$i], $attval);
  790. }
  791. else
  792. {
  793. array_push($outputs[$i], "");
  794. }
  795. }
  796. else
  797. {
  798. array_push($outputs[$i], "");
  799. }
  800. array_push($doneattributes, $row['attribute_id']);
  801. $i++;
  802. }
  803. /* Build a list of attribute names for which this user has NO values stored, keep it in $attributenotdone */
  804. $attributenotdone=array();
  805. /* The user has NO values stored against any attribute */
  806. if (count($doneattributes) == 0)
  807. {
  808. $attributenotdone = ParticipantAttributeNames::model()->getAttributes();
  809. }
  810. /* The user has SOME values stored against attributes */
  811. else
  812. {
  813. $attributenotdone = ParticipantAttributeNames::model()->getnotaddedAttributes($doneattributes);
  814. }
  815. /* Go through the empty attributes and build an entry in the output for them */
  816. foreach ($attributenotdone as $row)
  817. {
  818. $outputs[$i] = array("", $iParticipantId."_".$row['attribute_id'], $row['attribute_type'], $row['attribute_id'], $row['attribute_name'], "");
  819. if ($row['attribute_type'] == "DD")
  820. {
  821. $attvalues = ParticipantAttributeNames::model()->getAttributesValues($row['attribute_id']);
  822. if (!empty($attvalues))
  823. {
  824. $attval = "";
  825. foreach ($attvalues as $val)
  826. {
  827. $attval .= $val['value'] . ":" . $val['value'];
  828. $attval .= ";";
  829. }
  830. $attval = substr($attval, 0, -1);
  831. array_push($outputs[$i], $attval);
  832. }
  833. else
  834. {
  835. array_push($outputs[$i], "");
  836. }
  837. }
  838. else
  839. {
  840. array_push($outputs[$i], "");
  841. }
  842. $i++;
  843. }
  844. $outputs=subval_sort($outputs, 3, "asc");
  845. $aData = new stdClass();
  846. $aData->page = 1;
  847. $aData->rows[0]['id'] = $iParticipantId;
  848. $aData->rows[0]['cell'] = array();
  849. $aData->records = count($outputs);
  850. $aData->total = ceil($aData->records / 10);
  851. foreach($outputs as $key=>$output) {
  852. $aData->rows[$key]['id']=$output[1];
  853. $aData->rows[$key]['cell']=$output;
  854. }
  855. /* TODO: It'd be nice to do a natural sort on the attribute list at some point.
  856. Currently they're returned in order of attributes WITH values, then WITHOUT values
  857. */
  858. echo ls_json_encode($aData);
  859. }
  860. /*
  861. * Gets the data from the form for add participants and pass it to the participants model
  862. */
  863. function storeParticipants()
  864. {
  865. $aData = array('participant_id' => uniqid(),
  866. 'firstname' => Yii::app()->request->getPost('firstname'),
  867. 'lastname' => Yii::app()->request->getPost('lastname'),
  868. 'email' => Yii::app()->request->getPost('email'),
  869. 'language' => Yii::app()->request->getPost('language'),
  870. 'blacklisted' => Yii::app()->request->getPost('blacklisted'),
  871. 'owner_uid' => Yii::app()->request->getPost('owner_uid'));
  872. Participants::model()->insertParticipant($aData);
  873. }
  874. /*
  875. * Responsible for showing the additional attribute for central database
  876. */
  877. function viewAttribute()
  878. {
  879. $iAttributeId = Yii::app()->request->getQuery('aid');
  880. $aData = array(
  881. 'attributes' => ParticipantAttributeNames::model()->getAttribute($iAttributeId),
  882. 'attributenames' => ParticipantAttributeNames::model()->getAttributeNames($iAttributeId),
  883. 'attributevalues' => ParticipantAttributeNames::model()->getAttributesValues($iAttributeId)
  884. );
  885. $this->getController()->_css_admin_includes(Yii::app()->getConfig('adminstyleurl') . 'participants.css');
  886. $this->getController()->_css_admin_includes(Yii::app()->getConfig('adminstyleurl') . 'viewAttribute.css');
  887. $this->_renderWrappedTemplate('participants', array('participantsPanel', 'viewAttribute'), $aData);
  888. }
  889. /*
  890. * Responsible for saving the additional attribute. It iterates through all the new attributes added dynamically
  891. * and iterates through them
  892. */
  893. function saveAttribute()
  894. {
  895. $iAttributeId = Yii::app()->request->getQuery('aid');
  896. $aData = array(
  897. 'attribute_id' => $iAttributeId,
  898. 'attribute_type' => Yii::app()->request->getPost('attribute_type'),
  899. 'visible' => Yii::app()->request->getPost('visible')
  900. );
  901. ParticipantAttributeNames::model()->saveAttribute($aData);
  902. foreach ($_POST as $key => $value)
  903. {
  904. // check for language code in the post variables this is a hack as the only way to check for language data
  905. if (strlen($key) == 2)
  906. {
  907. $langdata = array(
  908. 'attribute_id' => $iAttributeId,
  909. 'attribute_name' => $value,
  910. 'lang' => $key
  911. );
  912. ParticipantAttributeNames::model()->saveAttributeLanguages($langdata);
  913. }
  914. }
  915. if (Yii::app()->request->getPost('langdata'))
  916. {
  917. $langdata = array(
  918. 'attribute_id' => $iAttributeId,
  919. 'attribute_name' => Yii::app()->request->getPost('attname'),
  920. 'lang' => Yii::app()->request->getPost('langdata')
  921. );
  922. ParticipantAttributeNames::model()->saveAttributeLanguages($langdata);
  923. }
  924. /* Create new attribute value */
  925. if (Yii::app()->request->getPost('attribute_value_name_1') || Yii::app()->request->getPost('attribute_value_name_1') == "0")
  926. {
  927. $i = 1;
  928. $attvaluename = 'attribute_value_name_' . $i;
  929. while (array_key_exists($attvaluename, $_POST) && $_POST[$attvaluename] != "")
  930. {
  931. if ($_POST[$attvaluename] != "")
  932. {
  933. $aDatavalues[$i] = array(
  934. 'attribute_id' => $iAttributeId,
  935. 'value' => Yii::app()->request->getPost($attvaluename)
  936. );
  937. }
  938. $attvaluename = 'attribute_value_name_' . ++$i;
  939. };
  940. ParticipantAttributeNames::model()->storeAttributeValues($aDatavalues);
  941. }
  942. /* Save updated attribute values */
  943. if (Yii::app()->request->getPost('editbox') || Yii::app()->request->getPost('editbox')=="0")
  944. {
  945. $editattvalue = array(
  946. 'attribute_id' => $iAttributeId,
  947. 'value_id' => Yii::app()->request->getPost('value_id'),
  948. 'value' => Yii::app()->request->getPost('editbox')
  949. );
  950. ParticipantAttributeNames::model()->saveAttributeValue($editattvalue);
  951. }
  952. Yii::app()->getController()->redirect(Yii::app()->getController()->createUrl('admin/participants/sa/attributeControl'));
  953. }
  954. /*
  955. * Responsible for deleting the additional attribute values in case of drop down.
  956. */
  957. function delAttributeValues()
  958. {
  959. $iAttributeId = Yii::app()->request->getQuery('aid');
  960. $iValueId = Yii::app()->request->getQuery('vid');
  961. ParticipantAttributeNames::model()->delAttributeValues($iAttributeId, $iValueId);
  962. Yii::app()->getController()->redirect(Yii::app()->getController()->createUrl('/admin/participants/sa/viewAttribute/aid/' . $iAttributeId));
  963. }
  964. /*
  965. * Responsible for editing the additional attributes values
  966. */
  967. function editAttributevalue()
  968. {
  969. if (Yii::app()->request->getPost('oper') == "edit" && (Yii::app()->request->getPost('attvalue') || Yii::app()->request->getPost('attvalue')=="0"))
  970. {
  971. $pid = explode('_',Yii::app()->request->getPost('participant_id'));
  972. $iAttributeId = Yii::app()->request->getPost('attid');
  973. $aData = array('participant_id' => $pid[0], 'attribute_id' => $iAttributeId, 'value' => Yii::app()->request->getPost('attvalue'));
  974. ParticipantAttributeNames::model()->editParticipantAttributeValue($aData);
  975. }
  976. }
  977. function attributeMapCSV()
  978. {
  979. $clang = $this->getController()->lang;
  980. $sRandomFileName=randomChars(20);
  981. $sFilePath = Yii::app()->getConfig('tempdir') . DIRECTORY_SEPARATOR . $sRandomFileName;
  982. $aPathinfo = pathinfo($_FILES['the_file']['name']);
  983. $sExtension = $pathinfo['extension'];
  984. if (strtolower($sExtension)=='csv')
  985. {
  986. $bMoveFileResult = @move_uploaded_file($_FILES['the_file']['tmp_name'], $sFilePath);
  987. $errorinupload = '';
  988. $filterblankemails = Yii::app()->request->getPost('filterbea');
  989. }
  990. else
  991. {
  992. $templateData['error_msg'] = sprintf($clang->gT("This is not a .csv file."), Yii::app()->getConfig('tempdir'));
  993. $errorinupload = array('error' => $this->upload->display_errors());
  994. Yii::app()->session['summary'] = array('errorinupload' => $errorinupload);
  995. $this->_renderWrappedTemplate('participants', array('participantsPanel', 'uploadSummary'));
  996. }
  997. if (!$bMoveFileResult)
  998. {
  999. $templateData['error_msg'] = sprintf($clang->gT("An error occurred uploading your file. This may be caused by incorrect permissions in your %s folder."), Yii::app()->getConfig('tempdir'));
  1000. $errorinupload = array('error' => $this->upload->display_errors());
  1001. Yii::app()->session['summary'] = array('errorinupload' => $errorinupload);
  1002. $this->_renderWrappedTemplate('participants', array('participantsPanel', 'uploadSummary'));
  1003. }
  1004. else
  1005. {
  1006. $aData = array('upload_data' => $_FILES['the_file']);
  1007. $sFileName = $_FILES['the_file']['name'];
  1008. $regularfields = array('firstname', 'participant_id', 'lastname', 'email', 'language', 'blacklisted', 'owner_uid');
  1009. $csvread = fopen($sFilePath, 'r');
  1010. $seperator = Yii::app()->request->getPost('seperatorused');
  1011. $firstline = fgetcsv($csvread, 1000, ',');
  1012. $selectedcsvfields = array();
  1013. foreach ($firstline as $key => $value)
  1014. {
  1015. $testvalue = preg_replace('/[^(\x20-\x7F)]*/','', $value); //Remove invalid characters from string
  1016. if (!in_array(strtolower($testvalue), $regularfields))
  1017. {
  1018. array_push($selectedcsvfields, $value);
  1019. }
  1020. $fieldlist[]=$value;
  1021. }
  1022. $linecount = count(file($sFilePath));
  1023. $attributes = ParticipantAttributeNames::model()->model()->getAttributes();
  1024. $aData = array(
  1025. 'attributes' => $attributes,
  1026. 'firstline' => $selectedcsvfields,
  1027. 'fullfilepath' => $sRandomFileName,
  1028. 'linecount' => $linecount - 1,
  1029. 'filterbea' => $filterblankemails,
  1030. 'participant_id_exists' => in_array('participant_id', $fieldlist)
  1031. );
  1032. $this->_renderWrappedTemplate('participants', 'attributeMapCSV', $aData);
  1033. }
  1034. }
  1035. /*
  1036. * Uploads the file to the server and process it for valid enteries and import them into database
  1037. */
  1038. function uploadCSV()
  1039. {
  1040. unset(Yii::app()->session['summary']);
  1041. $characterset = Yii::app()->request->getPost('characterset');
  1042. $seperator = Yii::app()->request->getPost('seperatorused');
  1043. $newarray = Yii::app()->request->getPost('newarray');
  1044. $mappedarray = Yii::app()->request->getPost('mappedarray');
  1045. $filterblankemails = Yii::app()->request->getPost('filterbea');
  1046. $overwrite = Yii::app()->request->getPost('overwrite');
  1047. $sFilePath = Yii::app()->getConfig('tempdir') . '/' . basename(Yii::app()->request->getPost('fullfilepath'));
  1048. $errorinupload = "";
  1049. $recordcount = 0;
  1050. $mandatory = 0;
  1051. $mincriteria = 0;
  1052. $imported = 0;
  1053. $dupcount = 0;
  1054. $overwritten = 0;
  1055. $dupreason="nameemail"; //Default duplicate comparison method
  1056. $duplicatelist = array();
  1057. $invalidemaillist = array();
  1058. $invalidformatlist = array();
  1059. $invalidattribute = array();
  1060. $invalidparticipantid = array();
  1061. /* Adjust system settings to read file with MAC line endings */
  1062. @ini_set('auto_detect_line_endings', true);
  1063. /* Open the uploaded file into an array */
  1064. $tokenlistarray = file($sFilePath);
  1065. // open it and trim the endings
  1066. $separator = Yii::app()->request->getPost('seperatorused');
  1067. $uploadcharset = Yii::app()->request->getPost('characterset');
  1068. /* The $newarray contains a list of fields that will be used
  1069. to create new attributes */
  1070. if (!empty($newarray))
  1071. {
  1072. /* Create a new entry in the lime_participant_attribute_names table,
  1073. and it's associated lime_participant_attribute_names_lang table
  1074. for each NEW attribute being created in this import process */
  1075. foreach ($newarray as $key => $value)
  1076. {
  1077. $aData = array('attribute_type' => 'TB', 'attribute_name' => $value, 'visible' => 'FALSE');
  1078. $insertid = ParticipantAttributeNames::model()->storeAttributeCSV($aData);
  1079. /* Keep a record of the attribute_id for this new attribute
  1080. in the $mappedarray string. For example, if the new attribute
  1081. has attribute_id of 35 and is called "gender",
  1082. $mappedarray['35']='gender' */
  1083. $mappedarray[$insertid] = $value;
  1084. }
  1085. }
  1086. if (!isset($uploadcharset))
  1087. {
  1088. $uploadcharset = 'auto';
  1089. }
  1090. foreach ($tokenlistarray as $buffer) //Iterate through the CSV file line by line
  1091. {
  1092. $buffer = @mb_convert_encoding($buffer, "UTF-8", $uploadcharset);
  1093. $firstname = "";
  1094. $lastname = "";
  1095. $email = "";
  1096. $language = "";
  1097. if ($recordcount == 0) {
  1098. //The first time we it…

Large files files are truncated, but you can click here to view the full file