PageRenderTime 36ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/trunk/setup/includes/modinstall.class.php

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