2008-05-30 11:40:08 +00:00
< ? php
/**
* The Project Task handles creating the base application
*
*
2010-10-03 16:38:58 +00:00
* PHP 5
2008-05-30 11:40:08 +00:00
*
2009-11-06 06:46:59 +00:00
* CakePHP ( tm ) : Rapid Development Framework ( http :// cakephp . org )
2010-01-26 19:18:20 +00:00
* Copyright 2005 - 2010 , Cake Software Foundation , Inc . ( http :// cakefoundation . org )
2008-05-30 11:40:08 +00:00
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice .
*
2010-01-26 19:18:20 +00:00
* @ copyright Copyright 2005 - 2010 , Cake Software Foundation , Inc . ( http :// cakefoundation . org )
2009-11-06 06:00:11 +00:00
* @ link http :// cakephp . org CakePHP ( tm ) Project
2008-10-30 17:30:26 +00:00
* @ package cake
2009-06-17 03:05:48 +00:00
* @ subpackage cake . cake . console . bake
2008-10-30 17:30:26 +00:00
* @ since CakePHP ( tm ) v 1.2
2009-11-06 06:51:51 +00:00
* @ license MIT License ( http :// www . opensource . org / licenses / mit - license . php )
2008-05-30 11:40:08 +00:00
*/
2010-10-30 03:26:18 +00:00
App :: import ( 'Core' , 'File' );
2008-05-30 11:40:08 +00:00
/**
* Task class for creating new project apps and plugins
*
2008-10-30 17:30:26 +00:00
* @ package cake
* @ subpackage cake . cake . console . libs . tasks
2008-05-30 11:40:08 +00:00
*/
class ProjectTask extends Shell {
2009-07-24 19:18:37 +00:00
2009-07-01 00:44:09 +00:00
/**
* configs path ( used in testing ) .
*
* @ var string
2009-11-14 12:19:25 +00:00
*/
2010-04-04 07:14:00 +00:00
public $configPath = null ;
2009-07-24 19:18:37 +00:00
2008-05-30 11:40:08 +00:00
/**
* Checks that given project path does not already exist , and
* finds the app directory in it . Then it calls bake () with that information .
*
* @ param string $project Project path
*/
2010-10-16 05:02:15 +00:00
public function execute () {
$project = null ;
if ( isset ( $this -> args [ 0 ])) {
$project = $this -> args [ 0 ];
2008-05-30 11:40:08 +00:00
}
2010-10-18 02:38:32 +00:00
if ( $project && isset ( $_SERVER [ 'PWD' ])) {
$project = $_SERVER [ 'PWD' ] . DS . $project ;
2008-05-30 11:40:08 +00:00
}
if ( empty ( $this -> params [ 'skel' ])) {
2010-10-18 02:38:32 +00:00
$core = App :: core ( 'shells' );
$skelPath = dirname ( $core [ 0 ]) . DS . 'templates' . DS . 'skel' ;
if ( is_dir ( $skelPath ) === true ) {
$this -> params [ 'skel' ] = $skelPath ;
2008-05-30 11:40:08 +00:00
}
}
while ( ! $project ) {
2010-04-15 15:43:39 +00:00
$prompt = __ ( " What is the full path for this app including the app directory name? \n Example: " );
2010-10-23 04:35:53 +00:00
$default = APP_PATH . 'myapp' ;
2009-08-05 01:41:57 +00:00
$project = $this -> in ( $prompt . $default , null , $default );
2008-05-30 11:40:08 +00:00
}
if ( $project ) {
$response = false ;
while ( $response == false && is_dir ( $project ) === true && file_exists ( $project . 'config' . 'core.php' )) {
2010-12-05 01:37:13 +00:00
$prompt = __ ( '<warning>A project already exists in this location:</warning> %s Overwrite?' , $project );
2009-08-05 01:41:57 +00:00
$response = $this -> in ( $prompt , array ( 'y' , 'n' ), 'n' );
2008-05-30 11:40:08 +00:00
if ( strtolower ( $response ) === 'n' ) {
$response = $project = false ;
}
}
}
2010-10-18 02:38:32 +00:00
$success = true ;
2008-10-23 00:10:44 +00:00
if ( $this -> bake ( $project )) {
2008-05-30 11:40:08 +00:00
$path = Folder :: slashTerm ( $project );
if ( $this -> createHome ( $path )) {
2010-10-18 02:38:32 +00:00
$this -> out ( __ ( ' * Welcome page created' ));
2008-05-30 11:40:08 +00:00
} else {
2010-10-18 02:38:32 +00:00
$this -> err ( __ ( 'The Welcome page was <error>NOT</error> created' ));
$success = false ;
2008-05-30 11:40:08 +00:00
}
2010-10-18 02:38:32 +00:00
if ( $this -> securitySalt ( $path ) === true ) {
$this -> out ( __ ( ' * Random hash key created for \'Security.salt\'' ));
2008-05-30 11:40:08 +00:00
} else {
2010-12-05 01:37:13 +00:00
$this -> err ( __ ( 'Unable to generate random hash for \'Security.salt\', you should change it in %s' , CONFIGS . 'core.php' ));
2010-10-18 02:38:32 +00:00
$success = false ;
2008-05-30 11:40:08 +00:00
}
2010-10-18 02:38:32 +00:00
if ( $this -> securityCipherSeed ( $path ) === true ) {
$this -> out ( __ ( ' * Random seed created for \'Security.cipherSeed\'' ));
2010-01-25 16:14:18 +00:00
} else {
2010-12-05 01:37:13 +00:00
$this -> err ( __ ( 'Unable to generate random seed for \'Security.cipherSeed\', you should change it in %s' , CONFIGS . 'core.php' ));
2010-10-18 02:38:32 +00:00
$success = false ;
2010-01-25 16:14:18 +00:00
}
2010-10-18 02:38:32 +00:00
if ( $this -> corePath ( $path ) === true ) {
2010-12-05 01:37:13 +00:00
$this -> out ( __ ( ' * CAKE_CORE_INCLUDE_PATH set to %s in webroot/index.php' , CAKE_CORE_INCLUDE_PATH ));
$this -> out ( __ ( ' * CAKE_CORE_INCLUDE_PATH set to %s in webroot/test.php' , CAKE_CORE_INCLUDE_PATH ));
2010-10-18 02:38:32 +00:00
$this -> out ( __ ( ' * <warning>Remember to check these value after moving to production server</warning>' ));
} else {
2010-12-05 01:37:13 +00:00
$this -> err ( __ ( 'Unable to set CAKE_CORE_INCLUDE_PATH, you should change it in %s' , $path . 'webroot' . DS . 'index.php' ));
2010-10-18 02:38:32 +00:00
$success = false ;
2008-05-30 11:40:08 +00:00
}
2010-10-18 02:38:32 +00:00
if ( $this -> consolePath ( $path ) === true ) {
$this -> out ( __ ( ' * app/console/cake.php path set.' ));
} else {
$this -> err ( __ ( 'Unable to set console path for app/console.' ));
$success = false ;
}
2008-05-30 11:40:08 +00:00
$Folder = new Folder ( $path );
if ( ! $Folder -> chmod ( $path . 'tmp' , 0777 )) {
2010-12-05 01:37:13 +00:00
$this -> err ( __ ( 'Could not set permissions on %s' , $path . DS . 'tmp' ));
$this -> out ( __ ( 'chmod -R 0777 %s' , $path . DS . 'tmp' ));
2010-10-18 02:38:32 +00:00
$success = false ;
2008-05-30 11:40:08 +00:00
}
2010-10-18 02:38:32 +00:00
if ( $success ) {
$this -> out ( __ ( '<success>Project baked successfully!</success>' ));
} else {
$this -> out ( __ ( 'Project baked but with <warning>some issues.</warning>.' ));
2008-05-30 11:40:08 +00:00
}
2010-10-23 04:35:53 +00:00
return $path ;
2008-05-30 11:40:08 +00:00
}
}
2009-07-24 19:18:37 +00:00
2008-05-30 11:40:08 +00:00
/**
* Looks for a skeleton template of a Cake application ,
* and if not found asks the user for a path . When there is a path
* this method will make a deep copy of the skeleton to the project directory .
* A default home page will be added , and the tmp file storage will be chmod ' ed to 0777.
*
* @ param string $path Project path
* @ param string $skel Path to copy from
* @ param string $skip array of directories to skip when copying
* @ access private
*/
function bake ( $path , $skel = null , $skip = array ( 'empty' )) {
2008-10-23 00:10:44 +00:00
if ( ! $skel ) {
2008-05-30 11:40:08 +00:00
$skel = $this -> params [ 'skel' ];
}
while ( ! $skel ) {
2010-12-05 01:37:13 +00:00
$skel = $this -> in ( __ ( " What is the path to the directory layout you wish to copy? \n Example: %s " , APP , null , ROOT . DS . 'myapp' . DS ));
2008-05-30 11:40:08 +00:00
if ( $skel == '' ) {
2010-10-16 05:02:15 +00:00
$this -> err ( __ ( 'The directory path you supplied was empty. Please try again.' ));
2008-05-30 11:40:08 +00:00
} else {
while ( is_dir ( $skel ) === false ) {
2010-04-15 15:43:39 +00:00
$skel = $this -> in ( __ ( 'Directory path does not exist please choose another:' ));
2008-05-30 11:40:08 +00:00
}
}
}
$app = basename ( $path );
2010-10-18 02:38:32 +00:00
$this -> out ( __ ( '<info>Skel Directory</info>: ' ) . $skel );
$this -> out ( __ ( '<info>Will be copied to</info>: ' ) . $path );
2008-05-30 11:40:08 +00:00
$this -> hr ();
2010-04-15 15:43:39 +00:00
$looksGood = $this -> in ( __ ( 'Look okay?' ), array ( 'y' , 'n' , 'q' ), 'y' );
2008-05-30 11:40:08 +00:00
2009-06-17 03:05:48 +00:00
if ( strtolower ( $looksGood ) == 'y' ) {
2008-05-30 11:40:08 +00:00
$Folder = new Folder ( $skel );
2009-11-16 23:04:33 +00:00
if ( ! empty ( $this -> params [ 'empty' ])) {
$skip = array ();
}
2010-10-17 23:01:44 +00:00
2008-05-30 11:40:08 +00:00
if ( $Folder -> copy ( array ( 'to' => $path , 'skip' => $skip ))) {
$this -> hr ();
2010-12-05 01:37:13 +00:00
$this -> out ( __ ( '<success>Created:</success> %s in %s' , $app , $path ));
2008-05-30 11:40:08 +00:00
$this -> hr ();
} else {
2010-12-05 01:37:13 +00:00
$this -> err ( __ ( " <error>Could not create</error> '%s' properly. " , $app ));
2008-05-30 11:40:08 +00:00
return false ;
}
2010-10-16 05:02:15 +00:00
foreach ( $Folder -> messages () as $message ) {
2010-10-18 02:38:32 +00:00
$this -> out ( String :: wrap ( ' * ' . $message ), 1 , Shell :: VERBOSE );
2008-05-30 11:40:08 +00:00
}
return true ;
2009-06-17 03:05:48 +00:00
} elseif ( strtolower ( $looksGood ) == 'q' ) {
2010-04-15 15:43:39 +00:00
$this -> out ( __ ( 'Bake Aborted.' ));
2008-05-30 11:40:08 +00:00
} else {
$this -> execute ( false );
return false ;
}
}
2009-07-24 19:18:37 +00:00
2008-05-30 11:40:08 +00:00
/**
* Writes a file with a default home page to the project .
*
* @ param string $dir Path to project
* @ return boolean Success
*/
2010-04-05 03:19:38 +00:00
public function createHome ( $dir ) {
2008-05-30 11:40:08 +00:00
$app = basename ( $dir );
$path = $dir . 'views' . DS . 'pages' . DS ;
2009-09-06 05:04:38 +00:00
$source = CAKE . 'console' . DS . 'templates' . DS . 'default' . DS . 'views' . DS . 'home.ctp' ;
2009-07-26 05:52:05 +00:00
include ( $source );
2008-05-30 11:40:08 +00:00
return $this -> createFile ( $path . 'home.ctp' , $output );
}
2009-07-24 19:18:37 +00:00
2010-10-18 02:38:32 +00:00
/**
* Generates the correct path to the CakePHP libs that are generating the project
* and points app / console / cake . php to the right place
*
* @ param string $path Project path .
* @ return boolean success
*/
public function consolePath ( $path ) {
$File = new File ( $path . 'console' . DS . 'cake.php' );
$contents = $File -> read ();
if ( preg_match ( '/(__CAKE_PATH__)/' , $contents , $match )) {
$path = CAKE_CORE_INCLUDE_PATH . DS . 'cake' . DS . 'console' . DS ;
$replacement = " ' " . str_replace ( DS , " ' . DIRECTORY_SEPARATOR . ' " , $path ) . " ' " ;
$result = str_replace ( $match [ 0 ], $replacement , $contents );
if ( $File -> write ( $result )) {
return true ;
}
return false ;
}
return false ;
}
2008-05-30 11:40:08 +00:00
/**
* Generates and writes 'Security.salt'
*
* @ param string $path Project path
* @ return boolean Success
*/
2010-04-05 03:19:38 +00:00
public function securitySalt ( $path ) {
2010-10-18 01:04:14 +00:00
$File = new File ( $path . 'config' . DS . 'core.php' );
2008-05-30 11:40:08 +00:00
$contents = $File -> read ();
2010-10-17 23:07:57 +00:00
if ( preg_match ( '/([\s]*Configure::write\(\'Security.salt\',[\s\'A-z0-9]*\);)/' , $contents , $match )) {
2008-06-11 15:46:31 +00:00
if ( ! class_exists ( 'Security' )) {
2009-07-23 20:53:37 +00:00
require LIBS . 'security.php' ;
2008-06-11 15:46:31 +00:00
}
2008-05-30 11:40:08 +00:00
$string = Security :: generateAuthKey ();
$result = str_replace ( $match [ 0 ], " \t " . 'Configure::write(\'Security.salt\', \'' . $string . '\');' , $contents );
if ( $File -> write ( $result )) {
return true ;
}
return false ;
}
2008-09-08 20:33:48 +00:00
return false ;
2008-05-30 11:40:08 +00:00
}
2009-07-24 19:18:37 +00:00
2010-10-18 02:38:32 +00:00
/**
* Generates and writes 'Security.cipherSeed'
*
* @ param string $path Project path
* @ return boolean Success
*/
public function securityCipherSeed ( $path ) {
$File = new File ( $path . 'config' . DS . 'core.php' );
$contents = $File -> read ();
if ( preg_match ( '/([\s]*Configure::write\(\'Security.cipherSeed\',[\s\'A-z0-9]*\);)/' , $contents , $match )) {
if ( ! class_exists ( 'Security' )) {
require LIBS . 'security.php' ;
}
$string = substr ( bin2hex ( Security :: generateAuthKey ()), 0 , 30 );
$result = str_replace ( $match [ 0 ], " \t " . 'Configure::write(\'Security.cipherSeed\', \'' . $string . '\');' , $contents );
if ( $File -> write ( $result )) {
return true ;
2010-01-25 16:14:18 +00:00
}
return false ;
}
2010-10-18 02:38:32 +00:00
return false ;
}
2010-01-25 16:14:18 +00:00
2008-05-30 11:40:08 +00:00
/**
* Generates and writes CAKE_CORE_INCLUDE_PATH
*
* @ param string $path Project path
* @ return boolean Success
*/
2010-04-05 03:19:38 +00:00
public function corePath ( $path ) {
2008-05-30 11:40:08 +00:00
if ( dirname ( $path ) !== CAKE_CORE_INCLUDE_PATH ) {
2010-10-18 02:38:32 +00:00
$File = new File ( $path . 'webroot' . DS . 'index.php' );
2008-05-30 11:40:08 +00:00
$contents = $File -> read ();
2010-10-17 23:07:57 +00:00
if ( preg_match ( '/([\s]*define\(\'CAKE_CORE_INCLUDE_PATH\',[\s\'A-z0-9]*\);)/' , $contents , $match )) {
2009-11-10 01:10:24 +00:00
$root = strpos ( CAKE_CORE_INCLUDE_PATH , '/' ) === 0 ? " DS . ' " : " ' " ;
$result = str_replace ( $match [ 0 ], " \t \t define('CAKE_CORE_INCLUDE_PATH', " . $root . str_replace ( DS , " ' . DS . ' " , trim ( CAKE_CORE_INCLUDE_PATH , DS )) . " '); " , $contents );
2008-05-30 11:40:08 +00:00
if ( ! $File -> write ( $result )) {
return false ;
}
} else {
return false ;
}
2010-10-18 02:38:32 +00:00
$File = new File ( $path . 'webroot' . DS . 'test.php' );
2008-05-30 11:40:08 +00:00
$contents = $File -> read ();
2010-10-17 23:07:57 +00:00
if ( preg_match ( '/([\s]*define\(\'CAKE_CORE_INCLUDE_PATH\',[\s\'A-z0-9]*\);)/' , $contents , $match )) {
2009-11-10 14:17:54 +00:00
$result = str_replace ( $match [ 0 ], " \t \t define('CAKE_CORE_INCLUDE_PATH', " . $root . str_replace ( DS , " ' . DS . ' " , trim ( CAKE_CORE_INCLUDE_PATH , DS )) . " '); " , $contents );
2008-05-30 11:40:08 +00:00
if ( ! $File -> write ( $result )) {
return false ;
}
} else {
return false ;
}
return true ;
}
}
2009-07-24 19:18:37 +00:00
2008-05-30 11:40:08 +00:00
/**
2009-12-09 04:52:02 +00:00
* Enables Configure :: read ( 'Routing.prefixes' ) in / app / config / core . php
2008-05-30 11:40:08 +00:00
*
* @ param string $name Name to use as admin routing
* @ return boolean Success
*/
2010-04-05 03:19:38 +00:00
public function cakeAdmin ( $name ) {
2009-07-01 00:44:09 +00:00
$path = ( empty ( $this -> configPath )) ? CONFIGS : $this -> configPath ;
2010-10-18 02:38:32 +00:00
$File = new File ( $path . 'core.php' );
2008-05-30 11:40:08 +00:00
$contents = $File -> read ();
2010-10-17 23:07:57 +00:00
if ( preg_match ( '%([/\s]*Configure::write\(\'Routing.prefixes\',[\s\'a-z,\)\(]*\);)%' , $contents , $match )) {
2009-10-07 04:46:13 +00:00
$result = str_replace ( $match [ 0 ], " \t " . 'Configure::write(\'Routing.prefixes\', array(\'' . $name . '\'));' , $contents );
2008-05-30 11:40:08 +00:00
if ( $File -> write ( $result )) {
2009-10-07 04:46:13 +00:00
Configure :: write ( 'Routing.prefixes' , array ( $name ));
2008-05-30 11:40:08 +00:00
return true ;
} else {
return false ;
}
} else {
return false ;
}
}
2009-07-24 19:18:37 +00:00
2009-07-01 00:44:09 +00:00
/**
2009-12-09 04:52:02 +00:00
* Checks for Configure :: read ( 'Routing.prefixes' ) and forces user to input it if not enabled
2009-07-01 00:44:09 +00:00
*
* @ return string Admin route to use
*/
2010-04-05 03:19:38 +00:00
public function getPrefix () {
2009-07-01 00:44:09 +00:00
$admin = '' ;
2009-10-07 04:46:13 +00:00
$prefixes = Configure :: read ( 'Routing.prefixes' );
if ( ! empty ( $prefixes )) {
2010-03-07 22:14:13 +00:00
if ( count ( $prefixes ) == 1 ) {
return $prefixes [ 0 ] . '_' ;
}
2009-11-11 14:56:35 +00:00
if ( $this -> interactive ) {
$this -> out ();
2010-04-15 15:43:39 +00:00
$this -> out ( __ ( 'You have more than one routing prefix configured' ));
2009-11-11 14:56:35 +00:00
}
2009-10-07 04:46:13 +00:00
$options = array ();
foreach ( $prefixes as $i => $prefix ) {
$options [] = $i + 1 ;
2009-11-11 14:56:35 +00:00
if ( $this -> interactive ) {
$this -> out ( $i + 1 . '. ' . $prefix );
}
2009-10-07 04:46:13 +00:00
}
2010-04-15 15:43:39 +00:00
$selection = $this -> in ( __ ( 'Please choose a prefix to bake with.' ), $options , 1 );
2009-10-07 04:46:13 +00:00
return $prefixes [ $selection - 1 ] . '_' ;
2009-07-01 00:44:09 +00:00
}
2009-11-11 14:56:35 +00:00
if ( $this -> interactive ) {
$this -> hr ();
2009-10-07 04:46:13 +00:00
$this -> out ( 'You need to enable Configure::write(\'Routing.prefixes\',array(\'admin\')) in /app/config/core.php to use prefix routing.' );
2010-04-15 15:43:39 +00:00
$this -> out ( __ ( 'What would you like the prefix route to be?' ));
$this -> out ( __ ( 'Example: www.example.com/admin/controller' ));
2009-11-11 14:56:35 +00:00
while ( $admin == '' ) {
2010-04-15 15:43:39 +00:00
$admin = $this -> in ( __ ( 'Enter a routing prefix:' ), null , 'admin' );
2009-11-11 14:56:35 +00:00
}
if ( $this -> cakeAdmin ( $admin ) !== true ) {
2010-10-17 23:07:57 +00:00
$this -> out ( __ ( '<error>Unable to write to</error> /app/config/core.php.' ));
2009-11-11 14:56:35 +00:00
$this -> out ( 'You need to enable Configure::write(\'Routing.prefixes\',array(\'admin\')) in /app/config/core.php to use prefix routing.' );
$this -> _stop ();
}
return $admin . '_' ;
2009-07-01 00:44:09 +00:00
}
2009-11-11 14:56:35 +00:00
return '' ;
2009-07-01 00:44:09 +00:00
}
2009-07-24 19:18:37 +00:00
2008-05-30 11:40:08 +00:00
/**
2010-10-13 02:55:19 +00:00
* get the option parser .
2008-05-30 11:40:08 +00:00
*
2010-10-13 02:55:19 +00:00
* @ return ConsoleOptionParser
2008-05-30 11:40:08 +00:00
*/
2010-10-13 02:55:19 +00:00
public function getOptionParser () {
$parser = parent :: getOptionParser ();
return $parser -> description (
__ ( 'Generate a new CakePHP project skeleton.' )
) -> addArgument ( 'name' , array (
'help' => __ ( 'Application directory to make, if it starts with "/" the path is absolute.' )
2010-10-17 23:01:44 +00:00
)) -> addOption ( 'empty' , array (
'help' => __ ( 'Create empty files in each of the directories. Good if you are using git' )
2010-10-18 02:38:32 +00:00
)) -> addOption ( 'skel' , array (
'help' => __ ( 'The directory layout to use for the new application skeleton. Defaults to cake/console/templates/skel of CakePHP used to create the project.' )
2010-10-13 02:55:19 +00:00
));
2008-05-30 11:40:08 +00:00
}
}