diff --git a/.gitignore b/.gitignore
index 89fc2a8..9291d3e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,4 +10,7 @@ nbproject
.settings
# windows thumbnail cache
-Thumbs.db
\ No newline at end of file
+Thumbs.db
+
+# composer vendor dir
+vendor
\ No newline at end of file
diff --git a/apps/bootstrap/protected/views/layouts/main.php b/apps/bootstrap/protected/views/layouts/main.php
index 8fb915a..1240053 100644
--- a/apps/bootstrap/protected/views/layouts/main.php
+++ b/apps/bootstrap/protected/views/layouts/main.php
@@ -39,6 +39,9 @@ $this->registerAssetBundle('app');
+ widget('yii\widgets\Breadcrumbs', array(
+ 'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : array(),
+ )); ?>
diff --git a/apps/bootstrap/protected/views/site/about.php b/apps/bootstrap/protected/views/site/about.php
index 7ebc5d5..86e19e1 100644
--- a/apps/bootstrap/protected/views/site/about.php
+++ b/apps/bootstrap/protected/views/site/about.php
@@ -4,6 +4,7 @@ use yii\helpers\Html;
* @var yii\base\View $this
*/
$this->title = 'About';
+$this->params['breadcrumbs'][] = $this->title;
?>
title); ?>
diff --git a/apps/bootstrap/protected/views/site/contact.php b/apps/bootstrap/protected/views/site/contact.php
index 4ca40f1..5cb5a8e 100644
--- a/apps/bootstrap/protected/views/site/contact.php
+++ b/apps/bootstrap/protected/views/site/contact.php
@@ -6,6 +6,7 @@ use yii\helpers\Html;
* @var app\models\ContactForm $model
*/
$this->title = 'Contact';
+$this->params['breadcrumbs'][] = $this->title;
?>
title); ?>
diff --git a/apps/bootstrap/protected/views/site/login.php b/apps/bootstrap/protected/views/site/login.php
index 8672eeb..f7f842e 100644
--- a/apps/bootstrap/protected/views/site/login.php
+++ b/apps/bootstrap/protected/views/site/login.php
@@ -6,6 +6,7 @@ use yii\helpers\Html;
* @var app\models\LoginForm $model
*/
$this->title = 'Login';
+$this->params['breadcrumbs'][] = $this->title;
?>
title); ?>
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..6a5c383
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,75 @@
+{
+ "name": "yiisoft/yii2",
+ "description": "Yii2 Web Programming Framework",
+ "keywords": ["yii", "framework"],
+ "homepage": "http://www.yiiframework.com/",
+ "type": "library",
+ "license": "BSD-3-Clause",
+ "authors": [
+ {
+ "name": "Qiang Xue",
+ "email": "qiang.xue@gmail.com",
+ "homepage": "http://www.yiiframework.com/",
+ "role": "Founder and project lead"
+ },
+ {
+ "name": "Alexander Makarov",
+ "email": "sam@rmcreative.ru",
+ "homepage": "http://rmcreative.ru/",
+ "role": "Core framework development"
+ },
+ {
+ "name": "Maurizio Domba",
+ "homepage": "http://mdomba.info/",
+ "role": "Core framework development"
+ },
+ {
+ "name": "Carsten Brandt",
+ "email": "mail@cebe.cc",
+ "homepage": "http://cebe.cc/",
+ "role": "Core framework development"
+ },
+ {
+ "name": "Timur Ruziev",
+ "email": "resurtm@gmail.com",
+ "homepage": "http://resurtm.com/",
+ "role": "Core framework development"
+ },
+ {
+ "name": "Paul Klimov",
+ "email": "klimov.paul@gmail.com",
+ "role": "Core framework development"
+ },
+ {
+ "name": "Wei Zhuo",
+ "email": "weizhuo@gmail.com",
+ "role": "Project site maintenance and development"
+ },
+ {
+ "name": "Sebastián Thierer",
+ "email": "sebas@artfos.com",
+ "role": "Component development"
+ },
+ {
+ "name": "Jeffrey Winesett",
+ "email": "jefftulsa@gmail.com",
+ "role": "Documentation and marketing"
+ }
+ ],
+ "support": {
+ "issues": "https://github.com/yiisoft/yii2/issues?state=open",
+ "forum": "http://www.yiiframework.com/forum/",
+ "wiki": "http://www.yiiframework.com/wiki/",
+ "irc": "irc://irc.freenode.net/yii",
+ "source": "https://github.com/yiisoft/yii2"
+ },
+ "bin": [
+ "framework/yiic"
+ ],
+ "require": {
+ "php": ">=5.3.0",
+ "michelf/php-markdown": "1.3",
+ "twig/twig": "1.12.*",
+ "smarty/smarty": "3.1.*"
+ }
+}
diff --git a/docs/guide/index.md b/docs/guide/index.md
index 6ad733c..dd72ca3 100644
--- a/docs/guide/index.md
+++ b/docs/guide/index.md
@@ -27,4 +27,4 @@
* [Performance Tuning](performance.md)
* [Testing](testing.md)
* [Automatic Code Generation](gii.md)
-* [Upgrading from 1.1 to 2.0](upgrade.md)
+* [Upgrading from 1.1 to 2.0](upgrade-from-v1.md)
diff --git a/docs/guide/upgrade-from-v1.md b/docs/guide/upgrade-from-v1.md
new file mode 100644
index 0000000..d35e6e0
--- /dev/null
+++ b/docs/guide/upgrade-from-v1.md
@@ -0,0 +1,459 @@
+Upgrading from Yii 1.1
+======================
+
+In this chapter, we list the major changes introduced in Yii 2.0 since version 1.1.
+We hope this list will make it easier for you to upgrade from Yii 1.1 and quickly
+master Yii 2.0 based on your existing Yii knowledge.
+
+
+Namespace
+---------
+
+The most obvious change in Yii 2.0 is the use of namespaces. Almost every core class
+is namespaced, e.g., `yii\web\Request`. The "C" prefix is no longer used in class names.
+The naming of the namespaces follows the directory structure. For example, `yii\web\Request`
+indicates the corresponding class file is `web/Request.php` under the Yii framework folder.
+You can use any core class without explicitly include that class file, thanks to the Yii
+class loader.
+
+
+Component and Object
+--------------------
+
+Yii 2.0 breaks the `CComponent` class in 1.1 into two classes: `Object` and `Component`.
+The `Object` class is a lightweight base class that allows defining class properties
+via getters and setters. The `Component` class extends from `Object` and supports
+the event feature and the behavior feature.
+
+If your class does not need the event or behavior feature, you should consider using
+`Object` as the base class. This is usually the case for classes that represent basic
+data structures.
+
+
+Object Configuration
+--------------------
+
+The `Object` class introduces a uniform way of configuring objects. Any descendant class
+of `Object` should declare its constructor (if needed) in the following way so that
+it can be properly configured:
+
+```php
+class MyClass extends \yii\Object
+{
+ public function __construct($param1, $param2, $config = array())
+ {
+ // ... initialization before configuration is applied
+
+ parent::__construct($config);
+ }
+
+ public function init()
+ {
+ parent::init();
+
+ // ... initialization after configuration is applied
+ }
+}
+```
+
+In the above, the last parameter of the constructor must take a configuration array
+which contains name-value pairs for initializing the properties at the end of the constructor.
+You can override the `init()` method to do initialization work that should be done after
+the configuration is applied.
+
+By following this convention, you will be able to create and configure a new object
+using a configuration array like the following:
+
+```php
+$object = Yii::createObject(array(
+ 'class' => 'MyClass',
+ 'property1' => 'abc',
+ 'property2' => 'cde',
+), $param1, $param2);
+```
+
+
+
+Events
+------
+
+There is no longer the need to define an `on`-method in order to define an event in Yii 2.0.
+Instead, you can use whatever event names. To attach a handler to an event, you should
+use the `on` method now:
+
+```php
+$component->on($eventName, $handler);
+// To detach the handler, use:
+// $component->off($eventName, $handler);
+```
+
+
+When you attach a handler, you can now associate it with some parameters which can be later
+accessed via the event parameter by the handler:
+
+```php
+$component->on($eventName, $handler, $params);
+```
+
+
+Because of this change, you can now use "global" events. Simply trigger and attach handlers to
+an event of the application instance:
+
+```php
+Yii::$app->on($eventName, $handler);
+....
+// this will trigger the event and cause $handler to be invoked.
+Yii::$app->trigger($eventName);
+```
+
+
+Path Alias
+----------
+
+Yii 2.0 expands the usage of path aliases to both file/directory paths and URLs. An alias
+must start with a `@` character so that it can be differentiated from file/directory paths and URLs.
+For example, the alias `@yii` refers to the Yii installation directory. Path aliases are
+supported in most places in the Yii core code. For example, `FileCache::cachePath` can take
+both a path alias and a normal directory path.
+
+Path alias is also closely related with class namespaces. It is recommended that a path
+alias defined for each root namespace so that you can use Yii class autoloader without
+any further configuration. For example, because `@yii` refers to the Yii installation directory,
+a class like `yii\web\Request` can be autoloaded by Yii. If you use a third party library
+such as Zend Framework, you may define a path alias `@Zend` which refers to its installation directory.
+And Yii will be able to autoload any class in this library.
+
+
+View
+----
+
+Yii 2.0 introduces a `View` class to represent the view part in the MVC pattern.
+It can be configured globally through the "view" application component. It is also
+accessible in any view file via `$this`. This is one of the biggest changes compared to 1.1:
+**`$this` in a view file no longer refers to the controller or widget object.**
+It refers to the view object that is used to render the view file. To access the controller
+or the widget object, you have to use `$this->context` now.
+
+Because you can access the view object through the "view" application component,
+you can now render a view file like the following anywhere in your code, not necessarily
+in controllers or widgets:
+
+```php
+$content = Yii::$app->view->renderFile($viewFile, $params);
+// You can also explicitly create a new View instance to do the rendering
+// $view = new View;
+// $view->renderFile($viewFile, $params);
+```
+
+
+Also, there is no more `CClientScript` in Yii 2.0. The `View` class has taken over its role
+with significant improvements. For more details, please see the "assets" subsection.
+
+While Yii 2.0 continues to use PHP as its main template language, it comes with built-in
+support for two popular template engines: Smarty and Twig. The Prado template engine is
+no longer supported. To use these template engines, you just need to use `tpl` as the file
+extension for your Smarty views, or `twig` for Twig views. You may also configure the
+`View::renderers` property to use other template engines.
+
+
+Models
+------
+
+A model is now associated with a form name returned its `formName()` method. This is
+mainly used when using HTML forms to collect user inputs for a model. Previously in 1.1,
+this is usually hardcoded as the class name of the model.
+
+
+Yii 2.0 introduces a new method called `scenarios()` to declare which attributes require
+validation under which scenario. Child classes should overwrite `scenarios()` to return
+a list of scenarios and the corresponding attributes that need to be validated when
+`validate()` is called. For example,
+
+```php
+public function scenarios()
+{
+ return array(
+ 'backend' => array('email', 'role'),
+ 'frontend' => array('email', '!name'),
+ );
+}
+```
+
+
+This method also determines which attributes are safe and which are not. In particular,
+given a scenario, if an attribute appears in the corresponding attribute list in `scenarios()`
+and the name is not prefixed with `!`, it is considered *safe*.
+
+Because of the above change, Yii 2.0 no longer has "safe" and "unsafe" validators.
+
+If your model only has one scenario (very common), you do not have to overwrite `scenarios()`,
+and everything will still work like the 1.1 way.
+
+
+Controllers
+-----------
+
+The `render()` and `renderPartial()` methods now return the rendering results instead of directly
+sending them out. You have to `echo` them explicitly, e.g., `echo $this->render(...);`.
+
+A new method called `populate()` is introduced to simplify the data population from user inputs
+to a model. For example,
+
+```php
+$model = new Post;
+if ($this->populate($_POST, $model)) {...}
+// which is equivalent to:
+if (isset($_POST['Post'])) {
+ $model->attributes = $_POST['Post'];
+}
+```
+
+
+
+Themes
+------
+
+Theme works completely different in 2.0. It is now based on a path map to "translate" a source
+view into a themed view. For example, if the path map for a theme is
+`array('/www/views' => '/www/themes/basic')`, then the themed version for a view file
+`/www/views/site/index.php` will be `/www/themes/basic/site/index.php`.
+
+For this reason, theme can now be applied to any view file, even if a view rendered outside
+of the context of a controller or a widget.
+
+There is no more `CThemeManager`. Instead, `theme` is a configurable property of the "view"
+application component.
+
+
+Console Applications
+--------------------
+
+Console applications are now composed by controllers, too, like Web applications. In fact,
+console controllers and Web controllers share the same base controller class.
+
+Each console controller is like `CConsoleCommand` in 1.1. It consists of one or several
+actions. You use the `yiic ` command to execute a console command, where ``
+stands for a controller route (e.g. `sitemap/index`). Additional anonymous arguments
+are passed as the parameters to the corresponding controller action method, and named arguments
+are treated as global options declared in `globalOptions()`.
+
+Yii 2.0 supports automatic generation of command help information from comment blocks.
+
+
+I18N
+----
+
+Yii 2.0 removes date formatter and number formatter in favor of the PECL intl PHP module.
+
+Message translation is still supported, but managed via the "i18n" application component.
+The component manages a set of message sources, which allows you to use different message
+sources based on message categories. For more information, see the class documentation for `I18N`.
+
+The message translation method is changed by merging the message category into the message being
+translated. For example, `Yii::t('yii|message to be translated')`.
+
+
+
+Action Filters
+--------------
+
+Action filters are implemented via behaviors now. You should extend from `ActionFilter` to
+define a new filter. To use a filter, you should attach the filter class to the controller
+as a behavior. For example, to use the `AccessControl` filter, you should have the following
+code in a controller:
+
+```php
+public function behaviors()
+{
+ return array(
+ 'access' => array(
+ 'class' => 'yii\web\AccessControl',
+ 'rules' => array(
+ array('allow' => true, 'actions' => array('admin'), 'roles' => array('@')),
+ array('allow' => false),
+ ),
+ ),
+ );
+}
+```
+
+
+
+Assets
+------
+
+Yii 2.0 introduces a new concept called *asset bundle*. It is a bit similar to script
+packages (managed by `CClientScript`) in 1.1, but with better support.
+
+An asset bundle is a collection of asset files (e.g. JavaScript files, CSS files, image files, etc.)
+under a directory. By registering an asset bundle via `View::registerAssetBundle()`, you
+will be able to make the assets in that bundle accessible via Web, and the current page
+will automatically contain references to the JavaScript and CSS files in that bundle.
+
+
+
+Static Helpers
+--------------
+
+Yii 2.0 introduces many commonly used static helper classes, such as `Html`, `ArrayHelper`,
+`StringHelper`. These classes are designed to be easily extended. Note that static classes
+are usually hard to be extended because of the fixed class name references. But Yii 2.0
+introduces the class map (via `Yii::$classMap`) to overcome this difficulty.
+
+
+`ActiveForm`
+------------
+
+Yii 2.0 introduces the *field* concept for building a form using `ActiveForm`. A field
+is a container consisting of a label, an input, and an error message. It is represented
+as an `ActiveField` object. Using fields, you can build a form more cleanly than before:
+
+```php
+beginWidget('yii\widgets\ActiveForm'); ?>
+ field($model, 'username')->textInput(); ?>
+ field($model, 'password')->passwordInput(); ?>
+
+
+
+endWidget(); ?>
+```
+
+
+
+Query Builder
+-------------
+
+In 1.1, query building is scattered among several classes, including `CDbCommand`,
+`CDbCriteria`, and `CDbCommandBuilder`. Yii 2.0 uses `Query` to represent a DB query
+and `QueryBuilder` to generate SQL statements from query objects. For example,
+
+```php
+$query = new \yii\db\Query;
+$query->select('id, name')
+ ->from('tbl_user')
+ ->limit(10);
+
+$command = $query->createCommand();
+$sql = $command->sql;
+$rows = $command->queryAll();
+```
+
+
+Best of all, such query building methods can be used together with `ActiveRecord`,
+as explained in the next sub-section.
+
+
+ActiveRecord
+------------
+
+ActiveRecord has undergone significant changes in Yii 2.0. The most important one
+is about relational ActiveRecord query. In 1.1, you have to declare the relations
+in the `relations()` method. In 2.0, this is done via getter methods that return
+an `ActiveQuery` object. For example, the following method declares an "orders" relation:
+
+```php
+class Customer extends \yii\db\ActiveRecord
+{
+ public function getOrders()
+ {
+ return $this->hasMany('Order', array('customer_id' => 'id'));
+ }
+}
+```
+
+
+You can use `$customer->orders` to access the customer's orders. You can also
+use `$customer->getOrders()->andWhere('status=1')->all()` to perform on-the-fly
+relational query with customized query conditions.
+
+When loading relational records in an eager way, Yii 2.0 does it differently from 1.1.
+In particular, in 1.1 a JOIN query would be used to bring both the primary and the relational
+records; while in 2.0, two SQL statements are executed without using JOIN: the first
+statement brings back the primary records and the second brings back the relational records
+by filtering with the primary keys of the primary records.
+
+
+Yii 2.0 no longer uses the `model()` method when performing queries. Instead, you
+use the `find()` method like the following:
+
+```php
+// to retrieve all *active* customers and order them by their ID:
+$customers = Customer::find()
+ ->where(array('status' => $active))
+ ->orderBy('id')
+ ->all();
+// return the customer whose PK is 1
+$customer = Customer::find(1);
+```
+
+
+The `find()` method returns an instance of `ActiveQuery` which is a subclass of `Query`.
+Therefore, you can use all query methods of `Query`.
+
+Instead of returning ActiveRecord objects, you may call `ActiveQuery::asArray()` to
+return results in terms of arrays. This is more efficient and is especially useful
+when you need to return large number of records. For example,
+
+```php
+$customers = Customer::find()->asArray()->all();
+```
+
+By default, ActiveRecord now only saves dirty attributes. In 1.1, all attributes
+would be saved to database when you call `save()`, regardless they are changed or not,
+unless you explicitly list the attributes to save.
+
+
+Auto-quoting Table and Column Names
+------------------------------------
+
+Yii 2.0 supports automatic quoting of database table and column names. A name enclosed
+within double curly brackets is treated as a table name, and a name enclosed within
+double square brackets is treated as a column name. They will be quoted according to
+the database driver being used. For example,
+
+```php
+$command = $connection->createCommand('SELECT [[id]] FROM {{posts}}');
+echo $command->sql; // MySQL: SELECT `id` FROM `posts`
+```
+
+This feature is especially useful if you are developing an application that supports
+different DBMS.
+
+
+User and Identity
+-----------------
+
+The `CWebUser` class in 1.1 is now replaced by `\yii\Web\User`, and there is no more
+`CUserIdentity` class. Instead, you should implement the `Identity` interface which
+is much more straightforward to implement. The bootstrap application provides such an example.
+
+
+URL Management
+--------------
+
+URL management is similar to 1.1. A major enhancement is that it now supports optional
+parameters. For example, if you have rule declared as follows, then it will match
+both `post/popular` and `post/1/popular`. In 1.1, you would have to use two rules to achieve
+the same goal.
+
+```php
+array(
+ 'pattern' => 'post//',
+ 'route' => 'post/index',
+ 'defaults' => array('page' => 1),
+)
+```
+
+
+
+Response
+--------
+
+Extensions
+----------
+
+Integration with Composer
+-------------------------
+
+TBD
+
diff --git a/framework/YiiBase.php b/framework/YiiBase.php
index ed975c9..c911f78 100644
--- a/framework/YiiBase.php
+++ b/framework/YiiBase.php
@@ -4,6 +4,8 @@
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
+namespace yii;
+
use yii\base\Exception;
use yii\base\InvalidConfigException;
use yii\base\InvalidParamException;
@@ -60,7 +62,7 @@ class YiiBase
*/
public static $enableIncludePath = true;
/**
- * @var yii\console\Application|yii\web\Application the application instance
+ * @var \yii\console\Application|\yii\web\Application the application instance
*/
public static $app;
/**
diff --git a/framework/base/Component.php b/framework/base/Component.php
index 582cf03..8e75835 100644
--- a/framework/base/Component.php
+++ b/framework/base/Component.php
@@ -90,6 +90,7 @@ class Component extends Object
// as behavior: attach behavior
$name = trim(substr($name, 3));
$this->attachBehavior($name, $value instanceof Behavior ? $value : Yii::createObject($value));
+ return;
} else {
// behavior property
$this->ensureBehaviors();
diff --git a/framework/base/View.php b/framework/base/View.php
index 4718d90..af1f0d5 100644
--- a/framework/base/View.php
+++ b/framework/base/View.php
@@ -72,23 +72,21 @@ class View extends Component
/**
* @var array a list of available renderers indexed by their corresponding supported file extensions.
* Each renderer may be a view renderer object or the configuration for creating the renderer object.
- * For example,
- *
- * ~~~
- * array(
- * 'tpl' => array(
- * 'class' => 'yii\renderers\SmartyRenderer',
- * ),
- * 'twig' => array(
- * 'class' => 'yii\renderers\TwigRenderer',
- * ),
- * )
- * ~~~
+ * The default setting supports both Smarty and Twig (their corresponding file extension is "tpl"
+ * and "twig" respectively. Please refer to [[SmartyRenderer]] and [[TwigRenderer]] on how to install
+ * the needed libraries for these template engines.
*
* If no renderer is available for the given view file, the view file will be treated as a normal PHP
* and rendered via [[renderPhpFile()]].
*/
- public $renderers = array();
+ public $renderers = array(
+ 'tpl' => array(
+ 'class' => 'yii\renderers\SmartyRenderer',
+ ),
+ 'twig' => array(
+ 'class' => 'yii\renderers\TwigRenderer',
+ ),
+ );
/**
* @var Theme|array the theme object or the configuration array for creating the theme object.
* If not set, it means theming is not enabled.
diff --git a/framework/db/ActiveRecord.php b/framework/db/ActiveRecord.php
index 45c53fb..709d139 100644
--- a/framework/db/ActiveRecord.php
+++ b/framework/db/ActiveRecord.php
@@ -8,6 +8,7 @@
namespace yii\db;
+use yii\base\InvalidConfigException;
use yii\base\Model;
use yii\base\InvalidParamException;
use yii\base\ModelEvent;
@@ -112,6 +113,7 @@ class ActiveRecord extends Model
* @return ActiveQuery|ActiveRecord|null When `$q` is null, a new [[ActiveQuery]] instance
* is returned; when `$q` is a scalar or an array, an ActiveRecord object matching it will be
* returned (null will be returned if there is no matching).
+ * @throws InvalidConfigException if the AR class does not have a primary key
* @see createQuery()
*/
public static function find($q = null)
@@ -122,7 +124,11 @@ class ActiveRecord extends Model
} elseif ($q !== null) {
// query by primary key
$primaryKey = static::primaryKey();
- return $query->where(array($primaryKey[0] => $q))->one();
+ if (isset($primaryKey[0])) {
+ return $query->where(array($primaryKey[0] => $q))->one();
+ } else {
+ throw new InvalidConfigException(get_called_class() . ' must have a primary key.');
+ }
}
return $query;
}
diff --git a/framework/helpers/base/Json.php b/framework/helpers/base/Json.php
index c92e208..262dd81 100644
--- a/framework/helpers/base/Json.php
+++ b/framework/helpers/base/Json.php
@@ -8,7 +8,7 @@
namespace yii\helpers\base;
use yii\base\InvalidParamException;
-use yii\helpers\JsExpression;
+use yii\web\JsExpression;
/**
* Json is a helper class providing JSON data encoding and decoding.
diff --git a/framework/validators/EmailValidator.php b/framework/validators/EmailValidator.php
index ad74dd6..949b3f9 100644
--- a/framework/validators/EmailValidator.php
+++ b/framework/validators/EmailValidator.php
@@ -9,7 +9,7 @@ namespace yii\validators;
use Yii;
use yii\helpers\Html;
-use yii\helpers\JsExpression;
+use yii\web\JsExpression;
use yii\helpers\Json;
/**
diff --git a/framework/validators/NumberValidator.php b/framework/validators/NumberValidator.php
index c0f81cd..10f0e52 100644
--- a/framework/validators/NumberValidator.php
+++ b/framework/validators/NumberValidator.php
@@ -9,7 +9,7 @@ namespace yii\validators;
use Yii;
use yii\helpers\Html;
-use yii\helpers\JsExpression;
+use yii\web\JsExpression;
use yii\helpers\Json;
/**
diff --git a/framework/validators/RegularExpressionValidator.php b/framework/validators/RegularExpressionValidator.php
index 79a1a3c..23419b9 100644
--- a/framework/validators/RegularExpressionValidator.php
+++ b/framework/validators/RegularExpressionValidator.php
@@ -10,7 +10,7 @@ namespace yii\validators;
use Yii;
use yii\base\InvalidConfigException;
use yii\helpers\Html;
-use yii\helpers\JsExpression;
+use yii\web\JsExpression;
use yii\helpers\Json;
/**
diff --git a/framework/validators/UrlValidator.php b/framework/validators/UrlValidator.php
index 0ed59bd..c418353 100644
--- a/framework/validators/UrlValidator.php
+++ b/framework/validators/UrlValidator.php
@@ -9,7 +9,7 @@ namespace yii\validators;
use Yii;
use yii\helpers\Html;
-use yii\helpers\JsExpression;
+use yii\web\JsExpression;
use yii\helpers\Json;
/**
diff --git a/framework/helpers/JsExpression.php b/framework/web/JsExpression.php
similarity index 97%
rename from framework/helpers/JsExpression.php
rename to framework/web/JsExpression.php
index 5a1f9bd..027c065 100644
--- a/framework/helpers/JsExpression.php
+++ b/framework/web/JsExpression.php
@@ -5,7 +5,7 @@
* @license http://www.yiiframework.com/license/
*/
-namespace yii\helpers;
+namespace yii\web;
use yii\base\Object;
diff --git a/framework/widgets/ActiveField.php b/framework/widgets/ActiveField.php
index 336966f..0e0381f 100644
--- a/framework/widgets/ActiveField.php
+++ b/framework/widgets/ActiveField.php
@@ -10,7 +10,7 @@ use yii\base\Component;
use yii\db\ActiveRecord;
use yii\helpers\Html;
use yii\base\Model;
-use yii\helpers\JsExpression;
+use yii\web\JsExpression;
/**
* @author Qiang Xue
diff --git a/framework/widgets/ActiveForm.php b/framework/widgets/ActiveForm.php
index c11bceb..61416e2 100644
--- a/framework/widgets/ActiveForm.php
+++ b/framework/widgets/ActiveForm.php
@@ -12,7 +12,6 @@ use yii\base\Widget;
use yii\base\Model;
use yii\helpers\Html;
use yii\helpers\Json;
-use yii\helpers\JsExpression;
/**
* ActiveForm ...
diff --git a/framework/widgets/Breadcrumbs.php b/framework/widgets/Breadcrumbs.php
new file mode 100644
index 0000000..22d09b3
--- /dev/null
+++ b/framework/widgets/Breadcrumbs.php
@@ -0,0 +1,139 @@
+widget('yii\widgets\Breadcrumbs', array(
+ * 'links' => array(
+ * array('label' => 'Sample Post', 'url' => array('post/edit', 'id' => 1)),
+ * 'Edit',
+ * ),
+ * ));
+ * ~~~
+ *
+ * Because breadcrumbs usually appears in nearly every page of a website, you may consider place it in a layout view.
+ * You can then use a view parameter (e.g. `$this->params['breadcrumbs']`) to configure the links in different
+ * views. In the layout view, you assign this view parameter to the [[links]] property like the following:
+ *
+ * ~~~
+ * $this->widget('yii\widgets\Breadcrumbs', array(
+ * 'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : array(),
+ * ));
+ * ~~~
+ *
+ * @author Qiang Xue
+ * @since 2.0
+ */
+class Breadcrumbs extends Widget
+{
+ /**
+ * @var string the name of the breadcrumb container tag.
+ */
+ public $tag = 'ul';
+ /**
+ * @var array the HTML attributes for the breadcrumb container tag.
+ */
+ public $options = array('class' => 'breadcrumb');
+ /**
+ * @var boolean whether to HTML-encode the link labels.
+ */
+ public $encodeLabels = true;
+ /**
+ * @var string the first hyperlink in the breadcrumbs (called home link).
+ * If this property is not set, it will default to a link pointing to [[\yii\web\Application::homeUrl]]
+ * with the label 'Home'. If this property is false, the home link will not be rendered.
+ */
+ public $homeLink;
+ /**
+ * @var array list of links to appear in the breadcrumbs. If this property is empty,
+ * the widget will not render anything. Each array element represents a single link in the breadcrumbs
+ * with the following structure:
+ *
+ * ~~~
+ * array(
+ * 'label' => 'label of the link', // required
+ * 'url' => 'url of the link', // optional, will be processed by Html::url()
+ * )
+ * ~~~
+ *
+ * If a link is active, you only need to specify its "label", and instead of writing `array('label' => $label)`,
+ * you should simply use `$label`.
+ */
+ public $links = array();
+ /**
+ * @var string the template used to render each inactive item in the breadcrumbs. The token `{link}`
+ * will be replaced with the actual HTML link for each inactive item.
+ */
+ public $itemTemplate = "{link} /\n";
+ /**
+ * @var string the template used to render each active item in the breadcrumbs. The token `{link}`
+ * will be replaced with the actual HTML link for each active item.
+ */
+ public $activeItemTemplate = "{link}\n";
+
+ /**
+ * Renders the widget.
+ */
+ public function run()
+ {
+ if (empty($this->links)) {
+ return;
+ }
+ $links = array();
+ if ($this->homeLink === null) {
+ $links[] = $this->renderItem(array(
+ 'label' => Yii::t('yii|Home'),
+ 'url' => Yii::$app->homeUrl,
+ ), $this->itemTemplate);
+ } elseif ($this->homeLink !== false) {
+ $links[] = $this->renderItem($this->homeLink, $this->itemTemplate);
+ }
+ foreach ($this->links as $link) {
+ if (!is_array($link)) {
+ $link = array('label' => $link);
+ }
+ $links[] = $this->renderItem($link, isset($link['url']) ? $this->itemTemplate : $this->activeItemTemplate);
+ }
+ echo Html::tag($this->tag, implode('', $links), $this->options);
+ }
+
+ /**
+ * Renders a single breadcrumb item.
+ * @param array $link the link to be rendered. It must contain the "label" element. The "url" element is optional.
+ * @param string $template the template to be used to rendered the link. The token "{link}" will be replaced by the link.
+ * @return string the rendering result
+ * @throws InvalidConfigException if `$link` does not have "label" element.
+ */
+ protected function renderItem($link, $template)
+ {
+ if (isset($link['label'])) {
+ $label = $this->encodeLabels ? Html::encode($link['label']) : $link['label'];
+ } else {
+ throw new InvalidConfigException('The "label" element is required for each link.');
+ }
+ if (isset($link['url'])) {
+ return strtr($template, array('{link}' => Html::a($label, $link['url'])));
+ } else {
+ return strtr($template, array('{link}' => $label));
+ }
+ }
+}
diff --git a/framework/widgets/Menu.php b/framework/widgets/Menu.php
new file mode 100644
index 0000000..3af620d
--- /dev/null
+++ b/framework/widgets/Menu.php
@@ -0,0 +1,282 @@
+
+ * @since 2.0
+ */
+class Menu extends Widget
+{
+ /**
+ * @var array list of menu items. Each menu item is specified as an array of name-value pairs.
+ * Possible option names include the following:
+ *
+ * - label: string, optional, specifies the menu item label. When {@link encodeLabel} is true, the label
+ * will be HTML-encoded. If the label is not specified, it defaults to an empty string.
+ * - url: string or array, optional, specifies the URL of the menu item. It is passed to {@link Html::normalizeUrl}
+ * to generate a valid URL. If this is not set, the menu item will be rendered as a span text.
+ * - visible: boolean, optional, whether this menu item is visible. Defaults to true.
+ * This can be used to control the visibility of menu items based on user permissions.
+ * - items: array, optional, specifies the sub-menu items. Its format is the same as the parent items.
+ * - active: boolean, optional, whether this menu item is in active state (currently selected).
+ * If a menu item is active and {@link activeClass} is not empty, its CSS class will be appended with {@link activeClass}.
+ * If this option is not set, the menu item will be set active automatically when the current request
+ * is triggered by {@link url}. Note that the GET parameters not specified in the 'url' option will be ignored.
+ * - template: string, optional, the template used to render this menu item.
+ * When this option is set, it will override the global setting {@link itemTemplate}.
+ * Please see {@link itemTemplate} for more details. This option has been available since version 1.1.1.
+ * - linkOptions: array, optional, additional HTML attributes to be rendered for the link or span tag of the menu item.
+ * - itemOptions: array, optional, additional HTML attributes to be rendered for the container tag of the menu item.
+ * - submenuOptions: array, optional, additional HTML attributes to be rendered for the container of the submenu if this menu item has one.
+ * When this option is set, the {@link submenuHtmlOptions} property will be ignored for this particular submenu.
+ * This option has been available since version 1.1.6.
+ *
+ */
+ public $items = array();
+ /**
+ * @var string the template used to render an individual menu item. In this template,
+ * the token "{menu}" will be replaced with the corresponding menu link or text.
+ * If this property is not set, each menu will be rendered without any decoration.
+ * This property will be overridden by the 'template' option set in individual menu items via {@items}.
+ * @since 1.1.1
+ */
+ public $itemTemplate;
+ /**
+ * @var boolean whether the labels for menu items should be HTML-encoded. Defaults to true.
+ */
+ public $encodeLabel = true;
+ /**
+ * @var string the CSS class to be appended to the active menu item. Defaults to 'active'.
+ * If empty, the CSS class of menu items will not be changed.
+ */
+ public $activeCssClass = 'active';
+ /**
+ * @var boolean whether to automatically activate items according to whether their route setting
+ * matches the currently requested route. Defaults to true.
+ * @since 1.1.3
+ */
+ public $activateItems = true;
+ /**
+ * @var boolean whether to activate parent menu items when one of the corresponding child menu items is active.
+ * The activated parent menu items will also have its CSS classes appended with {@link activeCssClass}.
+ * Defaults to false.
+ */
+ public $activateParents = false;
+ /**
+ * @var boolean whether to hide empty menu items. An empty menu item is one whose 'url' option is not
+ * set and which doesn't contain visible child menu items. Defaults to true.
+ */
+ public $hideEmptyItems = true;
+ /**
+ * @var array HTML attributes for the menu's root container tag
+ */
+ public $options = array();
+ /**
+ * @var array HTML attributes for the submenu's container tag.
+ */
+ public $submenuHtmlOptions = array();
+ /**
+ * @var string the HTML element name that will be used to wrap the label of all menu links.
+ * For example, if this property is set as 'span', a menu item may be rendered as
+ * <li><a href="url"><span>label</span></a></li>
+ * This is useful when implementing menu items using the sliding window technique.
+ * Defaults to null, meaning no wrapper tag will be generated.
+ * @since 1.1.4
+ */
+ public $linkLabelWrapper;
+ /**
+ * @var array HTML attributes for the links' wrap element specified in
+ * {@link linkLabelWrapper}.
+ * @since 1.1.13
+ */
+ public $linkLabelWrapperHtmlOptions = array();
+ /**
+ * @var string the CSS class that will be assigned to the first item in the main menu or each submenu.
+ * Defaults to null, meaning no such CSS class will be assigned.
+ * @since 1.1.4
+ */
+ public $firstItemCssClass;
+ /**
+ * @var string the CSS class that will be assigned to the last item in the main menu or each submenu.
+ * Defaults to null, meaning no such CSS class will be assigned.
+ * @since 1.1.4
+ */
+ public $lastItemCssClass;
+ /**
+ * @var string the CSS class that will be assigned to every item.
+ * Defaults to null, meaning no such CSS class will be assigned.
+ * @since 1.1.9
+ */
+ public $itemCssClass;
+
+ /**
+ * Initializes the menu widget.
+ * This method mainly normalizes the {@link items} property.
+ * If this method is overridden, make sure the parent implementation is invoked.
+ */
+ public function init()
+ {
+ $route = $this->getController()->getRoute();
+ $this->items = $this->normalizeItems($this->items, $route, $hasActiveChild);
+ }
+
+ /**
+ * Calls {@link renderMenu} to render the menu.
+ */
+ public function run()
+ {
+ if (count($this->items)) {
+ echo Html::beginTag('ul', $this->options) . "\n";
+ $this->renderItems($this->items);
+ echo Html::endTag('ul');
+ }
+ }
+
+ /**
+ * Recursively renders the menu items.
+ * @param array $items the menu items to be rendered recursively
+ */
+ protected function renderItems($items)
+ {
+ $count = 0;
+ $n = count($items);
+ foreach ($items as $item) {
+ $count++;
+ $options = isset($item['itemOptions']) ? $item['itemOptions'] : array();
+ $class = array();
+ if ($item['active'] && $this->activeCssClass != '') {
+ $class[] = $this->activeCssClass;
+ }
+ if ($count === 1 && $this->firstItemCssClass !== null) {
+ $class[] = $this->firstItemCssClass;
+ }
+ if ($count === $n && $this->lastItemCssClass !== null) {
+ $class[] = $this->lastItemCssClass;
+ }
+ if ($this->itemCssClass !== null) {
+ $class[] = $this->itemCssClass;
+ }
+ if ($class !== array()) {
+ if (empty($options['class'])) {
+ $options['class'] = implode(' ', $class);
+ } else {
+ $options['class'] .= ' ' . implode(' ', $class);
+ }
+ }
+
+ echo Html::beginTag('li', $options);
+
+ $menu = $this->renderItem($item);
+ if (isset($this->itemTemplate) || isset($item['template'])) {
+ $template = isset($item['template']) ? $item['template'] : $this->itemTemplate;
+ echo strtr($template, array('{menu}' => $menu));
+ } else {
+ echo $menu;
+ }
+
+ if (isset($item['items']) && count($item['items'])) {
+ echo "\n" . Html::beginTag('ul', isset($item['submenuOptions']) ? $item['submenuOptions'] : $this->submenuHtmlOptions) . "\n";
+ $this->renderItems($item['items']);
+ echo Html::endTag('ul') . "\n";
+ }
+
+ echo Html::endTag('li') . "\n";
+ }
+ }
+
+ /**
+ * Renders the content of a menu item.
+ * Note that the container and the sub-menus are not rendered here.
+ * @param array $item the menu item to be rendered. Please see {@link items} on what data might be in the item.
+ * @return string
+ * @since 1.1.6
+ */
+ protected function renderItem($item)
+ {
+ if (isset($item['url'])) {
+ $label = $this->linkLabelWrapper === null ? $item['label'] : Html::tag($this->linkLabelWrapper, $this->linkLabelWrapperHtmlOptions, $item['label']);
+ return Html::a($label, $item['url'], isset($item['linkOptions']) ? $item['linkOptions'] : array());
+ } else {
+ return Html::tag('span', isset($item['linkOptions']) ? $item['linkOptions'] : array(), $item['label']);
+ }
+ }
+
+ /**
+ * Normalizes the {@link items} property so that the 'active' state is properly identified for every menu item.
+ * @param array $items the items to be normalized.
+ * @param string $route the route of the current request.
+ * @param boolean $active whether there is an active child menu item.
+ * @return array the normalized menu items
+ */
+ protected function normalizeItems($items, $route, &$active)
+ {
+ foreach ($items as $i => $item) {
+ if (isset($item['visible']) && !$item['visible']) {
+ unset($items[$i]);
+ continue;
+ }
+ if (!isset($item['label'])) {
+ $item['label'] = '';
+ }
+ if ($this->encodeLabel) {
+ $items[$i]['label'] = Html::encode($item['label']);
+ }
+ $hasActiveChild = false;
+ if (isset($item['items'])) {
+ $items[$i]['items'] = $this->normalizeItems($item['items'], $route, $hasActiveChild);
+ if (empty($items[$i]['items']) && $this->hideEmptyItems) {
+ unset($items[$i]['items']);
+ if (!isset($item['url'])) {
+ unset($items[$i]);
+ continue;
+ }
+ }
+ }
+ if (!isset($item['active'])) {
+ if ($this->activateParents && $hasActiveChild || $this->activateItems && $this->isItemActive($item, $route)) {
+ $active = $items[$i]['active'] = true;
+ } else {
+ $items[$i]['active'] = false;
+ }
+ } elseif ($item['active']) {
+ $active = true;
+ }
+ }
+ return array_values($items);
+ }
+
+ /**
+ * Checks whether a menu item is active.
+ * This is done by checking if the currently requested URL is generated by the 'url' option
+ * of the menu item. Note that the GET parameters not specified in the 'url' option will be ignored.
+ * @param array $item the menu item to be checked
+ * @param string $route the route of the current request
+ * @return boolean whether the menu item is active
+ */
+ protected function isItemActive($item, $route)
+ {
+ if (isset($item['url']) && is_array($item['url']) && !strcasecmp(trim($item['url'][0], '/'), $route)) {
+ unset($item['url']['#']);
+ if (count($item['url']) > 1) {
+ foreach (array_splice($item['url'], 1) as $name => $value) {
+ if (!isset($_GET[$name]) || $_GET[$name] != $value) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+}
diff --git a/framework/yii.php b/framework/yii.php
index 828dc4f..109e2fd 100644
--- a/framework/yii.php
+++ b/framework/yii.php
@@ -18,7 +18,7 @@ require(__DIR__ . '/YiiBase.php');
* @author Qiang Xue
* @since 2.0
*/
-class Yii extends YiiBase
+class Yii extends \yii\YiiBase
{
}
diff --git a/readme.md b/readme.md
index f395e95..178acd4 100644
--- a/readme.md
+++ b/readme.md
@@ -27,10 +27,21 @@ REQUIREMENTS
The minimum requirement by Yii is that your Web server supports PHP 5.3.?.
+DOCUMENTATION
+-------------
+
+For 1.1 users, you may refer to [Upgrading from Yii 1.1](docs/guide/upgrade-from-v1.md)
+to have a general idea of what has changed in 2.0.
+
+We are writing more documentation to get you started and learn more in depth.
+
+
HOW TO PARTICIPATE
------------------
-You are welcome to participate in Yii 2 development in the following ways:
+**Your participation to Yii 2 development is very welcome!**
+
+You may participate in the following ways:
* [Report issues](https://github.com/yiisoft/yii2/issues)
* [Give us feedback or start a design discussion](http://www.yiiframework.com/forum/index.php/forum/42-design-discussions-for-yii-20/)
diff --git a/tests/unit/framework/helpers/JsonTest.php b/tests/unit/framework/helpers/JsonTest.php
index 6a78cd1..1795ce6 100644
--- a/tests/unit/framework/helpers/JsonTest.php
+++ b/tests/unit/framework/helpers/JsonTest.php
@@ -4,7 +4,7 @@
namespace yiiunit\framework\helpers;
use yii\helpers\Json;
-use yii\helpers\JsExpression;
+use yii\web\JsExpression;
class JsonTest extends \yii\test\TestCase
{