summaryrefslogtreecommitdiff
path: root/framework/gii
diff options
context:
space:
mode:
Diffstat (limited to 'framework/gii')
-rw-r--r--framework/gii/CCodeFile.php135
-rw-r--r--framework/gii/CCodeForm.php60
-rw-r--r--framework/gii/CCodeGenerator.php166
-rw-r--r--framework/gii/CCodeModel.php482
-rw-r--r--framework/gii/GiiModule.php239
-rw-r--r--framework/gii/assets/css/ie.css35
-rw-r--r--framework/gii/assets/css/main.css529
-rw-r--r--framework/gii/assets/css/print.css29
-rw-r--r--framework/gii/assets/css/screen.css235
-rw-r--r--framework/gii/assets/images/logo.pngbin0 -> 7207 bytes
-rw-r--r--framework/gii/assets/js/fancybox/blank.gifbin0 -> 43 bytes
-rw-r--r--framework/gii/assets/js/fancybox/fancy_close.pngbin0 -> 1075 bytes
-rw-r--r--framework/gii/assets/js/fancybox/fancy_loading.pngbin0 -> 8132 bytes
-rw-r--r--framework/gii/assets/js/fancybox/fancy_nav_left.pngbin0 -> 1029 bytes
-rw-r--r--framework/gii/assets/js/fancybox/fancy_nav_right.pngbin0 -> 1034 bytes
-rw-r--r--framework/gii/assets/js/fancybox/fancy_shadow_e.pngbin0 -> 107 bytes
-rw-r--r--framework/gii/assets/js/fancybox/fancy_shadow_n.pngbin0 -> 101 bytes
-rw-r--r--framework/gii/assets/js/fancybox/fancy_shadow_ne.pngbin0 -> 313 bytes
-rw-r--r--framework/gii/assets/js/fancybox/fancy_shadow_nw.pngbin0 -> 310 bytes
-rw-r--r--framework/gii/assets/js/fancybox/fancy_shadow_s.pngbin0 -> 107 bytes
-rw-r--r--framework/gii/assets/js/fancybox/fancy_shadow_se.pngbin0 -> 330 bytes
-rw-r--r--framework/gii/assets/js/fancybox/fancy_shadow_sw.pngbin0 -> 317 bytes
-rw-r--r--framework/gii/assets/js/fancybox/fancy_shadow_w.pngbin0 -> 99 bytes
-rw-r--r--framework/gii/assets/js/fancybox/fancy_title_left.pngbin0 -> 496 bytes
-rw-r--r--framework/gii/assets/js/fancybox/fancy_title_main.pngbin0 -> 96 bytes
-rw-r--r--framework/gii/assets/js/fancybox/fancy_title_over.pngbin0 -> 68 bytes
-rw-r--r--framework/gii/assets/js/fancybox/fancy_title_right.pngbin0 -> 495 bytes
-rw-r--r--framework/gii/assets/js/fancybox/fancybox-x.pngbin0 -> 159 bytes
-rw-r--r--framework/gii/assets/js/fancybox/fancybox-y.pngbin0 -> 128 bytes
-rw-r--r--framework/gii/assets/js/fancybox/fancybox.pngbin0 -> 14353 bytes
-rw-r--r--framework/gii/assets/js/fancybox/jquery.fancybox-1.3.1.css363
-rw-r--r--framework/gii/assets/js/fancybox/jquery.fancybox-1.3.1.pack.js44
-rw-r--r--framework/gii/assets/js/jquery.tooltip-1.2.6.min.js11
-rw-r--r--framework/gii/assets/js/main.js79
-rw-r--r--framework/gii/components/Pear/Text/Diff.php453
-rw-r--r--framework/gii/components/Pear/Text/Diff/Engine/native.php438
-rw-r--r--framework/gii/components/Pear/Text/Diff/Engine/shell.php162
-rw-r--r--framework/gii/components/Pear/Text/Diff/Engine/string.php237
-rw-r--r--framework/gii/components/Pear/Text/Diff/Engine/xdiff.php63
-rw-r--r--framework/gii/components/Pear/Text/Diff/Mapped.php55
-rw-r--r--framework/gii/components/Pear/Text/Diff/Renderer.php237
-rw-r--r--framework/gii/components/Pear/Text/Diff/Renderer/context.php77
-rw-r--r--framework/gii/components/Pear/Text/Diff/Renderer/inline.php170
-rw-r--r--framework/gii/components/Pear/Text/Diff/Renderer/unified.php67
-rw-r--r--framework/gii/components/Pear/Text/Diff/ThreeWay.php276
-rw-r--r--framework/gii/components/Pear/Text/Diff3.php276
-rw-r--r--framework/gii/components/TextDiff.php22
-rw-r--r--framework/gii/components/UserIdentity.php20
-rw-r--r--framework/gii/controllers/DefaultController.php58
-rw-r--r--framework/gii/generators/controller/ControllerCode.php130
-rw-r--r--framework/gii/generators/controller/ControllerGenerator.php6
-rw-r--r--framework/gii/generators/controller/templates/default/controller.php45
-rw-r--r--framework/gii/generators/controller/templates/default/view.php33
-rw-r--r--framework/gii/generators/controller/views/index.php44
-rw-r--r--framework/gii/generators/crud/CrudCode.php248
-rw-r--r--framework/gii/generators/crud/CrudGenerator.php6
-rw-r--r--framework/gii/generators/crud/templates/default/_form.php39
-rw-r--r--framework/gii/generators/crud/templates/default/_search.php32
-rw-r--r--framework/gii/generators/crud/templates/default/_view.php26
-rw-r--r--framework/gii/generators/crud/templates/default/admin.php70
-rw-r--r--framework/gii/generators/crud/templates/default/controller.php183
-rw-r--r--framework/gii/generators/crud/templates/default/create.php24
-rw-r--r--framework/gii/generators/crud/templates/default/index.php26
-rw-r--r--framework/gii/generators/crud/templates/default/update.php28
-rw-r--r--framework/gii/generators/crud/templates/default/view.php36
-rw-r--r--framework/gii/generators/crud/views/index.php64
-rw-r--r--framework/gii/generators/form/FormCode.php94
-rw-r--r--framework/gii/generators/form/FormGenerator.php6
-rw-r--r--framework/gii/generators/form/templates/default/action.php33
-rw-r--r--framework/gii/generators/form/templates/default/form.php34
-rw-r--r--framework/gii/generators/form/views/index.php49
-rw-r--r--framework/gii/generators/model/ModelCode.php395
-rw-r--r--framework/gii/generators/model/ModelGenerator.php6
-rw-r--r--framework/gii/generators/model/templates/default/model.php145
-rw-r--r--framework/gii/generators/model/views/index.php115
-rw-r--r--framework/gii/generators/module/ModuleCode.php91
-rw-r--r--framework/gii/generators/module/ModuleGenerator.php6
-rw-r--r--framework/gii/generators/module/templates/default/components/.yii0
-rw-r--r--framework/gii/generators/module/templates/default/controllers/DefaultController.php9
-rw-r--r--framework/gii/generators/module/templates/default/messages/.yii0
-rw-r--r--framework/gii/generators/module/templates/default/models/.yii0
-rw-r--r--framework/gii/generators/module/templates/default/module.php28
-rw-r--r--framework/gii/generators/module/templates/default/views/default/index.php15
-rw-r--r--framework/gii/generators/module/templates/default/views/layouts/.yii0
-rw-r--r--framework/gii/generators/module/views/index.php19
-rw-r--r--framework/gii/models/LoginForm.php49
-rw-r--r--framework/gii/views/common/code.php16
-rw-r--r--framework/gii/views/common/diff.php9
-rw-r--r--framework/gii/views/common/generator.php76
-rw-r--r--framework/gii/views/default/error.php5
-rw-r--r--framework/gii/views/default/index.php11
-rw-r--r--framework/gii/views/default/login.php11
-rw-r--r--framework/gii/views/layouts/column1.php7
-rw-r--r--framework/gii/views/layouts/generator.php25
-rw-r--r--framework/gii/views/layouts/main.php57
95 files changed, 7369 insertions, 0 deletions
diff --git a/framework/gii/CCodeFile.php b/framework/gii/CCodeFile.php
new file mode 100644
index 0000000..c5989b1
--- /dev/null
+++ b/framework/gii/CCodeFile.php
@@ -0,0 +1,135 @@
+<?php
+/**
+ * CCodeFile class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+/**
+ * CCodeFile represents a code file being generated.
+ *
+ * @property string $relativePath The code file path relative to the application base path.
+ * @property string $type The code file extension (e.g. php, txt).
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: CCodeFile.php 3426 2011-10-25 00:01:09Z alexander.makarow $
+ * @package system.gii
+ * @since 1.1.2
+ */
+class CCodeFile extends CComponent
+{
+ const OP_NEW='new';
+ const OP_OVERWRITE='overwrite';
+ const OP_SKIP='skip';
+
+ /**
+ * @var string the file path that the new code should be saved to.
+ */
+ public $path;
+ /**
+ * @var mixed the newly generated code. If this is null, it means {@link path}
+ * should be treated as a directory.
+ */
+ public $content;
+ /**
+ * @var string the operation to be performed
+ */
+ public $operation;
+ /**
+ * @var string the error occurred when saving the code into a file
+ */
+ public $error;
+
+ /**
+ * Constructor.
+ * @param string $path the file path that the new code should be saved to.
+ * @param string $content the newly generated code
+ */
+ public function __construct($path,$content)
+ {
+ $this->path=strtr($path,array('/'=>DIRECTORY_SEPARATOR,'\\'=>DIRECTORY_SEPARATOR));
+ $this->content=$content;
+ if(is_file($path))
+ $this->operation=file_get_contents($path)===$content ? self::OP_SKIP : self::OP_OVERWRITE;
+ else if($content===null) // is dir
+ $this->operation=is_dir($path) ? self::OP_SKIP : self::OP_NEW;
+ else
+ $this->operation=self::OP_NEW;
+ }
+
+ /**
+ * Saves the code into the file {@link path}.
+ */
+ public function save()
+ {
+ $module=Yii::app()->controller->module;
+ if($this->content===null) // a directory
+ {
+ if(!is_dir($this->path))
+ {
+ $oldmask=@umask(0);
+ $result=@mkdir($this->path,$module->newDirMode,true);
+ @umask($oldmask);
+ if(!$result)
+ {
+ $this->error="Unable to create the directory '{$this->path}'.";
+ return false;
+ }
+ }
+ return true;
+ }
+
+ if($this->operation===self::OP_NEW)
+ {
+ $dir=dirname($this->path);
+ if(!is_dir($dir))
+ {
+ $oldmask=@umask(0);
+ $result=@mkdir($dir,$module->newDirMode,true);
+ @umask($oldmask);
+ if(!$result)
+ {
+ $this->error="Unable to create the directory '$dir'.";
+ return false;
+ }
+ }
+ }
+ if(@file_put_contents($this->path,$this->content)===false)
+ {
+ $this->error="Unable to write the file '{$this->path}'.";
+ return false;
+ }
+ else
+ {
+ $oldmask=@umask(0);
+ @chmod($this->path,$module->newFileMode);
+ @umask($oldmask);
+ }
+ return true;
+ }
+
+ /**
+ * @return string the code file path relative to the application base path.
+ */
+ public function getRelativePath()
+ {
+ if(strpos($this->path,Yii::app()->basePath)===0)
+ return substr($this->path,strlen(Yii::app()->basePath)+1);
+ else
+ return $this->path;
+ }
+
+ /**
+ * @return string the code file extension (e.g. php, txt)
+ */
+ public function getType()
+ {
+ if(($pos=strrpos($this->path,'.'))!==false)
+ return substr($this->path,$pos+1);
+ else
+ return 'unknown';
+ }
+} \ No newline at end of file
diff --git a/framework/gii/CCodeForm.php b/framework/gii/CCodeForm.php
new file mode 100644
index 0000000..3989172
--- /dev/null
+++ b/framework/gii/CCodeForm.php
@@ -0,0 +1,60 @@
+<?php
+/**
+ * CCodeForm class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+/**
+ * CCodeForm represents the form for collecting code generation parameters.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: CCodeForm.php 2799 2011-01-01 19:31:13Z qiang.xue $
+ * @package system.gii
+ * @since 1.1.2
+ */
+class CCodeForm extends CActiveForm
+{
+ /**
+ * @var CCodeModel the code model associated with the form
+ */
+ public $model;
+
+ /**
+ * Initializes the widget.
+ * This renders the form open tag.
+ */
+ public function init()
+ {
+ echo <<<EOD
+<div class="form gii">
+ <p class="note">
+ Fields with <span class="required">*</span> are required.
+ Click on the <span class="sticky">highlighted fields</span> to edit them.
+ </p>
+EOD;
+ parent::init();
+ }
+
+ /**
+ * Runs the widget.
+ */
+ public function run()
+ {
+ $templates=array();
+ foreach($this->model->getTemplates() as $i=>$template)
+ $templates[$i]=basename($template).' ('.$template.')';
+
+ $this->renderFile(Yii::getPathOfAlias('gii.views.common.generator').'.php',array(
+ 'model'=>$this->model,
+ 'templates'=>$templates,
+ ));
+
+ parent::run();
+
+ echo "</div>";
+ }
+} \ No newline at end of file
diff --git a/framework/gii/CCodeGenerator.php b/framework/gii/CCodeGenerator.php
new file mode 100644
index 0000000..5930251
--- /dev/null
+++ b/framework/gii/CCodeGenerator.php
@@ -0,0 +1,166 @@
+<?php
+/**
+ * CCodeGenerator class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+/**
+ * CCodeGenerator is the base class for code generator classes.
+ *
+ * CCodeGenerator is a controller that predefines several actions for code generation purpose.
+ * Derived classes mainly need to configure the {@link codeModel} property
+ * override the {@link getSuccessMessage} method. The former specifies which
+ * code model (extending {@link CCodeModel}) that this generator should use,
+ * while the latter should return a success message to be displayed when
+ * code files are successfully generated.
+ *
+ * @property string $pageTitle The page title.
+ * @property string $viewPath The view path of the generator.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: CCodeGenerator.php 3426 2011-10-25 00:01:09Z alexander.makarow $
+ * @package system.gii
+ * @since 1.1.2
+ */
+class CCodeGenerator extends CController
+{
+ /**
+ * @var string the layout to be used by the generator. Defaults to 'generator'.
+ */
+ public $layout='generator';
+ /**
+ * @var array a list of available code templates (name=>path)
+ */
+ public $templates=array();
+ /**
+ * @var string the code model class. This can be either a class name (if it can be autoloaded)
+ * or a path alias referring to the class file.
+ * Child classes must configure this property with a concrete value.
+ */
+ public $codeModel;
+
+ private $_viewPath;
+
+ /**
+ * @return string the page title
+ */
+ public function getPageTitle()
+ {
+ return 'Gii - '.ucfirst($this->id).' Generator';
+ }
+
+ /**
+ * The code generation action.
+ * This is the action that displays the code generation interface.
+ * Child classes mainly need to provide the 'index' view for collecting user parameters
+ * for code generation.
+ */
+ public function actionIndex()
+ {
+ $model=$this->prepare();
+ if($model->files!=array() && isset($_POST['generate'], $_POST['answers']))
+ {
+ $model->answers=$_POST['answers'];
+ $model->status=$model->save() ? CCodeModel::STATUS_SUCCESS : CCodeModel::STATUS_ERROR;
+ }
+
+ $this->render('index',array(
+ 'model'=>$model,
+ ));
+ }
+
+ /**
+ * The code preview action.
+ * This action shows up the specified generated code.
+ */
+ public function actionCode()
+ {
+ $model=$this->prepare();
+ if(isset($_GET['id']) && isset($model->files[$_GET['id']]))
+ {
+ $this->renderPartial('/common/code', array(
+ 'file'=>$model->files[$_GET['id']],
+ ));
+ }
+ else
+ throw new CHttpException(404,'Unable to find the code you requested.');
+ }
+
+ /**
+ * The code diff action.
+ * This action shows up the difference between the newly generated code and the corresponding existing code.
+ */
+ public function actionDiff()
+ {
+ Yii::import('gii.components.TextDiff');
+
+ $model=$this->prepare();
+ if(isset($_GET['id']) && isset($model->files[$_GET['id']]))
+ {
+ $file=$model->files[$_GET['id']];
+ if(!in_array($file->type,array('php', 'txt','js','css')))
+ $diff=false;
+ else if($file->operation===CCodeFile::OP_OVERWRITE)
+ $diff=TextDiff::compare(file_get_contents($file->path), $file->content);
+ else
+ $diff='';
+
+ $this->renderPartial('/common/diff',array(
+ 'file'=>$file,
+ 'diff'=>$diff,
+ ));
+ }
+ else
+ throw new CHttpException(404,'Unable to find the code you requested.');
+ }
+
+ /**
+ * Returns the view path of the generator.
+ * The "views" directory under the directory containing the generator class file will be returned.
+ * @return string the view path of the generator
+ */
+ public function getViewPath()
+ {
+ if($this->_viewPath===null)
+ {
+ $class=new ReflectionClass(get_class($this));
+ $this->_viewPath=dirname($class->getFileName()).DIRECTORY_SEPARATOR.'views';
+ }
+ return $this->_viewPath;
+ }
+
+ /**
+ * @param string $value the view path of the generator.
+ */
+ public function setViewPath($value)
+ {
+ $this->_viewPath=$value;
+ }
+
+ /**
+ * Prepares the code model.
+ */
+ protected function prepare()
+ {
+ if($this->codeModel===null)
+ throw new CException(get_class($this).'.codeModel property must be specified.');
+ $modelClass=Yii::import($this->codeModel,true);
+ $model=new $modelClass;
+ $model->loadStickyAttributes();
+ if(isset($_POST[$modelClass]))
+ {
+ $model->attributes=$_POST[$modelClass];
+ $model->status=CCodeModel::STATUS_PREVIEW;
+ if($model->validate())
+ {
+ $model->saveStickyAttributes();
+ $model->prepare();
+ }
+ }
+ return $model;
+ }
+} \ No newline at end of file
diff --git a/framework/gii/CCodeModel.php b/framework/gii/CCodeModel.php
new file mode 100644
index 0000000..368c67b
--- /dev/null
+++ b/framework/gii/CCodeModel.php
@@ -0,0 +1,482 @@
+<?php
+/**
+ * CCodeModel class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+/**
+ * CCodeModel is the base class for model classes that are used to generate code.
+ *
+ * Each code generator should have at least one code model class that extends from this class.
+ * The purpose of a code model is to represent user-supplied parameters and use them to
+ * generate customized code.
+ *
+ * Derived classes should implement the {@link prepare} method whose main task is to
+ * fill up the {@link files} property based on the user parameters.
+ *
+ * The {@link files} property should be filled with a set of {@link CCodeFile} instances,
+ * each representing a single code file to be generated.
+ *
+ * CCodeModel implements the feature of "sticky attributes". A sticky attribute is an attribute
+ * that can remember its last valid value, even if the user closes his browser window
+ * and reopen it. To declare an attribute is sticky, simply list it in a validation rule with
+ * the validator name being "sticky".
+ *
+ * @property array $templates A list of available code templates (name=>directory).
+ * @property string $templatePath The directory that contains the template files.
+ * @property string $stickyFile The file path that stores the sticky attribute values.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: CCodeModel.php 3547 2012-01-24 10:07:28Z mdomba $
+ * @package system.gii
+ * @since 1.1.2
+ */
+abstract class CCodeModel extends CFormModel
+{
+ const STATUS_NEW=1;
+ const STATUS_PREVIEW=2;
+ const STATUS_SUCCESS=3;
+ const STATUS_ERROR=4;
+
+ static $keywords=array(
+ '__class__',
+ '__dir__',
+ '__file__',
+ '__function__',
+ '__line__',
+ '__method__',
+ '__namespace__',
+ 'abstract',
+ 'and',
+ 'array',
+ 'as',
+ 'break',
+ 'case',
+ 'catch',
+ 'cfunction',
+ 'class',
+ 'clone',
+ 'const',
+ 'continue',
+ 'declare',
+ 'default',
+ 'die',
+ 'do',
+ 'echo',
+ 'else',
+ 'elseif',
+ 'empty',
+ 'enddeclare',
+ 'endfor',
+ 'endforeach',
+ 'endif',
+ 'endswitch',
+ 'endwhile',
+ 'eval',
+ 'exception',
+ 'exit',
+ 'extends',
+ 'final',
+ 'final',
+ 'for',
+ 'foreach',
+ 'function',
+ 'global',
+ 'goto',
+ 'if',
+ 'implements',
+ 'include',
+ 'include_once',
+ 'instanceof',
+ 'interface',
+ 'isset',
+ 'list',
+ 'namespace',
+ 'new',
+ 'old_function',
+ 'or',
+ 'parent',
+ 'php_user_filter',
+ 'print',
+ 'private',
+ 'protected',
+ 'public',
+ 'require',
+ 'require_once',
+ 'return',
+ 'static',
+ 'switch',
+ 'this',
+ 'throw',
+ 'try',
+ 'unset',
+ 'use',
+ 'var',
+ 'while',
+ 'xor',
+ );
+
+ /**
+ * @var array user confirmations on whether to overwrite existing code files with the newly generated ones.
+ * The value of this property is internally managed by this class and {@link CCodeGenerator}.
+ */
+ public $answers;
+ /**
+ * @var string the name of the code template that the user has selected.
+ * The value of this property is internally managed by this class and {@link CCodeGenerator}.
+ */
+ public $template;
+ /**
+ * @var array a list of {@link CCodeFile} objects that represent the code files to be generated.
+ * The {@link prepare()} method is responsible to populate this property.
+ */
+ public $files=array();
+ /**
+ * @var integer the status of this model. T
+ * The value of this property is internally managed by {@link CCodeGenerator}.
+ */
+ public $status=self::STATUS_NEW;
+
+ private $_stickyAttributes=array();
+
+ /**
+ * Prepares the code files to be generated.
+ * This is the main method that child classes should implement. It should contain the logic
+ * that populates the {@link files} property with a list of code files to be generated.
+ */
+ abstract public function prepare();
+
+ /**
+ * Declares the model validation rules.
+ * Child classes must override this method in the following format:
+ * <pre>
+ * return array_merge(parent::rules(), array(
+ * ...rules for the child class...
+ * ));
+ * </pre>
+ * @return array validation rules
+ */
+ public function rules()
+ {
+ return array(
+ array('template', 'required'),
+ array('template', 'validateTemplate', 'skipOnError'=>true),
+ array('template', 'sticky'),
+ );
+ }
+
+ /**
+ * Validates the template selection.
+ * This method validates whether the user selects an existing template
+ * and the template contains all required template files as specified in {@link requiredTemplates}.
+ * @param string $attribute the attribute to be validated
+ * @param array $params validation parameters
+ */
+ public function validateTemplate($attribute,$params)
+ {
+ $templates=$this->templates;
+ if(!isset($templates[$this->template]))
+ $this->addError('template', 'Invalid template selection.');
+ else
+ {
+ $templatePath=$this->templatePath;
+ foreach($this->requiredTemplates() as $template)
+ {
+ if(!is_file($templatePath.'/'.$template))
+ $this->addError('template', "Unable to find the required code template file '$template'.");
+ }
+ }
+ }
+
+ /**
+ * Checks if the named class exists (in a case sensitive manner).
+ * @param string $name class name to be checked
+ * @return boolean whether the class exists
+ */
+ public function classExists($name)
+ {
+ return class_exists($name,false) && in_array($name, get_declared_classes());
+ }
+
+ /**
+ * Declares the model attribute labels.
+ * Child classes must override this method in the following format:
+ * <pre>
+ * return array_merge(parent::attributeLabels(), array(
+ * ...labels for the child class attributes...
+ * ));
+ * </pre>
+ * @return array the attribute labels
+ */
+ public function attributeLabels()
+ {
+ return array(
+ 'template'=>'Code Template',
+ );
+ }
+
+ /**
+ * Returns a list of code templates that are required.
+ * Derived classes usually should override this method.
+ * @return array list of code templates that are required. They should be file paths
+ * relative to {@link templatePath}.
+ */
+ public function requiredTemplates()
+ {
+ return array();
+ }
+
+ /**
+ * Saves the generated code into files.
+ */
+ public function save()
+ {
+ $result=true;
+ foreach($this->files as $file)
+ {
+ if($this->confirmed($file))
+ $result=$file->save() && $result;
+ }
+ return $result;
+ }
+
+ /**
+ * Returns the message to be displayed when the newly generated code is saved successfully.
+ * Child classes should override this method if the message needs to be customized.
+ * @return string the message to be displayed when the newly generated code is saved successfully.
+ */
+ public function successMessage()
+ {
+ return 'The code has been generated successfully.';
+ }
+
+ /**
+ * Returns the message to be displayed when some error occurred during code file saving.
+ * Child classes should override this method if the message needs to be customized.
+ * @return string the message to be displayed when some error occurred during code file saving.
+ */
+ public function errorMessage()
+ {
+ return 'There was some error when generating the code. Please check the following messages.';
+ }
+
+ /**
+ * Returns a list of available code templates (name=>directory).
+ * This method simply returns the {@link CCodeGenerator::templates} property value.
+ * @return array a list of available code templates (name=>directory).
+ */
+ public function getTemplates()
+ {
+ return Yii::app()->controller->templates;
+ }
+
+ /**
+ * @return string the directory that contains the template files.
+ * @throw CException if {@link templates} is empty or template selection is invalid
+ */
+ public function getTemplatePath()
+ {
+ $templates=$this->getTemplates();
+ if(isset($templates[$this->template]))
+ return $templates[$this->template];
+ else if(empty($templates))
+ throw new CHttpException(500,'No templates are available.');
+ else
+ throw new CHttpException(500,'Invalid template selection.');
+
+ }
+
+ /**
+ * @param CCodeFile $file whether the code file should be saved
+ */
+ public function confirmed($file)
+ {
+ return $this->answers===null && $file->operation===CCodeFile::OP_NEW
+ || is_array($this->answers) && isset($this->answers[md5($file->path)]);
+ }
+
+ /**
+ * Generates the code using the specified code template file.
+ * This method is manly used in {@link generate} to generate code.
+ * @param string $templateFile the code template file path
+ * @param array $_params_ a set of parameters to be extracted and made available in the code template
+ * @return string the generated code
+ */
+ public function render($templateFile,$_params_=null)
+ {
+ if(!is_file($templateFile))
+ throw new CException("The template file '$templateFile' does not exist.");
+
+ if(is_array($_params_))
+ extract($_params_,EXTR_PREFIX_SAME,'params');
+ else
+ $params=$_params_;
+ ob_start();
+ ob_implicit_flush(false);
+ require($templateFile);
+ return ob_get_clean();
+ }
+
+ /**
+ * @return string the code generation result log.
+ */
+ public function renderResults()
+ {
+ $output='Generating code using template "'.$this->templatePath."\"...\n";
+ foreach($this->files as $file)
+ {
+ if($file->error!==null)
+ $output.="<span class=\"error\">generating {$file->relativePath}<br/> {$file->error}</span>\n";
+ else if($file->operation===CCodeFile::OP_NEW && $this->confirmed($file))
+ $output.=' generated '.$file->relativePath."\n";
+ else if($file->operation===CCodeFile::OP_OVERWRITE && $this->confirmed($file))
+ $output.=' overwrote '.$file->relativePath."\n";
+ else
+ $output.=' skipped '.$file->relativePath."\n";
+ }
+ $output.="done!\n";
+ return $output;
+ }
+
+ /**
+ * The "sticky" validator.
+ * This validator does not really validate the attributes.
+ * It actually saves the attribute value in a file to make it sticky.
+ * @param string $attribute the attribute to be validated
+ * @param array $params the validation parameters
+ */
+ public function sticky($attribute,$params)
+ {
+ if(!$this->hasErrors())
+ $this->_stickyAttributes[$attribute]=$this->$attribute;
+ }
+
+ /**
+ * Loads sticky attributes from a file and populates them into the model.
+ */
+ public function loadStickyAttributes()
+ {
+ $this->_stickyAttributes=array();
+ $path=$this->getStickyFile();
+ if(is_file($path))
+ {
+ $result=@include($path);
+ if(is_array($result))
+ {
+ $this->_stickyAttributes=$result;
+ foreach($this->_stickyAttributes as $name=>$value)
+ {
+ if(property_exists($this,$name) || $this->canSetProperty($name))
+ $this->$name=$value;
+ }
+ }
+ }
+ }
+
+ /**
+ * Saves sticky attributes into a file.
+ */
+ public function saveStickyAttributes()
+ {
+ $path=$this->getStickyFile();
+ @mkdir(dirname($path),0755,true);
+ file_put_contents($path,"<?php\nreturn ".var_export($this->_stickyAttributes,true).";\n");
+ }
+
+ /**
+ * @return string the file path that stores the sticky attribute values.
+ */
+ public function getStickyFile()
+ {
+ return Yii::app()->runtimePath.'/gii-'.Yii::getVersion().'/'.get_class($this).'.php';
+ }
+
+ /**
+ * Converts a word to its plural form.
+ * Note that this is for English only!
+ * For example, 'apple' will become 'apples', and 'child' will become 'children'.
+ * @param string $name the word to be pluralized
+ * @return string the pluralized word
+ */
+ public function pluralize($name)
+ {
+ $rules=array(
+ '/move$/i' => 'moves',
+ '/foot$/i' => 'feet',
+ '/child$/i' => 'children',
+ '/human$/i' => 'humans',
+ '/man$/i' => 'men',
+ '/tooth$/i' => 'teeth',
+ '/person$/i' => 'people',
+ '/([m|l])ouse$/i' => '\1ice',
+ '/(x|ch|ss|sh|us|as|is|os)$/i' => '\1es',
+ '/([^aeiouy]|qu)y$/i' => '\1ies',
+ '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves',
+ '/(shea|lea|loa|thie)f$/i' => '\1ves',
+ '/([ti])um$/i' => '\1a',
+ '/(tomat|potat|ech|her|vet)o$/i' => '\1oes',
+ '/(bu)s$/i' => '\1ses',
+ '/(ax|test)is$/i' => '\1es',
+ '/s$/' => 's',
+ );
+ foreach($rules as $rule=>$replacement)
+ {
+ if(preg_match($rule,$name))
+ return preg_replace($rule,$replacement,$name);
+ }
+ return $name.'s';
+ }
+
+ /**
+ * Converts a class name into a HTML ID.
+ * For example, 'PostTag' will be converted as 'post-tag'.
+ * @param string $name the string to be converted
+ * @return string the resulting ID
+ */
+ public function class2id($name)
+ {
+ return trim(strtolower(str_replace('_','-',preg_replace('/(?<![A-Z])[A-Z]/', '-\0', $name))),'-');
+ }
+
+ /**
+ * Converts a class name into space-separated words.
+ * For example, 'PostTag' will be converted as 'Post Tag'.
+ * @param string $name the string to be converted
+ * @param boolean $ucwords whether to capitalize the first letter in each word
+ * @return string the resulting words
+ */
+ public function class2name($name,$ucwords=true)
+ {
+ $result=trim(strtolower(str_replace('_',' ',preg_replace('/(?<![A-Z])[A-Z]/', ' \0', $name))));
+ return $ucwords ? ucwords($result) : $result;
+ }
+
+ /**
+ * Converts a class name into a variable name with the first letter in lower case.
+ * This method is provided because lcfirst() PHP function is only available for PHP 5.3+.
+ * @param string $name the class name
+ * @return string the variable name converted from the class name
+ * @since 1.1.4
+ */
+ public function class2var($name)
+ {
+ $name[0]=strtolower($name[0]);
+ return $name;
+ }
+
+ /**
+ * Validates an attribute to make sure it is not taking a PHP reserved keyword.
+ * @param string $attribute the attribute to be validated
+ * @param array $params validation parameters
+ */
+ public function validateReservedWord($attribute,$params)
+ {
+ $value=$this->$attribute;
+ if(in_array(strtolower($value),self::$keywords))
+ $this->addError($attribute, $this->getAttributeLabel($attribute).' cannot take a reserved PHP keyword.');
+ }
+} \ No newline at end of file
diff --git a/framework/gii/GiiModule.php b/framework/gii/GiiModule.php
new file mode 100644
index 0000000..ebe75c0
--- /dev/null
+++ b/framework/gii/GiiModule.php
@@ -0,0 +1,239 @@
+<?php
+/**
+ * GiiModule class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+Yii::import('system.gii.CCodeGenerator');
+Yii::import('system.gii.CCodeModel');
+Yii::import('system.gii.CCodeFile');
+Yii::import('system.gii.CCodeForm');
+
+/**
+ * GiiModule is a module that provides Web-based code generation capabilities.
+ *
+ * To use GiiModule, you must include it as a module in the application configuration like the following:
+ * <pre>
+ * return array(
+ * ......
+ * 'modules'=>array(
+ * 'gii'=>array(
+ * 'class'=>'system.gii.GiiModule',
+ * 'password'=>***choose a password***
+ * ),
+ * ),
+ * )
+ * </pre>
+ *
+ * Because GiiModule generates new code files on the server, you should only use it on your own
+ * development machine. To prevent other people from using this module, it is required that
+ * you specify a secret password in the configuration. Later when you access
+ * the module via browser, you will be prompted to enter the correct password.
+ *
+ * By default, GiiModule can only be accessed by localhost. You may configure its {@link ipFilters}
+ * property if you want to make it accessible on other machines.
+ *
+ * With the above configuration, you will be able to access GiiModule in your browser using
+ * the following URL:
+ *
+ * http://localhost/path/to/index.php?r=gii
+ *
+ * If your application is using path-format URLs with some customized URL rules, you may need to add
+ * the following URLs in your application configuration in order to access GiiModule:
+ * <pre>
+ * 'components'=>array(
+ * 'urlManager'=>array(
+ * 'urlFormat'=>'path',
+ * 'rules'=>array(
+ * 'gii'=>'gii',
+ * 'gii/<controller:\w+>'=>'gii/<controller>',
+ * 'gii/<controller:\w+>/<action:\w+>'=>'gii/<controller>/<action>',
+ * ...other rules...
+ * ),
+ * )
+ * )
+ * </pre>
+ *
+ * You can then access GiiModule via:
+ *
+ * http://localhost/path/to/index.php/gii
+ *
+ * @property string $assetsUrl The base URL that contains all published asset files of gii.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: GiiModule.php 3426 2011-10-25 00:01:09Z alexander.makarow $
+ * @package system.gii
+ * @since 1.1.2
+ */
+class GiiModule extends CWebModule
+{
+ /**
+ * @var string the password that can be used to access GiiModule.
+ * If this property is set false, then GiiModule can be accessed without password
+ * (DO NOT DO THIS UNLESS YOU KNOW THE CONSEQUENCE!!!)
+ */
+ public $password;
+ /**
+ * @var array the IP filters that specify which IP addresses are allowed to access GiiModule.
+ * Each array element represents a single filter. A filter can be either an IP address
+ * or an address with wildcard (e.g. 192.168.0.*) to represent a network segment.
+ * If you want to allow all IPs to access gii, you may set this property to be false
+ * (DO NOT DO THIS UNLESS YOU KNOW THE CONSEQUENCE!!!)
+ * The default value is array('127.0.0.1', '::1'), which means GiiModule can only be accessed
+ * on the localhost.
+ */
+ public $ipFilters=array('127.0.0.1','::1');
+ /**
+ * @var array a list of path aliases that refer to the directories containing code generators.
+ * The directory referred by a single path alias may contain multiple code generators, each stored
+ * under a sub-directory whose name is the generator name.
+ * Defaults to array('application.gii').
+ */
+ public $generatorPaths=array('application.gii');
+ /**
+ * @var integer the permission to be set for newly generated code files.
+ * This value will be used by PHP chmod function.
+ * Defaults to 0666, meaning the file is read-writable by all users.
+ */
+ public $newFileMode=0666;
+ /**
+ * @var integer the permission to be set for newly generated directories.
+ * This value will be used by PHP chmod function.
+ * Defaults to 0777, meaning the directory can be read, written and executed by all users.
+ */
+ public $newDirMode=0777;
+
+ private $_assetsUrl;
+
+ /**
+ * Initializes the gii module.
+ */
+ public function init()
+ {
+ parent::init();
+ Yii::app()->setComponents(array(
+ 'errorHandler'=>array(
+ 'class'=>'CErrorHandler',
+ 'errorAction'=>$this->getId().'/default/error',
+ ),
+ 'user'=>array(
+ 'class'=>'CWebUser',
+ 'stateKeyPrefix'=>'gii',
+ 'loginUrl'=>Yii::app()->createUrl($this->getId().'/default/login'),
+ ),
+ ), false);
+ $this->generatorPaths[]='gii.generators';
+ $this->controllerMap=$this->findGenerators();
+ }
+
+ /**
+ * @return string the base URL that contains all published asset files of gii.
+ */
+ public function getAssetsUrl()
+ {
+ if($this->_assetsUrl===null)
+ $this->_assetsUrl=Yii::app()->getAssetManager()->publish(Yii::getPathOfAlias('gii.assets'));
+ return $this->_assetsUrl;
+ }
+
+ /**
+ * @param string $value the base URL that contains all published asset files of gii.
+ */
+ public function setAssetsUrl($value)
+ {
+ $this->_assetsUrl=$value;
+ }
+
+ /**
+ * Performs access check to gii.
+ * This method will check to see if user IP and password are correct if they attempt
+ * to access actions other than "default/login" and "default/error".
+ * @param CController $controller the controller to be accessed.
+ * @param CAction $action the action to be accessed.
+ * @return boolean whether the action should be executed.
+ */
+ public function beforeControllerAction($controller, $action)
+ {
+ if(parent::beforeControllerAction($controller, $action))
+ {
+ $route=$controller->id.'/'.$action->id;
+ if(!$this->allowIp(Yii::app()->request->userHostAddress) && $route!=='default/error')
+ throw new CHttpException(403,"You are not allowed to access this page.");
+
+ $publicPages=array(
+ 'default/login',
+ 'default/error',
+ );
+ if($this->password!==false && Yii::app()->user->isGuest && !in_array($route,$publicPages))
+ Yii::app()->user->loginRequired();
+ else
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Checks to see if the user IP is allowed by {@link ipFilters}.
+ * @param string $ip the user IP
+ * @return boolean whether the user IP is allowed by {@link ipFilters}.
+ */
+ protected function allowIp($ip)
+ {
+ if(empty($this->ipFilters))
+ return true;
+ foreach($this->ipFilters as $filter)
+ {
+ if($filter==='*' || $filter===$ip || (($pos=strpos($filter,'*'))!==false && !strncmp($ip,$filter,$pos)))
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Finds all available code generators and their code templates.
+ * @return array
+ */
+ protected function findGenerators()
+ {
+ $generators=array();
+ $n=count($this->generatorPaths);
+ for($i=$n-1;$i>=0;--$i)
+ {
+ $alias=$this->generatorPaths[$i];
+ $path=Yii::getPathOfAlias($alias);
+ if($path===false || !is_dir($path))
+ continue;
+
+ $names=scandir($path);
+ foreach($names as $name)
+ {
+ if($name[0]!=='.' && is_dir($path.'/'.$name))
+ {
+ $className=ucfirst($name).'Generator';
+ if(is_file("$path/$name/$className.php"))
+ {
+ $generators[$name]=array(
+ 'class'=>"$alias.$name.$className",
+ );
+ }
+
+ if(isset($generators[$name]) && is_dir("$path/$name/templates"))
+ {
+ $templatePath="$path/$name/templates";
+ $dirs=scandir($templatePath);
+ foreach($dirs as $dir)
+ {
+ if($dir[0]!=='.' && is_dir($templatePath.'/'.$dir))
+ $generators[$name]['templates'][$dir]=strtr($templatePath.'/'.$dir,array('/'=>DIRECTORY_SEPARATOR,'\\'=>DIRECTORY_SEPARATOR));
+ }
+ }
+ }
+ }
+ }
+ return $generators;
+ }
+} \ No newline at end of file
diff --git a/framework/gii/assets/css/ie.css b/framework/gii/assets/css/ie.css
new file mode 100644
index 0000000..f336f0e
--- /dev/null
+++ b/framework/gii/assets/css/ie.css
@@ -0,0 +1,35 @@
+/* -----------------------------------------------------------------------
+
+
+ Blueprint CSS Framework 0.9
+ 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 div.span-1, * html div.span-2, * html div.span-3, * html div.span-4, * html div.span-5, * html div.span-6, * html div.span-7, * html div.span-8, * html div.span-9, * html div.span-10, * html div.span-11, * html div.span-12, * html div.span-13, * html div.span-14, * html div.span-15, * html div.span-16, * html div.span-17, * html div.span-18, * html div.span-19, * html div.span-20, * html div.span-21, * html div.span-22, * html div.span-23, * html div.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;}
+textarea {overflow:auto;}
+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 label {position:relative;top:-0.25em;}
+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/gii/assets/css/main.css b/framework/gii/assets/css/main.css
new file mode 100644
index 0000000..f569d77
--- /dev/null
+++ b/framework/gii/assets/css/main.css
@@ -0,0 +1,529 @@
+body
+{
+ margin: 0;
+ padding: 0;
+ color: #555;
+ font: normal 10pt Arial,Helvetica,sans-serif;
+ font: Verdana,"DejaVu Sans","Bitstream Vera Sans",Geneva,sans-serif;
+ background: #EFEFEF;
+}
+
+h1
+{
+ font-size: 1.6em;
+ color: #666;
+}
+
+h2
+{
+ font-size: 1.4em;
+ color: #666;
+}
+
+h3
+{
+ font-size: 1.2em;
+ color: #666;
+}
+
+#page
+{
+ margin-top: 5px;
+ margin-bottom: 5px;
+ background: white;
+ border: 1px solid #C9E0ED;
+}
+
+#header
+{
+ padding: 0px;
+ margin: 0px 20px;
+ border-bottom: 1px solid #C9E0ED;
+}
+
+#content
+{
+ padding: 20px;
+ min-height: 400px;
+}
+
+#sidebar
+{
+ padding: 20px 0 20px 20px;
+}
+
+#footer
+{
+ margin: 0 auto;
+ width: 950px;
+ font-size: 0.8em;
+ text-align: center;
+}
+
+#logo
+{
+ padding: 5px 0px;
+}
+
+#logo a
+{
+ text-decoration: none;
+}
+
+#header .top-menus
+{
+ margin: 20px 0px;
+ float: right;
+}
+
+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.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: #79B4DC;
+ border-left: 5px solid #6293B3;
+}
+
+.portlet-title
+{
+ font-size: 12px;
+ font-weight: bold;
+ padding: 0;
+ margin: 0;
+ color: white;
+}
+
+.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;
+}
+
+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 .row.buttons
+{
+ padding: 5px;
+ margin: 10px 0;
+}
+
+div.form .row.buttons input
+{
+ margin: 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,
+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;
+}
+
+div.form .tooltip
+{
+ display: none;
+ background-color:#EFFDFF;
+ border:1px solid #79B4DC;
+ padding: 10px;
+ width: 300px;
+}
+
+div.form .tooltip ul
+{
+ margin: 0;
+ padding: 10px 0 0 20px;
+}
+
+div.form .tooltip code
+{
+ color: #CA0EE3;
+ font-size:0.9em;
+}
+
+div.form.login
+{
+ border: 1px solid #C9E0ED;
+ width: 200px;
+ margin: 0 auto;
+ margin-top: 50px;
+ margin-bottom: 50px;
+ padding: 20px 10px 10px 10px;
+ text-align: center;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+}
+
+div.form.login p
+{
+ margin: 0 0 10px 0;
+}
+
+div.form.gii .row.sticky .value, span.sticky
+{
+ padding: 3px;
+ background: lightyellow;
+}
+
+div.form.gii .row.template select
+{
+ width: 420px;
+}
+
+div.form.gii table.preview
+{
+ border-collapse: collapse;
+}
+
+div.form.gii table.preview th
+{
+ text-align: center;
+}
+
+div.form.gii table.preview th.confirm
+{
+ text-align: right;
+}
+
+div.form.gii table.preview th.confirm label
+{
+ display: inline;
+}
+
+div.form.gii table.preview td.confirm
+{
+ width: 80px;
+ text-align: right;
+}
+
+div.form.gii table.preview td.confirm input
+{
+ margin:0;
+}
+
+div.form.gii table.preview td.confirm label
+{
+ display: inline;
+ font-weight: normal;
+}
+
+div.form.gii table.preview,
+div.form.gii table.preview th,
+div.form.gii table.preview td
+{
+ border: 1px solid #529EC6;
+}
+
+div.form.gii table.preview tr.skip
+{
+ background-color: #eee;
+}
+
+div.form.gii table.preview tr.new
+{
+ background-color: #C5FBBD;
+}
+
+div.form.gii table.preview tr.overwrite
+{
+ background-color: #FFE0E1;
+}
+
+div.form.gii pre.results
+{
+ overflow: auto;
+ background-color: gray;
+ max-height: 300px;
+ color: white;
+ padding: 10px;
+}
+
+div.form.gii div.success
+{
+ background: #C5FBBD;
+ border: 1px solid #76C376;
+ padding: 10px;
+ margin: 10px 0;
+}
+
+div.form.gii div.error
+{
+ background: #FFE0E1;
+ border: 1px solid #FFA0A2;
+ padding: 10px;
+ margin: 10px 0;
+}
+
+div.form.gii div.success code
+{
+ overflow: auto;
+ display: block;
+ padding: 5px;
+ font-size: 12px;
+ background: white;
+}
+
+div.form.gii pre.results span.error
+{
+ background: #FFE0E1;
+ color: black;
+ padding: 1px;
+}
+
+#fancybox-inner .error
+{
+ color: red;
+}
+
+#fancybox-inner .title
+{
+ font-size: 12px;
+ font-weight: bold;
+ text-decoration: underline;
+}
+
+#fancybox-inner .buttons
+{
+ float: right;
+ padding: 0 10px 0 0;
+}
+
+#fancybox-inner .content
+{
+ background: #F0F4FF;
+ text-align: left;
+}
+
+#fancybox-inner pre.diff
+{
+ margin:0;
+}
+
+#fancybox-inner pre.diff del
+{
+ background: pink;
+}
+
+#fancybox-inner pre.diff ins
+{
+ background: lightgreen;
+ text-decoration: none;
+}
+
+#fancybox-wrap #tip7-title
+{
+ text-align: left;
+}
+
+#fancybox-wrap #tip7-title b
+{
+ display: block;
+}
+
+#fancybox-wrap #tip7-title span
+{
+ float: right;
+}
diff --git a/framework/gii/assets/css/print.css b/framework/gii/assets/css/print.css
new file mode 100644
index 0000000..fdb8220
--- /dev/null
+++ b/framework/gii/assets/css/print.css
@@ -0,0 +1,29 @@
+/* -----------------------------------------------------------------------
+
+
+ Blueprint CSS Framework 0.9
+ 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/gii/assets/css/screen.css b/framework/gii/assets/css/screen.css
new file mode 100644
index 0000000..98eef32
--- /dev/null
+++ b/framework/gii/assets/css/screen.css
@@ -0,0 +1,235 @@
+/* -----------------------------------------------------------------------
+
+
+ Blueprint CSS Framework 0.9
+ 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, 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 {margin:0;padding:0;border:0;font-weight:inherit;font-style:inherit;font-size:100%;font-family:inherit;vertical-align:baseline;}
+body {line-height:1.5;}
+table {border-collapse:separate;border-spacing:0;}
+caption, th, td {text-align:left;font-weight:normal;}
+table, td, th {vertical-align:middle;}
+blockquote:before, blockquote:after, q:before, q:after {content:"";}
+blockquote, q {quotes:"" "";}
+a img {border:none;}
+
+/* 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;}
+p img.left {float:left;margin:1.5em 1.5em 1.5em 0;padding:0;}
+p img.right {float:right;margin:1.5em 0 1.5em 1.5em;}
+a:focus, a:hover {color:#000;}
+a {color:#009;text-decoration:underline;}
+blockquote {margin:1.5em;color:#666;font-style:italic;}
+strong {font-weight:bold;}
+em, dfn {font-style:italic;}
+dfn {font-weight:bold;}
+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:3.333em;}
+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;}
+.showgrid {background:url(src/grid.png);}
+.column, div.span-1, div.span-2, div.span-3, div.span-4, div.span-5, div.span-6, div.span-7, div.span-8, div.span-9, div.span-10, div.span-11, div.span-12, div.span-13, div.span-14, div.span-15, div.span-16, div.span-17, div.span-18, div.span-19, div.span-20, div.span-21, div.span-22, div.span-23, div.span-24 {float:left;margin-right:10px;}
+.last, div.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, div.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!important;border-right-width:1px!important;padding-left:5px!important;padding-right:5px!important;}
+input.span-1, textarea.span-1 {width:18px!important;}
+input.span-2, textarea.span-2 {width:58px!important;}
+input.span-3, textarea.span-3 {width:98px!important;}
+input.span-4, textarea.span-4 {width:138px!important;}
+input.span-5, textarea.span-5 {width:178px!important;}
+input.span-6, textarea.span-6 {width:218px!important;}
+input.span-7, textarea.span-7 {width:258px!important;}
+input.span-8, textarea.span-8 {width:298px!important;}
+input.span-9, textarea.span-9 {width:338px!important;}
+input.span-10, textarea.span-10 {width:378px!important;}
+input.span-11, textarea.span-11 {width:418px!important;}
+input.span-12, textarea.span-12 {width:458px!important;}
+input.span-13, textarea.span-13 {width:498px!important;}
+input.span-14, textarea.span-14 {width:538px!important;}
+input.span-15, textarea.span-15 {width:578px!important;}
+input.span-16, textarea.span-16 {width:618px!important;}
+input.span-17, textarea.span-17 {width:658px!important;}
+input.span-18, textarea.span-18 {width:698px!important;}
+input.span-19, textarea.span-19 {width:738px!important;}
+input.span-20, textarea.span-20 {width:778px!important;}
+input.span-21, textarea.span-21 {width:818px!important;}
+input.span-22, textarea.span-22 {width:858px!important;}
+input.span-23, textarea.span-23 {width:898px!important;}
+input.span-24, textarea.span-24 {width:938px!important;}
+.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;}
+div.border {padding-right:4px;margin-right:5px;border-right:1px solid #eee;}
+div.colborder {padding-right:24px;margin-right:25px;border-right:1px solid #eee;}
+.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:right;position:relative;}
+.prepend-top {margin-top:1.5em;}
+.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:.1em;margin:0 0 1.45em;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;} \ No newline at end of file
diff --git a/framework/gii/assets/images/logo.png b/framework/gii/assets/images/logo.png
new file mode 100644
index 0000000..a920241
--- /dev/null
+++ b/framework/gii/assets/images/logo.png
Binary files differ
diff --git a/framework/gii/assets/js/fancybox/blank.gif b/framework/gii/assets/js/fancybox/blank.gif
new file mode 100644
index 0000000..35d42e8
--- /dev/null
+++ b/framework/gii/assets/js/fancybox/blank.gif
Binary files differ
diff --git a/framework/gii/assets/js/fancybox/fancy_close.png b/framework/gii/assets/js/fancybox/fancy_close.png
new file mode 100644
index 0000000..20bf870
--- /dev/null
+++ b/framework/gii/assets/js/fancybox/fancy_close.png
Binary files differ
diff --git a/framework/gii/assets/js/fancybox/fancy_loading.png b/framework/gii/assets/js/fancybox/fancy_loading.png
new file mode 100644
index 0000000..86759ea
--- /dev/null
+++ b/framework/gii/assets/js/fancybox/fancy_loading.png
Binary files differ
diff --git a/framework/gii/assets/js/fancybox/fancy_nav_left.png b/framework/gii/assets/js/fancybox/fancy_nav_left.png
new file mode 100644
index 0000000..69ebc3e
--- /dev/null
+++ b/framework/gii/assets/js/fancybox/fancy_nav_left.png
Binary files differ
diff --git a/framework/gii/assets/js/fancybox/fancy_nav_right.png b/framework/gii/assets/js/fancybox/fancy_nav_right.png
new file mode 100644
index 0000000..99cb3dd
--- /dev/null
+++ b/framework/gii/assets/js/fancybox/fancy_nav_right.png
Binary files differ
diff --git a/framework/gii/assets/js/fancybox/fancy_shadow_e.png b/framework/gii/assets/js/fancybox/fancy_shadow_e.png
new file mode 100644
index 0000000..e29ddba
--- /dev/null
+++ b/framework/gii/assets/js/fancybox/fancy_shadow_e.png
Binary files differ
diff --git a/framework/gii/assets/js/fancybox/fancy_shadow_n.png b/framework/gii/assets/js/fancybox/fancy_shadow_n.png
new file mode 100644
index 0000000..f850840
--- /dev/null
+++ b/framework/gii/assets/js/fancybox/fancy_shadow_n.png
Binary files differ
diff --git a/framework/gii/assets/js/fancybox/fancy_shadow_ne.png b/framework/gii/assets/js/fancybox/fancy_shadow_ne.png
new file mode 100644
index 0000000..a340f27
--- /dev/null
+++ b/framework/gii/assets/js/fancybox/fancy_shadow_ne.png
Binary files differ
diff --git a/framework/gii/assets/js/fancybox/fancy_shadow_nw.png b/framework/gii/assets/js/fancybox/fancy_shadow_nw.png
new file mode 100644
index 0000000..a17c0fe
--- /dev/null
+++ b/framework/gii/assets/js/fancybox/fancy_shadow_nw.png
Binary files differ
diff --git a/framework/gii/assets/js/fancybox/fancy_shadow_s.png b/framework/gii/assets/js/fancybox/fancy_shadow_s.png
new file mode 100644
index 0000000..ceba06b
--- /dev/null
+++ b/framework/gii/assets/js/fancybox/fancy_shadow_s.png
Binary files differ
diff --git a/framework/gii/assets/js/fancybox/fancy_shadow_se.png b/framework/gii/assets/js/fancybox/fancy_shadow_se.png
new file mode 100644
index 0000000..567f2e7
--- /dev/null
+++ b/framework/gii/assets/js/fancybox/fancy_shadow_se.png
Binary files differ
diff --git a/framework/gii/assets/js/fancybox/fancy_shadow_sw.png b/framework/gii/assets/js/fancybox/fancy_shadow_sw.png
new file mode 100644
index 0000000..35dbec8
--- /dev/null
+++ b/framework/gii/assets/js/fancybox/fancy_shadow_sw.png
Binary files differ
diff --git a/framework/gii/assets/js/fancybox/fancy_shadow_w.png b/framework/gii/assets/js/fancybox/fancy_shadow_w.png
new file mode 100644
index 0000000..7843c2a
--- /dev/null
+++ b/framework/gii/assets/js/fancybox/fancy_shadow_w.png
Binary files differ
diff --git a/framework/gii/assets/js/fancybox/fancy_title_left.png b/framework/gii/assets/js/fancybox/fancy_title_left.png
new file mode 100644
index 0000000..2a68d05
--- /dev/null
+++ b/framework/gii/assets/js/fancybox/fancy_title_left.png
Binary files differ
diff --git a/framework/gii/assets/js/fancybox/fancy_title_main.png b/framework/gii/assets/js/fancybox/fancy_title_main.png
new file mode 100644
index 0000000..ad0d91f
--- /dev/null
+++ b/framework/gii/assets/js/fancybox/fancy_title_main.png
Binary files differ
diff --git a/framework/gii/assets/js/fancybox/fancy_title_over.png b/framework/gii/assets/js/fancybox/fancy_title_over.png
new file mode 100644
index 0000000..75b2bac
--- /dev/null
+++ b/framework/gii/assets/js/fancybox/fancy_title_over.png
Binary files differ
diff --git a/framework/gii/assets/js/fancybox/fancy_title_right.png b/framework/gii/assets/js/fancybox/fancy_title_right.png
new file mode 100644
index 0000000..fa9318e
--- /dev/null
+++ b/framework/gii/assets/js/fancybox/fancy_title_right.png
Binary files differ
diff --git a/framework/gii/assets/js/fancybox/fancybox-x.png b/framework/gii/assets/js/fancybox/fancybox-x.png
new file mode 100644
index 0000000..09f8646
--- /dev/null
+++ b/framework/gii/assets/js/fancybox/fancybox-x.png
Binary files differ
diff --git a/framework/gii/assets/js/fancybox/fancybox-y.png b/framework/gii/assets/js/fancybox/fancybox-y.png
new file mode 100644
index 0000000..9f6301d
--- /dev/null
+++ b/framework/gii/assets/js/fancybox/fancybox-y.png
Binary files differ
diff --git a/framework/gii/assets/js/fancybox/fancybox.png b/framework/gii/assets/js/fancybox/fancybox.png
new file mode 100644
index 0000000..f546239
--- /dev/null
+++ b/framework/gii/assets/js/fancybox/fancybox.png
Binary files differ
diff --git a/framework/gii/assets/js/fancybox/jquery.fancybox-1.3.1.css b/framework/gii/assets/js/fancybox/jquery.fancybox-1.3.1.css
new file mode 100644
index 0000000..fd71d74
--- /dev/null
+++ b/framework/gii/assets/js/fancybox/jquery.fancybox-1.3.1.css
@@ -0,0 +1,363 @@
+/*
+ * FancyBox - jQuery Plugin
+ * Simple and fancy lightbox alternative
+ *
+ * Examples and documentation at: http://fancybox.net
+ *
+ * Copyright (c) 2008 - 2010 Janis Skarnelis
+ *
+ * Version: 1.3.1 (05/03/2010)
+ * Requires: jQuery v1.3+
+ *
+ * Dual licensed under the MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ */
+
+#fancybox-loading {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ height: 40px;
+ width: 40px;
+ margin-top: -20px;
+ margin-left: -20px;
+ cursor: pointer;
+ overflow: hidden;
+ z-index: 1104;
+ display: none;
+}
+
+* html #fancybox-loading { /* IE6 */
+ position: absolute;
+ margin-top: 0;
+}
+
+#fancybox-loading div {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 40px;
+ height: 480px;
+ background-image: url('fancybox.png');
+}
+
+#fancybox-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ right: 0;
+ background: #000;
+ z-index: 1100;
+ display: none;
+}
+
+* html #fancybox-overlay { /* IE6 */
+ position: absolute;
+ width: 100%;
+}
+
+#fancybox-tmp {
+ padding: 0;
+ margin: 0;
+ border: 0;
+ overflow: auto;
+ display: none;
+}
+
+#fancybox-wrap {
+ position: absolute;
+ top: 0;
+ left: 0;
+ margin: 0;
+ padding: 20px;
+ z-index: 1101;
+ display: none;
+}
+
+#fancybox-outer {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ background: #FFF;
+}
+
+#fancybox-inner {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: 0;
+ outline: none;
+ overflow: hidden;
+}
+
+#fancybox-hide-sel-frame {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: transparent;
+}
+
+#fancybox-close {
+ position: absolute;
+ top: -15px;
+ right: -15px;
+ width: 30px;
+ height: 30px;
+ background-image: url('fancybox.png');
+ background-position: -40px 0px;
+ cursor: pointer;
+ z-index: 1103;
+ display: none;
+}
+
+#fancybox_error {
+ color: #444;
+ font: normal 12px/20px Arial;
+ padding: 7px;
+ margin: 0;
+}
+
+#fancybox-content {
+ height: auto;
+ width: auto;
+ padding: 0;
+ margin: 0;
+}
+
+#fancybox-img {
+ width: 100%;
+ height: 100%;
+ padding: 0;
+ margin: 0;
+ border: none;
+ outline: none;
+ line-height: 0;
+ vertical-align: top;
+ -ms-interpolation-mode: bicubic;
+}
+
+#fancybox-frame {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ border: none;
+ display: block;
+}
+
+#fancybox-title {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ font-family: Arial;
+ font-size: 12px;
+ z-index: 1102;
+}
+
+.fancybox-title-inside {
+ padding: 10px 0;
+ text-align: center;
+ color: #333;
+}
+
+.fancybox-title-outside {
+ padding-top: 5px;
+ color: #FFF;
+ text-align: center;
+ font-weight: bold;
+}
+
+.fancybox-title-over {
+ color: #FFF;
+ text-align: left;
+}
+
+#fancybox-title-over {
+ padding: 10px;
+ background-image: url('fancy_title_over.png');
+ display: block;
+}
+
+#fancybox-title-wrap {
+ display: inline-block;
+}
+
+#fancybox-title-wrap span {
+ height: 32px;
+ float: left;
+}
+
+#fancybox-title-left {
+ padding-left: 15px;
+ background-image: url('fancybox.png');
+ background-position: -40px -90px;
+ background-repeat: no-repeat;
+}
+
+#fancybox-title-main {
+ font-weight: bold;
+ line-height: 29px;
+ background-image: url('fancybox-x.png');
+ background-position: 0px -40px;
+ color: #FFF;
+}
+
+#fancybox-title-right {
+ padding-left: 15px;
+ background-image: url('fancybox.png');
+ background-position: -55px -90px;
+ background-repeat: no-repeat;
+}
+
+#fancybox-left, #fancybox-right {
+ position: absolute;
+ bottom: 0px;
+ height: 100%;
+ width: 35%;
+ cursor: pointer;
+ outline: none;
+ background-image: url('blank.gif');
+ z-index: 1102;
+ display: none;
+}
+
+#fancybox-left {
+ left: 0px;
+}
+
+#fancybox-right {
+ right: 0px;
+}
+
+#fancybox-left-ico, #fancybox-right-ico {
+ position: absolute;
+ top: 50%;
+ left: -9999px;
+ width: 30px;
+ height: 30px;
+ margin-top: -15px;
+ cursor: pointer;
+ z-index: 1102;
+ display: block;
+}
+
+#fancybox-left-ico {
+ background-image: url('fancybox.png');
+ background-position: -40px -30px;
+}
+
+#fancybox-right-ico {
+ background-image: url('fancybox.png');
+ background-position: -40px -60px;
+}
+
+#fancybox-left:hover, #fancybox-right:hover {
+ visibility: visible; /* IE6 */
+}
+
+#fancybox-left:hover span {
+ left: 20px;
+}
+
+#fancybox-right:hover span {
+ left: auto;
+ right: 20px;
+}
+
+.fancy-bg {
+ position: absolute;
+ padding: 0;
+ margin: 0;
+ border: 0;
+ width: 20px;
+ height: 20px;
+ z-index: 1001;
+}
+
+#fancy-bg-n {
+ top: -20px;
+ left: 0;
+ width: 100%;
+ background-image: url('fancybox-x.png');
+}
+
+#fancy-bg-ne {
+ top: -20px;
+ right: -20px;
+ background-image: url('fancybox.png');
+ background-position: -40px -162px;
+}
+
+#fancy-bg-e {
+ top: 0;
+ right: -20px;
+ height: 100%;
+ background-image: url('fancybox-y.png');
+ background-position: -20px 0px;
+}
+
+#fancy-bg-se {
+ bottom: -20px;
+ right: -20px;
+ background-image: url('fancybox.png');
+ background-position: -40px -182px;
+}
+
+#fancy-bg-s {
+ bottom: -20px;
+ left: 0;
+ width: 100%;
+ background-image: url('fancybox-x.png');
+ background-position: 0px -20px;
+}
+
+#fancy-bg-sw {
+ bottom: -20px;
+ left: -20px;
+ background-image: url('fancybox.png');
+ background-position: -40px -142px;
+}
+
+#fancy-bg-w {
+ top: 0;
+ left: -20px;
+ height: 100%;
+ background-image: url('fancybox-y.png');
+}
+
+#fancy-bg-nw {
+ top: -20px;
+ left: -20px;
+ background-image: url('fancybox.png');
+ background-position: -40px -122px;
+}
+
+/* IE */
+
+#fancybox-loading.fancybox-ie div { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_loading.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-close { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_close.png', sizingMethod='scale'); }
+
+.fancybox-ie #fancybox-title-over { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_title_over.png', sizingMethod='scale'); zoom: 1; }
+.fancybox-ie #fancybox-title-left { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_title_left.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-title-main { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_title_main.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-title-right { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_title_right.png', sizingMethod='scale'); }
+
+.fancybox-ie #fancybox-left-ico { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_nav_left.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-right-ico { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_nav_right.png', sizingMethod='scale'); }
+
+.fancybox-ie .fancy-bg { background: transparent !important; }
+
+.fancybox-ie #fancy-bg-n { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_n.png', sizingMethod='scale'); }
+.fancybox-ie #fancy-bg-ne { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_ne.png', sizingMethod='scale'); }
+.fancybox-ie #fancy-bg-e { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_e.png', sizingMethod='scale'); }
+.fancybox-ie #fancy-bg-se { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_se.png', sizingMethod='scale'); }
+.fancybox-ie #fancy-bg-s { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_s.png', sizingMethod='scale'); }
+.fancybox-ie #fancy-bg-sw { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_sw.png', sizingMethod='scale'); }
+.fancybox-ie #fancy-bg-w { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_w.png', sizingMethod='scale'); }
+.fancybox-ie #fancy-bg-nw { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_nw.png', sizingMethod='scale'); }
diff --git a/framework/gii/assets/js/fancybox/jquery.fancybox-1.3.1.pack.js b/framework/gii/assets/js/fancybox/jquery.fancybox-1.3.1.pack.js
new file mode 100644
index 0000000..8421d53
--- /dev/null
+++ b/framework/gii/assets/js/fancybox/jquery.fancybox-1.3.1.pack.js
@@ -0,0 +1,44 @@
+/*
+ * FancyBox - jQuery Plugin
+ * Simple and fancy lightbox alternative
+ *
+ * Examples and documentation at: http://fancybox.net
+ *
+ * Copyright (c) 2008 - 2010 Janis Skarnelis
+ *
+ * Version: 1.3.1 (05/03/2010)
+ * Requires: jQuery v1.3+
+ *
+ * Dual licensed under the MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ */
+
+(function(b){var m,u,x,g,D,i,z,A,B,p=0,e={},q=[],n=0,c={},j=[],E=null,s=new Image,G=/\.(jpg|gif|png|bmp|jpeg)(.*)?$/i,S=/[^\.]\.(swf)\s*$/i,H,I=1,k,l,h=false,y=b.extend(b("<div/>")[0],{prop:0}),v=0,O=!b.support.opacity&&!window.XMLHttpRequest,J=function(){u.hide();s.onerror=s.onload=null;E&&E.abort();m.empty()},P=function(){b.fancybox('<p id="fancybox_error">The requested content cannot be loaded.<br />Please try again later.</p>',{scrolling:"no",padding:20,transitionIn:"none",transitionOut:"none"})},
+K=function(){return[b(window).width(),b(window).height(),b(document).scrollLeft(),b(document).scrollTop()]},T=function(){var a=K(),d={},f=c.margin,o=c.autoScale,t=(20+f)*2,w=(20+f)*2,r=c.padding*2;if(c.width.toString().indexOf("%")>-1){d.width=a[0]*parseFloat(c.width)/100-40;o=false}else d.width=c.width+r;if(c.height.toString().indexOf("%")>-1){d.height=a[1]*parseFloat(c.height)/100-40;o=false}else d.height=c.height+r;if(o&&(d.width>a[0]-t||d.height>a[1]-w))if(e.type=="image"||e.type=="swf"){t+=r;
+w+=r;o=Math.min(Math.min(a[0]-t,c.width)/c.width,Math.min(a[1]-w,c.height)/c.height);d.width=Math.round(o*(d.width-r))+r;d.height=Math.round(o*(d.height-r))+r}else{d.width=Math.min(d.width,a[0]-t);d.height=Math.min(d.height,a[1]-w)}d.top=a[3]+(a[1]-(d.height+40))*0.5;d.left=a[2]+(a[0]-(d.width+40))*0.5;if(c.autoScale===false){d.top=Math.max(a[3]+f,d.top);d.left=Math.max(a[2]+f,d.left)}return d},U=function(a){if(a&&a.length)switch(c.titlePosition){case "inside":return a;case "over":return'<span id="fancybox-title-over">'+
+a+"</span>";default:return'<span id="fancybox-title-wrap"><span id="fancybox-title-left"></span><span id="fancybox-title-main">'+a+'</span><span id="fancybox-title-right"></span></span>'}return false},V=function(){var a=c.title,d=l.width-c.padding*2,f="fancybox-title-"+c.titlePosition;b("#fancybox-title").remove();v=0;if(c.titleShow!==false){a=b.isFunction(c.titleFormat)?c.titleFormat(a,j,n,c):U(a);if(!(!a||a==="")){b('<div id="fancybox-title" class="'+f+'" />').css({width:d,paddingLeft:c.padding,
+paddingRight:c.padding}).html(a).appendTo("body");switch(c.titlePosition){case "inside":v=b("#fancybox-title").outerHeight(true)-c.padding;l.height+=v;break;case "over":b("#fancybox-title").css("bottom",c.padding);break;default:b("#fancybox-title").css("bottom",b("#fancybox-title").outerHeight(true)*-1);break}b("#fancybox-title").appendTo(D).hide()}}},W=function(){b(document).unbind("keydown.fb").bind("keydown.fb",function(a){if(a.keyCode==27&&c.enableEscapeButton){a.preventDefault();b.fancybox.close()}else if(a.keyCode==
+37){a.preventDefault();b.fancybox.prev()}else if(a.keyCode==39){a.preventDefault();b.fancybox.next()}});if(b.fn.mousewheel){g.unbind("mousewheel.fb");j.length>1&&g.bind("mousewheel.fb",function(a,d){a.preventDefault();h||d===0||(d>0?b.fancybox.prev():b.fancybox.next())})}if(c.showNavArrows){if(c.cyclic&&j.length>1||n!==0)A.show();if(c.cyclic&&j.length>1||n!=j.length-1)B.show()}},X=function(){var a,d;if(j.length-1>n){a=j[n+1].href;if(typeof a!=="undefined"&&a.match(G)){d=new Image;d.src=a}}if(n>0){a=
+j[n-1].href;if(typeof a!=="undefined"&&a.match(G)){d=new Image;d.src=a}}},L=function(){i.css("overflow",c.scrolling=="auto"?c.type=="image"||c.type=="iframe"||c.type=="swf"?"hidden":"auto":c.scrolling=="yes"?"auto":"visible");if(!b.support.opacity){i.get(0).style.removeAttribute("filter");g.get(0).style.removeAttribute("filter")}b("#fancybox-title").show();c.hideOnContentClick&&i.one("click",b.fancybox.close);c.hideOnOverlayClick&&x.one("click",b.fancybox.close);c.showCloseButton&&z.show();W();b(window).bind("resize.fb",
+b.fancybox.center);c.centerOnScroll?b(window).bind("scroll.fb",b.fancybox.center):b(window).unbind("scroll.fb");b.isFunction(c.onComplete)&&c.onComplete(j,n,c);h=false;X()},M=function(a){var d=Math.round(k.width+(l.width-k.width)*a),f=Math.round(k.height+(l.height-k.height)*a),o=Math.round(k.top+(l.top-k.top)*a),t=Math.round(k.left+(l.left-k.left)*a);g.css({width:d+"px",height:f+"px",top:o+"px",left:t+"px"});d=Math.max(d-c.padding*2,0);f=Math.max(f-(c.padding*2+v*a),0);i.css({width:d+"px",height:f+
+"px"});if(typeof l.opacity!=="undefined")g.css("opacity",a<0.5?0.5:a)},Y=function(a){var d=a.offset();d.top+=parseFloat(a.css("paddingTop"))||0;d.left+=parseFloat(a.css("paddingLeft"))||0;d.top+=parseFloat(a.css("border-top-width"))||0;d.left+=parseFloat(a.css("border-left-width"))||0;d.width=a.width();d.height=a.height();return d},Q=function(){var a=e.orig?b(e.orig):false,d={};if(a&&a.length){a=Y(a);d={width:a.width+c.padding*2,height:a.height+c.padding*2,top:a.top-c.padding-20,left:a.left-c.padding-
+20}}else{a=K();d={width:1,height:1,top:a[3]+a[1]*0.5,left:a[2]+a[0]*0.5}}return d},N=function(){u.hide();if(g.is(":visible")&&b.isFunction(c.onCleanup))if(c.onCleanup(j,n,c)===false){b.event.trigger("fancybox-cancel");h=false;return}j=q;n=p;c=e;i.get(0).scrollTop=0;i.get(0).scrollLeft=0;if(c.overlayShow){O&&b("select:not(#fancybox-tmp select)").filter(function(){return this.style.visibility!=="hidden"}).css({visibility:"hidden"}).one("fancybox-cleanup",function(){this.style.visibility="inherit"});
+x.css({"background-color":c.overlayColor,opacity:c.overlayOpacity}).unbind().show()}l=T();V();if(g.is(":visible")){b(z.add(A).add(B)).hide();var a=g.position(),d;k={top:a.top,left:a.left,width:g.width(),height:g.height()};d=k.width==l.width&&k.height==l.height;i.fadeOut(c.changeFade,function(){var f=function(){i.html(m.contents()).fadeIn(c.changeFade,L)};b.event.trigger("fancybox-change");i.empty().css("overflow","hidden");if(d){i.css({top:c.padding,left:c.padding,width:Math.max(l.width-c.padding*
+2,1),height:Math.max(l.height-c.padding*2-v,1)});f()}else{i.css({top:c.padding,left:c.padding,width:Math.max(k.width-c.padding*2,1),height:Math.max(k.height-c.padding*2,1)});y.prop=0;b(y).animate({prop:1},{duration:c.changeSpeed,easing:c.easingChange,step:M,complete:f})}})}else{g.css("opacity",1);if(c.transitionIn=="elastic"){k=Q();i.css({top:c.padding,left:c.padding,width:Math.max(k.width-c.padding*2,1),height:Math.max(k.height-c.padding*2,1)}).html(m.contents());g.css(k).show();if(c.opacity)l.opacity=
+0;y.prop=0;b(y).animate({prop:1},{duration:c.speedIn,easing:c.easingIn,step:M,complete:L})}else{i.css({top:c.padding,left:c.padding,width:Math.max(l.width-c.padding*2,1),height:Math.max(l.height-c.padding*2-v,1)}).html(m.contents());g.css(l).fadeIn(c.transitionIn=="none"?0:c.speedIn,L)}}},F=function(){m.width(e.width);m.height(e.height);if(e.width=="auto")e.width=m.width();if(e.height=="auto")e.height=m.height();N()},Z=function(){h=true;e.width=s.width;e.height=s.height;b("<img />").attr({id:"fancybox-img",
+src:s.src,alt:e.title}).appendTo(m);N()},C=function(){J();var a=q[p],d,f,o,t,w;e=b.extend({},b.fn.fancybox.defaults,typeof b(a).data("fancybox")=="undefined"?e:b(a).data("fancybox"));o=a.title||b(a).title||e.title||"";if(a.nodeName&&!e.orig)e.orig=b(a).children("img:first").length?b(a).children("img:first"):b(a);if(o===""&&e.orig)o=e.orig.attr("alt");d=a.nodeName&&/^(?:javascript|#)/i.test(a.href)?e.href||null:e.href||a.href||null;if(e.type){f=e.type;if(!d)d=e.content}else if(e.content)f="html";else if(d)if(d.match(G))f=
+"image";else if(d.match(S))f="swf";else if(b(a).hasClass("iframe"))f="iframe";else if(d.match(/#/)){a=d.substr(d.indexOf("#"));f=b(a).length>0?"inline":"ajax"}else f="ajax";else f="inline";e.type=f;e.href=d;e.title=o;if(e.autoDimensions&&e.type!=="iframe"&&e.type!=="swf"){e.width="auto";e.height="auto"}if(e.modal){e.overlayShow=true;e.hideOnOverlayClick=false;e.hideOnContentClick=false;e.enableEscapeButton=false;e.showCloseButton=false}if(b.isFunction(e.onStart))if(e.onStart(q,p,e)===false){h=false;
+return}m.css("padding",20+e.padding+e.margin);b(".fancybox-inline-tmp").unbind("fancybox-cancel").bind("fancybox-change",function(){b(this).replaceWith(i.children())});switch(f){case "html":m.html(e.content);F();break;case "inline":b('<div class="fancybox-inline-tmp" />').hide().insertBefore(b(a)).bind("fancybox-cleanup",function(){b(this).replaceWith(i.children())}).bind("fancybox-cancel",function(){b(this).replaceWith(m.children())});b(a).appendTo(m);F();break;case "image":h=false;b.fancybox.showActivity();
+s=new Image;s.onerror=function(){P()};s.onload=function(){s.onerror=null;s.onload=null;Z()};s.src=d;break;case "swf":t='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="'+e.width+'" height="'+e.height+'"><param name="movie" value="'+d+'"></param>';w="";b.each(e.swf,function(r,R){t+='<param name="'+r+'" value="'+R+'"></param>';w+=" "+r+'="'+R+'"'});t+='<embed src="'+d+'" type="application/x-shockwave-flash" width="'+e.width+'" height="'+e.height+'"'+w+"></embed></object>";m.html(t);
+F();break;case "ajax":a=d.split("#",2);f=e.ajax.data||{};if(a.length>1){d=a[0];if(typeof f=="string")f+="&selector="+a[1];else f.selector=a[1]}h=false;b.fancybox.showActivity();E=b.ajax(b.extend(e.ajax,{url:d,data:f,error:P,success:function(r){if(E.status==200){m.html(r);F()}}}));break;case "iframe":b('<iframe id="fancybox-frame" name="fancybox-frame'+(new Date).getTime()+'" frameborder="0" hspace="0" scrolling="'+e.scrolling+'" src="'+e.href+'"></iframe>').appendTo(m);N();break}},$=function(){if(u.is(":visible")){b("div",
+u).css("top",I*-40+"px");I=(I+1)%12}else clearInterval(H)},aa=function(){if(!b("#fancybox-wrap").length){b("body").append(m=b('<div id="fancybox-tmp"></div>'),u=b('<div id="fancybox-loading"><div></div></div>'),x=b('<div id="fancybox-overlay"></div>'),g=b('<div id="fancybox-wrap"></div>'));if(!b.support.opacity){g.addClass("fancybox-ie");u.addClass("fancybox-ie")}D=b('<div id="fancybox-outer"></div>').append('<div class="fancy-bg" id="fancy-bg-n"></div><div class="fancy-bg" id="fancy-bg-ne"></div><div class="fancy-bg" id="fancy-bg-e"></div><div class="fancy-bg" id="fancy-bg-se"></div><div class="fancy-bg" id="fancy-bg-s"></div><div class="fancy-bg" id="fancy-bg-sw"></div><div class="fancy-bg" id="fancy-bg-w"></div><div class="fancy-bg" id="fancy-bg-nw"></div>').appendTo(g);
+D.append(i=b('<div id="fancybox-inner"></div>'),z=b('<a id="fancybox-close"></a>'),A=b('<a href="javascript:;" id="fancybox-left"><span class="fancy-ico" id="fancybox-left-ico"></span></a>'),B=b('<a href="javascript:;" id="fancybox-right"><span class="fancy-ico" id="fancybox-right-ico"></span></a>'));z.click(b.fancybox.close);u.click(b.fancybox.cancel);A.click(function(a){a.preventDefault();b.fancybox.prev()});B.click(function(a){a.preventDefault();b.fancybox.next()});if(O){x.get(0).style.setExpression("height",
+"document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px'");u.get(0).style.setExpression("top","(-20 + (document.documentElement.clientHeight ? document.documentElement.clientHeight/2 : document.body.clientHeight/2 ) + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop )) + 'px'");D.prepend('<iframe id="fancybox-hide-sel-frame" src="javascript:\'\';" scrolling="no" frameborder="0" ></iframe>')}}};
+b.fn.fancybox=function(a){b(this).data("fancybox",b.extend({},a,b.metadata?b(this).metadata():{})).unbind("click.fb").bind("click.fb",function(d){d.preventDefault();if(!h){h=true;b(this).blur();q=[];p=0;d=b(this).attr("rel")||"";if(!d||d==""||d==="nofollow")q.push(this);else{q=b("a[rel="+d+"], area[rel="+d+"]");p=q.index(this)}C();return false}});return this};b.fancybox=function(a,d){if(!h){h=true;d=typeof d!=="undefined"?d:{};q=[];p=d.index||0;if(b.isArray(a)){for(var f=0,o=a.length;f<o;f++)if(typeof a[f]==
+"object")b(a[f]).data("fancybox",b.extend({},d,a[f]));else a[f]=b({}).data("fancybox",b.extend({content:a[f]},d));q=jQuery.merge(q,a)}else{if(typeof a=="object")b(a).data("fancybox",b.extend({},d,a));else a=b({}).data("fancybox",b.extend({content:a},d));q.push(a)}if(p>q.length||p<0)p=0;C()}};b.fancybox.showActivity=function(){clearInterval(H);u.show();H=setInterval($,66)};b.fancybox.hideActivity=function(){u.hide()};b.fancybox.next=function(){return b.fancybox.pos(n+1)};b.fancybox.prev=function(){return b.fancybox.pos(n-
+1)};b.fancybox.pos=function(a){if(!h){a=parseInt(a,10);if(a>-1&&j.length>a){p=a;C()}if(c.cyclic&&j.length>1&&a<0){p=j.length-1;C()}if(c.cyclic&&j.length>1&&a>=j.length){p=0;C()}}};b.fancybox.cancel=function(){if(!h){h=true;b.event.trigger("fancybox-cancel");J();e&&b.isFunction(e.onCancel)&&e.onCancel(q,p,e);h=false}};b.fancybox.close=function(){function a(){x.fadeOut("fast");g.hide();b.event.trigger("fancybox-cleanup");i.empty();b.isFunction(c.onClosed)&&c.onClosed(j,n,c);j=e=[];n=p=0;c=e={};h=false}
+if(!(h||g.is(":hidden"))){h=true;if(c&&b.isFunction(c.onCleanup))if(c.onCleanup(j,n,c)===false){h=false;return}J();b(z.add(A).add(B)).hide();b("#fancybox-title").remove();g.add(i).add(x).unbind();b(window).unbind("resize.fb scroll.fb");b(document).unbind("keydown.fb");i.css("overflow","hidden");if(c.transitionOut=="elastic"){k=Q();var d=g.position();l={top:d.top,left:d.left,width:g.width(),height:g.height()};if(c.opacity)l.opacity=1;y.prop=1;b(y).animate({prop:0},{duration:c.speedOut,easing:c.easingOut,
+step:M,complete:a})}else g.fadeOut(c.transitionOut=="none"?0:c.speedOut,a)}};b.fancybox.resize=function(){var a,d;if(!(h||g.is(":hidden"))){h=true;a=i.wrapInner("<div style='overflow:auto'></div>").children();d=a.height();g.css({height:d+c.padding*2+v});i.css({height:d});a.replaceWith(a.children());b.fancybox.center()}};b.fancybox.center=function(){h=true;var a=K(),d=c.margin,f={};f.top=a[3]+(a[1]-(g.height()-v+40))*0.5;f.left=a[2]+(a[0]-(g.width()+40))*0.5;f.top=Math.max(a[3]+d,f.top);f.left=Math.max(a[2]+
+d,f.left);g.css(f);h=false};b.fn.fancybox.defaults={padding:10,margin:20,opacity:false,modal:false,cyclic:false,scrolling:"auto",width:560,height:340,autoScale:true,autoDimensions:true,centerOnScroll:false,ajax:{},swf:{wmode:"transparent"},hideOnOverlayClick:true,hideOnContentClick:false,overlayShow:true,overlayOpacity:0.3,overlayColor:"#666",titleShow:true,titlePosition:"outside",titleFormat:null,transitionIn:"fade",transitionOut:"fade",speedIn:300,speedOut:300,changeSpeed:300,changeFade:"fast",
+easingIn:"swing",easingOut:"swing",showCloseButton:true,showNavArrows:true,enableEscapeButton:true,onStart:null,onCancel:null,onComplete:null,onCleanup:null,onClosed:null};b(document).ready(function(){aa()})})(jQuery); \ No newline at end of file
diff --git a/framework/gii/assets/js/jquery.tooltip-1.2.6.min.js b/framework/gii/assets/js/jquery.tooltip-1.2.6.min.js
new file mode 100644
index 0000000..9f28bef
--- /dev/null
+++ b/framework/gii/assets/js/jquery.tooltip-1.2.6.min.js
@@ -0,0 +1,11 @@
+/*!
+ * jQuery Tools v1.2.6 - The missing UI library for the Web
+ *
+ * tooltip/tooltip.js
+ *
+ * NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
+ *
+ * http://flowplayer.org/tools/
+ *
+ */
+(function(a){a.tools=a.tools||{version:"v1.2.6"},a.tools.tooltip={conf:{effect:"toggle",fadeOutSpeed:"fast",predelay:0,delay:30,opacity:1,tip:0,fadeIE:!1,position:["top","center"],offset:[0,0],relative:!1,cancelDefault:!0,events:{def:"mouseenter,mouseleave",input:"focus,blur",widget:"focus mouseenter,blur mouseleave",tooltip:"mouseenter,mouseleave"},layout:"<div/>",tipClass:"tooltip"},addEffect:function(a,c,d){b[a]=[c,d]}};var b={toggle:[function(a){var b=this.getConf(),c=this.getTip(),d=b.opacity;d<1&&c.css({opacity:d}),c.show(),a.call()},function(a){this.getTip().hide(),a.call()}],fade:[function(b){var c=this.getConf();!a.browser.msie||c.fadeIE?this.getTip().fadeTo(c.fadeInSpeed,c.opacity,b):(this.getTip().show(),b())},function(b){var c=this.getConf();!a.browser.msie||c.fadeIE?this.getTip().fadeOut(c.fadeOutSpeed,b):(this.getTip().hide(),b())}]};function c(b,c,d){var e=d.relative?b.position().top:b.offset().top,f=d.relative?b.position().left:b.offset().left,g=d.position[0];e-=c.outerHeight()-d.offset[0],f+=b.outerWidth()+d.offset[1],/iPad/i.test(navigator.userAgent)&&(e-=a(window).scrollTop());var h=c.outerHeight()+b.outerHeight();g=="center"&&(e+=h/2),g=="bottom"&&(e+=h),g=d.position[1];var i=c.outerWidth()+b.outerWidth();g=="center"&&(f-=i/2),g=="left"&&(f-=i);return{top:e,left:f}}function d(d,e){var f=this,g=d.add(f),h,i=0,j=0,k=d.attr("title"),l=d.attr("data-tooltip"),m=b[e.effect],n,o=d.is(":input"),p=o&&d.is(":checkbox, :radio, select, :button, :submit"),q=d.attr("type"),r=e.events[q]||e.events[o?p?"widget":"input":"def"];if(!m)throw"Nonexistent effect \""+e.effect+"\"";r=r.split(/,\s*/);if(r.length!=2)throw"Tooltip: bad events configuration for "+q;d.bind(r[0],function(a){clearTimeout(i),e.predelay?j=setTimeout(function(){f.show(a)},e.predelay):f.show(a)}).bind(r[1],function(a){clearTimeout(j),e.delay?i=setTimeout(function(){f.hide(a)},e.delay):f.hide(a)}),k&&e.cancelDefault&&(d.removeAttr("title"),d.data("title",k)),a.extend(f,{show:function(b){if(!h){l?h=a(l):e.tip?h=a(e.tip).eq(0):k?h=a(e.layout).addClass(e.tipClass).appendTo(document.body).hide().append(k):(h=d.next(),h.length||(h=d.parent().next()));if(!h.length)throw"Cannot find tooltip for "+d}if(f.isShown())return f;h.stop(!0,!0);var o=c(d,h,e);e.tip&&h.html(d.data("title")),b=a.Event(),b.type="onBeforeShow",g.trigger(b,[o]);if(b.isDefaultPrevented())return f;o=c(d,h,e),h.css({position:"absolute",top:o.top,left:o.left}),n=!0,m[0].call(f,function(){b.type="onShow",n="full",g.trigger(b)});var p=e.events.tooltip.split(/,\s*/);h.data("__set")||(h.unbind(p[0]).bind(p[0],function(){clearTimeout(i),clearTimeout(j)}),p[1]&&!d.is("input:not(:checkbox, :radio), textarea")&&h.unbind(p[1]).bind(p[1],function(a){a.relatedTarget!=d[0]&&d.trigger(r[1].split(" ")[0])}),e.tip||h.data("__set",!0));return f},hide:function(c){if(!h||!f.isShown())return f;c=a.Event(),c.type="onBeforeHide",g.trigger(c);if(!c.isDefaultPrevented()){n=!1,b[e.effect][1].call(f,function(){c.type="onHide",g.trigger(c)});return f}},isShown:function(a){return a?n=="full":n},getConf:function(){return e},getTip:function(){return h},getTrigger:function(){return d}}),a.each("onHide,onBeforeShow,onShow,onBeforeHide".split(","),function(b,c){a.isFunction(e[c])&&a(f).bind(c,e[c]),f[c]=function(b){b&&a(f).bind(c,b);return f}})}a.fn.tooltip=function(b){var c=this.data("tooltip");if(c)return c;b=a.extend(!0,{},a.tools.tooltip.conf,b),typeof b.position=="string"&&(b.position=b.position.split(/,?\s/)),this.each(function(){c=new d(a(this),b),a(this).data("tooltip",c)});return b.api?c:this}})(jQuery);
diff --git a/framework/gii/assets/js/main.js b/framework/gii/assets/js/main.js
new file mode 100644
index 0000000..d44c317
--- /dev/null
+++ b/framework/gii/assets/js/main.js
@@ -0,0 +1,79 @@
+$(document).ready(function() {
+ if($('div.form.login').length) { // in login page
+ $('input#LoginForm_password').focus();
+ }
+
+ $('table.preview input[name="checkAll"]').click(function() {
+ $('table.preview .confirm input').prop('checked', this.checked);
+ });
+
+ $('table.preview td.confirm input').click(function() {
+ $('table.preview input[name="checkAll"]').prop('checked', !$('table.preview td.confirm input:not(:checked)').length);
+ });
+ $('table.preview input[name="checkAll"]').prop('checked', !$('table.preview td.confirm input:not(:checked)').length);
+
+ $('.form .row.sticky input:not(.error), .form .row.sticky select:not(.error), .form .row.sticky textarea:not(.error)').each(function(){
+ var value;
+ if(this.tagName=='SELECT')
+ value=this.options[this.selectedIndex].text;
+ else if(this.tagName=='TEXTAREA')
+ value=$(this).html();
+ else
+ value=$(this).val();
+ if(value=='')
+ value='[empty]';
+ $(this).before('<div class="value">'+value+'</div>').hide();
+ });
+
+ $('.form.gii .row.sticky .value').live('click', function(){
+ $(this).hide();
+ $(this).next().show().get(0).focus();
+ });
+
+
+ $('.form.gii .row input, .form.gii .row textarea, .form.gii .row select, .with-tooltip').not('.no-tooltip, .no-tooltip *').tooltip({
+ position: "center right",
+ offset: [-2, 10]
+ });
+
+ $('.form.gii .row input').change(function(){
+ $('.form.gii .feedback').hide();
+ $('.form.gii input[name="generate"]').hide();
+ });
+
+ $('.form.gii .view-code').click(function(){
+ var title=$(this).attr('rel');
+ $.fancybox.showActivity();
+ $.ajax({
+ type: 'POST',
+ cache: false,
+ url: $(this).attr('href'),
+ data: $('.form.gii form').serializeArray(),
+ success: function(data){
+ $.fancybox(data, {
+ 'title': title,
+ 'titlePosition': 'inside',
+ 'titleFormat': function(title, currentArray, currentIndex, currentOpts) {
+ return '<div id="tip7-title"><span><a href="javascript:;" onclick="$.fancybox.close();">close</a></span>' + (title && title.length ? '<b>' + title + '</b>' : '' ) + '</div>';
+ },
+ 'showCloseButton': false,
+ 'autoDimensions': false,
+ 'width': 900,
+ 'height': 'auto',
+ 'onComplete':function(){
+ $('#fancybox-inner').scrollTop(0);
+ }
+ });
+ },
+ error: function(XMLHttpRequest, textStatus, errorThrown) {
+ $.fancybox('<div class="error">'+XMLHttpRequest.responseText+'</div>');
+ }
+ });
+ return false;
+ });
+
+ $('#fancybox-inner .close-code').live('click', function(){
+ $.fancybox.close();
+ return false;
+ });
+}); \ No newline at end of file
diff --git a/framework/gii/components/Pear/Text/Diff.php b/framework/gii/components/Pear/Text/Diff.php
new file mode 100644
index 0000000..205b8a7
--- /dev/null
+++ b/framework/gii/components/Pear/Text/Diff.php
@@ -0,0 +1,453 @@
+<?php
+/**
+ * General API for generating and formatting diffs - the differences between
+ * two sequences of strings.
+ *
+ * The original PHP version of this code was written by Geoffrey T. Dairiki
+ * <dairiki@dairiki.org>, and is used/adapted with his permission.
+ *
+ * $Horde: framework/Text_Diff/Diff.php,v 1.11.2.11 2008/02/24 10:57:46 jan Exp $
+ *
+ * Copyright 2004 Geoffrey T. Dairiki <dairiki@dairiki.org>
+ * Copyright 2004-2008 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you did
+ * not receive this file, see http://opensource.org/licenses/lgpl-license.php.
+ *
+ * @package Text_Diff
+ * @author Geoffrey T. Dairiki <dairiki@dairiki.org>
+ */
+class Text_Diff {
+
+ /**
+ * Array of changes.
+ *
+ * @var array
+ */
+ var $_edits;
+
+ /**
+ * Computes diffs between sequences of strings.
+ *
+ * @param string $engine Name of the diffing engine to use. 'auto'
+ * will automatically select the best.
+ * @param array $params Parameters to pass to the diffing engine.
+ * Normally an array of two arrays, each
+ * containing the lines from a file.
+ */
+ function Text_Diff($engine, $params)
+ {
+ // Backward compatibility workaround.
+ if (!is_string($engine)) {
+ $params = array($engine, $params);
+ $engine = 'auto';
+ }
+
+ if ($engine == 'auto') {
+ $engine = extension_loaded('xdiff') ? 'xdiff' : 'native';
+ } else {
+ $engine = basename($engine);
+ }
+
+ require_once 'Text/Diff/Engine/' . $engine . '.php';
+ $class = 'Text_Diff_Engine_' . $engine;
+ $diff_engine = new $class();
+
+ $this->_edits = call_user_func_array(array($diff_engine, 'diff'), $params);
+ }
+
+ /**
+ * Returns the array of differences.
+ */
+ function getDiff()
+ {
+ return $this->_edits;
+ }
+
+ /**
+ * returns the number of new (added) lines in a given diff.
+ *
+ * @since Text_Diff 1.1.0
+ * @since Horde 3.2
+ *
+ * @return integer The number of new lines
+ */
+ function countAddedLines()
+ {
+ $count = 0;
+ foreach ($this->_edits as $edit) {
+ if (is_a($edit, 'Text_Diff_Op_add') ||
+ is_a($edit, 'Text_Diff_Op_change')) {
+ $count += $edit->nfinal();
+ }
+ }
+ return $count;
+ }
+
+ /**
+ * Returns the number of deleted (removed) lines in a given diff.
+ *
+ * @since Text_Diff 1.1.0
+ * @since Horde 3.2
+ *
+ * @return integer The number of deleted lines
+ */
+ function countDeletedLines()
+ {
+ $count = 0;
+ foreach ($this->_edits as $edit) {
+ if (is_a($edit, 'Text_Diff_Op_delete') ||
+ is_a($edit, 'Text_Diff_Op_change')) {
+ $count += $edit->norig();
+ }
+ }
+ return $count;
+ }
+
+ /**
+ * Computes a reversed diff.
+ *
+ * Example:
+ * <code>
+ * $diff = new Text_Diff($lines1, $lines2);
+ * $rev = $diff->reverse();
+ * </code>
+ *
+ * @return Text_Diff A Diff object representing the inverse of the
+ * original diff. Note that we purposely don't return a
+ * reference here, since this essentially is a clone()
+ * method.
+ */
+ function reverse()
+ {
+ if (version_compare(zend_version(), '2', '>')) {
+ $rev = clone($this);
+ } else {
+ $rev = $this;
+ }
+ $rev->_edits = array();
+ foreach ($this->_edits as $edit) {
+ $rev->_edits[] = $edit->reverse();
+ }
+ return $rev;
+ }
+
+ /**
+ * Checks for an empty diff.
+ *
+ * @return boolean True if two sequences were identical.
+ */
+ function isEmpty()
+ {
+ foreach ($this->_edits as $edit) {
+ if (!is_a($edit, 'Text_Diff_Op_copy')) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Computes the length of the Longest Common Subsequence (LCS).
+ *
+ * This is mostly for diagnostic purposes.
+ *
+ * @return integer The length of the LCS.
+ */
+ function lcs()
+ {
+ $lcs = 0;
+ foreach ($this->_edits as $edit) {
+ if (is_a($edit, 'Text_Diff_Op_copy')) {
+ $lcs += count($edit->orig);
+ }
+ }
+ return $lcs;
+ }
+
+ /**
+ * Gets the original set of lines.
+ *
+ * This reconstructs the $from_lines parameter passed to the constructor.
+ *
+ * @return array The original sequence of strings.
+ */
+ function getOriginal()
+ {
+ $lines = array();
+ foreach ($this->_edits as $edit) {
+ if ($edit->orig) {
+ array_splice($lines, count($lines), 0, $edit->orig);
+ }
+ }
+ return $lines;
+ }
+
+ /**
+ * Gets the final set of lines.
+ *
+ * This reconstructs the $to_lines parameter passed to the constructor.
+ *
+ * @return array The sequence of strings.
+ */
+ function getFinal()
+ {
+ $lines = array();
+ foreach ($this->_edits as $edit) {
+ if ($edit->final) {
+ array_splice($lines, count($lines), 0, $edit->final);
+ }
+ }
+ return $lines;
+ }
+
+ /**
+ * Removes trailing newlines from a line of text. This is meant to be used
+ * with array_walk().
+ *
+ * @param string $line The line to trim.
+ * @param integer $key The index of the line in the array. Not used.
+ */
+ static function trimNewlines(&$line, $key)
+ {
+ $line = str_replace(array("\n", "\r"), '', $line);
+ }
+
+ /**
+ * Determines the location of the system temporary directory.
+ *
+ * @static
+ *
+ * @access protected
+ *
+ * @return string A directory name which can be used for temp files.
+ * Returns false if one could not be found.
+ */
+ function _getTempDir()
+ {
+ $tmp_locations = array('/tmp', '/var/tmp', 'c:\WUTemp', 'c:\temp',
+ 'c:\windows\temp', 'c:\winnt\temp');
+
+ /* Try PHP's upload_tmp_dir directive. */
+ $tmp = ini_get('upload_tmp_dir');
+
+ /* Otherwise, try to determine the TMPDIR environment variable. */
+ if (!strlen($tmp)) {
+ $tmp = getenv('TMPDIR');
+ }
+
+ /* If we still cannot determine a value, then cycle through a list of
+ * preset possibilities. */
+ while (!strlen($tmp) && count($tmp_locations)) {
+ $tmp_check = array_shift($tmp_locations);
+ if (@is_dir($tmp_check)) {
+ $tmp = $tmp_check;
+ }
+ }
+
+ /* If it is still empty, we have failed, so return false; otherwise
+ * return the directory determined. */
+ return strlen($tmp) ? $tmp : false;
+ }
+
+ /**
+ * Checks a diff for validity.
+ *
+ * This is here only for debugging purposes.
+ */
+ function _check($from_lines, $to_lines)
+ {
+ if (serialize($from_lines) != serialize($this->getOriginal())) {
+ trigger_error("Reconstructed original doesn't match", E_USER_ERROR);
+ }
+ if (serialize($to_lines) != serialize($this->getFinal())) {
+ trigger_error("Reconstructed final doesn't match", E_USER_ERROR);
+ }
+
+ $rev = $this->reverse();
+ if (serialize($to_lines) != serialize($rev->getOriginal())) {
+ trigger_error("Reversed original doesn't match", E_USER_ERROR);
+ }
+ if (serialize($from_lines) != serialize($rev->getFinal())) {
+ trigger_error("Reversed final doesn't match", E_USER_ERROR);
+ }
+
+ $prevtype = null;
+ foreach ($this->_edits as $edit) {
+ if ($prevtype == get_class($edit)) {
+ trigger_error("Edit sequence is non-optimal", E_USER_ERROR);
+ }
+ $prevtype = get_class($edit);
+ }
+
+ return true;
+ }
+
+}
+
+/**
+ * @package Text_Diff
+ * @author Geoffrey T. Dairiki <dairiki@dairiki.org>
+ */
+class Text_MappedDiff extends Text_Diff {
+
+ /**
+ * Computes a diff between sequences of strings.
+ *
+ * This can be used to compute things like case-insensitve diffs, or diffs
+ * which ignore changes in white-space.
+ *
+ * @param array $from_lines An array of strings.
+ * @param array $to_lines An array of strings.
+ * @param array $mapped_from_lines This array should have the same size
+ * number of elements as $from_lines. The
+ * elements in $mapped_from_lines and
+ * $mapped_to_lines are what is actually
+ * compared when computing the diff.
+ * @param array $mapped_to_lines This array should have the same number
+ * of elements as $to_lines.
+ */
+ function Text_MappedDiff($from_lines, $to_lines,
+ $mapped_from_lines, $mapped_to_lines)
+ {
+ assert(count($from_lines) == count($mapped_from_lines));
+ assert(count($to_lines) == count($mapped_to_lines));
+
+ parent::Text_Diff($mapped_from_lines, $mapped_to_lines);
+
+ $xi = $yi = 0;
+ for ($i = 0; $i < count($this->_edits); $i++) {
+ $orig = &$this->_edits[$i]->orig;
+ if (is_array($orig)) {
+ $orig = array_slice($from_lines, $xi, count($orig));
+ $xi += count($orig);
+ }
+
+ $final = &$this->_edits[$i]->final;
+ if (is_array($final)) {
+ $final = array_slice($to_lines, $yi, count($final));
+ $yi += count($final);
+ }
+ }
+ }
+
+}
+
+/**
+ * @package Text_Diff
+ * @author Geoffrey T. Dairiki <dairiki@dairiki.org>
+ *
+ * @access private
+ */
+class Text_Diff_Op {
+
+ var $orig;
+ var $final;
+
+ function &reverse()
+ {
+ trigger_error('Abstract method', E_USER_ERROR);
+ }
+
+ function norig()
+ {
+ return $this->orig ? count($this->orig) : 0;
+ }
+
+ function nfinal()
+ {
+ return $this->final ? count($this->final) : 0;
+ }
+
+}
+
+/**
+ * @package Text_Diff
+ * @author Geoffrey T. Dairiki <dairiki@dairiki.org>
+ *
+ * @access private
+ */
+class Text_Diff_Op_copy extends Text_Diff_Op {
+
+ function Text_Diff_Op_copy($orig, $final = false)
+ {
+ if (!is_array($final)) {
+ $final = $orig;
+ }
+ $this->orig = $orig;
+ $this->final = $final;
+ }
+
+ function &reverse()
+ {
+ $reverse = new Text_Diff_Op_copy($this->final, $this->orig);
+ return $reverse;
+ }
+
+}
+
+/**
+ * @package Text_Diff
+ * @author Geoffrey T. Dairiki <dairiki@dairiki.org>
+ *
+ * @access private
+ */
+class Text_Diff_Op_delete extends Text_Diff_Op {
+
+ function Text_Diff_Op_delete($lines)
+ {
+ $this->orig = $lines;
+ $this->final = false;
+ }
+
+ function &reverse()
+ {
+ $reverse = new Text_Diff_Op_add($this->orig);
+ return $reverse;
+ }
+
+}
+
+/**
+ * @package Text_Diff
+ * @author Geoffrey T. Dairiki <dairiki@dairiki.org>
+ *
+ * @access private
+ */
+class Text_Diff_Op_add extends Text_Diff_Op {
+
+ function Text_Diff_Op_add($lines)
+ {
+ $this->final = $lines;
+ $this->orig = false;
+ }
+
+ function &reverse()
+ {
+ $reverse = new Text_Diff_Op_delete($this->final);
+ return $reverse;
+ }
+
+}
+
+/**
+ * @package Text_Diff
+ * @author Geoffrey T. Dairiki <dairiki@dairiki.org>
+ *
+ * @access private
+ */
+class Text_Diff_Op_change extends Text_Diff_Op {
+
+ function Text_Diff_Op_change($orig, $final)
+ {
+ $this->orig = $orig;
+ $this->final = $final;
+ }
+
+ function &reverse()
+ {
+ $reverse = new Text_Diff_Op_change($this->final, $this->orig);
+ return $reverse;
+ }
+
+}
diff --git a/framework/gii/components/Pear/Text/Diff/Engine/native.php b/framework/gii/components/Pear/Text/Diff/Engine/native.php
new file mode 100644
index 0000000..410f849
--- /dev/null
+++ b/framework/gii/components/Pear/Text/Diff/Engine/native.php
@@ -0,0 +1,438 @@
+<?php
+/**
+ * Class used internally by Text_Diff to actually compute the diffs.
+ *
+ * This class is implemented using native PHP code.
+ *
+ * The algorithm used here is mostly lifted from the perl module
+ * Algorithm::Diff (version 1.06) by Ned Konz, which is available at:
+ * http://www.perl.com/CPAN/authors/id/N/NE/NEDKONZ/Algorithm-Diff-1.06.zip
+ *
+ * More ideas are taken from: http://www.ics.uci.edu/~eppstein/161/960229.html
+ *
+ * Some ideas (and a bit of code) are taken from analyze.c, of GNU
+ * diffutils-2.7, which can be found at:
+ * ftp://gnudist.gnu.org/pub/gnu/diffutils/diffutils-2.7.tar.gz
+ *
+ * Some ideas (subdivision by NCHUNKS > 2, and some optimizations) are from
+ * Geoffrey T. Dairiki <dairiki@dairiki.org>. The original PHP version of this
+ * code was written by him, and is used/adapted with his permission.
+ *
+ * $Horde: framework/Text_Diff/Diff/Engine/native.php,v 1.7.2.4 2008/01/04 10:38:10 jan Exp $
+ *
+ * Copyright 2004-2008 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you did
+ * not receive this file, see http://opensource.org/licenses/lgpl-license.php.
+ *
+ * @author Geoffrey T. Dairiki <dairiki@dairiki.org>
+ * @package Text_Diff
+ */
+class Text_Diff_Engine_native {
+
+ function diff($from_lines, $to_lines)
+ {
+ array_walk($from_lines, array('Text_Diff', 'trimNewlines'));
+ array_walk($to_lines, array('Text_Diff', 'trimNewlines'));
+
+ $n_from = count($from_lines);
+ $n_to = count($to_lines);
+
+ $this->xchanged = $this->ychanged = array();
+ $this->xv = $this->yv = array();
+ $this->xind = $this->yind = array();
+ unset($this->seq);
+ unset($this->in_seq);
+ unset($this->lcs);
+
+ // Skip leading common lines.
+ for ($skip = 0; $skip < $n_from && $skip < $n_to; $skip++) {
+ if ($from_lines[$skip] !== $to_lines[$skip]) {
+ break;
+ }
+ $this->xchanged[$skip] = $this->ychanged[$skip] = false;
+ }
+
+ // Skip trailing common lines.
+ $xi = $n_from; $yi = $n_to;
+ for ($endskip = 0; --$xi > $skip && --$yi > $skip; $endskip++) {
+ if ($from_lines[$xi] !== $to_lines[$yi]) {
+ break;
+ }
+ $this->xchanged[$xi] = $this->ychanged[$yi] = false;
+ }
+
+ // Ignore lines which do not exist in both files.
+ for ($xi = $skip; $xi < $n_from - $endskip; $xi++) {
+ $xhash[$from_lines[$xi]] = 1;
+ }
+ for ($yi = $skip; $yi < $n_to - $endskip; $yi++) {
+ $line = $to_lines[$yi];
+ if (($this->ychanged[$yi] = empty($xhash[$line]))) {
+ continue;
+ }
+ $yhash[$line] = 1;
+ $this->yv[] = $line;
+ $this->yind[] = $yi;
+ }
+ for ($xi = $skip; $xi < $n_from - $endskip; $xi++) {
+ $line = $from_lines[$xi];
+ if (($this->xchanged[$xi] = empty($yhash[$line]))) {
+ continue;
+ }
+ $this->xv[] = $line;
+ $this->xind[] = $xi;
+ }
+
+ // Find the LCS.
+ $this->_compareseq(0, count($this->xv), 0, count($this->yv));
+
+ // Merge edits when possible.
+ $this->_shiftBoundaries($from_lines, $this->xchanged, $this->ychanged);
+ $this->_shiftBoundaries($to_lines, $this->ychanged, $this->xchanged);
+
+ // Compute the edit operations.
+ $edits = array();
+ $xi = $yi = 0;
+ while ($xi < $n_from || $yi < $n_to) {
+ assert($yi < $n_to || $this->xchanged[$xi]);
+ assert($xi < $n_from || $this->ychanged[$yi]);
+
+ // Skip matching "snake".
+ $copy = array();
+ while ($xi < $n_from && $yi < $n_to
+ && !$this->xchanged[$xi] && !$this->ychanged[$yi]) {
+ $copy[] = $from_lines[$xi++];
+ ++$yi;
+ }
+ if ($copy) {
+ $edits[] = new Text_Diff_Op_copy($copy);
+ }
+
+ // Find deletes & adds.
+ $delete = array();
+ while ($xi < $n_from && $this->xchanged[$xi]) {
+ $delete[] = $from_lines[$xi++];
+ }
+
+ $add = array();
+ while ($yi < $n_to && $this->ychanged[$yi]) {
+ $add[] = $to_lines[$yi++];
+ }
+
+ if ($delete && $add) {
+ $edits[] = new Text_Diff_Op_change($delete, $add);
+ } elseif ($delete) {
+ $edits[] = new Text_Diff_Op_delete($delete);
+ } elseif ($add) {
+ $edits[] = new Text_Diff_Op_add($add);
+ }
+ }
+
+ return $edits;
+ }
+
+ /**
+ * Divides the Largest Common Subsequence (LCS) of the sequences (XOFF,
+ * XLIM) and (YOFF, YLIM) into NCHUNKS approximately equally sized
+ * segments.
+ *
+ * Returns (LCS, PTS). LCS is the length of the LCS. PTS is an array of
+ * NCHUNKS+1 (X, Y) indexes giving the diving points between sub
+ * sequences. The first sub-sequence is contained in (X0, X1), (Y0, Y1),
+ * the second in (X1, X2), (Y1, Y2) and so on. Note that (X0, Y0) ==
+ * (XOFF, YOFF) and (X[NCHUNKS], Y[NCHUNKS]) == (XLIM, YLIM).
+ *
+ * This function assumes that the first lines of the specified portions of
+ * the two files do not match, and likewise that the last lines do not
+ * match. The caller must trim matching lines from the beginning and end
+ * of the portions it is going to specify.
+ */
+ function _diag ($xoff, $xlim, $yoff, $ylim, $nchunks)
+ {
+ $flip = false;
+
+ if ($xlim - $xoff > $ylim - $yoff) {
+ /* Things seems faster (I'm not sure I understand why) when the
+ * shortest sequence is in X. */
+ $flip = true;
+ list ($xoff, $xlim, $yoff, $ylim)
+ = array($yoff, $ylim, $xoff, $xlim);
+ }
+
+ if ($flip) {
+ for ($i = $ylim - 1; $i >= $yoff; $i--) {
+ $ymatches[$this->xv[$i]][] = $i;
+ }
+ } else {
+ for ($i = $ylim - 1; $i >= $yoff; $i--) {
+ $ymatches[$this->yv[$i]][] = $i;
+ }
+ }
+
+ $this->lcs = 0;
+ $this->seq[0]= $yoff - 1;
+ $this->in_seq = array();
+ $ymids[0] = array();
+
+ $numer = $xlim - $xoff + $nchunks - 1;
+ $x = $xoff;
+ for ($chunk = 0; $chunk < $nchunks; $chunk++) {
+ if ($chunk > 0) {
+ for ($i = 0; $i <= $this->lcs; $i++) {
+ $ymids[$i][$chunk - 1] = $this->seq[$i];
+ }
+ }
+
+ $x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $chunk) / $nchunks);
+ for (; $x < $x1; $x++) {
+ $line = $flip ? $this->yv[$x] : $this->xv[$x];
+ if (empty($ymatches[$line])) {
+ continue;
+ }
+ $matches = $ymatches[$line];
+ reset($matches);
+ while (list(, $y) = each($matches)) {
+ if (empty($this->in_seq[$y])) {
+ $k = $this->_lcsPos($y);
+ assert($k > 0);
+ $ymids[$k] = $ymids[$k - 1];
+ break;
+ }
+ }
+ while (list(, $y) = each($matches)) {
+ if ($y > $this->seq[$k - 1]) {
+ assert($y <= $this->seq[$k]);
+ /* Optimization: this is a common case: next match is
+ * just replacing previous match. */
+ $this->in_seq[$this->seq[$k]] = false;
+ $this->seq[$k] = $y;
+ $this->in_seq[$y] = 1;
+ } elseif (empty($this->in_seq[$y])) {
+ $k = $this->_lcsPos($y);
+ assert($k > 0);
+ $ymids[$k] = $ymids[$k - 1];
+ }
+ }
+ }
+ }
+
+ $seps[] = $flip ? array($yoff, $xoff) : array($xoff, $yoff);
+ $ymid = $ymids[$this->lcs];
+ for ($n = 0; $n < $nchunks - 1; $n++) {
+ $x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $n) / $nchunks);
+ $y1 = $ymid[$n] + 1;
+ $seps[] = $flip ? array($y1, $x1) : array($x1, $y1);
+ }
+ $seps[] = $flip ? array($ylim, $xlim) : array($xlim, $ylim);
+
+ return array($this->lcs, $seps);
+ }
+
+ function _lcsPos($ypos)
+ {
+ $end = $this->lcs;
+ if ($end == 0 || $ypos > $this->seq[$end]) {
+ $this->seq[++$this->lcs] = $ypos;
+ $this->in_seq[$ypos] = 1;
+ return $this->lcs;
+ }
+
+ $beg = 1;
+ while ($beg < $end) {
+ $mid = (int)(($beg + $end) / 2);
+ if ($ypos > $this->seq[$mid]) {
+ $beg = $mid + 1;
+ } else {
+ $end = $mid;
+ }
+ }
+
+ assert($ypos != $this->seq[$end]);
+
+ $this->in_seq[$this->seq[$end]] = false;
+ $this->seq[$end] = $ypos;
+ $this->in_seq[$ypos] = 1;
+ return $end;
+ }
+
+ /**
+ * Finds LCS of two sequences.
+ *
+ * The results are recorded in the vectors $this->{x,y}changed[], by
+ * storing a 1 in the element for each line that is an insertion or
+ * deletion (ie. is not in the LCS).
+ *
+ * The subsequence of file 0 is (XOFF, XLIM) and likewise for file 1.
+ *
+ * Note that XLIM, YLIM are exclusive bounds. All line numbers are
+ * origin-0 and discarded lines are not counted.
+ */
+ function _compareseq ($xoff, $xlim, $yoff, $ylim)
+ {
+ /* Slide down the bottom initial diagonal. */
+ while ($xoff < $xlim && $yoff < $ylim
+ && $this->xv[$xoff] == $this->yv[$yoff]) {
+ ++$xoff;
+ ++$yoff;
+ }
+
+ /* Slide up the top initial diagonal. */
+ while ($xlim > $xoff && $ylim > $yoff
+ && $this->xv[$xlim - 1] == $this->yv[$ylim - 1]) {
+ --$xlim;
+ --$ylim;
+ }
+
+ if ($xoff == $xlim || $yoff == $ylim) {
+ $lcs = 0;
+ } else {
+ /* This is ad hoc but seems to work well. $nchunks =
+ * sqrt(min($xlim - $xoff, $ylim - $yoff) / 2.5); $nchunks =
+ * max(2,min(8,(int)$nchunks)); */
+ $nchunks = min(7, $xlim - $xoff, $ylim - $yoff) + 1;
+ list($lcs, $seps)
+ = $this->_diag($xoff, $xlim, $yoff, $ylim, $nchunks);
+ }
+
+ if ($lcs == 0) {
+ /* X and Y sequences have no common subsequence: mark all
+ * changed. */
+ while ($yoff < $ylim) {
+ $this->ychanged[$this->yind[$yoff++]] = 1;
+ }
+ while ($xoff < $xlim) {
+ $this->xchanged[$this->xind[$xoff++]] = 1;
+ }
+ } else {
+ /* Use the partitions to split this problem into subproblems. */
+ reset($seps);
+ $pt1 = $seps[0];
+ while ($pt2 = next($seps)) {
+ $this->_compareseq ($pt1[0], $pt2[0], $pt1[1], $pt2[1]);
+ $pt1 = $pt2;
+ }
+ }
+ }
+
+ /**
+ * Adjusts inserts/deletes of identical lines to join changes as much as
+ * possible.
+ *
+ * We do something when a run of changed lines include a line at one end
+ * and has an excluded, identical line at the other. We are free to
+ * choose which identical line is included. `compareseq' usually chooses
+ * the one at the beginning, but usually it is cleaner to consider the
+ * following identical line to be the "change".
+ *
+ * This is extracted verbatim from analyze.c (GNU diffutils-2.7).
+ */
+ function _shiftBoundaries($lines, &$changed, $other_changed)
+ {
+ $i = 0;
+ $j = 0;
+
+ assert('count($lines) == count($changed)');
+ $len = count($lines);
+ $other_len = count($other_changed);
+
+ while (1) {
+ /* Scan forward to find the beginning of another run of
+ * changes. Also keep track of the corresponding point in the
+ * other file.
+ *
+ * Throughout this code, $i and $j are adjusted together so that
+ * the first $i elements of $changed and the first $j elements of
+ * $other_changed both contain the same number of zeros (unchanged
+ * lines).
+ *
+ * Furthermore, $j is always kept so that $j == $other_len or
+ * $other_changed[$j] == false. */
+ while ($j < $other_len && $other_changed[$j]) {
+ $j++;
+ }
+
+ while ($i < $len && ! $changed[$i]) {
+ assert('$j < $other_len && ! $other_changed[$j]');
+ $i++; $j++;
+ while ($j < $other_len && $other_changed[$j]) {
+ $j++;
+ }
+ }
+
+ if ($i == $len) {
+ break;
+ }
+
+ $start = $i;
+
+ /* Find the end of this run of changes. */
+ while (++$i < $len && $changed[$i]) {
+ continue;
+ }
+
+ do {
+ /* Record the length of this run of changes, so that we can
+ * later determine whether the run has grown. */
+ $runlength = $i - $start;
+
+ /* Move the changed region back, so long as the previous
+ * unchanged line matches the last changed one. This merges
+ * with previous changed regions. */
+ while ($start > 0 && $lines[$start - 1] == $lines[$i - 1]) {
+ $changed[--$start] = 1;
+ $changed[--$i] = false;
+ while ($start > 0 && $changed[$start - 1]) {
+ $start--;
+ }
+ assert('$j > 0');
+ while ($other_changed[--$j]) {
+ continue;
+ }
+ assert('$j >= 0 && !$other_changed[$j]');
+ }
+
+ /* Set CORRESPONDING to the end of the changed run, at the
+ * last point where it corresponds to a changed run in the
+ * other file. CORRESPONDING == LEN means no such point has
+ * been found. */
+ $corresponding = $j < $other_len ? $i : $len;
+
+ /* Move the changed region forward, so long as the first
+ * changed line matches the following unchanged one. This
+ * merges with following changed regions. Do this second, so
+ * that if there are no merges, the changed region is moved
+ * forward as far as possible. */
+ while ($i < $len && $lines[$start] == $lines[$i]) {
+ $changed[$start++] = false;
+ $changed[$i++] = 1;
+ while ($i < $len && $changed[$i]) {
+ $i++;
+ }
+
+ assert('$j < $other_len && ! $other_changed[$j]');
+ $j++;
+ if ($j < $other_len && $other_changed[$j]) {
+ $corresponding = $i;
+ while ($j < $other_len && $other_changed[$j]) {
+ $j++;
+ }
+ }
+ }
+ } while ($runlength != $i - $start);
+
+ /* If possible, move the fully-merged run of changes back to a
+ * corresponding run in the other file. */
+ while ($corresponding < $i) {
+ $changed[--$start] = 1;
+ $changed[--$i] = 0;
+ assert('$j > 0');
+ while ($other_changed[--$j]) {
+ continue;
+ }
+ assert('$j >= 0 && !$other_changed[$j]');
+ }
+ }
+ }
+
+}
diff --git a/framework/gii/components/Pear/Text/Diff/Engine/shell.php b/framework/gii/components/Pear/Text/Diff/Engine/shell.php
new file mode 100644
index 0000000..f1aaa98
--- /dev/null
+++ b/framework/gii/components/Pear/Text/Diff/Engine/shell.php
@@ -0,0 +1,162 @@
+<?php
+/**
+ * Class used internally by Diff to actually compute the diffs.
+ *
+ * This class uses the Unix `diff` program via shell_exec to compute the
+ * differences between the two input arrays.
+ *
+ * $Horde: framework/Text_Diff/Diff/Engine/shell.php,v 1.6.2.3 2008/01/04 10:37:27 jan Exp $
+ *
+ * Copyright 2007-2008 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you did
+ * not receive this file, see http://opensource.org/licenses/lgpl-license.php.
+ *
+ * @author Milian Wolff <mail@milianw.de>
+ * @package Text_Diff
+ * @since 0.3.0
+ */
+class Text_Diff_Engine_shell {
+
+ /**
+ * Path to the diff executable
+ *
+ * @var string
+ */
+ var $_diffCommand = 'diff';
+
+ /**
+ * Returns the array of differences.
+ *
+ * @param array $from_lines lines of text from old file
+ * @param array $to_lines lines of text from new file
+ *
+ * @return array all changes made (array with Text_Diff_Op_* objects)
+ */
+ function diff($from_lines, $to_lines)
+ {
+ array_walk($from_lines, array('Text_Diff', 'trimNewlines'));
+ array_walk($to_lines, array('Text_Diff', 'trimNewlines'));
+
+ $temp_dir = Text_Diff::_getTempDir();
+
+ // Execute gnu diff or similar to get a standard diff file.
+ $from_file = tempnam($temp_dir, 'Text_Diff');
+ $to_file = tempnam($temp_dir, 'Text_Diff');
+ $fp = fopen($from_file, 'w');
+ fwrite($fp, implode("\n", $from_lines));
+ fclose($fp);
+ $fp = fopen($to_file, 'w');
+ fwrite($fp, implode("\n", $to_lines));
+ fclose($fp);
+ $diff = shell_exec($this->_diffCommand . ' ' . $from_file . ' ' . $to_file);
+ unlink($from_file);
+ unlink($to_file);
+
+ if (is_null($diff)) {
+ // No changes were made
+ return array(new Text_Diff_Op_copy($from_lines));
+ }
+
+ $from_line_no = 1;
+ $to_line_no = 1;
+ $edits = array();
+
+ // Get changed lines by parsing something like:
+ // 0a1,2
+ // 1,2c4,6
+ // 1,5d6
+ preg_match_all('#^(\d+)(?:,(\d+))?([adc])(\d+)(?:,(\d+))?$#m', $diff,
+ $matches, PREG_SET_ORDER);
+
+ foreach ($matches as $match) {
+ if (!isset($match[5])) {
+ // This paren is not set every time (see regex).
+ $match[5] = false;
+ }
+
+ if ($match[3] == 'a') {
+ $from_line_no--;
+ }
+
+ if ($match[3] == 'd') {
+ $to_line_no--;
+ }
+
+ if ($from_line_no < $match[1] || $to_line_no < $match[4]) {
+ // copied lines
+ assert('$match[1] - $from_line_no == $match[4] - $to_line_no');
+ array_push($edits,
+ new Text_Diff_Op_copy(
+ $this->_getLines($from_lines, $from_line_no, $match[1] - 1),
+ $this->_getLines($to_lines, $to_line_no, $match[4] - 1)));
+ }
+
+ switch ($match[3]) {
+ case 'd':
+ // deleted lines
+ array_push($edits,
+ new Text_Diff_Op_delete(
+ $this->_getLines($from_lines, $from_line_no, $match[2])));
+ $to_line_no++;
+ break;
+
+ case 'c':
+ // changed lines
+ array_push($edits,
+ new Text_Diff_Op_change(
+ $this->_getLines($from_lines, $from_line_no, $match[2]),
+ $this->_getLines($to_lines, $to_line_no, $match[5])));
+ break;
+
+ case 'a':
+ // added lines
+ array_push($edits,
+ new Text_Diff_Op_add(
+ $this->_getLines($to_lines, $to_line_no, $match[5])));
+ $from_line_no++;
+ break;
+ }
+ }
+
+ if (!empty($from_lines)) {
+ // Some lines might still be pending. Add them as copied
+ array_push($edits,
+ new Text_Diff_Op_copy(
+ $this->_getLines($from_lines, $from_line_no,
+ $from_line_no + count($from_lines) - 1),
+ $this->_getLines($to_lines, $to_line_no,
+ $to_line_no + count($to_lines) - 1)));
+ }
+
+ return $edits;
+ }
+
+ /**
+ * Get lines from either the old or new text
+ *
+ * @access private
+ *
+ * @param array &$text_lines Either $from_lines or $to_lines
+ * @param integer &$line_no Current line number
+ * @param integer $end Optional end line, when we want to chop more than one line.
+ * @return array The chopped lines
+ */
+ function _getLines(&$text_lines, &$line_no, $end = false)
+ {
+ if (!empty($end)) {
+ $lines = array();
+ // We can shift even more
+ while ($line_no <= $end) {
+ array_push($lines, array_shift($text_lines));
+ $line_no++;
+ }
+ } else {
+ $lines = array(array_shift($text_lines));
+ $line_no++;
+ }
+
+ return $lines;
+ }
+
+}
diff --git a/framework/gii/components/Pear/Text/Diff/Engine/string.php b/framework/gii/components/Pear/Text/Diff/Engine/string.php
new file mode 100644
index 0000000..4b29daa
--- /dev/null
+++ b/framework/gii/components/Pear/Text/Diff/Engine/string.php
@@ -0,0 +1,237 @@
+<?php
+/**
+ * Parses unified or context diffs output from eg. the diff utility.
+ *
+ * Example:
+ * <code>
+ * $patch = file_get_contents('example.patch');
+ * $diff = new Text_Diff('string', array($patch));
+ * $renderer = new Text_Diff_Renderer_inline();
+ * echo $renderer->render($diff);
+ * </code>
+ *
+ * $Horde: framework/Text_Diff/Diff/Engine/string.php,v 1.5.2.5 2008/09/10 08:31:58 jan Exp $
+ *
+ * Copyright 2005 Örjan Persson <o@42mm.org>
+ * Copyright 2005-2008 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you did
+ * not receive this file, see http://opensource.org/licenses/lgpl-license.php.
+ *
+ * @author Örjan Persson <o@42mm.org>
+ * @package Text_Diff
+ * @since 0.2.0
+ */
+class Text_Diff_Engine_string {
+
+ /**
+ * Parses a unified or context diff.
+ *
+ * First param contains the whole diff and the second can be used to force
+ * a specific diff type. If the second parameter is 'autodetect', the
+ * diff will be examined to find out which type of diff this is.
+ *
+ * @param string $diff The diff content.
+ * @param string $mode The diff mode of the content in $diff. One of
+ * 'context', 'unified', or 'autodetect'.
+ *
+ * @return array List of all diff operations.
+ */
+ function diff($diff, $mode = 'autodetect')
+ {
+ if ($mode != 'autodetect' && $mode != 'context' && $mode != 'unified') {
+ return PEAR::raiseError('Type of diff is unsupported');
+ }
+
+ if ($mode == 'autodetect') {
+ $context = strpos($diff, '***');
+ $unified = strpos($diff, '---');
+ if ($context === $unified) {
+ return PEAR::raiseError('Type of diff could not be detected');
+ } elseif ($context === false || $unified === false) {
+ $mode = $context !== false ? 'context' : 'unified';
+ } else {
+ $mode = $context < $unified ? 'context' : 'unified';
+ }
+ }
+
+ // Split by new line and remove the diff header, if there is one.
+ $diff = explode("\n", $diff);
+ if (($mode == 'context' && strpos($diff[0], '***') === 0) ||
+ ($mode == 'unified' && strpos($diff[0], '---') === 0)) {
+ array_shift($diff);
+ array_shift($diff);
+ }
+
+ if ($mode == 'context') {
+ return $this->parseContextDiff($diff);
+ } else {
+ return $this->parseUnifiedDiff($diff);
+ }
+ }
+
+ /**
+ * Parses an array containing the unified diff.
+ *
+ * @param array $diff Array of lines.
+ *
+ * @return array List of all diff operations.
+ */
+ function parseUnifiedDiff($diff)
+ {
+ $edits = array();
+ $end = count($diff) - 1;
+ for ($i = 0; $i < $end;) {
+ $diff1 = array();
+ switch (substr($diff[$i], 0, 1)) {
+ case ' ':
+ do {
+ $diff1[] = substr($diff[$i], 1);
+ } while (++$i < $end && substr($diff[$i], 0, 1) == ' ');
+ $edits[] = new Text_Diff_Op_copy($diff1);
+ break;
+
+ case '+':
+ // get all new lines
+ do {
+ $diff1[] = substr($diff[$i], 1);
+ } while (++$i < $end && substr($diff[$i], 0, 1) == '+');
+ $edits[] = new Text_Diff_Op_add($diff1);
+ break;
+
+ case '-':
+ // get changed or removed lines
+ $diff2 = array();
+ do {
+ $diff1[] = substr($diff[$i], 1);
+ } while (++$i < $end && substr($diff[$i], 0, 1) == '-');
+
+ while ($i < $end && substr($diff[$i], 0, 1) == '+') {
+ $diff2[] = substr($diff[$i++], 1);
+ }
+ if (count($diff2) == 0) {
+ $edits[] = new Text_Diff_Op_delete($diff1);
+ } else {
+ $edits[] = new Text_Diff_Op_change($diff1, $diff2);
+ }
+ break;
+
+ default:
+ $i++;
+ break;
+ }
+ }
+
+ return $edits;
+ }
+
+ /**
+ * Parses an array containing the context diff.
+ *
+ * @param array $diff Array of lines.
+ *
+ * @return array List of all diff operations.
+ */
+ function parseContextDiff(&$diff)
+ {
+ $edits = array();
+ $i = $max_i = $j = $max_j = 0;
+ $end = count($diff) - 1;
+ while ($i < $end && $j < $end) {
+ while ($i >= $max_i && $j >= $max_j) {
+ // Find the boundaries of the diff output of the two files
+ for ($i = $j;
+ $i < $end && substr($diff[$i], 0, 3) == '***';
+ $i++);
+ for ($max_i = $i;
+ $max_i < $end && substr($diff[$max_i], 0, 3) != '---';
+ $max_i++);
+ for ($j = $max_i;
+ $j < $end && substr($diff[$j], 0, 3) == '---';
+ $j++);
+ for ($max_j = $j;
+ $max_j < $end && substr($diff[$max_j], 0, 3) != '***';
+ $max_j++);
+ }
+
+ // find what hasn't been changed
+ $array = array();
+ while ($i < $max_i &&
+ $j < $max_j &&
+ strcmp($diff[$i], $diff[$j]) == 0) {
+ $array[] = substr($diff[$i], 2);
+ $i++;
+ $j++;
+ }
+
+ while ($i < $max_i && ($max_j-$j) <= 1) {
+ if ($diff[$i] != '' && substr($diff[$i], 0, 1) != ' ') {
+ break;
+ }
+ $array[] = substr($diff[$i++], 2);
+ }
+
+ while ($j < $max_j && ($max_i-$i) <= 1) {
+ if ($diff[$j] != '' && substr($diff[$j], 0, 1) != ' ') {
+ break;
+ }
+ $array[] = substr($diff[$j++], 2);
+ }
+ if (count($array) > 0) {
+ $edits[] = new Text_Diff_Op_copy($array);
+ }
+
+ if ($i < $max_i) {
+ $diff1 = array();
+ switch (substr($diff[$i], 0, 1)) {
+ case '!':
+ $diff2 = array();
+ do {
+ $diff1[] = substr($diff[$i], 2);
+ if ($j < $max_j && substr($diff[$j], 0, 1) == '!') {
+ $diff2[] = substr($diff[$j++], 2);
+ }
+ } while (++$i < $max_i && substr($diff[$i], 0, 1) == '!');
+ $edits[] = new Text_Diff_Op_change($diff1, $diff2);
+ break;
+
+ case '+':
+ do {
+ $diff1[] = substr($diff[$i], 2);
+ } while (++$i < $max_i && substr($diff[$i], 0, 1) == '+');
+ $edits[] = new Text_Diff_Op_add($diff1);
+ break;
+
+ case '-':
+ do {
+ $diff1[] = substr($diff[$i], 2);
+ } while (++$i < $max_i && substr($diff[$i], 0, 1) == '-');
+ $edits[] = new Text_Diff_Op_delete($diff1);
+ break;
+ }
+ }
+
+ if ($j < $max_j) {
+ $diff2 = array();
+ switch (substr($diff[$j], 0, 1)) {
+ case '+':
+ do {
+ $diff2[] = substr($diff[$j++], 2);
+ } while ($j < $max_j && substr($diff[$j], 0, 1) == '+');
+ $edits[] = new Text_Diff_Op_add($diff2);
+ break;
+
+ case '-':
+ do {
+ $diff2[] = substr($diff[$j++], 2);
+ } while ($j < $max_j && substr($diff[$j], 0, 1) == '-');
+ $edits[] = new Text_Diff_Op_delete($diff2);
+ break;
+ }
+ }
+ }
+
+ return $edits;
+ }
+
+}
diff --git a/framework/gii/components/Pear/Text/Diff/Engine/xdiff.php b/framework/gii/components/Pear/Text/Diff/Engine/xdiff.php
new file mode 100644
index 0000000..681ea4c
--- /dev/null
+++ b/framework/gii/components/Pear/Text/Diff/Engine/xdiff.php
@@ -0,0 +1,63 @@
+<?php
+/**
+ * Class used internally by Diff to actually compute the diffs.
+ *
+ * This class uses the xdiff PECL package (http://pecl.php.net/package/xdiff)
+ * to compute the differences between the two input arrays.
+ *
+ * $Horde: framework/Text_Diff/Diff/Engine/xdiff.php,v 1.4.2.3 2008/01/04 10:37:27 jan Exp $
+ *
+ * Copyright 2004-2008 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you did
+ * not receive this file, see http://opensource.org/licenses/lgpl-license.php.
+ *
+ * @author Jon Parise <jon@horde.org>
+ * @package Text_Diff
+ */
+class Text_Diff_Engine_xdiff {
+
+ /**
+ */
+ function diff($from_lines, $to_lines)
+ {
+ array_walk($from_lines, array('Text_Diff', 'trimNewlines'));
+ array_walk($to_lines, array('Text_Diff', 'trimNewlines'));
+
+ /* Convert the two input arrays into strings for xdiff processing. */
+ $from_string = implode("\n", $from_lines);
+ $to_string = implode("\n", $to_lines);
+
+ /* Diff the two strings and convert the result to an array. */
+ $diff = xdiff_string_diff($from_string, $to_string, count($to_lines));
+ $diff = explode("\n", $diff);
+
+ /* Walk through the diff one line at a time. We build the $edits
+ * array of diff operations by reading the first character of the
+ * xdiff output (which is in the "unified diff" format).
+ *
+ * Note that we don't have enough information to detect "changed"
+ * lines using this approach, so we can't add Text_Diff_Op_changed
+ * instances to the $edits array. The result is still perfectly
+ * valid, albeit a little less descriptive and efficient. */
+ $edits = array();
+ foreach ($diff as $line) {
+ switch ($line[0]) {
+ case ' ':
+ $edits[] = new Text_Diff_Op_copy(array(substr($line, 1)));
+ break;
+
+ case '+':
+ $edits[] = new Text_Diff_Op_add(array(substr($line, 1)));
+ break;
+
+ case '-':
+ $edits[] = new Text_Diff_Op_delete(array(substr($line, 1)));
+ break;
+ }
+ }
+
+ return $edits;
+ }
+
+}
diff --git a/framework/gii/components/Pear/Text/Diff/Mapped.php b/framework/gii/components/Pear/Text/Diff/Mapped.php
new file mode 100644
index 0000000..8403759
--- /dev/null
+++ b/framework/gii/components/Pear/Text/Diff/Mapped.php
@@ -0,0 +1,55 @@
+<?php
+/**
+ * $Horde: framework/Text_Diff/Diff/Mapped.php,v 1.3.2.3 2008/01/04 10:37:27 jan Exp $
+ *
+ * Copyright 2007-2008 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you did
+ * not receive this file, see http://opensource.org/licenses/lgpl-license.php.
+ *
+ * @package Text_Diff
+ * @author Geoffrey T. Dairiki <dairiki@dairiki.org>
+ */
+class Text_Diff_Mapped extends Text_Diff {
+
+ /**
+ * Computes a diff between sequences of strings.
+ *
+ * This can be used to compute things like case-insensitve diffs, or diffs
+ * which ignore changes in white-space.
+ *
+ * @param array $from_lines An array of strings.
+ * @param array $to_lines An array of strings.
+ * @param array $mapped_from_lines This array should have the same size
+ * number of elements as $from_lines. The
+ * elements in $mapped_from_lines and
+ * $mapped_to_lines are what is actually
+ * compared when computing the diff.
+ * @param array $mapped_to_lines This array should have the same number
+ * of elements as $to_lines.
+ */
+ function Text_Diff_Mapped($from_lines, $to_lines,
+ $mapped_from_lines, $mapped_to_lines)
+ {
+ assert(count($from_lines) == count($mapped_from_lines));
+ assert(count($to_lines) == count($mapped_to_lines));
+
+ parent::Text_Diff($mapped_from_lines, $mapped_to_lines);
+
+ $xi = $yi = 0;
+ for ($i = 0; $i < count($this->_edits); $i++) {
+ $orig = &$this->_edits[$i]->orig;
+ if (is_array($orig)) {
+ $orig = array_slice($from_lines, $xi, count($orig));
+ $xi += count($orig);
+ }
+
+ $final = &$this->_edits[$i]->final;
+ if (is_array($final)) {
+ $final = array_slice($to_lines, $yi, count($final));
+ $yi += count($final);
+ }
+ }
+ }
+
+}
diff --git a/framework/gii/components/Pear/Text/Diff/Renderer.php b/framework/gii/components/Pear/Text/Diff/Renderer.php
new file mode 100644
index 0000000..c0e2395
--- /dev/null
+++ b/framework/gii/components/Pear/Text/Diff/Renderer.php
@@ -0,0 +1,237 @@
+<?php
+/**
+ * A class to render Diffs in different formats.
+ *
+ * This class renders the diff in classic diff format. It is intended that
+ * this class be customized via inheritance, to obtain fancier outputs.
+ *
+ * $Horde: framework/Text_Diff/Diff/Renderer.php,v 1.5.10.10 2008/01/04 10:37:27 jan Exp $
+ *
+ * Copyright 2004-2008 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you did
+ * not receive this file, see http://opensource.org/licenses/lgpl-license.php.
+ *
+ * @package Text_Diff
+ */
+class Text_Diff_Renderer {
+
+ /**
+ * Number of leading context "lines" to preserve.
+ *
+ * This should be left at zero for this class, but subclasses may want to
+ * set this to other values.
+ */
+ var $_leading_context_lines = 0;
+
+ /**
+ * Number of trailing context "lines" to preserve.
+ *
+ * This should be left at zero for this class, but subclasses may want to
+ * set this to other values.
+ */
+ var $_trailing_context_lines = 0;
+
+ /**
+ * Constructor.
+ */
+ function Text_Diff_Renderer($params = array())
+ {
+ foreach ($params as $param => $value) {
+ $v = '_' . $param;
+ if (isset($this->$v)) {
+ $this->$v = $value;
+ }
+ }
+ }
+
+ /**
+ * Get any renderer parameters.
+ *
+ * @return array All parameters of this renderer object.
+ */
+ function getParams()
+ {
+ $params = array();
+ foreach (get_object_vars($this) as $k => $v) {
+ if ($k[0] == '_') {
+ $params[substr($k, 1)] = $v;
+ }
+ }
+
+ return $params;
+ }
+
+ /**
+ * Renders a diff.
+ *
+ * @param Text_Diff $diff A Text_Diff object.
+ *
+ * @return string The formatted output.
+ */
+ function render($diff)
+ {
+ $xi = $yi = 1;
+ $block = false;
+ $context = array();
+
+ $nlead = $this->_leading_context_lines;
+ $ntrail = $this->_trailing_context_lines;
+
+ $output = $this->_startDiff();
+
+ $diffs = $diff->getDiff();
+ foreach ($diffs as $i => $edit) {
+ /* If these are unchanged (copied) lines, and we want to keep
+ * leading or trailing context lines, extract them from the copy
+ * block. */
+ if (is_a($edit, 'Text_Diff_Op_copy')) {
+ /* Do we have any diff blocks yet? */
+ if (is_array($block)) {
+ /* How many lines to keep as context from the copy
+ * block. */
+ $keep = $i == count($diffs) - 1 ? $ntrail : $nlead + $ntrail;
+ if (count($edit->orig) <= $keep) {
+ /* We have less lines in the block than we want for
+ * context => keep the whole block. */
+ $block[] = $edit;
+ } else {
+ if ($ntrail) {
+ /* Create a new block with as many lines as we need
+ * for the trailing context. */
+ $context = array_slice($edit->orig, 0, $ntrail);
+ $block[] = new Text_Diff_Op_copy($context);
+ }
+ /* @todo */
+ $output .= $this->_block($x0, $ntrail + $xi - $x0,
+ $y0, $ntrail + $yi - $y0,
+ $block);
+ $block = false;
+ }
+ }
+ /* Keep the copy block as the context for the next block. */
+ $context = $edit->orig;
+ } else {
+ /* Don't we have any diff blocks yet? */
+ if (!is_array($block)) {
+ /* Extract context lines from the preceding copy block. */
+ $context = array_slice($context, count($context) - $nlead);
+ $x0 = $xi - count($context);
+ $y0 = $yi - count($context);
+ $block = array();
+ if ($context) {
+ $block[] = new Text_Diff_Op_copy($context);
+ }
+ }
+ $block[] = $edit;
+ }
+
+ if ($edit->orig) {
+ $xi += count($edit->orig);
+ }
+ if ($edit->final) {
+ $yi += count($edit->final);
+ }
+ }
+
+ if (is_array($block)) {
+ $output .= $this->_block($x0, $xi - $x0,
+ $y0, $yi - $y0,
+ $block);
+ }
+
+ return $output . $this->_endDiff();
+ }
+
+ function _block($xbeg, $xlen, $ybeg, $ylen, &$edits)
+ {
+ $output = $this->_startBlock($this->_blockHeader($xbeg, $xlen, $ybeg, $ylen));
+
+ foreach ($edits as $edit) {
+ switch (strtolower(get_class($edit))) {
+ case 'text_diff_op_copy':
+ $output .= $this->_context($edit->orig);
+ break;
+
+ case 'text_diff_op_add':
+ $output .= $this->_added($edit->final);
+ break;
+
+ case 'text_diff_op_delete':
+ $output .= $this->_deleted($edit->orig);
+ break;
+
+ case 'text_diff_op_change':
+ $output .= $this->_changed($edit->orig, $edit->final);
+ break;
+ }
+ }
+
+ return $output . $this->_endBlock();
+ }
+
+ function _startDiff()
+ {
+ return '';
+ }
+
+ function _endDiff()
+ {
+ return '';
+ }
+
+ function _blockHeader($xbeg, $xlen, $ybeg, $ylen)
+ {
+ if ($xlen > 1) {
+ $xbeg .= ',' . ($xbeg + $xlen - 1);
+ }
+ if ($ylen > 1) {
+ $ybeg .= ',' . ($ybeg + $ylen - 1);
+ }
+
+ // this matches the GNU Diff behaviour
+ if ($xlen && !$ylen) {
+ $ybeg--;
+ } elseif (!$xlen) {
+ $xbeg--;
+ }
+
+ return $xbeg . ($xlen ? ($ylen ? 'c' : 'd') : 'a') . $ybeg;
+ }
+
+ function _startBlock($header)
+ {
+ return $header . "\n";
+ }
+
+ function _endBlock()
+ {
+ return '';
+ }
+
+ function _lines($lines, $prefix = ' ')
+ {
+ return $prefix . implode("\n$prefix", $lines) . "\n";
+ }
+
+ function _context($lines)
+ {
+ return $this->_lines($lines, ' ');
+ }
+
+ function _added($lines)
+ {
+ return $this->_lines($lines, '> ');
+ }
+
+ function _deleted($lines)
+ {
+ return $this->_lines($lines, '< ');
+ }
+
+ function _changed($orig, $final)
+ {
+ return $this->_deleted($orig) . "---\n" . $this->_added($final);
+ }
+
+}
diff --git a/framework/gii/components/Pear/Text/Diff/Renderer/context.php b/framework/gii/components/Pear/Text/Diff/Renderer/context.php
new file mode 100644
index 0000000..7977500
--- /dev/null
+++ b/framework/gii/components/Pear/Text/Diff/Renderer/context.php
@@ -0,0 +1,77 @@
+<?php
+/**
+ * "Context" diff renderer.
+ *
+ * This class renders the diff in classic "context diff" format.
+ *
+ * $Horde: framework/Text_Diff/Diff/Renderer/context.php,v 1.3.2.3 2008/01/04 10:37:27 jan Exp $
+ *
+ * Copyright 2004-2008 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you did
+ * not receive this file, see http://opensource.org/licenses/lgpl-license.php.
+ *
+ * @package Text_Diff
+ */
+
+/** Text_Diff_Renderer */
+require_once 'Text/Diff/Renderer.php';
+
+/**
+ * @package Text_Diff
+ */
+class Text_Diff_Renderer_context extends Text_Diff_Renderer {
+
+ /**
+ * Number of leading context "lines" to preserve.
+ */
+ var $_leading_context_lines = 4;
+
+ /**
+ * Number of trailing context "lines" to preserve.
+ */
+ var $_trailing_context_lines = 4;
+
+ var $_second_block = '';
+
+ function _blockHeader($xbeg, $xlen, $ybeg, $ylen)
+ {
+ if ($xlen != 1) {
+ $xbeg .= ',' . $xlen;
+ }
+ if ($ylen != 1) {
+ $ybeg .= ',' . $ylen;
+ }
+ $this->_second_block = "--- $ybeg ----\n";
+ return "***************\n*** $xbeg ****";
+ }
+
+ function _endBlock()
+ {
+ return $this->_second_block;
+ }
+
+ function _context($lines)
+ {
+ $this->_second_block .= $this->_lines($lines, ' ');
+ return $this->_lines($lines, ' ');
+ }
+
+ function _added($lines)
+ {
+ $this->_second_block .= $this->_lines($lines, '+ ');
+ return '';
+ }
+
+ function _deleted($lines)
+ {
+ return $this->_lines($lines, '- ');
+ }
+
+ function _changed($orig, $final)
+ {
+ $this->_second_block .= $this->_lines($final, '! ');
+ return $this->_lines($orig, '! ');
+ }
+
+}
diff --git a/framework/gii/components/Pear/Text/Diff/Renderer/inline.php b/framework/gii/components/Pear/Text/Diff/Renderer/inline.php
new file mode 100644
index 0000000..7f4e5ef
--- /dev/null
+++ b/framework/gii/components/Pear/Text/Diff/Renderer/inline.php
@@ -0,0 +1,170 @@
+<?php
+/**
+ * "Inline" diff renderer.
+ *
+ * $Horde: framework/Text_Diff/Diff/Renderer/inline.php,v 1.4.10.14 2008/01/04 10:37:27 jan Exp $
+ *
+ * Copyright 2004-2008 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you did
+ * not receive this file, see http://opensource.org/licenses/lgpl-license.php.
+ *
+ * @author Ciprian Popovici
+ * @package Text_Diff
+ */
+
+/** Text_Diff_Renderer */
+require_once 'Text/Diff/Renderer.php';
+
+/**
+ * "Inline" diff renderer.
+ *
+ * This class renders diffs in the Wiki-style "inline" format.
+ *
+ * @author Ciprian Popovici
+ * @package Text_Diff
+ */
+class Text_Diff_Renderer_inline extends Text_Diff_Renderer {
+
+ /**
+ * Number of leading context "lines" to preserve.
+ */
+ var $_leading_context_lines = 10000;
+
+ /**
+ * Number of trailing context "lines" to preserve.
+ */
+ var $_trailing_context_lines = 10000;
+
+ /**
+ * Prefix for inserted text.
+ */
+ var $_ins_prefix = '<ins>';
+
+ /**
+ * Suffix for inserted text.
+ */
+ var $_ins_suffix = '</ins>';
+
+ /**
+ * Prefix for deleted text.
+ */
+ var $_del_prefix = '<del>';
+
+ /**
+ * Suffix for deleted text.
+ */
+ var $_del_suffix = '</del>';
+
+ /**
+ * Header for each change block.
+ */
+ var $_block_header = '';
+
+ /**
+ * What are we currently splitting on? Used to recurse to show word-level
+ * changes.
+ */
+ var $_split_level = 'lines';
+
+ function _blockHeader($xbeg, $xlen, $ybeg, $ylen)
+ {
+ return $this->_block_header;
+ }
+
+ function _startBlock($header)
+ {
+ return $header;
+ }
+
+ function _lines($lines, $prefix = ' ', $encode = true)
+ {
+ if ($encode) {
+ array_walk($lines, array(&$this, '_encode'));
+ }
+
+ if ($this->_split_level == 'words') {
+ return implode('', $lines);
+ } else {
+ return implode("\n", $lines) . "\n";
+ }
+ }
+
+ function _added($lines)
+ {
+ array_walk($lines, array(&$this, '_encode'));
+ $lines[0] = $this->_ins_prefix . $lines[0];
+ $lines[count($lines) - 1] .= $this->_ins_suffix;
+ return $this->_lines($lines, ' ', false);
+ }
+
+ function _deleted($lines, $words = false)
+ {
+ array_walk($lines, array(&$this, '_encode'));
+ $lines[0] = $this->_del_prefix . $lines[0];
+ $lines[count($lines) - 1] .= $this->_del_suffix;
+ return $this->_lines($lines, ' ', false);
+ }
+
+ function _changed($orig, $final)
+ {
+ /* If we've already split on words, don't try to do so again - just
+ * display. */
+ if ($this->_split_level == 'words') {
+ $prefix = '';
+ while ($orig[0] !== false && $final[0] !== false &&
+ substr($orig[0], 0, 1) == ' ' &&
+ substr($final[0], 0, 1) == ' ') {
+ $prefix .= substr($orig[0], 0, 1);
+ $orig[0] = substr($orig[0], 1);
+ $final[0] = substr($final[0], 1);
+ }
+ return $prefix . $this->_deleted($orig) . $this->_added($final);
+ }
+
+ $text1 = implode("\n", $orig);
+ $text2 = implode("\n", $final);
+
+ /* Non-printing newline marker. */
+ $nl = "\0";
+
+ /* We want to split on word boundaries, but we need to
+ * preserve whitespace as well. Therefore we split on words,
+ * but include all blocks of whitespace in the wordlist. */
+ $diff = new Text_Diff($this->_splitOnWords($text1, $nl),
+ $this->_splitOnWords($text2, $nl));
+
+ /* Get the diff in inline format. */
+ $renderer = new Text_Diff_Renderer_inline(array_merge($this->getParams(),
+ array('split_level' => 'words')));
+
+ /* Run the diff and get the output. */
+ return str_replace($nl, "\n", $renderer->render($diff)) . "\n";
+ }
+
+ function _splitOnWords($string, $newlineEscape = "\n")
+ {
+ // Ignore \0; otherwise the while loop will never finish.
+ $string = str_replace("\0", '', $string);
+
+ $words = array();
+ $length = strlen($string);
+ $pos = 0;
+
+ while ($pos < $length) {
+ // Eat a word with any preceding whitespace.
+ $spaces = strspn(substr($string, $pos), " \n");
+ $nextpos = strcspn(substr($string, $pos + $spaces), " \n");
+ $words[] = str_replace("\n", $newlineEscape, substr($string, $pos, $spaces + $nextpos));
+ $pos += $spaces + $nextpos;
+ }
+
+ return $words;
+ }
+
+ function _encode(&$string)
+ {
+ $string = htmlspecialchars($string);
+ }
+
+}
diff --git a/framework/gii/components/Pear/Text/Diff/Renderer/unified.php b/framework/gii/components/Pear/Text/Diff/Renderer/unified.php
new file mode 100644
index 0000000..943d519
--- /dev/null
+++ b/framework/gii/components/Pear/Text/Diff/Renderer/unified.php
@@ -0,0 +1,67 @@
+<?php
+/**
+ * "Unified" diff renderer.
+ *
+ * This class renders the diff in classic "unified diff" format.
+ *
+ * $Horde: framework/Text_Diff/Diff/Renderer/unified.php,v 1.3.10.6 2008/01/04 10:37:27 jan Exp $
+ *
+ * Copyright 2004-2008 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you did
+ * not receive this file, see http://opensource.org/licenses/lgpl-license.php.
+ *
+ * @author Ciprian Popovici
+ * @package Text_Diff
+ */
+
+/** Text_Diff_Renderer */
+require_once 'Text/Diff/Renderer.php';
+
+/**
+ * @package Text_Diff
+ */
+class Text_Diff_Renderer_unified extends Text_Diff_Renderer {
+
+ /**
+ * Number of leading context "lines" to preserve.
+ */
+ var $_leading_context_lines = 4;
+
+ /**
+ * Number of trailing context "lines" to preserve.
+ */
+ var $_trailing_context_lines = 4;
+
+ function _blockHeader($xbeg, $xlen, $ybeg, $ylen)
+ {
+ if ($xlen != 1) {
+ $xbeg .= ',' . $xlen;
+ }
+ if ($ylen != 1) {
+ $ybeg .= ',' . $ylen;
+ }
+ return "@@ -$xbeg +$ybeg @@";
+ }
+
+ function _context($lines)
+ {
+ return $this->_lines($lines, ' ');
+ }
+
+ function _added($lines)
+ {
+ return $this->_lines($lines, '+');
+ }
+
+ function _deleted($lines)
+ {
+ return $this->_lines($lines, '-');
+ }
+
+ function _changed($orig, $final)
+ {
+ return $this->_deleted($orig) . $this->_added($final);
+ }
+
+}
diff --git a/framework/gii/components/Pear/Text/Diff/ThreeWay.php b/framework/gii/components/Pear/Text/Diff/ThreeWay.php
new file mode 100644
index 0000000..4e4b939
--- /dev/null
+++ b/framework/gii/components/Pear/Text/Diff/ThreeWay.php
@@ -0,0 +1,276 @@
+<?php
+/**
+ * A class for computing three way diffs.
+ *
+ * $Horde: framework/Text_Diff/Diff/ThreeWay.php,v 1.3.2.3 2008/01/04 10:37:27 jan Exp $
+ *
+ * Copyright 2007-2008 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you did
+ * not receive this file, see http://opensource.org/licenses/lgpl-license.php.
+ *
+ * @package Text_Diff
+ * @since 0.3.0
+ */
+
+/** Text_Diff */
+require_once 'Text/Diff.php';
+
+/**
+ * A class for computing three way diffs.
+ *
+ * @package Text_Diff
+ * @author Geoffrey T. Dairiki <dairiki@dairiki.org>
+ */
+class Text_Diff_ThreeWay extends Text_Diff {
+
+ /**
+ * Conflict counter.
+ *
+ * @var integer
+ */
+ var $_conflictingBlocks = 0;
+
+ /**
+ * Computes diff between 3 sequences of strings.
+ *
+ * @param array $orig The original lines to use.
+ * @param array $final1 The first version to compare to.
+ * @param array $final2 The second version to compare to.
+ */
+ function Text_Diff_ThreeWay($orig, $final1, $final2)
+ {
+ if (extension_loaded('xdiff')) {
+ $engine = new Text_Diff_Engine_xdiff();
+ } else {
+ $engine = new Text_Diff_Engine_native();
+ }
+
+ $this->_edits = $this->_diff3($engine->diff($orig, $final1),
+ $engine->diff($orig, $final2));
+ }
+
+ /**
+ */
+ function mergedOutput($label1 = false, $label2 = false)
+ {
+ $lines = array();
+ foreach ($this->_edits as $edit) {
+ if ($edit->isConflict()) {
+ /* FIXME: this should probably be moved somewhere else. */
+ $lines = array_merge($lines,
+ array('<<<<<<<' . ($label1 ? ' ' . $label1 : '')),
+ $edit->final1,
+ array("======="),
+ $edit->final2,
+ array('>>>>>>>' . ($label2 ? ' ' . $label2 : '')));
+ $this->_conflictingBlocks++;
+ } else {
+ $lines = array_merge($lines, $edit->merged());
+ }
+ }
+
+ return $lines;
+ }
+
+ /**
+ * @access private
+ */
+ function _diff3($edits1, $edits2)
+ {
+ $edits = array();
+ $bb = new Text_Diff_ThreeWay_BlockBuilder();
+
+ $e1 = current($edits1);
+ $e2 = current($edits2);
+ while ($e1 || $e2) {
+ if ($e1 && $e2 && is_a($e1, 'Text_Diff_Op_copy') && is_a($e2, 'Text_Diff_Op_copy')) {
+ /* We have copy blocks from both diffs. This is the (only)
+ * time we want to emit a diff3 copy block. Flush current
+ * diff3 diff block, if any. */
+ if ($edit = $bb->finish()) {
+ $edits[] = $edit;
+ }
+
+ $ncopy = min($e1->norig(), $e2->norig());
+ assert($ncopy > 0);
+ $edits[] = new Text_Diff_ThreeWay_Op_copy(array_slice($e1->orig, 0, $ncopy));
+
+ if ($e1->norig() > $ncopy) {
+ array_splice($e1->orig, 0, $ncopy);
+ array_splice($e1->final, 0, $ncopy);
+ } else {
+ $e1 = next($edits1);
+ }
+
+ if ($e2->norig() > $ncopy) {
+ array_splice($e2->orig, 0, $ncopy);
+ array_splice($e2->final, 0, $ncopy);
+ } else {
+ $e2 = next($edits2);
+ }
+ } else {
+ if ($e1 && $e2) {
+ if ($e1->orig && $e2->orig) {
+ $norig = min($e1->norig(), $e2->norig());
+ $orig = array_splice($e1->orig, 0, $norig);
+ array_splice($e2->orig, 0, $norig);
+ $bb->input($orig);
+ }
+
+ if (is_a($e1, 'Text_Diff_Op_copy')) {
+ $bb->out1(array_splice($e1->final, 0, $norig));
+ }
+
+ if (is_a($e2, 'Text_Diff_Op_copy')) {
+ $bb->out2(array_splice($e2->final, 0, $norig));
+ }
+ }
+
+ if ($e1 && ! $e1->orig) {
+ $bb->out1($e1->final);
+ $e1 = next($edits1);
+ }
+ if ($e2 && ! $e2->orig) {
+ $bb->out2($e2->final);
+ $e2 = next($edits2);
+ }
+ }
+ }
+
+ if ($edit = $bb->finish()) {
+ $edits[] = $edit;
+ }
+
+ return $edits;
+ }
+
+}
+
+/**
+ * @package Text_Diff
+ * @author Geoffrey T. Dairiki <dairiki@dairiki.org>
+ *
+ * @access private
+ */
+class Text_Diff_ThreeWay_Op {
+
+ function Text_Diff_ThreeWay_Op($orig = false, $final1 = false, $final2 = false)
+ {
+ $this->orig = $orig ? $orig : array();
+ $this->final1 = $final1 ? $final1 : array();
+ $this->final2 = $final2 ? $final2 : array();
+ }
+
+ function merged()
+ {
+ if (!isset($this->_merged)) {
+ if ($this->final1 === $this->final2) {
+ $this->_merged = &$this->final1;
+ } elseif ($this->final1 === $this->orig) {
+ $this->_merged = &$this->final2;
+ } elseif ($this->final2 === $this->orig) {
+ $this->_merged = &$this->final1;
+ } else {
+ $this->_merged = false;
+ }
+ }
+
+ return $this->_merged;
+ }
+
+ function isConflict()
+ {
+ return $this->merged() === false;
+ }
+
+}
+
+/**
+ * @package Text_Diff
+ * @author Geoffrey T. Dairiki <dairiki@dairiki.org>
+ *
+ * @access private
+ */
+class Text_Diff_ThreeWay_Op_copy extends Text_Diff_ThreeWay_Op {
+
+ function Text_Diff_ThreeWay_Op_Copy($lines = false)
+ {
+ $this->orig = $lines ? $lines : array();
+ $this->final1 = &$this->orig;
+ $this->final2 = &$this->orig;
+ }
+
+ function merged()
+ {
+ return $this->orig;
+ }
+
+ function isConflict()
+ {
+ return false;
+ }
+
+}
+
+/**
+ * @package Text_Diff
+ * @author Geoffrey T. Dairiki <dairiki@dairiki.org>
+ *
+ * @access private
+ */
+class Text_Diff_ThreeWay_BlockBuilder {
+
+ function Text_Diff_ThreeWay_BlockBuilder()
+ {
+ $this->_init();
+ }
+
+ function input($lines)
+ {
+ if ($lines) {
+ $this->_append($this->orig, $lines);
+ }
+ }
+
+ function out1($lines)
+ {
+ if ($lines) {
+ $this->_append($this->final1, $lines);
+ }
+ }
+
+ function out2($lines)
+ {
+ if ($lines) {
+ $this->_append($this->final2, $lines);
+ }
+ }
+
+ function isEmpty()
+ {
+ return !$this->orig && !$this->final1 && !$this->final2;
+ }
+
+ function finish()
+ {
+ if ($this->isEmpty()) {
+ return false;
+ } else {
+ $edit = new Text_Diff_ThreeWay_Op($this->orig, $this->final1, $this->final2);
+ $this->_init();
+ return $edit;
+ }
+ }
+
+ function _init()
+ {
+ $this->orig = $this->final1 = $this->final2 = array();
+ }
+
+ function _append(&$array, $lines)
+ {
+ array_splice($array, sizeof($array), 0, $lines);
+ }
+
+}
diff --git a/framework/gii/components/Pear/Text/Diff3.php b/framework/gii/components/Pear/Text/Diff3.php
new file mode 100644
index 0000000..2c28370
--- /dev/null
+++ b/framework/gii/components/Pear/Text/Diff3.php
@@ -0,0 +1,276 @@
+<?php
+/**
+ * A class for computing three way diffs.
+ *
+ * $Horde: framework/Text_Diff/Diff3.php,v 1.2.10.6 2008/01/04 10:37:26 jan Exp $
+ *
+ * Copyright 2007-2008 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you did
+ * not receive this file, see http://opensource.org/licenses/lgpl-license.php.
+ *
+ * @package Text_Diff
+ * @since 0.3.0
+ */
+
+/** Text_Diff */
+require_once 'Text/Diff.php';
+
+/**
+ * A class for computing three way diffs.
+ *
+ * @package Text_Diff
+ * @author Geoffrey T. Dairiki <dairiki@dairiki.org>
+ */
+class Text_Diff3 extends Text_Diff {
+
+ /**
+ * Conflict counter.
+ *
+ * @var integer
+ */
+ var $_conflictingBlocks = 0;
+
+ /**
+ * Computes diff between 3 sequences of strings.
+ *
+ * @param array $orig The original lines to use.
+ * @param array $final1 The first version to compare to.
+ * @param array $final2 The second version to compare to.
+ */
+ function Text_Diff3($orig, $final1, $final2)
+ {
+ if (extension_loaded('xdiff')) {
+ $engine = new Text_Diff_Engine_xdiff();
+ } else {
+ $engine = new Text_Diff_Engine_native();
+ }
+
+ $this->_edits = $this->_diff3($engine->diff($orig, $final1),
+ $engine->diff($orig, $final2));
+ }
+
+ /**
+ */
+ function mergedOutput($label1 = false, $label2 = false)
+ {
+ $lines = array();
+ foreach ($this->_edits as $edit) {
+ if ($edit->isConflict()) {
+ /* FIXME: this should probably be moved somewhere else. */
+ $lines = array_merge($lines,
+ array('<<<<<<<' . ($label1 ? ' ' . $label1 : '')),
+ $edit->final1,
+ array("======="),
+ $edit->final2,
+ array('>>>>>>>' . ($label2 ? ' ' . $label2 : '')));
+ $this->_conflictingBlocks++;
+ } else {
+ $lines = array_merge($lines, $edit->merged());
+ }
+ }
+
+ return $lines;
+ }
+
+ /**
+ * @access private
+ */
+ function _diff3($edits1, $edits2)
+ {
+ $edits = array();
+ $bb = new Text_Diff3_BlockBuilder();
+
+ $e1 = current($edits1);
+ $e2 = current($edits2);
+ while ($e1 || $e2) {
+ if ($e1 && $e2 && is_a($e1, 'Text_Diff_Op_copy') && is_a($e2, 'Text_Diff_Op_copy')) {
+ /* We have copy blocks from both diffs. This is the (only)
+ * time we want to emit a diff3 copy block. Flush current
+ * diff3 diff block, if any. */
+ if ($edit = $bb->finish()) {
+ $edits[] = $edit;
+ }
+
+ $ncopy = min($e1->norig(), $e2->norig());
+ assert($ncopy > 0);
+ $edits[] = new Text_Diff3_Op_copy(array_slice($e1->orig, 0, $ncopy));
+
+ if ($e1->norig() > $ncopy) {
+ array_splice($e1->orig, 0, $ncopy);
+ array_splice($e1->final, 0, $ncopy);
+ } else {
+ $e1 = next($edits1);
+ }
+
+ if ($e2->norig() > $ncopy) {
+ array_splice($e2->orig, 0, $ncopy);
+ array_splice($e2->final, 0, $ncopy);
+ } else {
+ $e2 = next($edits2);
+ }
+ } else {
+ if ($e1 && $e2) {
+ if ($e1->orig && $e2->orig) {
+ $norig = min($e1->norig(), $e2->norig());
+ $orig = array_splice($e1->orig, 0, $norig);
+ array_splice($e2->orig, 0, $norig);
+ $bb->input($orig);
+ }
+
+ if (is_a($e1, 'Text_Diff_Op_copy')) {
+ $bb->out1(array_splice($e1->final, 0, $norig));
+ }
+
+ if (is_a($e2, 'Text_Diff_Op_copy')) {
+ $bb->out2(array_splice($e2->final, 0, $norig));
+ }
+ }
+
+ if ($e1 && ! $e1->orig) {
+ $bb->out1($e1->final);
+ $e1 = next($edits1);
+ }
+ if ($e2 && ! $e2->orig) {
+ $bb->out2($e2->final);
+ $e2 = next($edits2);
+ }
+ }
+ }
+
+ if ($edit = $bb->finish()) {
+ $edits[] = $edit;
+ }
+
+ return $edits;
+ }
+
+}
+
+/**
+ * @package Text_Diff
+ * @author Geoffrey T. Dairiki <dairiki@dairiki.org>
+ *
+ * @access private
+ */
+class Text_Diff3_Op {
+
+ function Text_Diff3_Op($orig = false, $final1 = false, $final2 = false)
+ {
+ $this->orig = $orig ? $orig : array();
+ $this->final1 = $final1 ? $final1 : array();
+ $this->final2 = $final2 ? $final2 : array();
+ }
+
+ function merged()
+ {
+ if (!isset($this->_merged)) {
+ if ($this->final1 === $this->final2) {
+ $this->_merged = &$this->final1;
+ } elseif ($this->final1 === $this->orig) {
+ $this->_merged = &$this->final2;
+ } elseif ($this->final2 === $this->orig) {
+ $this->_merged = &$this->final1;
+ } else {
+ $this->_merged = false;
+ }
+ }
+
+ return $this->_merged;
+ }
+
+ function isConflict()
+ {
+ return $this->merged() === false;
+ }
+
+}
+
+/**
+ * @package Text_Diff
+ * @author Geoffrey T. Dairiki <dairiki@dairiki.org>
+ *
+ * @access private
+ */
+class Text_Diff3_Op_copy extends Text_Diff3_Op {
+
+ function Text_Diff3_Op_Copy($lines = false)
+ {
+ $this->orig = $lines ? $lines : array();
+ $this->final1 = &$this->orig;
+ $this->final2 = &$this->orig;
+ }
+
+ function merged()
+ {
+ return $this->orig;
+ }
+
+ function isConflict()
+ {
+ return false;
+ }
+
+}
+
+/**
+ * @package Text_Diff
+ * @author Geoffrey T. Dairiki <dairiki@dairiki.org>
+ *
+ * @access private
+ */
+class Text_Diff3_BlockBuilder {
+
+ function Text_Diff3_BlockBuilder()
+ {
+ $this->_init();
+ }
+
+ function input($lines)
+ {
+ if ($lines) {
+ $this->_append($this->orig, $lines);
+ }
+ }
+
+ function out1($lines)
+ {
+ if ($lines) {
+ $this->_append($this->final1, $lines);
+ }
+ }
+
+ function out2($lines)
+ {
+ if ($lines) {
+ $this->_append($this->final2, $lines);
+ }
+ }
+
+ function isEmpty()
+ {
+ return !$this->orig && !$this->final1 && !$this->final2;
+ }
+
+ function finish()
+ {
+ if ($this->isEmpty()) {
+ return false;
+ } else {
+ $edit = new Text_Diff3_Op($this->orig, $this->final1, $this->final2);
+ $this->_init();
+ return $edit;
+ }
+ }
+
+ function _init()
+ {
+ $this->orig = $this->final1 = $this->final2 = array();
+ }
+
+ function _append(&$array, $lines)
+ {
+ array_splice($array, sizeof($array), 0, $lines);
+ }
+
+}
diff --git a/framework/gii/components/TextDiff.php b/framework/gii/components/TextDiff.php
new file mode 100644
index 0000000..b9e13f5
--- /dev/null
+++ b/framework/gii/components/TextDiff.php
@@ -0,0 +1,22 @@
+<?php
+
+error_reporting(E_ALL);
+
+Yii::import('gii.components.Pear.*');
+require_once 'Text/Diff.php';
+require_once 'Text/Diff/Renderer.php';
+require_once 'Text/Diff/Renderer/inline.php';
+
+class TextDiff extends CComponent
+{
+ public static function compare($lines1, $lines2)
+ {
+ if(is_string($lines1))
+ $lines1=explode("\n",$lines1);
+ if(is_string($lines2))
+ $lines2=explode("\n",$lines2);
+ $diff = new Text_Diff('auto', array($lines1, $lines2));
+ $renderer = new Text_Diff_Renderer_inline();
+ return $renderer->render($diff);
+ }
+} \ No newline at end of file
diff --git a/framework/gii/components/UserIdentity.php b/framework/gii/components/UserIdentity.php
new file mode 100644
index 0000000..9de480e
--- /dev/null
+++ b/framework/gii/components/UserIdentity.php
@@ -0,0 +1,20 @@
+<?php
+
+class UserIdentity extends CUserIdentity
+{
+ /**
+ * Authenticates a user.
+ * @return boolean whether authentication succeeds.
+ */
+ public function authenticate()
+ {
+ $password=Yii::app()->getModule('gii')->password;
+ if($password===null)
+ throw new CException('Please configure the "password" property of the "gii" module.');
+ else if($password===false || $password===$this->password)
+ $this->errorCode=self::ERROR_NONE;
+ else
+ $this->errorCode=self::ERROR_UNKNOWN_IDENTITY;
+ return !$this->errorCode;
+ }
+} \ No newline at end of file
diff --git a/framework/gii/controllers/DefaultController.php b/framework/gii/controllers/DefaultController.php
new file mode 100644
index 0000000..44815b5
--- /dev/null
+++ b/framework/gii/controllers/DefaultController.php
@@ -0,0 +1,58 @@
+<?php
+
+class DefaultController extends CController
+{
+ public $layout='/layouts/column1';
+
+ public function getPageTitle()
+ {
+ if($this->action->id==='index')
+ return 'Gii: a Web-based code generator for Yii';
+ else
+ return 'Gii - '.ucfirst($this->action->id).' Generator';
+ }
+
+ public function actionIndex()
+ {
+ $this->render('index');
+ }
+
+ public function actionError()
+ {
+ if($error=Yii::app()->errorHandler->error)
+ {
+ if(Yii::app()->request->isAjaxRequest)
+ echo $error['message'];
+ else
+ $this->render('error', $error);
+ }
+ }
+
+ /**
+ * Displays the login page
+ */
+ public function actionLogin()
+ {
+ $model=Yii::createComponent('gii.models.LoginForm');
+
+ // 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()->createUrl('gii/default/index'));
+ }
+ // 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(false);
+ $this->redirect(Yii::app()->createUrl('gii/default/index'));
+ }
+} \ No newline at end of file
diff --git a/framework/gii/generators/controller/ControllerCode.php b/framework/gii/generators/controller/ControllerCode.php
new file mode 100644
index 0000000..6cdc9fd
--- /dev/null
+++ b/framework/gii/generators/controller/ControllerCode.php
@@ -0,0 +1,130 @@
+<?php
+
+class ControllerCode extends CCodeModel
+{
+ public $controller;
+ public $baseClass='Controller';
+ public $actions='index';
+
+ public function rules()
+ {
+ return array_merge(parent::rules(), array(
+ array('controller, actions, baseClass', 'filter', 'filter'=>'trim'),
+ array('controller, baseClass', 'required'),
+ array('controller', 'match', 'pattern'=>'/^\w+[\w+\\/]*$/', 'message'=>'{attribute} should only contain word characters and slashes.'),
+ array('actions', 'match', 'pattern'=>'/^\w+[\w\s,]*$/', 'message'=>'{attribute} should only contain word characters, spaces and commas.'),
+ array('baseClass', 'match', 'pattern'=>'/^[a-zA-Z_]\w*$/', 'message'=>'{attribute} should only contain word characters.'),
+ array('baseClass', 'validateReservedWord', 'skipOnError'=>true),
+ array('baseClass, actions', 'sticky'),
+ ));
+ }
+
+ public function attributeLabels()
+ {
+ return array_merge(parent::attributeLabels(), array(
+ 'baseClass'=>'Base Class',
+ 'controller'=>'Controller ID',
+ 'actions'=>'Action IDs',
+ ));
+ }
+
+ public function requiredTemplates()
+ {
+ return array(
+ 'controller.php',
+ 'view.php',
+ );
+ }
+
+ public function successMessage()
+ {
+ $link=CHtml::link('try it now', Yii::app()->createUrl($this->controller), array('target'=>'_blank'));
+ return "The controller has been generated successfully. You may $link.";
+ }
+
+ public function prepare()
+ {
+ $this->files=array();
+ $templatePath=$this->templatePath;
+
+ $this->files[]=new CCodeFile(
+ $this->controllerFile,
+ $this->render($templatePath.'/controller.php')
+ );
+
+ foreach($this->getActionIDs() as $action)
+ {
+ $this->files[]=new CCodeFile(
+ $this->getViewFile($action),
+ $this->render($templatePath.'/view.php', array('action'=>$action))
+ );
+ }
+ }
+
+ public function getActionIDs()
+ {
+ $actions=preg_split('/[\s,]+/',$this->actions,-1,PREG_SPLIT_NO_EMPTY);
+ $actions=array_unique($actions);
+ sort($actions);
+ return $actions;
+ }
+
+ public function getControllerClass()
+ {
+ if(($pos=strrpos($this->controller,'/'))!==false)
+ return ucfirst(substr($this->controller,$pos+1)).'Controller';
+ else
+ return ucfirst($this->controller).'Controller';
+ }
+
+ public function getModule()
+ {
+ if(($pos=strpos($this->controller,'/'))!==false)
+ {
+ $id=substr($this->controller,0,$pos);
+ if(($module=Yii::app()->getModule($id))!==null)
+ return $module;
+ }
+ return Yii::app();
+ }
+
+ public function getControllerID()
+ {
+ if($this->getModule()!==Yii::app())
+ $id=substr($this->controller,strpos($this->controller,'/')+1);
+ else
+ $id=$this->controller;
+ if(($pos=strrpos($id,'/'))!==false)
+ $id[$pos+1]=strtolower($id[$pos+1]);
+ else
+ $id[0]=strtolower($id[0]);
+ return $id;
+ }
+
+ public function getUniqueControllerID()
+ {
+ $id=$this->controller;
+ if(($pos=strrpos($id,'/'))!==false)
+ $id[$pos+1]=strtolower($id[$pos+1]);
+ else
+ $id[0]=strtolower($id[0]);
+ return $id;
+ }
+
+ public function getControllerFile()
+ {
+ $module=$this->getModule();
+ $id=$this->getControllerID();
+ if(($pos=strrpos($id,'/'))!==false)
+ $id[$pos+1]=strtoupper($id[$pos+1]);
+ else
+ $id[0]=strtoupper($id[0]);
+ return $module->getControllerPath().'/'.$id.'Controller.php';
+ }
+
+ public function getViewFile($action)
+ {
+ $module=$this->getModule();
+ return $module->getViewPath().'/'.$this->getControllerID().'/'.$action.'.php';
+ }
+} \ No newline at end of file
diff --git a/framework/gii/generators/controller/ControllerGenerator.php b/framework/gii/generators/controller/ControllerGenerator.php
new file mode 100644
index 0000000..91ffaaa
--- /dev/null
+++ b/framework/gii/generators/controller/ControllerGenerator.php
@@ -0,0 +1,6 @@
+<?php
+
+class ControllerGenerator extends CCodeGenerator
+{
+ public $codeModel='gii.generators.controller.ControllerCode';
+} \ No newline at end of file
diff --git a/framework/gii/generators/controller/templates/default/controller.php b/framework/gii/generators/controller/templates/default/controller.php
new file mode 100644
index 0000000..ba0b627
--- /dev/null
+++ b/framework/gii/generators/controller/templates/default/controller.php
@@ -0,0 +1,45 @@
+<?php
+/**
+ * This is the template for generating a controller class file.
+ * The following variables are available in this template:
+ * - $this: the ControllerCode object
+ */
+?>
+<?php echo "<?php\n"; ?>
+
+class <?php echo $this->controllerClass; ?> extends <?php echo $this->baseClass."\n"; ?>
+{
+<?php foreach($this->getActionIDs() 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/gii/generators/controller/templates/default/view.php b/framework/gii/generators/controller/templates/default/view.php
new file mode 100644
index 0000000..26fb36a
--- /dev/null
+++ b/framework/gii/generators/controller/templates/default/view.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * This is the template for generating an action view file.
+ * The following variables are available in this template:
+ * - $this: the ControllerCode object
+ * - $action: the action ID
+ */
+?>
+<?php
+echo "<?php\n";
+$label=ucwords(trim(strtolower(str_replace(array('-','_','.'),' ',preg_replace('/(?<![A-Z])[A-Z]/', ' \0', basename($this->getControllerID()))))));
+if($action==='index')
+{
+ echo "\$this->breadcrumbs=array(
+ '$label',
+);";
+}
+else
+{
+ $action=ucfirst($action);
+ echo "\$this->breadcrumbs=array(
+ '$label'=>array('/{$this->uniqueControllerID}'),
+ '$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/gii/generators/controller/views/index.php b/framework/gii/generators/controller/views/index.php
new file mode 100644
index 0000000..6224e7e
--- /dev/null
+++ b/framework/gii/generators/controller/views/index.php
@@ -0,0 +1,44 @@
+<h1>Controller Generator</h1>
+
+<p>This generator helps you to quickly generate a new controller class,
+one or several controller actions and their corresponding views.</p>
+
+<?php $form=$this->beginWidget('CCodeForm', array('model'=>$model)); ?>
+
+ <div class="row">
+ <?php echo $form->labelEx($model,'controller'); ?>
+ <?php echo $form->textField($model,'controller',array('size'=>65)); ?>
+ <div class="tooltip">
+ Controller ID is case-sensitive. Below are some examples:
+ <ul>
+ <li><code>post</code> generates <code>PostController.php</code></li>
+ <li><code>postTag</code> generates <code>PostTagController.php</code></li>
+ <li><code>admin/user</code> generates <code>admin/UserController.php</code>.
+ If the application has an <code>admin</code> module enabled,
+ it will generate <code>UserController</code> within the module instead.
+ </li>
+ </ul>
+ </div>
+ <?php echo $form->error($model,'controller'); ?>
+ </div>
+
+ <div class="row sticky">
+ <?php echo $form->labelEx($model,'baseClass'); ?>
+ <?php echo $form->textField($model,'baseClass',array('size'=>65)); ?>
+ <div class="tooltip">
+ This is the class that the new controller class will extend from.
+ Please make sure the class exists and can be autoloaded.
+ </div>
+ <?php echo $form->error($model,'baseClass'); ?>
+ </div>
+
+ <div class="row">
+ <?php echo $form->labelEx($model,'actions'); ?>
+ <?php echo $form->textField($model,'actions',array('size'=>65)); ?>
+ <div class="tooltip">
+ Action IDs are case-insensitive. Separate multiple action IDs with commas or spaces.
+ </div>
+ <?php echo $form->error($model,'actions'); ?>
+ </div>
+
+<?php $this->endWidget(); ?>
diff --git a/framework/gii/generators/crud/CrudCode.php b/framework/gii/generators/crud/CrudCode.php
new file mode 100644
index 0000000..b08eb6d
--- /dev/null
+++ b/framework/gii/generators/crud/CrudCode.php
@@ -0,0 +1,248 @@
+<?php
+
+class CrudCode extends CCodeModel
+{
+ public $model;
+ public $controller;
+ public $baseControllerClass='Controller';
+
+ private $_modelClass;
+ private $_table;
+
+ public function rules()
+ {
+ return array_merge(parent::rules(), array(
+ array('model, controller', 'filter', 'filter'=>'trim'),
+ array('model, controller, baseControllerClass', 'required'),
+ array('model', 'match', 'pattern'=>'/^\w+[\w+\\.]*$/', 'message'=>'{attribute} should only contain word characters and dots.'),
+ array('controller', 'match', 'pattern'=>'/^\w+[\w+\\/]*$/', 'message'=>'{attribute} should only contain word characters and slashes.'),
+ array('baseControllerClass', 'match', 'pattern'=>'/^[a-zA-Z_]\w*$/', 'message'=>'{attribute} should only contain word characters.'),
+ array('baseControllerClass', 'validateReservedWord', 'skipOnError'=>true),
+ array('model', 'validateModel'),
+ array('baseControllerClass', 'sticky'),
+ ));
+ }
+
+ public function attributeLabels()
+ {
+ return array_merge(parent::attributeLabels(), array(
+ 'model'=>'Model Class',
+ 'controller'=>'Controller ID',
+ 'baseControllerClass'=>'Base Controller Class',
+ ));
+ }
+
+ public function requiredTemplates()
+ {
+ return array(
+ 'controller.php',
+ );
+ }
+
+ public function init()
+ {
+ if(Yii::app()->db===null)
+ throw new CHttpException(500,'An active "db" connection is required to run this generator.');
+ parent::init();
+ }
+
+ public function successMessage()
+ {
+ $link=CHtml::link('try it now', Yii::app()->createUrl($this->controller), array('target'=>'_blank'));
+ return "The controller has been generated successfully. You may $link.";
+ }
+
+ public function validateModel($attribute,$params)
+ {
+ if($this->hasErrors('model'))
+ return;
+ $class=@Yii::import($this->model,true);
+ if(!is_string($class) || !$this->classExists($class))
+ $this->addError('model', "Class '{$this->model}' does not exist or has syntax error.");
+ else if(!is_subclass_of($class,'CActiveRecord'))
+ $this->addError('model', "'{$this->model}' must extend from CActiveRecord.");
+ else
+ {
+ $table=CActiveRecord::model($class)->tableSchema;
+ if($table->primaryKey===null)
+ $this->addError('model',"Table '{$table->name}' does not have a primary key.");
+ else if(is_array($table->primaryKey))
+ $this->addError('model',"Table '{$table->name}' has a composite primary key which is not supported by crud generator.");
+ else
+ {
+ $this->_modelClass=$class;
+ $this->_table=$table;
+ }
+ }
+ }
+
+ public function prepare()
+ {
+ $this->files=array();
+ $templatePath=$this->templatePath;
+ $controllerTemplateFile=$templatePath.DIRECTORY_SEPARATOR.'controller.php';
+
+ $this->files[]=new CCodeFile(
+ $this->controllerFile,
+ $this->render($controllerTemplateFile)
+ );
+
+ $files=scandir($templatePath);
+ foreach($files as $file)
+ {
+ if(is_file($templatePath.'/'.$file) && CFileHelper::getExtension($file)==='php' && $file!=='controller.php')
+ {
+ $this->files[]=new CCodeFile(
+ $this->viewPath.DIRECTORY_SEPARATOR.$file,
+ $this->render($templatePath.'/'.$file)
+ );
+ }
+ }
+ }
+
+ public function getModelClass()
+ {
+ return $this->_modelClass;
+ }
+
+ public function getControllerClass()
+ {
+ if(($pos=strrpos($this->controller,'/'))!==false)
+ return ucfirst(substr($this->controller,$pos+1)).'Controller';
+ else
+ return ucfirst($this->controller).'Controller';
+ }
+
+ public function getModule()
+ {
+ if(($pos=strpos($this->controller,'/'))!==false)
+ {
+ $id=substr($this->controller,0,$pos);
+ if(($module=Yii::app()->getModule($id))!==null)
+ return $module;
+ }
+ return Yii::app();
+ }
+
+ public function getControllerID()
+ {
+ if($this->getModule()!==Yii::app())
+ $id=substr($this->controller,strpos($this->controller,'/')+1);
+ else
+ $id=$this->controller;
+ if(($pos=strrpos($id,'/'))!==false)
+ $id[$pos+1]=strtolower($id[$pos+1]);
+ else
+ $id[0]=strtolower($id[0]);
+ return $id;
+ }
+
+ public function getUniqueControllerID()
+ {
+ $id=$this->controller;
+ if(($pos=strrpos($id,'/'))!==false)
+ $id[$pos+1]=strtolower($id[$pos+1]);
+ else
+ $id[0]=strtolower($id[0]);
+ return $id;
+ }
+
+ public function getControllerFile()
+ {
+ $module=$this->getModule();
+ $id=$this->getControllerID();
+ if(($pos=strrpos($id,'/'))!==false)
+ $id[$pos+1]=strtoupper($id[$pos+1]);
+ else
+ $id[0]=strtoupper($id[0]);
+ return $module->getControllerPath().'/'.$id.'Controller.php';
+ }
+
+ public function getViewPath()
+ {
+ return $this->getModule()->getViewPath().'/'.$this->getControllerID();
+ }
+
+ public function getTableSchema()
+ {
+ return $this->_table;
+ }
+
+ 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';
+ }
+} \ No newline at end of file
diff --git a/framework/gii/generators/crud/CrudGenerator.php b/framework/gii/generators/crud/CrudGenerator.php
new file mode 100644
index 0000000..790e073
--- /dev/null
+++ b/framework/gii/generators/crud/CrudGenerator.php
@@ -0,0 +1,6 @@
+<?php
+
+class CrudGenerator extends CCodeGenerator
+{
+ public $codeModel='gii.generators.crud.CrudCode';
+} \ No newline at end of file
diff --git a/framework/gii/generators/crud/templates/default/_form.php b/framework/gii/generators/crud/templates/default/_form.php
new file mode 100644
index 0000000..6f86788
--- /dev/null
+++ b/framework/gii/generators/crud/templates/default/_form.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * The following variables are available in this template:
+ * - $this: the CrudCode object
+ */
+?>
+<div class="form">
+
+<?php echo "<?php \$form=\$this->beginWidget('CActiveForm', array(
+ 'id'=>'".$this->class2id($this->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($this->tableSchema->columns as $column)
+{
+ if($column->autoIncrement)
+ continue;
+?>
+ <div class="row">
+ <?php echo "<?php echo ".$this->generateActiveLabel($this->modelClass,$column)."; ?>\n"; ?>
+ <?php echo "<?php echo ".$this->generateActiveField($this->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/gii/generators/crud/templates/default/_search.php b/framework/gii/generators/crud/templates/default/_search.php
new file mode 100644
index 0000000..91c4fe8
--- /dev/null
+++ b/framework/gii/generators/crud/templates/default/_search.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * The following variables are available in this template:
+ * - $this: the CrudCode object
+ */
+?>
+<div class="wide form">
+
+<?php echo "<?php \$form=\$this->beginWidget('CActiveForm', array(
+ 'action'=>Yii::app()->createUrl(\$this->route),
+ 'method'=>'get',
+)); ?>\n"; ?>
+
+<?php foreach($this->tableSchema->columns as $column): ?>
+<?php
+ $field=$this->generateInputField($this->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($this->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/gii/generators/crud/templates/default/_view.php b/framework/gii/generators/crud/templates/default/_view.php
new file mode 100644
index 0000000..9c3529a
--- /dev/null
+++ b/framework/gii/generators/crud/templates/default/_view.php
@@ -0,0 +1,26 @@
+<?php
+/**
+ * The following variables are available in this template:
+ * - $this: the CrudCode object
+ */
+?>
+<div class="view">
+
+<?php
+echo "\t<b><?php echo CHtml::encode(\$data->getAttributeLabel('{$this->tableSchema->primaryKey}')); ?>:</b>\n";
+echo "\t<?php echo CHtml::link(CHtml::encode(\$data->{$this->tableSchema->primaryKey}), array('view', 'id'=>\$data->{$this->tableSchema->primaryKey})); ?>\n\t<br />\n\n";
+$count=0;
+foreach($this->tableSchema->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/gii/generators/crud/templates/default/admin.php b/framework/gii/generators/crud/templates/default/admin.php
new file mode 100644
index 0000000..cc060c2
--- /dev/null
+++ b/framework/gii/generators/crud/templates/default/admin.php
@@ -0,0 +1,70 @@
+<?php
+/**
+ * The following variables are available in this template:
+ * - $this: the CrudCode object
+ */
+?>
+<?php
+echo "<?php\n";
+$label=$this->pluralize($this->class2name($this->modelClass));
+echo "\$this->breadcrumbs=array(
+ '$label'=>array('index'),
+ 'Manage',
+);\n";
+?>
+
+$this->menu=array(
+ array('label'=>'List <?php echo $this->modelClass; ?>', 'url'=>array('index')),
+ array('label'=>'Create <?php echo $this->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($this->modelClass); ?>-grid', {
+ data: $(this).serialize()
+ });
+ return false;
+});
+");
+?>
+
+<h1>Manage <?php echo $this->pluralize($this->class2name($this->modelClass)); ?></h1>
+
+<p>
+You may optionally enter a comparison operator (<b>&lt;</b>, <b>&lt;=</b>, <b>&gt;</b>, <b>&gt;=</b>, <b>&lt;&gt;</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($this->modelClass); ?>-grid',
+ 'dataProvider'=>$model->search(),
+ 'filter'=>$model,
+ 'columns'=>array(
+<?php
+$count=0;
+foreach($this->tableSchema->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/gii/generators/crud/templates/default/controller.php b/framework/gii/generators/crud/templates/default/controller.php
new file mode 100644
index 0000000..56eb826
--- /dev/null
+++ b/framework/gii/generators/crud/templates/default/controller.php
@@ -0,0 +1,183 @@
+<?php
+/**
+ * This is the template for generating a controller class file for CRUD feature.
+ * The following variables are available in this template:
+ * - $this: the CrudCode object
+ */
+?>
+<?php echo "<?php\n"; ?>
+
+class <?php echo $this->controllerClass; ?> extends <?php echo $this->baseControllerClass."\n"; ?>
+{
+ /**
+ * @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';
+
+ /**
+ * @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.
+ * @param integer $id the ID of the model to be displayed
+ */
+ public function actionView($id)
+ {
+ $this->render('view',array(
+ 'model'=>$this->loadModel($id),
+ ));
+ }
+
+ /**
+ * Creates a new model.
+ * If creation is successful, the browser will be redirected to the 'view' page.
+ */
+ public function actionCreate()
+ {
+ $model=new <?php echo $this->modelClass; ?>;
+
+ // Uncomment the following line if AJAX validation is needed
+ // $this->performAjaxValidation($model);
+
+ if(isset($_POST['<?php echo $this->modelClass; ?>']))
+ {
+ $model->attributes=$_POST['<?php echo $this->modelClass; ?>'];
+ if($model->save())
+ $this->redirect(array('view','id'=>$model-><?php echo $this->tableSchema->primaryKey; ?>));
+ }
+
+ $this->render('create',array(
+ 'model'=>$model,
+ ));
+ }
+
+ /**
+ * Updates a particular model.
+ * If update is successful, the browser will be redirected to the 'view' page.
+ * @param integer $id the ID of the model to be updated
+ */
+ public function actionUpdate($id)
+ {
+ $model=$this->loadModel($id);
+
+ // Uncomment the following line if AJAX validation is needed
+ // $this->performAjaxValidation($model);
+
+ if(isset($_POST['<?php echo $this->modelClass; ?>']))
+ {
+ $model->attributes=$_POST['<?php echo $this->modelClass; ?>'];
+ if($model->save())
+ $this->redirect(array('view','id'=>$model-><?php echo $this->tableSchema->primaryKey; ?>));
+ }
+
+ $this->render('update',array(
+ 'model'=>$model,
+ ));
+ }
+
+ /**
+ * Deletes a particular model.
+ * If deletion is successful, the browser will be redirected to the 'admin' page.
+ * @param integer $id the ID of the model to be deleted
+ */
+ public function actionDelete($id)
+ {
+ if(Yii::app()->request->isPostRequest)
+ {
+ // we only allow deletion via POST request
+ $this->loadModel($id)->delete();
+
+ // if AJAX request (triggered by deletion via admin grid view), we should not redirect the browser
+ if(!isset($_GET['ajax']))
+ $this->redirect(isset($_POST['returnUrl']) ? $_POST['returnUrl'] : array('admin'));
+ }
+ 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 $this->modelClass; ?>');
+ $this->render('index',array(
+ 'dataProvider'=>$dataProvider,
+ ));
+ }
+
+ /**
+ * Manages all models.
+ */
+ public function actionAdmin()
+ {
+ $model=new <?php echo $this->modelClass; ?>('search');
+ $model->unsetAttributes(); // clear any default values
+ if(isset($_GET['<?php echo $this->modelClass; ?>']))
+ $model->attributes=$_GET['<?php echo $this->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.
+ * @param integer the ID of the model to be loaded
+ */
+ public function loadModel($id)
+ {
+ $model=<?php echo $this->modelClass; ?>::model()->findByPk($id);
+ if($model===null)
+ throw new CHttpException(404,'The requested page does not exist.');
+ return $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($this->modelClass); ?>-form')
+ {
+ echo CActiveForm::validate($model);
+ Yii::app()->end();
+ }
+ }
+}
diff --git a/framework/gii/generators/crud/templates/default/create.php b/framework/gii/generators/crud/templates/default/create.php
new file mode 100644
index 0000000..817a7f6
--- /dev/null
+++ b/framework/gii/generators/crud/templates/default/create.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * The following variables are available in this template:
+ * - $this: the CrudCode object
+ */
+?>
+<?php
+echo "<?php\n";
+$label=$this->pluralize($this->class2name($this->modelClass));
+echo "\$this->breadcrumbs=array(
+ '$label'=>array('index'),
+ 'Create',
+);\n";
+?>
+
+$this->menu=array(
+ array('label'=>'List <?php echo $this->modelClass; ?>', 'url'=>array('index')),
+ array('label'=>'Manage <?php echo $this->modelClass; ?>', 'url'=>array('admin')),
+);
+?>
+
+<h1>Create <?php echo $this->modelClass; ?></h1>
+
+<?php echo "<?php echo \$this->renderPartial('_form', array('model'=>\$model)); ?>"; ?>
diff --git a/framework/gii/generators/crud/templates/default/index.php b/framework/gii/generators/crud/templates/default/index.php
new file mode 100644
index 0000000..5bd1b0f
--- /dev/null
+++ b/framework/gii/generators/crud/templates/default/index.php
@@ -0,0 +1,26 @@
+<?php
+/**
+ * The following variables are available in this template:
+ * - $this: the CrudCode object
+ */
+?>
+<?php
+echo "<?php\n";
+$label=$this->pluralize($this->class2name($this->modelClass));
+echo "\$this->breadcrumbs=array(
+ '$label',
+);\n";
+?>
+
+$this->menu=array(
+ array('label'=>'Create <?php echo $this->modelClass; ?>', 'url'=>array('create')),
+ array('label'=>'Manage <?php echo $this->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/gii/generators/crud/templates/default/update.php b/framework/gii/generators/crud/templates/default/update.php
new file mode 100644
index 0000000..0004686
--- /dev/null
+++ b/framework/gii/generators/crud/templates/default/update.php
@@ -0,0 +1,28 @@
+<?php
+/**
+ * The following variables are available in this template:
+ * - $this: the CrudCode object
+ */
+?>
+<?php
+echo "<?php\n";
+$nameColumn=$this->guessNameColumn($this->tableSchema->columns);
+$label=$this->pluralize($this->class2name($this->modelClass));
+echo "\$this->breadcrumbs=array(
+ '$label'=>array('index'),
+ \$model->{$nameColumn}=>array('view','id'=>\$model->{$this->tableSchema->primaryKey}),
+ 'Update',
+);\n";
+?>
+
+$this->menu=array(
+ array('label'=>'List <?php echo $this->modelClass; ?>', 'url'=>array('index')),
+ array('label'=>'Create <?php echo $this->modelClass; ?>', 'url'=>array('create')),
+ array('label'=>'View <?php echo $this->modelClass; ?>', 'url'=>array('view', 'id'=>$model-><?php echo $this->tableSchema->primaryKey; ?>)),
+ array('label'=>'Manage <?php echo $this->modelClass; ?>', 'url'=>array('admin')),
+);
+?>
+
+<h1>Update <?php echo $this->modelClass." <?php echo \$model->{$this->tableSchema->primaryKey}; ?>"; ?></h1>
+
+<?php echo "<?php echo \$this->renderPartial('_form', array('model'=>\$model)); ?>"; ?> \ No newline at end of file
diff --git a/framework/gii/generators/crud/templates/default/view.php b/framework/gii/generators/crud/templates/default/view.php
new file mode 100644
index 0000000..b52004d
--- /dev/null
+++ b/framework/gii/generators/crud/templates/default/view.php
@@ -0,0 +1,36 @@
+<?php
+/**
+ * The following variables are available in this template:
+ * - $this: the CrudCode object
+ */
+?>
+<?php
+echo "<?php\n";
+$nameColumn=$this->guessNameColumn($this->tableSchema->columns);
+$label=$this->pluralize($this->class2name($this->modelClass));
+echo "\$this->breadcrumbs=array(
+ '$label'=>array('index'),
+ \$model->{$nameColumn},
+);\n";
+?>
+
+$this->menu=array(
+ array('label'=>'List <?php echo $this->modelClass; ?>', 'url'=>array('index')),
+ array('label'=>'Create <?php echo $this->modelClass; ?>', 'url'=>array('create')),
+ array('label'=>'Update <?php echo $this->modelClass; ?>', 'url'=>array('update', 'id'=>$model-><?php echo $this->tableSchema->primaryKey; ?>)),
+ array('label'=>'Delete <?php echo $this->modelClass; ?>', 'url'=>'#', 'linkOptions'=>array('submit'=>array('delete','id'=>$model-><?php echo $this->tableSchema->primaryKey; ?>),'confirm'=>'Are you sure you want to delete this item?')),
+ array('label'=>'Manage <?php echo $this->modelClass; ?>', 'url'=>array('admin')),
+);
+?>
+
+<h1>View <?php echo $this->modelClass." #<?php echo \$model->{$this->tableSchema->primaryKey}; ?>"; ?></h1>
+
+<?php echo "<?php"; ?> $this->widget('zii.widgets.CDetailView', array(
+ 'data'=>$model,
+ 'attributes'=>array(
+<?php
+foreach($this->tableSchema->columns as $column)
+ echo "\t\t'".$column->name."',\n";
+?>
+ ),
+)); ?>
diff --git a/framework/gii/generators/crud/views/index.php b/framework/gii/generators/crud/views/index.php
new file mode 100644
index 0000000..d7271dc
--- /dev/null
+++ b/framework/gii/generators/crud/views/index.php
@@ -0,0 +1,64 @@
+<?php
+$class=get_class($model);
+Yii::app()->clientScript->registerScript('gii.crud',"
+$('#{$class}_controller').change(function(){
+ $(this).data('changed',$(this).val()!='');
+});
+$('#{$class}_model').bind('keyup change', function(){
+ var controller=$('#{$class}_controller');
+ if(!controller.data('changed')) {
+ var id=new String($(this).val().match(/\\w*$/));
+ if(id.length>0)
+ id=id.substring(0,1).toLowerCase()+id.substring(1);
+ controller.val(id);
+ }
+});
+");
+?>
+<h1>Crud Generator</h1>
+
+<p>This generator generates a controller and views that implement CRUD operations for the specified data model.</p>
+
+<?php $form=$this->beginWidget('CCodeForm', array('model'=>$model)); ?>
+
+ <div class="row">
+ <?php echo $form->labelEx($model,'model'); ?>
+ <?php echo $form->textField($model,'model',array('size'=>65)); ?>
+ <div class="tooltip">
+ Model class is case-sensitive. It can be either a class name (e.g. <code>Post</code>)
+ or the path alias of the class file (e.g. <code>application.models.Post</code>).
+ Note that if the former, the class must be auto-loadable.
+ </div>
+ <?php echo $form->error($model,'model'); ?>
+ </div>
+
+ <div class="row">
+ <?php echo $form->labelEx($model,'controller'); ?>
+ <?php echo $form->textField($model,'controller',array('size'=>65)); ?>
+ <div class="tooltip">
+ Controller ID is case-sensitive. CRUD controllers are often named after
+ the model class name that they are dealing with. Below are some examples:
+ <ul>
+ <li><code>post</code> generates <code>PostController.php</code></li>
+ <li><code>postTag</code> generates <code>PostTagController.php</code></li>
+ <li><code>admin/user</code> generates <code>admin/UserController.php</code>.
+ If the application has an <code>admin</code> module enabled,
+ it will generate <code>UserController</code> (and other CRUD code)
+ within the module instead.
+ </li>
+ </ul>
+ </div>
+ <?php echo $form->error($model,'controller'); ?>
+ </div>
+
+ <div class="row sticky">
+ <?php echo $form->labelEx($model,'baseControllerClass'); ?>
+ <?php echo $form->textField($model,'baseControllerClass',array('size'=>65)); ?>
+ <div class="tooltip">
+ This is the class that the new CRUD controller class will extend from.
+ Please make sure the class exists and can be autoloaded.
+ </div>
+ <?php echo $form->error($model,'baseControllerClass'); ?>
+ </div>
+
+<?php $this->endWidget(); ?>
diff --git a/framework/gii/generators/form/FormCode.php b/framework/gii/generators/form/FormCode.php
new file mode 100644
index 0000000..e1def8d
--- /dev/null
+++ b/framework/gii/generators/form/FormCode.php
@@ -0,0 +1,94 @@
+<?php
+
+class FormCode extends CCodeModel
+{
+ public $model;
+ public $viewPath='application.views';
+ public $viewName;
+ public $scenario;
+
+ private $_modelClass;
+
+ public function rules()
+ {
+ return array_merge(parent::rules(), array(
+ array('model, viewName, scenario', 'filter', 'filter'=>'trim'),
+ array('model, viewName, viewPath', 'required'),
+ array('model, viewPath', 'match', 'pattern'=>'/^\w+[\.\w+]*$/', 'message'=>'{attribute} should only contain word characters and dots.'),
+ array('viewName', 'match', 'pattern'=>'/^\w+[\\/\w+]*$/', 'message'=>'{attribute} should only contain word characters and slashes.'),
+ array('model', 'validateModel'),
+ array('viewPath', 'validateViewPath'),
+ array('scenario', 'match', 'pattern'=>'/^\w+$/', 'message'=>'{attribute} should only contain word characters.'),
+ array('viewPath', 'sticky'),
+ ));
+ }
+
+ public function attributeLabels()
+ {
+ return array_merge(parent::attributeLabels(), array(
+ 'model'=>'Model Class',
+ 'viewName'=>'View Name',
+ 'viewPath'=>'View Path',
+ 'scenario'=>'Scenario',
+ ));
+ }
+
+ public function requiredTemplates()
+ {
+ return array(
+ 'form.php',
+ 'action.php',
+ );
+ }
+
+ public function successMessage()
+ {
+ $output=<<<EOD
+<p>The form has been generated successfully.</p>
+<p>You may add the following code in an appropriate controller class to invoke the view:</p>
+EOD;
+ $code="<?php\n".$this->render($this->templatePath.'/action.php');
+ return $output.highlight_string($code,true);
+ }
+
+ public function validateModel($attribute,$params)
+ {
+ if($this->hasErrors('model'))
+ return;
+ $class=@Yii::import($this->model,true);
+ if(!is_string($class) || !$this->classExists($class))
+ $this->addError('model', "Class '{$this->model}' does not exist or has syntax error.");
+ else if(!is_subclass_of($class,'CModel'))
+ $this->addError('model', "'{$this->model}' must extend from CModel.");
+ else
+ $this->_modelClass=$class;
+ }
+
+ public function validateViewPath($attribute,$params)
+ {
+ if($this->hasErrors('viewPath'))
+ return;
+ if(Yii::getPathOfAlias($this->viewPath)===false)
+ $this->addError('viewPath','View Path must be a valid path alias.');
+ }
+
+ public function prepare()
+ {
+ $templatePath=$this->templatePath;
+ $this->files[]=new CCodeFile(
+ Yii::getPathOfAlias($this->viewPath).'/'.$this->viewName.'.php',
+ $this->render($templatePath.'/form.php')
+ );
+ }
+
+ public function getModelClass()
+ {
+ return $this->_modelClass;
+ }
+
+ public function getModelAttributes()
+ {
+ $model=new $this->_modelClass($this->scenario);
+ return $model->getSafeAttributeNames();
+ }
+} \ No newline at end of file
diff --git a/framework/gii/generators/form/FormGenerator.php b/framework/gii/generators/form/FormGenerator.php
new file mode 100644
index 0000000..a9ec2df
--- /dev/null
+++ b/framework/gii/generators/form/FormGenerator.php
@@ -0,0 +1,6 @@
+<?php
+
+class FormGenerator extends CCodeGenerator
+{
+ public $codeModel='gii.generators.form.FormCode';
+} \ No newline at end of file
diff --git a/framework/gii/generators/form/templates/default/action.php b/framework/gii/generators/form/templates/default/action.php
new file mode 100644
index 0000000..4e88e5b
--- /dev/null
+++ b/framework/gii/generators/form/templates/default/action.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * This is the template for generating the action script for the form.
+ * - $this: the CrudCode object
+ */
+?>
+<?php
+$viewName=basename($this->viewName);
+?>
+public function action<?php echo ucfirst(trim($viewName,'_')); ?>()
+{
+ $model=new <?php echo $this->modelClass; ?><?php echo empty($this->scenario) ? '' : "('{$this->scenario}')"; ?>;
+
+ // uncomment the following code to enable ajax-based validation
+ /*
+ if(isset($_POST['ajax']) && $_POST['ajax']==='<?php echo $this->class2id($this->modelClass); ?>-<?php echo $viewName; ?>-form')
+ {
+ echo CActiveForm::validate($model);
+ Yii::app()->end();
+ }
+ */
+
+ if(isset($_POST['<?php echo $this->modelClass; ?>']))
+ {
+ $model->attributes=$_POST['<?php echo $this->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/gii/generators/form/templates/default/form.php b/framework/gii/generators/form/templates/default/form.php
new file mode 100644
index 0000000..1405618
--- /dev/null
+++ b/framework/gii/generators/form/templates/default/form.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * This is the template for generating a form script file.
+ * The following variables are available in this template:
+ * - $this: the FormCode object
+ */
+?>
+<div class="form">
+
+<?php echo "<?php \$form=\$this->beginWidget('CActiveForm', array(
+ 'id'=>'".$this->class2id($this->modelClass).'-'.basename($this->viewName)."-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($this->getModelAttributes() 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 endforeach; ?>
+
+ <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/gii/generators/form/views/index.php b/framework/gii/generators/form/views/index.php
new file mode 100644
index 0000000..8dcdf33
--- /dev/null
+++ b/framework/gii/generators/form/views/index.php
@@ -0,0 +1,49 @@
+<h1>Form Generator</h1>
+
+<p>This generator generates a view script file that displays a form to collect input for the specified model class.</p>
+
+<?php $form=$this->beginWidget('CCodeForm', array('model'=>$model)); ?>
+
+ <div class="row">
+ <?php echo $form->labelEx($model,'model'); ?>
+ <?php echo $form->textField($model,'model', array('size'=>65)); ?>
+ <div class="tooltip">
+ Model class is case-sensitive. It can be either a class name (e.g. <code>Post</code>)
+ or the path alias of the class file (e.g. <code>application.models.LoginForm</code>).
+ Note that if the former, the class must be auto-loadable.
+ </div>
+ <?php echo $form->error($model,'model'); ?>
+ </div>
+ <div class="row">
+ <?php echo $form->labelEx($model,'viewName'); ?>
+ <?php echo $form->textField($model,'viewName', array('size'=>65)); ?>
+ <div class="tooltip">
+ This refers to the name of the view script to be generated, for example,
+ <code>site/contact</code>, <code>user/login</code>. The actual view script file will be generated
+ under the View Path specified below.
+ </div>
+ <?php echo $form->error($model,'viewName'); ?>
+ </div>
+ <div class="row sticky">
+ <?php echo $form->labelEx($model,'viewPath'); ?>
+ <?php echo $form->textField($model,'viewPath', array('size'=>65)); ?>
+ <div class="tooltip">
+ This refers to the directory that the new view script file should be generated under.
+ It should be specified in the form of a path alias, for example, <code>application.views</code>,
+ <code>mymodule.views</code>.
+ </div>
+ <?php echo $form->error($model,'viewPath'); ?>
+ </div>
+ <div class="row">
+ <?php echo $form->labelEx($model,'scenario'); ?>
+ <?php echo $form->textField($model,'scenario', array('size'=>65)); ?>
+ <div class="tooltip">
+ This refers to the scenario in which the model should be used to collect user input.
+ For example, a <code>User</code> model can be used in both <code>login</code> and <code>register</code> scenarios.
+ To create a form for the login purpose, the scenario should be specified as <code>login</code>.
+ Leave this empty if the model does not need to differentiate scenarios.
+ </div>
+ <?php echo $form->error($model,'scenario'); ?>
+ </div>
+
+<?php $this->endWidget(); ?>
diff --git a/framework/gii/generators/model/ModelCode.php b/framework/gii/generators/model/ModelCode.php
new file mode 100644
index 0000000..08b2491
--- /dev/null
+++ b/framework/gii/generators/model/ModelCode.php
@@ -0,0 +1,395 @@
+<?php
+
+class ModelCode extends CCodeModel
+{
+ public $tablePrefix;
+ public $tableName;
+ public $modelClass;
+ public $modelPath='application.models';
+ public $baseClass='CActiveRecord';
+ public $buildRelations=true;
+
+ /**
+ * @var array list of candidate relation code. The array are indexed by AR class names and relation names.
+ * Each element represents the code of the one relation in one AR class.
+ */
+ protected $relations;
+
+ public function rules()
+ {
+ return array_merge(parent::rules(), array(
+ array('tablePrefix, baseClass, tableName, modelClass, modelPath', 'filter', 'filter'=>'trim'),
+ array('tableName, modelPath, baseClass', 'required'),
+ array('tablePrefix, tableName, modelPath', 'match', 'pattern'=>'/^(\w+[\w\.]*|\*?|\w+\.\*)$/', 'message'=>'{attribute} should only contain word characters, dots, and an optional ending asterisk.'),
+ array('tableName', 'validateTableName', 'skipOnError'=>true),
+ array('tablePrefix, modelClass, baseClass', 'match', 'pattern'=>'/^[a-zA-Z_]\w*$/', 'message'=>'{attribute} should only contain word characters.'),
+ array('modelPath', 'validateModelPath', 'skipOnError'=>true),
+ array('baseClass, modelClass', 'validateReservedWord', 'skipOnError'=>true),
+ array('baseClass', 'validateBaseClass', 'skipOnError'=>true),
+ array('tablePrefix, modelPath, baseClass, buildRelations', 'sticky'),
+ ));
+ }
+
+ public function attributeLabels()
+ {
+ return array_merge(parent::attributeLabels(), array(
+ 'tablePrefix'=>'Table Prefix',
+ 'tableName'=>'Table Name',
+ 'modelPath'=>'Model Path',
+ 'modelClass'=>'Model Class',
+ 'baseClass'=>'Base Class',
+ 'buildRelations'=>'Build Relations',
+ ));
+ }
+
+ public function requiredTemplates()
+ {
+ return array(
+ 'model.php',
+ );
+ }
+
+ public function init()
+ {
+ if(Yii::app()->db===null)
+ throw new CHttpException(500,'An active "db" connection is required to run this generator.');
+ $this->tablePrefix=Yii::app()->db->tablePrefix;
+ parent::init();
+ }
+
+ public function prepare()
+ {
+ if(($pos=strrpos($this->tableName,'.'))!==false)
+ {
+ $schema=substr($this->tableName,0,$pos);
+ $tableName=substr($this->tableName,$pos+1);
+ }
+ else
+ {
+ $schema='';
+ $tableName=$this->tableName;
+ }
+ if($tableName[strlen($tableName)-1]==='*')
+ {
+ $tables=Yii::app()->db->schema->getTables($schema);
+ if($this->tablePrefix!='')
+ {
+ foreach($tables as $i=>$table)
+ {
+ if(strpos($table->name,$this->tablePrefix)!==0)
+ unset($tables[$i]);
+ }
+ }
+ }
+ else
+ $tables=array($this->getTableSchema($this->tableName));
+
+ $this->files=array();
+ $templatePath=$this->templatePath;
+ $this->relations=$this->generateRelations();
+
+ foreach($tables as $table)
+ {
+ $tableName=$this->removePrefix($table->name);
+ $className=$this->generateClassName($table->name);
+ $params=array(
+ 'tableName'=>$schema==='' ? $tableName : $schema.'.'.$tableName,
+ 'modelClass'=>$className,
+ 'columns'=>$table->columns,
+ 'labels'=>$this->generateLabels($table),
+ 'rules'=>$this->generateRules($table),
+ 'relations'=>isset($this->relations[$className]) ? $this->relations[$className] : array(),
+ );
+ $this->files[]=new CCodeFile(
+ Yii::getPathOfAlias($this->modelPath).'/'.$className.'.php',
+ $this->render($templatePath.'/model.php', $params)
+ );
+ }
+ }
+
+ public function validateTableName($attribute,$params)
+ {
+ $invalidTables=array();
+ $invalidColumns=array();
+
+ if($this->tableName[strlen($this->tableName)-1]==='*')
+ {
+ if(($pos=strrpos($this->tableName,'.'))!==false)
+ $schema=substr($this->tableName,0,$pos);
+ else
+ $schema='';
+
+ $this->modelClass='';
+ $tables=Yii::app()->db->schema->getTables($schema);
+ foreach($tables as $table)
+ {
+ if($this->tablePrefix=='' || strpos($table->name,$this->tablePrefix)===0)
+ {
+ if(in_array(strtolower($table->name),self::$keywords))
+ $invalidTables[]=$table->name;
+ if(($invalidColumn=$this->checkColumns($table))!==null)
+ $invalidColumns[]=$invalidColumn;
+ }
+ }
+ }
+ else
+ {
+ if(($table=$this->getTableSchema($this->tableName))===null)
+ $this->addError('tableName',"Table '{$this->tableName}' does not exist.");
+ if($this->modelClass==='')
+ $this->addError('modelClass','Model Class cannot be blank.');
+
+ if(!$this->hasErrors($attribute) && ($invalidColumn=$this->checkColumns($table))!==null)
+ $invalidColumns[]=$invalidColumn;
+ }
+
+ if($invalidTables!=array())
+ $this->addError('tableName', 'Model class cannot take a reserved PHP keyword! Table name: '.implode(', ', $invalidTables).".");
+ if($invalidColumns!=array())
+ $this->addError('tableName', 'Column names that does not follow PHP variable naming convention: '.implode(', ', $invalidColumns).".");
+ }
+
+ /*
+ * Check that all database field names conform to PHP variable naming rules
+ * For example mysql allows field name like "2011aa", but PHP does not allow variable like "$model->2011aa"
+ * @param CDbTableSchema $table the table schema object
+ * @return string the invalid table column name. Null if no error.
+ */
+ public function checkColumns($table)
+ {
+ foreach($table->columns as $column)
+ {
+ if(!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/',$column->name))
+ return $table->name.'.'.$column->name;
+ }
+ }
+
+ public function validateModelPath($attribute,$params)
+ {
+ if(Yii::getPathOfAlias($this->modelPath)===false)
+ $this->addError('modelPath','Model Path must be a valid path alias.');
+ }
+
+ public function validateBaseClass($attribute,$params)
+ {
+ $class=@Yii::import($this->baseClass,true);
+ if(!is_string($class) || !$this->classExists($class))
+ $this->addError('baseClass', "Class '{$this->baseClass}' does not exist or has syntax error.");
+ else if($class!=='CActiveRecord' && !is_subclass_of($class,'CActiveRecord'))
+ $this->addError('baseClass', "'{$this->model}' must extend from CActiveRecord.");
+ }
+
+ public function getTableSchema($tableName)
+ {
+ return Yii::app()->db->getSchema()->getTable($tableName);
+ }
+
+ public function generateLabels($table)
+ {
+ $labels=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);
+ if($label==='Id')
+ $label='ID';
+ $labels[$column->name]=$label;
+ }
+ return $labels;
+ }
+
+ public function generateRules($table)
+ {
+ $rules=array();
+ $required=array();
+ $integers=array();
+ $numerical=array();
+ $length=array();
+ $safe=array();
+ foreach($table->columns as $column)
+ {
+ if($column->autoIncrement)
+ 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')";
+
+ return $rules;
+ }
+
+ public function getRelations($className)
+ {
+ return isset($this->relations[$className]) ? $this->relations[$className] : array();
+ }
+
+ protected function removePrefix($tableName,$addBrackets=true)
+ {
+ if($addBrackets && Yii::app()->db->tablePrefix=='')
+ return $tableName;
+ $prefix=$this->tablePrefix!='' ? $this->tablePrefix : Yii::app()->db->tablePrefix;
+ if($prefix!='')
+ {
+ if($addBrackets && Yii::app()->db->tablePrefix!='')
+ {
+ $prefix=Yii::app()->db->tablePrefix;
+ $lb='{{';
+ $rb='}}';
+ }
+ else
+ $lb=$rb='';
+ if(($pos=strrpos($tableName,'.'))!==false)
+ {
+ $schema=substr($tableName,0,$pos);
+ $name=substr($tableName,$pos+1);
+ if(strpos($name,$prefix)===0)
+ return $schema.'.'.$lb.substr($name,strlen($prefix)).$rb;
+ }
+ else if(strpos($tableName,$prefix)===0)
+ return $lb.substr($tableName,strlen($prefix)).$rb;
+ }
+ return $tableName;
+ }
+
+ protected function generateRelations()
+ {
+ if(!$this->buildRelations)
+ return array();
+ $relations=array();
+ foreach(Yii::app()->db->schema->getTables() as $table)
+ {
+ if($this->tablePrefix!='' && strpos($table->name,$this->tablePrefix)!==0)
+ continue;
+ $tableName=$table->name;
+
+ if ($this->isRelationTable($table))
+ {
+ $pks=$table->primaryKey;
+ $fks=$table->foreignKeys;
+
+ $table0=$fks[$pks[0]][0];
+ $table1=$fks[$pks[1]][0];
+ $className0=$this->generateClassName($table0);
+ $className1=$this->generateClassName($table1);
+
+ $unprefixedTableName=$this->removePrefix($tableName);
+
+ $relationName=$this->generateRelationName($table0, $table1, true);
+ $relations[$className0][$relationName]="array(self::MANY_MANY, '$className1', '$unprefixedTableName($pks[0], $pks[1])')";
+
+ $relationName=$this->generateRelationName($table1, $table0, true);
+ $relations[$className1][$relationName]="array(self::MANY_MANY, '$className0', '$unprefixedTableName($pks[1], $pks[0])')";
+ }
+ else
+ {
+ $className=$this->generateClassName($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->generateClassName($refTable);
+
+ // Add relation for this table
+ $relationName=$this->generateRelationName($tableName, $fkName, false);
+ $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,false), $relationType==='HAS_MANY');
+ $i=1;
+ $rawName=$relationName;
+ while(isset($relations[$refClassName][$relationName]))
+ $relationName=$rawName.($i++);
+ $relations[$refClassName][$relationName]="array(self::$relationType, '$className', '$fkName')";
+ }
+ }
+ }
+ return $relations;
+ }
+
+ /**
+ * Checks if the given table is a "many to many" pivot 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
+ }
+
+ protected function generateClassName($tableName)
+ {
+ if($this->tableName===$tableName || ($pos=strrpos($this->tableName,'.'))!==false && substr($this->tableName,$pos+1)===$tableName)
+ return $this->modelClass;
+
+ $tableName=$this->removePrefix($tableName,false);
+ $className='';
+ foreach(explode('_',$tableName) as $name)
+ {
+ if($name!=='')
+ $className.=ucfirst($name);
+ }
+ return $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
+ * @return string the relation name
+ */
+ 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);
+
+ if($multiple)
+ $relationName=$this->pluralize($relationName);
+
+ $names=preg_split('/_+/',$relationName,-1,PREG_SPLIT_NO_EMPTY);
+ if(empty($names)) return $relationName; // unlikely
+ for($name=$names[0], $i=1;$i<count($names);++$i)
+ $name.=ucfirst($names[$i]);
+
+ $rawName=$name;
+ $table=Yii::app()->db->schema->getTable($tableName);
+ $i=0;
+ while(isset($table->columns[$name]))
+ $name=$rawName.($i++);
+
+ return $name;
+ }
+} \ No newline at end of file
diff --git a/framework/gii/generators/model/ModelGenerator.php b/framework/gii/generators/model/ModelGenerator.php
new file mode 100644
index 0000000..fd2574e
--- /dev/null
+++ b/framework/gii/generators/model/ModelGenerator.php
@@ -0,0 +1,6 @@
+<?php
+
+class ModelGenerator extends CCodeGenerator
+{
+ public $codeModel='gii.generators.model.ModelCode';
+} \ No newline at end of file
diff --git a/framework/gii/generators/model/templates/default/model.php b/framework/gii/generators/model/templates/default/model.php
new file mode 100644
index 0000000..46e185e
--- /dev/null
+++ b/framework/gii/generators/model/templates/default/model.php
@@ -0,0 +1,145 @@
+<?php
+/**
+ * This is the template for generating the model class of a specified table.
+ * - $this: the ModelCode object
+ * - $tableName: the table name for this class (prefix is already removed if necessary)
+ * - $modelClass: the model class name
+ * - $columns: list of table columns (name=>CDbColumnSchema)
+ * - $labels: list of attribute labels (name=>label)
+ * - $rules: list of validation rules
+ * - $relations: list of relations (name=>relation declaration)
+ */
+?>
+<?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; ?>
+<?php if(!empty($relations)): ?>
+ *
+ * The followings are the available model relations:
+<?php foreach($relations as $name=>$relation): ?>
+ * @property <?php
+ if (preg_match("~^array\(self::([^,]+), '([^']+)', '([^']+)'\)$~", $relation, $matches))
+ {
+ $relationType = $matches[1];
+ $relationModel = $matches[2];
+
+ switch($relationType){
+ case 'HAS_ONE':
+ echo $relationModel.' $'.$name."\n";
+ break;
+ case 'BELONGS_TO':
+ echo $relationModel.' $'.$name."\n";
+ break;
+ case 'HAS_MANY':
+ echo $relationModel.'[] $'.$name."\n";
+ break;
+ case 'MANY_MANY':
+ echo $relationModel.'[] $'.$name."\n";
+ break;
+ default:
+ echo 'mixed $'.$name."\n";
+ }
+ }
+ ?>
+<?php endforeach; ?>
+<?php endif; ?>
+ */
+class <?php echo $modelClass; ?> extends <?php echo $this->baseClass."\n"; ?>
+{
+ /**
+ * Returns the static model of the specified AR class.
+ * @param string $className active record class name.
+ * @return <?php echo $modelClass; ?> 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 $name=>$label): ?>
+ <?php echo "'$name' => '$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";
+ }
+ else
+ {
+ echo "\t\t\$criteria->compare('$name',\$this->$name);\n";
+ }
+}
+?>
+
+ return new CActiveDataProvider($this, array(
+ 'criteria'=>$criteria,
+ ));
+ }
+} \ No newline at end of file
diff --git a/framework/gii/generators/model/views/index.php b/framework/gii/generators/model/views/index.php
new file mode 100644
index 0000000..28040d3
--- /dev/null
+++ b/framework/gii/generators/model/views/index.php
@@ -0,0 +1,115 @@
+<?php
+$class=get_class($model);
+Yii::app()->clientScript->registerScript('gii.model',"
+$('#{$class}_modelClass').change(function(){
+ $(this).data('changed',$(this).val()!='');
+});
+$('#{$class}_tableName').bind('keyup change', function(){
+ var model=$('#{$class}_modelClass');
+ var tableName=$(this).val();
+ if(tableName.substring(tableName.length-1)!='*') {
+ $('.form .row.model-class').show();
+ }
+ else {
+ $('#{$class}_modelClass').val('');
+ $('.form .row.model-class').hide();
+ }
+ if(!model.data('changed')) {
+ var i=tableName.lastIndexOf('.');
+ if(i>=0)
+ tableName=tableName.substring(i+1);
+ var tablePrefix=$('#{$class}_tablePrefix').val();
+ if(tablePrefix!='' && tableName.indexOf(tablePrefix)==0)
+ tableName=tableName.substring(tablePrefix.length);
+ var modelClass='';
+ $.each(tableName.split('_'), function() {
+ if(this.length>0)
+ modelClass+=this.substring(0,1).toUpperCase()+this.substring(1);
+ });
+ model.val(modelClass);
+ }
+});
+$('.form .row.model-class').toggle($('#{$class}_tableName').val().substring($('#{$class}_tableName').val().length-1)!='*');
+");
+?>
+<h1>Model Generator</h1>
+
+<p>This generator generates a model class for the specified database table.</p>
+
+<?php $form=$this->beginWidget('CCodeForm', array('model'=>$model)); ?>
+
+ <div class="row sticky">
+ <?php echo $form->labelEx($model,'tablePrefix'); ?>
+ <?php echo $form->textField($model,'tablePrefix', array('size'=>65)); ?>
+ <div class="tooltip">
+ This refers to the prefix name that is shared by all database tables.
+ Setting this property mainly affects how model classes are named based on
+ the table names. For example, a table prefix <code>tbl_</code> with a table name <code>tbl_post</code>
+ will generate a model class named <code>Post</code>.
+ <br/>
+ Leave this field empty if your database tables do not use common prefix.
+ </div>
+ <?php echo $form->error($model,'tablePrefix'); ?>
+ </div>
+ <div class="row">
+ <?php echo $form->labelEx($model,'tableName'); ?>
+ <?php $this->widget('zii.widgets.jui.CJuiAutoComplete',array(
+ 'model'=>$model,
+ 'attribute'=>'tableName',
+ 'name'=>'tableName',
+ 'source'=>array_keys(Yii::app()->db->schema->getTables()),
+ 'options'=>array(
+ 'minLength'=>'0',
+ ),
+ 'htmlOptions'=>array(
+ 'id'=>'ModelCode_tableName',
+ 'size'=>'65'
+ ),
+ )); ?>
+ <div class="tooltip">
+ This refers to the table name that a new model class should be generated for
+ (e.g. <code>tbl_user</code>). It can contain schema name, if needed (e.g. <code>public.tbl_post</code>).
+ You may also enter <code>*</code> (or <code>schemaName.*</code> for a particular DB schema)
+ to generate a model class for EVERY table.
+ </div>
+ <?php echo $form->error($model,'tableName'); ?>
+ </div>
+ <div class="row model-class">
+ <?php echo $form->label($model,'modelClass',array('required'=>true)); ?>
+ <?php echo $form->textField($model,'modelClass', array('size'=>65)); ?>
+ <div class="tooltip">
+ This is the name of the model class to be generated (e.g. <code>Post</code>, <code>Comment</code>).
+ It is case-sensitive.
+ </div>
+ <?php echo $form->error($model,'modelClass'); ?>
+ </div>
+ <div class="row sticky">
+ <?php echo $form->labelEx($model,'baseClass'); ?>
+ <?php echo $form->textField($model,'baseClass',array('size'=>65)); ?>
+ <div class="tooltip">
+ This is the class that the new model class will extend from.
+ Please make sure the class exists and can be autoloaded.
+ </div>
+ <?php echo $form->error($model,'baseClass'); ?>
+ </div>
+ <div class="row sticky">
+ <?php echo $form->labelEx($model,'modelPath'); ?>
+ <?php echo $form->textField($model,'modelPath', array('size'=>65)); ?>
+ <div class="tooltip">
+ This refers to the directory that the new model class file should be generated under.
+ It should be specified in the form of a path alias, for example, <code>application.models</code>.
+ </div>
+ <?php echo $form->error($model,'modelPath'); ?>
+ </div>
+ <div class="row">
+ <?php echo $form->labelEx($model,'buildRelations'); ?>
+ <?php echo $form->checkBox($model,'buildRelations'); ?>
+ <div class="tooltip">
+ Whether relations should be generated for the model class.
+ In order to generate relations, full scan of the whole database is needed.
+ You should disable this option if your database contains too many tables.
+ </div>
+ <?php echo $form->error($model,'buildRelations'); ?>
+ </div>
+
+<?php $this->endWidget(); ?>
diff --git a/framework/gii/generators/module/ModuleCode.php b/framework/gii/generators/module/ModuleCode.php
new file mode 100644
index 0000000..4fb90f0
--- /dev/null
+++ b/framework/gii/generators/module/ModuleCode.php
@@ -0,0 +1,91 @@
+<?php
+
+class ModuleCode extends CCodeModel
+{
+ public $moduleID;
+
+ public function rules()
+ {
+ return array_merge(parent::rules(), array(
+ array('moduleID', 'filter', 'filter'=>'trim'),
+ array('moduleID', 'required'),
+ array('moduleID', 'match', 'pattern'=>'/^\w+$/', 'message'=>'{attribute} should only contain word characters.'),
+ ));
+ }
+
+ public function attributeLabels()
+ {
+ return array_merge(parent::attributeLabels(), array(
+ 'moduleID'=>'Module ID',
+ ));
+ }
+
+ public function successMessage()
+ {
+ if(Yii::app()->hasModule($this->moduleID))
+ return 'The module has been generated successfully. You may '.CHtml::link('try it now', Yii::app()->createUrl($this->moduleID), array('target'=>'_blank')).'.';
+
+ $output=<<<EOD
+<p>The module has been generated successfully.</p>
+<p>To access the module, you need to modify the application configuration as follows:</p>
+EOD;
+ $code=<<<EOD
+<?php
+return array(
+ 'modules'=>array(
+ '{$this->moduleID}',
+ ),
+ ......
+);
+EOD;
+
+ return $output.highlight_string($code,true);
+ }
+
+ public function prepare()
+ {
+ $this->files=array();
+ $templatePath=$this->templatePath;
+ $modulePath=$this->modulePath;
+ $moduleTemplateFile=$templatePath.DIRECTORY_SEPARATOR.'module.php';
+
+ $this->files[]=new CCodeFile(
+ $modulePath.'/'.$this->moduleClass.'.php',
+ $this->render($moduleTemplateFile)
+ );
+
+ $files=CFileHelper::findFiles($templatePath,array(
+ 'exclude'=>array('.svn'),
+ ));
+
+ foreach($files as $file)
+ {
+ if($file!==$moduleTemplateFile)
+ {
+ if(CFileHelper::getExtension($file)==='php')
+ $content=$this->render($file);
+ else if(basename($file)==='.yii') // an empty directory
+ {
+ $file=dirname($file);
+ $content=null;
+ }
+ else
+ $content=file_get_contents($file);
+ $this->files[]=new CCodeFile(
+ $modulePath.substr($file,strlen($templatePath)),
+ $content
+ );
+ }
+ }
+ }
+
+ public function getModuleClass()
+ {
+ return ucfirst($this->moduleID).'Module';
+ }
+
+ public function getModulePath()
+ {
+ return Yii::app()->modulePath.DIRECTORY_SEPARATOR.$this->moduleID;
+ }
+} \ No newline at end of file
diff --git a/framework/gii/generators/module/ModuleGenerator.php b/framework/gii/generators/module/ModuleGenerator.php
new file mode 100644
index 0000000..c1ba1b6
--- /dev/null
+++ b/framework/gii/generators/module/ModuleGenerator.php
@@ -0,0 +1,6 @@
+<?php
+
+class ModuleGenerator extends CCodeGenerator
+{
+ public $codeModel='gii.generators.module.ModuleCode';
+} \ No newline at end of file
diff --git a/framework/gii/generators/module/templates/default/components/.yii b/framework/gii/generators/module/templates/default/components/.yii
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/framework/gii/generators/module/templates/default/components/.yii
diff --git a/framework/gii/generators/module/templates/default/controllers/DefaultController.php b/framework/gii/generators/module/templates/default/controllers/DefaultController.php
new file mode 100644
index 0000000..1936819
--- /dev/null
+++ b/framework/gii/generators/module/templates/default/controllers/DefaultController.php
@@ -0,0 +1,9 @@
+<?php echo "<?php\n"; ?>
+
+class DefaultController extends Controller
+{
+ public function actionIndex()
+ {
+ $this->render('index');
+ }
+} \ No newline at end of file
diff --git a/framework/gii/generators/module/templates/default/messages/.yii b/framework/gii/generators/module/templates/default/messages/.yii
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/framework/gii/generators/module/templates/default/messages/.yii
diff --git a/framework/gii/generators/module/templates/default/models/.yii b/framework/gii/generators/module/templates/default/models/.yii
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/framework/gii/generators/module/templates/default/models/.yii
diff --git a/framework/gii/generators/module/templates/default/module.php b/framework/gii/generators/module/templates/default/module.php
new file mode 100644
index 0000000..b0d98d0
--- /dev/null
+++ b/framework/gii/generators/module/templates/default/module.php
@@ -0,0 +1,28 @@
+<?php echo "<?php\n"; ?>
+
+class <?php echo $this->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 $this->moduleID; ?>.models.*',
+ '<?php echo $this->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/gii/generators/module/templates/default/views/default/index.php b/framework/gii/generators/module/templates/default/views/default/index.php
new file mode 100644
index 0000000..3ee8a6e
--- /dev/null
+++ b/framework/gii/generators/module/templates/default/views/default/index.php
@@ -0,0 +1,15 @@
+<?php echo "<?php\n"; ?>
+$this->breadcrumbs=array(
+ $this->module->id,
+);
+?>
+<h1><?php echo "<?php"; ?> echo $this->uniqueId . '/' . $this->action->id; ?></h1>
+
+<p>
+This is the view content for action "<?php echo "<?php"; ?> echo $this->action->id; ?>".
+The action belongs to the controller "<?php echo "<?php"; ?> echo get_class($this); ?>"
+in the "<?php echo "<?php"; ?> echo $this->module->id; ?>" module.
+</p>
+<p>
+You may customize this page by editing <tt><?php echo "<?php"; ?> echo __FILE__; ?></tt>
+</p> \ No newline at end of file
diff --git a/framework/gii/generators/module/templates/default/views/layouts/.yii b/framework/gii/generators/module/templates/default/views/layouts/.yii
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/framework/gii/generators/module/templates/default/views/layouts/.yii
diff --git a/framework/gii/generators/module/views/index.php b/framework/gii/generators/module/views/index.php
new file mode 100644
index 0000000..711e7e7
--- /dev/null
+++ b/framework/gii/generators/module/views/index.php
@@ -0,0 +1,19 @@
+<h1>Module Generator</h1>
+
+<p>This generator helps you to generate the skeleton code needed by a Yii module.</p>
+
+<?php $form=$this->beginWidget('CCodeForm', array('model'=>$model)); ?>
+
+ <div class="row">
+ <?php echo $form->labelEx($model,'moduleID'); ?>
+ <?php echo $form->textField($model,'moduleID',array('size'=>65)); ?>
+ <div class="tooltip">
+ Module ID is case-sensitive. It should only contain word characters.
+ The generated module class will be named after the module ID.
+ For example, a module ID <code>forum</code> will generate the module class
+ <code>ForumModule</code>.
+ </div>
+ <?php echo $form->error($model,'moduleID'); ?>
+ </div>
+
+<?php $this->endWidget(); ?>
diff --git a/framework/gii/models/LoginForm.php b/framework/gii/models/LoginForm.php
new file mode 100644
index 0000000..f4b838c
--- /dev/null
+++ b/framework/gii/models/LoginForm.php
@@ -0,0 +1,49 @@
+<?php
+
+Yii::import('gii.components.UserIdentity');
+
+class LoginForm extends CFormModel
+{
+ public $password;
+
+ private $_identity;
+
+ public function rules()
+ {
+ return array(
+ array('password', 'required'),
+ array('password', 'authenticate'),
+ );
+ }
+
+ /**
+ * Authenticates the password.
+ * This is the 'authenticate' validator as declared in rules().
+ */
+ public function authenticate($attribute,$params)
+ {
+ $this->_identity=new UserIdentity('yiier',$this->password);
+ if(!$this->_identity->authenticate())
+ $this->addError('password','Incorrect password.');
+ }
+
+ /**
+ * Logs in the user using the given password in the model.
+ * @return boolean whether login is successful
+ */
+ public function login()
+ {
+ if($this->_identity===null)
+ {
+ $this->_identity=new UserIdentity('yiier',$this->password);
+ $this->_identity->authenticate();
+ }
+ if($this->_identity->errorCode===UserIdentity::ERROR_NONE)
+ {
+ Yii::app()->user->login($this->_identity);
+ return true;
+ }
+ else
+ return false;
+ }
+}
diff --git a/framework/gii/views/common/code.php b/framework/gii/views/common/code.php
new file mode 100644
index 0000000..9d996c9
--- /dev/null
+++ b/framework/gii/views/common/code.php
@@ -0,0 +1,16 @@
+<?php
+if($file->type==='php')
+{
+ echo '<div class="content">';
+ highlight_string($file->content);
+ echo '</div>';
+}
+else if(in_array($file->type,array('txt','js','css')))
+{
+ echo '<div class="content">';
+ echo nl2br($file->content);
+ echo '</div>';
+}
+else
+ echo '<div class="error">Preview is not available for this file type.</div>';
+?> \ No newline at end of file
diff --git a/framework/gii/views/common/diff.php b/framework/gii/views/common/diff.php
new file mode 100644
index 0000000..ed3f89c
--- /dev/null
+++ b/framework/gii/views/common/diff.php
@@ -0,0 +1,9 @@
+<?php if($diff===false): ?>
+ <div class="error">Diff is not supported for this file type.</div>
+<?php elseif(empty($diff)): ?>
+ <div class="error">No changes.</div>
+<?php else: ?>
+ <div class="content">
+ <pre class="diff"><?php echo $diff; ?></pre>
+ </div>
+<?php endif; ?>
diff --git a/framework/gii/views/common/generator.php b/framework/gii/views/common/generator.php
new file mode 100644
index 0000000..8da17d5
--- /dev/null
+++ b/framework/gii/views/common/generator.php
@@ -0,0 +1,76 @@
+<div class="row template sticky">
+ <?php echo $this->labelEx($model,'template'); ?>
+ <?php echo $this->dropDownList($model,'template',$templates); ?>
+ <div class="tooltip">
+ Please select which set of the templates should be used to generated the code.
+ </div>
+ <?php echo $this->error($model,'template'); ?>
+</div>
+
+<div class="buttons">
+ <?php echo CHtml::submitButton('Preview',array('name'=>'preview')); ?>
+
+ <?php if($model->status===CCodeModel::STATUS_PREVIEW && !$model->hasErrors()): ?>
+ <?php echo CHtml::submitButton('Generate',array('name'=>'generate')); ?>
+ <?php endif; ?>
+</div>
+
+<?php if(!$model->hasErrors()): ?>
+ <div class="feedback">
+ <?php if($model->status===CCodeModel::STATUS_SUCCESS): ?>
+ <div class="success">
+ <?php echo $model->successMessage(); ?>
+ </div>
+ <?php elseif($model->status===CCodeModel::STATUS_ERROR): ?>
+ <div class="error">
+ <?php echo $model->errorMessage(); ?>
+ </div>
+ <?php endif; ?>
+
+ <?php if(isset($_POST['generate'])): ?>
+ <pre class="results"><?php echo $model->renderResults(); ?></pre>
+ <?php elseif(isset($_POST['preview'])): ?>
+ <?php echo CHtml::hiddenField("answers"); ?>
+ <table class="preview">
+ <tr>
+ <th class="file">Code File</th>
+ <th class="confirm">
+ <label for="check-all">Generate</label>
+ <?php
+ $count=0;
+ foreach($model->files as $file)
+ {
+ if($file->operation!==CCodeFile::OP_SKIP)
+ $count++;
+ }
+ if($count>1)
+ echo '<input type="checkbox" name="checkAll" id="check-all" />';
+ ?>
+ </th>
+ </tr>
+ <?php foreach($model->files as $i=>$file): ?>
+ <tr class="<?php echo $file->operation; ?>">
+ <td class="file">
+ <?php echo CHtml::link(CHtml::encode($file->relativePath), array('code','id'=>$i), array('class'=>'view-code','rel'=>$file->path)); ?>
+ <?php if($file->operation===CCodeFile::OP_OVERWRITE): ?>
+ (<?php echo CHtml::link('diff', array('diff','id'=>$i), array('class'=>'view-code','rel'=>$file->path)); ?>)
+ <?php endif; ?>
+ </td>
+ <td class="confirm">
+ <?php
+ if($file->operation===CCodeFile::OP_SKIP)
+ echo 'unchanged';
+ else
+ {
+ $key=md5($file->path);
+ echo CHtml::label($file->operation, "answers_{$key}")
+ . ' ' . CHtml::checkBox("answers[$key]", $model->confirmed($file));
+ }
+ ?>
+ </td>
+ </tr>
+ <?php endforeach; ?>
+ </table>
+ <?php endif; ?>
+ </div>
+<?php endif; ?>
diff --git a/framework/gii/views/default/error.php b/framework/gii/views/default/error.php
new file mode 100644
index 0000000..a6a25a3
--- /dev/null
+++ b/framework/gii/views/default/error.php
@@ -0,0 +1,5 @@
+<h1>Error <?php echo $code; ?></h1>
+
+<div class="error">
+<?php echo CHtml::encode($message); ?>
+</div> \ No newline at end of file
diff --git a/framework/gii/views/default/index.php b/framework/gii/views/default/index.php
new file mode 100644
index 0000000..f3470c9
--- /dev/null
+++ b/framework/gii/views/default/index.php
@@ -0,0 +1,11 @@
+<h1>Welcome to Yii Code Generator!</h1>
+
+<p>
+ You may use the following generators to quickly build up your Yii application:
+</p>
+<ul>
+ <?php foreach($this->module->controllerMap as $name=>$config): ?>
+ <li><?php echo CHtml::link(ucwords(CHtml::encode($name).' generator'),array('/gii/'.$name));?></li>
+ <?php endforeach; ?>
+</ul>
+
diff --git a/framework/gii/views/default/login.php b/framework/gii/views/default/login.php
new file mode 100644
index 0000000..6d462a0
--- /dev/null
+++ b/framework/gii/views/default/login.php
@@ -0,0 +1,11 @@
+<div class="form login">
+<?php $form=$this->beginWidget('CActiveForm'); ?>
+ <p>Please enter your password</p>
+
+ <?php echo $form->passwordField($model,'password'); ?>
+ <?php echo $form->error($model,'password'); ?>
+
+ <?php echo CHtml::submitButton('Enter'); ?>
+
+<?php $this->endWidget(); ?>
+</div><!-- form -->
diff --git a/framework/gii/views/layouts/column1.php b/framework/gii/views/layouts/column1.php
new file mode 100644
index 0000000..2d35771
--- /dev/null
+++ b/framework/gii/views/layouts/column1.php
@@ -0,0 +1,7 @@
+<?php $this->beginContent('gii.views.layouts.main'); ?>
+<div class="container">
+ <div id="content">
+ <?php echo $content; ?>
+ </div><!-- content -->
+</div>
+<?php $this->endContent(); ?> \ No newline at end of file
diff --git a/framework/gii/views/layouts/generator.php b/framework/gii/views/layouts/generator.php
new file mode 100644
index 0000000..3abbcf2
--- /dev/null
+++ b/framework/gii/views/layouts/generator.php
@@ -0,0 +1,25 @@
+<?php $this->beginContent('gii.views.layouts.main'); ?>
+<div class="container">
+ <div class="span-4">
+ <div id="sidebar">
+ <?php $this->beginWidget('zii.widgets.CPortlet', array(
+ 'title'=>'Generators',
+ )); ?>
+ <ul>
+ <?php foreach($this->module->controllerMap as $name=>$config): ?>
+ <li><?php echo CHtml::link(ucwords(CHtml::encode($name).' generator'),array('/gii/'.$name));?></li>
+ <?php endforeach; ?>
+ </ul>
+ <?php $this->endWidget(); ?>
+ </div><!-- sidebar -->
+ </div>
+ <div class="span-16">
+ <div id="content">
+ <?php echo $content; ?>
+ </div><!-- content -->
+ </div>
+ <div class="span-4 last">
+ &nbsp;
+ </div>
+</div>
+<?php $this->endContent(); ?> \ No newline at end of file
diff --git a/framework/gii/views/layouts/main.php b/framework/gii/views/layouts/main.php
new file mode 100644
index 0000000..7886025
--- /dev/null
+++ b/framework/gii/views/layouts/main.php
@@ -0,0 +1,57 @@
+<?php
+$cs=Yii::app()->clientScript;
+$cs->coreScriptPosition=CClientScript::POS_HEAD;
+$cs->scriptMap=array();
+$baseUrl=$this->module->assetsUrl;
+$cs->registerCoreScript('jquery');
+$cs->registerScriptFile($baseUrl.'/js/jquery.tooltip-1.2.6.min.js');
+$cs->registerScriptFile($baseUrl.'/js/fancybox/jquery.fancybox-1.3.1.pack.js');
+$cs->registerCssFile($baseUrl.'/js/fancybox/jquery.fancybox-1.3.1.css');
+?>
+<!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 $this->module->assetsUrl; ?>/css/screen.css" media="screen, projection" />
+ <link rel="stylesheet" type="text/css" href="<?php echo $this->module->assetsUrl; ?>/css/print.css" media="print" />
+ <!--[if lt IE 8]>
+ <link rel="stylesheet" type="text/css" href="<?php echo $this->module->assetsUrl; ?>/css/ie.css" media="screen, projection" />
+ <![endif]-->
+
+ <link rel="stylesheet" type="text/css" href="<?php echo $this->module->assetsUrl; ?>/css/main.css" />
+
+ <title><?php echo CHtml::encode($this->pageTitle); ?></title>
+
+ <script type="text/javascript" src="<?php echo $this->module->assetsUrl; ?>/js/main.js"></script>
+
+</head>
+
+<body>
+
+<div class="container" id="page">
+ <div id="header">
+ <div class="top-menus">
+ <?php echo CHtml::link('help','http://www.yiiframework.com/doc/guide/topics.gii'); ?> |
+ <?php echo CHtml::link('webapp',Yii::app()->homeUrl); ?> |
+ <a href="http://www.yiiframework.com">yii</a>
+ <?php if(!Yii::app()->user->isGuest): ?>
+ | <?php echo CHtml::link('logout',array('/gii/default/logout')); ?>
+ <?php endif; ?>
+ </div>
+ <div id="logo"><?php echo CHtml::link(CHtml::image($this->module->assetsUrl.'/images/logo.png'),array('/gii')); ?></div>
+ </div><!-- header -->
+
+ <?php echo $content; ?>
+
+</div><!-- page -->
+
+<div id="footer">
+ <?php echo Yii::powered(); ?>
+ <br/>A product of <a href="http://www.yiisoft.com">Yii Software LLC</a>.
+</div><!-- footer -->
+
+</body>
+</html> \ No newline at end of file