PageRenderTime 111ms CodeModel.GetById 39ms RepoModel.GetById 7ms app.codeStats 1ms

/application/controllers/admin/participantsaction.php

https://bitbucket.org/machaven/limesurvey
PHP | 1753 lines | 1316 code | 161 blank | 276 comment | 187 complexity | 76093fb7fbbe8384582aa539524a4a48 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()->deleteParticipant($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. $surveyname = Surveys_languagesettings::model()->getSurveyNames($row['survey_id']);
  544. $surveylink = "";
  545. /* Check permissions of each survey before creating a link*/
  546. if (!hasSurveyPermission($row['survey_id'], 'tokens', 'read'))
  547. {
  548. $surveylink = $row['survey_id'];
  549. } else
  550. {
  551. $surveylink = '<a href=' . Yii::app()->getController()->createUrl("/admin/tokens/sa/browse/surveyid/{$row['survey_id']}") . '>' . $row['survey_id'].'</a>';
  552. }
  553. $aData->rows[$i]['cell'] = array($surveyname[0]['surveyls_title'], $surveylink, $row['token_id'], $row['date_created'], $row['date_invited'], $row['date_completed']);
  554. $i++;
  555. }
  556. echo ls_json_encode($aData);
  557. }
  558. /**
  559. * Returns the count of the participants in the CSV and show it in the title of the modal box
  560. * This is to give the user the hint to see the number of participants he is exporting
  561. */
  562. function exporttocsvcount()
  563. {
  564. $searchconditionurl = Yii::app()->request->getPost('searchcondition');
  565. $searchcondition = basename($searchconditionurl);
  566. if ($searchcondition != 'getParticipants_json') // if there is a search condition then only the participants that match the search criteria are counted
  567. {
  568. $condition = explode("||", $searchcondition);
  569. $search = Participants::model()->getParticipantsSearchMultipleCondition($condition);
  570. } else {
  571. $search = null;
  572. }
  573. echo $this->csvExportCount($search);
  574. }
  575. /**
  576. * Outputs the count of participants when using the export all button on the top
  577. */
  578. function exporttocsvcountAll()
  579. {
  580. echo $this->csvExportCount();
  581. }
  582. /**
  583. * Responsible to export all the participants in the central table
  584. */
  585. function exporttocsvAll()
  586. {
  587. $this->csvExport(); // no search
  588. }
  589. /**
  590. * Similar to export to all message where it counts the number to participants to be copied
  591. * and echo them to be displayed in modal box header
  592. */
  593. function getaddtosurveymsg()
  594. {
  595. $searchcondition = basename(Yii::app()->request->getPost('searchcondition'));
  596. // If there is a search condition in the url of the jqGrid
  597. if ($searchcondition != 'getParticipants_json')
  598. {
  599. $participantid = "";
  600. $condition = explode("||", $searchcondition);
  601. $query = Participants::model()->getParticipantsSearchMultiple($condition, 0, 0);
  602. printf( $this->getController()->lang->gT("%s participant(s) are to be copied "), count($query));
  603. }
  604. // if there is no search condition the participants will be counted on the basis of who is logged in
  605. else
  606. {
  607. if (Yii::app()->session['USER_RIGHT_SUPERADMIN']) //If super admin all the participants will be visible
  608. {
  609. $count = Participants::model()->getParticipantsCountWithoutLimit();
  610. }
  611. else
  612. {
  613. $query = Participants::model()->getParticipantsOwner(Yii::app()->session['loginID']);
  614. $count = count($query);
  615. }
  616. printf($this->getController()->lang->gT("%s participant(s) are to be copied "), $count);
  617. }
  618. }
  619. /**
  620. * Gets the ids of participants to be copied to the individual survey
  621. */
  622. function getSearchIDs()
  623. {
  624. $searchcondition = basename(Yii::app()->request->getPost('searchcondition')); // get the search condition from the URL
  625. /* a search contains posted data inside $_POST['searchcondition'].
  626. * Each seperate query is made up of 3 fields, seperated by double-pipes ("|")
  627. * EG: fname||eq||jason||lname||ct||c
  628. *
  629. */
  630. if ($searchcondition != 'getParticipants_json') // if there is a search condition present
  631. {
  632. $participantid = "";
  633. $condition = explode("||", $searchcondition); // explode the condition to the array
  634. $query = Participants::model()->getParticipantsSearchMultiple($condition, 0, 0);
  635. foreach ($query as $key => $value)
  636. {
  637. if (Yii::app()->session['USER_RIGHT_SUPERADMIN'])
  638. {
  639. $participantid .= "," . $value['participant_id']; // combine the participant id's in an string
  640. } else
  641. {
  642. if(Participants::model()->is_owner($value['participant_id']))
  643. {
  644. $participantid .= "," . $value['participant_id']; // combine the participant id's in an string
  645. }
  646. }
  647. }
  648. echo $participantid; //echo the participant id's
  649. }
  650. else// if no search condition
  651. {
  652. $participantid = ""; // initiallise the participant id to blank
  653. if (Yii::app()->session['USER_RIGHT_SUPERADMIN']) //If super admin all the participants will be visible
  654. {
  655. $query = Participants::model()->getParticipantsWithoutLimit(); // get all the participant id if it is a super admin
  656. }
  657. else // get participants on which the user has right on
  658. {
  659. $query = Participants::model()->getParticipantsOwner(Yii::app()->session['loginID']);
  660. }
  661. foreach ($query as $key => $value)
  662. {
  663. $participantid = $participantid . "," . $value['participant_id']; // combine the participant id's in an string
  664. }
  665. echo $participantid; //echo the participant id's
  666. }
  667. }
  668. /**
  669. * Responsible for reading the CSV file line by line, check for duplicate participants
  670. * invalid participants and invalid attributes and copy them to the central table
  671. * Also responsible for creation of new attribute and mapping of old attribute to attribute in csv
  672. */
  673. function exporttocsv()
  674. {
  675. $searchconditionurl = Yii::app()->request->getPost('searchcondition');
  676. $searchcondition = basename($searchconditionurl);
  677. if ($searchcondition != 'getParticipants_json') // if there is a search condition then only the participants that match the search criteria are counted
  678. {
  679. $condition = explode("||", $searchcondition);
  680. $search = Participants::model()->getParticipantsSearchMultipleCondition($condition);
  681. } else {
  682. $search = null;
  683. }
  684. $this->csvExport($search);
  685. }
  686. /**
  687. * Equal to getParticipants_json() but now with a search
  688. */
  689. function getParticipantsResults_json()
  690. {
  691. $searchcondition = Yii::app()->request->getQuery('search');
  692. $searchcondition = urldecode($searchcondition);
  693. $finalcondition = array();
  694. $condition = explode("||", $searchcondition);
  695. $search = Participants::model()->getParticipantsSearchMultipleCondition($condition);
  696. return $this->getParticipants_json($search);
  697. }
  698. /*
  699. * Sends the data in JSON format extracted from the database to be displayed using the jqGrid
  700. */
  701. function getParticipants_json($search = null)
  702. {
  703. $page = Yii::app()->request->getPost('page');
  704. $limit = Yii::app()->request->getPost('rows');
  705. $limit = isset($limit) ? $limit : 50; //Stop division by zero errors
  706. $attid = ParticipantAttributeNames::model()->getVisibleAttributes();
  707. $participantfields = array('participant_id', 'can_edit', 'firstname', 'lastname', 'email', 'blacklisted', 'survey', 'language', 'owner_uid');
  708. foreach ($attid as $key => $value)
  709. {
  710. array_push($participantfields, $value['attribute_id']);
  711. }
  712. $sidx = Yii::app()->request->getPost('sidx');
  713. $sidx = !empty($sidx) ? $sidx : "lastname";
  714. $sord = Yii::app()->request->getPost('sord');
  715. $sord = !empty($sord) ? $sord : "asc";
  716. $order = $sidx. " ". $sord;
  717. $aData = new stdClass;
  718. //If super admin all the participants will be visible
  719. if (Yii::app()->session['USER_RIGHT_SUPERADMIN'])
  720. {
  721. $iUserID = null;
  722. } else {
  723. $iUserID = Yii::app()->session['loginID'];
  724. }
  725. $aData->records = Participants::model()->getParticipantsCount($attid, $search, $iUserID);
  726. $aData->total = ceil($aData->records / $limit);
  727. if ($page>$aData->total) {
  728. $page = $aData->total;
  729. }
  730. $aData->page = $page;
  731. $records = Participants::model()->getParticipants($page, $limit,$attid, $order, $search, $iUserID);
  732. $aRowToAdd=array();
  733. foreach ($records as $key => $row)
  734. {
  735. if (array_key_exists('can_edit', $row)) {
  736. $sCanEdit = $row['can_edit'];
  737. if (is_null($sCanEdit)) {
  738. $sCanEdit = 'true';
  739. }
  740. } else {
  741. // Super admin
  742. $sCanEdit = "true";
  743. }
  744. $aRowToAdd['cell'] = array($row['participant_id'], $sCanEdit, $row['firstname'], $row['lastname'], $row['email'], $row['blacklisted'], $row['survey'], $row['language'], $row['ownername']);
  745. $aRowToAdd['id'] = $row['participant_id'];
  746. unset($row['participant_id'], $row['firstname'], $row['lastname'], $row['email'], $row['blacklisted'], $row['language'],$row['ownername'],$row['owner_uid'], $row['can_edit'], $row['survey']);
  747. foreach($row as $key=>$attvalue)
  748. {
  749. $aRowToAdd['cell'][] = $attvalue;
  750. }
  751. $aData->rows[] = $aRowToAdd;
  752. }
  753. echo ls_json_encode($aData);
  754. }
  755. /*
  756. * Fetches the attributes of a participant to be displayed in the attribute subgrid
  757. */
  758. function getAttribute_json()
  759. {
  760. $iParticipantId = Yii::app()->request->getQuery('pid');
  761. $records = ParticipantAttributeNames::model()->getParticipantVisibleAttribute($iParticipantId);
  762. //$getallattributes = ParticipantAttributeNames::model()->with('participant_attribute_names_lang')->findAll();
  763. $records = subval_sort($records, "attribute_name", "asc");
  764. $i = 0;
  765. $doneattributes = array(); //If the user has any actual attribute values, they'll be stored here
  766. /* Iterate through each attribute owned by this user */
  767. foreach ($records as $row)
  768. {
  769. $outputs[$i] = array("", $row['participant_id']."_".$row['attribute_id'], $row['attribute_type'], $row['attribute_id'], $row['attribute_name'], $row['value']);
  770. /* Collect allowed values for a DropDown attribute */
  771. if ($row['attribute_type'] == "DD")
  772. {
  773. $attvalues = ParticipantAttributeNames::model()->getAttributesValues($row['attribute_id']);
  774. if (!empty($attvalues))
  775. {
  776. $attval = "";
  777. foreach ($attvalues as $val)
  778. {
  779. $attval .= $val['value'] . ":" . $val['value'];
  780. $attval .= ";";
  781. }
  782. $attval = substr($attval, 0, -1);
  783. array_push($outputs[$i], $attval);
  784. }
  785. else
  786. {
  787. array_push($outputs[$i], "");
  788. }
  789. }
  790. else
  791. {
  792. array_push($outputs[$i], "");
  793. }
  794. array_push($doneattributes, $row['attribute_id']);
  795. $i++;
  796. }
  797. /* Build a list of attribute names for which this user has NO values stored, keep it in $attributenotdone */
  798. $attributenotdone=array();
  799. /* The user has NO values stored against any attribute */
  800. if (count($doneattributes) == 0)
  801. {
  802. $attributenotdone = ParticipantAttributeNames::model()->getAttributes();
  803. }
  804. /* The user has SOME values stored against attributes */
  805. else
  806. {
  807. $attributenotdone = ParticipantAttributeNames::model()->getnotaddedAttributes($doneattributes);
  808. }
  809. /* Go through the empty attributes and build an entry in the output for them */
  810. foreach ($attributenotdone as $row)
  811. {
  812. $outputs[$i] = array("", $iParticipantId."_".$row['attribute_id'], $row['attribute_type'], $row['attribute_id'], $row['attribute_name'], "");
  813. if ($row['attribute_type'] == "DD")
  814. {
  815. $attvalues = ParticipantAttributeNames::model()->getAttributesValues($row['attribute_id']);
  816. if (!empty($attvalues))
  817. {
  818. $attval = "";
  819. foreach ($attvalues as $val)
  820. {
  821. $attval .= $val['value'] . ":" . $val['value'];
  822. $attval .= ";";
  823. }
  824. $attval = substr($attval, 0, -1);
  825. array_push($outputs[$i], $attval);
  826. }
  827. else
  828. {
  829. array_push($outputs[$i], "");
  830. }
  831. }
  832. else
  833. {
  834. array_push($outputs[$i], "");
  835. }
  836. $i++;
  837. }
  838. $outputs=subval_sort($outputs, 3, "asc");
  839. $aData = new stdClass();
  840. $aData->page = 1;
  841. $aData->rows[0]['id'] = $iParticipantId;
  842. $aData->rows[0]['cell'] = array();
  843. $aData->records = count($outputs);
  844. $aData->total = ceil($aData->records / 10);
  845. foreach($outputs as $key=>$output) {
  846. $aData->rows[$key]['id']=$output[1];
  847. $aData->rows[$key]['cell']=$output;
  848. }
  849. /* TODO: It'd be nice to do a natural sort on the attribute list at some point.
  850. Currently they're returned in order of attributes WITH values, then WITHOUT values
  851. */
  852. echo ls_json_encode($aData);
  853. }
  854. /*
  855. * Gets the data from the form for add participants and pass it to the participants model
  856. */
  857. function storeParticipants()
  858. {
  859. $aData = array('participant_id' => uniqid(),
  860. 'firstname' => Yii::app()->request->getPost('firstname'),
  861. 'lastname' => Yii::app()->request->getPost('lastname'),
  862. 'email' => Yii::app()->request->getPost('email'),
  863. 'language' => Yii::app()->request->getPost('language'),
  864. 'blacklisted' => Yii::app()->request->getPost('blacklisted'),
  865. 'owner_uid' => Yii::app()->request->getPost('owner_uid'));
  866. Participants::model()->insertParticipant($aData);
  867. }
  868. /*
  869. * Responsible for showing the additional attribute for central database
  870. */
  871. function viewAttribute()
  872. {
  873. $iAttributeId = Yii::app()->request->getQuery('aid');
  874. $aData = array(
  875. 'attributes' => ParticipantAttributeNames::model()->getAttribute($iAttributeId),
  876. 'attributenames' => ParticipantAttributeNames::model()->getAttributeNames($iAttributeId),
  877. 'attributevalues' => ParticipantAttributeNames::model()->getAttributesValues($iAttributeId)
  878. );
  879. $this->getController()->_css_admin_includes(Yii::app()->getConfig('adminstyleurl') . 'participants.css');
  880. $this->getController()->_css_admin_includes(Yii::app()->getConfig('adminstyleurl') . 'viewAttribute.css');
  881. $this->_renderWrappedTemplate('participants', array('participantsPanel', 'viewAttribute'), $aData);
  882. }
  883. /*
  884. * Responsible for saving the additional attribute. It iterates through all the new attributes added dynamically
  885. * and iterates through them
  886. */
  887. function saveAttribute()
  888. {
  889. $iAttributeId = Yii::app()->request->getQuery('aid');
  890. $aData = array(
  891. 'attribute_id' => $iAttributeId,
  892. 'attribute_type' => Yii::app()->request->getPost('attribute_type'),
  893. 'visible' => Yii::app()->request->getPost('visible')
  894. );
  895. ParticipantAttributeNames::model()->saveAttribute($aData);
  896. foreach ($_POST as $key => $value)
  897. {
  898. // check for language code in the post variables this is a hack as the only way to check for language data
  899. if (strlen($key) == 2)
  900. {
  901. $langdata = array(
  902. 'attribute_id' => $iAttributeId,
  903. 'attribute_name' => $value,
  904. 'lang' => $key
  905. );
  906. ParticipantAttributeNames::model()->saveAttributeLanguages($langdata);
  907. }
  908. }
  909. if (Yii::app()->request->getPost('langdata'))
  910. {
  911. $langdata = array(
  912. 'attribute_id' => $iAttributeId,
  913. 'attribute_name' => Yii::app()->request->getPost('attname'),
  914. 'lang' => Yii::app()->request->getPost('langdata')
  915. );
  916. ParticipantAttributeNames::model()->saveAttributeLanguages($langdata);
  917. }
  918. /* Create new attribute value */
  919. if (Yii::app()->request->getPost('attribute_value_name_1') || Yii::app()->request->getPost('attribute_value_name_1') == "0")
  920. {
  921. $i = 1;
  922. $attvaluename = 'attribute_value_name_' . $i;
  923. while (array_key_exists($attvaluename, $_POST) && $_POST[$attvaluename] != "")
  924. {
  925. if ($_POST[$attvaluename] != "")
  926. {
  927. $aDatavalues[$i] = array(
  928. 'attribute_id' => $iAttributeId,
  929. 'value' => Yii::app()->request->getPost($attvaluename)
  930. );
  931. }
  932. $attvaluename = 'attribute_value_name_' . ++$i;
  933. };
  934. ParticipantAttributeNames::model()->storeAttributeValues($aDatavalues);
  935. }
  936. /* Save updated attribute values */
  937. if (Yii::app()->request->getPost('editbox') || Yii::app()->request->getPost('editbox')=="0")
  938. {
  939. $editattvalue = array(
  940. 'attribute_id' => $iAttributeId,
  941. 'value_id' => Yii::app()->request->getPost('value_id'),
  942. 'value' => Yii::app()->request->getPost('editbox')
  943. );
  944. ParticipantAttributeNames::model()->saveAttributeValue($editattvalue);
  945. }
  946. Yii::app()->getController()->redirect(Yii::app()->getController()->createUrl('admin/participants/sa/attributeControl'));
  947. }
  948. /*
  949. * Responsible for deleting the additional attribute values in case of drop down.
  950. */
  951. function delAttributeValues()
  952. {
  953. $iAttributeId = Yii::app()->request->getQuery('aid');
  954. $iValueId = Yii::app()->request->getQuery('vid');
  955. ParticipantAttributeNames::model()->delAttributeValues($iAttributeId, $iValueId);
  956. Yii::app()->getController()->redirect(Yii::app()->getController()->createUrl('/admin/participants/sa/viewAttribute/aid/' . $iAttributeId));
  957. }
  958. /*
  959. * Responsible for editing the additional attributes values
  960. */
  961. function editAttributevalue()
  962. {
  963. if (Yii::app()->request->getPost('oper') == "edit" && (Yii::app()->request->getPost('attvalue') || Yii::app()->request->getPost('attvalue')=="0"))
  964. {
  965. $pid = explode('_',Yii::app()->request->getPost('participant_id'));
  966. $iAttributeId = Yii::app()->request->getPost('attid');
  967. $aData = array('participant_id' => $pid[0], 'attribute_id' => $iAttributeId, 'value' => Yii::app()->request->getPost('attvalue'));
  968. ParticipantAttributeNames::model()->editParticipantAttributeValue($aData);
  969. }
  970. }
  971. function attributeMapCSV()
  972. {
  973. $config['upload_path'] = './tmp/upload';
  974. $config['allowed_types'] = 'text/x-csv|text/plain|application/octet-stream|csv';
  975. $config['max_size'] = '1000';
  976. $clang = $this->getController()->lang;
  977. $sFilePath = preg_replace('/\\\/', '/', Yii::app()->getConfig('tempdir')) . "/" . $_FILES['the_file']['name'];
  978. $bMoveFileResult = @move_uploaded_file($_FILES['the_file']['tmp_name'], $sFilePath);
  979. $errorinupload = '';
  980. $filterblankemails = Yii::app()->request->getPost('filterbea');
  981. if (!$bMoveFileResult)
  982. {
  983. $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'));
  984. $errorinupload = array('error' => $this->upload->display_errors());
  985. Yii::app()->session['summary'] = array('errorinupload' => $errorinupload);
  986. $this->_renderWrappedTemplate('participants', array('participantsPanel', 'uploadSummary'));
  987. }
  988. else
  989. {
  990. $aData = array('upload_data' => $_FILES['the_file']);
  991. $sFileName = $_FILES['the_file']['name'];
  992. $regularfields = array('firstname', 'participant_id', 'lastname', 'email', 'language', 'blacklisted', 'owner_uid');
  993. $csvread = fopen($sFilePath, 'r');
  994. $seperator = Yii::app()->request->getPost('seperatorused');
  995. $firstline = fgetcsv($csvread, 1000, ',');
  996. $selectedcsvfields = array();
  997. foreach ($firstline as $key => $value)
  998. {
  999. $testvalue = preg_replace('/[^(\x20-\x7F)]*/','', $value); //Remove invalid characters from string
  1000. if (!in_array(strtolower($testvalue), $regularfields))
  1001. {
  1002. array_push($selectedcsvfields, $value);
  1003. }
  1004. $fieldlist[]=$value;
  1005. }
  1006. $linecount = count(file($sFilePath));
  1007. $attributes = ParticipantAttributeNames::model()->model()->getAttributes();
  1008. $aData = array(
  1009. 'attributes' => $attributes,
  1010. 'firstline' => $selectedcsvfields,
  1011. 'fullfilepath' => $sFilePath,
  1012. 'linecount' => $linecount - 1,
  1013. 'filterbea' => $filterblankemails,
  1014. 'participant_id_exists' => in_array('participant_id', $fieldlist)
  1015. );
  1016. $this->_renderWrappedTemplate('participants', 'attributeMapCSV', $aData);
  1017. }
  1018. }
  1019. /*
  1020. * Uploads the file to the server and process it for valid enteries and import them into database
  1021. */
  1022. function uploadCSV()
  1023. {
  1024. unset(Yii::app()->session['summary']);
  1025. $characterset = Yii::app()->request->getPost('characterset');
  1026. $seperator = Yii::app()->request->getPost('seperatorused');
  1027. $newarray = Yii::app()->request->getPost('newarray');
  1028. $mappedarray = Yii::app()->request->getPost('mappedarray');
  1029. $sFilePath = Yii::app()->request->getPost('fullfilepath');
  1030. $filterblankemails = Yii::app()->request->getPost('filterbea');
  1031. $overwrite = Yii::app()->request->getPost('overwrite');
  1032. $errorinupload = "";
  1033. $recordcount = 0;
  1034. $mandatory = 0;
  1035. $mincriteria = 0;
  1036. $imported = 0;
  1037. $dupcount = 0;
  1038. $overwritten = 0;
  1039. $dupreason="nameemail"; //Default duplicate comparison method
  1040. $duplicatelist = array();
  1041. $invalidemaillist = array();
  1042. $invalidformatlist = array();
  1043. $invalidattribute = array();
  1044. $invalidparticipantid = array();
  1045. /* Adjust system settings to read file with MAC line endings */
  1046. @ini_set('auto_detect_line_endings', true);
  1047. /* Open the uploaded file into an array */
  1048. $tokenlistarray = file($sFilePath);
  1049. // open it and trim the endings
  1050. $separator = Yii::app()->request->getPost('seperatorused');
  1051. $uploadcharset = Yii::app()->request->getPost('characterset');
  1052. /* The $newarray contains a list of fields that will be used
  1053. to create new attributes */
  1054. if (!empty($newarray))
  1055. {
  1056. /* Create a new entry in the lime_participant_attribute_names table,
  1057. and it's associated lime_participant_attribute_names_lang table
  1058. for each NEW attribute being created in this import process */
  1059. foreach ($newarray as $key => $value)
  1060. {
  1061. $aData = array('attribute_type' => 'TB', 'attribute_name' => $value, 'visible' => 'FALSE');
  1062. $insertid = ParticipantAttributeNames::model()->storeAttributeCSV($aData);
  1063. /* Keep a record of the attribute_id for this new attribute
  1064. in the $mappedarray string. For example, if the new attribute
  1065. has attribute_id of 35 and is called "gender",
  1066. $mappedarray['35']='gender' */
  1067. $mappedarray[$insertid] = $value;
  1068. }
  1069. }
  1070. if (!isset($uploadcharset))
  1071. {
  1072. $uploadcharset = 'auto';
  1073. }
  1074. foreach ($tokenlistarray as $buffer) //Iterate through the CSV file line by line
  1075. {
  1076. $buffer = @mb_convert_encoding($buffer, "UTF-8", $uploadcharset);
  1077. $firstname = "";
  1078. $lastname = "";
  1079. $email = "";
  1080. $language = "";
  1081. if ($recordcount == 0) {
  1082. //The first time we iterate through the file we look at the very
  1083. //first line, which contains field names, not values to import
  1084. // Pick apart the first line
  1085. $buffer = removeBOM($buffer);
  1086. $attrid = ParticipantAttributeNames::model()->getAttributeID();
  1087. $allowedfieldnames = array('participant_id', 'firstname', 'lastname', 'email', 'language', 'blacklisted');
  1088. if (!empty($mappedarray))
  1089. {
  1090. foreach ($mappedarray as $key => $value)
  1091. {
  1092. array_push($allowedfieldnames, $value);
  1093. }
  1094. }
  1095. //For Attributes
  1096. switch ($separator)
  1097. {
  1098. case 'comma':
  1099. $separator = ',';

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