PageRenderTime 57ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/application/controllers/InstallerController.php

https://github.com/alexgeertsen/LimeSurvey
PHP | 1270 lines | 961 code | 104 blank | 205 comment | 73 complexity | 1aca3559f20fc77dbcf2e402a7c9b8d3 MD5 | raw file
Possible License(s): LGPL-2.1, Apache-2.0, GPL-3.0, GPL-2.0, BSD-3-Clause, MIT

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

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