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