PageRenderTime 42ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/application/controllers/InstallerController.php

https://bitbucket.org/machaven/limesurvey
PHP | 1213 lines | 926 code | 106 blank | 181 comment | 66 complexity | 010540839b80555a6a85ed62957af7db MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, BSD-3-Clause, GPL-3.0, LGPL-3.0
  1. <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
  2. /*
  3. * LimeSurvey (tm)
  4. * Copyright (C) 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. * @author Shubham Sachdeva
  14. */
  15. /**
  16. * Installer
  17. *
  18. * @todo Output code belongs into view
  19. *
  20. * @package LimeSurvey
  21. * @author Shubham Sachdeva
  22. * @copyright 2011
  23. * @version $Id$
  24. * @access public
  25. */
  26. class InstallerController extends CController {
  27. /**
  28. * @var CDbConnection
  29. */
  30. public $connection;
  31. /**
  32. * clang
  33. */
  34. public $lang = null;
  35. /**
  36. * Checks for action specific authorization and then executes an action
  37. *
  38. * @access public
  39. * @param string $action
  40. * @return bool
  41. */
  42. public function run($action = 'index')
  43. {
  44. self::_checkInstallation();
  45. self::_sessioncontrol();
  46. switch ($action) {
  47. case 'welcome':
  48. $this->stepWelcome();
  49. break;
  50. case 'license':
  51. $this->stepLicense();
  52. break;
  53. case 'viewlicense':
  54. $this->stepViewLicense();
  55. break;
  56. case 'precheck':
  57. $this->stepPreInstallationCheck();
  58. break;
  59. case 'database':
  60. $this->stepDatabaseConfiguration();
  61. break;
  62. case 'createdb':
  63. $this->stepCreateDb();
  64. break;
  65. case 'populatedb':
  66. $this->stepPopulateDb();
  67. break;
  68. case 'optional':
  69. $this->stepOptionalConfiguration();
  70. break;
  71. case 'index' :
  72. default :
  73. $this->redirect($this->createUrl('installer/welcome'));
  74. break;
  75. }
  76. }
  77. /**
  78. * Installer::_checkInstallation()
  79. *
  80. * Based on existance of 'sample_installer_file.txt' file, check if
  81. * installation should proceed further or not.
  82. * @return
  83. */
  84. function _checkInstallation()
  85. {
  86. if (file_exists(APPPATH . 'config/config.php') && empty($_POST['InstallerConfigForm']))
  87. {
  88. throw new CHttpException(500, 'Installation has been done already. Installer disabled.');
  89. exit();
  90. }
  91. }
  92. /**
  93. * Load and set session vars
  94. *
  95. * @access protected
  96. * @return void
  97. */
  98. protected function _sessioncontrol()
  99. {
  100. if (empty(Yii::app()->session['installerLang']))
  101. Yii::app()->session['installerLang'] = 'en';
  102. Yii::import('application.libraries.Limesurvey_lang');
  103. $this->lang = new Limesurvey_lang(Yii::app()->session['installerLang']);
  104. Yii::app()->setLang($this->lang);
  105. }
  106. /**
  107. * welcome and language selection install step
  108. */
  109. private function stepWelcome()
  110. {
  111. if (!empty($_POST['installerLang']))
  112. {
  113. Yii::app()->session['installerLang'] = $_POST['installerLang'];
  114. $this->redirect($this->createUrl('installer/license'));
  115. }
  116. $this->loadHelper('surveytranslator');
  117. Yii::app()->session->remove('configFileWritten');
  118. $aData['clang'] = $clang = $this->lang;
  119. $aData['title'] = $clang->gT('Welcome');
  120. $aData['descp'] = $clang->gT('Welcome to the LimeSurvey installation wizard. This wizard will guide you through the installation, database setup and initial configuration of LimeSurvey.');
  121. $aData['classesForStep'] = array('on','off','off','off','off','off');
  122. $aData['progressValue'] = 0;
  123. if (isset(Yii::app()->session['installerLang']))
  124. {
  125. $currentLanguage=Yii::app()->session['installerLang'];
  126. }
  127. else
  128. $currentLanguage='en';
  129. foreach(getLanguageData(true, $currentLanguage) as $langkey => $languagekind)
  130. {
  131. $languages[htmlspecialchars($langkey)] = sprintf('%s - %s', $languagekind['nativedescription'], $languagekind['description']);
  132. }
  133. $aData['languages']=$languages;
  134. $this->render('/installer/welcome_view',$aData);
  135. }
  136. /**
  137. * Display license
  138. */
  139. private function stepLicense()
  140. {
  141. $aData['clang'] = $clang = $this->lang;
  142. // $aData array contain all the information required by view.
  143. $aData['title'] = $clang->gT('License');
  144. $aData['descp'] = $clang->gT('GNU General Public License:');
  145. $aData['classesForStep'] = array('off','on','off','off','off','off');
  146. $aData['progressValue']=0;
  147. if (strtolower($_SERVER['REQUEST_METHOD']) == 'post')
  148. {
  149. $this->redirect($this->createUrl('installer/precheck'));
  150. }
  151. Yii::app()->session['saveCheck'] = 'save'; // Checked in next step
  152. $this->render('/installer/license_view',$aData);
  153. }
  154. /**
  155. * display the license file as IIS for example
  156. * does not display it via the server.
  157. */
  158. public function stepViewLicense()
  159. {
  160. $filename = dirname(BASEPATH) . '/docs/license.txt';
  161. header('Content-Type: text/plain; charset=UTF-8');
  162. readfile($filename);
  163. exit;
  164. }
  165. /**
  166. * check a few writing permissions and optional settings
  167. */
  168. private function stepPreInstallationCheck()
  169. {
  170. $aData['clang'] = $clang = $this->lang;
  171. $model = new InstallerConfigForm();
  172. //usual data required by view
  173. $aData['title'] = $clang->gT('Pre-installation check');
  174. $aData['descp'] = $clang->gT('Pre-installation check for LimeSurvey ').Yii::app()->getConfig('versionnumber');
  175. $aData['classesForStep'] = array('off','off','on','off','off','off');
  176. $aData['progressValue'] = 20;
  177. $aData['phpVersion'] = phpversion();
  178. // variable storing next button link.initially null
  179. $aData['next'] = '';
  180. $aData['dbtypes']=$model->supported_db_types;
  181. $bProceed = $this->_check_requirements($aData);
  182. $aData['dbtypes']=$model->supported_db_types;
  183. if(count($aData['dbtypes'])==0)
  184. {
  185. $bProceed=false;
  186. }
  187. // after all check, if flag value is true, show next button and sabe step2 status.
  188. if ($bProceed)
  189. {
  190. $aData['next'] = true;
  191. Yii::app()->session['step2'] = true;
  192. }
  193. $this->render('/installer/precheck_view',$aData);
  194. }
  195. /**
  196. * Configure database screen
  197. */
  198. private function stepDatabaseConfiguration()
  199. {
  200. $this->loadHelper('surveytranslator');
  201. $aData['clang'] = $clang = $this->lang;
  202. // usual data required by view
  203. $aData['title'] = $clang->gT('Database configuration');
  204. $aData['descp'] = $clang->gT('Please enter the database settings you want to use for LimeSurvey:');
  205. $aData['classesForStep'] = array('off','off','off','on','off','off');
  206. $aData['progressValue'] = 40;
  207. $aData['model'] = $model = new InstallerConfigForm;
  208. if(isset($_POST['InstallerConfigForm']))
  209. {
  210. $model->attributes = $_POST['InstallerConfigForm'];
  211. //run validation, if it fails, load the view again else proceed to next step.
  212. if($model->validate()) {
  213. $sDatabaseType = $model->dbtype;
  214. $sDatabaseName = $model->dbname;
  215. $sDatabaseUser = $model->dbuser;
  216. $sDatabasePwd = $model->dbpwd;
  217. $sDatabasePrefix = $model->dbprefix;
  218. $sDatabaseLocation = $model->dblocation;
  219. $sDatabasePort = '';
  220. if (strpos($sDatabaseLocation, ':')!==false)
  221. {
  222. list($sDatabaseLocation, $sDatabasePort) = explode(':', $sDatabaseLocation, 2);
  223. }
  224. else
  225. {
  226. $sDatabasePort = self::_getDbPort($sDatabaseType, $sDatabasePort);
  227. }
  228. $bDBExists = false;
  229. $bDBConnectionWorks = false;
  230. $aDbConfig = compact('sDatabaseType', 'sDatabaseName', 'sDatabaseUser', 'sDatabasePwd', 'sDatabasePrefix', 'sDatabaseLocation', 'sDatabasePort');
  231. if (self::_dbConnect($aDbConfig, array())) {
  232. $bDBExists = true;
  233. $bDBConnectionWorks = true;
  234. } else {
  235. $aDbConfig['sDatabaseName'] = '';
  236. if (self::_dbConnect($aDbConfig, array())) {
  237. $bDBConnectionWorks = true;
  238. } else {
  239. $model->addError('dblocation', $clang->gT('Connection with database failed. Please check database location, user name and password and try again.'));
  240. $model->addError('dbpwd');
  241. $model->addError('dbuser');
  242. }
  243. }
  244. //if connection with database fail
  245. if ($bDBConnectionWorks)
  246. {
  247. //saving the form data
  248. foreach(array('dbname', 'dbtype', 'dbpwd', 'dbuser', 'dbprefix') as $sStatusKey) {
  249. Yii::app()->session[$sStatusKey] = $model->$sStatusKey;
  250. }
  251. Yii::app()->session['dbport'] = $sDatabasePort;
  252. Yii::app()->session['dblocation'] = $sDatabaseLocation;
  253. //check if table exists or not
  254. $sTestTablename = 'surveys';
  255. $bTablesDoNotExist = false;
  256. // Check if the surveys table exists or not
  257. if ($bDBExists == true) {
  258. try {
  259. $this->connection->createCommand()->select()->from('{{surveys}}')->queryAll();
  260. } catch(Exception $e) {
  261. $bTablesDoNotExist = true;
  262. }
  263. }
  264. $dbexistsbutempty = ($bDBExists && $bTablesDoNotExist);
  265. //store them in session
  266. Yii::app()->session['databaseexist'] = $bDBExists;
  267. Yii::app()->session['tablesexist'] = !$bTablesDoNotExist;
  268. // If database is up to date, redirect to administration screen.
  269. if ($bDBExists && !$bTablesDoNotExist)
  270. {
  271. Yii::app()->session['optconfig_message'] = sprintf('<b>%s</b>', $clang->gT('The database you specified is up to date.'));
  272. Yii::app()->session['step3'] = true;
  273. //wrte config file! as we no longer redirect to optional view
  274. $this->_writeConfigFile();
  275. //$this->redirect($this->createUrl("installer/loadOptView"));
  276. header("refresh:5;url=".$this->createUrl("/admin"));
  277. echo sprintf( $clang->gT('The database you specified is up to date. You\'ll be redirected in 5 seconds. If not, click <a href="%s">here</a>.', 'unescaped'), $this->createUrl("/admin"));
  278. exit();
  279. }
  280. if (in_array($model->dbtype, array('mysql', 'mysqli'))) {
  281. //for development - use mysql in the strictest mode //Checked)
  282. if (Yii::app()->getConfig('debug')>1) {
  283. $this->connection->createCommand("SET SESSION SQL_MODE='STRICT_ALL_TABLES,ANSI'")->execute();
  284. }
  285. $versioninfo = $this->connection->getServerVersion();
  286. if (version_compare($versioninfo,'4.1','<'))
  287. {
  288. die("<br />Error: You need at least MySQL version 4.1 to run LimeSurvey. Your version:".$versioninfo);
  289. }
  290. @$this->connection->createCommand("SET CHARACTER SET 'utf8'")->execute(); //Checked
  291. @$this->connection->createCommand("SET NAMES 'utf8'")->execute(); //Checked
  292. }
  293. // Setting dateformat for mssql driver. It seems if you don't do that the in- and output format could be different
  294. if (in_array($model->dbtype, array('mssql', 'sqlsrv'))) {
  295. @$this->connection->createCommand('SET DATEFORMAT ymd;')->execute(); //Checked
  296. @$this->connection->createCommand('SET QUOTED_IDENTIFIER ON;')->execute(); //Checked
  297. }
  298. //$aData array won't work here. changing the name
  299. $values['title'] = $clang->gT('Database settings');
  300. $values['descp'] = $clang->gT('Database settings');
  301. $values['classesForStep'] = array('off','off','off','off','on','off');
  302. $values['progressValue'] = 60;
  303. //it store text content
  304. $values['adminoutputText'] = '';
  305. //it store the form code to be displayed
  306. $values['adminoutputForm'] = '';
  307. //if DB exist, check if its empty or up to date. if not, tell user LS can create it.
  308. if (!$bDBExists)
  309. {
  310. Yii::app()->session['databaseDontExist'] = true;
  311. $values['adminoutputText'].= "\t<tr bgcolor='#efefef'><td align='center'>\n"
  312. ."<strong>".$clang->gT("Database doesn't exist!")."</strong><br /><br />\n"
  313. .$clang->gT("The database you specified does not exist:")."<br /><br />\n<strong>".$model->dbname."</strong><br /><br />\n"
  314. .$clang->gT("LimeSurvey can attempt to create this database for you.")."<br /><br />\n";
  315. $values['adminoutputForm'] = CHtml::form(array('installer/createdb'), 'post').
  316. "<input type='submit' value='"
  317. .$clang->gT("Create database")."' class='ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only' /></form>";
  318. }
  319. elseif ($dbexistsbutempty) //&& !(returnGlobal('createdbstep2')==$clang->gT("Populate database")))
  320. {
  321. Yii::app()->session['populatedatabase'] = true;
  322. //$this->connection->database = $model->dbname;
  323. // //$this->connection->createCommand("USE DATABASE `".$model->dbname."`")->execute();
  324. $values['adminoutputText'].= sprintf($clang->gT('A database named "%s" already exists.'),$model->dbname)."<br /><br />\n"
  325. .$clang->gT("Do you want to populate that database now by creating the necessary tables?")."<br /><br />";
  326. $values['adminoutputForm'] = CHtml::form(array('installer/populatedb'), 'post')
  327. ."<input class='ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only' type='submit' name='createdbstep2' value='".$clang->gT("Populate database")."' />"
  328. ."</form>";
  329. }
  330. elseif (!$dbexistsbutempty)
  331. {
  332. //DB EXISTS, CHECK FOR APPROPRIATE UPGRADES
  333. //$this->connection->database = $model->dbname;
  334. //$this->connection->createCommand("USE DATABASE `$databasename`")->execute();
  335. /* @todo Implement Upgrade */
  336. //$output=CheckForDBUpgrades();
  337. if ($output== '') {$values['adminoutput'].='<br />'.$clang->gT('LimeSurvey database is up to date. No action needed');}
  338. else {$values['adminoutput'].=$output;}
  339. $values['adminoutput'].= "<br />" . sprintf($clang->gT('Please <a href="%s">log in</a>.', 'unescaped'), $this->createUrl("/admin"));
  340. }
  341. $values['clang'] = $clang;
  342. $this->render('/installer/dbsettings_view', $values);
  343. } else {
  344. $this->render('/installer/dbconfig_view', $aData);
  345. }
  346. } else {
  347. $this->render('/installer/dbconfig_view', $aData);
  348. }
  349. } else {
  350. $this->render('/installer/dbconfig_view', $aData);
  351. }
  352. }
  353. /**
  354. * Installer::stepCreateDb()
  355. * Create database.
  356. * @return
  357. */
  358. function stepCreateDb()
  359. {
  360. // check status. to be called only when database don't exist else rdirect to proper link.
  361. if(!Yii::app()->session['databaseDontExist']) {
  362. $this->redirect($this->createUrl('installer/welcome'));
  363. }
  364. $aData['clang'] = $clang = $this->lang;
  365. $aData['model'] = $model = new InstallerConfigForm;
  366. $aData['title'] = $clang->gT("Database configuration");
  367. $aData['descp'] = $clang->gT("Please enter the database settings you want to use for LimeSurvey:");
  368. $aData['classesForStep'] = array('off','off','off','on','off','off');
  369. $aData['progressValue'] = 40;
  370. $aDbConfig = self::_getDatabaseConfig();
  371. extract($aDbConfig);
  372. // unset database name for connection, since we want to create it and it doesn't already exists
  373. $aDbConfig['sDatabaseName'] = '';
  374. self::_dbConnect($aDbConfig, $aData);
  375. $aData['adminoutputForm'] = '';
  376. // Yii doesn't have a method to create a database
  377. $createDb = true; // We are thinking positive
  378. switch ($sDatabaseType)
  379. {
  380. case 'mysqli':
  381. case 'mysql':
  382. try
  383. {
  384. $this->connection->createCommand("CREATE DATABASE `$sDatabaseName` DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci")->execute();
  385. }
  386. catch(Exception $e)
  387. {
  388. $createDb=false;
  389. }
  390. break;
  391. case 'mssql':
  392. case 'odbc':
  393. try
  394. {
  395. $this->connection->createCommand("CREATE DATABASE [$sDatabaseName];")->execute();
  396. }
  397. catch(Exception $e)
  398. {
  399. $createDb=false;
  400. }
  401. break;
  402. case 'postgres':
  403. try
  404. {
  405. $this->connection->createCommand("CREATE DATABASE \"$sDatabaseName\" ENCODING 'UTF8'")->execute();
  406. }
  407. catch (Exception $e)
  408. {
  409. $createdb = false;
  410. }
  411. break;
  412. default:
  413. try
  414. {
  415. $this->connection->createCommand("CREATE DATABASE $sDatabaseName")->execute();
  416. }
  417. catch(Exception $e)
  418. {
  419. $createDb=false;
  420. }
  421. break;
  422. }
  423. //$this->load->dbforge();
  424. if ($createDb) //Database has been successfully created
  425. {
  426. $sDsn = self::_getDsn($sDatabaseType, $sDatabaseLocation, $sDatabasePort, $sDatabaseName, $sDatabaseUser, $sDatabasePwd);
  427. $this->connection = new CDbConnection($sDsn, $sDatabaseUser, $sDatabasePwd);
  428. Yii::app()->session['populatedatabase'] = true;
  429. Yii::app()->session['databaseexist'] = true;
  430. unset(Yii::app()->session['databaseDontExist']);
  431. $aData['adminoutputText'] = "<tr bgcolor='#efefef'><td colspan='2' align='center'> <br />"
  432. ."<strong><font class='successtitle'>\n"
  433. .$clang->gT("Database has been created.")."</font></strong><br /><br />\n"
  434. .$clang->gT("Please continue with populating the database.")."<br /><br />\n";
  435. $aData['adminoutputForm'] = CHtml::form(array('installer/populatedb'), 'post')
  436. ."<input class='ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only' type='submit' name='createdbstep2' value='".$clang->gT("Populate database")."' /></form>";
  437. }
  438. else
  439. {
  440. $model->addError('dblocation', $clang->gT('Try again! Connection with database failed.'));
  441. $this->render('/installer/dbconfig_view',$aData);
  442. }
  443. $aData['title'] = $clang->gT("Database settings");
  444. $aData['descp'] = $clang->gT("Database settings");
  445. $aData['classesForStep'] = array('off','off','off','off','on','off');
  446. $aData['progressValue'] = 60;
  447. $this->render('/installer/dbsettings_view',$aData);
  448. }
  449. /**
  450. * Installer::stepPopulateDb()
  451. * Function to populate the database.
  452. * @return
  453. */
  454. function stepPopulateDb()
  455. {
  456. if (!Yii::app()->session['populatedatabase'])
  457. {
  458. $this->redirect($this->createUrl('installer/welcome'));
  459. }
  460. $aData['clang'] = $clang = $this->lang;
  461. $aData['model'] = $model = new InstallerConfigForm;
  462. $aData['title'] = $clang->gT("Database configuration");
  463. $aData['descp'] = $clang->gT("Please enter the database settings you want to use for LimeSurvey:");
  464. $aData['classesForStep'] = array('off','off','off','on','off','off');
  465. $aData['progressValue'] = 40;
  466. $aDbConfig = self::_getDatabaseConfig();
  467. extract($aDbConfig);
  468. self::_dbConnect($aDbConfig, $aData);
  469. /* @todo Use Yii as it supports various db types and would better handle this process */
  470. switch ($sDatabaseType)
  471. {
  472. case 'mysqli':
  473. case 'mysql':
  474. $sql_file = 'mysql';
  475. break;
  476. case 'sqlsrv':
  477. case 'mssql':
  478. $sql_file = 'mssql';
  479. break;
  480. case 'pgsql':
  481. $sql_file = 'pgsql';
  482. break;
  483. default:
  484. throw new Exception(sprintf('Unkown database type "%s".', $sDatabaseType));
  485. }
  486. //checking DB Connection
  487. $aErrors = self::_setup_tables(dirname(APPPATH).'/installer/sql/create-'.$sql_file.'.sql');
  488. if ($aErrors === false)
  489. {
  490. $model->addError('dblocation', $clang->gT('Try again! Connection with database failed. Reason: ').implode(', ', $aErrors));
  491. $this->render('/installer/dbconfig_view', $aData);
  492. }
  493. elseif (count($aErrors)==0)
  494. {
  495. //$data1['adminoutput'] = '';
  496. //$data1['adminoutput'] .= sprintf("Database `%s` has been successfully populated.",$dbname)."</font></strong></font><br /><br />\n";
  497. //$data1['adminoutput'] .= "<input type='submit' value='Main Admin Screen' onclick=''>";
  498. $confirmation = sprintf($clang->gT("Database %s has been successfully populated."), sprintf('<b>%s</b>', Yii::app()->session['dbname']));
  499. }
  500. else
  501. {
  502. $confirmation = $clang->gT('Database was populated but there were errors:').'<p><ul>';
  503. foreach ($aErrors as $sError)
  504. {
  505. $confirmation.='<li>'.htmlspecialchars($sError).'</li>';
  506. }
  507. $confirmation.='</ul>';
  508. }
  509. Yii::app()->session['tablesexist'] = true;
  510. Yii::app()->session['step3'] = true;
  511. Yii::app()->session['optconfig_message'] = $confirmation;
  512. unset(Yii::app()->session['populatedatabase']);
  513. $this->redirect($this->createUrl('installer/optional'));
  514. }
  515. /**
  516. * Optional settings screen
  517. */
  518. private function stepOptionalConfiguration()
  519. {
  520. $aData['clang'] = $clang = $this->lang;
  521. $aData['confirmation'] = Yii::app()->session['optconfig_message'];
  522. $aData['title'] = $clang->gT("Optional settings");
  523. $aData['descp'] = $clang->gT("Optional settings to give you a head start");
  524. $aData['classesForStep'] = array('off','off','off','off','off','on');
  525. $aData['progressValue'] = 80;
  526. $this->loadHelper('surveytranslator');
  527. $aData['model'] = $model = new InstallerConfigForm('optional');
  528. if(isset($_POST['InstallerConfigForm']))
  529. {
  530. $model->attributes = $_POST['InstallerConfigForm'];
  531. //run validation, if it fails, load the view again else proceed to next step.
  532. if($model->validate()) {
  533. $adminLoginPwd = $model->adminLoginPwd;
  534. $confirmPwd = $model->confirmPwd;
  535. $defaultuser = $model->adminLoginName;
  536. $defaultpass = $model->adminLoginPwd;
  537. $siteadminname = $model->adminName;
  538. $siteadminbounce = $siteadminemail = $model->adminEmail;
  539. $sitename = $model->siteName;
  540. $defaultlang = $model->surveylang;
  541. $aData['title'] = $clang->gT("Database configuration");
  542. $aData['descp'] = $clang->gT("Please enter the database settings you want to use for LimeSurvey:");
  543. $aData['classesForStep'] = array('off','off','off','on','off','off');
  544. $aData['progressValue'] = 40;
  545. //config file is written, and we've a db in place
  546. $this->connection = Yii::app()->db;
  547. //checking DB Connection
  548. if ($this->connection->getActive() == true) {
  549. $password_hash=hash('sha256', $defaultpass);
  550. try {
  551. $this->connection->createCommand()->insert('{{users}}', array('users_name' => $defaultuser, 'password' => $password_hash, 'full_name' => $siteadminname, 'parent_id' => 0, 'lang' => $defaultlang, 'email' => $siteadminemail, 'create_survey' => 1, 'create_user' => 1, 'participant_panel' => 1, 'delete_user' => 1, 'superadmin' => 1, 'configurator' => 1, 'manage_template' => 1, 'manage_label' => 1));
  552. $this->connection->createCommand()->insert("{{settings_global}}", array('stg_name' => 'SessionName', 'stg_value' => self::_getRandomString()));
  553. foreach(array('sitename', 'siteadminname', 'siteadminemail', 'siteadminbounce', 'defaultlang') as $insert) {
  554. $this->connection->createCommand()->insert("{{settings_global}}", array('stg_name' => $insert, 'stg_value' => $$insert));
  555. }
  556. // only continue if we're error free otherwise setup is broken.
  557. } catch (Exception $e) {
  558. throw new Exception(sprintf('Could not add optional settings: %s.', $e));
  559. }
  560. Yii::app()->session['deletedirectories'] = true;
  561. $aData['title'] = $clang->gT("Success!");
  562. $aData['descp'] = $clang->gT("LimeSurvey has been installed successfully.");
  563. $aData['classesForStep'] = array('off','off','off','off','off','off');
  564. $aData['progressValue'] = 100;
  565. $aData['user'] = $defaultuser;
  566. $aData['pwd'] = $defaultpass;
  567. $this->render('/installer/success_view', $aData);
  568. exit();
  569. }
  570. } else {
  571. // if passwords don't match, redirect to proper link.
  572. Yii::app()->session['optconfig_message'] = sprintf('<b>%s</b>', $clang->gT("Passwords don't match."));
  573. $this->redirect($this->createUrl('installer/optional'));
  574. }
  575. } elseif(empty(Yii::app()->session['configFileWritten'])) {
  576. $this->_writeConfigFile();
  577. }
  578. $this->render('/installer/optconfig_view', $aData);
  579. }
  580. /**
  581. * Loads a helper
  582. *
  583. * @access public
  584. * @param string $helper
  585. * @return void
  586. */
  587. public function loadHelper($helper)
  588. {
  589. Yii::import('application.helpers.' . $helper . '_helper', true);
  590. }
  591. /**
  592. * Loads a library
  593. *
  594. * @access public
  595. * @param string $helper
  596. * @return void
  597. */
  598. public function loadLibrary($library)
  599. {
  600. Yii::import('application.libraries.'.$library, true);
  601. }
  602. /**
  603. * check requirements
  604. *
  605. * @param array $data return theme variables
  606. * @return bool requirements met
  607. */
  608. private function _check_requirements(&$data)
  609. {
  610. // proceed variable check if all requirements are true. If any of them is false, proceed is set false.
  611. $bProceed = true; //lets be optimistic!
  612. /**
  613. * check image HTML template
  614. *
  615. * @param bool $result
  616. */
  617. function check_HTML_image($result)
  618. {
  619. $label = array('wrong', 'right');
  620. return sprintf('<img src="%s/installer/images/tick-%s.png" alt="Found" />', Yii::app()->baseUrl, $label[$result]);
  621. }
  622. function is_writable_recursive($dir)
  623. {
  624. $folder = opendir($dir);
  625. while($file = readdir( $folder ))
  626. if($file != '.' && $file != '..' &&
  627. ( !is_writable( $dir."/".$file ) ||
  628. ( is_dir( $dir."/".$file ) && !is_writable_recursive( $dir."/".$file ) ) ))
  629. {
  630. closedir($folder);
  631. return false;
  632. }
  633. closedir($folder);
  634. return true;
  635. }
  636. /**
  637. * check for a specific PHPFunction, return HTML image
  638. *
  639. * @param string $function
  640. * @param string $image return
  641. * @return bool result
  642. */
  643. function check_PHPFunction($function, &$image)
  644. {
  645. $result = function_exists($function);
  646. $image = check_HTML_image($result);
  647. return $result;
  648. }
  649. /**
  650. * check if file or directory exists and is writeable, returns via parameters by reference
  651. *
  652. * @param string $path file or directory to check
  653. * @param int $type 0:undefined (invalid), 1:file, 2:directory
  654. * @param string $data to manipulate
  655. * @param string $base key for data manipulation
  656. * @param string $keyError key for error data
  657. * @return bool result of check (that it is writeable which implies existance)
  658. */
  659. function check_PathWriteable($path, $type, &$data, $base, $keyError, $bRecursive=false)
  660. {
  661. $result = false;
  662. $data[$base.'Present'] = 'Not Found';
  663. $data[$base.'Writable'] = '';
  664. switch($type) {
  665. case 1:
  666. $exists = is_file($path);
  667. break;
  668. case 2:
  669. $exists = is_dir($path);
  670. break;
  671. default:
  672. throw new Exception('Invalid type given.');
  673. }
  674. if ($exists)
  675. {
  676. $data[$base.'Present'] = 'Found';
  677. if ((!$bRecursive && is_writable($path)) || ($bRecursive && is_writable_recursive($path)))
  678. {
  679. $data[$base.'Writable'] = 'Writable';
  680. $result = true;
  681. }
  682. else
  683. {
  684. $data[$base.'Writable'] = 'Unwritable';
  685. }
  686. }
  687. $result || $data[$keyError] = true;
  688. return $result;
  689. }
  690. /**
  691. * check if file exists and is writeable, returns via parameters by reference
  692. *
  693. * @param string $file to check
  694. * @param string $data to manipulate
  695. * @param string $base key for data manipulation
  696. * @param string $keyError key for error data
  697. * @return bool result of check (that it is writeable which implies existance)
  698. */
  699. function check_FileWriteable($file, &$data, $base, $keyError)
  700. {
  701. return check_PathWriteable($file, 1, $data, $base, $keyError);
  702. }
  703. /**
  704. * check if directory exists and is writeable, returns via parameters by reference
  705. *
  706. * @param string $directory to check
  707. * @param string $data to manipulate
  708. * @param string $base key for data manipulation
  709. * @param string $keyError key for error data
  710. * @return bool result of check (that it is writeable which implies existance)
  711. */
  712. function check_DirectoryWriteable($directory, &$data, $base, $keyError, $bRecursive=false)
  713. {
  714. return check_PathWriteable($directory, 2, $data, $base, $keyError, $bRecursive);
  715. }
  716. // version check
  717. if (version_compare(PHP_VERSION, '5.1.6', '<'))
  718. $bProceed = !$data['verror'] = true;
  719. // mbstring library check
  720. if (!check_PHPFunction('mb_convert_encoding', $data['mbstringPresent']))
  721. $bProceed = false;
  722. // JSON library check
  723. if (!check_PHPFunction('json_encode', $data['bJSONPresent']))
  724. $bProceed = false;
  725. // ** file and directory permissions checking **
  726. // config directory
  727. if (!check_DirectoryWriteable(Yii::app()->getConfig('rootdir').'/application/config', $data, 'config', 'derror') )
  728. $bProceed = false;
  729. // templates directory check
  730. if (!check_DirectoryWriteable(Yii::app()->getConfig('tempdir').'/', $data, 'tmpdir', 'tperror',true) )
  731. $bProceed = false;
  732. //upload directory check
  733. if (!check_DirectoryWriteable(Yii::app()->getConfig('uploaddir').'/', $data, 'uploaddir', 'uerror',true) )
  734. $bProceed = false;
  735. // Session writable check
  736. $session = Yii::app()->session; /* @var $session CHttpSession */
  737. $sessionWritable = ($session->get('saveCheck', null)==='save');
  738. $data['sessionWritable'] = $sessionWritable;
  739. $data['sessionWritableImg'] = check_HTML_image($sessionWritable);
  740. if (!$sessionWritable){
  741. // For recheck, try to set the value again
  742. $session['saveCheck'] = 'save';
  743. $bProceed = false;
  744. }
  745. // ** optional settings check **
  746. // gd library check
  747. if (function_exists('gd_info')) {
  748. $data['gdPresent'] = check_HTML_image(array_key_exists('FreeType Support', gd_info()));
  749. } else {
  750. $data['gdPresent'] = check_HTML_image(false);
  751. }
  752. // ldap library check
  753. check_PHPFunction('ldap_connect', $data['ldapPresent']);
  754. // php zip library check
  755. check_PHPFunction('zip_open', $data['zipPresent']);
  756. // zlib php library check
  757. check_PHPFunction('zlib_get_coding_type', $data['zlibPresent']);
  758. // imap php library check
  759. check_PHPFunction('imap_open', $data['bIMAPPresent']);
  760. return $bProceed;
  761. }
  762. /**
  763. * Installer::_setup_tables()
  764. * Function that actually modify the database. Read $sqlfile and execute it.
  765. * @param string $sqlfile
  766. * @return Empty string if everything was okay - otherwise the error messages
  767. */
  768. function _setup_tables($sFileName, $aDbConfig = array(), $sDatabasePrefix = '')
  769. {
  770. extract(empty($aDbConfig) ? self::_getDatabaseConfig() : $aDbConfig);
  771. switch ($sDatabaseType) {
  772. case 'mysql':
  773. case 'mysqli':
  774. $this->connection->createCommand("ALTER DATABASE ". $this->connection->quoteTableName($sDatabaseName) ." DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;")->execute();
  775. break;
  776. case 'pgsql':
  777. if (version_compare($this->connection->getServerVersion(),'9','>=')) {
  778. $this->connection->createCommand("ALTER DATABASE ". $this->connection->quoteTableName($sDatabaseName) ." SET bytea_output='escape';")->execute();
  779. }
  780. break;
  781. }
  782. return $this->_executeSQLFile($sFileName, $sDatabasePrefix);
  783. }
  784. /**
  785. * Executes an SQL file
  786. *
  787. * @param string $sFileName
  788. * @param string $sDatabasePrefix
  789. */
  790. function _executeSQLFile($sFileName, $sDatabasePrefix)
  791. {
  792. $aMessages = array();
  793. $sCommand = '';
  794. if (!is_readable($sFileName)) {
  795. return false;
  796. } else {
  797. $aLines = file($sFileName);
  798. }
  799. foreach ($aLines as $sLine) {
  800. $sLine = rtrim($sLine);
  801. $iLineLength = strlen($sLine);
  802. if ($iLineLength && $sLine[0] != '#' && substr($sLine,0,2) != '--') {
  803. if (substr($sLine, $iLineLength-1, 1) == ';') {
  804. $line = substr($sLine, 0, $iLineLength-1);
  805. $sCommand .= $sLine;
  806. $sCommand = str_replace('prefix_', $sDatabasePrefix, $sCommand); // Table prefixes
  807. try {
  808. $this->connection->createCommand($sCommand)->execute();
  809. } catch(Exception $e) {
  810. $aMessages[] = "Executing: ".$sCommand." failed! Reason: ".$e;
  811. }
  812. $sCommand = '';
  813. } else {
  814. $sCommand .= $sLine;
  815. }
  816. }
  817. }
  818. return $aMessages;
  819. }
  820. /**
  821. * Function to write given database settings in APPPATH.'config/config.php'
  822. */
  823. function _writeConfigFile()
  824. {
  825. $aData['clang'] = $clang = $this->lang;
  826. //write config.php if database exists and has been populated.
  827. if (Yii::app()->session['databaseexist'] && Yii::app()->session['tablesexist'])
  828. {
  829. extract(self::_getDatabaseConfig());
  830. $sDsn = self::_getDsn($sDatabaseType, $sDatabaseLocation, $sDatabasePort, $sDatabaseName, $sDatabaseUser, $sDatabasePwd);
  831. // mod_rewrite existence check
  832. // Section commented out until a better method of knowing whether the mod_rewrite actually
  833. // works is found. In the meantime, it is better to set $showScriptName to 'true' so it
  834. // works on all installations, and allow users to change it manually later.
  835. //if ((function_exists('apache_get_modules') && in_array('mod_rewrite', apache_get_modules())) || strtolower(getenv('HTTP_MOD_REWRITE')) == 'on')
  836. //{
  837. // $showScriptName = 'false';
  838. //}
  839. //else
  840. //{
  841. $showScriptName = 'true';
  842. //}
  843. if (stripos($_SERVER['SERVER_SOFTWARE'], 'apache') !== false)
  844. {
  845. $sURLFormat='path';
  846. }
  847. else
  848. {
  849. $sURLFormat='get'; // Fall back to get if an Apache server cannot be determined reliably
  850. }
  851. $dbdata = "<?php if (!defined('BASEPATH')) exit('No direct script access allowed');" . "\n"
  852. ."/*"."\n"
  853. ."| -------------------------------------------------------------------"."\n"
  854. ."| DATABASE CONNECTIVITY SETTINGS"."\n"
  855. ."| -------------------------------------------------------------------"."\n"
  856. ."| This file will contain the settings needed to access your database."."\n"
  857. ."|"."\n"
  858. ."| For complete instructions please consult the 'Database Connection'" ."\n"
  859. ."| page of the User Guide."."\n"
  860. ."|"."\n"
  861. ."| -------------------------------------------------------------------"."\n"
  862. ."| EXPLANATION OF VARIABLES"."\n"
  863. ."| -------------------------------------------------------------------"."\n"
  864. ."|" ."\n"
  865. ."| 'connectionString' Hostname, database, port and database type for " ."\n"
  866. ."| the connection. Driver example: mysql. Currently supported:" ."\n"
  867. ."| mysql, pgsql, mssql, sqlite, oci" ."\n"
  868. ."| 'username' The username used to connect to the database" ."\n"
  869. ."| 'password' The password used to connect to the database" ."\n"
  870. ."| 'tablePrefix' You can add an optional prefix, which will be added" ."\n"
  871. ."| to the table name when using the Active Record class" ."\n"
  872. ."|" ."\n"
  873. ."*/" ."\n"
  874. . "return array(" . "\n"
  875. ."\t" . "'basePath' => dirname(dirname(__FILE__))," . "\n"
  876. ."\t" . "'runtimePath' => dirname(dirname(dirname(__FILE__))).DIRECTORY_SEPARATOR.'tmp'.DIRECTORY_SEPARATOR.'runtime'," . "\n"
  877. ."\t" . "'name' => 'LimeSurvey'," . "\n"
  878. ."\t" . "'defaultController' => 'survey'," . "\n"
  879. ."\t" . "" . "\n"
  880. ."\t" . "'import' => array(" . "\n"
  881. ."\t\t" . "'application.core.*'," . "\n"
  882. ."\t\t" . "'application.models.*'," . "\n"
  883. ."\t\t" . "'application.controllers.*'," . "\n"
  884. ."\t\t" . "'application.modules.*'," . "\n"
  885. ."\t" . ")," . "\n"
  886. ."\t" . "" . "\n"
  887. ."\t" . "'components' => array(" . "\n"
  888. ."\t\t" . "'db' => array(" . "\n"
  889. ."\t\t\t" . "'connectionString' => '$sDsn'," . "\n";
  890. if ($sDatabaseType!='sqlsrv')
  891. {
  892. $dbdata .="\t\t\t" . "'emulatePrepare' => true," . "\n";
  893. }
  894. $dbdata .="\t\t\t" . "'username' => '$sDatabaseUser'," . "\n"
  895. ."\t\t\t" . "'password' => '$sDatabasePwd'," . "\n"
  896. ."\t\t\t" . "'charset' => 'utf8'," . "\n"
  897. ."\t\t\t" . "'tablePrefix' => '$sDatabasePrefix'," . "\n";
  898. if (in_array($sDatabaseType, array('mssql', 'sqlsrv'))) {
  899. $dbdata .="\t\t\t" ."'initSQLs'=>array('SET DATEFORMAT ymd;','SET QUOTED_IDENTIFIER ON;')," . "\n";
  900. }
  901. $dbdata .="\t\t" . ")," . "\n"
  902. ."\t\t" . "" . "\n"
  903. ."\t\t" . "// Uncomment the following line if you need table-based sessions". "\n"
  904. ."\t\t" . "// 'session' => array (" . "\n"
  905. ."\t\t\t" . "// 'class' => 'system.web.CDbHttpSession'," . "\n"
  906. ."\t\t\t" . "// 'connectionID' => 'db'," . "\n"
  907. ."\t\t\t" . "// 'sessionTableName' => '{{sessions}}'," . "\n"
  908. ."\t\t" . "// )," . "\n"
  909. ."\t\t" . "" . "\n"
  910. /** @todo Uncomment after implementing the error controller */
  911. /*
  912. ."\t\t" . "'errorHandler' => array(" . "\n"
  913. ."\t\t\t" . "'errorAction' => 'error'," . "\n"
  914. ."\t\t" . ")," . "\n"
  915. ."\t\t" . "" . "\n"
  916. */
  917. ."\t\t" . "'urlManager' => array(" . "\n"
  918. ."\t\t\t" . "'urlFormat' => '{$sURLFormat}'," . "\n"
  919. ."\t\t\t" . "'rules' => require('routes.php')," . "\n"
  920. ."\t\t\t" . "'showScriptName' => $showScriptName," . "\n"
  921. ."\t\t" . ")," . "\n"
  922. ."\t" . "" . "\n"
  923. ."\t" . ")," . "\n"
  924. ."\t" . "// Use the following config variable to set modified optional settings copied from config-defaults.php". "\n"
  925. ."\t" . "'config'=>array(" . "\n"
  926. ."\t" . "// debug: Set this to 1 if you are looking for errors. If you still get no errors after enabling this". "\n"
  927. ."\t" . "// then please check your error-logs - either in your hosting provider admin panel or in some /logs directory". "\n"
  928. ."\t" . "// on your webspace.". "\n"
  929. ."\t" . "// LimeSurvey developers: Set this to 2 to additionally display STRICT PHP error messages and get full access to standard templates". "\n"
  930. ."\t\t" . "'debug'=>0," . "\n"
  931. ."\t\t" . "'debugsql'=>0 // Set this to 1 to enanble sql logging, only active when debug = 2" . "\n"
  932. ."\t" . ")" . "\n"
  933. . ");" . "\n"
  934. . "/* End of file config.php */" . "\n"
  935. . "/* Location: ./application/config/config.php */";
  936. if (is_writable(APPPATH . 'config')) {
  937. file_put_contents(APPPATH . 'config/config.php', $dbdata);
  938. Yii::app()->session['configFileWritten'] = true;
  939. $oUrlManager = Yii::app()->getComponent('urlManager');
  940. /* @var $oUrlManager CUrlManager */
  941. $oUrlManager->setUrlFormat($sURLFormat);
  942. } else {
  943. header('refresh:5;url='.$this->createUrl("installer/welcome"));
  944. echo "<b>".$clang->gT("Configuration directory is not writable")."</b><br/>";
  945. printf($clang->gT('You will be redirected in about 5 secs. If not, click <a href="%s">here</a>.' ,'unescaped'), $this->createUrl('installer/welcome'));
  946. exit;
  947. }
  948. }
  949. }
  950. /**
  951. * Create a random ASCII string
  952. *
  953. * @return string
  954. */
  955. function _getRandomString()
  956. {
  957. $totalChar = 64; // number of chars in the sid
  958. $sResult='';
  959. for ($i=0;$i<$totalChar;$i++)
  960. {
  961. $sResult.=chr(rand(33,126));
  962. }
  963. return $sResult;
  964. }
  965. /**
  966. * Get the dsn for the database connection
  967. *
  968. * @param string $sDatabaseType
  969. * @param string $sDatabasePort
  970. */
  971. function _getDsn($sDatabaseType, $sDatabaseLocation, $sDatabasePort, $sDatabaseName, $sDatabaseUser, $sDatabasePwd)
  972. {
  973. switch ($sDatabaseType) {
  974. case 'mysql':
  975. case 'mysqli':
  976. $dsn = "mysql:host={$sDatabaseLocation};port={$sDatabasePort};dbname={$sDatabaseName};";
  977. break;
  978. case 'pgsql':
  979. if (empty($sDatabasePwd))
  980. {
  981. // If there's no password, we need to write password=""; instead of password=;,
  982. // or PostgreSQL's libpq will consider the DSN string part after "password="
  983. // (including the ";" and the potential dbname) as part of the password definition.
  984. $sDatabasePwd = '""';
  985. }
  986. $dsn = "pgsql:host={$sDatabaseLocation};port={$sDatabasePort};user={$sDatabaseUser};password={$sDatabasePwd};";
  987. if ($sDatabaseName!='')
  988. {
  989. $dsn.="dbname={$sDatabaseName};";
  990. }
  991. break;
  992. case 'mssql' :
  993. case 'sqlsrv':
  994. if ($sDatabasePort!=''){$sDatabaseLocation=$sDatabaseLocation.','.$sDatabasePort;}
  995. $dsn = $sDatabaseType.":Server={$sDatabaseLocation};Database={$sDatabaseName}";
  996. break;
  997. default:
  998. throw new Exception(sprintf('Unknown database type "%s".', $sDatabaseType));
  999. }
  1000. return $dsn;
  1001. }
  1002. /**
  1003. * Get the default port if database port is not set
  1004. *
  1005. * @param string $sDatabaseType
  1006. * @param string $sDatabasePort
  1007. * @return string
  1008. */
  1009. function _getDbPort($sDatabaseType, $sDatabasePort = '')
  1010. {
  1011. if (is_numeric($sDatabasePort))
  1012. return $sDatabasePort;
  1013. switch ($sDatabaseType) {
  1014. case 'mysql':
  1015. case 'mysqli':
  1016. $sDatabasePort = '3306';
  1017. break;
  1018. case 'pgsql':
  1019. $sDatabasePort = '5432';
  1020. break;
  1021. case 'mssql' :
  1022. case 'sqlsrv':
  1023. default:
  1024. $sDatabasePort = '';
  1025. }
  1026. return $sDatabasePort;
  1027. }
  1028. /**
  1029. * Gets the database configuration from the session
  1030. *
  1031. * @return array Database Config
  1032. */
  1033. function _getDatabaseConfig()
  1034. {
  1035. $sDatabaseType = Yii::app()->session['dbtype'];
  1036. $sDatabasePort = Yii::app()->session['dbport'];
  1037. $sDatabaseName = Yii::app()->session['dbname'];
  1038. $sDatabaseUser = Yii::app()->session['dbuser'];
  1039. $sDatabasePwd = Yii::app()->session['dbpwd'];
  1040. $sDatabasePrefix = Yii::app()->session['dbprefix'];
  1041. $sDatabaseLocation = Yii::app()->session['dblocation'];
  1042. return compact('sDatabaseLocation', 'sDatabaseName', 'sDatabasePort', 'sDatabasePrefix', 'sDatabasePwd', 'sDatabaseType', 'sDatabaseUser');
  1043. }
  1044. /**
  1045. * Connect to the database
  1046. *
  1047. * Throw an error if there's an error
  1048. */
  1049. function _dbConnect($aDbConfig = array(), $aData = array())
  1050. {
  1051. extract(empty($aDbConfig) ? self::_getDatabaseConfig() : $aDbConfig);
  1052. $sDsn = self::_getDsn($sDatabaseType, $sDatabaseLocation, $sDatabasePort, $sDatabaseName, $sDatabaseUser, $sDatabasePwd);
  1053. $sDatabaseName = empty($sDatabaseName) ? '' : $sDatabaseName;
  1054. $sDatabasePort = empty($sDatabasePort) ? '' : $sDatabasePort;
  1055. try {
  1056. $this->connection = new CDbConnection($sDsn, $sDatabaseUser, $sDatabasePwd);
  1057. if($sDatabaseType!='sqlsrv'){
  1058. $this->connection->emulatePrepare = true;
  1059. }
  1060. $this->connection->active = true;
  1061. $this->connection->tablePrefix = $sDatabasePrefix;
  1062. return true;
  1063. } catch(Exception $e) {
  1064. if (!empty($aData['model']) && !empty($aData['clang'])) {
  1065. $aData['model']->addError('dblocation', $aData['clang']->gT('Try again! Connection with database failed. Reason: ') . $e->message);
  1066. $this->render('/installer/dbconfig_view', $aData);
  1067. } else {
  1068. return false;
  1069. }
  1070. }
  1071. }
  1072. }