From 94e4c076eba53cafdb8c813e1ec4b52064366f62 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Thu, 15 Aug 2013 08:39:20 -0400 Subject: [PATCH] gii WIP --- framework/yii/gii/Generator.php | 428 +++++++++++++++++++++ framework/yii/gii/GiiAsset.php | 30 ++ framework/yii/gii/Module.php | 101 +++++ framework/yii/gii/assets/gii.js | 15 + framework/yii/gii/assets/logo.png | Bin 0 -> 7207 bytes framework/yii/gii/assets/main.css | 43 +++ .../yii/gii/controllers/DefaultController.php | 54 +++ .../yii/gii/generators/controller/Generator.php | 166 ++++++++ .../yii/gii/generators/controller/views/form.php | 36 ++ framework/yii/gii/generators/crud/Generator.php | 27 ++ framework/yii/gii/generators/form/Generator.php | 26 ++ framework/yii/gii/generators/model/Generator.php | 26 ++ framework/yii/gii/generators/module/Generator.php | 26 ++ framework/yii/gii/views/default/index.php | 34 ++ framework/yii/gii/views/default/view.php | 19 + framework/yii/gii/views/layouts/generator.php | 31 ++ framework/yii/gii/views/layouts/main.php | 55 +++ 17 files changed, 1117 insertions(+) create mode 100644 framework/yii/gii/Generator.php create mode 100644 framework/yii/gii/GiiAsset.php create mode 100644 framework/yii/gii/Module.php create mode 100644 framework/yii/gii/assets/gii.js create mode 100644 framework/yii/gii/assets/logo.png create mode 100644 framework/yii/gii/assets/main.css create mode 100644 framework/yii/gii/controllers/DefaultController.php create mode 100644 framework/yii/gii/generators/controller/Generator.php create mode 100644 framework/yii/gii/generators/controller/views/form.php create mode 100644 framework/yii/gii/generators/crud/Generator.php create mode 100644 framework/yii/gii/generators/form/Generator.php create mode 100644 framework/yii/gii/generators/model/Generator.php create mode 100644 framework/yii/gii/generators/module/Generator.php create mode 100644 framework/yii/gii/views/default/index.php create mode 100644 framework/yii/gii/views/default/view.php create mode 100644 framework/yii/gii/views/layouts/generator.php create mode 100644 framework/yii/gii/views/layouts/main.php diff --git a/framework/yii/gii/Generator.php b/framework/yii/gii/Generator.php new file mode 100644 index 0000000..5e7b1f0 --- /dev/null +++ b/framework/yii/gii/Generator.php @@ -0,0 +1,428 @@ + + * @since 2.0 + */ +class Generator extends Model +{ + /** + * @var string + */ + public $id; + + /** + * @return string name of the code generator + */ + public function getName() + { + return 'unknown'; + } + + public function getDescription() + { + return ''; + } + + public function getUrl() + { + return Yii::$app->controller->createUrl('default/view', array('id' => $this->id)); + } + + public function renderForm() + { + return ''; + } + + public function renderFileList() + { + return ''; + } + + 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. + */ + public function prepare() + { + + } + + /** + * Declares the model validation rules. + * Child classes must override this method in the following format: + *
+	 * return array_merge(parent::rules(), array(
+	 *     ...rules for the child class...
+	 * ));
+	 * 
+ * @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: + *
+	 * return array_merge(parent::attributeLabels(), array(
+	 *     ...labels for the child class attributes...
+	 * ));
+	 * 
+ * @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. + * @throws CHttpException 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]; + } elseif (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 + * @return bool whether the confirmation is found in {@link answers} with appropriate {@link operation} + */ + 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 + * @throws CException is template file does not exist + * @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 .= "generating {$file->relativePath}
{$file->error}
\n"; + } elseif ($file->operation === CCodeFile::OP_NEW && $this->confirmed($file)) { + $output .= ' generated ' . $file->relativePath . "\n"; + } elseif ($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, "_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'; + } + + /** + * 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.'); + } + } +} diff --git a/framework/yii/gii/GiiAsset.php b/framework/yii/gii/GiiAsset.php new file mode 100644 index 0000000..54b802c --- /dev/null +++ b/framework/yii/gii/GiiAsset.php @@ -0,0 +1,30 @@ + + * @since 2.0 + */ +class GiiAsset extends AssetBundle +{ + public $sourcePath = '@yii/gii/assets'; + public $css = array( + 'main.css', + ); + public $js = array( + 'gii.js', + ); + public $depends = array( + 'yii\web\YiiAsset', + 'yii\bootstrap\BootstrapAsset', + 'yii\bootstrap\BootstrapPluginAsset', + ); +} diff --git a/framework/yii/gii/Module.php b/framework/yii/gii/Module.php new file mode 100644 index 0000000..1f3be20 --- /dev/null +++ b/framework/yii/gii/Module.php @@ -0,0 +1,101 @@ + + * @since 2.0 + */ +class Module extends \yii\base\Module +{ + public $controllerNamespace = 'yii\gii\controllers'; + /** + * @var array the list of IPs that are allowed to access this module. + * Each array element represents a single IP filter which can be either an IP address + * or an address with wildcard (e.g. 192.168.0.*) to represent a network segment. + * The default value is `array('127.0.0.1', '::1')`, which means the module can only be accessed + * by localhost. + */ + public $allowedIPs = 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. + */ + public $generators = array(); + /** + * @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; + public $enabled = true; + + + /** + * Initializes the gii module. + */ + public function init() + { + parent::init(); + foreach (array_merge($this->coreGenerators(), $this->generators) as $id => $config) { + $config['id'] = $id; + $this->generators[$id] = Yii::createObject($config); + } + } + + public function beforeAction($action) + { + if ($this->checkAccess()) { + return parent::beforeAction($action); + } else { + throw new HttpException(403, 'You are not allowed to access this page.'); + } + } + + protected function checkAccess() + { + $ip = Yii::$app->getRequest()->getUserIP(); + foreach ($this->allowedIPs as $filter) { + if ($filter === '*' || $filter === $ip || (($pos = strpos($filter, '*')) !== false && !strncmp($ip, $filter, $pos))) { + return true; + } + } + return false; + } + + protected function coreGenerators() + { + return array( + 'model' => array( + 'class' => 'yii\gii\generators\model\Generator', + ), + 'crud' => array( + 'class' => 'yii\gii\generators\crud\Generator', + ), + 'controller' => array( + 'class' => 'yii\gii\generators\controller\Generator', + ), + 'form' => array( + 'class' => 'yii\gii\generators\form\Generator', + ), + 'module' => array( + 'class' => 'yii\gii\generators\module\Generator', + ), + ); + } +} diff --git a/framework/yii/gii/assets/gii.js b/framework/yii/gii/assets/gii.js new file mode 100644 index 0000000..1ff3f4a --- /dev/null +++ b/framework/yii/gii/assets/gii.js @@ -0,0 +1,15 @@ +yii.gii = (function ($) { + return { + init: function () { + $('.hint-block').each(function() { + var $hint = $(this); + $hint.parent().find('input,select,textarea').popover({ + html: true, + trigger: 'focus', + placement: 'right', + content: $hint.html() + }); + }); + } + }; +})(jQuery); diff --git a/framework/yii/gii/assets/logo.png b/framework/yii/gii/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..a92024118dfd06aabcdf39afb6721477bf2254ae GIT binary patch literal 7207 zcmV+?9N6QDP)@&TSyq~W zbg2T0-Keq0^mxAWhnaO;iX=Dc&CU2c|A(DD@0^|8eShyM@2vIYf3jrBk|j%)ELpOi zp(X|QduvumpT2e>z224qkR|I6$V1)lt$CpX9KSd=;Dp<;el+t)pV;YPy_?Gb$ddIt zv?OBC2#-jkyanN(lY6)!%?}?)b3*&m?2!JYZlQg;$NfA+bfD4+9jVNr)5Y_XyM&axc5^GA`AMfg0@CUj(*>(z6|_1=L0`p~(yC|^ zTESo~KWIyb2+ouAsB&d>OiF1Yo)etCXR|H~e|dejvpFST;W`Z%*k z>(%Du6=z24Se_~+0WN^`Wi&^tC}UcDd=Nur_+Vi~-wrYWvi>s4W|;DOH_a#*0FXE$ znYE%~%Ql*bRA$$S&N>04Zd5+?Q#wDZ2VI!clP*Fi{6|EJZTFH?LDc}+YYGI_|;^zgxvbZa4R>WO( zTA{jHrCwW^c)dA4@335bxrYpZtQwWkwce`*{hO!@j6R@31xKYJZRpJK52<8ydrs{{ zkh(HRpU?$>G`AOBT+oLuE$K&>mk*#Ts|@Mtn$PLldZXMM8%=uE1k?6JE4v*@!)QBe zTOupLinb;UCBU_jq1vEks8k%OgeZm7Z)v1a40rrqd*)rmsH<%k$6UOyB<3P5j=p$n zY4oLj8k_oBGq#<9ZAr|f`%7XjD`Ws<)u=+l4}7@cK*biE-VJLzhsJ`c{*>H+PthtPZBVCx?gZJvoB- zFYQSlPP>z=X(wwtg9WHIGgRIT)cQDcL@5C%<15Qm1NEOZO!Fhkr@2Rzlc@ROPhsXfPs!_$p&8HP9k>0hXQcmwsC!MwHOy?#tNUq%V`7indutFMK#Vq~t7YtcPx) zWg}z&WYwq=(^j2}&0F6`R4PGK64SfM-71?6b3S8`xJFvi_i;q>GGdU7>H0QPy0KHi z5Si1rJ{I(?{}8$rVEMzh2P`c_|G^p7^@G#LmIR*~OHk0MF%+0KngY^BkuQSmxN|p+>m)Y@$-Ng{TFlLc75yb34W=6aX_u02>@}lXehg9IP`VvF zlx`gwMz;?SyLsoBRYg=G@_6}3=J|wef_-ZO?eK4{8I$$+t+CA;@y{ zJAKK~H+L}+y;Rd!TmRF|oqThT@%Y=URT>|Dc_a9kL^=7NUMSiCSfwowVq4y7830*N zRq5FF+so{#iAqTCR#w(Y-)!L2Zi@-s*rBXU?R*)eAS;eU4AD_*x*Ixz?nW|5QKRTy z%xJo)c327DQF%^nqjMd8h{|=O$Xo{s&#`ByZ0RTibtnTsjX~N+J}D!R@^Qz{iklKc zGQ87F{;xKe#`@&y*zU_Nu-lhI_AI-7xgOPxv)@_puHD|8G>ofM_6iMT`*KIJjWpVm zv+%Jk$5Lvu^~v#+0g&}nT^ZN5!C9xyndhd6wUQW>7^JTT($)1Gl{Rx!+RjmFw;6+E z!Ks}k-8wi7(TMJa0i=<1FLo5&j~heZ#gCyYNt5rTr?31N{?!E&L*w!%Qf!_RMW1G< zavTt)7^n#XP-6*A3ZIi4of5f?o?x}*Y1eGMJGU4KBRoyoEVL6AMLG3_pHpG29xEMWS!w# zQgc`YS#L>6v)-0bzY27lQ_W%5jN6>1foa&5R2$eNKHN37t*I8UX|y@*g~9}oyi{hn z>*ADaWB}@>^(xc8t71NXG5302bM4&lqFWDN+~sh28EU10l}(3DoZf9m=Z>Qi8VZs}6kGZ+eFEJ)HJJ*N=TS+rM`!h&Y5z_so^ma@Xfh=f zP7;7}VxTG`6dDw0N+3GzJ86@(Np1aBU0|}@n3Q9=F_|jlos?p}E9vbj0zcl{dC%%u4lSZe)4=O zNLdgBpBbf7_okhhO37!YP-4*}iYstI%1051PDuCCE1MDf(nd|G3;0H!$vVol2{~r# z5(rYROGq{GPODO9nXXM#vW*^(OVq%mSeIa9=JnWSx;9<|6CYc!NR(o2{0oK2TWw;% zFg@Vtv`0qe=~|~!9%J3zBiu>zLUqHV-&r3C^p5K! z6@!$3h%|v7X4upBoWA{1=t!5dW>M}*56Vkh@?)NAN28qb8N)M6r&9$`MN{Ym!xWn* zM5in}p>`Ub5u9TEdR<{M_EhH#_B=rlKdu(0L95kD*fm-muYqZ>r`kqp8|3*ICS%V8 z3)m$7>kQMzBy+u|)i$2VMmk<cwHf-?iF4F8cz>T+0ysf4)kNbGyPKHOeGoa zl$E-ivQMp`%(OL&E`DpLms2+LW=6?$N-1)sgn~(=%5$dU3?EXx!@Li5AY=3(08lrW zqIC>cs&YPGsUpbmOI7M89#vs7{4!PvyGCECG%yWZp|X+M2Ch_TVETNO+CpkGTp72j z4ylEbCwmUxzIx_|9YTUcyD+4J**54w*H4`-^9Pf zW)Q-?)QZt;Um$)?9tGC_gxxDnVJDn_kL}-a>RYg0tsDUx#vppL?FaVp3;(_9rBY1S z02rqn$G*@V6pS#Ak zJjSBm@@SIyuN5XtB;n^J=lJhd1}VDoSm}=lBZ7o4m~G!MB-_{)$d7;J$0b;$wI&;} zis6fO3UKoH-*BEdZbO`+D<3~(KiA+7ATd-w@o|C(aSg(R+#pyr!2Ev3t2O-7camO)^|7J)hD<(bs0Ztybh3yA~aY5)< zVhkcIgK!JJg!gspQ(OGux-LA-l*?0?EHSnbG$>P4s<1d4xLO} zM`@XBDK&FFoy_w7F)l5rQ(n32qDq*iFieveCPXF&3d^=l4Jo#J`}e~1nMY(!&&81h z@#AV?`phFr3A?BsizAmk_7Q0Vn?_3_-8Hr)$1R?)c|@$LEe+v`aUNfiG-bky1}N65 zOb6f?(6Gwzg^6Pr022{9ab;yc7g<^2UK?PTiYMQx2#5x zRLU-xXsoXvuh`~oG?P-Lu*5i|PoHe6c`i zUL)SL9sB_t=|Y3g2|#hgnu=p&35Z6lhaBNX;x)xI^SO%h$G5m!GHd0j46k1^Ggnb+ zmKP;uZKj0mtrVBFZ)W~EM`cFIwBN)uFE$S_*maofK%W3WaP8Xugh&u2 zrg=j8nW|l9GRO;gT$OSF5eTWF#GfA`izF*pRB zy3RjF5I;Wop@+IkOP$9kyUxQHaa^bdrjO@`*>s&BsQF1 zR?GqbfTz0JL|lE5;%VP=D4n#hVd7y`MExS##nm4Z7OTAw9b6d9ANjetsB!RXW{CX^ zWQ8J4@=_Z&s}3flpnPs7R%q=-K?;+Q)^W|C7!NSuACYIhT49Vj1*sNl9rzKhWh4AZ zBL->{Kq*Nr@4z2?i2k@X>W?Ukv=*Ni8W@NSVzjPKXj~wQtW%qG=7yHtniqbNzBqoH!+S9u&kmx*{OuH# z8oD$*LfdxUkp>;z4rg|BJ3>$gw)jD{?WQ=Dj2^AyVzG?KRMB!F8b~0!)BK z>JRBHU_wNygjlhoHh-wg04Dqi4UBEob=MG`*me?;20k!WfX}xUm=f7uB-*Qq$X5`R z5dGj2ukpe_8m}&Y%{914#Xo1DImb%KJ^U=qiMUQnVy;qf=5dNG^nY-?VCUe30*CxK zPVb^~9jE;#;4(P>YCHX)s@;r(1ht)U@LWsRLk3l#n|@dU`(qtgrm@X9U?a7)nSM|M zQ|lQAES|I-su3nJ??!H4>lm!^@g^Or+7y;JAL$@NGzqJ>c4~!5A%#hpP!EtcVr3VF z2@EKZE>_oe20)b)(JV=Ol}3bkZL@m?UCLgeoJWnmA1i5Q`PYtq=p4Y;4e~mEl z#CH_xp)OTdQKcUyi;#j5Y9G-qE$D^L(?>#k{WqK=+NB*cG0npS#mZb=+K7}8<7)yN z5eF?}jM07{Qkc-%M-h*jsJeX`q($QUtP4zt;ArpT`n3I_UKITS&gx)VAEz)EI*GGU z3u!G7C4A0NNhT1;x(S%XeJ21@-D{)sh^PoZcwa9Rrjgszt;X%n`qkbi8$VZ|$@>dw z+QDL)#X!vsD`lwg|0rCfJ?V)aiA9S3e-^+^oCA#BpBVV)1D8OmRKPdQCIvX_Ir?`t znBv!=sY_tbrY-@CV0)|}4NUJ(46>2h-k%ttfvK5uki`==m%tif!h{A449jw<0d;}U zDH?|f2}raj9E5rSt0qz=Oe$f*c*3ULQbwvIHaBpLj-fb4(&a0MznU8m3QH+8qOjUa zo(Ei}Li#nax?7vXw5raLNo_h&hbSjarUyp9@17AoHMG#SEzA%_9H>I>2wmJ2t&9+lCc2@@3?#UuY-_zt0P(V|IiwJ4o z7an*2R&MwbVUmtRI*G{$2OHv* ztsPT~cAvF}bjUDm+ScEha&ta4K#lYl2FPpB354BjINLF8; zq!C-wX!MSBn!rHW`{a^yz-gKiTtL$f6?58G%C*#4=Rdz#+w+>PgWp~~2ftq!9;!}k zdnDR)9sM*gHFoqLD;=ln;JZ{~bMPCcT8 zZDj&H*DzKDSh~-K_pJ>R-n-D4aKf=TSrIj*{-l#v0@EWY93qSWNr3(i>x!_^yd0k^ zj>X%Z04v5pBIFEUTP$vZ?`r^Ey2m7i3H8`>Wr^<)u;klyi0^SD+%P(H<4GF6Ih96l zPp5GlrR?`1?K@4AgYr4;EBbBPk<$Lpifdha-ysZ*KPyY$!LNk>7PI1^nqbnm^Xf-gq6FKk71ojVl$XcZ$}V=y*} ztFg9Fgd@6O^6=!Ij2b~m^#)4Eq4R|ZhC33F@}V}X>jV&1>rs5(G^B1=sR5JrHK8y^ z`yY{z$FJX@R`%Au2Ha(U28#AQig>SUV@+FY*OGWm_(N)lpGs&$gZHHUytsi48=@BA z7hek*u^8?94{n{t)VcESFN78^5 zF=VtVj+E=-X^3|cS#M4un{8>8X&(dS=$A_q1M|)}9WK&+=7e*Xui^OM#*J-*8^X}Y z&Zqs85ysyG!~V_!K-xt-goOYpnAp^i^_=wK%){25zc@0de*^JHsm-GEs?^+(PrG=9VT&;8Fi4f zJBJP*HcrKsx~%6Tw4B4*PCvjKnn$VYyin?~C>%ghMcTKzGVQb6h&n2jMs7Rx^QfI! zegFLMsD6dhyTzQMUEy@>4*$(%ctW|Gx0N4zAw_!Dc-==Puff4<-dt>!}B*!>q&9d2R^ty>f)Kum5sR`@V|9kL{55 zag<6v_$+`%$_7a4Dz1GN$rGSbYgDH1$eck4V;tp>7WPuDSXOQguyz4n_dsW7$Y7^px z{j^&a>JVQ~=|pYf>`%=n2T{A}2dU%iBZM17dn^p6UQ41L@o$8NKASoz^bKxh8!^;V zIDK2kY25+-n<8}bzTxMu^L3u#wt0})rIRJ=zfL?^k5-8QPJV>6uMMYtAI&_BDD`Xi zd0{Tk8}x>gg5N?j0=1EWb+BAW%la>tQ^u=Ydn8sWX(Yas(5>+htOSpQ)C)4-tK>x~*Udb44} zhHurcU;p2FdT%%2W1aWjdrz1DHErCu@%uVDI?WBZ_n@QGw7H($zC8Vj!K6Pi2qLae z?&^-&maIF(V+`+<;?CdKb#&fr1Rw10odyjW;1{s3v0h=lECV3xe@U+b90monG}Y77 zYs0{_GceHUz>h!X$KCbxbUtg+M7NK=exrdrW{`nFV-wwX-x#F(#v8_ZBiAPCP1s(p zXR|g=SK+oTb(%7K!U_RfF&*{g1d-C~RS#8;O6Tm71AnSiE zD!pQ0VDS1o@4QnV5rm@$bM#IVe%uUENMHZGRsv)n>g(%t)YEI&8BjE7+@PCo>rZ>@ zx9a*S1Jp*RUOhytmii6f=+wALlWrIbFd5dC@TIT+t^xc5B1AucECV3x?+o6{LQ42b pH89|L=%qj8U6w3avSi8s{vT&N%Gvofi7@~G002ovPDHLkV1kbi)v^Ep literal 0 HcmV?d00001 diff --git a/framework/yii/gii/assets/main.css b/framework/yii/gii/assets/main.css new file mode 100644 index 0000000..3748bf0 --- /dev/null +++ b/framework/yii/gii/assets/main.css @@ -0,0 +1,43 @@ +body { + padding-top: 70px; +} + +.footer { + border-top: 1px solid #ddd; + margin-top: 30px; + padding-top: 15px; + padding-bottom: 30px; +} + +.jumbotron { + text-align: center; + background-color: transparent; +} + +.jumbotron .btn { + font-size: 21px; + padding: 14px 24px; +} + +.navbar-brand { + padding: 0 15px; +} + +.default-index .generator { + min-height: 200px; + margin-bottom: 20px; +} + +.list-group .glyphicon { + float: right; + margin-right: -15px; +} + +.popover { + max-width: 400px; + width: 400px; +} + +.hint-block { + display: none; +} diff --git a/framework/yii/gii/controllers/DefaultController.php b/framework/yii/gii/controllers/DefaultController.php new file mode 100644 index 0000000..943c084 --- /dev/null +++ b/framework/yii/gii/controllers/DefaultController.php @@ -0,0 +1,54 @@ + + * @since 2.0 + */ +class DefaultController extends Controller +{ + public $layout = 'generator'; + public $generator; + + public function actionIndex() + { + $this->layout = 'main'; + return $this->render('index'); + } + + public function actionView($id) + { + $generator = $this->loadGenerator($id); + return $this->render('view', array( + 'generator' => $generator + )); + } + + public function actionCode($file) + { + + } + + public function actionDiff($file1, $file2) + { + + } + + protected function loadGenerator($id) + { + if (isset($this->module->generators[$id])) { + return $this->generator = $this->module->generators[$id]; + } else { + throw new HttpException(404, "Code generator not found: $id"); + } + } +} diff --git a/framework/yii/gii/generators/controller/Generator.php b/framework/yii/gii/generators/controller/Generator.php new file mode 100644 index 0000000..1deb1c1 --- /dev/null +++ b/framework/yii/gii/generators/controller/Generator.php @@ -0,0 +1,166 @@ + + * @since 2.0 + */ +class Generator extends \yii\gii\Generator +{ + public $controller; + public $baseClass = 'yii\web\Controller'; + public $actions = 'index'; + + public function getName() + { + return 'Controller Generator'; + } + + public function getDescription() + { + return 'This generator helps you to quickly generate a new controller class, + one or several controller actions and their corresponding views.'; + } + + public function renderForm() + { + return Yii::$app->getView()->renderFile(__DIR__ . '/views/form.php', array( + 'model' => $this, + )); + } + + 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+\\/]*$/', '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 and backslashes.'), + 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'; + } +} diff --git a/framework/yii/gii/generators/controller/views/form.php b/framework/yii/gii/generators/controller/views/form.php new file mode 100644 index 0000000..a2b0fad --- /dev/null +++ b/framework/yii/gii/generators/controller/views/form.php @@ -0,0 +1,36 @@ + +
+
+
+ 'login-form')); ?> + field($model, 'controller')->hint(' + Controller ID is case-sensitive and can contain module ID(s). For example: +
    +
  • order generates OrderController.php
  • +
  • order-item generates OrderItemController.php
  • +
  • admin/user generates UserController.php within the admin module.
  • +
+ '); ?> + field($model, 'baseClass')->hint(' + This is the class that the new controller class will extend from. + Please make sure the class exists and can be autoloaded. + '); ?> + field($model, 'actions')->hint(' + Provide one or multiple action IDs to generate empty action method(s) in the controller. + Separate multiple action IDs with commas or spaces. + '); ?> +
+ 'btn btn-primary')); ?> +
+ +
+
+
diff --git a/framework/yii/gii/generators/crud/Generator.php b/framework/yii/gii/generators/crud/Generator.php new file mode 100644 index 0000000..025ff24 --- /dev/null +++ b/framework/yii/gii/generators/crud/Generator.php @@ -0,0 +1,27 @@ + + * @since 2.0 + */ +class Generator extends \yii\gii\Generator +{ + public function getName() + { + return 'CRUD Generator'; + } + + public function getDescription() + { + return 'This generator generates a controller and views that implement CRUD (Create, Read, Update, Delete) + operations for the specified data model.'; + } +} diff --git a/framework/yii/gii/generators/form/Generator.php b/framework/yii/gii/generators/form/Generator.php new file mode 100644 index 0000000..750a7b8 --- /dev/null +++ b/framework/yii/gii/generators/form/Generator.php @@ -0,0 +1,26 @@ + + * @since 2.0 + */ +class Generator extends \yii\gii\Generator +{ + public function getName() + { + return 'Form Generator'; + } + + public function getDescription() + { + return 'This generator generates a view script file that displays a form to collect input for the specified model class.'; + } +} diff --git a/framework/yii/gii/generators/model/Generator.php b/framework/yii/gii/generators/model/Generator.php new file mode 100644 index 0000000..936c75a --- /dev/null +++ b/framework/yii/gii/generators/model/Generator.php @@ -0,0 +1,26 @@ + + * @since 2.0 + */ +class Generator extends \yii\gii\Generator +{ + public function getName() + { + return 'Model Generator'; + } + + public function getDescription() + { + return 'This generator generates a model class for the specified database table.'; + } +} diff --git a/framework/yii/gii/generators/module/Generator.php b/framework/yii/gii/generators/module/Generator.php new file mode 100644 index 0000000..7d49122 --- /dev/null +++ b/framework/yii/gii/generators/module/Generator.php @@ -0,0 +1,26 @@ + + * @since 2.0 + */ +class Generator extends \yii\gii\Generator +{ + public function getName() + { + return 'Module Generator'; + } + + public function getDescription() + { + return 'This generator helps you to generate the skeleton code needed by a Yii module.'; + } +} diff --git a/framework/yii/gii/views/default/index.php b/framework/yii/gii/views/default/index.php new file mode 100644 index 0000000..57c8da5 --- /dev/null +++ b/framework/yii/gii/views/default/index.php @@ -0,0 +1,34 @@ +controller->module->generators; +$activeGenerator = Yii::$app->controller->generator; +$this->title = 'Welcome to Gii'; +?> +
+ + +

Start the fun with the following code generators:

+ +
+ $generator): ?> +
+

getName()); ?>

+

getDescription(); ?>

+

getUrl(), array('class' => 'btn btn-default')); ?>

+
+ +
+ + +

Get More Generators

+ +
diff --git a/framework/yii/gii/views/default/view.php b/framework/yii/gii/views/default/view.php new file mode 100644 index 0000000..3cbb914 --- /dev/null +++ b/framework/yii/gii/views/default/view.php @@ -0,0 +1,19 @@ +title = $generator->getName(); +?> +
+

getName()); ?>

+

getDescription(); ?>

+ + renderForm(); ?> + + renderFileList(); ?> +
diff --git a/framework/yii/gii/views/layouts/generator.php b/framework/yii/gii/views/layouts/generator.php new file mode 100644 index 0000000..fcf9fbc --- /dev/null +++ b/framework/yii/gii/views/layouts/generator.php @@ -0,0 +1,31 @@ +controller->module->generators; +$activeGenerator = Yii::$app->controller->generator; +?> +beginContent('@yii/gii/views/layouts/main.php'); ?> +
+
+
+ $generator) { + $label = '' . Html::encode($generator->getName()); + echo Html::a($label, array('default/view', 'id' => $id), array( + 'class' => $generator === $activeGenerator ? 'list-group-item active' : 'list-group-item', + )); + } + ?> +
+
+
+ +
+
+endContent(); ?> diff --git a/framework/yii/gii/views/layouts/main.php b/framework/yii/gii/views/layouts/main.php new file mode 100644 index 0000000..5a70ac4 --- /dev/null +++ b/framework/yii/gii/views/layouts/main.php @@ -0,0 +1,55 @@ + +beginPage(); ?> + + + + + <?php echo Html::encode($this->title); ?> + head(); ?> + + +beginBody(); ?> + Html::img($asset->baseUrl . '/logo.png'), + 'brandUrl' => array('default/index'), + 'options' => array( + 'class' => 'navbar-inverse navbar-fixed-top', + ), +)); +echo Nav::widget(array( + 'options' => array('class' => 'nav navbar-nav pull-right'), + 'items' => array( + array('label' => 'Home', 'url' => array('default/index')), + array('label' => 'Help', 'url' => 'http://www.yiiframework.com/doc/guide/topics.gii'), + array('label' => 'Application', 'url' => Yii::$app->homeUrl), + ), +)); +NavBar::end(); +?> + +
+ +
+ + + +endBody(); ?> + + +endPage(); ?>