Fixing #119. Akelos was silencing fatal errors on controllers, which could lead to...
[akelos.git] / app / models / framework_setup.php
blob52ab9de3079e765fa1e422449b03bdc66c2126ab
1 <?php
3 require_once(AK_LIB_DIR.DS.'AkLogger.php');
5 class FrameworkSetup extends AkObject
7 var $available_databases = array(
8 'mysql' => 'MySQL',
9 'pgsql' => 'PostgreSQL',
10 'sqlite' => 'SQLite'
12 var $available_locales = array('en', 'es');
13 var $locales = array('en');
15 var $stylesheets = array('scaffold','forms');
17 function __construct()
19 if(file_exists(AK_CONFIG_DIR.DS.'config.php')){
20 echo Ak::t('The framework_setup.php found that you already have a configuration file at config/config.php. You need to remove that file first in order to run the setup.', array(), 'framework_setup');
21 die();
27 /**
28 * Will try to guess the best database on current server.
29 * Per example, if current server has MySQL and PostgreSQL it will pick up
30 * PostgreSQL if MySQL doens't support InnoDB tables.
32 * If none of the above is
35 function suggestDatabaseType()
37 /**
38 * @todo add database check for postgre
40 return $this->_suggestMysql() ? 'mysql' : (function_exists('pg_connect') ? 'pgsql' : (AK_PHP5 ? 'sqlite' : 'mysql'));
43 function _suggestMysql()
45 if(function_exists('mysql_connect')){
46 if($db = @mysql_connect( $this->getDatabaseHost(),
47 $this->getDatabaseUser(),
48 $this->getDatabasePassword())){
49 return true;
50 /**
51 * @todo Checking if innodb is available break on some systems
52 * we should investigate a better way to do this
55 $result = mysql_query($db, "SHOW VARIABLES LIKE 'have_innodb';");
56 return strstr(
57 strtolower(
58 @array_pop(
59 @mysql_fetch_row(
60 @mysql_query("SHOW VARIABLES LIKE 'have_innodb';", $db)
64 'yes');
68 return false;
71 function createDatabase($mode)
73 $success = true;
75 $db = $this->databaseConnection('admin');
77 if($db){
78 if($this->getDatabaseType($mode) != 'sqlite'){
79 $DataDict = NewDataDictionary($db);
80 if($this->getDatabaseType($mode) == 'mysql'){
81 $success = $this->_createMysqlDatabase($db, $mode) ? $success : false;
84 return $success;
86 return false;
89 function _createMysqlDatabase(&$db, $mode)
91 $success = true;
92 $success = $db->Execute('CREATE DATABASE '.$this->getDatabaseName($mode)) ? $success : false;
93 $success = $db->Execute("GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP,ALTER ON ".
94 $this->getDatabaseName($mode).".* TO '".$this->getDatabaseUser($mode)."'@'".
95 $this->getDatabaseHost($mode)."' IDENTIFIED BY '".$this->getDatabasePassword($mode)."'") ? $success : false;
96 return $success;
99 function getDatabaseHost($mode = '')
101 return !isset($this->{$mode.'_database_host'}) ? $this->suggestDatabaseHost() : $this->{$mode.'_database_host'};
104 function getDatabaseUser($mode = '')
106 return !isset($this->{$mode.'_database_user'}) ? $this->suggestUserName() : $this->{$mode.'_database_user'};
109 function getDatabasePassword($mode = '')
111 return !isset($this->{$mode.'_database_password'}) ? '' : $this->{$mode.'_database_password'};
114 function getDatabaseType()
116 return !isset($this->database_type) ? $this->suggestDatabaseType() : $this->database_type;
119 function setDatabaseType($database_type)
121 $database_type = strtolower($database_type);
122 if(!in_array($database_type, array_keys($this->available_databases))){
123 trigger_error(Ak::t('Selected database is not supported yet by the Akelos Framework',array(),'framework_setup'));
124 }elseif(!$this->isDatabaseDriverAvalible($database_type)){
125 trigger_error(Ak::t('Could not set %database_type as database type. Your current PHP settings do not support %database_type databases', array('%database_type '=>$database_type), 'framework_setup'));
126 }else{
127 $this->database_type = $database_type;
128 return $this->database_type;
130 return false;
133 function getDatabaseName($mode)
135 return !isset($this->{$mode.'_database_name'}) ?
136 $this->guessApplicationName().($mode=='development'?'_dev':($mode=='testing'?'_tests':'')) :
137 $this->{$mode.'_database_name'};
140 function setDatabaseName($database_name, $mode)
142 $this->{$mode.'_database_name'} = $database_name;
145 function setDatabaseHost($host, $mode)
147 $this->{$mode.'_database_host'} = $host;
150 function setDatabaseUser($user, $mode)
152 $this->{$mode.'_database_user'} = $user;
155 function setDatabasePassword($password, $mode)
157 $this->{$mode.'_database_password'} = $password;
161 function getDatabaseAdminUser()
163 return !isset($this->admin_database_user) ? 'root' : $this->admin_database_user;
166 function getDatabaseAdminPassword()
168 return !isset($this->admin_database_password) ? '' : $this->admin_database_password;
172 function setDatabaseAdminUser($user)
174 $this->admin_database_user = $user;
177 function setDatabaseAdminPassword($password)
179 $this->admin_database_password = $password;
183 function databaseConnection($mode)
185 static $connections = array();
186 require_once(AK_CONTRIB_DIR.DS.'adodb'.DS.'adodb.inc.php');
188 $dsn = $this->_getDsn($mode);
189 if(!isset($connections[$dsn])){
190 if(!$connections[$dsn] = @NewADOConnection($dsn)){
191 return false;
194 return $connections[$dsn];
197 function _getDsn($mode)
199 if($mode == 'admin'){
200 $db_type = $this->getDatabaseType('production');
201 return $db_type.'://'.
202 $this->getDatabaseAdminUser().':'.
203 $this->getDatabaseAdminPassword().'@'.$this->getDatabaseHost('production').($db_type == 'mysql' ? '/mysql' : '');
204 }else{
205 return $this->getDatabaseType() == 'sqlite' ?
206 "sqlite://".urlencode(AK_CONFIG_DIR.DS.$this->getDatabaseName($mode).'-'.$this->random.'.sqlite') :
207 $this->getDatabaseType($mode)."://".$this->getDatabaseUser($mode).":".$this->getDatabasePassword($mode).
208 "@".$this->getDatabaseHost($mode)."/".$this->getDatabaseName($mode);
213 function getAvailableDatabases()
215 $databases = array();
216 foreach ($this->available_databases as $type=>$description){
217 if($this->isDatabaseDriverAvalible($type)){
218 $databases[] = array('type' => $type, 'name' => $description);
222 return $databases;
226 function isDatabaseDriverAvalible($database_type = null)
228 $database_type = empty($database_type) ? $this->getDatabaseType() : $database_type;
229 if(strstr($database_type,'mysql')){
230 $function = 'mysql_connect';
231 }elseif (strstr($database_type,'pg') || strstr($database_type,'gre')){
232 $function = 'pg_connect';
233 }elseif (strstr($database_type,'lite')){
234 $function = 'sqlite_open';
235 }else{
236 $function = $database_type.'_connect';
238 return function_exists($function);
241 function runFrameworkInstaller()
243 static $unique_dsn = array();
244 require_once(AK_LIB_DIR.DS.'AkInstaller.php');
245 require_once(AK_APP_DIR.DS.'installers'.DS.'framework_installer.php');
247 foreach (array('production', 'development') as $mode){
248 $dsn = $this->_getDsn($mode);
249 if(!isset($unique_dsn[$dsn])){
250 $DbInstance =& AkDbAdapter::getInstance(array(
251 'type' => $this->getDatabaseType($mode),
252 'file' => AK_CONFIG_DIR.DS.$this->getDatabaseName($mode).'-'.$this->random.'.sqlite',
253 'user' => $this->getDatabaseUser($mode),
254 'password' => $this->getDatabasePassword($mode),
255 'host' => $this->getDatabaseHost($mode),
256 'database_name' => $this->getDatabaseName($mode)
259 $installer =& new FrameworkInstaller($DbInstance);
260 $installer->install(null, array('mode' => $mode));
261 $unique_dsn[$dsn] = true;
265 return true;
271 function getUrlSuffix()
273 return empty($this->url_suffix) ? AK_SITE_URL_SUFFIX : $this->url_suffix;
277 function getConfigurationFile($settings = array())
281 $configuration_template = <<<CONFIG
282 <?php
284 \$database_settings = array(
285 'production' => array(
286 'type' => '%production_database_type', // mysql, sqlite or pgsql
287 'database_file' => '%production_database_file', // you only need this for SQLite
288 'host' => '%production_database_host',
289 'port' => '',
290 'database_name' => '%production_database_name',
291 'user' => '%production_database_user',
292 'password' => '%production_database_password',
293 'options' => '' // persistent, debug, fetchmode, new
296 'development' => array(
297 'type' => '%development_database_type',
298 'database_file' => '%development_database_file',
299 'host' => '%development_database_host',
300 'port' => '',
301 'database_name' => '%development_database_name',
302 'user' => '%development_database_user',
303 'password' => '%development_database_password',
304 'options' => ''
307 // Warning: The database defined as 'testing' will be erased and
308 // re-generated from your development database when you run './script/test app'.
309 // Do not set this db to the same as development or production.
310 'testing' => array(
311 'type' => '%testing_database_type',
312 'database_file' => '%testing_database_file',
313 'host' => '%testing_database_host',
314 'port' => '',
315 'database_name' => '%testing_database_name',
316 'user' => '%testing_database_user',
317 'password' => '%testing_database_password',
318 'options' => ''
322 // If you want to write/delete/create files or directories using ftp instead of local file
323 // access, you can set an ftp connection string like:
324 // \$ftp_settings = 'ftp://username:password@example.com/path/to_your/base/dir';
325 \$ftp_settings = '%ftp_settings';
327 // Current environment. Options are: development, testing and production
328 defined('AK_ENVIRONMENT') ? null : define('AK_ENVIRONMENT', 'development');
332 // Locale settings ( you must create a file at /config/locales/ using en.php as departure point)
333 // Please be aware that your charset needs to be UTF-8 in order to edit the locales files
334 // auto will enable all the locales at config/locales/ dir
335 define('AK_AVAILABLE_LOCALES', '%locales');
337 // Use this in order to allow only these locales on web requests
338 define('AK_ACTIVE_RECORD_DEFAULT_LOCALES', '%locales');
339 define('AK_APP_LOCALES', '%locales');
340 define('AK_PUBLIC_LOCALES', '%locales');
342 %AK_URL_REWRITINGdefined('AK_URL_REWRITE_ENABLED') ? null : define('AK_URL_REWRITE_ENABLED', true);
344 %AK_FRAMEWORK_DIR
346 include_once(dirname(__FILE__).DIRECTORY_SEPARATOR.'boot.php');
350 CONFIG;
351 if(empty($settings)){
352 $settings = array();
353 foreach (array('production','development','testing') as $mode){
354 $settings['%'.$mode.'_database_type'] = $this->getDatabaseType($mode);
355 if($settings['%'.$mode.'_database_type'] == 'sqlite'){
357 $settings['%'.$mode.'_database_file'] = AK_CONFIG_DIR.DS.$this->getDatabaseName($mode).'-'.$this->random.'.sqlite';
358 $settings['%'.$mode.'_database_user'] =
359 $settings['%'.$mode.'_database_password'] =
360 $settings['%'.$mode.'_database_host'] =
361 $settings['%'.$mode.'_database_name'] = '';
362 }else{
363 $settings['%'.$mode.'_database_user'] = $this->getDatabaseUser($mode);
364 $settings['%'.$mode.'_database_password'] = $this->getDatabasePassword($mode);
365 $settings['%'.$mode.'_database_host'] = $this->getDatabaseHost($mode);
366 $settings['%'.$mode.'_database_name'] = $this->getDatabaseName($mode);
367 $settings['%'.$mode.'_database_file'] = '';
371 $settings['%ftp_settings'] = isset($this->ftp_enabled) ? 'ftp://'.$this->getFtpUser().':'.$this->getFtpPassword().'@'.$this->getFtpHost().$this->getFtpPath() : '';
374 $settings['%locales'] = $this->getLocales();
376 $settings['%AK_URL_REWRITING'] = $this->isUrlRewriteEnabled() ? '' : "// The web configuration wizard could not detect if you have mod_rewrite enabled. \n// If that is the case, you should uncomment the next line line for better performance. \n// ";
377 $settings['%AK_FRAMEWORK_DIR'] = defined('AK_FRAMEWORK_DIR') ?
378 "defined('AK_FRAMEWORK_DIR') ? null : define('AK_FRAMEWORK_DIR', '".AK_FRAMEWORK_DIR."');" : '';
381 return str_replace(array_keys($settings), array_values($settings), $configuration_template);
385 function writeConfigurationFile($configuration_details)
387 if($this->canWriteConfigurationFile()){
388 return Ak::file_put_contents(AK_CONFIG_DIR.DS.'config.php', $configuration_details);
390 return false;
393 function canWriteConfigurationFile()
395 if(isset($this->ftp_enabled)){
396 $this->testFtpSettings();
398 $file_path = AK_CONFIG_DIR.DS.'config.php';
399 return !file_exists($file_path);
402 function writeRoutesFile()
404 if(isset($this->ftp_enabled)){
405 $this->testFtpSettings();
407 $file_path = AK_CONFIG_DIR.DS.'routes.php';
408 if(!file_exists($file_path)){
409 return Ak::file_put_contents($file_path, file_get_contents(AK_CONFIG_DIR.DS.'DEFAULT-routes.php'));
411 return false;
414 function modifyHtaccessFiles()
416 if($this->isUrlRewriteEnabled()){
417 return true;
420 if(isset($this->ftp_enabled)){
421 $this->testFtpSettings();
423 $file_1 = AK_BASE_DIR.DS.'.htaccess';
424 $file_2 = AK_PUBLIC_DIR.DS.'.htaccess';
425 $file_1_content = @Ak::file_get_contents($file_1);
426 $file_2_content = @Ak::file_get_contents($file_2);
428 $url_suffix = $this->getUrlSuffix();
430 $url_suffix = $url_suffix[0] != '/' ? '/'.$url_suffix : $url_suffix;
432 empty($file_1_content) ? null : @Ak::file_put_contents($file_1, str_replace('# RewriteBase /framework',' RewriteBase '.$url_suffix, $file_1_content));
433 empty($file_2_content) ? null : @Ak::file_put_contents($file_2, str_replace('# RewriteBase /framework',' RewriteBase '.$url_suffix, $file_2_content));
436 function isUrlRewriteEnabled()
438 return @file_get_contents(AK_SITE_URL.'/framework_setup/url_rewrite_check') == 'url_rewrite_working';
441 function getApplicationName()
443 if(!isset($this->application_name)){
444 $this->setApplicationName($this->guessApplicationName());
446 return $this->application_name;
450 function setApplicationName($application_name)
452 $this->application_name = $application_name;
455 function guessApplicationName()
457 $application_name = array_pop(explode('/',AK_SITE_URL_SUFFIX));
458 $application_name = empty($application_name) ? substr(AK_BASE_DIR, strrpos(AK_BASE_DIR, DS)+1) : $application_name;
459 return empty($application_name) ? 'my_app' : $application_name;
462 function needsFtpFileHandling()
464 return !$this->_writeToTemporaryFile(AK_CONFIG_DIR.DS.'test_file.txt');
467 function _writeToTemporaryFile($file_path, $content = '', $mode = 'a+')
469 $result = false;
470 if(strstr($file_path, AK_BASE_DIR)){
471 if(!$fp = @fopen($file_path, $mode)) {
472 return false;
474 $this->_temporaryFilesCleanUp($file_path);
475 $result = @fwrite($fp, $content);
476 if (false !== $result){
477 $result = true;
479 @fclose($fp);
481 return $result;
484 function _temporaryFilesCleanUp($file_path = null)
486 static $file_paths = array();
487 if($file_path == null && count($file_paths) > 0){
488 foreach ($file_paths as $file_path){
489 // we try to prevent removing nothing outside the framework
490 if(strstr($file_path, AK_BASE_DIR)){
491 @unlink($file_path);
494 return ;
495 }elseif (!empty($file_path) && count($file_paths) == 0){
496 register_shutdown_function(array($this, '_temporaryFilesCleanUp'));
498 $file_paths[$file_path] = $file_path;
501 function addSetupOptions($options = array())
503 $options = array_merge($this->getDefaultOptions(), $options);
505 if(!$this->isDatabaseDriverAvalible($options['database']['type'])){
506 $this->addError(Ak::t('Your current PHP settings do not have support for %database_type databases.',
507 array('%database_type'=>$options['database']['type']),'framework_setup'));
508 }elseif(!$db = $this->databaseConnection(
509 $options['database']['type'],
510 $options['database']['host'],
511 $options['database']['user'],
512 $options['database']['password'],
513 $options['database']['name'])){
514 $this->addError(Ak::t('Could not connect to the database using %details', array(), 'framework_setup'),
515 array('%details'=>var_dump($options['database'])));
518 $options['server']['locales'] = str_replace(' ','', $options['server']['locales']);
519 $options['server']['locales'] = empty($options['server']['locales']) ? 'en' : $options['server']['locales'];
521 foreach ($options as $group=>$details){
522 if(!is_array($details)){
523 continue;
525 foreach ($details as $detail=>$value){
526 $this->{$group.'_'.$detail} = $value;
530 $this->options = $options;
533 function getSetupOptions()
535 return isset($this->options) ? $this->options : array();
538 function addError($error)
540 $this->errors[] = $error;
543 function getErrors()
545 return $this->errors;
548 function getDefaultOptions()
550 return array(
551 'production_database_type'=> $this->getDatabaseType('production'),
552 'production_database_host'=> $this->getDatabaseHost('production'),
553 'production_database_name'=> $this->getDatabaseName('production'),
554 'production_database_user'=> $this->getDatabaseUser('production'),
555 'production_database_password'=> '',
557 'development_database_type'=> $this->getDatabaseType('development'),
558 'development_database_host'=> $this->getDatabaseHost('development'),
559 'development_database_name'=> $this->getDatabaseName('development'),
560 'development_database_user'=> $this->getDatabaseUser('development'),
561 'development_database_password'=> '',
563 'testing_database_type'=> $this->getDatabaseType('testing'),
564 'testing_database_host'=> $this->getDatabaseHost('testing'),
565 'testing_database_name'=> $this->getDatabaseName('testing'),
566 'testing_database_user'=> $this->getDatabaseUser('testing'),
567 'testing_database_password'=> '',
569 'admin_database_user' => $this->getDatabaseAdminUser(),
570 'admin_database_password' => $this->getDatabaseAdminPassword(),
572 'url_suffix'=> trim(AK_SITE_URL_SUFFIX, '/'),
573 'locales'=> join(',',$this->suggestLocales()),
574 'ftp_user' => $this->getFtpUser(),
575 'ftp_host' => $this->getFtpHost(),
576 'ftp_path' => $this->getFtpPath(),
578 'random' => Ak::randomString(),
582 function canUseFtpFileHandling()
584 return function_exists('ftp_connect');
587 function getFtpHost()
589 if(!isset($this->ftp_host)){
590 return array_shift(explode('/', str_replace(array('http://','https://','www.'),array('','','ftp.'), AK_SITE_URL).'/'));
592 return $this->ftp_host;
595 function setFtpHost($ftp_host)
597 $this->ftp_host = trim($ftp_host, '/');
600 function getFtpPath()
602 if(!isset($this->ftp_path)){
603 return '/'.trim(join('/',array_slice(
604 explode('/',
605 str_replace(array('http://','https://'),'', AK_SITE_URL).'/'),1)
606 ),'/');
608 return $this->ftp_path;
611 function setFtpPath($ftp_path)
613 $this->ftp_path = empty($ftp_path) ? '' : '/'.trim($ftp_path,'/');
616 function getFtpUser()
618 return !isset($this->ftp_user) ? $this->suggestUserName() : $this->ftp_user;
621 function setFtpUser($ftp_user)
623 $this->ftp_user = $ftp_user;
626 function getFtpPassword()
628 return !isset($this->ftp_password) ? '' : $this->ftp_password;
631 function setFtpPassword($ftp_password)
633 $this->ftp_password = $ftp_password;
637 function setDefaultOptions()
639 foreach ($this->getDefaultOptions() as $k=>$v){
640 $this->$k = $v;
644 function hasUrlSuffix()
646 return !empty($this->url_suffix) && trim($this->url_suffix,'/') != '';
649 function suggestUserName()
651 if(AK_OS === 'WINDOWS'){
652 return 'root';
654 $script_owner = get_current_user();
655 return $script_owner == '' ? 'root' : $script_owner;
659 function testFtpSettings()
661 if(!$this->canUseFtpFileHandling()){
662 return false;
665 $ftp_path = 'ftp://'.$this->getFtpUser().':'.$this->getFtpPassword().'@'.
666 $this->getFtpHost().$this->getFtpPath();
668 @define('AK_UPLOAD_FILES_USING_FTP', true);
669 @define('AK_READ_FILES_USING_FTP', false);
670 @define('AK_DELETE_FILES_USING_FTP', true);
671 @define('AK_FTP_PATH', $ftp_path);
672 @define('AK_FTP_AUTO_DISCONNECT', true);
674 if(@Ak::file_put_contents(AK_CONFIG_DIR.DS.'test_file.txt','hello from ftp')){
675 $text = @Ak::file_get_contents(AK_CONFIG_DIR.DS.'test_file.txt');
676 @Ak::file_delete(AK_CONFIG_DIR.DS.'test_file.txt');
679 $this->ftp_enabled = (isset($text) && $text == 'hello from ftp');
681 return $this->ftp_enabled;
684 function getLocales()
686 return join(',',!isset($this->locales) ? $this->suggestLocales() : $this->_getLocales($this->locales));
689 function setLocales($locales)
691 $this->locales = $this->_getLocales($locales);
694 function _getLocales($locales)
696 return array_map('trim',array_unique(array_diff((is_array($locales) ? $locales : explode(',',$locales.',')), array(''))));
699 function suggestLocales()
701 require_once(AK_LIB_DIR.DS.'AkLocaleManager.php');
703 $LocaleManager = new AkLocaleManager();
705 $langs = array('en');
706 if(AK_OS === 'WINDOWS'){
707 $langs[] = @$_ENV['LANG'];
709 $langs = array_merge($langs, $LocaleManager->getBrowserLanguages());
711 return array_unique(array_map('strtolower',array_diff($langs,array(''))));
714 function suggestDatabaseHost()
716 return 'localhost';
719 function relativizeStylesheetPaths()
721 $asset_path = $this->_getAssetBasePath();
722 if($this->hasUrlSuffix() || !empty($asset_path)){
723 $url_suffix = trim($this->getUrlSuffix(),'/');
724 if(!empty($asset_path)){
725 $url_suffix = trim($url_suffix.'/'.$asset_path,'/');
727 foreach ($this->stylesheets as $stylesheet) {
728 $filename = AK_PUBLIC_DIR.DS.'stylesheets'.DS.$stylesheet.'.css';
729 $relativized_css = preg_replace("/url\((\'|\")?\/images/","url($1/$url_suffix/images", @Ak::file_get_contents($filename));
730 empty($relativized_css) ? null : @Ak::file_put_contents($filename, $relativized_css);
735 function _getAssetBasePath()
737 return defined('AK_INSECURE_APP_DIRECTORY_LAYOUT') && AK_INSECURE_APP_DIRECTORY_LAYOUT ? 'public' : '';
740 function removeSetupFiles()
742 @array_map(array('Ak','file_delete'), array(
743 AK_APP_DIR.DS.'installers'.DS.'database_installer.php',
744 AK_APP_DIR.DS.'installers'.DS.'framework_installer.php',
745 AK_APP_DIR.DS.'installers'.DS.'database_version.txt',
746 AK_APP_DIR.DS.'installers'.DS.'framework_version.txt',
747 AK_APP_DIR.DS.'models'.DS.'framework_setup.php',
748 AK_APP_DIR.DS.'controllers'.DS.'framework_setup_controller.php'
750 @array_map(array('Ak','directory_delete'), array(
751 AK_APP_DIR.DS.'views'.DS.'framework_setup',
752 AK_APP_DIR.DS.'locales'.DS.'framework_setup'