PageRenderTime 58ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/setup/includes/modinstall.class.php

https://github.com/krisj/revolution
PHP | 904 lines | 627 code | 72 blank | 205 comment | 139 complexity | f90c9808f99c94979f4ca929708b1820 MD5 | raw file
Possible License(s): AGPL-1.0, LGPL-2.1, GPL-2.0, GPL-3.0, LGPL-2.0
  1. <?php
  2. /*
  3. * MODx Revolution
  4. *
  5. * Copyright 2006, 2007, 2008, 2009, 2010 by the MODx Team.
  6. * All rights reserved.
  7. *
  8. * This program is free software; you can redistribute it and/or modify it under
  9. * the terms of the GNU General Public License as published by the Free Software
  10. * Foundation; either version 2 of the License, or (at your option) any later
  11. * version.
  12. *
  13. * This program is distributed in the hope that it will be useful, but WITHOUT
  14. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  15. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  16. * details.
  17. *
  18. * You should have received a copy of the GNU General Public License along with
  19. * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
  20. * Place, Suite 330, Boston, MA 02111-1307 USA
  21. */
  22. /**
  23. * Common classes for the MODx installation and provisioning services.
  24. *
  25. * @package setup
  26. */
  27. /**
  28. * Provides common functionality and data for installation and provisioning.
  29. *
  30. * @package setup
  31. */
  32. class modInstall {
  33. const MODE_NEW = 0;
  34. const MODE_UPGRADE_REVO = 1;
  35. const MODE_UPGRADE_EVO = 2;
  36. const MODE_UPGRADE_REVO_ADVANCED = 3;
  37. public $xpdo = null;
  38. public $options = array ();
  39. public $config = array ();
  40. public $action = '';
  41. public $lexicon = null;
  42. public $finished = false;
  43. /**
  44. * The constructor for the modInstall object.
  45. *
  46. * @constructor
  47. * @param array $options An array of configuration options.
  48. */
  49. function __construct(array $options = array()) {
  50. if (isset ($_REQUEST['action'])) {
  51. $this->action = $_REQUEST['action'];
  52. }
  53. if (is_array($options)) {
  54. $this->options = $options;
  55. }
  56. }
  57. /**
  58. * Loads the request handler for the setup.
  59. * @return boolean True if successful.
  60. */
  61. public function loadRequestHandler($class = 'modInstallRequest') {
  62. $path = dirname(__FILE__).'/'.strtolower($class).'.class.php';
  63. $included = @include $path;
  64. if ($included) {
  65. $this->request = new $class($this);
  66. } else {
  67. $this->_fatalError($this->lexicon('request_handler_err_nf',array('path' => $path)));
  68. }
  69. return $included;
  70. }
  71. /**
  72. * Load settings class
  73. *
  74. * @access public
  75. * @param string $class The settings class to load.
  76. * @return boolean True if successful.
  77. */
  78. public function loadSettings($class = 'modInstallSettings') {
  79. $path = dirname(__FILE__).'/'.strtolower($class).'.class.php';
  80. $included = @include_once $path;
  81. if ($included) {
  82. $this->settings = new $class($this);
  83. } else {
  84. $this->_fatalError($this->lexicon('settings_handler_err_nf',array('path' => $path)));
  85. }
  86. return $included;
  87. }
  88. /**
  89. * Loads the lexicon class for the install process.
  90. *
  91. * @param string $class The class name of the lexicon class to use.
  92. * @return boolean True if successful.
  93. */
  94. public function loadLexicon($class = 'modInstallLexicon') {
  95. $path = dirname(__FILE__).'/'.strtolower($class).'.class.php';
  96. $included = @include $path;
  97. $this->lexicon = new modInstallLexicon($this);
  98. return $included;
  99. }
  100. /**
  101. * Shortcut method for modInstallLexicon::get. {@see modInstallLexicon::get}
  102. */
  103. public function lexicon($key,array $placeholders = array()) {
  104. return $this->lexicon->get($key,$placeholders);
  105. }
  106. /**
  107. * Get the existing or create a new configuration.
  108. *
  109. * @param integer $mode The install mode.
  110. * @param array $config An array of config attributes.
  111. * @return array A copy of the config attributes array.
  112. */
  113. public function getConfig($mode = 0, $config = array ()) {
  114. global $database_dsn, $database_type, $database_server, $dbase, $database_user,
  115. $database_password, $database_connection_charset, $table_prefix;
  116. if (!is_array($config)) {
  117. $config = array ();
  118. }
  119. /* get http host */
  120. $https_port = isset ($_POST['httpsport']) ? $_POST['httpsport'] : '443';
  121. $isSecureRequest = ((isset ($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') || $_SERVER['SERVER_PORT'] == $https_port);
  122. $http_host= $_SERVER['HTTP_HOST'];
  123. if ($_SERVER['SERVER_PORT'] != 80) {
  124. $http_host= str_replace(':' . $_SERVER['SERVER_PORT'], '', $http_host); /* remove port from HTTP_HOST */
  125. }
  126. $http_host .= ($_SERVER['SERVER_PORT'] == 80 || $isSecureRequest) ? '' : ':' . $_SERVER['SERVER_PORT'];
  127. switch ($mode) {
  128. case modInstall::MODE_UPGRADE_EVO :
  129. $included = @ include MODX_INSTALL_PATH . 'manager/includes/config.inc.php';
  130. if ($included && isset ($dbase))
  131. break;
  132. case modInstall::MODE_UPGRADE_REVO :
  133. case modInstall::MODE_UPGRADE_REVO_ADVANCED :
  134. $included = @ include MODX_CORE_PATH . 'config/' . MODX_CONFIG_KEY . '.inc.php';
  135. if ($included && isset ($dbase)) {
  136. $config['mgr_path'] = MODX_MANAGER_PATH;
  137. $config['connectors_path'] = MODX_CONNECTORS_PATH;
  138. $config['web_path'] = MODX_BASE_PATH;
  139. $config['context_mgr_path'] = MODX_MANAGER_PATH;
  140. $config['context_connectors_path'] = MODX_CONNECTORS_PATH;
  141. $config['context_web_path'] = MODX_BASE_PATH;
  142. $config['mgr_url'] = MODX_MANAGER_URL;
  143. $config['connectors_url'] = MODX_CONNECTORS_URL;
  144. $config['web_url'] = MODX_BASE_URL;
  145. $config['context_mgr_url'] = MODX_MANAGER_URL;
  146. $config['context_connectors_url'] = MODX_CONNECTORS_URL;
  147. $config['context_web_url'] = MODX_BASE_URL;
  148. $config['core_path'] = MODX_CORE_PATH;
  149. $config['processors_path'] = MODX_CORE_PATH.'model/modx/processors/';
  150. $config['assets_path'] = MODX_ASSETS_PATH;
  151. $config['assets_url'] = MODX_ASSETS_URL;
  152. break;
  153. }
  154. default :
  155. $included = false;
  156. $database_type = isset ($_POST['databasetype']) ? $_POST['databasetype'] : 'mysql';
  157. $database_server = isset ($_POST['databasehost']) ? $_POST['databasehost'] : 'localhost';
  158. $database_user = isset ($_POST['databaseloginname']) ? $_POST['databaseloginname'] : '';
  159. $database_password = isset ($_POST['databaseloginpassword']) ? $_POST['databaseloginpassword'] : '';
  160. $dbase = isset ($_POST['database_name']) ? $_POST['database_name'] : 'modx';
  161. $table_prefix = isset ($_POST['tableprefix']) ? $_POST['tableprefix'] : 'modx_';
  162. $https_port = isset ($_POST['httpsport']) ? $_POST['httpsport'] : '443';
  163. $cache_disabled = isset ($_POST['cache_disabled']) ? $_POST['cache_disabled'] : 'false';
  164. $site_sessionname = 'SN' . uniqid('');
  165. break;
  166. }
  167. $config = array_merge($config,array(
  168. 'database_type' => $database_type,
  169. 'database_server' => $database_server,
  170. 'dbase' => trim($dbase, '`[]'),
  171. 'database_user' => $database_user,
  172. 'database_password' => $database_password,
  173. 'database_connection_charset' => $database_connection_charset,
  174. 'database_charset' => $database_connection_charset,
  175. 'table_prefix' => $table_prefix,
  176. 'https_port' => isset ($https_port) ? $https_port : '443',
  177. 'http_host' => defined('MODX_HTTP_HOST') ? MODX_HTTP_HOST : $http_host,
  178. 'site_sessionname' => isset ($site_sessionname) ? $site_sessionname : 'SN' . uniqid(''),
  179. 'cache_disabled' => isset ($cache_disabled) && $cache_disabled ? 'true' : 'false',
  180. 'inplace' => isset ($_POST['inplace']) ? 1 : 0,
  181. 'unpacked' => isset ($_POST['unpacked']) ? 1 : 0,
  182. ));
  183. $this->config = array_merge($this->config, $config);
  184. switch ($this->config['database_type']) {
  185. case 'sqlsrv':
  186. $database_dsn = $this->config['database_dsn'] = "{$this->config['database_type']}:server={$this->config['database_server']};database={$this->config['dbase']}";
  187. break;
  188. case 'mysql':
  189. $database_dsn = $this->config['database_dsn'] = "{$this->config['database_type']}:host={$this->config['database_server']};dbname={$this->config['dbase']};charset={$this->config['database_connection_charset']}";
  190. break;
  191. default:
  192. break;
  193. }
  194. return $this->config;
  195. }
  196. /**
  197. * Get an xPDO connection to the database.
  198. *
  199. * @return xPDO A copy of the xpdo object.
  200. */
  201. public function getConnection($mode = modInstall::MODE_NEW) {
  202. if ($mode === modInstall::MODE_UPGRADE_REVO) {
  203. $errors = array ();
  204. $this->xpdo = $this->_modx($errors);
  205. } else if (!is_object($this->xpdo)) {
  206. $options = array();
  207. if ($this->settings->get('new_folder_permissions')) $options['new_folder_permissions'] = $this->settings->get('new_folder_permissions');
  208. if ($this->settings->get('new_file_permissions')) $options['new_file_permissions'] = $this->settings->get('new_file_permissions');
  209. $this->xpdo = $this->_connect(
  210. $this->settings->get('database_dsn')
  211. ,$this->settings->get('database_user')
  212. ,$this->settings->get('database_password')
  213. ,$this->settings->get('table_prefix')
  214. ,$options
  215. );
  216. if (!($this->xpdo instanceof xPDO)) { return $this->xpdo; }
  217. $this->xpdo->setOption('cache_path',MODX_CORE_PATH . 'cache/');
  218. if ($mode === modInstall::MODE_UPGRADE_REVO_ADVANCED) {
  219. if ($this->xpdo->connect()) {
  220. $errors = array ();
  221. $this->xpdo = $this->_modx($errors);
  222. } else {
  223. return $this->lexicon('db_err_connect_upgrade');
  224. }
  225. }
  226. }
  227. if (is_object($this->xpdo) && $this->xpdo instanceof xPDO) {
  228. $this->xpdo->setLogTarget(array(
  229. 'target' => 'FILE',
  230. 'options' => array(
  231. 'filename' => 'install.' . MODX_CONFIG_KEY . '.' . strftime('%Y-%m-%dT%H:%M:%S')
  232. )
  233. ));
  234. $this->xpdo->setLogLevel(xPDO::LOG_LEVEL_ERROR);
  235. $this->xpdo->setPackage('modx', MODX_CORE_PATH . 'model/', $this->settings->get('table_prefix'));
  236. }
  237. return $this->xpdo;
  238. }
  239. /**
  240. * Load distribution-specific test handlers
  241. */
  242. public function loadTestHandler($class = 'modInstallTest') {
  243. $path = dirname(__FILE__).'/'.strtolower($class).'.class.php';
  244. $included = @include $path;
  245. if ($included) {
  246. $this->lexicon->load('test');
  247. $class = $class.ucfirst(trim(MODX_SETUP_KEY, '@'));
  248. $versionPath = dirname(__FILE__).'/checks/'.strtolower($class).'.class.php';
  249. $included = @include $versionPath;
  250. if (!$included) {
  251. $this->_fatalError($this->lexicon('test_version_class_nf',array('path' => $versionPath)));
  252. }
  253. $this->test = new $class($this);
  254. return $this->test;
  255. } else {
  256. $this->_fatalError($this->lexicon('test_class_nf',array('path' => $path)));
  257. }
  258. }
  259. /**
  260. * Perform a series of pre-installation tests.
  261. *
  262. * @param integer $mode The install mode.
  263. * @param string $test_class The class to run tests with
  264. * @return array An array of result messages collected during the process.
  265. */
  266. public function test($mode = modInstall::MODE_NEW,$test_class = 'modInstallTest') {
  267. $test = $this->loadTestHandler($test_class);
  268. $results = $this->test->run($mode);
  269. return $results;
  270. }
  271. /**
  272. * Load version-specific installer.
  273. *
  274. * @access public
  275. * @param string $class The class to load.
  276. */
  277. public function loadVersionInstaller($class = 'modInstallVersion') {
  278. $path = dirname(__FILE__).'/'.strtolower($class).'.class.php';
  279. $included = @include $path;
  280. if ($included) {
  281. $this->versioner = new $class($this);
  282. return $this->versioner;
  283. } else {
  284. $this->_fatalError($this->lexicon('versioner_err_nf',array('path' => $path)));
  285. }
  286. }
  287. /**
  288. * Execute the installation process.
  289. *
  290. * @param integer $mode The install mode.
  291. * @return array An array of result messages collected during execution.
  292. */
  293. public function execute($mode) {
  294. $results = array ();
  295. /* set the time limit infinite in case it takes a bit
  296. * TODO: fix this by allowing resume when it takes a long time
  297. */
  298. @ set_time_limit(0);
  299. @ ini_set('max_execution_time', 240);
  300. @ ini_set('memory_limit','128M');
  301. /* write config file */
  302. $this->writeConfig($results);
  303. /* get connection */
  304. $this->getConnection($mode);
  305. /* run appropriate database routines */
  306. switch ($mode) {
  307. /* TODO: MODx Evolution to Revolution migration */
  308. case modInstall::MODE_UPGRADE_EVO :
  309. $results = include MODX_SETUP_PATH . 'includes/tables_migrate.php';
  310. break;
  311. /* revo-alpha+ upgrades */
  312. case modInstall::MODE_UPGRADE_REVO :
  313. case modInstall::MODE_UPGRADE_REVO_ADVANCED :
  314. $this->loadVersionInstaller();
  315. $results = $this->versioner->install();
  316. break;
  317. /* new install, create tables */
  318. default :
  319. $results = include MODX_SETUP_PATH . 'includes/tables_create.php';
  320. break;
  321. }
  322. if ($this->xpdo) {
  323. /* add required core data */
  324. $this->xpdo->loadClass('transport.xPDOTransport', XPDO_CORE_PATH, true, true);
  325. $packageDirectory = MODX_CORE_PATH . 'packages/';
  326. $packageState = $this->settings->get('unpacked') == 1 ? xPDOTransport::STATE_UNPACKED : xPDOTransport::STATE_PACKED;
  327. $package = xPDOTransport :: retrieve($this->xpdo, $packageDirectory . 'core.transport.zip', $packageDirectory, $packageState);
  328. if (!is_object($package) || !($package instanceof xPDOTransport)) {
  329. $results[] = array (
  330. 'class' => 'failed',
  331. 'msg' => '<p class="notok">'.$this->lexicon('package_execute_err_retrieve',array('path' => $this->settings->get('core_path'))).'</p>'
  332. );
  333. return $results;
  334. }
  335. if (!defined('MODX_BASE_PATH'))
  336. define('MODX_BASE_PATH', $this->settings->get('context_web_path'));
  337. if (!defined('MODX_ASSETS_PATH'))
  338. define('MODX_ASSETS_PATH', $this->settings->get('context_assets_path'));
  339. if (!defined('MODX_MANAGER_PATH'))
  340. define('MODX_MANAGER_PATH', $this->settings->get('context_mgr_path'));
  341. if (!defined('MODX_CONNECTORS_PATH'))
  342. define('MODX_CONNECTORS_PATH', $this->settings->get('context_connectors_path'));
  343. $package->install(array (
  344. xPDOTransport::RESOLVE_FILES => ($this->settings->get('inplace') == 0 ? 1 : 0)
  345. ,xPDOTransport::INSTALL_FILES => ($this->settings->get('inplace') == 0 ? 1 : 0)
  346. , xPDOTransport::PREEXISTING_MODE => xPDOTransport::REMOVE_PREEXISTING
  347. ));
  348. /* set default workspace path */
  349. $workspace = $this->xpdo->getObject('modWorkspace', array (
  350. 'active' => 1
  351. ));
  352. if ($workspace) {
  353. $path = $workspace->get('path');
  354. if (!empty($path)) {
  355. $path = trim($path);
  356. }
  357. if (empty ($path) || !file_exists($path)) {
  358. $workspace->set('path', MODX_CORE_PATH);
  359. if (!$workspace->save()) {
  360. $results[] = array (
  361. 'class' => 'error',
  362. 'msg' => '<p class="notok">'.$this->lexicon('workspace_err_path').'</p>'
  363. );
  364. } else {
  365. $results[] = array (
  366. 'class' => 'success',
  367. 'msg' => '<p class="ok">'.$this->lexicon('workspace_path_updated').'</p>'
  368. );
  369. }
  370. }
  371. } else {
  372. $results[] = array (
  373. 'class' => 'error',
  374. 'msg' => '<p class="notok">'.$this->lexicon('workspace_err_nf').'</p>'
  375. );
  376. }
  377. unset($workspace);
  378. $modx =& $this->xpdo;
  379. /* if new install */
  380. if ($mode == modInstall::MODE_NEW) {
  381. include MODX_SETUP_PATH.'includes/new.install.php';
  382. /* if upgrade */
  383. } else {
  384. include MODX_SETUP_PATH.'includes/upgrade.install.php';
  385. }
  386. /* empty sessions table to prevent old permissions from loading */
  387. $tableName = $this->xpdo->getTableName('modSession');
  388. $this->xpdo->exec($this->driver->truncate($tableName));
  389. /* clear cache */
  390. $this->xpdo->cacheManager->deleteTree(MODX_CORE_PATH.'cache/',array(
  391. 'skipDirs' => false,
  392. 'extensions' => array(
  393. '.cache.php',
  394. '.tpl.php',
  395. ),
  396. ));
  397. $this->settings->store(array(
  398. 'finished' => true,
  399. ));
  400. }
  401. return $results;
  402. }
  403. /**
  404. * Verify that the modX class can be initialized.
  405. *
  406. * @param integer $mode Indicates the installation mode.
  407. * @return array An array of error messages collected during the process.
  408. */
  409. public function verify() {
  410. $errors = array ();
  411. $modx = $this->_modx($errors);
  412. if (is_object($modx) && $modx instanceof modX) {
  413. if ($modx->getCacheManager()) {
  414. $modx->cacheManager->clearCache(array(), array(
  415. 'objects' => '*',
  416. 'publishing' => 1
  417. ));
  418. }
  419. }
  420. return $errors;
  421. }
  422. /**
  423. * Cleans up after install.
  424. *
  425. * TODO: implement this function to cleanup any temporary files
  426. * @param array $options
  427. */
  428. public function cleanup(array $options = array ()) {
  429. $errors = array();
  430. $modx = $this->_modx($errors);
  431. if (empty($modx) || !($modx instanceof modX)) {
  432. $errors['modx_class'] = $this->lexicon('modx_err_instantiate');
  433. return $errors;
  434. }
  435. /* create the directories for Package Management */
  436. $cacheManager = $modx->getCacheManager();
  437. $directoryOptions = array(
  438. 'new_folder_permissions' => $modx->getOption('new_folder_permissions',null,0775),
  439. );
  440. /* create assets/ */
  441. $assetsPath = $modx->getOption('base_path').'assets/';
  442. if (!is_dir($assetsPath)) {
  443. $cacheManager->writeTree($assetsPath,$directoryOptions);
  444. }
  445. if (!is_dir($assetsPath) || !$this->is_writable2($assetsPath)) {
  446. $errors['assets_not_created'] = str_replace('[[+path]]',$assetsPath,$this->lexicon('setup_err_assets'));
  447. }
  448. unset($assetsPath);
  449. /* create assets/components/ */
  450. $assetsCompPath = $modx->getOption('base_path').'assets/components/';
  451. if (!is_dir($assetsCompPath)) {
  452. $cacheManager->writeTree($assetsCompPath,$directoryOptions);
  453. }
  454. if (!is_dir($assetsCompPath) || !$this->is_writable2($assetsCompPath)) {
  455. $errors['assets_comp_not_created'] = str_replace('[[+path]]',$assetsCompPath,$this->lexicon('setup_err_assets_comp'));
  456. }
  457. unset($assetsCompPath);
  458. /* create core/components/ */
  459. $coreCompPath = $modx->getOption('core_path').'components/';
  460. if (!is_dir($coreCompPath)) {
  461. $cacheManager->writeTree($coreCompPath,$directoryOptions);
  462. }
  463. if (!is_dir($coreCompPath) || !$this->is_writable2($coreCompPath)) {
  464. $errors['core_comp_not_created'] = str_replace('[[+path]]',$coreCompPath,$this->lexicon('setup_err_core_comp'));
  465. }
  466. unset($coreCompPath);
  467. return $errors;
  468. }
  469. /**
  470. * Removes the setup directory
  471. *
  472. * @access publics
  473. */
  474. public function removeSetupDirectory(array $options = array()) {
  475. $errors = array();
  476. $modx = $this->_modx($errors);
  477. if ($modx) {
  478. $cacheManager = $modx->getCacheManager();
  479. if ($cacheManager) {
  480. $setupPath = $modx->getOption('base_path').'setup/';
  481. if (!$cacheManager->deleteTree($setupPath,true,false,false)) {
  482. $modx->log(modX::LOG_LEVEL_ERROR,$this->lexicon('setup_err_remove'));
  483. }
  484. } else {
  485. $modx->log(modX::LOG_LEVEL_ERROR,$this->lexicon('cache_manager_err'));
  486. }
  487. } else {
  488. $modx->log(modX::LOG_LEVEL_ERROR,$this->lexicon('modx_object_err'));
  489. }
  490. return $errors;
  491. }
  492. /**
  493. * Writes the config file.
  494. *
  495. * @param array $results An array of result messages.
  496. * @return boolean Returns true if successful; false otherwise.
  497. */
  498. public function writeConfig(array &$results) {
  499. $written = false;
  500. $configTpl = MODX_CORE_PATH . 'docs/config.inc.tpl';
  501. $configFile = MODX_CORE_PATH . 'config/' . MODX_CONFIG_KEY . '.inc.php';
  502. $settings = $this->settings->fetch();
  503. $settings['last_install_time'] = time();
  504. $settings['site_id'] = uniqid('modx',true);
  505. /* make UUID if not set */
  506. if (empty($settings['uuid'])) {
  507. $settings['uuid'] = $this->generateUUID();
  508. }
  509. if (file_exists($configTpl)) {
  510. if ($tplHandle = @ fopen($configTpl, 'rb')) {
  511. $content = @ fread($tplHandle, filesize($configTpl));
  512. @ fclose($tplHandle);
  513. if ($content) {
  514. $replace = array ();
  515. while (list ($key, $value) = each($settings)) {
  516. $replace['{' . $key . '}'] = "{$value}";
  517. }
  518. $content = str_replace(array_keys($replace), array_values($replace), $content);
  519. if ($configHandle = @ fopen($configFile, 'wb')) {
  520. $written = @ fwrite($configHandle, $content);
  521. @ fclose($configHandle);
  522. }
  523. }
  524. }
  525. }
  526. $perms = $this->settings->get('new_file_permissions', sprintf("%04o", 0666 & (0666 - umask())));
  527. if (is_string($perms)) $perms = octdec($perms);
  528. $chmodSuccess = @ chmod($configFile, $perms);
  529. if (!is_array($results)) {
  530. $results = array ();
  531. }
  532. if ($written) {
  533. $results[] = array (
  534. 'class' => 'success',
  535. 'msg' => '<p class="ok">'.$this->lexicon('config_file_written').'</p>'
  536. );
  537. } else {
  538. $results[] = array (
  539. 'class' => 'failed',
  540. 'msg' => '<p class="notok">'.$this->lexicon('config_file_err_w').'</p>'
  541. );
  542. }
  543. if ($chmodSuccess) {
  544. $results[] = array (
  545. 'class' => 'success',
  546. 'msg' => '<p class="ok">'.$this->lexicon('config_file_perms_set').'</p>'
  547. );
  548. } else {
  549. $results[] = array (
  550. 'class' => 'warning',
  551. 'msg' => '<p>'.$this->lexicon('config_file_perms_notset').'</p>'
  552. );
  553. }
  554. return $results;
  555. }
  556. /**
  557. * Generates a random universal unique ID for identifying modx installs
  558. *
  559. * @return string A universally unique ID
  560. */
  561. public function generateUUID() {
  562. srand(intval(microtime(true) * 1000));
  563. $b = md5(uniqid(rand(),true),true);
  564. $b[6] = chr((ord($b[6]) & 0x0F) | 0x40);
  565. $b[8] = chr((ord($b[8]) & 0x3F) | 0x80);
  566. return implode('-',unpack('H8a/H4b/H4c/H4d/H12e',$b));
  567. }
  568. /**
  569. * Installs a transport package.
  570. *
  571. * @param string The package signature.
  572. * @param array $attributes An array of installation attributes.
  573. * @return array An array of error messages collected during the process.
  574. */
  575. public function installPackage($pkg, array $attributes = array ()) {
  576. $errors = array ();
  577. /* instantiate the modX class */
  578. if (@ require_once (MODX_CORE_PATH . 'model/modx/modx.class.php')) {
  579. $modx = new modX(MODX_CORE_PATH . 'config/');
  580. if (!is_object($modx) || !($modx instanceof modX)) {
  581. $errors[] = '<p>'.$this->lexicon('modx_err_instantiate').'</p>';
  582. } else {
  583. /* try to initialize the mgr context */
  584. $modx->initialize('mgr');
  585. if (!$modx->_initialized) {
  586. $errors[] = '<p>'.$this->lexicon('modx_err_instantiate_mgr').'</p>';
  587. } else {
  588. $loaded = $modx->loadClass('transport.xPDOTransport', XPDO_CORE_PATH, true, true);
  589. if (!$loaded)
  590. $errors[] = '<p>'.$this->lexicon('transport_class_err_load').'</p>';
  591. $packageDirectory = MODX_CORE_PATH . 'packages/';
  592. $packageState = (isset ($attributes[xPDOTransport::PACKAGE_STATE]) ? $attributes[xPDOTransport::PACKAGE_STATE] : xPDOTransport::STATE_PACKED);
  593. $package = xPDOTransport :: retrieve($modx, $packageDirectory . $pkg . '.transport.zip', $packageDirectory, $packageState);
  594. if ($package) {
  595. if (!$package->install($attributes)) {
  596. $errors[] = '<p>'.$this->lexicon('package_err_install',array('package' => $pkg)).'</p>';
  597. } else {
  598. $modx->log(xPDO::LOG_LEVEL_INFO,$this->lexicon('package_installed',array('package' => $pkg)));
  599. }
  600. } else {
  601. $errors[] = '<p>'.$this->lexicon('package_err_nf',array('package' => $pkg)).'</p>';
  602. }
  603. }
  604. }
  605. } else {
  606. $errors[] = '<p>'.$this->lexicon('modx_class_err_nf').'</p>';
  607. }
  608. return $errors;
  609. }
  610. /**
  611. * Gets the manager login URL.
  612. *
  613. * @return string The URL of the installed manager context.
  614. */
  615. public function getManagerLoginUrl() {
  616. $url = '';
  617. /* instantiate the modX class */
  618. if (@ require_once (MODX_CORE_PATH . 'model/modx/modx.class.php')) {
  619. $modx = new modX(MODX_CORE_PATH . 'config/');
  620. if (is_object($modx) && $modx instanceof modX) {
  621. /* try to initialize the mgr context */
  622. $modx->initialize('mgr');
  623. $url = MODX_URL_SCHEME.$modx->getOption('http_host').$modx->getOption('manager_url');
  624. }
  625. }
  626. return $url;
  627. }
  628. /**
  629. * Determines the possible install modes.
  630. *
  631. * @access public
  632. * @return integer One of three possible mode indicators:<ul>
  633. * <li>0 = new install only</li>
  634. * <li>1 = new OR upgrade from older versions of MODx Revolution</li>
  635. * <li>2 = new OR upgrade from MODx Evolution</li>
  636. * </ul>
  637. */
  638. public function getInstallMode() {
  639. $mode = modInstall::MODE_NEW;
  640. if (isset ($_POST['installmode'])) {
  641. $mode = intval($_POST['installmode']);
  642. } else {
  643. global $dbase;
  644. if (file_exists(MODX_CORE_PATH . 'config/' . MODX_CONFIG_KEY . '.inc.php')) {
  645. /* Include the file so we can test its validity */
  646. $included = @ include (MODX_CORE_PATH . 'config/' . MODX_CONFIG_KEY . '.inc.php');
  647. $mode = ($included && isset ($dbase)) ? modInstall::MODE_UPGRADE_REVO : modInstall::MODE_NEW;
  648. }
  649. if (!$mode && file_exists(MODX_INSTALL_PATH . 'manager/includes/config.inc.php')) {
  650. $included = @ include (MODX_INSTALL_PATH . 'manager/includes/config.inc.php');
  651. $mode = ($included && isset ($dbase)) ? modInstall::MODE_UPGRADE_EVO : modInstall::MODE_NEW;
  652. }
  653. }
  654. return $mode;
  655. }
  656. /**
  657. * Creates the database connection for the installation process.
  658. *
  659. * @access private
  660. * @return xPDO The xPDO instance to be used by the installation.
  661. */
  662. public function _connect($dsn, $user = '', $password = '', $prefix = '', array $options = array()) {
  663. if (include_once (MODX_CORE_PATH . 'xpdo/xpdo.class.php')) {
  664. $this->xpdo = new xPDO($dsn, $user, $password, array_merge(array(
  665. xPDO::OPT_CACHE_PATH => MODX_CORE_PATH . 'cache/',
  666. xPDO::OPT_TABLE_PREFIX => $prefix,
  667. xPDO::OPT_LOADER_CLASSES => array('modAccessibleObject'),
  668. xPDO::OPT_SETUP => true,
  669. ), $options),
  670. array(PDO::ATTR_ERRMODE => PDO::ERRMODE_SILENT)
  671. );
  672. $this->xpdo->setLogTarget(array(
  673. 'target' => 'FILE',
  674. 'options' => array(
  675. 'filename' => 'install.' . MODX_CONFIG_KEY . '.' . strftime('%Y%m%dT%H%M%S') . '.log'
  676. )
  677. ));
  678. $this->xpdo->setLogLevel(xPDO::LOG_LEVEL_ERROR);
  679. return $this->xpdo;
  680. } else {
  681. return $this->lexicon('xpdo_err_nf', array('path' => MODX_CORE_PATH.'xpdo/xpdo.class.php'));
  682. }
  683. }
  684. /**
  685. * Instantiate an existing modX configuration.
  686. *
  687. * @param array &$errors An array in which error messages are collected.
  688. * @return modX|null The modX instance, or null if it could not be instantiated.
  689. */
  690. private function _modx(array & $errors) {
  691. $modx = null;
  692. /* to validate installation, instantiate the modX class and run a few tests */
  693. if (include_once (MODX_CORE_PATH . 'model/modx/modx.class.php')) {
  694. $modx = new modX(MODX_CORE_PATH . 'config/', array(
  695. xPDO::OPT_SETUP => true,
  696. ));
  697. if (!is_object($modx) || !($modx instanceof modX)) {
  698. $errors[] = '<p>'.$this->lexicon('modx_err_instantiate').'</p>';
  699. } else {
  700. $modx->setLogTarget(array(
  701. 'target' => 'FILE',
  702. 'options' => array(
  703. 'filename' => 'install.' . MODX_CONFIG_KEY . '.' . strftime('%Y%m%dT%H%M%S') . '.log'
  704. )
  705. ));
  706. /* try to initialize the mgr context */
  707. $modx->initialize('mgr');
  708. if (!$modx->isInitialized()) {
  709. $errors[] = '<p>'.$this->lexicon('modx_err_instantiate_mgr').'</p>';
  710. }
  711. }
  712. } else {
  713. $errors[] = '<p>'.$this->lexicon('modx_class_err_nf').'</p>';
  714. }
  715. return $modx;
  716. }
  717. /**
  718. * Finds the core directory, if possible. If core cannot be found, loads the
  719. * findcore controller.
  720. *
  721. * @return Returns true if core directory is found.
  722. */
  723. public function findCore() {
  724. $exists = false;
  725. if (defined('MODX_CORE_PATH') && file_exists(MODX_CORE_PATH) && is_dir(MODX_CORE_PATH)) {
  726. if (file_exists(MODX_CORE_PATH . 'xpdo/xpdo.class.php') && file_exists(MODX_CORE_PATH . 'model/modx/modx.class.php')) {
  727. $exists = true;
  728. }
  729. }
  730. if (!$exists) {
  731. include(MODX_SETUP_PATH . 'templates/findcore.php');
  732. die();
  733. }
  734. return $exists;
  735. }
  736. /**
  737. * Does all the pre-load checks, before setup loads.
  738. *
  739. * @access public
  740. */
  741. public function doPreloadChecks() {
  742. $this->lexicon->load('preload');
  743. $errors= array();
  744. if (!extension_loaded('pdo')) {
  745. $errors[] = $this->lexicon('preload_err_pdo');
  746. }
  747. if (!file_exists(MODX_CORE_PATH) || !is_dir(MODX_CORE_PATH)) {
  748. $errors[] = $this->lexicon('preload_err_core_path');
  749. }
  750. if (!file_exists(MODX_CORE_PATH . 'cache/') || !is_dir(MODX_CORE_PATH . 'cache/') || !$this->is_writable2(MODX_CORE_PATH . 'cache/')) {
  751. $errors[] = $this->lexicon('preload_err_cache',array('path' => MODX_CORE_PATH));
  752. }
  753. if (!empty($errors)) {
  754. $this->_fatalError($errors);
  755. }
  756. }
  757. /**
  758. * Outputs a fatal error message and then dies.
  759. *
  760. * @access private
  761. * @param string/array A string or array of errors
  762. */
  763. private function _fatalError($errors) {
  764. $output = '<html><head><title></title></head><body><h1>'.$this->lexicon('fatal_error').'</h1><ul>';
  765. if (is_array($errors)) {
  766. foreach ($errors as $error) {
  767. $output .= '<li>'.$error.'</li>';
  768. }
  769. } else {
  770. $output .= '<li>'.$errors.'</li>';
  771. }
  772. $output .= '</ul></body></html>';
  773. die($output);
  774. }
  775. /**
  776. * Custom is_writable function to test on problematic servers
  777. *
  778. * @param string $path
  779. * @return boolean True if write was successful
  780. */
  781. public function is_writable2($path) {
  782. $written = false;
  783. if (!is_string($path)) return false;
  784. /* if is file get parent dir */
  785. if (is_file($path)) { $path = dirname($path) . '/'; }
  786. /* ensure / at end, translate \ to / for windows */
  787. if (substr($path,strlen($path)-1) != '/') { $path .= '/'; }
  788. $path = strtr($path,'\\','/');
  789. /* get test file */
  790. $filePath = $path.uniqid().'.cache.php';
  791. /* attempt to create test file */
  792. $fp = @fopen($filePath,'w');
  793. if ($fp === false || !file_exists($filePath)) return false;
  794. /* attempt to write to test file */
  795. $written = @fwrite($fp,'<?php echo "test";');
  796. if (!$written) { /* if fails try to delete it */
  797. @fclose($fp);
  798. @unlink($filePath);
  799. return false;
  800. }
  801. /* attempt to delete test file */
  802. @fclose($fp);
  803. $written = @unlink($filePath);
  804. return $written;
  805. }
  806. /**
  807. * Loads the correct database driver for this environment.
  808. *
  809. * @return boolean True if successful.
  810. */
  811. public function loadDriver() {
  812. $this->loadSettings();
  813. $path = dirname(__FILE__).'/drivers/';
  814. /* db specific driver */
  815. $class = 'modInstallDriver_'.strtolower($this->settings->get('database_type','mysql'));
  816. $driverPath = $path.strtolower($class.'.class.php');
  817. $included = @include_once $driverPath;
  818. if ($included) {
  819. $this->driver = new $class($this);
  820. } else {
  821. $this->_fatalError($this->lexicon('driver_class_err_nf',array('path' => $driverPath)));
  822. }
  823. return $included;
  824. }
  825. }