PageRenderTime 43ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

/setup/includes/modinstall.class.php

http://github.com/modxcms/revolution
PHP | 653 lines | 586 code | 17 blank | 50 comment | 14 complexity | 81a15cf6d4518468cbab0f6eb628eca5 MD5 | raw file
Possible License(s): GPL-2.0, Apache-2.0, BSD-3-Clause, LGPL-2.1
  1. <?php
  2. /*
  3. * This file is part of MODX Revolution.
  4. *
  5. * Copyright (c) MODX, LLC. All Rights Reserved.
  6. *
  7. * For complete copyright and license information, see the COPYRIGHT and LICENSE
  8. * files found in the top-level directory of this distribution.
  9. */
  10. /**
  11. * Common classes for the MODX installation and provisioning services.
  12. *
  13. * @package setup
  14. */
  15. /**
  16. * Provides common functionality and data for installation and provisioning.
  17. *
  18. * @package setup
  19. */
  20. class modInstall {
  21. const MODE_NEW = 0;
  22. const MODE_UPGRADE_REVO = 1;
  23. const MODE_UPGRADE_EVO = 2;
  24. const MODE_UPGRADE_REVO_ADVANCED = 3;
  25. /** @var xPDO $xpdo */
  26. public $xpdo = null;
  27. public $options = array ();
  28. /** @var modInstallRequest $request */
  29. public $request = null;
  30. /** @var modInstallSettings $settings */
  31. public $settings = null;
  32. /** @var modInstallLexicon $lexicon */
  33. public $lexicon = null;
  34. /** @var modInstallTest $test */
  35. public $test;
  36. /** @var modInstallDriver $driver */
  37. public $driver;
  38. /** @var modInstallRunner $runner */
  39. public $runner;
  40. /** @var array $config */
  41. public $config = array ();
  42. public $action = '';
  43. public $finished = false;
  44. /**
  45. * The constructor for the modInstall object.
  46. *
  47. * @constructor
  48. * @param array $options An array of configuration options.
  49. */
  50. function __construct(array $options = array()) {
  51. if (isset ($_REQUEST['action'])) {
  52. $this->action = preg_replace('/[\.]{2,}/', '', htmlspecialchars($_REQUEST['action']));
  53. }
  54. if (is_array($options)) {
  55. $this->options = $options;
  56. }
  57. }
  58. /**
  59. * Load a class file for setup
  60. * @param string $class The name of the class to load
  61. * @param string $path The path to load the class from
  62. * @return array|bool
  63. */
  64. public function loadClass($class,$path = '') {
  65. $classFile = str_replace('.', '/', strtolower($class));
  66. $className = explode('.',$class);
  67. $className = array_reverse($className);
  68. $className = $className[0];
  69. if (empty($path)) {
  70. $path = strtr(realpath(MODX_SETUP_PATH.'includes'),'\\','/').'/';
  71. }
  72. $classPath = $path.$classFile.'.class.php';
  73. $included = require_once $classPath;
  74. return $included ? $className : false;
  75. }
  76. /**
  77. * Return a service class instance
  78. * @param string $name
  79. * @param string $class
  80. * @param string $path
  81. * @param array $config
  82. * @return Object|null
  83. */
  84. public function getService($name,$class,$path = '',array $config = array()) {
  85. if (empty($this->$name)) {
  86. $className = $this->loadClass($class,$path);
  87. if (!empty($className)) {
  88. $this->$name = new $className($this,$config);
  89. } else {
  90. $this->_fatalError($this->lexicon('service_err_nf',array(
  91. 'name' => $name,
  92. 'class' => $class,
  93. 'path' => $path,
  94. )));
  95. }
  96. }
  97. return $this->$name;
  98. }
  99. /**
  100. * Load settings class
  101. *
  102. * @access public
  103. * @param string $class The settings class to load.
  104. * @param string $path
  105. * @return modInstallSettings
  106. */
  107. public function loadSettings($class = 'modInstallSettings',$path = '') {
  108. if (empty($this->settings)) {
  109. $className = $this->loadClass($class,$path);
  110. if (!empty($className)) {
  111. $this->settings = new $className($this);
  112. } else {
  113. $this->_fatalError($this->lexicon('settings_handler_err_nf',array('path' => $className)));
  114. }
  115. }
  116. return $this->settings;
  117. }
  118. /**
  119. * Shortcut method for modInstallLexicon::get. {@see modInstallLexicon::get}
  120. *
  121. * @param string $key
  122. * @param array $placeholders
  123. * @return string
  124. */
  125. public function lexicon($key,array $placeholders = array()) {
  126. return $this->lexicon->get($key,$placeholders);
  127. }
  128. /**
  129. * Get an xPDO connection to the database.
  130. *
  131. * @param int $mode
  132. * @return xPDO A copy of the xpdo object.
  133. */
  134. public function getConnection($mode = 0) {
  135. if ($this->settings && empty($mode)) $mode = (int)$this->settings->get('installmode');
  136. if (empty($mode)) $mode = modInstall::MODE_NEW;
  137. if ($mode === modInstall::MODE_UPGRADE_REVO) {
  138. $errors = array ();
  139. $this->xpdo = $this->_modx($errors);
  140. } else if (!is_object($this->xpdo)) {
  141. $options = array();
  142. if ($this->settings->get('new_folder_permissions')) $options['new_folder_permissions'] = $this->settings->get('new_folder_permissions');
  143. if ($this->settings->get('new_file_permissions')) $options['new_file_permissions'] = $this->settings->get('new_file_permissions');
  144. $this->xpdo = $this->_connect(
  145. $this->settings->get('database_dsn')
  146. ,$this->settings->get('database_user')
  147. ,$this->settings->get('database_password')
  148. ,$this->settings->get('table_prefix')
  149. ,$options
  150. );
  151. if (!($this->xpdo instanceof xPDO)) { return $this->xpdo; }
  152. $this->xpdo->setOption('cache_path',MODX_CORE_PATH . 'cache/');
  153. $config_options = (array)$this->settings->get('config_options');
  154. foreach ($config_options as $config_option => $config_value) {
  155. $this->xpdo->setOption($config_option, $config_value);
  156. }
  157. if ($mode === modInstall::MODE_UPGRADE_REVO_ADVANCED) {
  158. if ($this->xpdo->connect()) {
  159. $errors = array ();
  160. $this->xpdo = $this->_modx($errors);
  161. } else {
  162. return $this->lexicon('db_err_connect_upgrade');
  163. }
  164. }
  165. }
  166. if (is_object($this->xpdo) && $this->xpdo instanceof xPDO) {
  167. $this->xpdo->setLogTarget(array(
  168. 'target' => 'FILE',
  169. 'options' => array(
  170. 'filename' => 'install.' . MODX_CONFIG_KEY . '.' . strftime('%Y-%m-%dT%H.%M.%S').'.log'
  171. )
  172. ));
  173. $this->xpdo->setLogLevel(xPDO::LOG_LEVEL_ERROR);
  174. $this->xpdo->setPackage('modx', MODX_CORE_PATH . 'model/', $this->settings->get('table_prefix'));
  175. }
  176. return $this->xpdo;
  177. }
  178. /**
  179. * Load distribution-specific test handlers
  180. *
  181. * @param string $class
  182. * @param string $path
  183. * @param array $config
  184. * @return modInstallTest|void
  185. */
  186. public function loadTestHandler($class = 'test.modInstallTest',$path = '',array $config = array()) {
  187. $className = $this->loadClass($class,$path);
  188. if (!empty($className)) {
  189. $this->lexicon->load('test');
  190. $distributionClass = 'test.'.$className.ucfirst(trim(MODX_SETUP_KEY, '@'));
  191. $distributionClassName = $this->loadClass($distributionClass,$path);
  192. if (empty($distributionClassName)) {
  193. $this->_fatalError($this->lexicon('test_version_class_nf',array('path' => $distributionClass)));
  194. }
  195. $this->test = new $distributionClassName($this);
  196. } else {
  197. $this->_fatalError($this->lexicon('test_class_nf',array('path' => $path)));
  198. }
  199. return $this->test;
  200. }
  201. /**
  202. * Perform a series of pre-installation tests.
  203. *
  204. * @param integer $mode The install mode.
  205. * @param string $testClass The class to run tests with
  206. * @param string $testClassPath
  207. * @return array An array of result messages collected during the process.
  208. */
  209. public function test($mode = modInstall::MODE_NEW,$testClass = 'test.modInstallTest',$testClassPath = '') {
  210. $this->loadTestHandler($testClass,$testClassPath);
  211. return $this->test->run($mode);
  212. }
  213. /**
  214. * Verify that the modX class can be initialized.
  215. *
  216. * @return array An array of error messages collected during the process.
  217. */
  218. public function verify() {
  219. $errors = array ();
  220. $modx = $this->_modx($errors);
  221. if (is_object($modx) && $modx instanceof modX) {
  222. if ($modx->getCacheManager()) {
  223. $modx->cacheManager->refresh();
  224. }
  225. }
  226. return $errors;
  227. }
  228. /**
  229. * Cleans up after install.
  230. *
  231. * TODO: implement this function to cleanup any temporary files
  232. * @param array $options
  233. * @return array
  234. */
  235. public function cleanup(array $options = array ()) {
  236. $errors = array();
  237. $modx = $this->_modx($errors);
  238. if (empty($modx) || !($modx instanceof modX)) {
  239. $errors['modx_class'] = $this->lexicon('modx_err_instantiate');
  240. return $errors;
  241. }
  242. /* create the directories for Package Management */
  243. /** @var modCacheManager $cacheManager */
  244. $cacheManager = $modx->getCacheManager();
  245. $directoryOptions = array(
  246. 'new_folder_permissions' => $modx->getOption('new_folder_permissions',null,0775),
  247. );
  248. /* create assets/ */
  249. $assetsPath = $this->settings->get('assets_path',$this->settings->get('web_path',$modx->getOption('base_path')).'assets/');
  250. if (!is_dir($assetsPath)) {
  251. $cacheManager->writeTree($assetsPath,$directoryOptions);
  252. }
  253. if (!is_dir($assetsPath) || !$this->is_writable2($assetsPath)) {
  254. $errors['assets_not_created'] = str_replace('[[+path]]',$assetsPath,$this->lexicon('setup_err_assets'));
  255. }
  256. unset($assetsPath);
  257. /* create assets/components/ */
  258. $assetsCompPath = $this->settings->get('assets_path',$this->settings->get('web_path',$modx->getOption('base_path')).'assets/').'components/';
  259. if (!is_dir($assetsCompPath)) {
  260. $cacheManager->writeTree($assetsCompPath,$directoryOptions);
  261. }
  262. if (!is_dir($assetsCompPath) || !$this->is_writable2($assetsCompPath)) {
  263. $errors['assets_comp_not_created'] = str_replace('[[+path]]',$assetsCompPath,$this->lexicon('setup_err_assets_comp'));
  264. }
  265. unset($assetsCompPath);
  266. /* create core/components/ */
  267. $coreCompPath = $this->settings->get('core_path',$modx->getOption('core_path',null,MODX_CORE_PATH)).'components/';
  268. if (!is_dir($coreCompPath)) {
  269. $cacheManager->writeTree($coreCompPath,$directoryOptions);
  270. }
  271. if (!is_dir($coreCompPath) || !$this->is_writable2($coreCompPath)) {
  272. $errors['core_comp_not_created'] = str_replace('[[+path]]',$coreCompPath,$this->lexicon('setup_err_core_comp'));
  273. }
  274. unset($coreCompPath);
  275. return $errors;
  276. }
  277. /**
  278. * Removes the setup directory
  279. *
  280. * @access public
  281. * @param array $options
  282. * @return array
  283. */
  284. public function removeSetupDirectory(array $options = array()) {
  285. $errors = array();
  286. $modx = $this->_modx($errors);
  287. if ($modx) {
  288. /** @var modCacheManager $cacheManager */
  289. $cacheManager = $modx->getCacheManager();
  290. if ($cacheManager) {
  291. $setupPath = $modx->getOption('base_path').'setup/';
  292. if (!$cacheManager->deleteTree($setupPath,true,false,false)) {
  293. $modx->log(modX::LOG_LEVEL_ERROR,$this->lexicon('setup_err_remove'));
  294. }
  295. } else {
  296. $modx->log(modX::LOG_LEVEL_ERROR,$this->lexicon('cache_manager_err'));
  297. }
  298. } else {
  299. $modx->log(modX::LOG_LEVEL_ERROR,$this->lexicon('modx_object_err'));
  300. }
  301. return $errors;
  302. }
  303. /**
  304. * Generates a random universal unique ID for identifying modx installs
  305. *
  306. * @return string A universally unique ID
  307. */
  308. public function generateUUID() {
  309. srand(intval(microtime(true) * 1000));
  310. $b = md5(uniqid(rand(),true),true);
  311. $b[6] = chr((ord($b[6]) & 0x0F) | 0x40);
  312. $b[8] = chr((ord($b[8]) & 0x3F) | 0x80);
  313. return implode('-',unpack('H8a/H4b/H4c/H4d/H12e',$b));
  314. }
  315. /**
  316. * Installs a transport package.
  317. *
  318. * @param string $pkg The package signature.
  319. * @param array $attributes An array of installation attributes.
  320. * @return array An array of error messages collected during the process.
  321. */
  322. public function installPackage($pkg, array $attributes = array ()) {
  323. $errors = array ();
  324. /* instantiate the modX class */
  325. if (@ require_once (MODX_CORE_PATH . 'model/modx/modx.class.php')) {
  326. $modx = new modX(MODX_CORE_PATH . 'config/');
  327. if (!is_object($modx) || !($modx instanceof modX)) {
  328. $errors[] = '<p>'.$this->lexicon('modx_err_instantiate').'</p>';
  329. } else {
  330. /* try to initialize the mgr context */
  331. $modx->initialize('mgr');
  332. if (!$modx->isInitialized()) {
  333. $errors[] = '<p>'.$this->lexicon('modx_err_instantiate_mgr').'</p>';
  334. } else {
  335. $loaded = $modx->loadClass('transport.xPDOTransport', XPDO_CORE_PATH, true, true);
  336. if (!$loaded)
  337. $errors[] = '<p>'.$this->lexicon('transport_class_err_load').'</p>';
  338. $packageDirectory = MODX_CORE_PATH . 'packages/';
  339. $packageState = (isset ($attributes[xPDOTransport::PACKAGE_STATE]) ? $attributes[xPDOTransport::PACKAGE_STATE] : xPDOTransport::STATE_PACKED);
  340. $package = xPDOTransport :: retrieve($modx, $packageDirectory . $pkg . '.transport.zip', $packageDirectory, $packageState);
  341. if ($package) {
  342. if (!$package->install($attributes)) {
  343. $errors[] = '<p>'.$this->lexicon('package_err_install',array('package' => $pkg)).'</p>';
  344. } else {
  345. $modx->log(xPDO::LOG_LEVEL_INFO,$this->lexicon('package_installed',array('package' => $pkg)));
  346. }
  347. } else {
  348. $errors[] = '<p>'.$this->lexicon('package_err_nf',array('package' => $pkg)).'</p>';
  349. }
  350. }
  351. }
  352. } else {
  353. $errors[] = '<p>'.$this->lexicon('modx_class_err_nf').'</p>';
  354. }
  355. return $errors;
  356. }
  357. /**
  358. * Gets the manager login URL.
  359. *
  360. * @return string The URL of the installed manager context.
  361. */
  362. public function getManagerLoginUrl() {
  363. $url = '';
  364. /* instantiate the modX class */
  365. if (@ require_once (MODX_CORE_PATH . 'model/modx/modx.class.php')) {
  366. $modx = new modX(MODX_CORE_PATH . 'config/');
  367. if (is_object($modx) && $modx instanceof modX) {
  368. /* try to initialize the mgr context */
  369. $modx->initialize('mgr');
  370. $url = MODX_URL_SCHEME.$modx->getOption('http_host').$modx->getOption('manager_url');
  371. }
  372. }
  373. return $url;
  374. }
  375. /**
  376. * Determines the possible install modes.
  377. *
  378. * @access public
  379. * @return integer One of three possible mode indicators:<ul>
  380. * <li>0 = new install only</li>
  381. * <li>1 = new OR upgrade from older versions of MODX Revolution</li>
  382. * <li>2 = new OR upgrade from MODX Evolution</li>
  383. * </ul>
  384. */
  385. public function getInstallMode() {
  386. $mode = modInstall::MODE_NEW;
  387. if (isset ($_POST['installmode'])) {
  388. $mode = intval($_POST['installmode']);
  389. } else {
  390. global $dbase;
  391. if (file_exists(MODX_CORE_PATH . 'config/' . MODX_CONFIG_KEY . '.inc.php')) {
  392. /* Include the file so we can test its validity */
  393. $included = @ include (MODX_CORE_PATH . 'config/' . MODX_CONFIG_KEY . '.inc.php');
  394. $mode = ($included && isset ($dbase)) ? modInstall::MODE_UPGRADE_REVO : modInstall::MODE_NEW;
  395. }
  396. if (!$mode && file_exists(MODX_INSTALL_PATH . 'manager/includes/config.inc.php')) {
  397. $included = @ include (MODX_INSTALL_PATH . 'manager/includes/config.inc.php');
  398. $mode = ($included && isset ($dbase)) ? modInstall::MODE_UPGRADE_EVO : modInstall::MODE_NEW;
  399. }
  400. }
  401. return $mode;
  402. }
  403. /**
  404. * Creates the database connection for the installation process.
  405. *
  406. * @access private
  407. * @return xPDO The xPDO instance to be used by the installation.
  408. */
  409. public function _connect($dsn, $user = '', $password = '', $prefix = '', array $options = array()) {
  410. if (include_once (MODX_CORE_PATH . 'xpdo/xpdo.class.php')) {
  411. $this->xpdo = new xPDO($dsn, $user, $password, array_merge(array(
  412. xPDO::OPT_CACHE_PATH => MODX_CORE_PATH . 'cache/',
  413. xPDO::OPT_TABLE_PREFIX => $prefix,
  414. xPDO::OPT_SETUP => true,
  415. ), $options),
  416. array(PDO::ATTR_ERRMODE => PDO::ERRMODE_SILENT)
  417. );
  418. $this->xpdo->setLogTarget(array(
  419. 'target' => 'FILE',
  420. 'options' => array(
  421. 'filename' => 'install.' . MODX_CONFIG_KEY . '.' . strftime('%Y%m%dT%H%M%S') . '.log'
  422. )
  423. ));
  424. $this->xpdo->setLogLevel(xPDO::LOG_LEVEL_ERROR);
  425. return $this->xpdo;
  426. } else {
  427. return $this->lexicon('xpdo_err_nf', array('path' => MODX_CORE_PATH.'xpdo/xpdo.class.php'));
  428. }
  429. }
  430. /**
  431. * Instantiate an existing modX configuration.
  432. *
  433. * @param array &$errors An array in which error messages are collected.
  434. * @return modX|null The modX instance, or null if it could not be instantiated.
  435. */
  436. private function _modx(array & $errors) {
  437. $modx = null;
  438. /* to validate installation, instantiate the modX class and run a few tests */
  439. if (include_once (MODX_CORE_PATH . 'model/modx/modx.class.php')) {
  440. $modx = new modX(MODX_CORE_PATH . 'config/', array(
  441. xPDO::OPT_SETUP => true,
  442. ));
  443. if (!is_object($modx) || !($modx instanceof modX)) {
  444. $errors[] = '<p>'.$this->lexicon('modx_err_instantiate').'</p>';
  445. } else {
  446. $modx->setLogTarget(array(
  447. 'target' => 'FILE',
  448. 'options' => array(
  449. 'filename' => 'install.' . MODX_CONFIG_KEY . '.' . strftime('%Y%m%dT%H%M%S') . '.log'
  450. )
  451. ));
  452. /* try to initialize the mgr context */
  453. $modx->initialize('mgr');
  454. if (!$modx->isInitialized()) {
  455. $errors[] = '<p>'.$this->lexicon('modx_err_instantiate_mgr').'</p>';
  456. }
  457. }
  458. } else {
  459. $errors[] = '<p>'.$this->lexicon('modx_class_err_nf').'</p>';
  460. }
  461. return $modx;
  462. }
  463. /**
  464. * Finds the core directory, if possible. If core cannot be found, loads the
  465. * findcore controller.
  466. *
  467. * @return Returns true if core directory is found.
  468. */
  469. public function findCore() {
  470. $exists = false;
  471. if (defined('MODX_CORE_PATH') && file_exists(MODX_CORE_PATH) && is_dir(MODX_CORE_PATH)) {
  472. if (file_exists(MODX_CORE_PATH . 'xpdo/xpdo.class.php') && file_exists(MODX_CORE_PATH . 'model/modx/modx.class.php')) {
  473. $exists = true;
  474. }
  475. }
  476. if (!$exists) {
  477. include(MODX_SETUP_PATH . 'templates/findcore.php');
  478. die();
  479. }
  480. return $exists;
  481. }
  482. /**
  483. * Does all the pre-load checks, before setup loads.
  484. *
  485. * @access public
  486. */
  487. public function doPreloadChecks() {
  488. $this->lexicon->load('preload');
  489. $errors= array();
  490. if (!extension_loaded('pdo')) {
  491. $errors[] = $this->lexicon('preload_err_pdo');
  492. }
  493. if (!file_exists(MODX_CORE_PATH) || !is_dir(MODX_CORE_PATH)) {
  494. $errors[] = $this->lexicon('preload_err_core_path');
  495. }
  496. if (!file_exists(MODX_CORE_PATH . 'cache/') || !is_dir(MODX_CORE_PATH . 'cache/') || !$this->is_writable2(MODX_CORE_PATH . 'cache/')) {
  497. $errors[] = $this->lexicon('preload_err_cache',array('path' => MODX_CORE_PATH));
  498. }
  499. if (!empty($errors)) {
  500. $this->_fatalError($errors);
  501. }
  502. }
  503. /**
  504. * Outputs a fatal error message and then dies.
  505. *
  506. * @param string|array $errors A string or array of errors
  507. * @return void
  508. */
  509. public function _fatalError($errors) {
  510. $output = '<html><head><title></title></head><body><h1>'.$this->lexicon('fatal_error').'</h1><ul>';
  511. if (is_array($errors)) {
  512. foreach ($errors as $error) {
  513. $output .= '<li>'.$error.'</li>';
  514. }
  515. } else {
  516. $output .= '<li>'.$errors.'</li>';
  517. }
  518. $output .= '</ul></body></html>';
  519. die($output);
  520. }
  521. /**
  522. * Custom is_writable function to test on problematic servers
  523. *
  524. * @param string $path
  525. * @return boolean True if write was successful
  526. */
  527. public function is_writable2($path) {
  528. $written = false;
  529. if (!is_string($path)) return false;
  530. /* if is file get parent dir */
  531. if (is_file($path)) { $path = dirname($path) . '/'; }
  532. /* ensure / at end, translate \ to / for windows */
  533. if (substr($path,strlen($path)-1) != '/') { $path .= '/'; }
  534. $path = strtr($path,'\\','/');
  535. /* get test file */
  536. $filePath = $path.uniqid().'.cache.php';
  537. /* attempt to create test file */
  538. $fp = @fopen($filePath,'w');
  539. if ($fp === false || !file_exists($filePath)) return false;
  540. /* attempt to write to test file */
  541. $written = @fwrite($fp,'<?php echo "test";');
  542. if (!$written) { /* if fails try to delete it */
  543. @fclose($fp);
  544. @unlink($filePath);
  545. return false;
  546. }
  547. /* attempt to delete test file */
  548. @fclose($fp);
  549. $written = @unlink($filePath);
  550. return $written;
  551. }
  552. /**
  553. * Loads the correct database driver for this environment.
  554. *
  555. * @param string $path
  556. * @return boolean True if successful.
  557. */
  558. public function loadDriver($path = '') {
  559. $this->loadSettings();
  560. /* db specific driver */
  561. $class = 'drivers.modInstallDriver_'.strtolower($this->settings->get('database_type','mysql'));
  562. $className = $this->loadClass($class,$path);
  563. if (!empty($className)) {
  564. $this->driver = new $className($this);
  565. } else {
  566. $this->_fatalError($this->lexicon('driver_class_err_nf',array('path' => $class)));
  567. }
  568. return !empty($className);
  569. }
  570. public function lock() {
  571. $errors = array();
  572. $modx = $this->_modx($errors);
  573. if ($modx) {
  574. /** @var modCacheManager $cacheManager */
  575. $cacheManager = $modx->getCacheManager();
  576. if ($cacheManager) {
  577. if (!$cacheManager->writeTree(MODX_SETUP_PATH . '.locked')) {
  578. $modx->log(modX::LOG_LEVEL_ERROR,$this->lexicon('setup_err_lock'));
  579. }
  580. } else {
  581. $modx->log(modX::LOG_LEVEL_ERROR,$this->lexicon('cache_manager_err'));
  582. }
  583. } else {
  584. $modx->log(modX::LOG_LEVEL_ERROR,$this->lexicon('modx_object_err'));
  585. }
  586. return $errors;
  587. }
  588. public function isLocked() {
  589. if (file_exists(MODX_SETUP_PATH . '.locked')) {
  590. return true;
  591. }
  592. return false;
  593. }
  594. }