PageRenderTime 60ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/www/manager/setup/includes/modinstall.class.php

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