diff --git a/docs/internals/errors_and_exceptions.md b/docs/internals/errors_and_exceptions.md
new file mode 100644
index 0000000..98542f1
--- /dev/null
+++ b/docs/internals/errors_and_exceptions.md
@@ -0,0 +1,9 @@
+Errors and Exceptions
+=====================
+
+
+Errors i18n
+-----------
+
+A general rule is to tranlate only errors that are shown to end users only. If
+it is a regular exception, debug screen etc. then message should not be translated.
\ No newline at end of file
diff --git a/docs/internals/versions.md b/docs/internals/versions.md
new file mode 100644
index 0000000..ba349f6
--- /dev/null
+++ b/docs/internals/versions.md
@@ -0,0 +1,12 @@
+Yii version numbering
+=====================
+
+A.B.C
+
+A = For Yii2 it's always 2.
+B = Major version. Non-BC changes with upgrade instructions.
+C = BC changes and additions.
+
+A.B.CrcD
+
+This is when we want to release release candidate. D is the RC number. Starts with 1 and increments till we're getting a stable release with no critical bugs and BC incompatibility reports.
\ No newline at end of file
diff --git a/framework/YiiBase.php b/framework/YiiBase.php
index 5f71a1a..35236ac 100644
--- a/framework/YiiBase.php
+++ b/framework/YiiBase.php
@@ -477,7 +477,7 @@ class YiiBase
/**
* Translates a message to the specified language.
- * Starting from version 1.0.2, this method supports choice format (see {@link CChoiceFormat}),
+ * This method supports choice format (see {@link CChoiceFormat}),
* i.e., the message returned will be chosen from a few candidates according to the given
* number value. This feature is mainly used to solve plural format issue in case
* a message has different plural forms in some languages.
@@ -486,16 +486,15 @@ class YiiBase
* more interpretation about message category.
* @param string $message the original message
* @param array $params parameters to be applied to the message using strtr
.
- * Starting from version 1.0.2, the first parameter can be a number without key.
+ * The first parameter can be a number without key.
* And in this case, the method will call {@link CChoiceFormat::format} to choose
* an appropriate message translation.
- * Starting from version 1.1.6 you can pass parameter for {@link CChoiceFormat::format}
+ * You can pass parameter for {@link CChoiceFormat::format}
* or plural forms format without wrapping it with array.
* @param string $source which message source application component to use.
* Defaults to null, meaning using 'coreMessages' for messages belonging to
* the 'yii' category and using 'messages' for the rest messages.
* @param string $language the target language. If null (default), the {@link CApplication::getLanguage application language} will be used.
- * This parameter has been available since version 1.0.3.
* @return string the translated message
* @see CMessageSource
*/
diff --git a/framework/base/Application.php b/framework/base/Application.php
index e6023fc..e12d095 100644
--- a/framework/base/Application.php
+++ b/framework/base/Application.php
@@ -8,6 +8,8 @@
* @license http://www.yiiframework.com/license/
*/
+namespace yii\base;
+
/**
* Application is the base class for all application classes.
*
@@ -114,7 +116,7 @@ abstract class Application extends Module
*/
public function __construct($config = null)
{
- Yii::setApplication($this);
+ \Yii::$app = $this;
// set basePath at early as possible to avoid trouble
if (is_string($config))
@@ -126,9 +128,9 @@ abstract class Application extends Module
}
else
$this->setBasePath('protected');
- Yii::setPathOfAlias('application', $this->getBasePath());
- Yii::setPathOfAlias('webroot', dirname($_SERVER['SCRIPT_FILENAME']));
- Yii::setPathOfAlias('ext', $this->getBasePath() . DIRECTORY_SEPARATOR . 'extensions');
+ \Yii::setAlias('application', $this->getBasePath());
+ \Yii::setAlias('webroot', dirname($_SERVER['SCRIPT_FILENAME']));
+ \Yii::setAlias('ext', $this->getBasePath() . DIRECTORY_SEPARATOR . 'extensions');
$this->preinit();
@@ -151,10 +153,10 @@ abstract class Application extends Module
*/
public function run()
{
- if ($this->hasEventHandler('onBeginRequest'))
+ if ($this->hasEventHandlers('onBeginRequest'))
$this->onBeginRequest(new CEvent($this));
$this->processRequest();
- if ($this->hasEventHandler('onEndRequest'))
+ if ($this->hasEventHandlers('onEndRequest'))
$this->onEndRequest(new CEvent($this));
}
@@ -168,7 +170,7 @@ abstract class Application extends Module
*/
public function end($status = 0, $exit = true)
{
- if ($this->hasEventHandler('onEndRequest'))
+ if ($this->hasEventHandlers('onEndRequest'))
$this->onEndRequest(new CEvent($this));
if ($exit)
exit($status);
@@ -235,7 +237,7 @@ abstract class Application extends Module
public function setBasePath($path)
{
if (($this->_basePath = realpath($path)) === false || !is_dir($this->_basePath))
- throw new CException(Yii::t('yii', 'Application base path "{path}" is not a valid directory.',
+ throw new \yii\base\Exception(\Yii::t('yii', 'Application base path "{path}" is not a valid directory.',
array('{path}' => $path)));
}
@@ -262,7 +264,7 @@ abstract class Application extends Module
public function setRuntimePath($path)
{
if (($runtimePath = realpath($path)) === false || !is_dir($runtimePath) || !is_writable($runtimePath))
- throw new CException(Yii::t('yii', 'Application runtime path "{path}" is not valid. Please make sure it is a directory writable by the Web server process.',
+ throw new \yii\base\Exception(\Yii::t('yii', 'Application runtime path "{path}" is not valid. Please make sure it is a directory writable by the Web server process.',
array('{path}' => $path)));
$this->_runtimePath = $runtimePath;
}
@@ -273,7 +275,7 @@ abstract class Application extends Module
*/
public function getExtensionPath()
{
- return Yii::getPathOfAlias('ext');
+ return \Yii::getPathOfAlias('ext');
}
/**
@@ -283,9 +285,9 @@ abstract class Application extends Module
public function setExtensionPath($path)
{
if (($extensionPath = realpath($path)) === false || !is_dir($extensionPath))
- throw new CException(Yii::t('yii', 'Extension path "{path}" does not exist.',
+ throw new \yii\base\Exception(\Yii::t('yii', 'Extension path "{path}" does not exist.',
array('{path}' => $path)));
- Yii::setPathOfAlias('ext', $extensionPath);
+ \Yii::setAlias('ext', $extensionPath);
}
/**
@@ -319,7 +321,6 @@ abstract class Application extends Module
* This is a simple wrapper of PHP function date_default_timezone_get().
* @return string the time zone used by this application.
* @see http://php.net/manual/en/function.date-default-timezone-get.php
- * @since 1.0.9
*/
public function getTimeZone()
{
@@ -331,7 +332,6 @@ abstract class Application extends Module
* This is a simple wrapper of PHP function date_default_timezone_set().
* @param string $value the time zone used by this application.
* @see http://php.net/manual/en/function.date-default-timezone-set.php
- * @since 1.0.9
*/
public function setTimeZone($value)
{
@@ -382,17 +382,15 @@ abstract class Application extends Module
/**
* Returns the directory that contains the locale data.
* @return string the directory that contains the locale data. It defaults to 'framework/i18n/data'.
- * @since 1.1.0
*/
public function getLocaleDataPath()
{
- return CLocale::$dataPath === null ? Yii::getPathOfAlias('system.i18n.data') : CLocale::$dataPath;
+ return CLocale::$dataPath === null ? \Yii::getPathOfAlias('system.i18n.data') : CLocale::$dataPath;
}
/**
* Sets the directory that contains the locale data.
* @param string $value the directory that contains the locale data.
- * @since 1.1.0
*/
public function setLocaleDataPath($value)
{
@@ -501,7 +499,6 @@ abstract class Application extends Module
/**
* @return CController the currently active controller. Null is returned in this base class.
- * @since 1.1.8
*/
public function getController()
{
@@ -641,7 +638,7 @@ abstract class Application extends Module
/**
* Loads the global state data from persistent storage.
* @see getStatePersister
- * @throws CException if the state persister is not available
+ * @throws \yii\base\Exception if the state persister is not available
*/
public function loadGlobalState()
{
@@ -688,25 +685,31 @@ abstract class Application extends Module
restore_exception_handler();
$category = 'exception.' . get_class($exception);
- if ($exception instanceof CHttpException)
+ if ($exception instanceof \yii\web\HttpException)
$category .= '.' . $exception->statusCode;
// php <5.2 doesn't support string conversion auto-magically
$message = $exception->__toString();
if (isset($_SERVER['REQUEST_URI']))
$message .= ' REQUEST_URI=' . $_SERVER['REQUEST_URI'];
- Yii::log($message, CLogger::LEVEL_ERROR, $category);
+ \Yii::error($message, $category);
try
{
- $event = new CExceptionEvent($this, $exception);
+ // TODO: do we need separate exception class as it was in 1.1?
+ //$event = new CExceptionEvent($this, $exception);
+ $event = new Event($this, array('exception' => $exception));
$this->onException($event);
if (!$event->handled)
{
// try an error handler
if (($handler = $this->getErrorHandler()) !== null)
+ {
$handler->handle($event);
+ }
else
+ {
$this->displayException($exception);
+ }
}
}
catch(Exception $e)
@@ -777,11 +780,11 @@ abstract class Application extends Module
}
if (isset($_SERVER['REQUEST_URI']))
$log .= 'REQUEST_URI=' . $_SERVER['REQUEST_URI'];
- Yii::log($log, CLogger::LEVEL_ERROR, 'php');
+ \Yii::error($log, 'php');
try
{
- Yii::import('CErrorEvent', true);
+ \Yii::import('CErrorEvent', true);
$event = new CErrorEvent($this, $code, $message, $file, $line);
$this->onError($event);
if (!$event->handled)
@@ -940,9 +943,10 @@ abstract class Application extends Module
'messages' => array(
'class' => 'CPhpMessageSource',
),
- 'errorHandler' => array(
- 'class' => 'CErrorHandler',
- ),
+ // TODO: uncomment when error handler is properly implemented
+// 'errorHandler' => array(
+// 'class' => 'CErrorHandler',
+// ),
'securityManager' => array(
'class' => 'CSecurityManager',
),
diff --git a/framework/base/Event.php b/framework/base/Event.php
index afe7e57..f508f47 100644
--- a/framework/base/Event.php
+++ b/framework/base/Event.php
@@ -47,7 +47,9 @@ class Event extends Object
/**
* Constructor.
+ *
* @param mixed $sender sender of the event
+ * @param mixed $params parameters of the event
*/
public function __construct($sender=null, $params=null)
{
diff --git a/framework/base/Module.php b/framework/base/Module.php
index 521db25..e28d81a 100644
--- a/framework/base/Module.php
+++ b/framework/base/Module.php
@@ -253,13 +253,17 @@ abstract class Module extends Component
$config = $this->_moduleConfig[$id];
if (!isset($config['enabled']) || $config['enabled'])
{
- Yii::trace("Loading \"$id\" module", 'system.base.CModule');
+ \Yii::trace("Loading \"$id\" module", 'system.base.CModule');
$class = $config['class'];
unset($config['class'], $config['enabled']);
- if ($this === Yii::app())
+ if ($this === \Yii::$app)
+ {
$module = Yii::create($class, $id, null, $config);
+ }
else
+ {
$module = Yii::create($class, $this->getId() . '/' . $id, $this, $config);
+ }
return $this->_modules[$id] = $module;
}
}
@@ -269,7 +273,6 @@ abstract class Module extends Component
* Returns a value indicating whether the specified module is installed.
* @param string $id the module ID
* @return boolean whether the specified module is installed.
- * @since 1.1.2
*/
public function hasModule($id)
{
@@ -360,10 +363,9 @@ abstract class Module extends Component
$config = $this->_componentConfig[$id];
if (!isset($config['enabled']) || $config['enabled'])
{
- Yii::trace("Loading \"$id\" application component", 'system.CModule');
+ \Yii::trace("Loading \"$id\" application component", 'system.CModule');
unset($config['enabled']);
- $component = Yii::create($config);
- $component->init();
+ $component = \Yii::create($config);
return $this->_components[$id] = $component;
}
}
diff --git a/framework/console/Application.php b/framework/console/Application.php
new file mode 100644
index 0000000..167e008
--- /dev/null
+++ b/framework/console/Application.php
@@ -0,0 +1,181 @@
+
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright © 2008-2012 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\console;
+
+/**
+ * \yii\console\Application represents a console application.
+ *
+ * \yii\console\Application extends {@link \yii\base\Application} by providing functionalities
+ * specific to console requests. In particular, it deals with console requests
+ * through a command-based approach:
+ *
+ * - A console application consists of one or several possible user commands;
+ * - Each user command is implemented as a class extending {@link \yii\console\Command};
+ * - User specifies which command to run on the command line;
+ * - The command processes the user request with the specified parameters.
+ *
+ *
+ * The command classes reside in the directory {@link getCommandPath commandPath}.
+ * The name of the class follows the pattern: <command-name>Command, and its
+ * file name is the same the class name. For example, the 'ShellCommand' class defines
+ * a 'shell' command and the class file name is 'ShellCommand.php'.
+ *
+ * To run the console application, enter the following on the command line:
+ *
+ * php path/to/entry_script.php [param 1] [param 2] ...
+ *
+ *
+ * You may use the following to see help instructions about a command:
+ *
+ * php path/to/entry_script.php help
+ *
+ *
+ * @property string $commandPath The directory that contains the command classes. Defaults to 'protected/commands'.
+ * @property CommandRunner $commandRunner The command runner.
+ *
+ * @author Qiang Xue
+ * @since 2.0
+ */
+class Application extends \yii\base\Application
+{
+ /**
+ * @var array mapping from command name to command configurations.
+ * Each command configuration can be either a string or an array.
+ * If the former, the string should be the file path of the command class.
+ * If the latter, the array must contain a 'class' element which specifies
+ * the command's class name or {@link \YiiBase::getPathOfAlias class path alias}.
+ * The rest name-value pairs in the array are used to initialize
+ * the corresponding command properties. For example,
+ *
+ * array(
+ * 'email'=>array(
+ * 'class'=>'path.to.Mailer',
+ * 'interval'=>3600,
+ * ),
+ * 'log'=>'path/to/LoggerCommand.php',
+ * )
+ *
+ */
+ public $commandMap=array();
+
+ private $_commandPath;
+
+ /**
+ * @var \yii\console\CommandRunner
+ */
+ private $_runner;
+
+ /**
+ * Initializes the application by creating the command runner.
+ */
+ public function init()
+ {
+ if(!isset($_SERVER['argv']))
+ {
+ die('This script must be run from the command line.');
+ }
+ $this->_runner=$this->createCommandRunner();
+ $this->_runner->commands=$this->commandMap;
+ $this->_runner->addCommands($this->getCommandPath());
+ }
+
+ /**
+ * Processes the user request.
+ * This method creates a console command runner to handle the particular user command.
+ */
+ public function processRequest()
+ {
+ $this->_runner->run($_SERVER['argv']);
+ }
+
+ /**
+ * Creates the command runner instance.
+ * @return CommandRunner the command runner
+ */
+ protected function createCommandRunner()
+ {
+ return new CommandRunner();
+ }
+
+ /**
+ * Displays the captured PHP error.
+ * This method displays the error in console mode when there is
+ * no active error handler.
+ * @param integer $code error code
+ * @param string $message error message
+ * @param string $file error file
+ * @param string $line error line
+ */
+ public function displayError($code,$message,$file,$line)
+ {
+ echo "PHP Error[$code]: $message\n";
+ echo " in file $file at line $line\n";
+ $trace=debug_backtrace();
+ // skip the first 4 stacks as they do not tell the error position
+ if(count($trace)>4)
+ $trace=array_slice($trace,4);
+ foreach($trace as $i=>$t)
+ {
+ if(!isset($t['file']))
+ $t['file']='unknown';
+ if(!isset($t['line']))
+ $t['line']=0;
+ if(!isset($t['function']))
+ $t['function']='unknown';
+ echo "#$i {$t['file']}({$t['line']}): ";
+ if(isset($t['object']) && is_object($t['object']))
+ echo get_class($t['object']).'->';
+ echo "{$t['function']}()\n";
+ }
+ }
+
+ /**
+ * Displays the uncaught PHP exception.
+ * This method displays the exception in console mode when there is
+ * no active error handler.
+ * @param Exception $exception the uncaught exception
+ */
+ public function displayException($exception)
+ {
+ echo $exception;
+ }
+
+ /**
+ * @return string the directory that contains the command classes. Defaults to 'protected/commands'.
+ */
+ public function getCommandPath()
+ {
+ $applicationCommandPath = $this->getBasePath().DIRECTORY_SEPARATOR.'commands';
+ if($this->_commandPath===null && file_exists($applicationCommandPath))
+ $this->setCommandPath($applicationCommandPath);
+ return $this->_commandPath;
+ }
+
+ /**
+ * @param string $value the directory that contains the command classes.
+ * @throws CException if the directory is invalid
+ */
+ public function setCommandPath($value)
+ {
+ if(($this->_commandPath=realpath($value))===false || !is_dir($this->_commandPath))
+ throw new \yii\base\Exception(Yii::t('yii','The command path "{path}" is not a valid directory.',
+ array('{path}'=>$value)));
+ }
+
+ /**
+ * Returns the command runner.
+ * @return CConsoleCommandRunner the command runner.
+ */
+ public function getCommandRunner()
+ {
+ return $this->_runner;
+ }
+}
diff --git a/framework/console/Command.php b/framework/console/Command.php
new file mode 100644
index 0000000..7f8ee57
--- /dev/null
+++ b/framework/console/Command.php
@@ -0,0 +1,339 @@
+
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright © 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\console;
+
+/**
+ * Command represents an executable console command.
+ *
+ * It works like {@link \yii\web\Controller} by parsing command line options and dispatching
+ * the request to a specific action with appropriate option values.
+ *
+ * Users call a console command via the following command format:
+ *
+ * yiic CommandName ActionName --Option1=Value1 --Option2=Value2 ...
+ *
+ *
+ * Child classes mainly needs to implement various action methods whose name must be
+ * prefixed with "action". The parameters to an action method are considered as options
+ * for that specific action. The action specified as {@link defaultAction} will be invoked
+ * when a user does not specify the action name in his command.
+ *
+ * Options are bound to action parameters via parameter names. For example, the following
+ * action method will allow us to run a command with yiic sitemap --type=News
:
+ *
+ * class SitemapCommand {
+ * public function actionIndex($type) {
+ * ....
+ * }
+ * }
+ *
+ *
+ * @property string $name The command name.
+ * @property CommandRunner $commandRunner The command runner instance.
+ * @property string $help The command description. Defaults to 'Usage: php entry-script.php command-name'.
+ * @property array $optionHelp The command option help information. Each array element describes
+ * the help information for a single action.
+ *
+ * @author Qiang Xue
+ * @since 2.0
+ */
+abstract class Command extends \yii\base\Component
+{
+ /**
+ * @var string the name of the default action. Defaults to 'index'.
+ */
+ public $defaultAction='index';
+
+ private $_name;
+ private $_runner;
+
+ /**
+ * Constructor.
+ * @param string $name name of the command
+ * @param CConsoleCommandRunner $runner the command runner
+ */
+ public function __construct($name,$runner)
+ {
+ $this->_name=$name;
+ $this->_runner=$runner;
+ }
+
+ /**
+ * Initializes the command object.
+ * This method is invoked after a command object is created and initialized with configurations.
+ * You may override this method to further customize the command before it executes.
+ */
+ public function init()
+ {
+ }
+
+ /**
+ * Executes the command.
+ * The default implementation will parse the input parameters and
+ * dispatch the command request to an appropriate action with the corresponding
+ * option values
+ * @param array $args command line parameters for this command.
+ */
+ public function run($args)
+ {
+ list($action, $options, $args)=$this->resolveRequest($args);
+ $methodName='action'.$action;
+ if(!preg_match('/^\w+$/',$action) || !method_exists($this,$methodName))
+ $this->usageError("Unknown action: ".$action);
+
+ $method=new \ReflectionMethod($this,$methodName);
+ $params=array();
+ // named and unnamed options
+ foreach($method->getParameters() as $param)
+ {
+ $name=$param->getName();
+ if(isset($options[$name]))
+ {
+ if($param->isArray())
+ $params[]=is_array($options[$name]) ? $options[$name] : array($options[$name]);
+ else if(!is_array($options[$name]))
+ $params[]=$options[$name];
+ else
+ $this->usageError("Option --$name requires a scalar. Array is given.");
+ }
+ else if($name==='args')
+ $params[]=$args;
+ else if($param->isDefaultValueAvailable())
+ $params[]=$param->getDefaultValue();
+ else
+ $this->usageError("Missing required option --$name.");
+ unset($options[$name]);
+ }
+
+ // try global options
+ if(!empty($options))
+ {
+ $class=new \ReflectionClass(get_class($this));
+ foreach($options as $name=>$value)
+ {
+ if($class->hasProperty($name))
+ {
+ $property=$class->getProperty($name);
+ if($property->isPublic() && !$property->isStatic())
+ {
+ $this->$name=$value;
+ unset($options[$name]);
+ }
+ }
+ }
+ }
+
+ if(!empty($options))
+ $this->usageError("Unknown options: ".implode(', ',array_keys($options)));
+
+ if($this->beforeAction($action,$params))
+ {
+ $method->invokeArgs($this,$params);
+ $this->afterAction($action,$params);
+ }
+ }
+
+ /**
+ * This method is invoked right before an action is to be executed.
+ * You may override this method to do last-minute preparation for the action.
+ * @param string $action the action name
+ * @param array $params the parameters to be passed to the action method.
+ * @return boolean whether the action should be executed.
+ */
+ protected function beforeAction($action,$params)
+ {
+ return true;
+ }
+
+ /**
+ * This method is invoked right after an action finishes execution.
+ * You may override this method to do some postprocessing for the action.
+ * @param string $action the action name
+ * @param array $params the parameters to be passed to the action method.
+ */
+ protected function afterAction($action,$params)
+ {
+ }
+
+ /**
+ * Parses the command line arguments and determines which action to perform.
+ * @param array $args command line arguments
+ * @return array the action name, named options (name=>value), and unnamed options
+ */
+ protected function resolveRequest($args)
+ {
+ $options=array(); // named parameters
+ $params=array(); // unnamed parameters
+ foreach($args as $arg)
+ {
+ if(preg_match('/^--(\w+)(=(.*))?$/',$arg,$matches)) // an option
+ {
+ $name=$matches[1];
+ $value=isset($matches[3]) ? $matches[3] : true;
+ if(isset($options[$name]))
+ {
+ if(!is_array($options[$name]))
+ $options[$name]=array($options[$name]);
+ $options[$name][]=$value;
+ }
+ else
+ $options[$name]=$value;
+ }
+ else if(isset($action))
+ $params[]=$arg;
+ else
+ $action=$arg;
+ }
+ if(!isset($action))
+ $action=$this->defaultAction;
+
+ return array($action,$options,$params);
+ }
+
+ /**
+ * @return string the command name.
+ */
+ public function getName()
+ {
+ return $this->_name;
+ }
+
+ /**
+ * @return \yii\console\CommandRunner the command runner instance
+ */
+ public function getCommandRunner()
+ {
+ return $this->_runner;
+ }
+
+ /**
+ * Provides the command description.
+ * This method may be overridden to return the actual command description.
+ * @return string the command description. Defaults to 'Usage: php entry-script.php command-name'.
+ */
+ public function getHelp()
+ {
+ $help='Usage: '.$this->getCommandRunner()->getScriptName().' '.$this->getName();
+ $options=$this->getOptionHelp();
+ if(empty($options))
+ return $help;
+ if(count($options)===1)
+ return $help.' '.$options[0];
+ $help.=" \nActions:\n";
+ foreach($options as $option)
+ $help.=' '.$option."\n";
+ return $help;
+ }
+
+ /**
+ * Provides the command option help information.
+ * The default implementation will return all available actions together with their
+ * corresponding option information.
+ * @return array the command option help information. Each array element describes
+ * the help information for a single action.
+ */
+ public function getOptionHelp()
+ {
+ $options=array();
+ $class=new \ReflectionClass(get_class($this));
+ foreach($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method)
+ {
+ $name=$method->getName();
+ if(!strncasecmp($name,'action',6) && strlen($name)>6)
+ {
+ $name=substr($name,6);
+ $name[0]=strtolower($name[0]);
+ $help=$name;
+
+ foreach($method->getParameters() as $param)
+ {
+ $optional=$param->isDefaultValueAvailable();
+ $defaultValue=$optional ? $param->getDefaultValue() : null;
+ $name=$param->getName();
+ if($optional)
+ $help.=" [--$name=$defaultValue]";
+ else
+ $help.=" --$name=value";
+ }
+ $options[]=$help;
+ }
+ }
+ return $options;
+ }
+
+ /**
+ * Displays a usage error.
+ * This method will then terminate the execution of the current application.
+ * @param string $message the error message
+ */
+ public function usageError($message)
+ {
+ echo "Error: $message\n\n".$this->getHelp()."\n";
+ exit(1);
+ }
+
+ /**
+ * Renders a view file.
+ * @param string $_viewFile_ view file path
+ * @param array $_data_ optional data to be extracted as local view variables
+ * @param boolean $_return_ whether to return the rendering result instead of displaying it
+ * @return mixed the rendering result if required. Null otherwise.
+ */
+ public function renderFile($_viewFile_,$_data_=null,$_return_=false)
+ {
+ if(is_array($_data_))
+ extract($_data_,EXTR_PREFIX_SAME,'data');
+ else
+ $data=$_data_;
+ if($_return_)
+ {
+ ob_start();
+ ob_implicit_flush(false);
+ require($_viewFile_);
+ return ob_get_clean();
+ }
+ else
+ require($_viewFile_);
+ }
+
+ /**
+ * Reads input via the readline PHP extension if that's available, or fgets() if readline is not installed.
+ *
+ * @param string $message to echo out before waiting for user input
+ * @return mixed line read as a string, or false if input has been closed
+ */
+ public function prompt($message)
+ {
+ if(extension_loaded('readline'))
+ {
+ $input = readline($message.' ');
+ readline_add_history($input);
+ return $input;
+ }
+ else
+ {
+ echo $message.' ';
+ return trim(fgets(STDIN));
+ }
+ }
+
+ /**
+ * Asks user to confirm by typing y or n.
+ *
+ * @param string $message to echo out before waiting for user input
+ * @return bool if user confirmed
+ */
+ public function confirm($message)
+ {
+ echo $message.' [yes|no] ';
+ return !strncasecmp(trim(fgets(STDIN)),'y',1);
+ }
+}
\ No newline at end of file
diff --git a/framework/console/CommandRunner.php b/framework/console/CommandRunner.php
new file mode 100644
index 0000000..1a6ed55
--- /dev/null
+++ b/framework/console/CommandRunner.php
@@ -0,0 +1,142 @@
+
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright © 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\console;
+
+/**
+ * CConsoleCommandRunner manages commands and executes the requested command.
+ *
+ * @property string $scriptName The entry script name.
+ *
+ * @author Qiang Xue
+ * @since 2.0
+ */
+class CommandRunner extends \yii\base\Component
+{
+ /**
+ * @var array list of all available commands (command name=>command configuration).
+ * Each command configuration can be either a string or an array.
+ * If the former, the string should be the class name or
+ * {@link YiiBase::getPathOfAlias class path alias} of the command.
+ * If the latter, the array must contain a 'class' element which specifies
+ * the command's class name or {@link YiiBase::getPathOfAlias class path alias}.
+ * The rest name-value pairs in the array are used to initialize
+ * the corresponding command properties. For example,
+ *
+ * array(
+ * 'email'=>array(
+ * 'class'=>'path.to.Mailer',
+ * 'interval'=>3600,
+ * ),
+ * 'log'=>'path.to.LoggerCommand',
+ * )
+ *
+ */
+ public $commands=array();
+
+ private $_scriptName;
+
+ /**
+ * Executes the requested command.
+ * @param array $args list of user supplied parameters (including the entry script name and the command name).
+ */
+ public function run($args)
+ {
+ $this->_scriptName=$args[0];
+ array_shift($args);
+ if(isset($args[0]))
+ {
+ $name=$args[0];
+ array_shift($args);
+ }
+ else
+ $name='help';
+
+ if(($command=$this->createCommand($name))===null)
+ $command=$this->createCommand('help');
+ $command->init();
+ $command->run($args);
+ }
+
+ /**
+ * @return string the entry script name
+ */
+ public function getScriptName()
+ {
+ return $this->_scriptName;
+ }
+
+ /**
+ * Searches for commands under the specified directory.
+ * @param string $path the directory containing the command class files.
+ * @return array list of commands (command name=>command class file)
+ */
+ public function findCommands($path)
+ {
+ if(($dir=@opendir($path))===false)
+ return array();
+ $commands=array();
+ while(($name=readdir($dir))!==false)
+ {
+ $file=$path.DIRECTORY_SEPARATOR.$name;
+ if(!strcasecmp(substr($name,-11),'Command.php') && is_file($file))
+ $commands[strtolower(substr($name,0,-11))]=$file;
+ }
+ closedir($dir);
+ return $commands;
+ }
+
+ /**
+ * Adds commands from the specified command path.
+ * If a command already exists, the new one will be ignored.
+ * @param string $path the alias of the directory containing the command class files.
+ */
+ public function addCommands($path)
+ {
+ if(($commands=$this->findCommands($path))!==array())
+ {
+ foreach($commands as $name=>$file)
+ {
+ if(!isset($this->commands[$name]))
+ $this->commands[$name]=$file;
+ }
+ }
+ }
+
+ /**
+ * @param string $name command name (case-insensitive)
+ * @return \yii\console\Command the command object. Null if the name is invalid.
+ */
+ public function createCommand($name)
+ {
+ $name=strtolower($name);
+ if(isset($this->commands[$name]))
+ {
+ if(is_string($this->commands[$name])) // class file path or alias
+ {
+ if(strpos($this->commands[$name],'/')!==false || strpos($this->commands[$name],'\\')!==false)
+ {
+ $className=substr(basename($this->commands[$name]),0,-4);
+ if(!class_exists($className,false))
+ require_once($this->commands[$name]);
+ }
+ else // an alias
+ $className=\Yii::import($this->commands[$name]);
+ return new $className($name,$this);
+ }
+ else // an array configuration
+ return \Yii::create($this->commands[$name],$name,$this);
+ }
+ else if($name==='help')
+ return new HelpCommand('help',$this);
+ else
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/framework/console/HelpCommand.php b/framework/console/HelpCommand.php
new file mode 100644
index 0000000..e97ac42
--- /dev/null
+++ b/framework/console/HelpCommand.php
@@ -0,0 +1,77 @@
+
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright © 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\console;
+
+/**
+ * CHelpCommand represents a console help command.
+ *
+ * CHelpCommand displays the available command list or the help instructions
+ * about a specific command.
+ *
+ * To use this command, enter the following on the command line:
+ *
+ * php path/to/entry_script.php help [command name]
+ *
+ * In the above, if the command name is not provided, it will display all
+ * available commands.
+ *
+ * @property string $help The command description.
+ *
+ * @author Qiang Xue
+ * @since 2.0
+ */
+class HelpCommand extends Command
+{
+ /**
+ * Execute the action.
+ * @param array $args command line parameters specific for this command
+ */
+ public function run($args)
+ {
+ $runner=$this->getCommandRunner();
+ $commands=$runner->commands;
+ if(isset($args[0]))
+ {
+ $name=strtolower($args[0]);
+ }
+ if(!isset($args[0]) || !isset($commands[$name]))
+ {
+ if(!empty($commands))
+ {
+ echo "Yii command runner (based on Yii v".\Yii::getVersion().")\n";
+ echo "Usage: ".$runner->getScriptName()." [parameters...]\n";
+ echo "\nThe following commands are available:\n";
+ $commandNames=array_keys($commands);
+ sort($commandNames);
+ echo ' - '.implode("\n - ",$commandNames);
+ echo "\n\nTo see individual command help, use the following:\n";
+ echo " ".$runner->getScriptName()." help \n";
+ }
+ else
+ {
+ echo "No available commands.\n";
+ echo "Please define them under the following directory:\n";
+ echo "\t".\Yii::$app->getCommandPath()."\n";
+ }
+ }
+ else
+ echo $runner->createCommand($name)->getHelp();
+ }
+
+ /**
+ * Provides the command description.
+ * @return string the command description.
+ */
+ public function getHelp()
+ {
+ return parent::getHelp().' [command-name]';
+ }
+}
\ No newline at end of file
diff --git a/framework/db/Migration.php b/framework/db/Migration.php
index e529d2d..8a22918 100644
--- a/framework/db/Migration.php
+++ b/framework/db/Migration.php
@@ -28,11 +28,9 @@
* applying migrations.
*
* @author Qiang Xue
- * @version $Id: CDbMigration.php 3218 2011-05-13 00:06:44Z alexander.makarow $
- * @package system.db
- * @since 1.1.6
+ * @since 2.0
*/
-abstract class CDbMigration extends CComponent
+abstract class CDbMigration extends yii\base\Component
{
private $_db;
@@ -94,7 +92,6 @@ abstract class CDbMigration extends CComponent
* Child classes may implement this method instead of {@link up} if the DB logic
* needs to be within a transaction.
* @return boolean
- * @since 1.1.7
*/
public function safeUp()
{
@@ -107,7 +104,6 @@ abstract class CDbMigration extends CComponent
* Child classes may implement this method instead of {@link up} if the DB logic
* needs to be within a transaction.
* @return boolean
- * @since 1.1.7
*/
public function safeDown()
{
@@ -146,7 +142,6 @@ abstract class CDbMigration extends CComponent
* This method executes the specified SQL statement using {@link dbConnection}.
* @param string $sql the SQL statement to be executed
* @param array $params input parameters (name=>value) for the SQL execution. See {@link CDbCommand::execute} for more details.
- * @since 1.1.7
*/
public function execute($sql, $params = array())
{
diff --git a/framework/db/ar/ActiveRecord.php b/framework/db/ar/ActiveRecord.php
index b0a8601..cff8532 100644
--- a/framework/db/ar/ActiveRecord.php
+++ b/framework/db/ar/ActiveRecord.php
@@ -51,9 +51,7 @@ $post->save();
* about this class.
*
* @author Qiang Xue
- * @version $Id: ActiveRecord.php 3344 2011-07-06 22:04:50Z alexander.makarow $
- * @package system.db.ar
- * @since 1.0
+ * @since 2.0
*
* @property array $attributes
*/
@@ -111,7 +109,6 @@ abstract class ActiveRecord extends \yii\base\Model
* @param integer $queryCount number of SQL queries that need to be cached after calling this method. Defaults to 1,
* meaning that the next SQL query will be cached.
* @return ActiveRecord the active record instance itself.
- * @since 1.1.7
*/
public function cache($duration, $dependency = null, $queryCount = 1)
{
@@ -174,7 +171,6 @@ abstract class ActiveRecord extends \yii\base\Model
* if the named attribute is null or not.
* @param string $name the property name or the event name
* @return boolean whether the property value is null
- * @since 1.0.1
*/
public function __isset($name)
{
@@ -195,7 +191,6 @@ abstract class ActiveRecord extends \yii\base\Model
* This method overrides the parent implementation by clearing
* the specified attribute value.
* @param string $name the property name or the event name
- * @since 1.0.1
*/
public function __unset($name)
{
@@ -214,7 +209,6 @@ abstract class ActiveRecord extends \yii\base\Model
* @param string $name the method name
* @param array $parameters method parameters
* @return mixed the method return value
- * @since 1.0.5
*/
public function __call($name, $parameters)
{
@@ -249,7 +243,6 @@ abstract class ActiveRecord extends \yii\base\Model
* This parameter has been available since version 1.0.5.
* @return mixed the related object(s).
* @throws CDbException if the relation is not specified in {@link relations}.
- * @since 1.0.2
*/
public function getRelated($name, $refresh = false, $params = array())
{
@@ -307,7 +300,6 @@ abstract class ActiveRecord extends \yii\base\Model
* Returns a value indicating whether the named related object(s) has been loaded.
* @param string $name the relation name
* @return boolean a value indicating whether the named related object(s) has been loaded.
- * @since 1.0.3
*/
public function hasRelated($name)
{
@@ -320,7 +312,6 @@ abstract class ActiveRecord extends \yii\base\Model
* @return CDbCriteria the query criteria that is associated with this model.
* This criteria is mainly used by {@link scopes named scope} feature to accumulate
* different criteria specifications.
- * @since 1.0.5
*/
public function getDbCriteria($createIfNull = true)
{
@@ -335,7 +326,6 @@ abstract class ActiveRecord extends \yii\base\Model
/**
* Sets the query criteria for the current model.
* @param CDbCriteria $criteria the query criteria
- * @since 1.1.3
*/
public function setDbCriteria($criteria)
{
@@ -349,7 +339,6 @@ abstract class ActiveRecord extends \yii\base\Model
* if the model needs to be queried with some default criteria (e.g. only active records should be returned).
* @return array the query criteria. This will be used as the parameter to the constructor
* of {@link CDbCriteria}.
- * @since 1.0.5
*/
public function defaultScope()
{
@@ -360,7 +349,6 @@ abstract class ActiveRecord extends \yii\base\Model
* Resets all scopes and criterias applied including default scope.
*
* @return ActiveRecord
- * @since 1.1.2
*/
public function resetScope()
{
@@ -415,7 +403,6 @@ abstract class ActiveRecord extends \yii\base\Model
* This is useful if the table schema has been changed and you want to use the latest
* available table schema. Make sure you have called {@link CDbSchema::refresh}
* before you call this method. Otherwise, old table schema data will still be used.
- * @since 1.0.8
*/
public function refreshMetaData()
{
@@ -446,7 +433,6 @@ abstract class ActiveRecord extends \yii\base\Model
* If the key is a single column, it should return the column name;
* If the key is a composite one consisting of several columns, it should
* return the array of the key column names.
- * @since 1.0.4
*/
public function primaryKey()
{
@@ -566,7 +552,6 @@ abstract class ActiveRecord extends \yii\base\Model
* @return array the scope definition. The array keys are scope names; the array
* values are the corresponding scope definitions. Each scope definition is represented
* as an array whose keys must be properties of {@link CDbCriteria}.
- * @since 1.0.5
*/
public function scopes()
{
@@ -577,7 +562,6 @@ abstract class ActiveRecord extends \yii\base\Model
* Returns the list of all attribute names of the model.
* This would return all column names of the table associated with this AR class.
* @return array list of attribute names.
- * @since 1.0.1
*/
public function attributeNames()
{
@@ -593,7 +577,6 @@ abstract class ActiveRecord extends \yii\base\Model
* @param string $attribute the attribute name
* @return string the attribute label
* @see generateAttributeLabel
- * @since 1.1.4
*/
public function getAttributeLabel($attribute)
{
@@ -834,7 +817,6 @@ abstract class ActiveRecord extends \yii\base\Model
* This event is raised before the record is saved.
* By setting {@link CModelEvent::isValid} to be false, the normal {@link save()} process will be stopped.
* @param CModelEvent $event the event parameter
- * @since 1.0.2
*/
public function onBeforeSave($event)
{
@@ -844,7 +826,6 @@ abstract class ActiveRecord extends \yii\base\Model
/**
* This event is raised after the record is saved.
* @param CEvent $event the event parameter
- * @since 1.0.2
*/
public function onAfterSave($event)
{
@@ -855,7 +836,6 @@ abstract class ActiveRecord extends \yii\base\Model
* This event is raised before the record is deleted.
* By setting {@link CModelEvent::isValid} to be false, the normal {@link delete()} process will be stopped.
* @param CModelEvent $event the event parameter
- * @since 1.0.2
*/
public function onBeforeDelete($event)
{
@@ -865,7 +845,6 @@ abstract class ActiveRecord extends \yii\base\Model
/**
* This event is raised after the record is deleted.
* @param CEvent $event the event parameter
- * @since 1.0.2
*/
public function onAfterDelete($event)
{
@@ -880,7 +859,6 @@ abstract class ActiveRecord extends \yii\base\Model
* You can modify either criteria to customize them based on needs.
* @param CModelEvent $event the event parameter
* @see beforeFind
- * @since 1.0.9
*/
public function onBeforeFind($event)
{
@@ -890,7 +868,6 @@ abstract class ActiveRecord extends \yii\base\Model
/**
* This event is raised after the record is instantiated by a find method.
* @param CEvent $event the event parameter
- * @since 1.0.2
*/
public function onAfterFind($event)
{
@@ -971,8 +948,6 @@ abstract class ActiveRecord extends \yii\base\Model
*
* Starting from version 1.1.5, this method may be called with a hidden {@link CDbCriteria}
* parameter which represents the current query criteria as passed to a find method of AR.
- *
- * @since 1.0.9
*/
protected function beforeFind()
{
@@ -1000,7 +975,6 @@ abstract class ActiveRecord extends \yii\base\Model
/**
* Calls {@link beforeFind}.
* This method is internally used.
- * @since 1.0.11
*/
public function beforeFindInternal()
{
@@ -1010,7 +984,6 @@ abstract class ActiveRecord extends \yii\base\Model
/**
* Calls {@link afterFind}.
* This method is internally used.
- * @since 1.0.3
*/
public function afterFindInternal()
{
@@ -1153,7 +1126,6 @@ abstract class ActiveRecord extends \yii\base\Model
* @param array $counters the counters to be updated (column name=>increment value)
* @return boolean whether the saving is successful
* @see updateCounters
- * @since 1.1.8
*/
public function saveCounters($counters)
{
@@ -1256,7 +1228,6 @@ abstract class ActiveRecord extends \yii\base\Model
* After calling this method, the old primary key value can be obtained from {@link oldPrimaryKey}.
* @param mixed $value the new primary key value. If the primary key is composite, the new value
* should be provided as an array (column name=>column value).
- * @since 1.1.0
*/
public function setPrimaryKey($value)
{
@@ -1278,7 +1249,6 @@ abstract class ActiveRecord extends \yii\base\Model
* The value remains unchanged even if the primary key attribute is manually assigned with a different value.
* @return mixed the old primary key value. An array (column name=>column value) is returned if the primary key is composite.
* If primary key is not defined, null will be returned.
- * @since 1.1.0
*/
public function getOldPrimaryKey()
{
@@ -1288,7 +1258,6 @@ abstract class ActiveRecord extends \yii\base\Model
/**
* Sets the old primary key value.
* @param mixed $value the old primary key value.
- * @since 1.1.3
*/
public function setOldPrimaryKey($value)
{
@@ -1301,7 +1270,6 @@ abstract class ActiveRecord extends \yii\base\Model
* @param CDbCriteria $criteria the query criteria
* @param boolean $all whether to return all data
* @return mixed the AR objects populated with the query result
- * @since 1.1.7
*/
protected function query($criteria, $all = false)
{
@@ -1326,7 +1294,6 @@ abstract class ActiveRecord extends \yii\base\Model
* This method merges {@link dbCriteria} with the given criteria parameter.
* It then resets {@link dbCriteria} to be null.
* @param CDbCriteria $criteria the query criteria. This parameter may be modified by merging {@link dbCriteria}.
- * @since 1.0.12
*/
public function applyScopes(&$criteria)
{
@@ -1382,7 +1349,6 @@ abstract class ActiveRecord extends \yii\base\Model
* This parameter must be set false when calling this method in {@link defaultScope}.
* An infinite loop would be formed otherwise.
* @return string the default table alias
- * @since 1.1.1
*/
public function getTableAlias($quote = false, $checkScopes = true)
{
@@ -1396,7 +1362,6 @@ abstract class ActiveRecord extends \yii\base\Model
/**
* Sets the table alias to be used in queries.
* @param string $alias the table alias to be used in queries. The alias should NOT be quoted.
- * @since 1.1.3
*/
public function setTableAlias($alias)
{
@@ -1578,7 +1543,6 @@ abstract class ActiveRecord extends \yii\base\Model
* @param mixed $condition query condition or criteria.
* @param array $params parameters to be bound to an SQL statement.
* @return string the number of rows satisfying the specified query condition. Note: type is string to keep max. precision.
- * @since 1.1.4
*/
public function countByAttributes($attributes, $condition = '', $params = array())
{
@@ -1686,7 +1650,6 @@ abstract class ActiveRecord extends \yii\base\Model
* This is only used in relational AR query. Please refer to {@link CDbCriteria::together}
* for more details.
* @return ActiveRecord the AR object itself
- * @since 1.1.4
*/
public function together()
{
@@ -1792,7 +1755,6 @@ abstract class ActiveRecord extends \yii\base\Model
* @param mixed $condition query condition or criteria.
* @param array $params parameters to be bound to an SQL statement.
* @return integer number of rows affected by the execution.
- * @since 1.0.9
*/
public function deleteAllByAttributes($attributes, $condition = '', $params = array())
{
@@ -1873,7 +1835,6 @@ abstract class ActiveRecord extends \yii\base\Model
* you may implement the so-called single-table inheritance mapping.
* @param array $attributes list of attribute values for the active records.
* @return ActiveRecord the active record
- * @since 1.0.2
*/
protected function instantiate($attributes)
{
@@ -1887,7 +1848,6 @@ abstract class ActiveRecord extends \yii\base\Model
* This method is required by the interface ArrayAccess.
* @param mixed $offset the offset to check on
* @return boolean
- * @since 1.0.2
*/
public function offsetExists($offset)
{
@@ -1899,9 +1859,6 @@ abstract class ActiveRecord extends \yii\base\Model
/**
* CBaseActiveRelation is the base class for all active relations.
* @author Qiang Xue
- * @version $Id: ActiveRecord.php 3344 2011-07-06 22:04:50Z alexander.makarow $
- * @package system.db.ar
- * @since 1.0.4
*/
class CBaseActiveRelation extends CComponent
{
@@ -1941,7 +1898,6 @@ class CBaseActiveRelation extends CComponent
/**
* @var string how to join with other tables. This refers to the JOIN clause in an SQL statement.
* For example, 'LEFT JOIN users ON users.id=authorID'
.
- * @since 1.1.3
*/
public $join = '';
/**
@@ -1975,7 +1931,6 @@ class CBaseActiveRelation extends CComponent
* Merges this relation with a criteria specified dynamically.
* @param array $criteria the dynamically specified criteria
* @param boolean $fromScope whether the criteria to be merged is from scopes
- * @since 1.0.5
*/
public function mergeWith($criteria, $fromScope = false)
{
@@ -2042,9 +1997,6 @@ class CBaseActiveRelation extends CComponent
/**
* CStatRelation represents a statistical relational query.
* @author Qiang Xue
- * @version $Id: ActiveRecord.php 3344 2011-07-06 22:04:50Z alexander.makarow $
- * @package system.db.ar
- * @since 1.0.4
*/
class CStatRelation extends CBaseActiveRelation
{
@@ -2063,7 +2015,6 @@ class CStatRelation extends CBaseActiveRelation
* Merges this relation with a criteria specified dynamically.
* @param array $criteria the dynamically specified criteria
* @param boolean $fromScope whether the criteria to be merged is from scopes
- * @since 1.0.5
*/
public function mergeWith($criteria, $fromScope = false)
{
@@ -2080,9 +2031,7 @@ class CStatRelation extends CBaseActiveRelation
/**
* CActiveRelation is the base class for representing active relations that bring back related objects.
* @author Qiang Xue
- * @version $Id: ActiveRecord.php 3344 2011-07-06 22:04:50Z alexander.makarow $
- * @package system.db.ar
- * @since 1.0
+ * @since 2.0
*/
class CActiveRelation extends CBaseActiveRelation
{
@@ -2092,13 +2041,11 @@ class CActiveRelation extends CBaseActiveRelation
public $joinType = 'LEFT OUTER JOIN';
/**
* @var string ON clause. The condition specified here will be appended to the joining condition using AND operator.
- * @since 1.0.2
*/
public $on = '';
/**
* @var string the alias for the table that this relation refers to. Defaults to null, meaning
* the alias will be the same as the relation name.
- * @since 1.0.1
*/
public $alias;
/**
@@ -2123,7 +2070,6 @@ class CActiveRelation extends CBaseActiveRelation
* Single scope: 'scopes'=>'scopeName'.
* Multiple scopes: 'scopes'=>array('scopeName1','scopeName2').
*
- * @since 1.1.9
*/
public $scopes;
@@ -2131,7 +2077,6 @@ class CActiveRelation extends CBaseActiveRelation
* Merges this relation with a criteria specified dynamically.
* @param array $criteria the dynamically specified criteria
* @param boolean $fromScope whether the criteria to be merged is from scopes
- * @since 1.0.5
*/
public function mergeWith($criteria, $fromScope = false)
{
@@ -2177,9 +2122,7 @@ class CActiveRelation extends CBaseActiveRelation
/**
* CBelongsToRelation represents the parameters specifying a BELONGS_TO relation.
* @author Qiang Xue
- * @version $Id: ActiveRecord.php 3344 2011-07-06 22:04:50Z alexander.makarow $
- * @package system.db.ar
- * @since 1.0
+ * @since 2.0
*/
class CBelongsToRelation extends CActiveRelation
{
@@ -2189,16 +2132,13 @@ class CBelongsToRelation extends CActiveRelation
/**
* CHasOneRelation represents the parameters specifying a HAS_ONE relation.
* @author Qiang Xue
- * @version $Id: ActiveRecord.php 3344 2011-07-06 22:04:50Z alexander.makarow $
- * @package system.db.ar
- * @since 1.0
+ * @since 2.0
*/
class CHasOneRelation extends CActiveRelation
{
/**
* @var string the name of the relation that should be used as the bridge to this relation.
* Defaults to null, meaning don't use any bridge.
- * @since 1.1.7
*/
public $through;
}
@@ -2207,9 +2147,7 @@ class CHasOneRelation extends CActiveRelation
/**
* CHasManyRelation represents the parameters specifying a HAS_MANY relation.
* @author Qiang Xue
- * @version $Id: ActiveRecord.php 3344 2011-07-06 22:04:50Z alexander.makarow $
- * @package system.db.ar
- * @since 1.0
+ * @since 2.0
*/
class CHasManyRelation extends CActiveRelation
{
@@ -2224,13 +2162,11 @@ class CHasManyRelation extends CActiveRelation
/**
* @var string the name of the column that should be used as the key for storing related objects.
* Defaults to null, meaning using zero-based integer IDs.
- * @since 1.0.7
*/
public $index;
/**
* @var string the name of the relation that should be used as the bridge to this relation.
* Defaults to null, meaning don't use any bridge.
- * @since 1.1.7
*/
public $through;
@@ -2238,7 +2174,6 @@ class CHasManyRelation extends CActiveRelation
* Merges this relation with a criteria specified dynamically.
* @param array $criteria the dynamically specified criteria
* @param boolean $fromScope whether the criteria to be merged is from scopes
- * @since 1.0.5
*/
public function mergeWith($criteria, $fromScope = false)
{
@@ -2260,9 +2195,7 @@ class CHasManyRelation extends CActiveRelation
/**
* CManyManyRelation represents the parameters specifying a MANY_MANY relation.
* @author Qiang Xue
- * @version $Id: ActiveRecord.php 3344 2011-07-06 22:04:50Z alexander.makarow $
- * @package system.db.ar
- * @since 1.0
+ * @since 2.0
*/
class CManyManyRelation extends CHasManyRelation
{
@@ -2273,9 +2206,7 @@ class CManyManyRelation extends CHasManyRelation
* ActiveRecordMetaData represents the meta-data for an Active Record class.
*
* @author Qiang Xue
- * @version $Id: ActiveRecord.php 3344 2011-07-06 22:04:50Z alexander.makarow $
- * @package system.db.ar
- * @since 1.0
+ * @since 2.0
*/
class ActiveRecordMetaData
{
@@ -2349,7 +2280,6 @@ class ActiveRecordMetaData
* @param string $name $name Name of the relation.
* @param array $config $config Relation parameters.
* @return void
- * @since 1.1.2
*/
public function addRelation($name, $config)
{
@@ -2364,7 +2294,6 @@ class ActiveRecordMetaData
*
* @param string $name $name Name of the relation.
* @return boolean
- * @since 1.1.2
*/
public function hasRelation($name)
{
@@ -2376,7 +2305,6 @@ class ActiveRecordMetaData
*
* @param string $name $name
* @return void
- * @since 1.1.2
*/
public function removeRelation($name)
{
diff --git a/framework/db/ar/ActiveRecordBehavior.php b/framework/db/ar/ActiveRecordBehavior.php
index 509e175..6971cde 100644
--- a/framework/db/ar/ActiveRecordBehavior.php
+++ b/framework/db/ar/ActiveRecordBehavior.php
@@ -14,9 +14,7 @@
* that are only defined by {@link CActiveRecord}.
*
* @author Qiang Xue
- * @version $Id: CActiveRecordBehavior.php 2799 2011-01-01 19:31:13Z qiang.xue $
- * @package system.db.ar
- * @since 1.0.2
+ * @since 2.0
*/
class CActiveRecordBehavior extends CModelBehavior
{
@@ -80,7 +78,6 @@ class CActiveRecordBehavior extends CModelBehavior
* Responds to {@link CActiveRecord::onBeforeFind} event.
* Overrides this method if you want to handle the corresponding event of the {@link CBehavior::owner owner}.
* @param CEvent $event event parameter
- * @since 1.0.9
*/
public function beforeFind($event)
{
diff --git a/framework/db/dao/Command.php b/framework/db/dao/Command.php
index 59b2c28..7c44eb9 100644
--- a/framework/db/dao/Command.php
+++ b/framework/db/dao/Command.php
@@ -671,7 +671,6 @@ class Command extends \yii\base\Component
/**
* Sets the LIMIT part of the query.
* @param integer $limit the limit
- * @param integer $offset the offset
* @return Command the command object itself
*/
public function limit($limit)
diff --git a/framework/db/dao/Query.php b/framework/db/dao/Query.php
index 6ad28f3..b65d2c7 100644
--- a/framework/db/dao/Query.php
+++ b/framework/db/dao/Query.php
@@ -235,7 +235,6 @@ class Query extends \yii\base\Object
* @param mixed $condition the new condition. It can be either a string or an array of strings.
* @param string $operator the operator to join different conditions. Defaults to 'AND'.
* @return Query the criteria object itself
- * @since 1.0.9
*/
public function addCondition($condition, $operator = 'AND')
{
@@ -269,7 +268,6 @@ class Query extends \yii\base\Object
* Defaults to 'AND'.
* @param string $like the LIKE operator. Defaults to 'LIKE'. You may also set this to be 'NOT LIKE'.
* @return Query the criteria object itself
- * @since 1.0.10
*/
public function addSearchCondition($column, $keyword, $escape = true, $operator = 'AND', $like = 'LIKE')
{
@@ -293,7 +291,6 @@ class Query extends \yii\base\Object
* @param string $operator the operator used to concatenate the new condition with the existing one.
* Defaults to 'AND'.
* @return Query the criteria object itself
- * @since 1.0.10
*/
public function addInCondition($column, $values, $operator = 'AND')
{
@@ -331,7 +328,6 @@ class Query extends \yii\base\Object
* @param string $operator the operator used to concatenate the new condition with the existing one.
* Defaults to 'AND'.
* @return Query the criteria object itself
- * @since 1.1.1
*/
public function addNotInCondition($column, $values, $operator = 'AND')
{
@@ -368,7 +364,6 @@ class Query extends \yii\base\Object
* @param string $operator the operator used to concatenate the new condition with the existing one.
* Defaults to 'AND'.
* @return Query the criteria object itself
- * @since 1.0.10
*/
public function addColumnCondition($columns, $columnOperator = 'AND', $operator = 'AND')
{
@@ -428,7 +423,6 @@ class Query extends \yii\base\Object
* character on both ends. When this parameter is false, the value will be directly used for
* matching without any change.
* @return Query the criteria object itself
- * @since 1.1.1
*/
public function compare($column, $value, $partialMatch = false, $operator = 'AND', $escape = true)
{
@@ -482,7 +476,6 @@ class Query extends \yii\base\Object
* @param string $operator the operator used to concatenate the new condition with the existing one.
* Defaults to 'AND'.
* @return Query the criteria object itself
- * @since 1.1.2
*/
public function addBetweenCondition($column, $valueStart, $valueEnd, $operator = 'AND')
{
diff --git a/framework/db/dao/QueryBuilder.php b/framework/db/dao/QueryBuilder.php
index bf5a8db..0fc53bf 100644
--- a/framework/db/dao/QueryBuilder.php
+++ b/framework/db/dao/QueryBuilder.php
@@ -110,7 +110,6 @@ class QueryBuilder extends \yii\base\Object
* refer to {@link where} on how to specify conditions.
* @param array $params the parameters to be bound to the query.
* @return integer number of rows affected by the execution.
- * @since 1.1.6
*/
public function update($table, $columns, $conditions = '', &$params = array())
{
@@ -144,7 +143,6 @@ class QueryBuilder extends \yii\base\Object
* refer to {@link where} on how to specify conditions.
* @param array $params the parameters to be bound to the query.
* @return integer number of rows affected by the execution.
- * @since 1.1.6
*/
public function delete($table, $conditions = '')
{
@@ -224,7 +222,6 @@ class QueryBuilder extends \yii\base\Object
* into the physical one. Anything that is not recognized as abstract type will be kept in the generated SQL.
* For example, 'string' will be turned into 'varchar(255)', while 'string not null' will become 'varchar(255) not null'.
* @return string the SQL statement for adding a new column.
- * @since 1.1.6
*/
public function addColumn($table, $column, $type)
{
@@ -238,7 +235,6 @@ class QueryBuilder extends \yii\base\Object
* @param string $table the table whose column is to be dropped. The name will be properly quoted by the method.
* @param string $column the name of the column to be dropped. The name will be properly quoted by the method.
* @return string the SQL statement for dropping a DB column.
- * @since 1.1.6
*/
public function dropColumn($table, $column)
{
@@ -252,7 +248,6 @@ class QueryBuilder extends \yii\base\Object
* @param string $name the old name of the column. The name will be properly quoted by the method.
* @param string $newName the new name of the column. The name will be properly quoted by the method.
* @return string the SQL statement for renaming a DB column.
- * @since 1.1.6
*/
public function renameColumn($table, $name, $newName)
{
diff --git a/framework/db/dao/Schema.php b/framework/db/dao/Schema.php
index 992993a..f5fdd6d 100644
--- a/framework/db/dao/Schema.php
+++ b/framework/db/dao/Schema.php
@@ -35,7 +35,7 @@ abstract class Schema extends \yii\base\Object
/**
* Constructor.
- * @param CDbConnection $conn database connection.
+ * @param CDbConnection $connection database connection.
*/
public function __construct($connection)
{
diff --git a/framework/db/dao/mysql/QueryBuilder.php b/framework/db/dao/mysql/QueryBuilder.php
index 497f457..a53bd9a 100644
--- a/framework/db/dao/mysql/QueryBuilder.php
+++ b/framework/db/dao/mysql/QueryBuilder.php
@@ -45,7 +45,6 @@ class QueryBuilder extends \yii\db\dao\QueryBuilder
* @param string $name the old name of the column. The name will be properly quoted by the method.
* @param string $newName the new name of the column. The name will be properly quoted by the method.
* @return string the SQL statement for renaming a DB column.
- * @since 1.1.6
*/
public function renameColumn($table, $name, $newName)
{
@@ -77,7 +76,6 @@ class QueryBuilder extends \yii\db\dao\QueryBuilder
* @param string $name the name of the foreign key constraint to be dropped. The name will be properly quoted by the method.
* @param string $table the table whose foreign is to be dropped. The name will be properly quoted by the method.
* @return string the SQL statement for dropping a foreign key constraint.
- * @since 1.1.6
*/
public function dropForeignKey($name, $table)
{
diff --git a/framework/logging/ProfileTarget.php b/framework/logging/ProfileTarget.php
index 1525192..39c4172 100644
--- a/framework/logging/ProfileTarget.php
+++ b/framework/logging/ProfileTarget.php
@@ -21,9 +21,7 @@
*
*
* @author Qiang Xue
- * @version $Id: CProfileLogRoute.php 3204 2011-05-05 21:36:32Z alexander.makarow $
- * @package system.logging
- * @since 1.0
+ * @since 2.0
*/
class CProfileLogRoute extends CWebLogRoute
{
@@ -32,7 +30,6 @@ class CProfileLogRoute extends CWebLogRoute
* If false, the results will be aggregated by categories.
* Defaults to true. Note that this property only affects the summary report
* that is enabled when {@link report} is 'summary'.
- * @since 1.0.6
*/
public $groupByToken = true;
/**
diff --git a/framework/logging/WebTarget.php b/framework/logging/WebTarget.php
index cdc622e..ede5d3f 100644
--- a/framework/logging/WebTarget.php
+++ b/framework/logging/WebTarget.php
@@ -15,9 +15,7 @@
* or in FireBug console window (if {@link showInFireBug} is set true).
*
* @author Qiang Xue
- * @version $Id: CWebLogRoute.php 3001 2011-02-24 16:42:44Z alexander.makarow $
- * @package system.logging
- * @since 1.0
+ * @since 2.0
*/
class CWebLogRoute extends CLogRoute
{
@@ -32,7 +30,7 @@ class CWebLogRoute extends CLogRoute
* For example if the ajax call expects a json type result any output from the logger will cause ajax call to fail.
*/
public $ignoreAjaxInFireBug = true;
-
+
/**
* Displays the log messages.
* @param array $logs list of log messages
diff --git a/framework/util/File.php b/framework/util/File.php
new file mode 100644
index 0000000..679305d
--- /dev/null
+++ b/framework/util/File.php
@@ -0,0 +1,141 @@
+
+ * @author Alex Makarov
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright © 2008-2012 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\util;
+
+/**
+ * Filesystem helper
+ *
+ * @since 2.0
+ */
+class File
+{
+ /**
+ * Copies a list of files from one place to another.
+ * @param array $fileList the list of files to be copied (name=>spec).
+ * The array keys are names displayed during the copy process, and array values are specifications
+ * for files to be copied. Each array value must be an array of the following structure:
+ *
+ * @see buildFileList
+ */
+ public function copyFiles($fileList)
+ {
+ $overwriteAll=false;
+ foreach($fileList as $name=>$file)
+ {
+ $source=strtr($file['source'],'/\\',DIRECTORY_SEPARATOR);
+ $target=strtr($file['target'],'/\\',DIRECTORY_SEPARATOR);
+ $callback=isset($file['callback']) ? $file['callback'] : null;
+ $params=isset($file['params']) ? $file['params'] : null;
+
+ if(is_dir($source))
+ {
+ $this->ensureDirectory($target);
+ continue;
+ }
+
+ if($callback!==null)
+ $content=call_user_func($callback,$source,$params);
+ else
+ $content=file_get_contents($source);
+ if(is_file($target))
+ {
+ if($content===file_get_contents($target))
+ {
+ echo " unchanged $name\n";
+ continue;
+ }
+ if($overwriteAll)
+ echo " overwrite $name\n";
+ else
+ {
+ echo " exist $name\n";
+ echo " ...overwrite? [Yes|No|All|Quit] ";
+ $answer=trim(fgets(STDIN));
+ if(!strncasecmp($answer,'q',1))
+ return;
+ else if(!strncasecmp($answer,'y',1))
+ echo " overwrite $name\n";
+ else if(!strncasecmp($answer,'a',1))
+ {
+ echo " overwrite $name\n";
+ $overwriteAll=true;
+ }
+ else
+ {
+ echo " skip $name\n";
+ continue;
+ }
+ }
+ }
+ else
+ {
+ $this->ensureDirectory(dirname($target));
+ echo " generate $name\n";
+ }
+ file_put_contents($target,$content);
+ }
+ }
+
+ /**
+ * Builds the file list of a directory.
+ * This method traverses through the specified directory and builds
+ * a list of files and subdirectories that the directory contains.
+ * The result of this function can be passed to {@link copyFiles}.
+ * @param string $sourceDir the source directory
+ * @param string $targetDir the target directory
+ * @param string $baseDir base directory
+ * @return array the file list (see {@link copyFiles})
+ */
+ public function buildFileList($sourceDir, $targetDir, $baseDir='')
+ {
+ $list=array();
+ $handle=opendir($sourceDir);
+ while(($file=readdir($handle))!==false)
+ {
+ if($file==='.' || $file==='..' || $file==='.svn' ||$file==='.yii')
+ continue;
+ $sourcePath=$sourceDir.DIRECTORY_SEPARATOR.$file;
+ $targetPath=$targetDir.DIRECTORY_SEPARATOR.$file;
+ $name=$baseDir===''?$file : $baseDir.'/'.$file;
+ $list[$name]=array('source'=>$sourcePath, 'target'=>$targetPath);
+ if(is_dir($sourcePath))
+ $list=array_merge($list,$this->buildFileList($sourcePath,$targetPath,$name));
+ }
+ closedir($handle);
+ return $list;
+ }
+
+ /**
+ * Creates all parent directories if they do not exist.
+ * @param string $directory the directory to be checked
+ */
+ public function ensureDirectory($directory)
+ {
+ if(!is_dir($directory))
+ {
+ $this->ensureDirectory(dirname($directory));
+ echo " mkdir ".strtr($directory,'\\','/')."\n";
+ mkdir($directory);
+ }
+ }
+}
diff --git a/framework/util/Text.php b/framework/util/Text.php
new file mode 100644
index 0000000..582775f
--- /dev/null
+++ b/framework/util/Text.php
@@ -0,0 +1,43 @@
+
+ * @author Alex Makarov
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright © 2008-2012 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\util;
+
+/**
+ * Text helper
+ *
+ * @since 2.0
+ */
+class Text
+{
+ /**
+ * Converts a word to its plural form.
+ * @param string $name the word to be pluralized
+ * @return string the pluralized word
+ */
+ public function pluralize($name)
+ {
+ $rules=array(
+ '/(x|ch|ss|sh|us|as|is|os)$/i' => '\1es',
+ '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves',
+ '/(m)an$/i' => '\1en',
+ '/(child)$/i' => '\1ren',
+ '/(r)y$/i' => '\1ies',
+ '/s$/' => 's',
+ );
+ foreach($rules as $rule=>$replacement)
+ {
+ if(preg_match($rule,$name))
+ return preg_replace($rule,$replacement,$name);
+ }
+ return $name.'s';
+ }
+}
diff --git a/framework/validators/DateValidator.php b/framework/validators/DateValidator.php
index 6fb2b2b..f3309f2 100644
--- a/framework/validators/DateValidator.php
+++ b/framework/validators/DateValidator.php
@@ -16,9 +16,7 @@ namespace yii\validators;
* must be in. If the given date value doesn't follow the format, the attribute is considered as invalid.
*
* @author Qiang Xue
- * @version $Id: CDateValidator.php 2799 2011-01-01 19:31:13Z qiang.xue $
- * @package system.validators
- * @since 1.1.7
+ * @since 2.0
*/
class CDateValidator extends Validator
{
diff --git a/framework/validators/ExistValidator.php b/framework/validators/ExistValidator.php
index bb68a10..10f4e82 100644
--- a/framework/validators/ExistValidator.php
+++ b/framework/validators/ExistValidator.php
@@ -16,9 +16,7 @@ namespace yii\validators;
* that can be found in the foreign table.
*
* @author Qiang Xue
- * @version $Id: CExistValidator.php 2799 2011-01-01 19:31:13Z qiang.xue $
- * @package system.validators
- * @since 1.0.4
+ * @since 2.0
*/
class CExistValidator extends Validator
{
@@ -41,7 +39,6 @@ class CExistValidator extends Validator
* @var array additional query criteria. This will be combined with the condition
* that checks if the attribute value exists in the corresponding table column.
* This array will be used to instantiate a {@link CDbCriteria} object.
- * @since 1.0.8
*/
public $criteria = array();
/**
diff --git a/framework/validators/FileValidator.php b/framework/validators/FileValidator.php
index 95ad356..448be36 100644
--- a/framework/validators/FileValidator.php
+++ b/framework/validators/FileValidator.php
@@ -40,9 +40,7 @@ namespace yii\validators;
* You can use {@link CFileValidator} to validate the file attribute.
*
* @author Qiang Xue
- * @version $Id: CFileValidator.php 2799 2011-01-01 19:31:13Z qiang.xue $
- * @package system.validators
- * @since 1.0
+ * @since 2.0
*/
class CFileValidator extends Validator
{
diff --git a/framework/validators/UniqueValidator.php b/framework/validators/UniqueValidator.php
index c6856c1..ef13e26 100644
--- a/framework/validators/UniqueValidator.php
+++ b/framework/validators/UniqueValidator.php
@@ -13,8 +13,6 @@ namespace yii\validators;
* CUniqueValidator validates that the attribute value is unique in the corresponding database table.
*
* @author Qiang Xue
- * @version $Id: CUniqueValidator.php 3260 2011-06-13 20:56:54Z alexander.makarow $
- * @package system.validators
* @since 1.0
*/
class CUniqueValidator extends Validator
@@ -35,7 +33,6 @@ class CUniqueValidator extends Validator
* the class of the object currently being validated.
* You may use path alias to reference a class name here.
* @see attributeName
- * @since 1.0.8
*/
public $className;
/**
@@ -43,14 +40,12 @@ class CUniqueValidator extends Validator
* used to look for the attribute value being validated. Defaults to null,
* meaning using the name of the attribute being validated.
* @see className
- * @since 1.0.8
*/
public $attributeName;
/**
* @var array additional query criteria. This will be combined with the condition
* that checks if the attribute value exists in the corresponding table column.
* This array will be used to instantiate a {@link CDbCriteria} object.
- * @since 1.0.8
*/
public $criteria = array();
/**
@@ -61,7 +56,6 @@ class CUniqueValidator extends Validator
/**
* @var boolean whether this validation rule should be skipped if when there is already a validation
* error for the current attribute. Defaults to true.
- * @since 1.1.1
*/
public $skipOnError = true;
diff --git a/framework/web/HttpException.php b/framework/web/HttpException.php
new file mode 100644
index 0000000..761d31c
--- /dev/null
+++ b/framework/web/HttpException.php
@@ -0,0 +1,40 @@
+
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright © 2008-2012 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\web;
+
+/**
+ * CHttpException represents an exception caused by invalid operations of end-users.
+ *
+ * The HTTP error code can be obtained via {@link statusCode}.
+ * Error handlers may use this status code to decide how to format the error page.
+ *
+ * @author Qiang Xue
+ * @since 2.0
+ */
+class HttpException extends \yii\base\Exception
+{
+ /**
+ * @var integer HTTP status code, such as 403, 404, 500, etc.
+ */
+ public $statusCode;
+
+ /**
+ * Constructor.
+ * @param integer $status HTTP status code, such as 404, 500, etc.
+ * @param string $message error message
+ * @param integer $code error code
+ */
+ public function __construct($status,$message=null,$code=0)
+ {
+ $this->statusCode=$status;
+ parent::__construct($message,$code);
+ }
+}
diff --git a/framework/yiic b/framework/yiic
new file mode 100644
index 0000000..cba5566
--- /dev/null
+++ b/framework/yiic
@@ -0,0 +1,14 @@
+#!/usr/bin/env php
+
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright © 2012 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+require_once(__DIR__.'/yiic.php');
diff --git a/framework/yiic.bat b/framework/yiic.bat
new file mode 100644
index 0000000..c63fc81
--- /dev/null
+++ b/framework/yiic.bat
@@ -0,0 +1,22 @@
+@echo off
+
+rem -------------------------------------------------------------
+rem Yii command line script for Windows.
+rem
+rem This is the bootstrap script for running yiic on Windows.
+rem
+rem @author Qiang Xue
+rem @link http://www.yiiframework.com/
+rem @copyright Copyright © 2012 Yii Software LLC
+rem @license http://www.yiiframework.com/license/
+rem -------------------------------------------------------------
+
+@setlocal
+
+set YII_PATH=%~dp0
+
+if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe
+
+"%PHP_COMMAND%" "%YII_PATH%yiic" %*
+
+@endlocal
\ No newline at end of file
diff --git a/framework/yiic.php b/framework/yiic.php
new file mode 100644
index 0000000..72f6f36
--- /dev/null
+++ b/framework/yiic.php
@@ -0,0 +1,30 @@
+commandRunner->addCommands(YII_PATH.'/cli/commands');
+ $env=@getenv('YII_CONSOLE_COMMANDS');
+ if(!empty($env))
+ $app->commandRunner->addCommands($env);
+}
+else
+{
+ $app=new \yii\console\Application(array(
+ 'basePath'=>__DIR__.'/cli',
+ ));
+}
+
+$app->run();
\ No newline at end of file
diff --git a/todo.md b/todo.md
index ad23eb6..867d287 100644
--- a/todo.md
+++ b/todo.md
@@ -58,4 +58,6 @@
- assets
* ability to manage scripts order (store these in a vector?)
* http://ryanbigg.com/guides/asset_pipeline.html, http://guides.rubyonrails.org/asset_pipeline.html, use content hash instead of mtime + directory hash.
-- Requirement checker
\ No newline at end of file
+- Requirement checker
+- widgets
+ * if we're going to supply default ones, these should generate really unique IDs. This will solve a lot of AJAX-nesting problems.
\ No newline at end of file