Browse Source

gii WIP

tags/2.0.0-beta
Qiang Xue 12 years ago
parent
commit
1896dd1707
  1. 2
      apps/advanced/backend/views/site/login.php
  2. 2
      apps/advanced/frontend/views/site/contact.php
  3. 2
      apps/advanced/frontend/views/site/login.php
  4. 2
      apps/advanced/frontend/views/site/requestPasswordResetToken.php
  5. 2
      apps/advanced/frontend/views/site/resetPassword.php
  6. 2
      apps/advanced/frontend/views/site/signup.php
  7. 2
      apps/basic/views/site/contact.php
  8. 2
      apps/basic/views/site/login.php
  9. 2
      docs/guide/upgrade-from-v1.md
  10. 127
      framework/yii/gii/CodeFile.php
  11. 424
      framework/yii/gii/Generator.php
  12. 1
      framework/yii/gii/Module.php
  13. 13
      framework/yii/gii/assets/main.css
  14. 33
      framework/yii/gii/controllers/DefaultController.php
  15. 50
      framework/yii/gii/generators/controller/Generator.php
  16. 8
      framework/yii/gii/generators/controller/templates/controller.php
  17. 8
      framework/yii/gii/generators/controller/templates/view.php
  18. 45
      framework/yii/gii/generators/controller/views/form.php
  19. 5
      framework/yii/gii/generators/crud/Generator.php
  20. 6
      framework/yii/gii/generators/form/Generator.php
  21. 5
      framework/yii/gii/generators/model/Generator.php
  22. 6
      framework/yii/gii/generators/module/Generator.php
  23. 64
      framework/yii/gii/views/default/_files.php
  24. 5
      framework/yii/gii/views/default/index.php
  25. 50
      framework/yii/gii/views/default/view.php
  26. 10
      framework/yii/requirements/views/web/css.php

2
apps/advanced/backend/views/site/login.php

@ -21,7 +21,7 @@ $this->params['breadcrumbs'][] = $this->title;
<?php echo $form->field($model, 'username'); ?> <?php echo $form->field($model, 'username'); ?>
<?php echo $form->field($model, 'password')->passwordInput(); ?> <?php echo $form->field($model, 'password')->passwordInput(); ?>
<?php echo $form->field($model, 'rememberMe')->checkbox(); ?> <?php echo $form->field($model, 'rememberMe')->checkbox(); ?>
<div class="form-actions"> <div class="form-group">
<?php echo Html::submitButton('Login', array('class' => 'btn btn-primary')); ?> <?php echo Html::submitButton('Login', array('class' => 'btn btn-primary')); ?>
</div> </div>
<?php ActiveForm::end(); ?> <?php ActiveForm::end(); ?>

2
apps/advanced/frontend/views/site/contact.php

@ -29,7 +29,7 @@ $this->params['breadcrumbs'][] = $this->title;
'options' => array('class' => 'form-control'), 'options' => array('class' => 'form-control'),
'template' => '<div class="row"><div class="col-lg-3">{image}</div><div class="col-lg-6">{input}</div></div>', 'template' => '<div class="row"><div class="col-lg-3">{image}</div><div class="col-lg-6">{input}</div></div>',
)); ?> )); ?>
<div class="form-actions"> <div class="form-group">
<?php echo Html::submitButton('Submit', array('class' => 'btn btn-primary')); ?> <?php echo Html::submitButton('Submit', array('class' => 'btn btn-primary')); ?>
</div> </div>
<?php ActiveForm::end(); ?> <?php ActiveForm::end(); ?>

2
apps/advanced/frontend/views/site/login.php

@ -24,7 +24,7 @@ $this->params['breadcrumbs'][] = $this->title;
<div style="color:#999;margin:1em 0"> <div style="color:#999;margin:1em 0">
If you forgot your password you can <?php echo Html::a('reset it', array('site/request-password-reset'))?>. If you forgot your password you can <?php echo Html::a('reset it', array('site/request-password-reset'))?>.
</div> </div>
<div class="form-actions"> <div class="form-group">
<?php echo Html::submitButton('Login', array('class' => 'btn btn-primary')); ?> <?php echo Html::submitButton('Login', array('class' => 'btn btn-primary')); ?>
</div> </div>
<?php ActiveForm::end(); ?> <?php ActiveForm::end(); ?>

2
apps/advanced/frontend/views/site/requestPasswordResetToken.php

@ -19,7 +19,7 @@ $this->params['breadcrumbs'][] = $this->title;
<div class="col-lg-5"> <div class="col-lg-5">
<?php $form = ActiveForm::begin(array('id' => 'request-password-reset-form')); ?> <?php $form = ActiveForm::begin(array('id' => 'request-password-reset-form')); ?>
<?php echo $form->field($model, 'email'); ?> <?php echo $form->field($model, 'email'); ?>
<div class="form-actions"> <div class="form-group">
<?php echo Html::submitButton('Send', array('class' => 'btn btn-primary')); ?> <?php echo Html::submitButton('Send', array('class' => 'btn btn-primary')); ?>
</div> </div>
<?php ActiveForm::end(); ?> <?php ActiveForm::end(); ?>

2
apps/advanced/frontend/views/site/resetPassword.php

@ -19,7 +19,7 @@ $this->params['breadcrumbs'][] = $this->title;
<div class="col-lg-5"> <div class="col-lg-5">
<?php $form = ActiveForm::begin(array('id' => 'reset-password-form')); ?> <?php $form = ActiveForm::begin(array('id' => 'reset-password-form')); ?>
<?php echo $form->field($model, 'password')->passwordInput(); ?> <?php echo $form->field($model, 'password')->passwordInput(); ?>
<div class="form-actions"> <div class="form-group">
<?php echo Html::submitButton('Save', array('class' => 'btn btn-primary')); ?> <?php echo Html::submitButton('Save', array('class' => 'btn btn-primary')); ?>
</div> </div>
<?php ActiveForm::end(); ?> <?php ActiveForm::end(); ?>

2
apps/advanced/frontend/views/site/signup.php

@ -21,7 +21,7 @@ $this->params['breadcrumbs'][] = $this->title;
<?php echo $form->field($model, 'username'); ?> <?php echo $form->field($model, 'username'); ?>
<?php echo $form->field($model, 'email'); ?> <?php echo $form->field($model, 'email'); ?>
<?php echo $form->field($model, 'password')->passwordInput(); ?> <?php echo $form->field($model, 'password')->passwordInput(); ?>
<div class="form-actions"> <div class="form-group">
<?php echo Html::submitButton('Signup', array('class' => 'btn btn-primary')); ?> <?php echo Html::submitButton('Signup', array('class' => 'btn btn-primary')); ?>
</div> </div>
<?php ActiveForm::end(); ?> <?php ActiveForm::end(); ?>

2
apps/basic/views/site/contact.php

@ -37,7 +37,7 @@ $this->params['breadcrumbs'][] = $this->title;
'options' => array('class' => 'form-control'), 'options' => array('class' => 'form-control'),
'template' => '<div class="row"><div class="col-lg-3">{image}</div><div class="col-lg-6">{input}</div></div>', 'template' => '<div class="row"><div class="col-lg-3">{image}</div><div class="col-lg-6">{input}</div></div>',
)); ?> )); ?>
<div class="form-actions"> <div class="form-group">
<?php echo Html::submitButton('Submit', array('class' => 'btn btn-primary')); ?> <?php echo Html::submitButton('Submit', array('class' => 'btn btn-primary')); ?>
</div> </div>
<?php ActiveForm::end(); ?> <?php ActiveForm::end(); ?>

2
apps/basic/views/site/login.php

@ -21,7 +21,7 @@ $this->params['breadcrumbs'][] = $this->title;
<?php echo $form->field($model, 'username'); ?> <?php echo $form->field($model, 'username'); ?>
<?php echo $form->field($model, 'password')->passwordInput(); ?> <?php echo $form->field($model, 'password')->passwordInput(); ?>
<?php echo $form->field($model, 'rememberMe')->checkbox(); ?> <?php echo $form->field($model, 'rememberMe')->checkbox(); ?>
<div class="form-actions"> <div class="form-group">
<?php echo Html::submitButton('Login', array('class' => 'btn btn-primary')); ?> <?php echo Html::submitButton('Login', array('class' => 'btn btn-primary')); ?>
</div> </div>
<?php ActiveForm::end(); ?> <?php ActiveForm::end(); ?>

2
docs/guide/upgrade-from-v1.md

@ -338,7 +338,7 @@ It is represented as an `ActiveField` object. Using fields, you can build a form
<?php $form = yii\widgets\ActiveForm::begin(); ?> <?php $form = yii\widgets\ActiveForm::begin(); ?>
<?php echo $form->field($model, 'username'); ?> <?php echo $form->field($model, 'username'); ?>
<?php echo $form->field($model, 'password')->passwordInput(); ?> <?php echo $form->field($model, 'password')->passwordInput(); ?>
<div class="form-actions"> <div class="form-group">
<?php echo Html::submitButton('Login'); ?> <?php echo Html::submitButton('Login'); ?>
</div> </div>
<?php yii\widgets\ActiveForm::end(); ?> <?php yii\widgets\ActiveForm::end(); ?>

127
framework/yii/gii/CodeFile.php

@ -0,0 +1,127 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\gii;
use Yii;
use yii\base\Object;
/**
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class CodeFile extends Object
{
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;
} elseif ($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';
}
}
}

424
framework/yii/gii/Generator.php

@ -8,161 +8,73 @@
namespace yii\gii; namespace yii\gii;
use Yii; use Yii;
use ReflectionClass;
use yii\base\InvalidConfigException;
use yii\base\Model; use yii\base\Model;
use yii\base\View;
/** /**
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
class Generator extends Model abstract class Generator extends Model
{ {
public $templates = array();
/** /**
* @var string * @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 $id; public $template;
/** /**
* @return string name of the code generator * @return string name of the code generator
*/ */
public function getName() abstract public function getName();
public function init()
{ {
return 'unknown'; parent::init();
if (!isset($this->templates['default'])) {
$this->templates['default'] = $this->getDefaultTemplate();
}
} }
public function getDescription() /**
* 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()
{ {
return ''; return array();
} }
public function getUrl() /**
* 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 Yii::$app->controller->createUrl('default/view', array('id' => $this->id)); return array();
} }
public function renderForm() public function getViewFile()
{ {
return ''; $class = new ReflectionClass($this);
return dirname($class->getFileName()) . '/views/form.php';
} }
public function renderFileList() public function getDefaultTemplate()
{ {
return ''; $class = new ReflectionClass($this);
return dirname($class->getFileName()) . '/templates';
} }
const STATUS_NEW = 1; public function getDescription()
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()
{ {
return '';
} }
/** /**
@ -178,35 +90,12 @@ class Generator extends Model
public function rules() public function rules()
{ {
return array( return array(
array('template', 'required'), array('template', 'required', 'message' => 'A code template must be selected.'),
array('template', 'validateTemplate', 'skipOnError' => true), 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). * Checks if the named class exists (in a case sensitive manner).
* @param string $name class name to be checked * @param string $name class name to be checked
* @return boolean whether the class exists * @return boolean whether the class exists
@ -217,40 +106,12 @@ class Generator extends Model
} }
/** /**
* 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. * Saves the generated code into files.
*/ */
public function save() public function save($files, $answers = array())
{ {
$result = true; $result = true;
foreach ($this->files as $file) { foreach ($files as $file) {
if ($this->confirmed($file)) { if ($this->confirmed($file)) {
$result = $file->save() && $result; $result = $file->save() && $result;
} }
@ -259,59 +120,25 @@ class Generator extends Model
} }
/** /**
* 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. * @return string the directory that contains the template files.
* @throws CHttpException if {@link templates} is empty or template selection is invalid * @throws InvalidConfigException if {@link templates} is empty or template selection is invalid
*/ */
public function getTemplatePath() public function getTemplatePath()
{ {
$templates = $this->getTemplates(); if (isset($this->templates[$this->template])) {
if (isset($templates[$this->template])) { return $this->templates[$this->template];
return $templates[$this->template];
} elseif (empty($templates)) {
throw new CHttpException(500, 'No templates are available.');
} else { } else {
throw new CHttpException(500, 'Invalid template selection.'); throw new InvalidConfigException("Unknown template: {$this->template}");
} }
} }
/** /**
* @param CCodeFile $file whether the code file should be saved * @param CodeFile $file whether the code file should be saved
* @return bool whether the confirmation is found in {@link answers} with appropriate {@link operation} * @return bool whether the confirmation is found in {@link answers} with appropriate {@link operation}
*/ */
public function confirmed($file) public function confirmed($file)
{ {
return $this->answers === null && $file->operation === CCodeFile::OP_NEW return $this->answers === null && $file->operation === CodeFile::OP_NEW
|| is_array($this->answers) && isset($this->answers[md5($file->path)]); || is_array($this->answers) && isset($this->answers[md5($file->path)]);
} }
@ -320,24 +147,12 @@ class Generator extends Model
* This method is manly used in {@link generate} to generate code. * This method is manly used in {@link generate} to generate code.
* @param string $templateFile the code template file path * @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 * @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 * @return string the generated code
*/ */
public function render($templateFile, $_params_ = null) public function render($templateFile, $params = array())
{ {
if (!is_file($templateFile)) { $view = new View;
throw new CException("The template file '$templateFile' does not exist."); return $view->renderFile($templateFile, $params, $this);
}
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();
} }
/** /**
@ -349,9 +164,9 @@ class Generator extends Model
foreach ($this->files as $file) { foreach ($this->files as $file) {
if ($file->error !== null) { if ($file->error !== null) {
$output .= "<span class=\"error\">generating {$file->relativePath}<br/> {$file->error}</span>\n"; $output .= "<span class=\"error\">generating {$file->relativePath}<br/> {$file->error}</span>\n";
} elseif ($file->operation === CCodeFile::OP_NEW && $this->confirmed($file)) { } elseif ($file->operation === CodeFile::OP_NEW && $this->confirmed($file)) {
$output .= ' generated ' . $file->relativePath . "\n"; $output .= ' generated ' . $file->relativePath . "\n";
} elseif ($file->operation === CCodeFile::OP_OVERWRITE && $this->confirmed($file)) { } elseif ($file->operation === CodeFile::OP_OVERWRITE && $this->confirmed($file)) {
$output .= ' overwrote ' . $file->relativePath . "\n"; $output .= ' overwrote ' . $file->relativePath . "\n";
} else { } else {
$output .= ' skipped ' . $file->relativePath . "\n"; $output .= ' skipped ' . $file->relativePath . "\n";
@ -362,66 +177,111 @@ class Generator extends Model
} }
/** /**
* The "sticky" validator. * Validates the template selection.
* This validator does not really validate the attributes. * This method validates whether the user selects an existing template
* It actually saves the attribute value in a file to make it sticky. * and the template contains all required template files as specified in {@link requiredTemplates}.
* @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() public function validateTemplate()
{ {
$this->_stickyAttributes = array(); $templates = $this->templates;
$path = $this->getStickyFile(); if (!isset($templates[$this->template])) {
if (is_file($path)) { $this->addError('template', 'Invalid template selection.');
$result = @include($path); } else {
if (is_array($result)) { $templatePath = $this->templates[$this->template];
$this->_stickyAttributes = $result; foreach ($this->requiredTemplates() as $template) {
foreach ($this->_stickyAttributes as $name => $value) { if (!is_file($templatePath . '/' . $template)) {
if (property_exists($this, $name) || $this->canSetProperty($name)) { $this->addError('template', "Unable to find the required code template file '$template'.");
$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';
}
/**
* Validates an attribute to make sure it is not taking a PHP reserved keyword. * Validates an attribute to make sure it is not taking a PHP reserved keyword.
* @param string $attribute the attribute to be validated * @param string $attribute the attribute to be validated
* @param array $params validation parameters * @param array $params validation parameters
*/ */
public function validateReservedWord($attribute, $params) public function validateReservedWord($attribute, $params)
{ {
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',
);
$value = $this->$attribute; $value = $this->$attribute;
if (in_array(strtolower($value), self::$keywords)) { if (in_array(strtolower($value), $keywords)) {
$this->addError($attribute, $this->getAttributeLabel($attribute) . ' cannot take a reserved PHP keyword.'); $this->addError($attribute, $this->getAttributeLabel($attribute) . ' cannot take a reserved PHP keyword.');
} }
} }

1
framework/yii/gii/Module.php

@ -53,7 +53,6 @@ class Module extends \yii\base\Module
{ {
parent::init(); parent::init();
foreach (array_merge($this->coreGenerators(), $this->generators) as $id => $config) { foreach (array_merge($this->coreGenerators(), $this->generators) as $id => $config) {
$config['id'] = $id;
$this->generators[$id] = Yii::createObject($config); $this->generators[$id] = Yii::createObject($config);
} }
} }

13
framework/yii/gii/assets/main.css

@ -41,3 +41,16 @@ body {
.hint-block { .hint-block {
display: none; display: none;
} }
table.code-files .file {
}
table.code-files .action {
width: 100px;
}
table.code-files .check {
width: 25px;
text-align: center;
}

33
framework/yii/gii/controllers/DefaultController.php

@ -17,6 +17,13 @@ use yii\web\HttpException;
class DefaultController extends Controller class DefaultController extends Controller
{ {
public $layout = 'generator'; public $layout = 'generator';
/**
* @var \yii\gii\Module
*/
public $module;
/**
* @var \yii\gii\Generator
*/
public $generator; public $generator;
public function actionIndex() public function actionIndex()
@ -28,9 +35,19 @@ class DefaultController extends Controller
public function actionView($id) public function actionView($id)
{ {
$generator = $this->loadGenerator($id); $generator = $this->loadGenerator($id);
return $this->render('view', array( $params = array('generator' => $generator);
'generator' => $generator if (isset($_POST['preview']) || isset($_POST['generate'])) {
)); if ($generator->validate()) {
$files = $generator->prepare();
if (isset($_POST['generate'], $_POST['answers'])) {
$params['result'] = $generator->save($files, $_POST['answers']);
} else {
$params['files'] = $files;
}
}
}
return $this->render('view', $params);
} }
public function actionCode($file) public function actionCode($file)
@ -43,10 +60,18 @@ class DefaultController extends Controller
} }
/**
* Loads the generator with the specified ID.
* @param string $id the ID of the generator to be loaded.
* @return \yii\gii\Generator the loaded generator
* @throws \yii\web\HttpException
*/
protected function loadGenerator($id) protected function loadGenerator($id)
{ {
if (isset($this->module->generators[$id])) { if (isset($this->module->generators[$id])) {
return $this->generator = $this->module->generators[$id]; $this->generator = $this->module->generators[$id];
$this->generator->load($_POST);
return $this->generator;
} else { } else {
throw new HttpException(404, "Code generator not found: $id"); throw new HttpException(404, "Code generator not found: $id");
} }

50
framework/yii/gii/generators/controller/Generator.php

@ -8,6 +8,8 @@
namespace yii\gii\generators\controller; namespace yii\gii\generators\controller;
use Yii; use Yii;
use yii\gii\CodeFile;
use yii\helpers\Html;
/** /**
* *
@ -31,33 +33,25 @@ class Generator extends \yii\gii\Generator
one or several controller actions and their corresponding views.'; 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() public function rules()
{ {
return array_merge(parent::rules(), array( return array_merge(parent::rules(), array(
array('controller, actions, baseClass', 'filter', 'filter' => 'trim'), array('controller, actions, baseClass', 'filter', 'filter' => 'trim'),
array('controller, baseClass', 'required'), array('controller, baseClass', 'required'),
array('controller', 'match', 'pattern' => '/^[\w+\\/]*$/', 'message' => '{attribute} should only contain word characters and slashes.'), array('controller', 'match', 'pattern' => '/^[\w+\\/]*$/', 'message' => 'Only word characters and slashes are allowed.'),
array('actions', 'match', 'pattern' => '/^\w+[\w\s,]*$/', 'message' => '{attribute} should only contain word characters, spaces and commas.'), array('actions', 'match', 'pattern' => '/^\w+[\w\s,]*$/', 'message' => 'Only word characters, spaces and commas are allowed.'),
array('baseClass', 'match', 'pattern' => '/^[a-zA-Z_][\w\\\\]*$/', 'message' => '{attribute} should only contain word characters and backslashes.'), array('baseClass', 'match', 'pattern' => '/^[a-zA-Z_][\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'),
array('baseClass', 'validateReservedWord', 'skipOnError' => true), array('baseClass', 'validateReservedWord', 'skipOnError' => true),
array('baseClass, actions', 'sticky'),
)); ));
} }
public function attributeLabels() public function attributeLabels()
{ {
return array_merge(parent::attributeLabels(), array( return array(
'baseClass' => 'Base Class', 'baseClass' => 'Base Class',
'controller' => 'Controller ID', 'controller' => 'Controller ID',
'actions' => 'Action IDs', 'actions' => 'Action IDs',
)); );
} }
public function requiredTemplates() public function requiredTemplates()
@ -70,32 +64,34 @@ class Generator extends \yii\gii\Generator
public function successMessage() public function successMessage()
{ {
$link = CHtml::link('try it now', Yii::app()->createUrl($this->controller), array('target' => '_blank')); $link = Html::a('try it now', Yii::$app->getUrlManager()->createUrl($this->controller), array('target' => '_blank'));
return "The controller has been generated successfully. You may $link."; return "The controller has been generated successfully. You may $link.";
} }
public function prepare() public function prepare()
{ {
$this->files = array(); $files = array();
$templatePath = $this->templatePath;
$this->files[] = new CCodeFile( $templatePath = $this->getTemplatePath();
$this->controllerFile,
$files[] = new CodeFile(
$this->getControllerFile(),
$this->render($templatePath . '/controller.php') $this->render($templatePath . '/controller.php')
); );
foreach ($this->getActionIDs() as $action) { foreach ($this->getActionIDs() as $action) {
$this->files[] = new CCodeFile( $files[] = new CodeFile(
$this->getViewFile($action), $this->getViewFile($action),
$this->render($templatePath . '/view.php', array('action' => $action)) $this->render($templatePath . '/view.php', array('action' => $action))
); );
} }
return $files;
} }
public function getActionIDs() public function getActionIDs()
{ {
$actions = preg_split('/[\s,]+/', $this->actions, -1, PREG_SPLIT_NO_EMPTY); $actions = array_unique(preg_split('/[\s,]+/', $this->actions, -1, PREG_SPLIT_NO_EMPTY));
$actions = array_unique($actions);
sort($actions); sort($actions);
return $actions; return $actions;
} }
@ -113,16 +109,16 @@ class Generator extends \yii\gii\Generator
{ {
if (($pos = strpos($this->controller, '/')) !== false) { if (($pos = strpos($this->controller, '/')) !== false) {
$id = substr($this->controller, 0, $pos); $id = substr($this->controller, 0, $pos);
if (($module = Yii::app()->getModule($id)) !== null) { if (($module = Yii::$app->getModule($id)) !== null) {
return $module; return $module;
} }
} }
return Yii::app(); return Yii::$app;
} }
public function getControllerID() public function getControllerID()
{ {
if ($this->getModule() !== Yii::app()) { if ($this->getModule() !== Yii::$app) {
$id = substr($this->controller, strpos($this->controller, '/') + 1); $id = substr($this->controller, strpos($this->controller, '/') + 1);
} else { } else {
$id = $this->controller; $id = $this->controller;
@ -157,10 +153,4 @@ class Generator extends \yii\gii\Generator
} }
return $module->getControllerPath() . '/' . $id . 'Controller.php'; return $module->getControllerPath() . '/' . $id . 'Controller.php';
} }
public function getViewFile($action)
{
$module = $this->getModule();
return $module->getViewPath() . '/' . $this->getControllerID() . '/' . $action . '.php';
}
} }

8
framework/yii/gii/generators/controller/templates/controller.php

@ -0,0 +1,8 @@
<?php
/**
* Created by JetBrains PhpStorm.
* User: qiang
* Date: 8/15/13
* Time: 4:45 PM
* To change this template use File | Settings | File Templates.
*/

8
framework/yii/gii/generators/controller/templates/view.php

@ -0,0 +1,8 @@
<?php
/**
* Created by JetBrains PhpStorm.
* User: qiang
* Date: 8/15/13
* Time: 4:46 PM
* To change this template use File | Settings | File Templates.
*/

45
framework/yii/gii/generators/controller/views/form.php

@ -1,36 +1,27 @@
<?php <?php
use yii\helpers\Html; use yii\helpers\Html;
use yii\widgets\ActiveForm; use yii\widgets\ActiveForm;
/** /**
* @var yii\base\View $this * @var yii\base\View $this
* @var yii\widgets\ActiveForm $form * @var yii\widgets\ActiveForm $form
* @var yii\gii\generators\controller\Generator $generator
*/ */
?> ?>
<div class="controller-form"> <?php echo $form->field($generator, 'controller')->hint('
<div class="row"> Controller ID is case-sensitive and can contain module ID(s). For example:
<div class="col-lg-6"> <ul>
<?php $form = ActiveForm::begin(array('id' => 'login-form')); ?> <li><code>order</code> generates <code>OrderController.php</code></li>
<?php echo $form->field($model, 'controller')->hint(' <li><code>order-item</code> generates <code>OrderItemController.php</code></li>
Controller ID is case-sensitive and can contain module ID(s). For example: <li><code>admin/user</code> generates <code>UserController.php</code> within the <code>admin</code> module.</li>
<ul> </ul>
<li><code>order</code> generates <code>OrderController.php</code></li> '); ?>
<li><code>order-item</code> generates <code>OrderItemController.php</code></li> <?php echo $form->field($generator, 'baseClass')->hint('
<li><code>admin/user</code> generates <code>UserController.php</code> within the <code>admin</code> module.</li> This is the class that the new controller class will extend from.
</ul> Please make sure the class exists and can be autoloaded.
'); ?> '); ?>
<?php echo $form->field($model, 'baseClass')->hint(' <?php echo $form->field($generator, 'actions')->hint('
This is the class that the new controller class will extend from. Provide one or multiple action IDs to generate empty action method(s) in the controller.
Please make sure the class exists and can be autoloaded. Separate multiple action IDs with commas or spaces.
'); ?> '); ?>
<?php echo $form->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.
'); ?>
<div class="form-actions">
<?php echo Html::submitButton('Preview', array('class' => 'btn btn-primary')); ?>
</div>
<?php ActiveForm::end(); ?>
</div>
</div>
</div>

5
framework/yii/gii/generators/crud/Generator.php

@ -24,4 +24,9 @@ class Generator extends \yii\gii\Generator
return 'This generator generates a controller and views that implement CRUD (Create, Read, Update, Delete) return 'This generator generates a controller and views that implement CRUD (Create, Read, Update, Delete)
operations for the specified data model.'; operations for the specified data model.';
} }
public function getViewFile()
{
return __DIR__ . '/views/form.php';
}
} }

6
framework/yii/gii/generators/form/Generator.php

@ -23,4 +23,10 @@ class Generator extends \yii\gii\Generator
{ {
return 'This generator generates a view script file that displays a form to collect input for the specified model class.'; return 'This generator generates a view script file that displays a form to collect input for the specified model class.';
} }
public function getViewFile()
{
return __DIR__ . '/views/form.php';
}
} }

5
framework/yii/gii/generators/model/Generator.php

@ -23,4 +23,9 @@ class Generator extends \yii\gii\Generator
{ {
return 'This generator generates a model class for the specified database table.'; return 'This generator generates a model class for the specified database table.';
} }
public function getViewFile()
{
return __DIR__ . '/views/form.php';
}
} }

6
framework/yii/gii/generators/module/Generator.php

@ -23,4 +23,10 @@ class Generator extends \yii\gii\Generator
{ {
return 'This generator helps you to generate the skeleton code needed by a Yii module.'; return 'This generator helps you to generate the skeleton code needed by a Yii module.';
} }
public function getViewFile()
{
return __DIR__ . '/views/form.php';
}
} }

64
framework/yii/gii/views/default/_files.php

@ -0,0 +1,64 @@
<?php
use yii\gii\Generator;
use yii\helpers\Html;
use yii\gii\CodeFile;
/**
* @var $this \yii\base\View
* @var $generator \yii\gii\Generator
* @var CodeFile[] $files
*/
?>
<table class="table table-bordered table-striped table-condensed code-files">
<thead>
<tr>
<th class="file">Code File</th>
<th class="action">Action</th>
<th class="check">
<?php
$count = 0;
foreach ($files as $file) {
if ($file->operation !== CodeFile::OP_SKIP) {
$count++;
}
}
if ($count > 1) {
echo '<input type="checkbox" name="checkAll" id="check-all" />';
}
?>
</th>
</tr>
</thead>
<tbody>
<?php foreach ($files as $i => $file): ?>
<tr class="<?php echo $file->operation; ?>">
<td class="file">
<?php echo Html::a(Html::encode($file->getRelativePath()), array('code', 'file' => $i), array('class' => 'view-code', 'rel' => $file->path)); ?>
<?php if ($file->operation === CodeFile::OP_OVERWRITE): ?>
(<?php echo Html::a('diff', array('diff', 'file' => $i), array('class' => 'view-code', 'rel' => $file->path)); ?>)
<?php endif; ?>
</td>
<td class="action">
<?php
if ($file->operation === CodeFile::OP_SKIP) {
echo 'unchanged';
} else {
echo $file->operation;
}
?>
</td>
<td class="check">
<?php
if ($file->operation === CodeFile::OP_SKIP) {
echo '&nbsp;';
} else {
$key = md5($file->path);
echo Html::checkBox("answers[$key]");
}
?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>

5
framework/yii/gii/views/default/index.php

@ -19,16 +19,15 @@ $this->title = 'Welcome to Gii';
<p class="lead">Start the fun with the following code generators:</p> <p class="lead">Start the fun with the following code generators:</p>
<div class="row"> <div class="row">
<?php foreach (array_values($generators) as $i => $generator): ?> <?php foreach ($generators as $id => $generator): ?>
<div class="generator col-lg-4"> <div class="generator col-lg-4">
<h3><?php echo Html::encode($generator->getName()); ?></h3> <h3><?php echo Html::encode($generator->getName()); ?></h3>
<p><?php echo $generator->getDescription(); ?></p> <p><?php echo $generator->getDescription(); ?></p>
<p><?php echo Html::a('Start »', $generator->getUrl(), array('class' => 'btn btn-default')); ?></p> <p><?php echo Html::a('Start »', array('default/view', 'id' => $id), array('class' => 'btn btn-default')); ?></p>
</div> </div>
<?php endforeach; ?> <?php endforeach; ?>
</div> </div>
<p><a class="btn btn-success" href="http://www.yiiframework.com/extensions/?tag=gii">Get More Generators</a></p> <p><a class="btn btn-success" href="http://www.yiiframework.com/extensions/?tag=gii">Get More Generators</a></p>
</div> </div>

50
framework/yii/gii/views/default/view.php

@ -1,19 +1,59 @@
<?php <?php
use yii\gii\Generator;
use yii\helpers\Html; use yii\helpers\Html;
use yii\widgets\ActiveForm;
use yii\gii\CodeFile;
/** /**
* @var $this \yii\base\View * @var yii\base\View $this
* @var $generator \yii\gii\Generator * @var yii\gii\Generator $generator
* @var yii\widgets\ActiveForm $form
* @var string $result
* @var CodeFile[] $files
*/ */
$this->title = $generator->getName(); $this->title = $generator->getName();
$templates = array();
foreach ($generator->templates as $name => $path) {
$templates[$name] = "$name ($path)";
}
?> ?>
<div class="default-view"> <div class="default-view">
<h1><?php echo Html::encode($generator->getName()); ?></h1> <h1><?php echo Html::encode($this->title); ?></h1>
<p><?php echo $generator->getDescription(); ?></p> <p><?php echo $generator->getDescription(); ?></p>
<?php echo $generator->renderForm(); ?> <?php $form = ActiveForm::begin(); ?>
<div class="row">
<div class="col-lg-6">
<?php echo $this->renderFile($generator->getViewFile(), array(
'generator' => $generator,
'form' => $form,
)); ?>
<?php echo $form->field($generator, 'template')->label(array('label' => 'Code Template'))->dropDownList($templates)->hint('
Please select which set of the templates should be used to generated the code.
'); ?>
<div class="form-group">
<?php echo Html::submitButton('Preview', array('name' => 'preview', 'class' => 'btn btn-primary')); ?>
<?php if(isset($files)): ?>
<?php echo Html::submitButton('Generate', array('name' => 'generate', 'class' => 'btn btn-danger')); ?>
<?php endif; ?>
</div>
</div>
</div>
<?php
if (isset($result)) {
echo '<div class="result">' . $result . '</div>';
} elseif (isset($files)) {
echo $this->render('_files', array(
'generator' => $generator,
'files' => $files,
));
}
?>
<?php echo $generator->renderFileList(); ?> <?php ActiveForm::end(); ?>
</div> </div>

10
framework/yii/requirements/views/web/css.php

@ -1627,7 +1627,7 @@ select:focus:invalid:focus {
box-shadow: 0 0 6px #f8b9b7; box-shadow: 0 0 6px #f8b9b7;
} }
.form-actions { .form-group {
padding: 19px 20px 20px; padding: 19px 20px 20px;
margin-top: 20px; margin-top: 20px;
margin-bottom: 20px; margin-bottom: 20px;
@ -1636,14 +1636,14 @@ select:focus:invalid:focus {
*zoom: 1; *zoom: 1;
} }
.form-actions:before, .form-group:before,
.form-actions:after { .form-group:after {
display: table; display: table;
line-height: 0; line-height: 0;
content: ""; content: "";
} }
.form-actions:after { .form-group:after {
clear: both; clear: both;
} }
@ -1991,7 +1991,7 @@ legend + .control-group {
margin-top: 10px; margin-top: 10px;
} }
.form-horizontal .form-actions { .form-horizontal .form-group {
padding-left: 180px; padding-left: 180px;
} }

Loading…
Cancel
Save