diff options
Diffstat (limited to 'framework/cli')
84 files changed, 4797 insertions, 0 deletions
diff --git a/framework/cli/commands/MessageCommand.php b/framework/cli/commands/MessageCommand.php new file mode 100644 index 0000000..410e275 --- /dev/null +++ b/framework/cli/commands/MessageCommand.php @@ -0,0 +1,211 @@ +<?php +/** + * MessageCommand class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * MessageCommand extracts messages to be translated from source files. + * The extracted messages are saved as PHP message source files + * under the specified directory. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: MessageCommand.php 3394 2011-09-14 21:31:30Z alexander.makarow $ + * @package system.cli.commands + * @since 1.0 + */ +class MessageCommand extends CConsoleCommand +{ + public function getHelp() + { + return <<<EOD +USAGE + yiic message <config-file> + +DESCRIPTION + This command searches for messages to be translated in the specified + source files and compiles them into PHP arrays as message source. + +PARAMETERS + * config-file: required, the path of the configuration file. You can find + an example in framework/messages/config.php. + + The file can be placed anywhere and must be a valid PHP script which + returns an array of name-value pairs. Each name-value pair represents + a configuration option. + + The following options are available: + + - sourcePath: string, root directory of all source files. + - messagePath: string, root directory containing message translations. + - languages: array, list of language codes that the extracted messages + should be translated to. For example, array('zh_cn','en_au'). + - fileTypes: array, a list of file extensions (e.g. 'php', 'xml'). + Only the files whose extension name can be found in this list + will be processed. If empty, all files will be processed. + - exclude: array, a list of directory and file exclusions. Each + exclusion can be either a name or a path. If a file or directory name + or path matches the exclusion, it will not be copied. For example, + an exclusion of '.svn' will exclude all files and directories whose + name is '.svn'. And an exclusion of '/a/b' will exclude file or + directory 'sourcePath/a/b'. + - translator: the name of the function for translating messages. + Defaults to 'Yii::t'. This is used as a mark to find messages to be + translated. + - overwrite: if message file must be overwritten with the merged messages. + - removeOld: if message no longer needs translation it will be removed, + instead of being enclosed between a pair of '@@' marks. + +EOD; + } + + /** + * Execute the action. + * @param array command line parameters specific for this command + */ + public function run($args) + { + if(!isset($args[0])) + $this->usageError('the configuration file is not specified.'); + if(!is_file($args[0])) + $this->usageError("the configuration file {$args[0]} does not exist."); + + $config=require_once($args[0]); + $translator='Yii::t'; + extract($config); + + if(!isset($sourcePath,$messagePath,$languages)) + $this->usageError('The configuration file must specify "sourcePath", "messagePath" and "languages".'); + if(!is_dir($sourcePath)) + $this->usageError("The source path $sourcePath is not a valid directory."); + if(!is_dir($messagePath)) + $this->usageError("The message path $messagePath is not a valid directory."); + if(empty($languages)) + $this->usageError("Languages cannot be empty."); + + if(!isset($overwrite)) + $overwrite = false; + + if(!isset($removeOld)) + $removeOld = false; + + $options=array(); + if(isset($fileTypes)) + $options['fileTypes']=$fileTypes; + if(isset($exclude)) + $options['exclude']=$exclude; + $files=CFileHelper::findFiles(realpath($sourcePath),$options); + + $messages=array(); + foreach($files as $file) + $messages=array_merge_recursive($messages,$this->extractMessages($file,$translator)); + + foreach($languages as $language) + { + $dir=$messagePath.DIRECTORY_SEPARATOR.$language; + if(!is_dir($dir)) + @mkdir($dir); + foreach($messages as $category=>$msgs) + { + $msgs=array_values(array_unique($msgs)); + $this->generateMessageFile($msgs,$dir.DIRECTORY_SEPARATOR.$category.'.php',$overwrite,$removeOld); + } + } + } + + protected function extractMessages($fileName,$translator) + { + echo "Extracting messages from $fileName...\n"; + $subject=file_get_contents($fileName); + $n=preg_match_all('/\b'.$translator.'\s*\(\s*(\'.*?(?<!\\\\)\'|".*?(?<!\\\\)")\s*,\s*(\'.*?(?<!\\\\)\'|".*?(?<!\\\\)")\s*[,\)]/s',$subject,$matches,PREG_SET_ORDER); + $messages=array(); + for($i=0;$i<$n;++$i) + { + if(($pos=strpos($matches[$i][1],'.'))!==false) + $category=substr($matches[$i][1],$pos+1,-1); + else + $category=substr($matches[$i][1],1,-1); + $message=$matches[$i][2]; + $messages[$category][]=eval("return $message;"); // use eval to eliminate quote escape + } + return $messages; + } + + protected function generateMessageFile($messages,$fileName,$overwrite,$removeOld) + { + echo "Saving messages to $fileName..."; + if(is_file($fileName)) + { + $translated=require($fileName); + sort($messages); + ksort($translated); + if(array_keys($translated)==$messages) + { + echo "nothing new...skipped.\n"; + return; + } + $merged=array(); + $untranslated=array(); + foreach($messages as $message) + { + if(!empty($translated[$message])) + $merged[$message]=$translated[$message]; + else + $untranslated[]=$message; + } + ksort($merged); + sort($untranslated); + $todo=array(); + foreach($untranslated as $message) + $todo[$message]=''; + ksort($translated); + foreach($translated as $message=>$translation) + { + if(!isset($merged[$message]) && !isset($todo[$message]) && !$removeOld) + $todo[$message]='@@'.$translation.'@@'; + } + $merged=array_merge($todo,$merged); + if($overwrite === false) + $fileName.='.merged'; + echo "translation merged.\n"; + } + else + { + $merged=array(); + foreach($messages as $message) + $merged[$message]=''; + ksort($merged); + echo "saved.\n"; + } + $array=str_replace("\r",'',var_export($merged,true)); + $content=<<<EOD +<?php +/** + * Message translations. + * + * This file is automatically generated by 'yiic message' command. + * It contains the localizable messages extracted from source code. + * You may modify this file by translating the extracted messages. + * + * Each array element represents the translation (value) of a message (key). + * If the value is empty, the message is considered as not translated. + * Messages that no longer need translation will have their translations + * enclosed between a pair of '@@' marks. + * + * Message string can be used with plural forms format. Check i18n section + * of the guide for details. + * + * NOTE, this file must be saved in UTF-8 encoding. + * + * @version \$Id: \$ + */ +return $array; + +EOD; + file_put_contents($fileName, $content); + } +}
\ No newline at end of file diff --git a/framework/cli/commands/MigrateCommand.php b/framework/cli/commands/MigrateCommand.php new file mode 100644 index 0000000..5111b9a --- /dev/null +++ b/framework/cli/commands/MigrateCommand.php @@ -0,0 +1,561 @@ +<?php +/** + * MigrateCommand class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * MigrateCommand manages the database migrations. + * + * The implementation of this command and other supporting classes referenced + * the yii-dbmigrations extension ((https://github.com/pieterclaerhout/yii-dbmigrations), + * authored by Pieter Claerhout. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: MigrateCommand.php 3514 2011-12-27 20:28:26Z alexander.makarow $ + * @package system.cli.commands + * @since 1.1.6 + */ +class MigrateCommand extends CConsoleCommand +{ + const BASE_MIGRATION='m000000_000000_base'; + + /** + * @var string the directory that stores the migrations. This must be specified + * in terms of a path alias, and the corresponding directory must exist. + * Defaults to 'application.migrations' (meaning 'protected/migrations'). + */ + public $migrationPath='application.migrations'; + /** + * @var string the name of the table for keeping applied migration information. + * This table will be automatically created if not exists. Defaults to 'tbl_migration'. + * The table structure is: (version varchar(255) primary key, apply_time integer) + */ + public $migrationTable='tbl_migration'; + /** + * @var string the application component ID that specifies the database connection for + * storing migration information. Defaults to 'db'. + */ + public $connectionID='db'; + /** + * @var string the path of the template file for generating new migrations. This + * must be specified in terms of a path alias (e.g. application.migrations.template). + * If not set, an internal template will be used. + */ + public $templateFile; + /** + * @var string the default command action. It defaults to 'up'. + */ + public $defaultAction='up'; + /** + * @var boolean whether to execute the migration in an interactive mode. Defaults to true. + * Set this to false when performing migration in a cron job or background process. + */ + public $interactive=true; + + public function beforeAction($action,$params) + { + $path=Yii::getPathOfAlias($this->migrationPath); + if($path===false || !is_dir($path)) + die('Error: The migration directory does not exist: '.$this->migrationPath."\n"); + $this->migrationPath=$path; + + $yiiVersion=Yii::getVersion(); + echo "\nYii Migration Tool v1.0 (based on Yii v{$yiiVersion})\n\n"; + + return true; + } + + public function actionUp($args) + { + if(($migrations=$this->getNewMigrations())===array()) + { + echo "No new migration found. Your system is up-to-date.\n"; + return; + } + + $total=count($migrations); + $step=isset($args[0]) ? (int)$args[0] : 0; + if($step>0) + $migrations=array_slice($migrations,0,$step); + + $n=count($migrations); + if($n===$total) + echo "Total $n new ".($n===1 ? 'migration':'migrations')." to be applied:\n"; + else + echo "Total $n out of $total new ".($total===1 ? 'migration':'migrations')." to be applied:\n"; + + foreach($migrations as $migration) + echo " $migration\n"; + echo "\n"; + + if($this->confirm('Apply the above '.($n===1 ? 'migration':'migrations')."?")) + { + foreach($migrations as $migration) + { + if($this->migrateUp($migration)===false) + { + echo "\nMigration failed. All later migrations are canceled.\n"; + return; + } + } + echo "\nMigrated up successfully.\n"; + } + } + + public function actionDown($args) + { + $step=isset($args[0]) ? (int)$args[0] : 1; + if($step<1) + die("Error: The step parameter must be greater than 0.\n"); + + if(($migrations=$this->getMigrationHistory($step))===array()) + { + echo "No migration has been done before.\n"; + return; + } + $migrations=array_keys($migrations); + + $n=count($migrations); + echo "Total $n ".($n===1 ? 'migration':'migrations')." to be reverted:\n"; + foreach($migrations as $migration) + echo " $migration\n"; + echo "\n"; + + if($this->confirm('Revert the above '.($n===1 ? 'migration':'migrations')."?")) + { + foreach($migrations as $migration) + { + if($this->migrateDown($migration)===false) + { + echo "\nMigration failed. All later migrations are canceled.\n"; + return; + } + } + echo "\nMigrated down successfully.\n"; + } + } + + public function actionRedo($args) + { + $step=isset($args[0]) ? (int)$args[0] : 1; + if($step<1) + die("Error: The step parameter must be greater than 0.\n"); + + if(($migrations=$this->getMigrationHistory($step))===array()) + { + echo "No migration has been done before.\n"; + return; + } + $migrations=array_keys($migrations); + + $n=count($migrations); + echo "Total $n ".($n===1 ? 'migration':'migrations')." to be redone:\n"; + foreach($migrations as $migration) + echo " $migration\n"; + echo "\n"; + + if($this->confirm('Redo the above '.($n===1 ? 'migration':'migrations')."?")) + { + foreach($migrations as $migration) + { + if($this->migrateDown($migration)===false) + { + echo "\nMigration failed. All later migrations are canceled.\n"; + return; + } + } + foreach(array_reverse($migrations) as $migration) + { + if($this->migrateUp($migration)===false) + { + echo "\nMigration failed. All later migrations are canceled.\n"; + return; + } + } + echo "\nMigration redone successfully.\n"; + } + } + + public function actionTo($args) + { + if(isset($args[0])) + $version=$args[0]; + else + $this->usageError('Please specify which version to migrate to.'); + + $originalVersion=$version; + if(preg_match('/^m?(\d{6}_\d{6})(_.*?)?$/',$version,$matches)) + $version='m'.$matches[1]; + else + die("Error: The version option must be either a timestamp (e.g. 101129_185401)\nor the full name of a migration (e.g. m101129_185401_create_user_table).\n"); + + // try migrate up + $migrations=$this->getNewMigrations(); + foreach($migrations as $i=>$migration) + { + if(strpos($migration,$version.'_')===0) + { + $this->actionUp(array($i+1)); + return; + } + } + + // try migrate down + $migrations=array_keys($this->getMigrationHistory(-1)); + foreach($migrations as $i=>$migration) + { + if(strpos($migration,$version.'_')===0) + { + if($i===0) + echo "Already at '$originalVersion'. Nothing needs to be done.\n"; + else + $this->actionDown(array($i)); + return; + } + } + + die("Error: Unable to find the version '$originalVersion'.\n"); + } + + public function actionMark($args) + { + if(isset($args[0])) + $version=$args[0]; + else + $this->usageError('Please specify which version to mark to.'); + $originalVersion=$version; + if(preg_match('/^m?(\d{6}_\d{6})(_.*?)?$/',$version,$matches)) + $version='m'.$matches[1]; + else + die("Error: The version option must be either a timestamp (e.g. 101129_185401)\nor the full name of a migration (e.g. m101129_185401_create_user_table).\n"); + + $db=$this->getDbConnection(); + + // try mark up + $migrations=$this->getNewMigrations(); + foreach($migrations as $i=>$migration) + { + if(strpos($migration,$version.'_')===0) + { + if($this->confirm("Set migration history at $originalVersion?")) + { + $command=$db->createCommand(); + for($j=0;$j<=$i;++$j) + { + $command->insert($this->migrationTable, array( + 'version'=>$migrations[$j], + 'apply_time'=>time(), + )); + } + echo "The migration history is set at $originalVersion.\nNo actual migration was performed.\n"; + } + return; + } + } + + // try mark down + $migrations=array_keys($this->getMigrationHistory(-1)); + foreach($migrations as $i=>$migration) + { + if(strpos($migration,$version.'_')===0) + { + if($i===0) + echo "Already at '$originalVersion'. Nothing needs to be done.\n"; + else + { + if($this->confirm("Set migration history at $originalVersion?")) + { + $command=$db->createCommand(); + for($j=0;$j<$i;++$j) + $command->delete($this->migrationTable, $db->quoteColumnName('version').'=:version', array(':version'=>$migrations[$j])); + echo "The migration history is set at $originalVersion.\nNo actual migration was performed.\n"; + } + } + return; + } + } + + die("Error: Unable to find the version '$originalVersion'.\n"); + } + + public function actionHistory($args) + { + $limit=isset($args[0]) ? (int)$args[0] : -1; + $migrations=$this->getMigrationHistory($limit); + if($migrations===array()) + echo "No migration has been done before.\n"; + else + { + $n=count($migrations); + if($limit>0) + echo "Showing the last $n applied ".($n===1 ? 'migration' : 'migrations').":\n"; + else + echo "Total $n ".($n===1 ? 'migration has' : 'migrations have')." been applied before:\n"; + foreach($migrations as $version=>$time) + echo " (".date('Y-m-d H:i:s',$time).') '.$version."\n"; + } + } + + public function actionNew($args) + { + $limit=isset($args[0]) ? (int)$args[0] : -1; + $migrations=$this->getNewMigrations(); + if($migrations===array()) + echo "No new migrations found. Your system is up-to-date.\n"; + else + { + $n=count($migrations); + if($limit>0 && $n>$limit) + { + $migrations=array_slice($migrations,0,$limit); + echo "Showing $limit out of $n new ".($n===1 ? 'migration' : 'migrations').":\n"; + } + else + echo "Found $n new ".($n===1 ? 'migration' : 'migrations').":\n"; + + foreach($migrations as $migration) + echo " ".$migration."\n"; + } + } + + public function actionCreate($args) + { + if(isset($args[0])) + $name=$args[0]; + else + $this->usageError('Please provide the name of the new migration.'); + + if(!preg_match('/^\w+$/',$name)) + die("Error: The name of the migration must contain letters, digits and/or underscore characters only.\n"); + + $name='m'.gmdate('ymd_His').'_'.$name; + $content=strtr($this->getTemplate(), array('{ClassName}'=>$name)); + $file=$this->migrationPath.DIRECTORY_SEPARATOR.$name.'.php'; + + if($this->confirm("Create new migration '$file'?")) + { + file_put_contents($file, $content); + echo "New migration created successfully.\n"; + } + } + + public function confirm($message) + { + if(!$this->interactive) + return true; + return parent::confirm($message); + } + + protected function migrateUp($class) + { + if($class===self::BASE_MIGRATION) + return; + + echo "*** applying $class\n"; + $start=microtime(true); + $migration=$this->instantiateMigration($class); + if($migration->up()!==false) + { + $this->getDbConnection()->createCommand()->insert($this->migrationTable, array( + 'version'=>$class, + 'apply_time'=>time(), + )); + $time=microtime(true)-$start; + echo "*** applied $class (time: ".sprintf("%.3f",$time)."s)\n\n"; + } + else + { + $time=microtime(true)-$start; + echo "*** failed to apply $class (time: ".sprintf("%.3f",$time)."s)\n\n"; + return false; + } + } + + protected function migrateDown($class) + { + if($class===self::BASE_MIGRATION) + return; + + echo "*** reverting $class\n"; + $start=microtime(true); + $migration=$this->instantiateMigration($class); + if($migration->down()!==false) + { + $db=$this->getDbConnection(); + $db->createCommand()->delete($this->migrationTable, $db->quoteColumnName('version').'=:version', array(':version'=>$class)); + $time=microtime(true)-$start; + echo "*** reverted $class (time: ".sprintf("%.3f",$time)."s)\n\n"; + } + else + { + $time=microtime(true)-$start; + echo "*** failed to revert $class (time: ".sprintf("%.3f",$time)."s)\n\n"; + return false; + } + } + + protected function instantiateMigration($class) + { + $file=$this->migrationPath.DIRECTORY_SEPARATOR.$class.'.php'; + require_once($file); + $migration=new $class; + $migration->setDbConnection($this->getDbConnection()); + return $migration; + } + + /** + * @var CDbConnection + */ + private $_db; + protected function getDbConnection() + { + if($this->_db!==null) + return $this->_db; + else if(($this->_db=Yii::app()->getComponent($this->connectionID)) instanceof CDbConnection) + return $this->_db; + else + die("Error: CMigrationCommand.connectionID '{$this->connectionID}' is invalid. Please make sure it refers to the ID of a CDbConnection application component.\n"); + } + + protected function getMigrationHistory($limit) + { + $db=$this->getDbConnection(); + if($db->schema->getTable($this->migrationTable)===null) + { + $this->createMigrationHistoryTable(); + } + return CHtml::listData($db->createCommand() + ->select('version, apply_time') + ->from($this->migrationTable) + ->order('version DESC') + ->limit($limit) + ->queryAll(), 'version', 'apply_time'); + } + + protected function createMigrationHistoryTable() + { + $db=$this->getDbConnection(); + echo 'Creating migration history table "'.$this->migrationTable.'"...'; + $db->createCommand()->createTable($this->migrationTable,array( + 'version'=>'string NOT NULL PRIMARY KEY', + 'apply_time'=>'integer', + )); + $db->createCommand()->insert($this->migrationTable,array( + 'version'=>self::BASE_MIGRATION, + 'apply_time'=>time(), + )); + echo "done.\n"; + } + + protected function getNewMigrations() + { + $applied=array(); + foreach($this->getMigrationHistory(-1) as $version=>$time) + $applied[substr($version,1,13)]=true; + + $migrations=array(); + $handle=opendir($this->migrationPath); + while(($file=readdir($handle))!==false) + { + if($file==='.' || $file==='..') + continue; + $path=$this->migrationPath.DIRECTORY_SEPARATOR.$file; + if(preg_match('/^(m(\d{6}_\d{6})_.*?)\.php$/',$file,$matches) && is_file($path) && !isset($applied[$matches[2]])) + $migrations[]=$matches[1]; + } + closedir($handle); + sort($migrations); + return $migrations; + } + + public function getHelp() + { + return <<<EOD +USAGE + yiic migrate [action] [parameter] + +DESCRIPTION + This command provides support for database migrations. The optional + 'action' parameter specifies which specific migration task to perform. + It can take these values: up, down, to, create, history, new, mark. + If the 'action' parameter is not given, it defaults to 'up'. + Each action takes different parameters. Their usage can be found in + the following examples. + +EXAMPLES + * yiic migrate + Applies ALL new migrations. This is equivalent to 'yiic migrate up'. + + * yiic migrate create create_user_table + Creates a new migration named 'create_user_table'. + + * yiic migrate up 3 + Applies the next 3 new migrations. + + * yiic migrate down + Reverts the last applied migration. + + * yiic migrate down 3 + Reverts the last 3 applied migrations. + + * yiic migrate to 101129_185401 + Migrates up or down to version 101129_185401. + + * yiic migrate mark 101129_185401 + Modifies the migration history up or down to version 101129_185401. + No actual migration will be performed. + + * yiic migrate history + Shows all previously applied migration information. + + * yiic migrate history 10 + Shows the last 10 applied migrations. + + * yiic migrate new + Shows all new migrations. + + * yiic migrate new 10 + Shows the next 10 migrations that have not been applied. + +EOD; + } + + protected function getTemplate() + { + if($this->templateFile!==null) + return file_get_contents(Yii::getPathOfAlias($this->templateFile).'.php'); + else + return <<<EOD +<?php + +class {ClassName} extends CDbMigration +{ + public function up() + { + } + + public function down() + { + echo "{ClassName} does not support migration down.\\n"; + return false; + } + + /* + // Use safeUp/safeDown to do migration with transaction + public function safeUp() + { + } + + public function safeDown() + { + } + */ +} +EOD; + } +} diff --git a/framework/cli/commands/ShellCommand.php b/framework/cli/commands/ShellCommand.php new file mode 100644 index 0000000..e90d9bf --- /dev/null +++ b/framework/cli/commands/ShellCommand.php @@ -0,0 +1,148 @@ +<?php +/** + * ShellCommand class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + * @version $Id: ShellCommand.php 3477 2011-12-06 22:33:37Z alexander.makarow $ + */ + +/** + * ShellCommand executes the specified Web application and provides a shell for interaction. + * + * @property string $help The help information for the shell command. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: ShellCommand.php 3477 2011-12-06 22:33:37Z alexander.makarow $ + * @package system.cli.commands + * @since 1.0 + */ +class ShellCommand extends CConsoleCommand +{ + /** + * @return string the help information for the shell command + */ + public function getHelp() + { + return <<<EOD +USAGE + yiic shell [entry-script | config-file] + +DESCRIPTION + This command allows you to interact with a Web application + on the command line. It also provides tools to automatically + generate new controllers, views and data models. + + It is recommended that you execute this command under + the directory that contains the entry script file of + the Web application. + +PARAMETERS + * entry-script | config-file: optional, the path to + the entry script file or the configuration file for + the Web application. If not given, it is assumed to be + the 'index.php' file under the current directory. + +EOD; + } + + /** + * Execute the action. + * @param array $args command line parameters specific for this command + */ + public function run($args) + { + if(!isset($args[0])) + $args[0]='index.php'; + $entryScript=isset($args[0]) ? $args[0] : 'index.php'; + if(($entryScript=realpath($args[0]))===false || !is_file($entryScript)) + $this->usageError("{$args[0]} does not exist or is not an entry script file."); + + // fake the web server setting + $cwd=getcwd(); + chdir(dirname($entryScript)); + $_SERVER['SCRIPT_NAME']='/'.basename($entryScript); + $_SERVER['REQUEST_URI']=$_SERVER['SCRIPT_NAME']; + $_SERVER['SCRIPT_FILENAME']=$entryScript; + $_SERVER['HTTP_HOST']='localhost'; + $_SERVER['SERVER_NAME']='localhost'; + $_SERVER['SERVER_PORT']=80; + + // reset context to run the web application + restore_error_handler(); + restore_exception_handler(); + Yii::setApplication(null); + Yii::setPathOfAlias('application',null); + + ob_start(); + $config=require($entryScript); + ob_end_clean(); + + // oops, the entry script turns out to be a config file + if(is_array($config)) + { + chdir($cwd); + $_SERVER['SCRIPT_NAME']='/index.php'; + $_SERVER['REQUEST_URI']=$_SERVER['SCRIPT_NAME']; + $_SERVER['SCRIPT_FILENAME']=$cwd.DIRECTORY_SEPARATOR.'index.php'; + Yii::createWebApplication($config); + } + + restore_error_handler(); + restore_exception_handler(); + + $yiiVersion=Yii::getVersion(); + echo <<<EOD +Yii Interactive Tool v1.1 (based on Yii v{$yiiVersion}) +Please type 'help' for help. Type 'exit' to quit. +EOD; + $this->runShell(); + } + + protected function runShell() + { + // disable E_NOTICE so that the shell is more friendly + error_reporting(E_ALL ^ E_NOTICE); + + $_runner_=new CConsoleCommandRunner; + $_runner_->addCommands(dirname(__FILE__).'/shell'); + $_runner_->addCommands(Yii::getPathOfAlias('application.commands.shell')); + if(($_path_=@getenv('YIIC_SHELL_COMMAND_PATH'))!==false) + $_runner_->addCommands($_path_); + $_commands_=$_runner_->commands; + $log=Yii::app()->log; + + while(($_line_=$this->prompt("\n>>"))!==false) + { + $_line_=trim($_line_); + if($_line_==='exit') + return; + try + { + $_args_=preg_split('/[\s,]+/',rtrim($_line_,';'),-1,PREG_SPLIT_NO_EMPTY); + if(isset($_args_[0]) && isset($_commands_[$_args_[0]])) + { + $_command_=$_runner_->createCommand($_args_[0]); + array_shift($_args_); + $_command_->init(); + $_command_->run($_args_); + } + else + echo eval($_line_.';'); + } + catch(Exception $e) + { + if($e instanceof ShellException) + echo $e->getMessage(); + else + echo $e; + } + } + } +} + +class ShellException extends CException +{ +}
\ No newline at end of file diff --git a/framework/cli/commands/WebAppCommand.php b/framework/cli/commands/WebAppCommand.php new file mode 100644 index 0000000..f5a94f1 --- /dev/null +++ b/framework/cli/commands/WebAppCommand.php @@ -0,0 +1,129 @@ +<?php +/** + * WebAppCommand class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + * @version $Id: WebAppCommand.php 3477 2011-12-06 22:33:37Z alexander.makarow $ + */ + +/** + * WebAppCommand creates an Yii Web application at the specified location. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: WebAppCommand.php 3477 2011-12-06 22:33:37Z alexander.makarow $ + * @package system.cli.commands + * @since 1.0 + */ +class WebAppCommand extends CConsoleCommand +{ + private $_rootPath; + + public function getHelp() + { + return <<<EOD +USAGE + yiic webapp <app-path> + +DESCRIPTION + This command generates an Yii Web Application at the specified location. + +PARAMETERS + * app-path: required, the directory where the new application will be created. + If the directory does not exist, it will be created. After the application + is created, please make sure the directory can be accessed by Web users. + +EOD; + } + + /** + * Execute the action. + * @param array command line parameters specific for this command + */ + public function run($args) + { + if(!isset($args[0])) + $this->usageError('the Web application location is not specified.'); + $path=strtr($args[0],'/\\',DIRECTORY_SEPARATOR); + if(strpos($path,DIRECTORY_SEPARATOR)===false) + $path='.'.DIRECTORY_SEPARATOR.$path; + $dir=rtrim(realpath(dirname($path)),'\\/'); + if($dir===false || !is_dir($dir)) + $this->usageError("The directory '$path' is not valid. Please make sure the parent directory exists."); + if(basename($path)==='.') + $this->_rootPath=$path=$dir; + else + $this->_rootPath=$path=$dir.DIRECTORY_SEPARATOR.basename($path); + if($this->confirm("Create a Web application under '$path'?")) + { + $sourceDir=realpath(dirname(__FILE__).'/../views/webapp'); + if($sourceDir===false) + die("\nUnable to locate the source directory.\n"); + $list=$this->buildFileList($sourceDir,$path); + $list['index.php']['callback']=array($this,'generateIndex'); + $list['index-test.php']['callback']=array($this,'generateIndex'); + $list['protected/tests/bootstrap.php']['callback']=array($this,'generateTestBoostrap'); + $list['protected/yiic.php']['callback']=array($this,'generateYiic'); + $this->copyFiles($list); + @chmod($path.'/assets',0777); + @chmod($path.'/protected/runtime',0777); + @chmod($path.'/protected/data',0777); + @chmod($path.'/protected/data/testdrive.db',0777); + @chmod($path.'/protected/yiic',0755); + echo "\nYour application has been created successfully under {$path}.\n"; + } + } + + public function generateIndex($source,$params) + { + $content=file_get_contents($source); + $yii=realpath(dirname(__FILE__).'/../../yii.php'); + $yii=$this->getRelativePath($yii,$this->_rootPath.DIRECTORY_SEPARATOR.'index.php'); + $yii=str_replace('\\','\\\\',$yii); + return preg_replace('/\$yii\s*=(.*?);/',"\$yii=$yii;",$content); + } + + public function generateTestBoostrap($source,$params) + { + $content=file_get_contents($source); + $yii=realpath(dirname(__FILE__).'/../../yiit.php'); + $yii=$this->getRelativePath($yii,$this->_rootPath.DIRECTORY_SEPARATOR.'protected'.DIRECTORY_SEPARATOR.'tests'.DIRECTORY_SEPARATOR.'bootstrap.php'); + $yii=str_replace('\\','\\\\',$yii); + return preg_replace('/\$yiit\s*=(.*?);/',"\$yiit=$yii;",$content); + } + + public function generateYiic($source,$params) + { + $content=file_get_contents($source); + $yiic=realpath(dirname(__FILE__).'/../../yiic.php'); + $yiic=$this->getRelativePath($yiic,$this->_rootPath.DIRECTORY_SEPARATOR.'protected'.DIRECTORY_SEPARATOR.'yiic.php'); + $yiic=str_replace('\\','\\\\',$yiic); + return preg_replace('/\$yiic\s*=(.*?);/',"\$yiic=$yiic;",$content); + } + + protected function getRelativePath($path1,$path2) + { + $segs1=explode(DIRECTORY_SEPARATOR,$path1); + $segs2=explode(DIRECTORY_SEPARATOR,$path2); + $n1=count($segs1); + $n2=count($segs2); + + for($i=0;$i<$n1 && $i<$n2;++$i) + { + if($segs1[$i]!==$segs2[$i]) + break; + } + + if($i===0) + return "'".$path1."'"; + $up=''; + for($j=$i;$j<$n2-1;++$j) + $up.='/..'; + for(;$i<$n1-1;++$i) + $up.='/'.$segs1[$i]; + + return 'dirname(__FILE__).\''.$up.'/'.basename($path1).'\''; + } +}
\ No newline at end of file diff --git a/framework/cli/commands/shell/ControllerCommand.php b/framework/cli/commands/shell/ControllerCommand.php new file mode 100644 index 0000000..f7447b6 --- /dev/null +++ b/framework/cli/commands/shell/ControllerCommand.php @@ -0,0 +1,176 @@ +<?php +/** + * ControllerCommand class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + * @version $Id: ControllerCommand.php 2799 2011-01-01 19:31:13Z qiang.xue $ + */ + +/** + * ControllerCommand generates a controller class. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: ControllerCommand.php 2799 2011-01-01 19:31:13Z qiang.xue $ + * @package system.cli.commands.shell + * @since 1.0 + */ +class ControllerCommand extends CConsoleCommand +{ + /** + * @var string the directory that contains templates for the model command. + * Defaults to null, meaning using 'framework/cli/views/shell/controller'. + * If you set this path and some views are missing in the directory, + * the default views will be used. + */ + public $templatePath; + + public function getHelp() + { + return <<<EOD +USAGE + controller <controller-ID> [action-ID] ... + +DESCRIPTION + This command generates a controller and views associated with + the specified actions. + +PARAMETERS + * controller-ID: required, controller ID, e.g., 'post'. + If the controller should be located under a subdirectory, + please specify the controller ID as 'path/to/ControllerID', + e.g., 'admin/user'. + + If the controller belongs to a module, please specify + the controller ID as 'ModuleID/ControllerID' or + 'ModuleID/path/to/Controller' (assuming the controller is + under a subdirectory of that module). + + * action-ID: optional, action ID. You may supply one or several + action IDs. A default 'index' action will always be generated. + +EXAMPLES + * Generates the 'post' controller: + controller post + + * Generates the 'post' controller with additional actions 'contact' + and 'about': + controller post contact about + + * Generates the 'post' controller which should be located under + the 'admin' subdirectory of the base controller path: + controller admin/post + + * Generates the 'post' controller which should belong to + the 'admin' module: + controller admin/post + +NOTE: in the last two examples, the commands are the same, but +the generated controller file is located under different directories. +Yii is able to detect whether 'admin' refers to a module or a subdirectory. + +EOD; + } + + /** + * Execute the action. + * @param array command line parameters specific for this command + */ + public function run($args) + { + if(!isset($args[0])) + { + echo "Error: controller name is required.\n"; + echo $this->getHelp(); + return; + } + + $module=Yii::app(); + $controllerID=$args[0]; + if(($pos=strrpos($controllerID,'/'))===false) + { + $controllerClass=ucfirst($controllerID).'Controller'; + $controllerFile=$module->controllerPath.DIRECTORY_SEPARATOR.$controllerClass.'.php'; + $controllerID[0]=strtolower($controllerID[0]); + } + else + { + $last=substr($controllerID,$pos+1); + $last[0]=strtolower($last[0]); + $pos2=strpos($controllerID,'/'); + $first=substr($controllerID,0,$pos2); + $middle=$pos===$pos2?'':substr($controllerID,$pos2+1,$pos-$pos2); + + $controllerClass=ucfirst($last).'Controller'; + $controllerFile=($middle===''?'':$middle.'/').$controllerClass.'.php'; + $controllerID=$middle===''?$last:$middle.'/'.$last; + if(($m=Yii::app()->getModule($first))!==null) + $module=$m; + else + { + $controllerFile=$first.'/'.$controllerClass.'.php'; + $controllerID=$first.'/'.$controllerID; + } + + $controllerFile=$module->controllerPath.DIRECTORY_SEPARATOR.str_replace('/',DIRECTORY_SEPARATOR,$controllerFile); + } + + $args[]='index'; + $actions=array_unique(array_splice($args,1)); + + $templatePath=$this->templatePath===null?YII_PATH.'/cli/views/shell/controller':$this->templatePath; + + $list=array( + basename($controllerFile)=>array( + 'source'=>$templatePath.DIRECTORY_SEPARATOR.'controller.php', + 'target'=>$controllerFile, + 'callback'=>array($this,'generateController'), + 'params'=>array($controllerClass, $actions), + ), + ); + + $viewPath=$module->viewPath.DIRECTORY_SEPARATOR.str_replace('/',DIRECTORY_SEPARATOR,$controllerID); + foreach($actions as $name) + { + $list[$name.'.php']=array( + 'source'=>$templatePath.DIRECTORY_SEPARATOR.'view.php', + 'target'=>$viewPath.DIRECTORY_SEPARATOR.$name.'.php', + 'callback'=>array($this,'generateAction'), + 'params'=>array('controller'=>$controllerClass, 'action'=>$name), + ); + } + + $this->copyFiles($list); + + if($module instanceof CWebModule) + $moduleID=$module->id.'/'; + else + $moduleID=''; + + echo <<<EOD + +Controller '{$controllerID}' has been created in the following file: + $controllerFile + +You may access it in the browser using the following URL: + http://hostname/path/to/index.php?r={$moduleID}{$controllerID} + +EOD; + } + + public function generateController($source,$params) + { + if(!is_file($source)) // fall back to default ones + $source=YII_PATH.'/cli/views/shell/controller/'.basename($source); + return $this->renderFile($source,array('className'=>$params[0],'actions'=>$params[1]),true); + } + + public function generateAction($source,$params) + { + if(!is_file($source)) // fall back to default ones + $source=YII_PATH.'/cli/views/shell/controller/'.basename($source); + return $this->renderFile($source,$params,true); + } +}
\ No newline at end of file diff --git a/framework/cli/commands/shell/CrudCommand.php b/framework/cli/commands/shell/CrudCommand.php new file mode 100644 index 0000000..5932dea --- /dev/null +++ b/framework/cli/commands/shell/CrudCommand.php @@ -0,0 +1,327 @@ +<?php +/** + * CrudCommand class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + * @version $Id: CrudCommand.php 2799 2011-01-01 19:31:13Z qiang.xue $ + */ + +/** + * CrudCommand generates code implementing CRUD operations. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CrudCommand.php 2799 2011-01-01 19:31:13Z qiang.xue $ + * @package system.cli.commands.shell + * @since 1.0 + */ +class CrudCommand extends CConsoleCommand +{ + /** + * @var string the directory that contains templates for crud commands. + * Defaults to null, meaning using 'framework/cli/views/shell/crud'. + * If you set this path and some views are missing in the directory, + * the default views will be used. + */ + public $templatePath; + /** + * @var string the directory that contains functional test classes. + * Defaults to null, meaning using 'protected/tests/functional'. + * If this is false, it means functional test file should NOT be generated. + */ + public $functionalTestPath; + /** + * @var array list of actions to be created. Each action must be associated with a template file with the same name. + */ + public $actions=array('create','update','index','view','admin','_form','_view','_search'); + + public function getHelp() + { + return <<<EOD +USAGE + crud <model-class> [controller-ID] ... + +DESCRIPTION + This command generates a controller and views that accomplish + CRUD operations for the specified data model. + +PARAMETERS + * model-class: required, the name of the data model class. This can + also be specified as a path alias (e.g. application.models.Post). + If the model class belongs to a module, it should be specified + as 'ModuleID.models.ClassName'. + + * controller-ID: optional, the controller ID (e.g. 'post'). + If this is not specified, the model class name will be used + as the controller ID. In this case, if the model belongs to + a module, the controller will also be created under the same + module. + + If the controller should be located under a subdirectory, + please specify the controller ID as 'path/to/ControllerID' + (e.g. 'admin/user'). + + If the controller belongs to a module (different from the module + that the model belongs to), please specify the controller ID + as 'ModuleID/ControllerID' or 'ModuleID/path/to/Controller'. + +EXAMPLES + * Generates CRUD for the Post model: + crud Post + + * Generates CRUD for the Post model which belongs to module 'admin': + crud admin.models.Post + + * Generates CRUD for the Post model. The generated controller should + belong to module 'admin', but not the model class: + crud Post admin/post + +EOD; + } + + /** + * Execute the action. + * @param array command line parameters specific for this command + */ + public function run($args) + { + if(!isset($args[0])) + { + echo "Error: data model class is required.\n"; + echo $this->getHelp(); + return; + } + $module=Yii::app(); + $modelClass=$args[0]; + if(($pos=strpos($modelClass,'.'))===false) + $modelClass='application.models.'.$modelClass; + else + { + $id=substr($modelClass,0,$pos); + if(($m=Yii::app()->getModule($id))!==null) + $module=$m; + } + $modelClass=Yii::import($modelClass); + + if(isset($args[1])) + { + $controllerID=$args[1]; + if(($pos=strrpos($controllerID,'/'))===false) + { + $controllerClass=ucfirst($controllerID).'Controller'; + $controllerFile=$module->controllerPath.DIRECTORY_SEPARATOR.$controllerClass.'.php'; + $controllerID[0]=strtolower($controllerID[0]); + } + else + { + $last=substr($controllerID,$pos+1); + $last[0]=strtolower($last); + $pos2=strpos($controllerID,'/'); + $first=substr($controllerID,0,$pos2); + $middle=$pos===$pos2?'':substr($controllerID,$pos2+1,$pos-$pos2); + + $controllerClass=ucfirst($last).'Controller'; + $controllerFile=($middle===''?'':$middle.'/').$controllerClass.'.php'; + $controllerID=$middle===''?$last:$middle.'/'.$last; + if(($m=Yii::app()->getModule($first))!==null) + $module=$m; + else + { + $controllerFile=$first.'/'.$controllerFile; + $controllerID=$first.'/'.$controllerID; + } + + $controllerFile=$module->controllerPath.DIRECTORY_SEPARATOR.str_replace('/',DIRECTORY_SEPARATOR,$controllerFile); + } + } + else + { + $controllerID=$modelClass; + $controllerClass=ucfirst($controllerID).'Controller'; + $controllerFile=$module->controllerPath.DIRECTORY_SEPARATOR.$controllerClass.'.php'; + $controllerID[0]=strtolower($controllerID[0]); + } + + $templatePath=$this->templatePath===null?YII_PATH.'/cli/views/shell/crud':$this->templatePath; + $functionalTestPath=$this->functionalTestPath===null?Yii::getPathOfAlias('application.tests.functional'):$this->functionalTestPath; + + $viewPath=$module->viewPath.DIRECTORY_SEPARATOR.str_replace('.',DIRECTORY_SEPARATOR,$controllerID); + $fixtureName=$this->pluralize($modelClass); + $fixtureName[0]=strtolower($fixtureName); + $list=array( + basename($controllerFile)=>array( + 'source'=>$templatePath.'/controller.php', + 'target'=>$controllerFile, + 'callback'=>array($this,'generateController'), + 'params'=>array($controllerClass,$modelClass), + ), + ); + + if($functionalTestPath!==false) + { + $list[$modelClass.'Test.php']=array( + 'source'=>$templatePath.'/test.php', + 'target'=>$functionalTestPath.DIRECTORY_SEPARATOR.$modelClass.'Test.php', + 'callback'=>array($this,'generateTest'), + 'params'=>array($controllerID,$fixtureName,$modelClass), + ); + } + + foreach($this->actions as $action) + { + $list[$action.'.php']=array( + 'source'=>$templatePath.'/'.$action.'.php', + 'target'=>$viewPath.'/'.$action.'.php', + 'callback'=>array($this,'generateView'), + 'params'=>$modelClass, + ); + } + + $this->copyFiles($list); + + if($module instanceof CWebModule) + $moduleID=$module->id.'/'; + else + $moduleID=''; + + echo "\nCrud '{$controllerID}' has been successfully created. You may access it via:\n"; + echo "http://hostname/path/to/index.php?r={$moduleID}{$controllerID}\n"; + } + + public function generateController($source,$params) + { + list($controllerClass,$modelClass)=$params; + $model=CActiveRecord::model($modelClass); + $id=$model->tableSchema->primaryKey; + if($id===null) + throw new ShellException(Yii::t('yii','Error: Table "{table}" does not have a primary key.',array('{table}'=>$model->tableName()))); + else if(is_array($id)) + throw new ShellException(Yii::t('yii','Error: Table "{table}" has a composite primary key which is not supported by crud command.',array('{table}'=>$model->tableName()))); + + if(!is_file($source)) // fall back to default ones + $source=YII_PATH.'/cli/views/shell/crud/'.basename($source); + + return $this->renderFile($source,array( + 'ID'=>$id, + 'controllerClass'=>$controllerClass, + 'modelClass'=>$modelClass, + ),true); + } + + public function generateView($source,$modelClass) + { + $model=CActiveRecord::model($modelClass); + $table=$model->getTableSchema(); + $columns=$table->columns; + if(!is_file($source)) // fall back to default ones + $source=YII_PATH.'/cli/views/shell/crud/'.basename($source); + return $this->renderFile($source,array( + 'ID'=>$table->primaryKey, + 'modelClass'=>$modelClass, + 'columns'=>$columns),true); + } + + public function generateTest($source,$params) + { + list($controllerID,$fixtureName,$modelClass)=$params; + if(!is_file($source)) // fall back to default ones + $source=YII_PATH.'/cli/views/shell/crud/'.basename($source); + return $this->renderFile($source, array( + 'controllerID'=>$controllerID, + 'fixtureName'=>$fixtureName, + 'modelClass'=>$modelClass, + ),true); + } + + public function generateInputLabel($modelClass,$column) + { + return "CHtml::activeLabelEx(\$model,'{$column->name}')"; + } + + public function generateInputField($modelClass,$column) + { + if($column->type==='boolean') + return "CHtml::activeCheckBox(\$model,'{$column->name}')"; + else if(stripos($column->dbType,'text')!==false) + return "CHtml::activeTextArea(\$model,'{$column->name}',array('rows'=>6, 'cols'=>50))"; + else + { + if(preg_match('/^(password|pass|passwd|passcode)$/i',$column->name)) + $inputField='activePasswordField'; + else + $inputField='activeTextField'; + + if($column->type!=='string' || $column->size===null) + return "CHtml::{$inputField}(\$model,'{$column->name}')"; + else + { + if(($size=$maxLength=$column->size)>60) + $size=60; + return "CHtml::{$inputField}(\$model,'{$column->name}',array('size'=>$size,'maxlength'=>$maxLength))"; + } + } + } + + public function generateActiveLabel($modelClass,$column) + { + return "\$form->labelEx(\$model,'{$column->name}')"; + } + + public function generateActiveField($modelClass,$column) + { + if($column->type==='boolean') + return "\$form->checkBox(\$model,'{$column->name}')"; + else if(stripos($column->dbType,'text')!==false) + return "\$form->textArea(\$model,'{$column->name}',array('rows'=>6, 'cols'=>50))"; + else + { + if(preg_match('/^(password|pass|passwd|passcode)$/i',$column->name)) + $inputField='passwordField'; + else + $inputField='textField'; + + if($column->type!=='string' || $column->size===null) + return "\$form->{$inputField}(\$model,'{$column->name}')"; + else + { + if(($size=$maxLength=$column->size)>60) + $size=60; + return "\$form->{$inputField}(\$model,'{$column->name}',array('size'=>$size,'maxlength'=>$maxLength))"; + } + } + } + + public function guessNameColumn($columns) + { + foreach($columns as $column) + { + if(!strcasecmp($column->name,'name')) + return $column->name; + } + foreach($columns as $column) + { + if(!strcasecmp($column->name,'title')) + return $column->name; + } + foreach($columns as $column) + { + if($column->isPrimaryKey) + return $column->name; + } + return 'id'; + } + + public function class2id($className) + { + return trim(strtolower(str_replace('_','-',preg_replace('/(?<![A-Z])[A-Z]/', '-\0', $className))),'-'); + } + + public function class2name($className,$pluralize=false) + { + if($pluralize) + $className=$this->pluralize($className); + return ucwords(trim(strtolower(str_replace(array('-','_'),' ',preg_replace('/(?<![A-Z])[A-Z]/', ' \0', $className))))); + } +} diff --git a/framework/cli/commands/shell/FormCommand.php b/framework/cli/commands/shell/FormCommand.php new file mode 100644 index 0000000..c3f064f --- /dev/null +++ b/framework/cli/commands/shell/FormCommand.php @@ -0,0 +1,123 @@ +<?php +/** + * FormCommand class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + * @version $Id: FormCommand.php 2799 2011-01-01 19:31:13Z qiang.xue $ + */ + +/** + * FormCommand generates a form view based on a specified model. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: FormCommand.php 2799 2011-01-01 19:31:13Z qiang.xue $ + * @package system.cli.commands.shell + * @since 1.0 + */ +class FormCommand extends CConsoleCommand +{ + /** + * @var string the directory that contains templates for the form command. + * Defaults to null, meaning using 'framework/cli/views/shell/form'. + * If you set this path and some views are missing in the directory, + * the default views will be used. + */ + public $templatePath; + + public function getHelp() + { + return <<<EOD +USAGE + form <model-class> <view-name> [scenario] + +DESCRIPTION + This command generates a form view that can be used to collect inputs + for the specified model. + +PARAMETERS + * model-class: required, model class. This can be either the name of + the model class (e.g. 'ContactForm') or the path alias of the model + class file (e.g. 'application.models.ContactForm'). The former can + be used only if the class can be autoloaded. + + * view-name: required, the name of the view to be generated. This should + be the path alias of the view script (e.g. 'application.views.site.contact'). + + * scenario: optional, the name of the scenario in which the model is used + (e.g. 'update', 'login'). This determines which model attributes the + generated form view will be used to collect user inputs for. If this + is not provided, the scenario will be assumed to be '' (empty string). + +EXAMPLES + * Generates the view script for the 'ContactForm' model: + form ContactForm application.views.site.contact + +EOD; + } + + /** + * Execute the action. + * @param array command line parameters specific for this command + */ + public function run($args) + { + if(!isset($args[0],$args[1])) + { + echo "Error: both model class and view name are required.\n"; + echo $this->getHelp(); + return; + } + $scenario=isset($args[2]) ? $args[2] : ''; + $modelClass=Yii::import($args[0],true); + $model=new $modelClass($scenario); + $attributes=$model->getSafeAttributeNames(); + + $templatePath=$this->templatePath===null?YII_PATH.'/cli/views/shell/form':$this->templatePath; + $viewPath=Yii::getPathOfAlias($args[1]); + $viewName=basename($viewPath); + $viewPath.='.php'; + $params=array( + 'modelClass'=>$modelClass, + 'viewName'=>$viewName, + 'attributes'=>$attributes, + ); + $list=array( + basename($viewPath)=>array( + 'source'=>$templatePath.'/form.php', + 'target'=>$viewPath, + 'callback'=>array($this,'generateForm'), + 'params'=>$params, + ), + ); + + $this->copyFiles($list); + + $actionFile=$templatePath.'/action.php'; + if(!is_file($actionFile)) // fall back to default ones + $actionFile=YII_PATH.'/cli/views/shell/form/action.php'; + + echo "The following form view has been successfully created:\n"; + echo "\t$viewPath\n\n"; + echo "You may use the following code in your controller action:\n\n"; + echo $this->renderFile($actionFile,$params,true); + echo "\n"; + } + + public function generateForm($source,$params) + { + if(!is_file($source)) // fall back to default ones + $source=YII_PATH.'/cli/views/shell/form/'.basename($source); + + return $this->renderFile($source,$params,true); + } + + public function class2id($className) + { + if(strrpos($className,'Form')===strlen($className)-4) + $className=substr($className,0,strlen($className)-4); + return trim(strtolower(str_replace('_','-',preg_replace('/(?<![A-Z])[A-Z]/', '-\0', $className))),'-'); + } +}
\ No newline at end of file diff --git a/framework/cli/commands/shell/HelpCommand.php b/framework/cli/commands/shell/HelpCommand.php new file mode 100644 index 0000000..8007373 --- /dev/null +++ b/framework/cli/commands/shell/HelpCommand.php @@ -0,0 +1,78 @@ +<?php +/** + * HelpCommand class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + * @version $Id: HelpCommand.php 3426 2011-10-25 00:01:09Z alexander.makarow $ + */ + +/** + * HelpCommand displays help information for commands under yiic shell. + * + * @property string $help The command description. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: HelpCommand.php 3426 2011-10-25 00:01:09Z alexander.makarow $ + * @package system.cli.commands.shell + * @since 1.0 + */ +class HelpCommand extends CConsoleCommand +{ + /** + * Execute the action. + * @param array command line parameters specific for this command + */ + public function run($args) + { + $runner=$this->getCommandRunner(); + $commands=$runner->commands; + if(isset($args[0])) + $name=strtolower($args[0]); + if(!isset($args[0]) || !isset($commands[$name])) + { + echo <<<EOD +At the prompt, you may enter a PHP statement or one of the following commands: + +EOD; + $commandNames=array_keys($commands); + sort($commandNames); + echo ' - '.implode("\n - ",$commandNames); + echo <<<EOD + + +Type 'help <command-name>' for details about a command. + +To expand the above command list, place your command class files +under 'protected/commands/shell', or a directory specified +by the 'YIIC_SHELL_COMMAND_PATH' environment variable. The command class +must extend from CConsoleCommand. + +EOD; + } + else + echo $runner->createCommand($name)->getHelp(); + } + + /** + * Provides the command description. + * @return string the command description. + */ + public function getHelp() + { + return <<<EOD +USAGE + help [command-name] + +DESCRIPTION + Display the help information for the specified command. + If the command name is not given, all commands will be listed. + +PARAMETERS + * command-name: optional, the name of the command to show help information. + +EOD; + } +}
\ No newline at end of file diff --git a/framework/cli/commands/shell/ModelCommand.php b/framework/cli/commands/shell/ModelCommand.php new file mode 100644 index 0000000..cfd8c21 --- /dev/null +++ b/framework/cli/commands/shell/ModelCommand.php @@ -0,0 +1,488 @@ +<?php +/** + * ModelCommand class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + * @version $Id: ModelCommand.php 3477 2011-12-06 22:33:37Z alexander.makarow $ + */ + +/** + * ModelCommand generates a model class. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: ModelCommand.php 3477 2011-12-06 22:33:37Z alexander.makarow $ + * @package system.cli.commands.shell + * @since 1.0 + */ +class ModelCommand extends CConsoleCommand +{ + /** + * @var string the directory that contains templates for the model command. + * Defaults to null, meaning using 'framework/cli/views/shell/model'. + * If you set this path and some views are missing in the directory, + * the default views will be used. + */ + public $templatePath; + /** + * @var string the directory that contains test fixtures. + * Defaults to null, meaning using 'protected/tests/fixtures'. + * If this is false, it means fixture file should NOT be generated. + */ + public $fixturePath; + /** + * @var string the directory that contains unit test classes. + * Defaults to null, meaning using 'protected/tests/unit'. + * If this is false, it means unit test file should NOT be generated. + */ + public $unitTestPath; + + private $_schema; + private $_relations; // where we keep table relations + private $_tables; + private $_classes; + + public function getHelp() + { + return <<<EOD +USAGE + model <class-name> [table-name] + +DESCRIPTION + This command generates a model class with the specified class name. + +PARAMETERS + * class-name: required, model class name. By default, the generated + model class file will be placed under the directory aliased as + 'application.models'. To override this default, specify the class + name in terms of a path alias, e.g., 'application.somewhere.ClassName'. + + If the model class belongs to a module, it should be specified + as 'ModuleID.models.ClassName'. + + If the class name ends with '*', then a model class will be generated + for EVERY table in the database. + + If the class name contains a regular expression deliminated by slashes, + then a model class will be generated for those tables whose name + matches the regular expression. If the regular expression contains + sub-patterns, the first sub-pattern will be used to generate the model + class name. + + * table-name: optional, the associated database table name. If not given, + it is assumed to be the model class name. + + Note, when the class name ends with '*', this parameter will be + ignored. + +EXAMPLES + * Generates the Post model: + model Post + + * Generates the Post model which is associated with table 'posts': + model Post posts + + * Generates the Post model which should belong to module 'admin': + model admin.models.Post + + * Generates a model class for every table in the current database: + model * + + * Same as above, but the model class files should be generated + under 'protected/models2': + model application.models2.* + + * Generates a model class for every table whose name is prefixed + with 'tbl_' in the current database. The model class will not + contain the table prefix. + model /^tbl_(.*)$/ + + * Same as above, but the model class files should be generated + under 'protected/models2': + model application.models2./^tbl_(.*)$/ + +EOD; + } + + /** + * Checks if the given table is a "many to many" helper table. + * Their PK has 2 fields, and both of those fields are also FK to other separate tables. + * @param CDbTableSchema table to inspect + * @return boolean true if table matches description of helpter table. + */ + protected function isRelationTable($table) + { + $pk=$table->primaryKey; + return (count($pk) === 2 // we want 2 columns + && isset($table->foreignKeys[$pk[0]]) // pk column 1 is also a foreign key + && isset($table->foreignKeys[$pk[1]]) // pk column 2 is also a foriegn key + && $table->foreignKeys[$pk[0]][0] !== $table->foreignKeys[$pk[1]][0]); // and the foreign keys point different tables + } + + /** + * Generate code to put in ActiveRecord class's relations() function. + * @return array indexed by table names, each entry contains array of php code to go in appropriate ActiveRecord class. + * Empty array is returned if database couldn't be connected. + */ + protected function generateRelations() + { + $this->_relations=array(); + $this->_classes=array(); + foreach($this->_schema->getTables() as $table) + { + $tableName=$table->name; + + if ($this->isRelationTable($table)) + { + $pks=$table->primaryKey; + $fks=$table->foreignKeys; + + $table0=$fks[$pks[1]][0]; + $table1=$fks[$pks[0]][0]; + $className0=$this->getClassName($table0); + $className1=$this->getClassName($table1); + + $unprefixedTableName=$this->removePrefix($tableName,true); + + $relationName=$this->generateRelationName($table0, $table1, true); + $this->_relations[$className0][$relationName]="array(self::MANY_MANY, '$className1', '$unprefixedTableName($pks[0], $pks[1])')"; + + $relationName=$this->generateRelationName($table1, $table0, true); + $this->_relations[$className1][$relationName]="array(self::MANY_MANY, '$className0', '$unprefixedTableName($pks[0], $pks[1])')"; + } + else + { + $this->_classes[$tableName]=$className=$this->getClassName($tableName); + foreach ($table->foreignKeys as $fkName => $fkEntry) + { + // Put table and key name in variables for easier reading + $refTable=$fkEntry[0]; // Table name that current fk references to + $refKey=$fkEntry[1]; // Key in that table being referenced + $refClassName=$this->getClassName($refTable); + + // Add relation for this table + $relationName=$this->generateRelationName($tableName, $fkName, false); + $this->_relations[$className][$relationName]="array(self::BELONGS_TO, '$refClassName', '$fkName')"; + + // Add relation for the referenced table + $relationType=$table->primaryKey === $fkName ? 'HAS_ONE' : 'HAS_MANY'; + $relationName=$this->generateRelationName($refTable, $this->removePrefix($tableName), $relationType==='HAS_MANY'); + $this->_relations[$refClassName][$relationName]="array(self::$relationType, '$className', '$fkName')"; + } + } + } + } + + protected function getClassName($tableName) + { + return isset($this->_tables[$tableName]) ? $this->_tables[$tableName] : $this->generateClassName($tableName); + } + + /** + * Generates model class name based on a table name + * @param string the table name + * @return string the generated model class name + */ + protected function generateClassName($tableName) + { + return str_replace(' ','', + ucwords( + trim( + strtolower( + str_replace(array('-','_'),' ', + preg_replace('/(?<![A-Z])[A-Z]/', ' \0', $tableName)))))); + } + + /** + * Generates the mapping table between table names and class names. + * @param CDbSchema the database schema + * @param string a regular expression that may be used to filter table names + */ + protected function generateClassNames($schema,$pattern=null) + { + $this->_tables=array(); + foreach($schema->getTableNames() as $name) + { + if($pattern===null) + $this->_tables[$name]=$this->generateClassName($this->removePrefix($name)); + else if(preg_match($pattern,$name,$matches)) + { + if(count($matches)>1 && !empty($matches[1])) + $className=$this->generateClassName($matches[1]); + else + $className=$this->generateClassName($matches[0]); + $this->_tables[$name]=empty($className) ? $name : $className; + } + } + } + + /** + * Generate a name for use as a relation name (inside relations() function in a model). + * @param string the name of the table to hold the relation + * @param string the foreign key name + * @param boolean whether the relation would contain multiple objects + */ + protected function generateRelationName($tableName, $fkName, $multiple) + { + if(strcasecmp(substr($fkName,-2),'id')===0 && strcasecmp($fkName,'id')) + $relationName=rtrim(substr($fkName, 0, -2),'_'); + else + $relationName=$fkName; + $relationName[0]=strtolower($relationName); + + $rawName=$relationName; + if($multiple) + $relationName=$this->pluralize($relationName); + + $table=$this->_schema->getTable($tableName); + $i=0; + while(isset($table->columns[$relationName])) + $relationName=$rawName.($i++); + return $relationName; + } + + /** + * Execute the action. + * @param array command line parameters specific for this command + */ + public function run($args) + { + if(!isset($args[0])) + { + echo "Error: model class name is required.\n"; + echo $this->getHelp(); + return; + } + $className=$args[0]; + + if(($db=Yii::app()->getDb())===null) + { + echo "Error: an active 'db' connection is required.\n"; + echo "If you already added 'db' component in application configuration,\n"; + echo "please quit and re-enter the yiic shell.\n"; + return; + } + + $db->active=true; + $this->_schema=$db->schema; + + if(!preg_match('/^[\w\.\-\*]*(.*?)$/',$className,$matches)) + { + echo "Error: model class name is invalid.\n"; + return; + } + + if(empty($matches[1])) // without regular expression + { + $this->generateClassNames($this->_schema); + if(($pos=strrpos($className,'.'))===false) + $basePath=Yii::getPathOfAlias('application.models'); + else + { + $basePath=Yii::getPathOfAlias(substr($className,0,$pos)); + $className=substr($className,$pos+1); + } + if($className==='*') // generate all models + $this->generateRelations(); + else + { + $tableName=isset($args[1])?$args[1]:$className; + $tableName=$this->addPrefix($tableName); + $this->_tables[$tableName]=$className; + $this->generateRelations(); + $this->_classes=array($tableName=>$className); + } + } + else // with regular expression + { + $pattern=$matches[1]; + $pos=strrpos($className,$pattern); + if($pos>0) // only regexp is given + $basePath=Yii::getPathOfAlias(rtrim(substr($className,0,$pos),'.')); + else + $basePath=Yii::getPathOfAlias('application.models'); + $this->generateClassNames($this->_schema,$pattern); + $classes=$this->_tables; + $this->generateRelations(); + $this->_classes=$classes; + } + + if(count($this->_classes)>1) + { + $entries=array(); + $count=0; + foreach($this->_classes as $tableName=>$className) + $entries[]=++$count.". $className ($tableName)"; + echo "The following model classes (tables) match your criteria:\n"; + echo implode("\n",$entries)."\n\n"; + if(!$this->confirm("Do you want to generate the above classes?")) + return; + } + + $templatePath=$this->templatePath===null?YII_PATH.'/cli/views/shell/model':$this->templatePath; + $fixturePath=$this->fixturePath===null?Yii::getPathOfAlias('application.tests.fixtures'):$this->fixturePath; + $unitTestPath=$this->unitTestPath===null?Yii::getPathOfAlias('application.tests.unit'):$this->unitTestPath; + + $list=array(); + $files=array(); + foreach ($this->_classes as $tableName=>$className) + { + $files[$className]=$classFile=$basePath.DIRECTORY_SEPARATOR.$className.'.php'; + $list['models/'.$className.'.php']=array( + 'source'=>$templatePath.DIRECTORY_SEPARATOR.'model.php', + 'target'=>$classFile, + 'callback'=>array($this,'generateModel'), + 'params'=>array($className,$tableName), + ); + if($fixturePath!==false) + { + $list['fixtures/'.$tableName.'.php']=array( + 'source'=>$templatePath.DIRECTORY_SEPARATOR.'fixture.php', + 'target'=>$fixturePath.DIRECTORY_SEPARATOR.$tableName.'.php', + 'callback'=>array($this,'generateFixture'), + 'params'=>$this->_schema->getTable($tableName), + ); + } + if($unitTestPath!==false) + { + $fixtureName=$this->pluralize($className); + $fixtureName[0]=strtolower($fixtureName); + $list['unit/'.$className.'Test.php']=array( + 'source'=>$templatePath.DIRECTORY_SEPARATOR.'test.php', + 'target'=>$unitTestPath.DIRECTORY_SEPARATOR.$className.'Test.php', + 'callback'=>array($this,'generateTest'), + 'params'=>array($className,$fixtureName), + ); + } + } + + $this->copyFiles($list); + + foreach($files as $className=>$file) + { + if(!class_exists($className,false)) + include_once($file); + } + + $classes=implode(", ", $this->_classes); + + echo <<<EOD + +The following model classes are successfully generated: + $classes + +If you have a 'db' database connection, you can test these models now with: + \$model={$className}::model()->find(); + print_r(\$model); + +EOD; + } + + public function generateModel($source,$params) + { + list($className,$tableName)=$params; + $rules=array(); + $labels=array(); + $relations=array(); + if(($table=$this->_schema->getTable($tableName))!==null) + { + $required=array(); + $integers=array(); + $numerical=array(); + $length=array(); + $safe=array(); + foreach($table->columns as $column) + { + $label=ucwords(trim(strtolower(str_replace(array('-','_'),' ',preg_replace('/(?<![A-Z])[A-Z]/', ' \0', $column->name))))); + $label=preg_replace('/\s+/',' ',$label); + if(strcasecmp(substr($label,-3),' id')===0) + $label=substr($label,0,-3); + $labels[$column->name]=$label; + if($column->isPrimaryKey && $table->sequenceName!==null) + continue; + $r=!$column->allowNull && $column->defaultValue===null; + if($r) + $required[]=$column->name; + if($column->type==='integer') + $integers[]=$column->name; + else if($column->type==='double') + $numerical[]=$column->name; + else if($column->type==='string' && $column->size>0) + $length[$column->size][]=$column->name; + else if(!$column->isPrimaryKey && !$r) + $safe[]=$column->name; + } + if($required!==array()) + $rules[]="array('".implode(', ',$required)."', 'required')"; + if($integers!==array()) + $rules[]="array('".implode(', ',$integers)."', 'numerical', 'integerOnly'=>true)"; + if($numerical!==array()) + $rules[]="array('".implode(', ',$numerical)."', 'numerical')"; + if($length!==array()) + { + foreach($length as $len=>$cols) + $rules[]="array('".implode(', ',$cols)."', 'length', 'max'=>$len)"; + } + if($safe!==array()) + $rules[]="array('".implode(', ',$safe)."', 'safe')"; + + if(isset($this->_relations[$className]) && is_array($this->_relations[$className])) + $relations=$this->_relations[$className]; + } + else + echo "Warning: the table '$tableName' does not exist in the database.\n"; + + if(!is_file($source)) // fall back to default ones + $source=YII_PATH.'/cli/views/shell/model/'.basename($source); + return $this->renderFile($source,array( + 'className'=>$className, + 'tableName'=>$this->removePrefix($tableName,true), + 'columns'=>isset($table) ? $table->columns : array(), + 'rules'=>$rules, + 'labels'=>$labels, + 'relations'=>$relations, + ),true); + } + + public function generateFixture($source,$table) + { + if(!is_file($source)) // fall back to default ones + $source=YII_PATH.'/cli/views/shell/model/'.basename($source); + return $this->renderFile($source, array( + 'table'=>$table, + ),true); + } + + public function generateTest($source,$params) + { + list($className,$fixtureName)=$params; + if(!is_file($source)) // fall back to default ones + $source=YII_PATH.'/cli/views/shell/model/'.basename($source); + return $this->renderFile($source, array( + 'className'=>$className, + 'fixtureName'=>$fixtureName, + ),true); + } + + protected function removePrefix($tableName,$addBrackets=false) + { + $tablePrefix=Yii::app()->getDb()->tablePrefix; + if($tablePrefix!='' && !strncmp($tableName,$tablePrefix,strlen($tablePrefix))) + { + $tableName=substr($tableName,strlen($tablePrefix)); + if($addBrackets) + $tableName='{{'.$tableName.'}}'; + } + return $tableName; + } + + protected function addPrefix($tableName) + { + $tablePrefix=Yii::app()->getDb()->tablePrefix; + if($tablePrefix!='' && strncmp($tableName,$tablePrefix,strlen($tablePrefix))) + $tableName=$tablePrefix.$tableName; + return $tableName; + } +}
\ No newline at end of file diff --git a/framework/cli/commands/shell/ModuleCommand.php b/framework/cli/commands/shell/ModuleCommand.php new file mode 100644 index 0000000..51a2158 --- /dev/null +++ b/framework/cli/commands/shell/ModuleCommand.php @@ -0,0 +1,92 @@ +<?php +/** + * ModuleCommand class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + * @version $Id: ModuleCommand.php 433 2008-12-30 22:59:17Z qiang.xue $ + */ + +/** + * ModuleCommand generates a controller class. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: ModuleCommand.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.cli.commands.shell + */ +class ModuleCommand extends CConsoleCommand +{ + /** + * @var string the directory that contains templates for the module command. + * Defaults to null, meaning using 'framework/cli/views/shell/module'. + * If you set this path and some views are missing in the directory, + * the default views will be used. + */ + public $templatePath; + + public function getHelp() + { + return <<<EOD +USAGE + module <module-ID> + +DESCRIPTION + This command generates an application module. + +PARAMETERS + * module-ID: required, module ID. It is case-sensitive. + +EOD; + } + + /** + * Execute the action. + * @param array command line parameters specific for this command + */ + public function run($args) + { + if(!isset($args[0])) + { + echo "Error: module ID is required.\n"; + echo $this->getHelp(); + return; + } + + $moduleID=$args[0]; + $moduleClass=ucfirst($moduleID).'Module'; + $modulePath=Yii::app()->getModulePath().DIRECTORY_SEPARATOR.$moduleID; + + $sourceDir=$this->templatePath===null?YII_PATH.'/cli/views/shell/module':$this->templatePath; + $list=$this->buildFileList($sourceDir,$modulePath); + $list['module.php']['target']=$modulePath.DIRECTORY_SEPARATOR.$moduleClass.'.php'; + $list['module.php']['callback']=array($this,'generateModuleClass'); + $list['module.php']['params']=array( + 'moduleClass'=>$moduleClass, + 'moduleID'=>$moduleID, + ); + $list[$moduleClass.'.php']=$list['module.php']; + unset($list['module.php']); + + $this->copyFiles($list); + + echo <<<EOD + +Module '{$moduleID}' has been created under the following folder: + $modulePath + +You may access it in the browser using the following URL: + http://hostname/path/to/index.php?r=$moduleID + +Note, the module needs to be installed first by adding '{$moduleID}' +to the 'modules' property in the application configuration. + +EOD; + } + + public function generateModuleClass($source,$params) + { + return $this->renderFile($source,$params,true); + } +}
\ No newline at end of file diff --git a/framework/cli/runtime/.yii b/framework/cli/runtime/.yii new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/framework/cli/runtime/.yii diff --git a/framework/cli/views/shell/controller/controller.php b/framework/cli/views/shell/controller/controller.php new file mode 100644 index 0000000..295fc5f --- /dev/null +++ b/framework/cli/views/shell/controller/controller.php @@ -0,0 +1,47 @@ +<?php +/** + * This is the template for generating a controller class file. + * The following variables are available in this template: + * - $className: the class name of the controller + * - $actions: a list of action names for the controller + */ +?> +<?php echo "<?php\n"; ?> + +class <?php echo $className; ?> extends Controller +{ +<?php foreach($actions as $action): ?> + public function action<?php echo ucfirst($action); ?>() + { + $this->render('<?php echo $action; ?>'); + } + +<?php endforeach; ?> + // ----------------------------------------------------------- + // Uncomment the following methods and override them if needed + /* + public function filters() + { + // return the filter configuration for this controller, e.g.: + return array( + 'inlineFilterName', + array( + 'class'=>'path.to.FilterClass', + 'propertyName'=>'propertyValue', + ), + ); + } + + public function actions() + { + // return external action classes, e.g.: + return array( + 'action1'=>'path.to.ActionClass', + 'action2'=>array( + 'class'=>'path.to.AnotherActionClass', + 'propertyName'=>'propertyValue', + ), + ); + } + */ +}
\ No newline at end of file diff --git a/framework/cli/views/shell/controller/view.php b/framework/cli/views/shell/controller/view.php new file mode 100644 index 0000000..0bcc212 --- /dev/null +++ b/framework/cli/views/shell/controller/view.php @@ -0,0 +1,26 @@ +<?php +echo "<?php\n"; +$controller=substr($controller,0,strlen($controller)-10); +$label=ucwords(trim(strtolower(str_replace(array('-','_','.'),' ',preg_replace('/(?<![A-Z])[A-Z]/', ' \0', $controller))))); + +if($action==='index') +{ + echo "\$this->breadcrumbs=array( + '$label', +);"; +} +else +{ + $route=$controller.'/index'; + $route[0]=strtolower($route[0]); + $action=ucfirst($action); + echo "\$this->breadcrumbs=array( + '$label'=>array('$route'), + '$action', +);"; +} +?> +?> +<h1><?php echo '<?php'; ?> echo $this->id . '/' . $this->action->id; ?></h1> + +<p>You may change the content of this page by modifying the file <tt><?php echo '<?php'; ?> echo __FILE__; ?></tt>.</p> diff --git a/framework/cli/views/shell/crud/_form.php b/framework/cli/views/shell/crud/_form.php new file mode 100644 index 0000000..e0de820 --- /dev/null +++ b/framework/cli/views/shell/crud/_form.php @@ -0,0 +1,42 @@ +<?php +/** + * This is the template for generating the form view for crud. + * The following variables are available in this template: + * - $ID: the primary key name + * - $modelClass: the model class name + * - $columns: a list of column schema objects + */ +?> +<div class="form"> + +<?php echo "<?php \$form=\$this->beginWidget('CActiveForm', array( + 'id'=>'".$this->class2id($modelClass)."-form', + 'enableAjaxValidation'=>false, +)); ?>\n"; ?> + + <p class="note">Fields with <span class="required">*</span> are required.</p> + + <?php echo "<?php echo \$form->errorSummary(\$model); ?>\n"; ?> + +<?php +foreach($columns as $column) +{ + if($column->isPrimaryKey) + continue; +?> + <div class="row"> + <?php echo "<?php echo ".$this->generateActiveLabel($modelClass,$column)."; ?>\n"; ?> + <?php echo "<?php echo ".$this->generateActiveField($modelClass,$column)."; ?>\n"; ?> + <?php echo "<?php echo \$form->error(\$model,'{$column->name}'); ?>\n"; ?> + </div> + +<?php +} +?> + <div class="row buttons"> + <?php echo "<?php echo CHtml::submitButton(\$model->isNewRecord ? 'Create' : 'Save'); ?>\n"; ?> + </div> + +<?php echo "<?php \$this->endWidget(); ?>\n"; ?> + +</div><!-- form -->
\ No newline at end of file diff --git a/framework/cli/views/shell/crud/_search.php b/framework/cli/views/shell/crud/_search.php new file mode 100644 index 0000000..fd81c35 --- /dev/null +++ b/framework/cli/views/shell/crud/_search.php @@ -0,0 +1,35 @@ +<?php +/** + * This is the template for generating the form view for crud. + * The following variables are available in this template: + * - $ID: the primary key name + * - $modelClass: the model class name + * - $columns: a list of column schema objects + */ +?> +<div class="wide form"> + +<?php echo "<?php \$form=\$this->beginWidget('CActiveForm', array( + 'action'=>Yii::app()->createUrl(\$this->route), + 'method'=>'get', +)); ?>\n"; ?> + +<?php foreach($columns as $column): ?> +<?php + $field=$this->generateInputField($modelClass,$column); + if(strpos($field,'password')!==false) + continue; +?> + <div class="row"> + <?php echo "<?php echo \$form->label(\$model,'{$column->name}'); ?>\n"; ?> + <?php echo "<?php echo ".$this->generateActiveField($modelClass,$column)."; ?>\n"; ?> + </div> + +<?php endforeach; ?> + <div class="row buttons"> + <?php echo "<?php echo CHtml::submitButton('Search'); ?>\n"; ?> + </div> + +<?php echo "<?php \$this->endWidget(); ?>\n"; ?> + +</div><!-- search-form -->
\ No newline at end of file diff --git a/framework/cli/views/shell/crud/_view.php b/framework/cli/views/shell/crud/_view.php new file mode 100644 index 0000000..02f383d --- /dev/null +++ b/framework/cli/views/shell/crud/_view.php @@ -0,0 +1,29 @@ +<?php +/** + * This is the template for generating the partial view for rendering a single model. + * The following variables are available in this template: + * - $ID: the primary key name + * - $modelClass: the model class name + * - $columns: a list of column schema objects + */ +?> +<div class="view"> + +<?php +echo "\t<b><?php echo CHtml::encode(\$data->getAttributeLabel('{$ID}')); ?>:</b>\n"; +echo "\t<?php echo CHtml::link(CHtml::encode(\$data->{$ID}), array('view', 'id'=>\$data->{$ID})); ?>\n\t<br />\n\n"; +$count=0; +foreach($columns as $column) +{ + if($column->isPrimaryKey) + continue; + if(++$count==7) + echo "\t<?php /*\n"; + echo "\t<b><?php echo CHtml::encode(\$data->getAttributeLabel('{$column->name}')); ?>:</b>\n"; + echo "\t<?php echo CHtml::encode(\$data->{$column->name}); ?>\n\t<br />\n\n"; +} +if($count>=7) + echo "\t*/ ?>\n"; +?> + +</div>
\ No newline at end of file diff --git a/framework/cli/views/shell/crud/admin.php b/framework/cli/views/shell/crud/admin.php new file mode 100644 index 0000000..ae0bc0f --- /dev/null +++ b/framework/cli/views/shell/crud/admin.php @@ -0,0 +1,73 @@ +<?php +/** + * This is the template for generating the admin view for crud. + * The following variables are available in this template: + * - $ID: the primary key name + * - $modelClass: the model class name + * - $columns: a list of column schema objects + */ +?> +<?php +echo "<?php\n"; +$label=$this->class2name($modelClass,true); +echo "\$this->breadcrumbs=array( + '$label'=>array('index'), + 'Manage', +);\n"; +?> + +$this->menu=array( + array('label'=>'List <?php echo $modelClass; ?>', 'url'=>array('index')), + array('label'=>'Create <?php echo $modelClass; ?>', 'url'=>array('create')), +); + +Yii::app()->clientScript->registerScript('search', " +$('.search-button').click(function(){ + $('.search-form').toggle(); + return false; +}); +$('.search-form form').submit(function(){ + $.fn.yiiGridView.update('<?php echo $this->class2id($modelClass); ?>-grid', { + data: $(this).serialize() + }); + return false; +}); +"); +?> + +<h1>Manage <?php echo $this->class2name($modelClass,true); ?></h1> + +<p> +You may optionally enter a comparison operator (<b><</b>, <b><=</b>, <b>></b>, <b>>=</b>, <b><></b> +or <b>=</b>) at the beginning of each of your search values to specify how the comparison should be done. +</p> + +<?php echo "<?php echo CHtml::link('Advanced Search','#',array('class'=>'search-button')); ?>"; ?> + +<div class="search-form" style="display:none"> +<?php echo "<?php \$this->renderPartial('_search',array( + 'model'=>\$model, +)); ?>\n"; ?> +</div><!-- search-form --> + +<?php echo "<?php"; ?> $this->widget('zii.widgets.grid.CGridView', array( + 'id'=>'<?php echo $this->class2id($modelClass); ?>-grid', + 'dataProvider'=>$model->search(), + 'filter'=>$model, + 'columns'=>array( +<?php +$count=0; +foreach($columns as $column) +{ + if(++$count==7) + echo "\t\t/*\n"; + echo "\t\t'".$column->name."',\n"; +} +if($count>=7) + echo "\t\t*/\n"; +?> + array( + 'class'=>'CButtonColumn', + ), + ), +)); ?> diff --git a/framework/cli/views/shell/crud/controller.php b/framework/cli/views/shell/crud/controller.php new file mode 100644 index 0000000..cd7f79f --- /dev/null +++ b/framework/cli/views/shell/crud/controller.php @@ -0,0 +1,190 @@ +<?php +/** + * This is the template for generating the controller class file for crud. + * The following variables are available in this template: + * - $ID: the primary key name + * - $controllerClass: the controller class name + * - $modelClass: the model class name + */ +?> +<?php echo "<?php\n"; ?> + +class <?php echo $controllerClass; ?> extends Controller +{ + /** + * @var string the default layout for the views. Defaults to '//layouts/column2', meaning + * using two-column layout. See 'protected/views/layouts/column2.php'. + */ + public $layout='//layouts/column2'; + + /** + * @var CActiveRecord the currently loaded data model instance. + */ + private $_model; + + /** + * @return array action filters + */ + public function filters() + { + return array( + 'accessControl', // perform access control for CRUD operations + ); + } + + /** + * Specifies the access control rules. + * This method is used by the 'accessControl' filter. + * @return array access control rules + */ + public function accessRules() + { + return array( + array('allow', // allow all users to perform 'index' and 'view' actions + 'actions'=>array('index','view'), + 'users'=>array('*'), + ), + array('allow', // allow authenticated user to perform 'create' and 'update' actions + 'actions'=>array('create','update'), + 'users'=>array('@'), + ), + array('allow', // allow admin user to perform 'admin' and 'delete' actions + 'actions'=>array('admin','delete'), + 'users'=>array('admin'), + ), + array('deny', // deny all users + 'users'=>array('*'), + ), + ); + } + + /** + * Displays a particular model. + */ + public function actionView() + { + $this->render('view',array( + 'model'=>$this->loadModel(), + )); + } + + /** + * Creates a new model. + * If creation is successful, the browser will be redirected to the 'view' page. + */ + public function actionCreate() + { + $model=new <?php echo $modelClass; ?>; + + // Uncomment the following line if AJAX validation is needed + // $this->performAjaxValidation($model); + + if(isset($_POST['<?php echo $modelClass; ?>'])) + { + $model->attributes=$_POST['<?php echo $modelClass; ?>']; + if($model->save()) + $this->redirect(array('view','id'=>$model-><?php echo $ID; ?>)); + } + + $this->render('create',array( + 'model'=>$model, + )); + } + + /** + * Updates a particular model. + * If update is successful, the browser will be redirected to the 'view' page. + */ + public function actionUpdate() + { + $model=$this->loadModel(); + + // Uncomment the following line if AJAX validation is needed + // $this->performAjaxValidation($model); + + if(isset($_POST['<?php echo $modelClass; ?>'])) + { + $model->attributes=$_POST['<?php echo $modelClass; ?>']; + if($model->save()) + $this->redirect(array('view','id'=>$model-><?php echo $ID; ?>)); + } + + $this->render('update',array( + 'model'=>$model, + )); + } + + /** + * Deletes a particular model. + * If deletion is successful, the browser will be redirected to the 'index' page. + */ + public function actionDelete() + { + if(Yii::app()->request->isPostRequest) + { + // we only allow deletion via POST request + $this->loadModel()->delete(); + + // if AJAX request (triggered by deletion via admin grid view), we should not redirect the browser + if(!isset($_GET['ajax'])) + $this->redirect(array('index')); + } + else + throw new CHttpException(400,'Invalid request. Please do not repeat this request again.'); + } + + /** + * Lists all models. + */ + public function actionIndex() + { + $dataProvider=new CActiveDataProvider('<?php echo $modelClass; ?>'); + $this->render('index',array( + 'dataProvider'=>$dataProvider, + )); + } + + /** + * Manages all models. + */ + public function actionAdmin() + { + $model=new <?php echo $modelClass; ?>('search'); + $model->unsetAttributes(); // clear any default values + if(isset($_GET['<?php echo $modelClass; ?>'])) + $model->attributes=$_GET['<?php echo $modelClass; ?>']; + + $this->render('admin',array( + 'model'=>$model, + )); + } + + /** + * Returns the data model based on the primary key given in the GET variable. + * If the data model is not found, an HTTP exception will be raised. + */ + public function loadModel() + { + if($this->_model===null) + { + if(isset($_GET['id'])) + $this->_model=<?php echo $modelClass; ?>::model()->findbyPk($_GET['id']); + if($this->_model===null) + throw new CHttpException(404,'The requested page does not exist.'); + } + return $this->_model; + } + + /** + * Performs the AJAX validation. + * @param CModel the model to be validated + */ + protected function performAjaxValidation($model) + { + if(isset($_POST['ajax']) && $_POST['ajax']==='<?php echo $this->class2id($modelClass); ?>-form') + { + echo CActiveForm::validate($model); + Yii::app()->end(); + } + } +} diff --git a/framework/cli/views/shell/crud/create.php b/framework/cli/views/shell/crud/create.php new file mode 100644 index 0000000..64e3cac --- /dev/null +++ b/framework/cli/views/shell/crud/create.php @@ -0,0 +1,27 @@ +<?php +/** + * This is the template for generating the create view for crud. + * The following variables are available in this template: + * - $ID: the primary key name + * - $modelClass: the model class name + * - $columns: a list of column schema objects + */ +?> +<?php +echo "<?php\n"; +$label=$this->class2name($modelClass,true); +echo "\$this->breadcrumbs=array( + '$label'=>array('index'), + 'Create', +);\n"; +?> + +$this->menu=array( + array('label'=>'List <?php echo $modelClass; ?>', 'url'=>array('index')), + array('label'=>'Manage <?php echo $modelClass; ?>', 'url'=>array('admin')), +); +?> + +<h1>Create <?php echo $modelClass; ?></h1> + +<?php echo "<?php echo \$this->renderPartial('_form', array('model'=>\$model)); ?>"; ?> diff --git a/framework/cli/views/shell/crud/index.php b/framework/cli/views/shell/crud/index.php new file mode 100644 index 0000000..658896e --- /dev/null +++ b/framework/cli/views/shell/crud/index.php @@ -0,0 +1,31 @@ +<?php +/** + * This is the template for generating the index view for crud. + * The following variables are available in this template: + * - $ID: the primary key name + * - $modelClass: the model class name + * - $columns: a list of column schema objects + */ +?> +<?php +echo "<?php\n"; +$label=$this->class2name($modelClass,true); +$route=$modelClass.'/index'; +$route[0]=strtolower($route[0]); +echo "\$this->breadcrumbs=array( + '$label', +);\n"; +?> + +$this->menu=array( + array('label'=>'Create <?php echo $modelClass; ?>', 'url'=>array('create')), + array('label'=>'Manage <?php echo $modelClass; ?>', 'url'=>array('admin')), +); +?> + +<h1><?php echo $label; ?></h1> + +<?php echo "<?php"; ?> $this->widget('zii.widgets.CListView', array( + 'dataProvider'=>$dataProvider, + 'itemView'=>'_view', +)); ?> diff --git a/framework/cli/views/shell/crud/test.php b/framework/cli/views/shell/crud/test.php new file mode 100644 index 0000000..fbb1fd9 --- /dev/null +++ b/framework/cli/views/shell/crud/test.php @@ -0,0 +1,47 @@ +<?php +/** + * This is the template for generating the functional test for controller. + * The following variables are available in this template: + * - $controllerID: the controller ID + * - $fixtureName: the fixture name + * - $modelClass: the model class name + */ +?> +<?php echo "<?php\n"; ?> + +class <?php echo $modelClass; ?>Test extends WebTestCase +{ + public $fixtures=array( + '<?php echo $fixtureName; ?>'=>'<?php echo $modelClass; ?>', + ); + + public function testShow() + { + $this->open('?r=<?php echo $controllerID; ?>/view&id=1'); + } + + public function testCreate() + { + $this->open('?r=<?php echo $controllerID; ?>/create'); + } + + public function testUpdate() + { + $this->open('?r=<?php echo $controllerID; ?>/update&id=1'); + } + + public function testDelete() + { + $this->open('?r=<?php echo $controllerID; ?>/view&id=1'); + } + + public function testList() + { + $this->open('?r=<?php echo $controllerID; ?>/index'); + } + + public function testAdmin() + { + $this->open('?r=<?php echo $controllerID; ?>/admin'); + } +} diff --git a/framework/cli/views/shell/crud/update.php b/framework/cli/views/shell/crud/update.php new file mode 100644 index 0000000..88aee25 --- /dev/null +++ b/framework/cli/views/shell/crud/update.php @@ -0,0 +1,31 @@ +<?php +/** + * This is the template for generating the update view for crud. + * The following variables are available in this template: + * - $ID: the primary key name + * - $modelClass: the model class name + * - $columns: a list of column schema objects + */ +?> +<?php +echo "<?php\n"; +$nameColumn=$this->guessNameColumn($columns); +$label=$this->class2name($modelClass,true); +echo "\$this->breadcrumbs=array( + '$label'=>array('index'), + \$model->{$nameColumn}=>array('view','id'=>\$model->{$ID}), + 'Update', +);\n"; +?> + +$this->menu=array( + array('label'=>'List <?php echo $modelClass; ?>', 'url'=>array('index')), + array('label'=>'Create <?php echo $modelClass; ?>', 'url'=>array('create')), + array('label'=>'View <?php echo $modelClass; ?>', 'url'=>array('view', 'id'=>$model-><?php echo $ID; ?>)), + array('label'=>'Manage <?php echo $modelClass; ?>', 'url'=>array('admin')), +); +?> + +<h1>Update <?php echo $modelClass." <?php echo \$model->{$ID}; ?>"; ?></h1> + +<?php echo "<?php echo \$this->renderPartial('_form', array('model'=>\$model)); ?>"; ?>
\ No newline at end of file diff --git a/framework/cli/views/shell/crud/view.php b/framework/cli/views/shell/crud/view.php new file mode 100644 index 0000000..692b328 --- /dev/null +++ b/framework/cli/views/shell/crud/view.php @@ -0,0 +1,39 @@ +<?php +/** + * This is the template for generating the 'view' view for crud. + * The following variables are available in this template: + * - $ID: the primary key name + * - $modelClass: the model class name + * - $columns: a list of column schema objects + */ +?> +<?php +echo "<?php\n"; +$nameColumn=$this->guessNameColumn($columns); +$label=$this->class2name($modelClass,true); +echo "\$this->breadcrumbs=array( + '$label'=>array('index'), + \$model->{$nameColumn}, +);\n"; +?> + +$this->menu=array( + array('label'=>'List <?php echo $modelClass; ?>', 'url'=>array('index')), + array('label'=>'Create <?php echo $modelClass; ?>', 'url'=>array('create')), + array('label'=>'Update <?php echo $modelClass; ?>', 'url'=>array('update', 'id'=>$model-><?php echo $ID; ?>)), + array('label'=>'Delete <?php echo $modelClass; ?>', 'url'=>'#', 'linkOptions'=>array('submit'=>array('delete','id'=>$model-><?php echo $ID; ?>),'confirm'=>'Are you sure you want to delete this item?')), + array('label'=>'Manage <?php echo $modelClass; ?>', 'url'=>array('admin')), +); +?> + +<h1>View <?php echo $modelClass." #<?php echo \$model->{$ID}; ?>"; ?></h1> + +<?php echo "<?php"; ?> $this->widget('zii.widgets.CDetailView', array( + 'data'=>$model, + 'attributes'=>array( +<?php +foreach($columns as $column) + echo "\t\t'".$column->name."',\n"; +?> + ), +)); ?> diff --git a/framework/cli/views/shell/form/action.php b/framework/cli/views/shell/form/action.php new file mode 100644 index 0000000..367c8ba --- /dev/null +++ b/framework/cli/views/shell/form/action.php @@ -0,0 +1,37 @@ +<?php +/** + * This is the template for generating the action script for the form. + * The following variables are available in this template: + * - $modelClass: the model class name + * - $viewName: the name of the view + */ +?> +<?php +$actionName=$modelClass; +if(strrpos($modelClass,'Form')===strlen($modelClass)-4) + $actionName=substr($modelClass,0,strlen($modelClass)-4); +?> +public function action<?php echo $actionName; ?>() +{ + $model=new <?php echo $modelClass; ?>; + + // uncomment the following code to enable ajax-based validation + /* + if(isset($_POST['ajax']) && $_POST['ajax']==='<?php echo $this->class2id($modelClass); ?>-form') + { + echo CActiveForm::validate($model); + Yii::app()->end(); + } + */ + + if(isset($_POST['<?php echo $modelClass; ?>'])) + { + $model->attributes=$_POST['<?php echo $modelClass; ?>']; + if($model->validate()) + { + // form inputs are valid, do something here + return; + } + } + $this->render('<?php echo $viewName; ?>',array('model'=>$model)); +}
\ No newline at end of file diff --git a/framework/cli/views/shell/form/form.php b/framework/cli/views/shell/form/form.php new file mode 100644 index 0000000..cbf63bb --- /dev/null +++ b/framework/cli/views/shell/form/form.php @@ -0,0 +1,39 @@ +<?php +/** + * This is the template for generating the form view for the specified model. + * The following variables are available in this template: + * - $modelClass: the model class name + * - $attributes: a list of attribute names to receive form inputs + */ +?> +<div class="form"> + +<?php echo "<?php \$form=\$this->beginWidget('CActiveForm', array( + 'id'=>'".$this->class2id($modelClass)."-form', + 'enableAjaxValidation'=>false, +)); ?>\n"; ?> + + <p class="note">Fields with <span class="required">*</span> are required.</p> + + <?php echo "<?php echo \$form->errorSummary(\$model); ?>\n"; ?> + +<?php +foreach($attributes as $attribute) +{ +?> + <div class="row"> + <?php echo "<?php echo \$form->labelEx(\$model,'$attribute'); ?>\n"; ?> + <?php echo "<?php echo \$form->textField(\$model,'$attribute'); ?>\n"; ?> + <?php echo "<?php echo \$form->error(\$model,'$attribute'); ?>\n"; ?> + </div> + +<?php +} +?> + <div class="row buttons"> + <?php echo "<?php echo CHtml::submitButton('Submit'); ?>\n"; ?> + </div> + +<?php echo "<?php \$this->endWidget(); ?>\n"; ?> + +</div><!-- form -->
\ No newline at end of file diff --git a/framework/cli/views/shell/model/fixture.php b/framework/cli/views/shell/model/fixture.php new file mode 100644 index 0000000..a159585 --- /dev/null +++ b/framework/cli/views/shell/model/fixture.php @@ -0,0 +1,25 @@ +<?php +/** + * This is the template for generating the fixture file for a model class. + * The following variables are available in this template: + * - $table: the table schema + */ +?> +<?php echo "<?php\n"; ?> + +return array( + /* + 'sample1'=>array( +<?php foreach($table->columns as $name=>$column) { + if($table->sequenceName===null || $table->primaryKey!==$column->name) + echo "\t\t'$name' => '',\n"; +} ?> + ), + 'sample2'=>array( +<?php foreach($table->columns as $name=>$column) { + if($table->sequenceName===null || $table->primaryKey!==$column->name) + echo "\t\t'$name' => '',\n"; +} ?> + ), + */ +); diff --git a/framework/cli/views/shell/model/model.php b/framework/cli/views/shell/model/model.php new file mode 100644 index 0000000..b1137db --- /dev/null +++ b/framework/cli/views/shell/model/model.php @@ -0,0 +1,113 @@ +<?php +/** + * This is the template for generating a model class file. + * The following variables are available in this template: + * - $className: the class name + * - $tableName: the table name + * - $columns: a list of table column schema objects + * - $rules: a list of validation rules (string) + * - $labels: a list of labels (column name => label) + * - $relations: a list of relations (string) + */ +?> +<?php echo "<?php\n"; ?> + +/** + * This is the model class for table "<?php echo $tableName; ?>". + * + * The followings are the available columns in table '<?php echo $tableName; ?>': +<?php foreach($columns as $column): ?> + * @property <?php echo $column->type.' $'.$column->name."\n"; ?> +<?php endforeach; ?> + */ +class <?php echo $className; ?> extends CActiveRecord +{ + /** + * Returns the static model of the specified AR class. + * @return <?php echo $className; ?> the static model class + */ + public static function model($className=__CLASS__) + { + return parent::model($className); + } + + /** + * @return string the associated database table name + */ + public function tableName() + { + return '<?php echo $tableName; ?>'; + } + + /** + * @return array validation rules for model attributes. + */ + public function rules() + { + // NOTE: you should only define rules for those attributes that + // will receive user inputs. + return array( +<?php foreach($rules as $rule): ?> + <?php echo $rule.",\n"; ?> +<?php endforeach; ?> + // The following rule is used by search(). + // Please remove those attributes that should not be searched. + array('<?php echo implode(', ', array_keys($columns)); ?>', 'safe', 'on'=>'search'), + ); + } + + /** + * @return array relational rules. + */ + public function relations() + { + // NOTE: you may need to adjust the relation name and the related + // class name for the relations automatically generated below. + return array( +<?php foreach($relations as $name=>$relation): ?> + <?php echo "'$name' => $relation,\n"; ?> +<?php endforeach; ?> + ); + } + + /** + * @return array customized attribute labels (name=>label) + */ + public function attributeLabels() + { + return array( +<?php foreach($labels as $column=>$label): ?> + <?php echo "'$column' => '$label',\n"; ?> +<?php endforeach; ?> + ); + } + + /** + * Retrieves a list of models based on the current search/filter conditions. + * @return CActiveDataProvider the data provider that can return the models based on the search/filter conditions. + */ + public function search() + { + // Warning: Please modify the following code to remove attributes that + // should not be searched. + + $criteria=new CDbCriteria; + +<?php +foreach($columns as $name=>$column) +{ + if($column->type==='string') + { + echo "\t\t\$criteria->compare('$name',\$this->$name,true);\n\n"; + } + else + { + echo "\t\t\$criteria->compare('$name',\$this->$name);\n\n"; + } +} +?> + return new CActiveDataProvider('<?php echo $className; ?>', array( + 'criteria'=>$criteria, + )); + } +}
\ No newline at end of file diff --git a/framework/cli/views/shell/model/test.php b/framework/cli/views/shell/model/test.php new file mode 100644 index 0000000..93aa17e --- /dev/null +++ b/framework/cli/views/shell/model/test.php @@ -0,0 +1,21 @@ +<?php +/** + * This is the template for generating the unit test for a model class. + * The following variables are available in this template: + * - $className: the class name + * - $fixtureName: the fixture name + */ +?> +<?php echo "<?php\n"; ?> + +class <?php echo $className; ?>Test extends CDbTestCase +{ + public $fixtures=array( + '<?php echo $fixtureName; ?>'=>'<?php echo $className; ?>', + ); + + public function testCreate() + { + + } +}
\ No newline at end of file diff --git a/framework/cli/views/shell/module/components/.yii b/framework/cli/views/shell/module/components/.yii new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/framework/cli/views/shell/module/components/.yii diff --git a/framework/cli/views/shell/module/controllers/DefaultController.php b/framework/cli/views/shell/module/controllers/DefaultController.php new file mode 100644 index 0000000..957551e --- /dev/null +++ b/framework/cli/views/shell/module/controllers/DefaultController.php @@ -0,0 +1,9 @@ +<?php + +class DefaultController extends Controller +{ + public function actionIndex() + { + $this->render('index'); + } +}
\ No newline at end of file diff --git a/framework/cli/views/shell/module/messages/.yii b/framework/cli/views/shell/module/messages/.yii new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/framework/cli/views/shell/module/messages/.yii diff --git a/framework/cli/views/shell/module/models/.yii b/framework/cli/views/shell/module/models/.yii new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/framework/cli/views/shell/module/models/.yii diff --git a/framework/cli/views/shell/module/module.php b/framework/cli/views/shell/module/module.php new file mode 100644 index 0000000..c8f6e02 --- /dev/null +++ b/framework/cli/views/shell/module/module.php @@ -0,0 +1,28 @@ +<?php echo "<?php\n"; ?> + +class <?php echo $moduleClass; ?> extends CWebModule +{ + public function init() + { + // this method is called when the module is being created + // you may place code here to customize the module or the application + + // import the module-level models and components + $this->setImport(array( + '<?php echo $moduleID; ?>.models.*', + '<?php echo $moduleID; ?>.components.*', + )); + } + + public function beforeControllerAction($controller, $action) + { + if(parent::beforeControllerAction($controller, $action)) + { + // this method is called before any module controller action is performed + // you may place customized code here + return true; + } + else + return false; + } +} diff --git a/framework/cli/views/shell/module/views/default/index.php b/framework/cli/views/shell/module/views/default/index.php new file mode 100644 index 0000000..b726985 --- /dev/null +++ b/framework/cli/views/shell/module/views/default/index.php @@ -0,0 +1,14 @@ +<?php +$this->breadcrumbs=array( + $this->module->id, +); +?> +<h1><?php echo $this->uniqueId . '/' . $this->action->id; ?></h1> + +<p> +This is the view content for action "<?php echo $this->action->id; ?>". +The action belongs to the controller "<?php echo get_class($this); ?>" in the "<?php echo $this->module->id; ?>" module. +</p> +<p> +You may customize this page by editing <tt><?php echo __FILE__; ?></tt> +</p>
\ No newline at end of file diff --git a/framework/cli/views/shell/module/views/layouts/.yii b/framework/cli/views/shell/module/views/layouts/.yii new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/framework/cli/views/shell/module/views/layouts/.yii diff --git a/framework/cli/views/webapp/assets/.yii b/framework/cli/views/webapp/assets/.yii new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/framework/cli/views/webapp/assets/.yii diff --git a/framework/cli/views/webapp/css/bg.gif b/framework/cli/views/webapp/css/bg.gif Binary files differnew file mode 100644 index 0000000..4283989 --- /dev/null +++ b/framework/cli/views/webapp/css/bg.gif diff --git a/framework/cli/views/webapp/css/form.css b/framework/cli/views/webapp/css/form.css new file mode 100644 index 0000000..b194603 --- /dev/null +++ b/framework/cli/views/webapp/css/form.css @@ -0,0 +1,160 @@ +/** + * CSS styles for forms generated by yiic. + * + * The styles can be applied to the following form structure: + * + * <div class="form"> + * <div class="row"> + * <label for="inputid">xyz</label> + * <input name="inputid" id="inputid" type="text" /> + * <p class="hint">hint text</p> + * </div> + * <div class="row"> + * <label for="inputid">xyz</label> + * <input name="inputid" id="inputid" type="text" /> + * <p class="hint">hint text</p> + * </div> + * <div class="row buttons"> + * <label for="inputid">xyz</label> + * <input name="inputid" id="inputid" type="text" /> + * <p class="hint">hint text</p> + * </div> + * </div> + * + * The above code will render the labels and input fields in separate lines. + * In order to render them in the same line, please use the "wide" form as follows, + * + * <div class="wide form"> + * ...... + * </div> + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2010 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +div.form +{ +} + +div.form input, +div.form textarea, +div.form select +{ + margin: 0.2em 0 0.5em 0; +} + +div.form fieldset +{ + border: 1px solid #DDD; + padding: 10px; + margin: 0 0 10px 0; + -moz-border-radius:7px; +} + +div.form label +{ + font-weight: bold; + font-size: 0.9em; + display: block; +} + +div.form .row +{ + margin: 5px 0; +} + +div.form .hint +{ + margin: 0; + padding: 0; + color: #999; +} + +div.form .note +{ + font-style: italic; +} + +div.form span.required +{ + color: red; +} + +div.form div.error label:first-child, +div.form label.error, +div.form span.error +{ + color: #C00; +} + +div.form div.error input, +div.form div.error textarea, +div.form div.error select, +div.form input.error, +div.form textarea.error, +div.form select.error +{ + background: #FEE; + border-color: #C00; +} + +div.form div.success input, +div.form div.success textarea, +div.form div.success select, +div.form input.success, +div.form textarea.success, +div.form select.success +{ + background: #E6EFC2; + border-color: #C6D880; +} + + +div.form .errorSummary +{ + border: 2px solid #C00; + padding: 7px 7px 12px 7px; + margin: 0 0 20px 0; + background: #FEE; + font-size: 0.9em; +} + +div.form .errorMessage +{ + color: red; + font-size: 0.9em; +} + +div.form .errorSummary p +{ + margin: 0; + padding: 5px; +} + +div.form .errorSummary ul +{ + margin: 0; + padding: 0 0 0 20px; +} + +div.wide.form label +{ + float: left; + margin-right: 10px; + position: relative; + text-align: right; + width: 100px; +} + +div.wide.form .row +{ + clear: left; +} + +div.wide.form .buttons, div.wide.form .hint, div.wide.form .errorMessage +{ + clear: left; + padding-left: 110px; +} diff --git a/framework/cli/views/webapp/css/ie.css b/framework/cli/views/webapp/css/ie.css new file mode 100644 index 0000000..f015399 --- /dev/null +++ b/framework/cli/views/webapp/css/ie.css @@ -0,0 +1,36 @@ +/* ----------------------------------------------------------------------- + + + Blueprint CSS Framework 1.0.1 + http://blueprintcss.org + + * Copyright (c) 2007-Present. See LICENSE for more info. + * See README for instructions on how to use Blueprint. + * For credits and origins, see AUTHORS. + * This is a compressed file. See the sources in the 'src' directory. + +----------------------------------------------------------------------- */ + +/* ie.css */ +body {text-align:center;} +.container {text-align:left;} +* html .column, * html .span-1, * html .span-2, * html .span-3, * html .span-4, * html .span-5, * html .span-6, * html .span-7, * html .span-8, * html .span-9, * html .span-10, * html .span-11, * html .span-12, * html .span-13, * html .span-14, * html .span-15, * html .span-16, * html .span-17, * html .span-18, * html .span-19, * html .span-20, * html .span-21, * html .span-22, * html .span-23, * html .span-24 {display:inline;overflow-x:hidden;} +* html legend {margin:0px -8px 16px 0;padding:0;} +sup {vertical-align:text-top;} +sub {vertical-align:text-bottom;} +html>body p code {*white-space:normal;} +hr {margin:-8px auto 11px;} +img {-ms-interpolation-mode:bicubic;} +.clearfix, .container {display:inline-block;} +* html .clearfix, * html .container {height:1%;} +fieldset {padding-top:0;} +legend {margin-top:-0.2em;margin-bottom:1em;margin-left:-0.5em;} +textarea {overflow:auto;} +label {vertical-align:middle;position:relative;top:-0.25em;} +input.text, input.title, textarea {background-color:#fff;border:1px solid #bbb;} +input.text:focus, input.title:focus {border-color:#666;} +input.text, input.title, textarea, select {margin:0.5em 0;} +input.checkbox, input.radio {position:relative;top:.25em;} +form.inline div, form.inline p {vertical-align:middle;} +form.inline input.checkbox, form.inline input.radio, form.inline input.button, form.inline button {margin:0.5em 0;} +button, input.button {position:relative;top:0.25em;}
\ No newline at end of file diff --git a/framework/cli/views/webapp/css/main.css b/framework/cli/views/webapp/css/main.css new file mode 100644 index 0000000..13327b8 --- /dev/null +++ b/framework/cli/views/webapp/css/main.css @@ -0,0 +1,229 @@ +body +{ + margin: 0; + padding: 0; + color: #555; + font: normal 10pt Arial,Helvetica,sans-serif; + background: #EFEFEF; +} + +#page +{ + margin-top: 5px; + margin-bottom: 5px; + background: white; + border: 1px solid #C9E0ED; +} + +#header +{ + margin: 0; + padding: 0; + border-top: 3px solid #C9E0ED; +} + +#content +{ + padding: 20px; +} + +#sidebar +{ + padding: 20px 20px 20px 0; +} + +#footer +{ + padding: 10px; + margin: 10px 20px; + font-size: 0.8em; + text-align: center; + border-top: 1px solid #C9E0ED; +} + +#logo +{ + padding: 10px 20px; + font-size: 200%; +} + +#mainmenu +{ + background:white url(bg.gif) repeat-x left top; +} + +#mainmenu ul +{ + padding:6px 20px 5px 20px; + margin:0px; +} + +#mainmenu ul li +{ + display: inline; +} + +#mainmenu ul li a +{ + color:#ffffff; + background-color:transparent; + font-size:12px; + font-weight:bold; + text-decoration:none; + padding:5px 8px; +} + +#mainmenu ul li a:hover, #mainmenu ul li.active a +{ + color: #6399cd; + background-color:#EFF4FA; + text-decoration:none; +} + +div.flash-error, div.flash-notice, div.flash-success +{ + padding:.8em; + margin-bottom:1em; + border:2px solid #ddd; +} + +div.flash-error +{ + background:#FBE3E4; + color:#8a1f11; + border-color:#FBC2C4; +} + +div.flash-notice +{ + background:#FFF6BF; + color:#514721; + border-color:#FFD324; +} + +div.flash-success +{ + background:#E6EFC2; + color:#264409; + border-color:#C6D880; +} + +div.flash-error a +{ + color:#8a1f11; +} + +div.flash-notice a +{ + color:#514721; +} + +div.flash-success a +{ + color:#264409; +} + +div.form .rememberMe label +{ + display: inline; +} + +div.view +{ + padding: 10px; + margin: 10px 0; + border: 1px solid #C9E0ED; +} + +div.breadcrumbs +{ + font-size: 0.9em; + padding: 5px 20px; +} + +div.breadcrumbs span +{ + font-weight: bold; +} + +div.search-form +{ + padding: 10px; + margin: 10px 0; + background: #eee; +} + +.portlet +{ + +} + +.portlet-decoration +{ + padding: 3px 8px; + background: #B7D6E7; + border-left: 5px solid #6FACCF; +} + +.portlet-title +{ + font-size: 12px; + font-weight: bold; + padding: 0; + margin: 0; + color: #298dcd; +} + +.portlet-content +{ + font-size:0.9em; + margin: 0 0 15px 0; + padding: 5px 8px; + background:#EFFDFF; +} + +.portlet-content ul +{ + list-style-image:none; + list-style-position:outside; + list-style-type:none; + margin: 0; + padding: 0; +} + +.portlet-content li +{ + padding: 2px 0 4px 0px; +} + +.operations +{ + list-style-type: none; + margin: 0; + padding: 0; +} + +.operations li +{ + padding-bottom: 2px; +} + +.operations li a +{ + font: bold 12px Arial; + color: #0066A4; + display: block; + padding: 2px 0 2px 8px; + line-height: 15px; + text-decoration: none; +} + +.operations li a:visited +{ + color: #0066A4; +} + +.operations li a:hover +{ + background: #80CFFF; +}
\ No newline at end of file diff --git a/framework/cli/views/webapp/css/print.css b/framework/cli/views/webapp/css/print.css new file mode 100644 index 0000000..bd79afd --- /dev/null +++ b/framework/cli/views/webapp/css/print.css @@ -0,0 +1,29 @@ +/* ----------------------------------------------------------------------- + + + Blueprint CSS Framework 1.0.1 + http://blueprintcss.org + + * Copyright (c) 2007-Present. See LICENSE for more info. + * See README for instructions on how to use Blueprint. + * For credits and origins, see AUTHORS. + * This is a compressed file. See the sources in the 'src' directory. + +----------------------------------------------------------------------- */ + +/* print.css */ +body {line-height:1.5;font-family:"Helvetica Neue", Arial, Helvetica, sans-serif;color:#000;background:none;font-size:10pt;} +.container {background:none;} +hr {background:#ccc;color:#ccc;width:100%;height:2px;margin:2em 0;padding:0;border:none;} +hr.space {background:#fff;color:#fff;visibility:hidden;} +h1, h2, h3, h4, h5, h6 {font-family:"Helvetica Neue", Arial, "Lucida Grande", sans-serif;} +code {font:.9em "Courier New", Monaco, Courier, monospace;} +a img {border:none;} +p img.top {margin-top:0;} +blockquote {margin:1.5em;padding:1em;font-style:italic;font-size:.9em;} +.small {font-size:.9em;} +.large {font-size:1.1em;} +.quiet {color:#999;} +.hide {display:none;} +a:link, a:visited {background:transparent;font-weight:700;text-decoration:underline;} +a:link:after, a:visited:after {content:" (" attr(href) ")";font-size:90%;}
\ No newline at end of file diff --git a/framework/cli/views/webapp/css/screen.css b/framework/cli/views/webapp/css/screen.css new file mode 100644 index 0000000..7824d07 --- /dev/null +++ b/framework/cli/views/webapp/css/screen.css @@ -0,0 +1,238 @@ +/* ----------------------------------------------------------------------- + + + Blueprint CSS Framework 1.0.1 + http://blueprintcss.org + + * Copyright (c) 2007-Present. See LICENSE for more info. + * See README for instructions on how to use Blueprint. + * For credits and origins, see AUTHORS. + * This is a compressed file. See the sources in the 'src' directory. + +----------------------------------------------------------------------- */ + +/* reset.css */ +html {margin:0;padding:0;border:0;} +body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, code, del, dfn, em, img, q, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, dialog, figure, footer, header, hgroup, nav, section {margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:baseline;} +article, aside, details, figcaption, figure, dialog, footer, header, hgroup, menu, nav, section {display:block;} +body {line-height:1.5;background:white;} +table {border-collapse:separate;border-spacing:0;} +caption, th, td {text-align:left;font-weight:normal;float:none !important;} +table, th, td {vertical-align:middle;} +blockquote:before, blockquote:after, q:before, q:after {content:'';} +blockquote, q {quotes:"" "";} +a img {border:none;} +:focus {outline:0;} + +/* typography.css */ +html {font-size:100.01%;} +body {font-size:75%;color:#222;background:#fff;font-family:"Helvetica Neue", Arial, Helvetica, sans-serif;} +h1, h2, h3, h4, h5, h6 {font-weight:normal;color:#111;} +h1 {font-size:2em;line-height:1;margin-bottom:0.5em;} +h2 {font-size:1.6em;margin-bottom:0.75em;} +h3 {font-size:1.4em;line-height:1;margin-bottom:1em;} +h4 {font-size:1.2em;line-height:1.25;margin-bottom:1.25em;} +h5 {font-size:1em;font-weight:bold;margin-bottom:1.5em;} +h6 {font-size:1em;font-weight:bold;} +h1 img, h2 img, h3 img, h4 img, h5 img, h6 img {margin:0;} +p {margin:0 0 1.5em;} +.left {float:left !important;} +p .left {margin:1.5em 1.5em 1.5em 0;padding:0;} +.right {float:right !important;} +p .right {margin:1.5em 0 1.5em 1.5em;padding:0;} +a:focus, a:hover {color:#09f;} +a {color:#06c;text-decoration:underline;} +blockquote {margin:1.5em;color:#666;font-style:italic;} +strong, dfn {font-weight:bold;} +em, dfn {font-style:italic;} +sup, sub {line-height:0;} +abbr, acronym {border-bottom:1px dotted #666;} +address {margin:0 0 1.5em;font-style:italic;} +del {color:#666;} +pre {margin:1.5em 0;white-space:pre;} +pre, code, tt {font:1em 'andale mono', 'lucida console', monospace;line-height:1.5;} +li ul, li ol {margin:0;} +ul, ol {margin:0 1.5em 1.5em 0;padding-left:1.5em;} +ul {list-style-type:disc;} +ol {list-style-type:decimal;} +dl {margin:0 0 1.5em 0;} +dl dt {font-weight:bold;} +dd {margin-left:1.5em;} +table {margin-bottom:1.4em;width:100%;} +th {font-weight:bold;} +thead th {background:#c3d9ff;} +th, td, caption {padding:4px 10px 4px 5px;} +tfoot {font-style:italic;} +caption {background:#eee;} +.small {font-size:.8em;margin-bottom:1.875em;line-height:1.875em;} +.large {font-size:1.2em;line-height:2.5em;margin-bottom:1.25em;} +.hide {display:none;} +.quiet {color:#666;} +.loud {color:#000;} +.highlight {background:#ff0;} +.added {background:#060;color:#fff;} +.removed {background:#900;color:#fff;} +.first {margin-left:0;padding-left:0;} +.last {margin-right:0;padding-right:0;} +.top {margin-top:0;padding-top:0;} +.bottom {margin-bottom:0;padding-bottom:0;} + +/* grid.css */ +.container {width:950px;margin:0 auto;} +.column, .span-1, .span-2, .span-3, .span-4, .span-5, .span-6, .span-7, .span-8, .span-9, .span-10, .span-11, .span-12, .span-13, .span-14, .span-15, .span-16, .span-17, .span-18, .span-19, .span-20, .span-21, .span-22, .span-23, .span-24 {float:left;margin-right:10px;} +.last {margin-right:0;} +.span-1 {width:30px;} +.span-2 {width:70px;} +.span-3 {width:110px;} +.span-4 {width:150px;} +.span-5 {width:190px;} +.span-6 {width:230px;} +.span-7 {width:270px;} +.span-8 {width:310px;} +.span-9 {width:350px;} +.span-10 {width:390px;} +.span-11 {width:430px;} +.span-12 {width:470px;} +.span-13 {width:510px;} +.span-14 {width:550px;} +.span-15 {width:590px;} +.span-16 {width:630px;} +.span-17 {width:670px;} +.span-18 {width:710px;} +.span-19 {width:750px;} +.span-20 {width:790px;} +.span-21 {width:830px;} +.span-22 {width:870px;} +.span-23 {width:910px;} +.span-24 {width:950px;margin-right:0;} +input.span-1, textarea.span-1, input.span-2, textarea.span-2, input.span-3, textarea.span-3, input.span-4, textarea.span-4, input.span-5, textarea.span-5, input.span-6, textarea.span-6, input.span-7, textarea.span-7, input.span-8, textarea.span-8, input.span-9, textarea.span-9, input.span-10, textarea.span-10, input.span-11, textarea.span-11, input.span-12, textarea.span-12, input.span-13, textarea.span-13, input.span-14, textarea.span-14, input.span-15, textarea.span-15, input.span-16, textarea.span-16, input.span-17, textarea.span-17, input.span-18, textarea.span-18, input.span-19, textarea.span-19, input.span-20, textarea.span-20, input.span-21, textarea.span-21, input.span-22, textarea.span-22, input.span-23, textarea.span-23, input.span-24, textarea.span-24 {border-left-width:1px;border-right-width:1px;padding-left:5px;padding-right:5px;} +input.span-1, textarea.span-1 {width:18px;} +input.span-2, textarea.span-2 {width:58px;} +input.span-3, textarea.span-3 {width:98px;} +input.span-4, textarea.span-4 {width:138px;} +input.span-5, textarea.span-5 {width:178px;} +input.span-6, textarea.span-6 {width:218px;} +input.span-7, textarea.span-7 {width:258px;} +input.span-8, textarea.span-8 {width:298px;} +input.span-9, textarea.span-9 {width:338px;} +input.span-10, textarea.span-10 {width:378px;} +input.span-11, textarea.span-11 {width:418px;} +input.span-12, textarea.span-12 {width:458px;} +input.span-13, textarea.span-13 {width:498px;} +input.span-14, textarea.span-14 {width:538px;} +input.span-15, textarea.span-15 {width:578px;} +input.span-16, textarea.span-16 {width:618px;} +input.span-17, textarea.span-17 {width:658px;} +input.span-18, textarea.span-18 {width:698px;} +input.span-19, textarea.span-19 {width:738px;} +input.span-20, textarea.span-20 {width:778px;} +input.span-21, textarea.span-21 {width:818px;} +input.span-22, textarea.span-22 {width:858px;} +input.span-23, textarea.span-23 {width:898px;} +input.span-24, textarea.span-24 {width:938px;} +.append-1 {padding-right:40px;} +.append-2 {padding-right:80px;} +.append-3 {padding-right:120px;} +.append-4 {padding-right:160px;} +.append-5 {padding-right:200px;} +.append-6 {padding-right:240px;} +.append-7 {padding-right:280px;} +.append-8 {padding-right:320px;} +.append-9 {padding-right:360px;} +.append-10 {padding-right:400px;} +.append-11 {padding-right:440px;} +.append-12 {padding-right:480px;} +.append-13 {padding-right:520px;} +.append-14 {padding-right:560px;} +.append-15 {padding-right:600px;} +.append-16 {padding-right:640px;} +.append-17 {padding-right:680px;} +.append-18 {padding-right:720px;} +.append-19 {padding-right:760px;} +.append-20 {padding-right:800px;} +.append-21 {padding-right:840px;} +.append-22 {padding-right:880px;} +.append-23 {padding-right:920px;} +.prepend-1 {padding-left:40px;} +.prepend-2 {padding-left:80px;} +.prepend-3 {padding-left:120px;} +.prepend-4 {padding-left:160px;} +.prepend-5 {padding-left:200px;} +.prepend-6 {padding-left:240px;} +.prepend-7 {padding-left:280px;} +.prepend-8 {padding-left:320px;} +.prepend-9 {padding-left:360px;} +.prepend-10 {padding-left:400px;} +.prepend-11 {padding-left:440px;} +.prepend-12 {padding-left:480px;} +.prepend-13 {padding-left:520px;} +.prepend-14 {padding-left:560px;} +.prepend-15 {padding-left:600px;} +.prepend-16 {padding-left:640px;} +.prepend-17 {padding-left:680px;} +.prepend-18 {padding-left:720px;} +.prepend-19 {padding-left:760px;} +.prepend-20 {padding-left:800px;} +.prepend-21 {padding-left:840px;} +.prepend-22 {padding-left:880px;} +.prepend-23 {padding-left:920px;} +.border {padding-right:4px;margin-right:5px;border-right:1px solid #ddd;} +.colborder {padding-right:24px;margin-right:25px;border-right:1px solid #ddd;} +.pull-1 {margin-left:-40px;} +.pull-2 {margin-left:-80px;} +.pull-3 {margin-left:-120px;} +.pull-4 {margin-left:-160px;} +.pull-5 {margin-left:-200px;} +.pull-6 {margin-left:-240px;} +.pull-7 {margin-left:-280px;} +.pull-8 {margin-left:-320px;} +.pull-9 {margin-left:-360px;} +.pull-10 {margin-left:-400px;} +.pull-11 {margin-left:-440px;} +.pull-12 {margin-left:-480px;} +.pull-13 {margin-left:-520px;} +.pull-14 {margin-left:-560px;} +.pull-15 {margin-left:-600px;} +.pull-16 {margin-left:-640px;} +.pull-17 {margin-left:-680px;} +.pull-18 {margin-left:-720px;} +.pull-19 {margin-left:-760px;} +.pull-20 {margin-left:-800px;} +.pull-21 {margin-left:-840px;} +.pull-22 {margin-left:-880px;} +.pull-23 {margin-left:-920px;} +.pull-24 {margin-left:-960px;} +.pull-1, .pull-2, .pull-3, .pull-4, .pull-5, .pull-6, .pull-7, .pull-8, .pull-9, .pull-10, .pull-11, .pull-12, .pull-13, .pull-14, .pull-15, .pull-16, .pull-17, .pull-18, .pull-19, .pull-20, .pull-21, .pull-22, .pull-23, .pull-24 {float:left;position:relative;} +.push-1 {margin:0 -40px 1.5em 40px;} +.push-2 {margin:0 -80px 1.5em 80px;} +.push-3 {margin:0 -120px 1.5em 120px;} +.push-4 {margin:0 -160px 1.5em 160px;} +.push-5 {margin:0 -200px 1.5em 200px;} +.push-6 {margin:0 -240px 1.5em 240px;} +.push-7 {margin:0 -280px 1.5em 280px;} +.push-8 {margin:0 -320px 1.5em 320px;} +.push-9 {margin:0 -360px 1.5em 360px;} +.push-10 {margin:0 -400px 1.5em 400px;} +.push-11 {margin:0 -440px 1.5em 440px;} +.push-12 {margin:0 -480px 1.5em 480px;} +.push-13 {margin:0 -520px 1.5em 520px;} +.push-14 {margin:0 -560px 1.5em 560px;} +.push-15 {margin:0 -600px 1.5em 600px;} +.push-16 {margin:0 -640px 1.5em 640px;} +.push-17 {margin:0 -680px 1.5em 680px;} +.push-18 {margin:0 -720px 1.5em 720px;} +.push-19 {margin:0 -760px 1.5em 760px;} +.push-20 {margin:0 -800px 1.5em 800px;} +.push-21 {margin:0 -840px 1.5em 840px;} +.push-22 {margin:0 -880px 1.5em 880px;} +.push-23 {margin:0 -920px 1.5em 920px;} +.push-24 {margin:0 -960px 1.5em 960px;} +.push-1, .push-2, .push-3, .push-4, .push-5, .push-6, .push-7, .push-8, .push-9, .push-10, .push-11, .push-12, .push-13, .push-14, .push-15, .push-16, .push-17, .push-18, .push-19, .push-20, .push-21, .push-22, .push-23, .push-24 {float:left;position:relative;} +div.prepend-top, .prepend-top {margin-top:1.5em;} +div.append-bottom, .append-bottom {margin-bottom:1.5em;} +.box {padding:1.5em;margin-bottom:1.5em;background:#e5eCf9;} +hr {background:#ddd;color:#ddd;clear:both;float:none;width:100%;height:1px;margin:0 0 17px;border:none;} +hr.space {background:#fff;color:#fff;visibility:hidden;} +.clearfix:after, .container:after {content:"\0020";display:block;height:0;clear:both;visibility:hidden;overflow:hidden;} +.clearfix, .container {display:block;} +.clear {clear:both;} diff --git a/framework/cli/views/webapp/images/.yii b/framework/cli/views/webapp/images/.yii new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/framework/cli/views/webapp/images/.yii diff --git a/framework/cli/views/webapp/index-test.php b/framework/cli/views/webapp/index-test.php new file mode 100644 index 0000000..c6d5a04 --- /dev/null +++ b/framework/cli/views/webapp/index-test.php @@ -0,0 +1,15 @@ +<?php +/** + * This is the bootstrap file for test application. + * This file should be removed when the application is deployed for production. + */ + +// change the following paths if necessary +$yii=dirname(__FILE__).'/../framework/yii.php'; +$config=dirname(__FILE__).'/protected/config/test.php'; + +// remove the following line when in production mode +defined('YII_DEBUG') or define('YII_DEBUG',true); + +require_once($yii); +Yii::createWebApplication($config)->run(); diff --git a/framework/cli/views/webapp/index.php b/framework/cli/views/webapp/index.php new file mode 100644 index 0000000..11332f3 --- /dev/null +++ b/framework/cli/views/webapp/index.php @@ -0,0 +1,13 @@ +<?php + +// change the following paths if necessary +$yii=dirname(__FILE__).'/../framework/yii.php'; +$config=dirname(__FILE__).'/protected/config/main.php'; + +// remove the following lines when in production mode +defined('YII_DEBUG') or define('YII_DEBUG',true); +// specify how many levels of call stack should be shown in each log message +defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL',3); + +require_once($yii); +Yii::createWebApplication($config)->run(); diff --git a/framework/cli/views/webapp/protected/.htaccess b/framework/cli/views/webapp/protected/.htaccess new file mode 100644 index 0000000..8d2f256 --- /dev/null +++ b/framework/cli/views/webapp/protected/.htaccess @@ -0,0 +1 @@ +deny from all diff --git a/framework/cli/views/webapp/protected/commands/shell/.yii b/framework/cli/views/webapp/protected/commands/shell/.yii new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/framework/cli/views/webapp/protected/commands/shell/.yii diff --git a/framework/cli/views/webapp/protected/components/Controller.php b/framework/cli/views/webapp/protected/components/Controller.php new file mode 100644 index 0000000..4d27862 --- /dev/null +++ b/framework/cli/views/webapp/protected/components/Controller.php @@ -0,0 +1,23 @@ +<?php +/** + * Controller is the customized base controller class. + * All controller classes for this application should extend from this base class. + */ +class Controller extends CController +{ + /** + * @var string the default layout for the controller view. Defaults to '//layouts/column1', + * meaning using a single column layout. See 'protected/views/layouts/column1.php'. + */ + public $layout='//layouts/column1'; + /** + * @var array context menu items. This property will be assigned to {@link CMenu::items}. + */ + public $menu=array(); + /** + * @var array the breadcrumbs of the current page. The value of this property will + * be assigned to {@link CBreadcrumbs::links}. Please refer to {@link CBreadcrumbs::links} + * for more details on how to specify this property. + */ + public $breadcrumbs=array(); +}
\ No newline at end of file diff --git a/framework/cli/views/webapp/protected/components/UserIdentity.php b/framework/cli/views/webapp/protected/components/UserIdentity.php new file mode 100644 index 0000000..a9704e5 --- /dev/null +++ b/framework/cli/views/webapp/protected/components/UserIdentity.php @@ -0,0 +1,33 @@ +<?php + +/** + * UserIdentity represents the data needed to identity a user. + * It contains the authentication method that checks if the provided + * data can identity the user. + */ +class UserIdentity extends CUserIdentity +{ + /** + * Authenticates a user. + * The example implementation makes sure if the username and password + * are both 'demo'. + * In practical applications, this should be changed to authenticate + * against some persistent user identity storage (e.g. database). + * @return boolean whether authentication succeeds. + */ + public function authenticate() + { + $users=array( + // username => password + 'demo'=>'demo', + 'admin'=>'admin', + ); + if(!isset($users[$this->username])) + $this->errorCode=self::ERROR_USERNAME_INVALID; + else if($users[$this->username]!==$this->password) + $this->errorCode=self::ERROR_PASSWORD_INVALID; + else + $this->errorCode=self::ERROR_NONE; + return !$this->errorCode; + } +}
\ No newline at end of file diff --git a/framework/cli/views/webapp/protected/config/console.php b/framework/cli/views/webapp/protected/config/console.php new file mode 100644 index 0000000..15c9353 --- /dev/null +++ b/framework/cli/views/webapp/protected/config/console.php @@ -0,0 +1,24 @@ +<?php + +// This is the configuration for yiic console application. +// Any writable CConsoleApplication properties can be configured here. +return array( + 'basePath'=>dirname(__FILE__).DIRECTORY_SEPARATOR.'..', + 'name'=>'My Console Application', + // application components + 'components'=>array( + 'db'=>array( + 'connectionString' => 'sqlite:'.dirname(__FILE__).'/../data/testdrive.db', + ), + // uncomment the following to use a MySQL database + /* + 'db'=>array( + 'connectionString' => 'mysql:host=localhost;dbname=testdrive', + 'emulatePrepare' => true, + 'username' => 'root', + 'password' => '', + 'charset' => 'utf8', + ), + */ + ), +);
\ No newline at end of file diff --git a/framework/cli/views/webapp/protected/config/main.php b/framework/cli/views/webapp/protected/config/main.php new file mode 100644 index 0000000..9e840e4 --- /dev/null +++ b/framework/cli/views/webapp/protected/config/main.php @@ -0,0 +1,90 @@ +<?php + +// uncomment the following to define a path alias +// Yii::setPathOfAlias('local','path/to/local-folder'); + +// This is the main Web application configuration. Any writable +// CWebApplication properties can be configured here. +return array( + 'basePath'=>dirname(__FILE__).DIRECTORY_SEPARATOR.'..', + 'name'=>'My Web Application', + + // preloading 'log' component + 'preload'=>array('log'), + + // autoloading model and component classes + 'import'=>array( + 'application.models.*', + 'application.components.*', + ), + + 'modules'=>array( + // uncomment the following to enable the Gii tool + /* + 'gii'=>array( + 'class'=>'system.gii.GiiModule', + 'password'=>'Enter Your Password Here', + // If removed, Gii defaults to localhost only. Edit carefully to taste. + 'ipFilters'=>array('127.0.0.1','::1'), + ), + */ + ), + + // application components + 'components'=>array( + 'user'=>array( + // enable cookie-based authentication + 'allowAutoLogin'=>true, + ), + // uncomment the following to enable URLs in path-format + /* + 'urlManager'=>array( + 'urlFormat'=>'path', + 'rules'=>array( + '<controller:\w+>/<id:\d+>'=>'<controller>/view', + '<controller:\w+>/<action:\w+>/<id:\d+>'=>'<controller>/<action>', + '<controller:\w+>/<action:\w+>'=>'<controller>/<action>', + ), + ), + */ + 'db'=>array( + 'connectionString' => 'sqlite:'.dirname(__FILE__).'/../data/testdrive.db', + ), + // uncomment the following to use a MySQL database + /* + 'db'=>array( + 'connectionString' => 'mysql:host=localhost;dbname=testdrive', + 'emulatePrepare' => true, + 'username' => 'root', + 'password' => '', + 'charset' => 'utf8', + ), + */ + 'errorHandler'=>array( + // use 'site/error' action to display errors + 'errorAction'=>'site/error', + ), + 'log'=>array( + 'class'=>'CLogRouter', + 'routes'=>array( + array( + 'class'=>'CFileLogRoute', + 'levels'=>'error, warning', + ), + // uncomment the following to show log messages on web pages + /* + array( + 'class'=>'CWebLogRoute', + ), + */ + ), + ), + ), + + // application-level parameters that can be accessed + // using Yii::app()->params['paramName'] + 'params'=>array( + // this is used in contact page + 'adminEmail'=>'webmaster@example.com', + ), +);
\ No newline at end of file diff --git a/framework/cli/views/webapp/protected/config/test.php b/framework/cli/views/webapp/protected/config/test.php new file mode 100644 index 0000000..fd7085a --- /dev/null +++ b/framework/cli/views/webapp/protected/config/test.php @@ -0,0 +1,17 @@ +<?php + +return CMap::mergeArray( + require(dirname(__FILE__).'/main.php'), + array( + 'components'=>array( + 'fixture'=>array( + 'class'=>'system.test.CDbFixtureManager', + ), + /* uncomment the following to provide test database connection + 'db'=>array( + 'connectionString'=>'DSN for test database', + ), + */ + ), + ) +); diff --git a/framework/cli/views/webapp/protected/controllers/SiteController.php b/framework/cli/views/webapp/protected/controllers/SiteController.php new file mode 100644 index 0000000..8d3084c --- /dev/null +++ b/framework/cli/views/webapp/protected/controllers/SiteController.php @@ -0,0 +1,103 @@ +<?php + +class SiteController extends Controller +{ + /** + * Declares class-based actions. + */ + public function actions() + { + return array( + // captcha action renders the CAPTCHA image displayed on the contact page + 'captcha'=>array( + 'class'=>'CCaptchaAction', + 'backColor'=>0xFFFFFF, + ), + // page action renders "static" pages stored under 'protected/views/site/pages' + // They can be accessed via: index.php?r=site/page&view=FileName + 'page'=>array( + 'class'=>'CViewAction', + ), + ); + } + + /** + * This is the default 'index' action that is invoked + * when an action is not explicitly requested by users. + */ + public function actionIndex() + { + // renders the view file 'protected/views/site/index.php' + // using the default layout 'protected/views/layouts/main.php' + $this->render('index'); + } + + /** + * This is the action to handle external exceptions. + */ + public function actionError() + { + if($error=Yii::app()->errorHandler->error) + { + if(Yii::app()->request->isAjaxRequest) + echo $error['message']; + else + $this->render('error', $error); + } + } + + /** + * Displays the contact page + */ + public function actionContact() + { + $model=new ContactForm; + if(isset($_POST['ContactForm'])) + { + $model->attributes=$_POST['ContactForm']; + if($model->validate()) + { + $headers="From: {$model->email}\r\nReply-To: {$model->email}"; + mail(Yii::app()->params['adminEmail'],$model->subject,$model->body,$headers); + Yii::app()->user->setFlash('contact','Thank you for contacting us. We will respond to you as soon as possible.'); + $this->refresh(); + } + } + $this->render('contact',array('model'=>$model)); + } + + /** + * Displays the login page + */ + public function actionLogin() + { + $model=new LoginForm; + + // if it is ajax validation request + if(isset($_POST['ajax']) && $_POST['ajax']==='login-form') + { + echo CActiveForm::validate($model); + Yii::app()->end(); + } + + // collect user input data + if(isset($_POST['LoginForm'])) + { + $model->attributes=$_POST['LoginForm']; + // validate user input and redirect to the previous page if valid + if($model->validate() && $model->login()) + $this->redirect(Yii::app()->user->returnUrl); + } + // display the login form + $this->render('login',array('model'=>$model)); + } + + /** + * Logs out the current user and redirect to homepage. + */ + public function actionLogout() + { + Yii::app()->user->logout(); + $this->redirect(Yii::app()->homeUrl); + } +}
\ No newline at end of file diff --git a/framework/cli/views/webapp/protected/data/schema.mysql.sql b/framework/cli/views/webapp/protected/data/schema.mysql.sql new file mode 100644 index 0000000..32788bd --- /dev/null +++ b/framework/cli/views/webapp/protected/data/schema.mysql.sql @@ -0,0 +1,28 @@ +CREATE TABLE tbl_user ( + id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, + username VARCHAR(128) NOT NULL, + password VARCHAR(128) NOT NULL, + email VARCHAR(128) NOT NULL +); + +INSERT INTO tbl_user (username, password, email) VALUES ('test1', 'pass1', 'test1@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test2', 'pass2', 'test2@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test3', 'pass3', 'test3@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test4', 'pass4', 'test4@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test5', 'pass5', 'test5@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test6', 'pass6', 'test6@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test7', 'pass7', 'test7@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test8', 'pass8', 'test8@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test9', 'pass9', 'test9@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test10', 'pass10', 'test10@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test11', 'pass11', 'test11@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test12', 'pass12', 'test12@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test13', 'pass13', 'test13@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test14', 'pass14', 'test14@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test15', 'pass15', 'test15@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test16', 'pass16', 'test16@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test17', 'pass17', 'test17@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test18', 'pass18', 'test18@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test19', 'pass19', 'test19@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test20', 'pass20', 'test20@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test21', 'pass21', 'test21@example.com'); diff --git a/framework/cli/views/webapp/protected/data/schema.sqlite.sql b/framework/cli/views/webapp/protected/data/schema.sqlite.sql new file mode 100644 index 0000000..e5e0830 --- /dev/null +++ b/framework/cli/views/webapp/protected/data/schema.sqlite.sql @@ -0,0 +1,28 @@ +CREATE TABLE tbl_user ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + username VARCHAR(128) NOT NULL, + password VARCHAR(128) NOT NULL, + email VARCHAR(128) NOT NULL +); + +INSERT INTO tbl_user (username, password, email) VALUES ('test1', 'pass1', 'test1@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test2', 'pass2', 'test2@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test3', 'pass3', 'test3@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test4', 'pass4', 'test4@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test5', 'pass5', 'test5@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test6', 'pass6', 'test6@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test7', 'pass7', 'test7@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test8', 'pass8', 'test8@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test9', 'pass9', 'test9@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test10', 'pass10', 'test10@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test11', 'pass11', 'test11@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test12', 'pass12', 'test12@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test13', 'pass13', 'test13@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test14', 'pass14', 'test14@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test15', 'pass15', 'test15@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test16', 'pass16', 'test16@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test17', 'pass17', 'test17@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test18', 'pass18', 'test18@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test19', 'pass19', 'test19@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test20', 'pass20', 'test20@example.com'); +INSERT INTO tbl_user (username, password, email) VALUES ('test21', 'pass21', 'test21@example.com'); diff --git a/framework/cli/views/webapp/protected/data/testdrive.db b/framework/cli/views/webapp/protected/data/testdrive.db Binary files differnew file mode 100644 index 0000000..0672b21 --- /dev/null +++ b/framework/cli/views/webapp/protected/data/testdrive.db diff --git a/framework/cli/views/webapp/protected/extensions/.yii b/framework/cli/views/webapp/protected/extensions/.yii new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/framework/cli/views/webapp/protected/extensions/.yii diff --git a/framework/cli/views/webapp/protected/messages/.yii b/framework/cli/views/webapp/protected/messages/.yii new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/framework/cli/views/webapp/protected/messages/.yii diff --git a/framework/cli/views/webapp/protected/migrations/.yii b/framework/cli/views/webapp/protected/migrations/.yii new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/framework/cli/views/webapp/protected/migrations/.yii diff --git a/framework/cli/views/webapp/protected/models/ContactForm.php b/framework/cli/views/webapp/protected/models/ContactForm.php new file mode 100644 index 0000000..86541cb --- /dev/null +++ b/framework/cli/views/webapp/protected/models/ContactForm.php @@ -0,0 +1,42 @@ +<?php + +/** + * ContactForm class. + * ContactForm is the data structure for keeping + * contact form data. It is used by the 'contact' action of 'SiteController'. + */ +class ContactForm extends CFormModel +{ + public $name; + public $email; + public $subject; + public $body; + public $verifyCode; + + /** + * Declares the validation rules. + */ + public function rules() + { + return array( + // name, email, subject and body are required + array('name, email, subject, body', 'required'), + // email has to be a valid email address + array('email', 'email'), + // verifyCode needs to be entered correctly + array('verifyCode', 'captcha', 'allowEmpty'=>!CCaptcha::checkRequirements()), + ); + } + + /** + * Declares customized attribute labels. + * If not declared here, an attribute would have a label that is + * the same as its name with the first letter in upper case. + */ + public function attributeLabels() + { + return array( + 'verifyCode'=>'Verification Code', + ); + } +}
\ No newline at end of file diff --git a/framework/cli/views/webapp/protected/models/LoginForm.php b/framework/cli/views/webapp/protected/models/LoginForm.php new file mode 100644 index 0000000..eb36e4a --- /dev/null +++ b/framework/cli/views/webapp/protected/models/LoginForm.php @@ -0,0 +1,77 @@ +<?php + +/** + * LoginForm class. + * LoginForm is the data structure for keeping + * user login form data. It is used by the 'login' action of 'SiteController'. + */ +class LoginForm extends CFormModel +{ + public $username; + public $password; + public $rememberMe; + + private $_identity; + + /** + * Declares the validation rules. + * The rules state that username and password are required, + * and password needs to be authenticated. + */ + public function rules() + { + return array( + // username and password are required + array('username, password', 'required'), + // rememberMe needs to be a boolean + array('rememberMe', 'boolean'), + // password needs to be authenticated + array('password', 'authenticate'), + ); + } + + /** + * Declares attribute labels. + */ + public function attributeLabels() + { + return array( + 'rememberMe'=>'Remember me next time', + ); + } + + /** + * Authenticates the password. + * This is the 'authenticate' validator as declared in rules(). + */ + public function authenticate($attribute,$params) + { + if(!$this->hasErrors()) + { + $this->_identity=new UserIdentity($this->username,$this->password); + if(!$this->_identity->authenticate()) + $this->addError('password','Incorrect username or password.'); + } + } + + /** + * Logs in the user using the given username and password in the model. + * @return boolean whether login is successful + */ + public function login() + { + if($this->_identity===null) + { + $this->_identity=new UserIdentity($this->username,$this->password); + $this->_identity->authenticate(); + } + if($this->_identity->errorCode===UserIdentity::ERROR_NONE) + { + $duration=$this->rememberMe ? 3600*24*30 : 0; // 30 days + Yii::app()->user->login($this->_identity,$duration); + return true; + } + else + return false; + } +} diff --git a/framework/cli/views/webapp/protected/runtime/.yii b/framework/cli/views/webapp/protected/runtime/.yii new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/framework/cli/views/webapp/protected/runtime/.yii diff --git a/framework/cli/views/webapp/protected/tests/WebTestCase.php b/framework/cli/views/webapp/protected/tests/WebTestCase.php new file mode 100644 index 0000000..d252bba --- /dev/null +++ b/framework/cli/views/webapp/protected/tests/WebTestCase.php @@ -0,0 +1,25 @@ +<?php + +/** + * Change the following URL based on your server configuration + * Make sure the URL ends with a slash so that we can use relative URLs in test cases + */ +define('TEST_BASE_URL','http://localhost/testdrive/index-test.php/'); + +/** + * The base class for functional test cases. + * In this class, we set the base URL for the test application. + * We also provide some common methods to be used by concrete test classes. + */ +class WebTestCase extends CWebTestCase +{ + /** + * Sets up before each test method runs. + * This mainly sets the base URL for the test application. + */ + protected function setUp() + { + parent::setUp(); + $this->setBrowserUrl(TEST_BASE_URL); + } +} diff --git a/framework/cli/views/webapp/protected/tests/bootstrap.php b/framework/cli/views/webapp/protected/tests/bootstrap.php new file mode 100644 index 0000000..4404cac --- /dev/null +++ b/framework/cli/views/webapp/protected/tests/bootstrap.php @@ -0,0 +1,10 @@ +<?php + +// change the following paths if necessary +$yiit=dirname(__FILE__).'/../../../framework/yiit.php'; +$config=dirname(__FILE__).'/../config/test.php'; + +require_once($yiit); +require_once(dirname(__FILE__).'/WebTestCase.php'); + +Yii::createWebApplication($config); diff --git a/framework/cli/views/webapp/protected/tests/fixtures/.yii b/framework/cli/views/webapp/protected/tests/fixtures/.yii new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/framework/cli/views/webapp/protected/tests/fixtures/.yii diff --git a/framework/cli/views/webapp/protected/tests/functional/SiteTest.php b/framework/cli/views/webapp/protected/tests/functional/SiteTest.php new file mode 100644 index 0000000..cb9727c --- /dev/null +++ b/framework/cli/views/webapp/protected/tests/functional/SiteTest.php @@ -0,0 +1,47 @@ +<?php + +class SiteTest extends WebTestCase +{ + public function testIndex() + { + $this->open(''); + $this->assertTextPresent('Welcome'); + } + + public function testContact() + { + $this->open('?r=site/contact'); + $this->assertTextPresent('Contact Us'); + $this->assertElementPresent('name=ContactForm[name]'); + + $this->type('name=ContactForm[name]','tester'); + $this->type('name=ContactForm[email]','tester@example.com'); + $this->type('name=ContactForm[subject]','test subject'); + $this->click("//input[@value='Submit']"); + $this->waitForTextPresent('Body cannot be blank.'); + } + + public function testLoginLogout() + { + $this->open(''); + // ensure the user is logged out + if($this->isTextPresent('Logout')) + $this->clickAndWait('link=Logout (demo)'); + + // test login process, including validation + $this->clickAndWait('link=Login'); + $this->assertElementPresent('name=LoginForm[username]'); + $this->type('name=LoginForm[username]','demo'); + $this->click("//input[@value='Login']"); + $this->waitForTextPresent('Password cannot be blank.'); + $this->type('name=LoginForm[password]','demo'); + $this->clickAndWait("//input[@value='Login']"); + $this->assertTextNotPresent('Password cannot be blank.'); + $this->assertTextPresent('Logout'); + + // test logout process + $this->assertTextNotPresent('Login'); + $this->clickAndWait('link=Logout (demo)'); + $this->assertTextPresent('Login'); + } +} diff --git a/framework/cli/views/webapp/protected/tests/phpunit.xml b/framework/cli/views/webapp/protected/tests/phpunit.xml new file mode 100644 index 0000000..22c96ff --- /dev/null +++ b/framework/cli/views/webapp/protected/tests/phpunit.xml @@ -0,0 +1,13 @@ +<phpunit bootstrap="bootstrap.php" + colors="false" + convertErrorsToExceptions="true" + convertNoticesToExceptions="true" + convertWarningsToExceptions="true" + stopOnFailure="false"> + + <selenium> + <browser name="Internet Explorer" browser="*iexplore" /> + <browser name="Firefox" browser="*firefox" /> + </selenium> + +</phpunit>
\ No newline at end of file diff --git a/framework/cli/views/webapp/protected/tests/report/.yii b/framework/cli/views/webapp/protected/tests/report/.yii new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/framework/cli/views/webapp/protected/tests/report/.yii diff --git a/framework/cli/views/webapp/protected/tests/unit/.yii b/framework/cli/views/webapp/protected/tests/unit/.yii new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/framework/cli/views/webapp/protected/tests/unit/.yii diff --git a/framework/cli/views/webapp/protected/views/layouts/column1.php b/framework/cli/views/webapp/protected/views/layouts/column1.php new file mode 100644 index 0000000..f70b154 --- /dev/null +++ b/framework/cli/views/webapp/protected/views/layouts/column1.php @@ -0,0 +1,5 @@ +<?php $this->beginContent('//layouts/main'); ?> +<div id="content"> + <?php echo $content; ?> +</div><!-- content --> +<?php $this->endContent(); ?>
\ No newline at end of file diff --git a/framework/cli/views/webapp/protected/views/layouts/column2.php b/framework/cli/views/webapp/protected/views/layouts/column2.php new file mode 100644 index 0000000..e435a69 --- /dev/null +++ b/framework/cli/views/webapp/protected/views/layouts/column2.php @@ -0,0 +1,21 @@ +<?php $this->beginContent('//layouts/main'); ?> +<div class="span-19"> + <div id="content"> + <?php echo $content; ?> + </div><!-- content --> +</div> +<div class="span-5 last"> + <div id="sidebar"> + <?php + $this->beginWidget('zii.widgets.CPortlet', array( + 'title'=>'Operations', + )); + $this->widget('zii.widgets.CMenu', array( + 'items'=>$this->menu, + 'htmlOptions'=>array('class'=>'operations'), + )); + $this->endWidget(); + ?> + </div><!-- sidebar --> +</div> +<?php $this->endContent(); ?>
\ No newline at end of file diff --git a/framework/cli/views/webapp/protected/views/layouts/main.php b/framework/cli/views/webapp/protected/views/layouts/main.php new file mode 100644 index 0000000..f2ff75a --- /dev/null +++ b/framework/cli/views/webapp/protected/views/layouts/main.php @@ -0,0 +1,58 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> + <meta name="language" content="en" /> + + <!-- blueprint CSS framework --> + <link rel="stylesheet" type="text/css" href="<?php echo Yii::app()->request->baseUrl; ?>/css/screen.css" media="screen, projection" /> + <link rel="stylesheet" type="text/css" href="<?php echo Yii::app()->request->baseUrl; ?>/css/print.css" media="print" /> + <!--[if lt IE 8]> + <link rel="stylesheet" type="text/css" href="<?php echo Yii::app()->request->baseUrl; ?>/css/ie.css" media="screen, projection" /> + <![endif]--> + + <link rel="stylesheet" type="text/css" href="<?php echo Yii::app()->request->baseUrl; ?>/css/main.css" /> + <link rel="stylesheet" type="text/css" href="<?php echo Yii::app()->request->baseUrl; ?>/css/form.css" /> + + <title><?php echo CHtml::encode($this->pageTitle); ?></title> +</head> + +<body> + +<div class="container" id="page"> + + <div id="header"> + <div id="logo"><?php echo CHtml::encode(Yii::app()->name); ?></div> + </div><!-- header --> + + <div id="mainmenu"> + <?php $this->widget('zii.widgets.CMenu',array( + 'items'=>array( + array('label'=>'Home', 'url'=>array('/site/index')), + array('label'=>'About', 'url'=>array('/site/page', 'view'=>'about')), + array('label'=>'Contact', 'url'=>array('/site/contact')), + array('label'=>'Login', 'url'=>array('/site/login'), 'visible'=>Yii::app()->user->isGuest), + array('label'=>'Logout ('.Yii::app()->user->name.')', 'url'=>array('/site/logout'), 'visible'=>!Yii::app()->user->isGuest) + ), + )); ?> + </div><!-- mainmenu --> + <?php if(isset($this->breadcrumbs)):?> + <?php $this->widget('zii.widgets.CBreadcrumbs', array( + 'links'=>$this->breadcrumbs, + )); ?><!-- breadcrumbs --> + <?php endif?> + + <?php echo $content; ?> + + <div class="clear"></div> + + <div id="footer"> + Copyright © <?php echo date('Y'); ?> by My Company.<br/> + All Rights Reserved.<br/> + <?php echo Yii::powered(); ?> + </div><!-- footer --> + +</div><!-- page --> + +</body> +</html> diff --git a/framework/cli/views/webapp/protected/views/site/contact.php b/framework/cli/views/webapp/protected/views/site/contact.php new file mode 100644 index 0000000..51f90a9 --- /dev/null +++ b/framework/cli/views/webapp/protected/views/site/contact.php @@ -0,0 +1,81 @@ +<?php +$this->pageTitle=Yii::app()->name . ' - Contact Us'; +$this->breadcrumbs=array( + 'Contact', +); +?> + +<h1>Contact Us</h1> + +<?php if(Yii::app()->user->hasFlash('contact')): ?> + +<div class="flash-success"> + <?php echo Yii::app()->user->getFlash('contact'); ?> +</div> + +<?php else: ?> + +<p> +If you have business inquiries or other questions, please fill out the following form to contact us. Thank you. +</p> + +<div class="form"> + +<?php $form=$this->beginWidget('CActiveForm', array( + 'id'=>'contact-form', + 'enableClientValidation'=>true, + 'clientOptions'=>array( + 'validateOnSubmit'=>true, + ), +)); ?> + + <p class="note">Fields with <span class="required">*</span> are required.</p> + + <?php echo $form->errorSummary($model); ?> + + <div class="row"> + <?php echo $form->labelEx($model,'name'); ?> + <?php echo $form->textField($model,'name'); ?> + <?php echo $form->error($model,'name'); ?> + </div> + + <div class="row"> + <?php echo $form->labelEx($model,'email'); ?> + <?php echo $form->textField($model,'email'); ?> + <?php echo $form->error($model,'email'); ?> + </div> + + <div class="row"> + <?php echo $form->labelEx($model,'subject'); ?> + <?php echo $form->textField($model,'subject',array('size'=>60,'maxlength'=>128)); ?> + <?php echo $form->error($model,'subject'); ?> + </div> + + <div class="row"> + <?php echo $form->labelEx($model,'body'); ?> + <?php echo $form->textArea($model,'body',array('rows'=>6, 'cols'=>50)); ?> + <?php echo $form->error($model,'body'); ?> + </div> + + <?php if(CCaptcha::checkRequirements()): ?> + <div class="row"> + <?php echo $form->labelEx($model,'verifyCode'); ?> + <div> + <?php $this->widget('CCaptcha'); ?> + <?php echo $form->textField($model,'verifyCode'); ?> + </div> + <div class="hint">Please enter the letters as they are shown in the image above. + <br/>Letters are not case-sensitive.</div> + <?php echo $form->error($model,'verifyCode'); ?> + </div> + <?php endif; ?> + + <div class="row buttons"> + <?php echo CHtml::submitButton('Submit'); ?> + </div> + +<?php $this->endWidget(); ?> + +</div><!-- form --> + +<?php endif; ?>
\ No newline at end of file diff --git a/framework/cli/views/webapp/protected/views/site/error.php b/framework/cli/views/webapp/protected/views/site/error.php new file mode 100644 index 0000000..4607ff3 --- /dev/null +++ b/framework/cli/views/webapp/protected/views/site/error.php @@ -0,0 +1,12 @@ +<?php +$this->pageTitle=Yii::app()->name . ' - Error'; +$this->breadcrumbs=array( + 'Error', +); +?> + +<h2>Error <?php echo $code; ?></h2> + +<div class="error"> +<?php echo CHtml::encode($message); ?> +</div>
\ No newline at end of file diff --git a/framework/cli/views/webapp/protected/views/site/index.php b/framework/cli/views/webapp/protected/views/site/index.php new file mode 100644 index 0000000..b44b2e6 --- /dev/null +++ b/framework/cli/views/webapp/protected/views/site/index.php @@ -0,0 +1,16 @@ +<?php $this->pageTitle=Yii::app()->name; ?> + +<h1>Welcome to <i><?php echo CHtml::encode(Yii::app()->name); ?></i></h1> + +<p>Congratulations! You have successfully created your Yii application.</p> + +<p>You may change the content of this page by modifying the following two files:</p> +<ul> + <li>View file: <tt><?php echo __FILE__; ?></tt></li> + <li>Layout file: <tt><?php echo $this->getLayoutFile('main'); ?></tt></li> +</ul> + +<p>For more details on how to further develop this application, please read +the <a href="http://www.yiiframework.com/doc/">documentation</a>. +Feel free to ask in the <a href="http://www.yiiframework.com/forum/">forum</a>, +should you have any questions.</p>
\ No newline at end of file diff --git a/framework/cli/views/webapp/protected/views/site/login.php b/framework/cli/views/webapp/protected/views/site/login.php new file mode 100644 index 0000000..c53bf62 --- /dev/null +++ b/framework/cli/views/webapp/protected/views/site/login.php @@ -0,0 +1,49 @@ +<?php +$this->pageTitle=Yii::app()->name . ' - Login'; +$this->breadcrumbs=array( + 'Login', +); +?> + +<h1>Login</h1> + +<p>Please fill out the following form with your login credentials:</p> + +<div class="form"> +<?php $form=$this->beginWidget('CActiveForm', array( + 'id'=>'login-form', + 'enableClientValidation'=>true, + 'clientOptions'=>array( + 'validateOnSubmit'=>true, + ), +)); ?> + + <p class="note">Fields with <span class="required">*</span> are required.</p> + + <div class="row"> + <?php echo $form->labelEx($model,'username'); ?> + <?php echo $form->textField($model,'username'); ?> + <?php echo $form->error($model,'username'); ?> + </div> + + <div class="row"> + <?php echo $form->labelEx($model,'password'); ?> + <?php echo $form->passwordField($model,'password'); ?> + <?php echo $form->error($model,'password'); ?> + <p class="hint"> + Hint: You may login with <tt>demo/demo</tt> or <tt>admin/admin</tt>. + </p> + </div> + + <div class="row rememberMe"> + <?php echo $form->checkBox($model,'rememberMe'); ?> + <?php echo $form->label($model,'rememberMe'); ?> + <?php echo $form->error($model,'rememberMe'); ?> + </div> + + <div class="row buttons"> + <?php echo CHtml::submitButton('Login'); ?> + </div> + +<?php $this->endWidget(); ?> +</div><!-- form --> diff --git a/framework/cli/views/webapp/protected/views/site/pages/about.php b/framework/cli/views/webapp/protected/views/site/pages/about.php new file mode 100644 index 0000000..c6c05dc --- /dev/null +++ b/framework/cli/views/webapp/protected/views/site/pages/about.php @@ -0,0 +1,10 @@ +<?php +$this->pageTitle=Yii::app()->name . ' - About'; +$this->breadcrumbs=array( + 'About', +); +?> +<h1>About</h1> + +<p>This is a "static" page. You may change the content of this page +by updating the file <tt><?php echo __FILE__; ?></tt>.</p>
\ No newline at end of file diff --git a/framework/cli/views/webapp/protected/yiic b/framework/cli/views/webapp/protected/yiic new file mode 100644 index 0000000..7f56f54 --- /dev/null +++ b/framework/cli/views/webapp/protected/yiic @@ -0,0 +1,4 @@ +#!/usr/bin/env php +<?php + +require_once(dirname(__FILE__).'/yiic.php'); diff --git a/framework/cli/views/webapp/protected/yiic.bat b/framework/cli/views/webapp/protected/yiic.bat new file mode 100644 index 0000000..e8e4eda --- /dev/null +++ b/framework/cli/views/webapp/protected/yiic.bat @@ -0,0 +1,16 @@ +@echo off + +rem ------------------------------------------------------------- +rem Yii command line script for Windows. +rem This is the bootstrap script for running yiic on Windows. +rem ------------------------------------------------------------- + +@setlocal + +set BIN_PATH=%~dp0 + +if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe + +"%PHP_COMMAND%" "%BIN_PATH%yiic.php" %* + +@endlocal
\ No newline at end of file diff --git a/framework/cli/views/webapp/protected/yiic.php b/framework/cli/views/webapp/protected/yiic.php new file mode 100644 index 0000000..564d2f6 --- /dev/null +++ b/framework/cli/views/webapp/protected/yiic.php @@ -0,0 +1,7 @@ +<?php + +// change the following paths if necessary +$yiic=dirname(__FILE__).'/../../framework/yiic.php'; +$config=dirname(__FILE__).'/config/console.php'; + +require_once($yiic); diff --git a/framework/cli/views/webapp/themes/classic/views/.htaccess b/framework/cli/views/webapp/themes/classic/views/.htaccess new file mode 100644 index 0000000..8d2f256 --- /dev/null +++ b/framework/cli/views/webapp/themes/classic/views/.htaccess @@ -0,0 +1 @@ +deny from all diff --git a/framework/cli/views/webapp/themes/classic/views/layouts/.yii b/framework/cli/views/webapp/themes/classic/views/layouts/.yii new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/framework/cli/views/webapp/themes/classic/views/layouts/.yii diff --git a/framework/cli/views/webapp/themes/classic/views/site/.yii b/framework/cli/views/webapp/themes/classic/views/site/.yii new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/framework/cli/views/webapp/themes/classic/views/site/.yii diff --git a/framework/cli/views/webapp/themes/classic/views/system/.yii b/framework/cli/views/webapp/themes/classic/views/system/.yii new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/framework/cli/views/webapp/themes/classic/views/system/.yii |
