diff --git a/README.md b/README.md index 54bd499..9bd6480 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ DIRECTORY STRUCTURE ------------------- apps/ ready-to-use Web apps built on Yii 2 - bootstrap/ a simple app supporting user login and contact page + basic/ a simple app supporting user login and contact page build/ internally used build tools docs/ documentation yii/ framework source files diff --git a/apps/advanced/.gitignore b/apps/advanced/.gitignore new file mode 100644 index 0000000..b1cf719 --- /dev/null +++ b/apps/advanced/.gitignore @@ -0,0 +1 @@ +/yii \ No newline at end of file diff --git a/apps/bootstrap/LICENSE.md b/apps/advanced/LICENSE.md similarity index 100% rename from apps/bootstrap/LICENSE.md rename to apps/advanced/LICENSE.md diff --git a/apps/advanced/README.md b/apps/advanced/README.md new file mode 100644 index 0000000..a2bcdd4 --- /dev/null +++ b/apps/advanced/README.md @@ -0,0 +1,98 @@ +Yii 2 Advanced Application Template +=================================== + +**NOTE** Yii 2 and the relevant applications and extensions are still under heavy +development. We may make significant changes without prior notices. Please do not +use them for production. Please consider using [Yii v1.1](https://github.com/yiisoft/yii) +if you have a project to be deployed for production soon. + + +Thank you for using Yii 2 Advanced Application Template - an application template +that works out-of-box and can be easily customized to fit for your needs. + +Yii 2 Advanced Application Template is best suitable for large projects requiring frontend and backstage separation, +deployment in different environments, configuration nesting etc. + + +DIRECTORY STRUCTURE +------------------- + +``` +common + config/ contains shared configurations + models/ contains model classes used in both backstage and frontend +console + config/ contains console configurations + controllers/ contains console controllers (commands) + migrations/ contains database migrations + models/ contains console-specific model classes + runtime/ contains files generated during runtime +backstage + assets/ contains application assets such as JavaScript and CSS + config/ contains backstage configurations + controllers/ contains Web controller classes + models/ contains backstage-specific model classes + runtime/ contains files generated during runtime + views/ contains view files for the Web application + www/ contains the entry script and Web resources +frontend + assets/ contains application assets such as JavaScript and CSS + config/ contains frontend configurations + controllers/ contains Web controller classes + models/ contains frontend-specific model classes + runtime/ contains files generated during runtime + views/ contains view files for the Web application + www/ contains the entry script and Web resources +vendor/ contains dependent 3rd-party packages +environments/ contains environment-based overrides +``` + + + +REQUIREMENTS +------------ + +The minimum requirement by Yii is that your Web server supports PHP 5.3.?. + + +INSTALLATION +------------ + +### Install via Composer + +If you do not have [Composer](http://getcomposer.org/), you may download it from +[http://getcomposer.org/](http://getcomposer.org/) or run the following command on Linux/Unix/MacOS: + +~~~ +curl -s http://getcomposer.org/installer | php +~~~ + +You can then install the Bootstrap Application using the following command: + +~~~ +php composer.phar create-project --stability=dev yiisoft/yii2-app-advanced yii-advanced +~~~ + +Now you should be able to access: + +- the frontend using the URL `http://localhost/yii-advanced/frontend/www/` +- the backstage using the URL `http://localhost/yii-advanced/backstage/www/` + +assuming `yii-advanced` is directly under the document root of your Web server. + + +### Install from an Archive File + +This is not currently available. We will provide it when Yii 2 is formally released. + +GETTING STARTED +--------------- + +After template application and its dependencies are downloaded you need to initialize it and set some config values to +match your application requirements. + +1. Execute `install` command selecting `dev` as environment. +2. Set `id` value in `console/config/main.php`, `frontend/config/main.php`, `backstage/config/main.php`. +3. Create new database. It is assumed that MySQL InnoDB is used. If not, adjust `console/migrations/m130524_201442_init.php`. +4. In `common/config/params.php` set your database details in `components.db` values. + diff --git a/apps/bootstrap/assets/.gitignore b/apps/advanced/backstage/assets/.gitkeep similarity index 100% rename from apps/bootstrap/assets/.gitignore rename to apps/advanced/backstage/assets/.gitkeep diff --git a/apps/advanced/backstage/config/.gitignore b/apps/advanced/backstage/config/.gitignore new file mode 100644 index 0000000..20da318 --- /dev/null +++ b/apps/advanced/backstage/config/.gitignore @@ -0,0 +1,2 @@ +main-local.php +params-local.php \ No newline at end of file diff --git a/apps/bootstrap/config/assets.php b/apps/advanced/backstage/config/assets.php similarity index 100% rename from apps/bootstrap/config/assets.php rename to apps/advanced/backstage/config/assets.php diff --git a/apps/advanced/backstage/config/main.php b/apps/advanced/backstage/config/main.php new file mode 100644 index 0000000..4898bfd --- /dev/null +++ b/apps/advanced/backstage/config/main.php @@ -0,0 +1,40 @@ +<?php +$rootDir = __DIR__ . '/../..'; + +$params = array_merge( + require($rootDir . '/common/config/params.php'), + require($rootDir . '/common/config/params-local.php'), + require(__DIR__ . '/params.php'), + require(__DIR__ . '/params-local.php') +); + +return array( + 'id' => 'change-me', + 'basePath' => dirname(__DIR__), + 'vendorPath' => dirname(dirname(__DIR__)) . '/vendor', + 'preload' => array('log'), + 'controllerNamespace' => 'backstage\controllers', + 'modules' => array( + ), + 'components' => array( + 'db' => $params['components.db'], + 'cache' => $params['components.cache'], + 'user' => array( + 'class' => 'yii\web\User', + 'identityClass' => 'common\models\User', + ), + 'assetManager' => array( + 'bundles' => require(__DIR__ . '/assets.php'), + ), + 'log' => array( + 'class' => 'yii\logging\Router', + 'targets' => array( + array( + 'class' => 'yii\logging\FileTarget', + 'levels' => array('error', 'warning'), + ), + ), + ), + ), + 'params' => $params, +); diff --git a/apps/bootstrap/config/params.php b/apps/advanced/backstage/config/params.php similarity index 100% rename from apps/bootstrap/config/params.php rename to apps/advanced/backstage/config/params.php diff --git a/apps/advanced/backstage/controllers/SiteController.php b/apps/advanced/backstage/controllers/SiteController.php new file mode 100644 index 0000000..d40738a --- /dev/null +++ b/apps/advanced/backstage/controllers/SiteController.php @@ -0,0 +1,33 @@ +<?php + +namespace backstage\controllers; + +use Yii; +use yii\web\Controller; +use common\models\LoginForm; + +class SiteController extends Controller +{ + public function actionIndex() + { + echo $this->render('index'); + } + + public function actionLogin() + { + $model = new LoginForm(); + if ($this->populate($_POST, $model) && $model->login()) { + Yii::$app->response->redirect(array('site/index')); + } else { + echo $this->render('login', array( + 'model' => $model, + )); + } + } + + public function actionLogout() + { + Yii::$app->getUser()->logout(); + Yii::$app->getResponse()->redirect(array('site/index')); + } +} diff --git a/apps/bootstrap/runtime/.gitignore b/apps/advanced/backstage/models/.gitkeep similarity index 100% rename from apps/bootstrap/runtime/.gitignore rename to apps/advanced/backstage/models/.gitkeep diff --git a/apps/advanced/backstage/runtime/.gitignore b/apps/advanced/backstage/runtime/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/apps/advanced/backstage/runtime/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/apps/advanced/backstage/views/layouts/main.php b/apps/advanced/backstage/views/layouts/main.php new file mode 100644 index 0000000..44117f4 --- /dev/null +++ b/apps/advanced/backstage/views/layouts/main.php @@ -0,0 +1,64 @@ +<?php +use yii\helpers\Html; +use yii\widgets\Menu; +use yii\widgets\Breadcrumbs; +use yii\debug\Toolbar; + +/** + * @var $this \yii\base\View + * @var $content string + */ +$this->registerAssetBundle('app'); +?> +<?php $this->beginPage(); ?> +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="utf-8"/> + <title><?php echo Html::encode($this->title); ?></title> + <?php $this->head(); ?> +</head> +<body> +<div class="container"> + <?php $this->beginBody(); ?> + <div class="masthead"> + <h3 class="muted">My Company</h3> + + <div class="navbar"> + <div class="navbar-inner"> + <div class="container"> + <?php echo Menu::widget(array( + 'options' => array('class' => 'nav'), + 'items' => array( + array('label' => 'Home', 'url' => array('/site/index')), + Yii::$app->user->isGuest ? + array('label' => 'Login', 'url' => array('/site/login')) : + array('label' => 'Logout (' . Yii::$app->user->identity->username .')' , 'url' => array('/site/logout')), + ), + )); ?> + </div> + </div> + </div> + <!-- /.navbar --> + </div> + + <?php echo Breadcrumbs::widget(array( + 'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : array(), + )); ?> + <?php echo $content; ?> + + <hr> + + <div class="footer"> + <p>© My Company <?php echo date('Y'); ?></p> + <p> + <?php echo Yii::powered(); ?> + Template by <a href="http://twitter.github.io/bootstrap/">Twitter Bootstrap</a> + </p> + </div> + <?php $this->endBody(); ?> +</div> +<?php echo Toolbar::widget(); ?> +</body> +</html> +<?php $this->endPage(); ?> diff --git a/apps/bootstrap/views/site/index.php b/apps/advanced/backstage/views/site/index.php similarity index 100% rename from apps/bootstrap/views/site/index.php rename to apps/advanced/backstage/views/site/index.php diff --git a/apps/bootstrap/views/site/login.php b/apps/advanced/backstage/views/site/login.php similarity index 100% rename from apps/bootstrap/views/site/login.php rename to apps/advanced/backstage/views/site/login.php diff --git a/apps/advanced/backstage/www/.gitignore b/apps/advanced/backstage/www/.gitignore new file mode 100644 index 0000000..148f2b0 --- /dev/null +++ b/apps/advanced/backstage/www/.gitignore @@ -0,0 +1 @@ +/index.php \ No newline at end of file diff --git a/apps/advanced/backstage/www/assets/.gitignore b/apps/advanced/backstage/www/assets/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/apps/advanced/backstage/www/assets/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/apps/bootstrap/www/css/site.css b/apps/advanced/backstage/www/css/site.css similarity index 100% rename from apps/bootstrap/www/css/site.css rename to apps/advanced/backstage/www/css/site.css diff --git a/apps/advanced/common/config/.gitignore b/apps/advanced/common/config/.gitignore new file mode 100644 index 0000000..46f6eb4 --- /dev/null +++ b/apps/advanced/common/config/.gitignore @@ -0,0 +1 @@ +params-local.php \ No newline at end of file diff --git a/apps/advanced/common/config/params.php b/apps/advanced/common/config/params.php new file mode 100644 index 0000000..b9409f9 --- /dev/null +++ b/apps/advanced/common/config/params.php @@ -0,0 +1,16 @@ +<?php + +return array( + 'adminEmail' => 'admin@example.com', + + 'components.cache' => array( + 'class' => 'yii\caching\FileCache', + ), + + 'components.db' => array( + 'class' => 'yii\db\Connection', + 'dsn' => 'mysql:host=localhost;dbname=yii2advanced', + 'username' => 'root', + 'password' => '', + ), +); \ No newline at end of file diff --git a/apps/advanced/common/models/LoginForm.php b/apps/advanced/common/models/LoginForm.php new file mode 100644 index 0000000..4631dbd --- /dev/null +++ b/apps/advanced/common/models/LoginForm.php @@ -0,0 +1,58 @@ +<?php + +namespace common\models; + +use Yii; +use yii\base\Model; + +/** + * LoginForm is the model behind the login form. + */ +class LoginForm extends Model +{ + public $username; + public $password; + public $rememberMe = true; + + /** + * @return array the validation rules. + */ + public function rules() + { + return array( + // username and password are both required + array('username, password', 'required'), + // password is validated by validatePassword() + array('password', 'validatePassword'), + // rememberMe must be a boolean value + array('rememberMe', 'boolean'), + ); + } + + /** + * Validates the password. + * This method serves as the inline validation for password. + */ + public function validatePassword() + { + $user = User::findByUsername($this->username); + if (!$user || !$user->validatePassword($this->password)) { + $this->addError('password', 'Incorrect username or password.'); + } + } + + /** + * Logs in a user using the provided username and password. + * @return boolean whether the user is logged in successfully + */ + public function login() + { + if ($this->validate()) { + $user = User::findByUsername($this->username); + Yii::$app->user->login($user, $this->rememberMe ? 3600*24*30 : 0); + return true; + } else { + return false; + } + } +} diff --git a/apps/advanced/common/models/User.php b/apps/advanced/common/models/User.php new file mode 100644 index 0000000..7830718 --- /dev/null +++ b/apps/advanced/common/models/User.php @@ -0,0 +1,114 @@ +<?php +namespace common\models; + +use yii\db\ActiveRecord; +use yii\helpers\SecurityHelper; +use yii\web\Identity; + +/** + * Class User + * @package common\models + * + * @property integer $id + * @property string $username + * @property string $password_hash + * @property string $email + * @property string $auth_key + * @property integer $role + * @property integer $status + * @property integer $create_time + * @property integer $update_time + */ +class User extends ActiveRecord implements Identity +{ + /** + * @var string the raw password. Used to collect password input and isn't saved in database + */ + public $password; + + const STATUS_DELETED = 0; + const STATUS_ACTIVE = 10; + + const ROLE_USER = 10; + + public function behaviors() + { + return array( + 'timestamp' => array( + 'class' => 'yii\behaviors\AutoTimestamp', + 'attributes' => array( + ActiveRecord::EVENT_BEFORE_INSERT => array('create_time', 'update_time'), + ActiveRecord::EVENT_BEFORE_UPDATE => 'update_time', + ), + ), + ); + } + + public static function findIdentity($id) + { + return static::find($id); + } + + public static function findByUsername($username) + { + return static::find(array('username' => $username, 'status' => static::STATUS_ACTIVE)); + } + + public function getId() + { + return $this->id; + } + + public function getAuthKey() + { + return $this->auth_key; + } + + public function validateAuthKey($authKey) + { + return $this->auth_key === $authKey; + } + + public function validatePassword($password) + { + return SecurityHelper::validatePassword($password, $this->password_hash); + } + + public function rules() + { + return array( + array('username', 'filter', 'filter' => 'trim'), + array('username', 'required'), + array('username', 'length', 'min' => 2, 'max' => 255), + + array('email', 'filter', 'filter' => 'trim'), + array('email', 'required'), + array('email', 'email'), + array('email', 'unique', 'message' => 'This email address has already been taken.'), + + array('password', 'required'), + array('password', 'length', 'min' => 6), + ); + } + + public function scenarios() + { + return array( + 'signup' => array('username', 'email', 'password'), + 'login' => array('username', 'password'), + ); + } + + public function beforeSave($insert) + { + if(parent::beforeSave($insert)) { + if($this->isNewRecord) { + if(!empty($this->password)) { + $this->password_hash = SecurityHelper::generatePasswordHash($this->password); + } + } + return true; + } + return false; + } +} diff --git a/apps/advanced/composer.json b/apps/advanced/composer.json new file mode 100644 index 0000000..db97efd --- /dev/null +++ b/apps/advanced/composer.json @@ -0,0 +1,44 @@ +{ + "name": "yiisoft/yii2-app-advanced", + "description": "Yii 2 Advanced Application Template", + "keywords": ["yii", "framework", "advanced", "application template"], + "homepage": "http://www.yiiframework.com/", + "type": "project", + "license": "BSD-3-Clause", + "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" + }, + "minimum-stability": "dev", + "require": { + "php": ">=5.3.0", + "yiisoft/yii2": "dev-master", + "yiisoft/yii2-composer": "dev-master" + }, + "scripts": { + "post-install-cmd": [ + "yii\\composer\\InstallHandler::setPermissions" + ], + "post-update-cmd": [ + "yii\\composer\\InstallHandler::setPermissions" + ] + }, + "extra": { + "yii-install-writable": [ + "backstage/runtime", + "backstage/www/assets", + + "console/runtime", + "console/migrations", + + "frontend/runtime", + "frontend/www/assets" + ], + "yii-install-executable": [ + "yii" + ] + } +} diff --git a/apps/advanced/composer.lock b/apps/advanced/composer.lock new file mode 100644 index 0000000..761ae2f --- /dev/null +++ b/apps/advanced/composer.lock @@ -0,0 +1,164 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" + ], + "hash": "0b96a35ac23eae4e84ffd588653e88d2", + "packages": [ + { + "name": "yiisoft/yii2", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/yiisoft/yii2-framework.git", + "reference": "15a8d0559260e39954a8eb6de0d28bfb7de95e7b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/15a8d0559260e39954a8eb6de0d28bfb7de95e7b", + "reference": "15a8d0559260e39954a8eb6de0d28bfb7de95e7b", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "lib-pcre": "*", + "php": ">=5.3.7" + }, + "suggest": { + "ezyang/htmlpurifier": "Required by HtmlPurifier.", + "michelf/php-markdown": "Required by Markdown.", + "smarty/smarty": "Required by SmartyViewRenderer.", + "twig/twig": "Required by TwigViewRenderer." + }, + "type": "library", + "autoload": { + "psr-0": { + "yii\\": "/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "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": "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" + }, + { + "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" + } + ], + "description": "Yii2 Web Programming Framework", + "homepage": "http://www.yiiframework.com/", + "keywords": [ + "framework", + "yii" + ], + "time": "2013-05-25 20:59:05" + }, + { + "name": "yiisoft/yii2-composer", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/yiisoft/yii2-composer.git", + "reference": "7ce4060faca940b836ab88de207638940a0a0568" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/yiisoft/yii2-composer/zipball/7ce4060faca940b836ab88de207638940a0a0568", + "reference": "7ce4060faca940b836ab88de207638940a0a0568", + "shasum": "" + }, + "require": { + "yiisoft/yii2": "*" + }, + "type": "library", + "autoload": { + "psr-0": { + "yii\\composer": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Qiang Xue", + "email": "qiang.xue@gmail.com", + "homepage": "http://www.yiiframework.com/", + "role": "Founder and project lead" + } + ], + "description": "The composer integration for the Yii framework", + "keywords": [ + "composer", + "install", + "update", + "yii" + ], + "time": "2013-05-23 19:12:45" + } + ], + "packages-dev": [ + + ], + "aliases": [ + + ], + "minimum-stability": "dev", + "stability-flags": { + "yiisoft/yii2": 20, + "yiisoft/yii2-composer": 20 + }, + "platform": { + "php": ">=5.3.0" + }, + "platform-dev": [ + + ] +} diff --git a/apps/advanced/console/config/.gitignore b/apps/advanced/console/config/.gitignore new file mode 100644 index 0000000..20da318 --- /dev/null +++ b/apps/advanced/console/config/.gitignore @@ -0,0 +1,2 @@ +main-local.php +params-local.php \ No newline at end of file diff --git a/apps/advanced/console/config/main.php b/apps/advanced/console/config/main.php new file mode 100644 index 0000000..cceb311 --- /dev/null +++ b/apps/advanced/console/config/main.php @@ -0,0 +1,33 @@ +<?php +$rootDir = __DIR__ . '/../..'; + +$params = array_merge( + require($rootDir . '/common/config/params.php'), + require($rootDir . '/common/config/params-local.php'), + require(__DIR__ . '/params.php'), + require(__DIR__ . '/params-local.php') +); + +return array( + 'id' => 'change-me', + 'basePath' => dirname(__DIR__), + 'vendorPath' => dirname(dirname(__DIR__)) . '/vendor', + 'preload' => array('log'), + 'controllerNamespace' => 'console\controllers', + 'modules' => array( + ), + 'components' => array( + 'db' => $params['components.db'], + 'cache' => $params['components.cache'], + 'log' => array( + 'class' => 'yii\logging\Router', + 'targets' => array( + array( + 'class' => 'yii\logging\FileTarget', + 'levels' => array('error', 'warning'), + ), + ), + ), + ), + 'params' => $params, +); diff --git a/apps/advanced/console/config/params.php b/apps/advanced/console/config/params.php new file mode 100644 index 0000000..1e197d0 --- /dev/null +++ b/apps/advanced/console/config/params.php @@ -0,0 +1,5 @@ +<?php + +return array( + 'adminEmail' => 'admin@example.com', +); \ No newline at end of file diff --git a/apps/advanced/console/controllers/.gitkeep b/apps/advanced/console/controllers/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/apps/advanced/console/controllers/.gitkeep diff --git a/apps/advanced/console/migrations/m130524_201442_init.php b/apps/advanced/console/migrations/m130524_201442_init.php new file mode 100644 index 0000000..24a74c3 --- /dev/null +++ b/apps/advanced/console/migrations/m130524_201442_init.php @@ -0,0 +1,27 @@ +<?php + +class m130524_201442_init extends \yii\db\Migration +{ + public function up() + { + // MySQL-specific table options. Adjust if you plan working with another DBMS + $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE=InnoDB'; + + $this->createTable('tbl_user', array( + 'id' => Schema::TYPE_PK, + 'username' => Schema::TYPE_STRING.' NOT NULL', + 'password_hash' => Schema::TYPE_STRING.' NOT NULL', + 'email' => Schema::TYPE_STRING.' NOT NULL', + 'role' => 'tinyint NOT NULL DEFAULT 10', + + 'status' => 'tinyint NOT NULL DEFAULT 10', + 'create_time' => Schema::TYPE_INTEGER.' NOT NULL', + 'update_time' => Schema::TYPE_INTEGER.' NOT NULL', + ), $tableOptions); + } + + public function down() + { + $this->dropTable('tbl_user'); + } +} diff --git a/apps/bootstrap/vendor/.gitignore b/apps/advanced/console/models/.gitkeep similarity index 100% rename from apps/bootstrap/vendor/.gitignore rename to apps/advanced/console/models/.gitkeep diff --git a/apps/advanced/console/runtime/.gitignore b/apps/advanced/console/runtime/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/apps/advanced/console/runtime/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/apps/advanced/environments/dev/backstage/config/main-local.php b/apps/advanced/environments/dev/backstage/config/main-local.php new file mode 100644 index 0000000..f74bfa3 --- /dev/null +++ b/apps/advanced/environments/dev/backstage/config/main-local.php @@ -0,0 +1,17 @@ +<?php +return array( + 'modules' => array( +// 'debug' => array( +// 'class' => 'yii\debug\Module', +// ), + ), + 'components' => array( + 'log' => array( + 'targets' => array( +// array( +// 'class' => 'yii\logging\DebugTarget', +// ) + ), + ), + ), +); diff --git a/apps/advanced/environments/dev/backstage/config/params-local.php b/apps/advanced/environments/dev/backstage/config/params-local.php new file mode 100644 index 0000000..2670143 --- /dev/null +++ b/apps/advanced/environments/dev/backstage/config/params-local.php @@ -0,0 +1,3 @@ +<?php +return array( +); \ No newline at end of file diff --git a/apps/advanced/environments/dev/backstage/www/index.php b/apps/advanced/environments/dev/backstage/www/index.php new file mode 100644 index 0000000..10a118b --- /dev/null +++ b/apps/advanced/environments/dev/backstage/www/index.php @@ -0,0 +1,14 @@ +<?php +// comment out the following line to disable debug mode +defined('YII_DEBUG') or define('YII_DEBUG', true); + +require(__DIR__ . '/../../vendor/yiisoft/yii2/yii/Yii.php'); +require(__DIR__ . '/../../vendor/autoload.php'); + +$config = yii\helpers\ArrayHelper::merge( + require(__DIR__ . '/../config/main.php'), + require(__DIR__ . '/../config/main-local.php') +); + +$application = new yii\web\Application($config); +$application->run(); diff --git a/apps/advanced/environments/dev/common/config/params-local.php b/apps/advanced/environments/dev/common/config/params-local.php new file mode 100644 index 0000000..2670143 --- /dev/null +++ b/apps/advanced/environments/dev/common/config/params-local.php @@ -0,0 +1,3 @@ +<?php +return array( +); \ No newline at end of file diff --git a/apps/advanced/environments/dev/console/config/main-local.php b/apps/advanced/environments/dev/console/config/main-local.php new file mode 100644 index 0000000..5b61b0e --- /dev/null +++ b/apps/advanced/environments/dev/console/config/main-local.php @@ -0,0 +1,3 @@ +<?php +return array( +); diff --git a/apps/advanced/environments/dev/console/config/params-local.php b/apps/advanced/environments/dev/console/config/params-local.php new file mode 100644 index 0000000..2670143 --- /dev/null +++ b/apps/advanced/environments/dev/console/config/params-local.php @@ -0,0 +1,3 @@ +<?php +return array( +); \ No newline at end of file diff --git a/apps/advanced/environments/dev/frontend/config/main-local.php b/apps/advanced/environments/dev/frontend/config/main-local.php new file mode 100644 index 0000000..b77abed --- /dev/null +++ b/apps/advanced/environments/dev/frontend/config/main-local.php @@ -0,0 +1,17 @@ +<?php +return array( + 'modules' => array( +// 'debug' => array( +// 'class' => 'yii\debug\Module', +// ), + ), + 'components' => array( + 'log' => array( + 'targets' => array( +// array( +// 'class' => 'yii\logging\DebugTarget', +// ) + ), + ), + ), +); diff --git a/apps/advanced/environments/dev/frontend/config/params-local.php b/apps/advanced/environments/dev/frontend/config/params-local.php new file mode 100644 index 0000000..2670143 --- /dev/null +++ b/apps/advanced/environments/dev/frontend/config/params-local.php @@ -0,0 +1,3 @@ +<?php +return array( +); \ No newline at end of file diff --git a/apps/advanced/environments/dev/frontend/www/index.php b/apps/advanced/environments/dev/frontend/www/index.php new file mode 100644 index 0000000..9a2ffaa --- /dev/null +++ b/apps/advanced/environments/dev/frontend/www/index.php @@ -0,0 +1,15 @@ +<?php + +// comment out the following line to disable debug mode +defined('YII_DEBUG') or define('YII_DEBUG', true); + +require(__DIR__ . '/../../vendor/yiisoft/yii2/yii/Yii.php'); +require(__DIR__ . '/../../vendor/autoload.php'); + +$config = yii\helpers\ArrayHelper::merge( + require(__DIR__ . '/../config/main.php'), + require(__DIR__ . '/../config/main-local.php') +); + +$application = new yii\web\Application($config); +$application->run(); diff --git a/apps/advanced/environments/dev/yii b/apps/advanced/environments/dev/yii new file mode 100644 index 0000000..d763217 --- /dev/null +++ b/apps/advanced/environments/dev/yii @@ -0,0 +1,25 @@ +#!/usr/bin/env php +<?php +/** + * Yii console bootstrap file. + * + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +defined('YII_DEBUG') or define('YII_DEBUG', true); + +// fcgi doesn't have STDIN defined by default +defined('STDIN') or define('STDIN', fopen('php://stdin', 'r')); + +require(__DIR__ . '/vendor/yiisoft/yii2/yii/Yii.php'); +require(__DIR__ . '/vendor/autoload.php'); + +$config = yii\helpers\ArrayHelper::merge( + require(__DIR__ . '/console/config/main.php'), + require(__DIR__ . '/console/config/main-local.php') +); + +$application = new yii\console\Application($config); +$application->run(); diff --git a/apps/advanced/environments/index.php b/apps/advanced/environments/index.php new file mode 100644 index 0000000..ff907d2 --- /dev/null +++ b/apps/advanced/environments/index.php @@ -0,0 +1,38 @@ +<?php +/** + * The manifest of files that are local to specific environment. + * This file returns a list of environments that the application + * may be installed under. The returned data must be in the following + * format: + * + * ```php + * return array( + * 'environment name' => array( + * 'path' => 'directory storing the local files', + * 'writable' => array( + * // list of directories that should be set writable + * ), + * ), + * ); + * ``` + */ +return array( + 'Development' => array( + 'path' => 'dev', + 'writable' => array( + // handled by composer.json already + ), + 'executable' => array( + 'yiic', + ), + ), + 'Production' => array( + 'path' => 'prod', + 'writable' => array( + // handled by composer.json already + ), + 'executable' => array( + 'yiic', + ), + ), +); diff --git a/apps/advanced/environments/prod/backstage/config/main-local.php b/apps/advanced/environments/prod/backstage/config/main-local.php new file mode 100644 index 0000000..5b61b0e --- /dev/null +++ b/apps/advanced/environments/prod/backstage/config/main-local.php @@ -0,0 +1,3 @@ +<?php +return array( +); diff --git a/apps/advanced/environments/prod/backstage/config/params-local.php b/apps/advanced/environments/prod/backstage/config/params-local.php new file mode 100644 index 0000000..2670143 --- /dev/null +++ b/apps/advanced/environments/prod/backstage/config/params-local.php @@ -0,0 +1,3 @@ +<?php +return array( +); \ No newline at end of file diff --git a/apps/advanced/environments/prod/backstage/www/index.php b/apps/advanced/environments/prod/backstage/www/index.php new file mode 100644 index 0000000..1bff166 --- /dev/null +++ b/apps/advanced/environments/prod/backstage/www/index.php @@ -0,0 +1,14 @@ +<?php +// comment out the following line to disable debug mode +defined('YII_DEBUG') or define('YII_DEBUG', false); + +require(__DIR__ . '/../../vendor/yiisoft/yii2/yii/Yii.php'); +require(__DIR__ . '/../../vendor/autoload.php'); + +$config = yii\helpers\ArrayHelper::merge( + require(__DIR__ . '/../config/main.php'), + require(__DIR__ . '/../config/main-local.php') +); + +$application = new yii\web\Application($config); +$application->run(); diff --git a/apps/advanced/environments/prod/common/config/params-local.php b/apps/advanced/environments/prod/common/config/params-local.php new file mode 100644 index 0000000..2670143 --- /dev/null +++ b/apps/advanced/environments/prod/common/config/params-local.php @@ -0,0 +1,3 @@ +<?php +return array( +); \ No newline at end of file diff --git a/apps/advanced/environments/prod/console/config/main-local.php b/apps/advanced/environments/prod/console/config/main-local.php new file mode 100644 index 0000000..5b61b0e --- /dev/null +++ b/apps/advanced/environments/prod/console/config/main-local.php @@ -0,0 +1,3 @@ +<?php +return array( +); diff --git a/apps/advanced/environments/prod/console/config/params-local.php b/apps/advanced/environments/prod/console/config/params-local.php new file mode 100644 index 0000000..2670143 --- /dev/null +++ b/apps/advanced/environments/prod/console/config/params-local.php @@ -0,0 +1,3 @@ +<?php +return array( +); \ No newline at end of file diff --git a/apps/advanced/environments/prod/frontend/config/main-local.php b/apps/advanced/environments/prod/frontend/config/main-local.php new file mode 100644 index 0000000..5b61b0e --- /dev/null +++ b/apps/advanced/environments/prod/frontend/config/main-local.php @@ -0,0 +1,3 @@ +<?php +return array( +); diff --git a/apps/advanced/environments/prod/frontend/config/params-local.php b/apps/advanced/environments/prod/frontend/config/params-local.php new file mode 100644 index 0000000..2670143 --- /dev/null +++ b/apps/advanced/environments/prod/frontend/config/params-local.php @@ -0,0 +1,3 @@ +<?php +return array( +); \ No newline at end of file diff --git a/apps/advanced/environments/prod/frontend/www/index.php b/apps/advanced/environments/prod/frontend/www/index.php new file mode 100644 index 0000000..e797bf0 --- /dev/null +++ b/apps/advanced/environments/prod/frontend/www/index.php @@ -0,0 +1,15 @@ +<?php + +// comment out the following line to disable debug mode +defined('YII_DEBUG') or define('YII_DEBUG', false); + +require(__DIR__ . '/../../vendor/yiisoft/yii2/yii/Yii.php'); +require(__DIR__ . '/../../vendor/autoload.php'); + +$config = yii\helpers\ArrayHelper::merge( + require(__DIR__ . '/../config/main.php'), + require(__DIR__ . '/../config/main-local.php') +); + +$application = new yii\web\Application($config); +$application->run(); diff --git a/apps/advanced/environments/prod/yii b/apps/advanced/environments/prod/yii new file mode 100644 index 0000000..395aede --- /dev/null +++ b/apps/advanced/environments/prod/yii @@ -0,0 +1,25 @@ +#!/usr/bin/env php +<?php +/** + * Yii console bootstrap file. + * + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +defined('YII_DEBUG') or define('YII_DEBUG', false); + +// fcgi doesn't have STDIN defined by default +defined('STDIN') or define('STDIN', fopen('php://stdin', 'r')); + +require(__DIR__ . '/vendor/yiisoft/yii2/yii/Yii.php'); +require(__DIR__ . '/vendor/autoload.php'); + +$config = yii\helpers\ArrayHelper::merge( + require(__DIR__ . '/console/config/main.php'), + require(__DIR__ . '/console/config/main-local.php') +); + +$application = new yii\console\Application($config); +$application->run(); diff --git a/apps/advanced/frontend/assets/.gitkeep b/apps/advanced/frontend/assets/.gitkeep new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/apps/advanced/frontend/assets/.gitkeep @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/apps/advanced/frontend/config/.gitignore b/apps/advanced/frontend/config/.gitignore new file mode 100644 index 0000000..20da318 --- /dev/null +++ b/apps/advanced/frontend/config/.gitignore @@ -0,0 +1,2 @@ +main-local.php +params-local.php \ No newline at end of file diff --git a/apps/advanced/frontend/config/assets.php b/apps/advanced/frontend/config/assets.php new file mode 100644 index 0000000..ee0d610 --- /dev/null +++ b/apps/advanced/frontend/config/assets.php @@ -0,0 +1,18 @@ +<?php + +return array( + 'app' => array( + 'basePath' => '@wwwroot', + 'baseUrl' => '@www', + 'css' => array( + 'css/site.css', + ), + 'js' => array( + + ), + 'depends' => array( + 'yii', + 'yii/bootstrap/responsive', + ), + ), +); diff --git a/apps/advanced/frontend/config/main.php b/apps/advanced/frontend/config/main.php new file mode 100644 index 0000000..02a66c9 --- /dev/null +++ b/apps/advanced/frontend/config/main.php @@ -0,0 +1,40 @@ +<?php +$rootDir = __DIR__ . '/../..'; + +$params = array_merge( + require($rootDir . '/common/config/params.php'), + require($rootDir . '/common/config/params-local.php'), + require(__DIR__ . '/params.php'), + require(__DIR__ . '/params-local.php') +); + +return array( + 'id' => 'change-me', + 'basePath' => dirname(__DIR__), + 'vendorPath' => dirname(dirname(__DIR__)) . '/vendor', + 'preload' => array('log'), + 'controllerNamespace' => 'frontend\controllers', + 'modules' => array( + ), + 'components' => array( + 'db' => $params['components.db'], + 'cache' => $params['components.cache'], + 'user' => array( + 'class' => 'yii\web\User', + 'identityClass' => 'common\models\User', + ), + 'assetManager' => array( + 'bundles' => require(__DIR__ . '/assets.php'), + ), + 'log' => array( + 'class' => 'yii\logging\Router', + 'targets' => array( + array( + 'class' => 'yii\logging\FileTarget', + 'levels' => array('error', 'warning'), + ), + ), + ), + ), + 'params' => $params, +); diff --git a/apps/advanced/frontend/config/params.php b/apps/advanced/frontend/config/params.php new file mode 100644 index 0000000..1e197d0 --- /dev/null +++ b/apps/advanced/frontend/config/params.php @@ -0,0 +1,5 @@ +<?php + +return array( + 'adminEmail' => 'admin@example.com', +); \ No newline at end of file diff --git a/apps/advanced/frontend/controllers/SiteController.php b/apps/advanced/frontend/controllers/SiteController.php new file mode 100644 index 0000000..cd3339c --- /dev/null +++ b/apps/advanced/frontend/controllers/SiteController.php @@ -0,0 +1,61 @@ +<?php + +namespace frontend\controllers; + +use Yii; +use yii\web\Controller; +use common\models\LoginForm; +use frontend\models\ContactForm; + +class SiteController extends Controller +{ + public function actions() + { + return array( + 'captcha' => array( + 'class' => 'yii\web\CaptchaAction', + ), + ); + } + + public function actionIndex() + { + echo $this->render('index'); + } + + public function actionLogin() + { + $model = new LoginForm(); + if ($this->populate($_POST, $model) && $model->login()) { + Yii::$app->response->redirect(array('site/index')); + } else { + echo $this->render('login', array( + 'model' => $model, + )); + } + } + + public function actionLogout() + { + Yii::$app->getUser()->logout(); + Yii::$app->getResponse()->redirect(array('site/index')); + } + + public function actionContact() + { + $model = new ContactForm; + if ($this->populate($_POST, $model) && $model->contact(Yii::$app->params['adminEmail'])) { + Yii::$app->session->setFlash('contactFormSubmitted'); + Yii::$app->response->refresh(); + } else { + echo $this->render('contact', array( + 'model' => $model, + )); + } + } + + public function actionAbout() + { + echo $this->render('about'); + } +} diff --git a/apps/advanced/frontend/models/ContactForm.php b/apps/advanced/frontend/models/ContactForm.php new file mode 100644 index 0000000..b3d8682 --- /dev/null +++ b/apps/advanced/frontend/models/ContactForm.php @@ -0,0 +1,63 @@ +<?php + +namespace frontend\models; + +use yii\base\Model; + +/** + * ContactForm is the model behind the contact form. + */ +class ContactForm extends Model +{ + public $name; + public $email; + public $subject; + public $body; + public $verifyCode; + + /** + * @return array the validation rules. + */ + public function rules() + { + return array( + // name, email, subject and body are required + array('name, email, subject, body', 'required'), + // email has to be a valid email address + array('email', 'email'), + // verifyCode needs to be entered correctly + array('verifyCode', 'captcha'), + ); + } + + /** + * @return array customized attribute labels + */ + public function attributeLabels() + { + return array( + 'verifyCode' => 'Verification Code', + ); + } + + /** + * Sends an email to the specified email address using the information collected by this model. + * @param string $email the target email address + * @return boolean whether the model passes validation + */ + public function contact($email) + { + if ($this->validate()) { + $name = '=?UTF-8?B?' . base64_encode($this->name) . '?='; + $subject = '=?UTF-8?B?' . base64_encode($this->subject) . '?='; + $headers = "From: $name <{$this->email}>\r\n" . + "Reply-To: {$this->email}\r\n" . + "MIME-Version: 1.0\r\n" . + "Content-type: text/plain; charset=UTF-8"; + mail($email, $subject, $this->body, $headers); + return true; + } else { + return false; + } + } +} diff --git a/apps/advanced/frontend/runtime/.gitignore b/apps/advanced/frontend/runtime/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/apps/advanced/frontend/runtime/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/apps/bootstrap/views/layouts/main.php b/apps/advanced/frontend/views/layouts/main.php similarity index 100% rename from apps/bootstrap/views/layouts/main.php rename to apps/advanced/frontend/views/layouts/main.php diff --git a/apps/bootstrap/views/site/about.php b/apps/advanced/frontend/views/site/about.php similarity index 100% rename from apps/bootstrap/views/site/about.php rename to apps/advanced/frontend/views/site/about.php diff --git a/apps/bootstrap/views/site/contact.php b/apps/advanced/frontend/views/site/contact.php similarity index 100% rename from apps/bootstrap/views/site/contact.php rename to apps/advanced/frontend/views/site/contact.php diff --git a/apps/advanced/frontend/views/site/index.php b/apps/advanced/frontend/views/site/index.php new file mode 100644 index 0000000..158b61c --- /dev/null +++ b/apps/advanced/frontend/views/site/index.php @@ -0,0 +1,47 @@ +<?php +/** + * @var yii\base\View $this + */ +$this->title = 'Welcome'; +?> +<div class="jumbotron"> + <h1>Welcome!</h1> + + <p class="lead">Cras justo odio, dapibus ac facilisis in, egestas eget quam. Fusce dapibus, tellus ac cursus + commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.</p> + <a class="btn btn-large btn-success" href="http://www.yiiframework.com">Get started with Yii</a> +</div> + +<hr> + +<!-- Example row of columns --> +<div class="row-fluid"> + <div class="span4"> + <h2>Heading</h2> + + <p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris + condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. + Donec sed odio dui. </p> + + <p><a class="btn" href="#">View details »</a></p> + </div> + <div class="span4"> + <h2>Heading</h2> + + <p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris + condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. + Donec sed odio dui. </p> + + <p><a class="btn" href="#">View details »</a></p> + </div> + <div class="span4"> + <h2>Heading</h2> + + <p>Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta + felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum + massa.</p> + + <p><a class="btn" href="#">View details »</a></p> + </div> +</div> + diff --git a/apps/advanced/frontend/views/site/login.php b/apps/advanced/frontend/views/site/login.php new file mode 100644 index 0000000..f676b98 --- /dev/null +++ b/apps/advanced/frontend/views/site/login.php @@ -0,0 +1,24 @@ +<?php +use yii\helpers\Html; +use yii\widgets\ActiveForm; + +/** + * @var yii\base\View $this + * @var yii\widgets\ActiveForm $form + * @var app\models\LoginForm $model + */ +$this->title = 'Login'; +$this->params['breadcrumbs'][] = $this->title; +?> +<h1><?php echo Html::encode($this->title); ?></h1> + +<p>Please fill out the following fields to login:</p> + +<?php $form = ActiveForm::begin(array('options' => array('class' => 'form-horizontal'))); ?> + <?php echo $form->field($model, 'username')->textInput(); ?> + <?php echo $form->field($model, 'password')->passwordInput(); ?> + <?php echo $form->field($model, 'rememberMe')->checkbox(); ?> + <div class="form-actions"> + <?php echo Html::submitButton('Login', null, null, array('class' => 'btn btn-primary')); ?> + </div> +<?php ActiveForm::end(); ?> diff --git a/apps/advanced/frontend/www/.gitignore b/apps/advanced/frontend/www/.gitignore new file mode 100644 index 0000000..148f2b0 --- /dev/null +++ b/apps/advanced/frontend/www/.gitignore @@ -0,0 +1 @@ +/index.php \ No newline at end of file diff --git a/apps/advanced/frontend/www/assets/.gitignore b/apps/advanced/frontend/www/assets/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/apps/advanced/frontend/www/assets/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/apps/advanced/frontend/www/css/site.css b/apps/advanced/frontend/www/css/site.css new file mode 100644 index 0000000..890a953 --- /dev/null +++ b/apps/advanced/frontend/www/css/site.css @@ -0,0 +1,78 @@ +body { + padding-top: 20px; + padding-bottom: 60px; +} + +/* Custom container */ +.container { + margin: 0 auto; + max-width: 1000px; +} + +.container > hr { + margin: 60px 0; +} + +/* Main marketing message and sign up button */ +.jumbotron { + margin: 80px 0; + text-align: center; +} + +.jumbotron h1 { + font-size: 100px; + line-height: 1; +} + +.jumbotron .lead { + font-size: 24px; + line-height: 1.25; +} + +.jumbotron .btn { + font-size: 21px; + padding: 14px 24px; +} + +/* Supporting marketing content */ +.marketing { + margin: 60px 0; +} + +.marketing p + h4 { + margin-top: 28px; +} + +/* Customize the navbar links to be fill the entire space of the .navbar */ +.navbar .navbar-inner { + padding: 0; +} + +.navbar .nav { + margin: 0; + display: table; + width: 100%; +} + +.navbar .nav li { + display: table-cell; + width: 1%; + float: none; +} + +.navbar .nav li a { + font-weight: bold; + text-align: center; + border-left: 1px solid rgba(255, 255, 255, .75); + border-right: 1px solid rgba(0, 0, 0, .1); +} + +.navbar .nav li:first-child a { + border-left: 0; + border-radius: 3px 0 0 3px; +} + +.navbar .nav li:last-child a { + border-right: 0; + border-radius: 0 3px 3px 0; +} diff --git a/apps/advanced/install b/apps/advanced/install new file mode 100755 index 0000000..6864440 --- /dev/null +++ b/apps/advanced/install @@ -0,0 +1,112 @@ +#!/usr/bin/env php +<?php +$root = str_replace('\\', '/', __DIR__); +$envs = require("$root/environments/index.php"); +$envNames = array_keys($envs); + +echo "Yii Application Installation Tool v1.0\n\n"; +echo "Which environment do you want to install the application to?\n\n"; +foreach ($envNames as $i => $name) { + echo " [$i] $name\n"; +} +echo "\n Your choice [0-" . (count($envs) - 1) . ', or "q" to quit] '; +$answer = trim(fgets(STDIN)); +if (!ctype_digit($answer) || !isset($envNames[$answer])) { + echo "\n Quit installation.\n"; + return; +} + +$env = $envs[$envNames[$answer]]; +echo "\n Install the application under '{$envNames[$answer]}' environment? [yes|no] "; +$answer = trim(fgets(STDIN)); +if (strncasecmp($answer, 'y', 1)) { + echo "\n Quit installation.\n"; + return; +} + +echo "\n Start installation ...\n\n"; +$files = getFileList("$root/environments/{$env['path']}"); +$all = false; +foreach ($files as $file) { + if (!copyFile($root, "environments/{$env['path']}/$file", $file, $all)) { + break; + } +} + +if (isset($env['writable'])) { + foreach ($env['writable'] as $writable) { + echo " chmod 0777 $writable\n"; + @chmod("$root/$writable", 0777); + } +} + +if (isset($env['executable'])) { + foreach ($env['executable'] as $executable) { + echo " chmod 0755 $executable\n"; + @chmod("$root/$executable", 0755); + } +} + +echo "\n ... installation completed.\n\n"; + +function getFileList($root, $basePath = '') +{ + $files = array(); + $handle = opendir($root); + while (($path = readdir($handle)) !== false) { + if ($path === '.svn' || $path === '.' || $path === '..') { + continue; + } + $fullPath = "$root/$path"; + $relativePath = $basePath === '' ? $path : "$basePath/$path"; + if (is_dir($fullPath)) { + $files = array_merge($files, getFileList($fullPath, $relativePath)); + } else { + $files[] = $relativePath; + } + } + closedir($handle); + return $files; +} + +function copyFile($root, $source, $target, &$all) +{ + if (!is_file($root . '/' . $source)) { + echo " skip $target ($source not exist)\n"; + return true; + } + if (is_file($root . '/' . $target)) { + if (file_get_contents($root . '/' . $source) === file_get_contents($root . '/' . $target)) { + echo " unchanged $target\n"; + return true; + } + if ($all) { + echo " overwrite $target\n"; + } else { + echo " exist $target\n"; + echo " ...overwrite? [Yes|No|All|Quit] "; + $answer = trim(fgets(STDIN)); + if (!strncasecmp($answer, 'q', 1)) { + return false; + } else { + if (!strncasecmp($answer, 'y', 1)) { + echo " overwrite $target\n"; + } else { + if (!strncasecmp($answer, 'a', 1)) { + echo " overwrite $target\n"; + $all = true; + } else { + echo " skip $target\n"; + return true; + } + } + } + } + file_put_contents($root . '/' . $target, file_get_contents($root . '/' . $source)); + return true; + } + echo " generate $target\n"; + @mkdir(dirname($root . '/' . $target), 0777, true); + file_put_contents($root . '/' . $target, file_get_contents($root . '/' . $source)); + return true; +} diff --git a/apps/advanced/install.bat b/apps/advanced/install.bat new file mode 100644 index 0000000..dc2cd83 --- /dev/null +++ b/apps/advanced/install.bat @@ -0,0 +1,20 @@ +@echo off + +rem ------------------------------------------------------------- +rem Yii command line install script for Windows. +rem +rem @author Qiang Xue <qiang.xue@gmail.com> +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%install" %* + +@endlocal diff --git a/apps/bootstrap/requirements.php b/apps/advanced/requirements.php similarity index 94% rename from apps/bootstrap/requirements.php rename to apps/advanced/requirements.php index 5a2d910..c9e6493 100644 --- a/apps/bootstrap/requirements.php +++ b/apps/advanced/requirements.php @@ -11,7 +11,14 @@ */ // you may need to adjust this path to the correct Yii framework path -$frameworkPath = dirname(__FILE__) . '/../../yii'; +$frameworkPath = dirname(__FILE__) . '/vendor/yiisoft/yii2/yii'; + +if (!is_dir($frameworkPath)) { + echo '<h1>Error</h1>'; + echo '<p><strong>The path to yii framework seems to be incorrect.</strong></p>'; + echo '<p>You need to install Yii framework via composer or adjust the framework path in file <abbr title="' . __FILE__ . '">' . basename(__FILE__) .'</abbr>.</p>'; + echo '<p>Please refer to the <abbr title="' . dirname(__FILE__) . '/README.md">README</abbr> on how to install Yii.</p>'; +} require_once($frameworkPath . '/requirements/YiiRequirementChecker.php'); $requirementsChecker = new YiiRequirementChecker(); diff --git a/apps/advanced/vendor/.gitignore b/apps/advanced/vendor/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/apps/advanced/vendor/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/apps/bootstrap/yii.bat b/apps/advanced/yii.bat similarity index 100% rename from apps/bootstrap/yii.bat rename to apps/advanced/yii.bat diff --git a/apps/basic/LICENSE.md b/apps/basic/LICENSE.md new file mode 100644 index 0000000..6edcc4f --- /dev/null +++ b/apps/basic/LICENSE.md @@ -0,0 +1,32 @@ +The Yii framework is free software. It is released under the terms of +the following BSD License. + +Copyright © 2008-2013 by Yii Software LLC (http://www.yiisoft.com) +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of Yii Software LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/apps/bootstrap/README.md b/apps/basic/README.md similarity index 75% rename from apps/bootstrap/README.md rename to apps/basic/README.md index a1376ba..5300448 100644 --- a/apps/bootstrap/README.md +++ b/apps/basic/README.md @@ -1,5 +1,5 @@ -Yii 2 Bootstrap Application -=========================== +Yii 2 Basic Application Template +================================ **NOTE** Yii 2 and the relevant applications and extensions are still under heavy development. We may make significant changes without prior notices. Please do not @@ -7,10 +7,10 @@ use them for production. Please consider using [Yii v1.1](https://github.com/yii if you have a project to be deployed for production soon. -Thank you for choosing Yii 2 - the new generation of high-performance PHP framework. +Thank you for using Yii 2 Basic Application Template - an application template +that works out-of-box and can be easily customized to fit for your needs. -The Yii 2 Bootstrap Application is a Web application template that you can easily customize -to fit for your needs. It is particularly suitable for small Websites which mainly contain +Yii 2 Basic Application Template is best suitable for small Websites which mainly contain a few informational pages. @@ -49,11 +49,11 @@ curl -s http://getcomposer.org/installer | php You can then install the Bootstrap Application using the following command: ~~~ -php composer.phar create-project --stability=dev yiisoft/yii2-bootstrap bootstrap +php composer.phar create-project --stability=dev yiisoft/yii2-app-basic yii-basic ~~~ -Now you should be able to access the Bootstrap Application using the URL `http://localhost/bootstrap/www/`, -assuming `bootstrap` is directly under the document root of your Web server. +Now you should be able to access the application using the URL `http://localhost/yii-basic/www/`, +assuming `yii-basic` is directly under the document root of your Web server. ### Install from an Archive File diff --git a/apps/bootstrap/www/assets/.gitignore b/apps/basic/assets/.gitkeep similarity index 100% rename from apps/bootstrap/www/assets/.gitignore rename to apps/basic/assets/.gitkeep diff --git a/apps/bootstrap/commands/HelloController.php b/apps/basic/commands/HelloController.php similarity index 100% rename from apps/bootstrap/commands/HelloController.php rename to apps/basic/commands/HelloController.php diff --git a/apps/bootstrap/composer.json b/apps/basic/composer.json similarity index 92% rename from apps/bootstrap/composer.json rename to apps/basic/composer.json index d44e35a..29b05d1 100644 --- a/apps/bootstrap/composer.json +++ b/apps/basic/composer.json @@ -1,7 +1,7 @@ { - "name": "yiisoft/yii2-bootstrap", - "description": "Yii 2 Bootstrap Application", - "keywords": ["yii", "framework", "bootstrap"], + "name": "yiisoft/yii2-app-basic", + "description": "Yii 2 Basic Application Template", + "keywords": ["yii", "framework", "basic", "application template"], "homepage": "http://www.yiiframework.com/", "type": "project", "license": "BSD-3-Clause", diff --git a/apps/basic/composer.lock b/apps/basic/composer.lock new file mode 100644 index 0000000..a66bbea --- /dev/null +++ b/apps/basic/composer.lock @@ -0,0 +1,164 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" + ], + "hash": "0411dbbd774aa1c89256c77c68023940", + "packages": [ + { + "name": "yiisoft/yii2", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/yiisoft/yii2-framework.git", + "reference": "15a8d0559260e39954a8eb6de0d28bfb7de95e7b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/15a8d0559260e39954a8eb6de0d28bfb7de95e7b", + "reference": "15a8d0559260e39954a8eb6de0d28bfb7de95e7b", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "lib-pcre": "*", + "php": ">=5.3.7" + }, + "suggest": { + "ezyang/htmlpurifier": "Required by HtmlPurifier.", + "michelf/php-markdown": "Required by Markdown.", + "smarty/smarty": "Required by SmartyViewRenderer.", + "twig/twig": "Required by TwigViewRenderer." + }, + "type": "library", + "autoload": { + "psr-0": { + "yii\\": "/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "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": "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" + }, + { + "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" + } + ], + "description": "Yii2 Web Programming Framework", + "homepage": "http://www.yiiframework.com/", + "keywords": [ + "framework", + "yii" + ], + "time": "2013-05-25 20:59:05" + }, + { + "name": "yiisoft/yii2-composer", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/yiisoft/yii2-composer.git", + "reference": "7ce4060faca940b836ab88de207638940a0a0568" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/yiisoft/yii2-composer/zipball/7ce4060faca940b836ab88de207638940a0a0568", + "reference": "7ce4060faca940b836ab88de207638940a0a0568", + "shasum": "" + }, + "require": { + "yiisoft/yii2": "*" + }, + "type": "library", + "autoload": { + "psr-0": { + "yii\\composer": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Qiang Xue", + "email": "qiang.xue@gmail.com", + "homepage": "http://www.yiiframework.com/", + "role": "Founder and project lead" + } + ], + "description": "The composer integration for the Yii framework", + "keywords": [ + "composer", + "install", + "update", + "yii" + ], + "time": "2013-05-23 19:12:45" + } + ], + "packages-dev": [ + + ], + "aliases": [ + + ], + "minimum-stability": "dev", + "stability-flags": { + "yiisoft/yii2": 20, + "yiisoft/yii2-composer": 20 + }, + "platform": { + "php": ">=5.3.0" + }, + "platform-dev": [ + + ] +} diff --git a/apps/basic/config/assets.php b/apps/basic/config/assets.php new file mode 100644 index 0000000..ee0d610 --- /dev/null +++ b/apps/basic/config/assets.php @@ -0,0 +1,18 @@ +<?php + +return array( + 'app' => array( + 'basePath' => '@wwwroot', + 'baseUrl' => '@www', + 'css' => array( + 'css/site.css', + ), + 'js' => array( + + ), + 'depends' => array( + 'yii', + 'yii/bootstrap/responsive', + ), + ), +); diff --git a/apps/bootstrap/config/console.php b/apps/basic/config/console.php similarity index 94% rename from apps/bootstrap/config/console.php rename to apps/basic/config/console.php index df96023..bfb3ed7 100644 --- a/apps/bootstrap/config/console.php +++ b/apps/basic/config/console.php @@ -1,5 +1,5 @@ <?php - +$params = require(__DIR__ . '/params.php'); return array( 'id' => 'bootstrap-console', 'basePath' => dirname(__DIR__), @@ -22,5 +22,5 @@ return array( ), ), ), - 'params' => require(__DIR__ . '/params.php'), + 'params' => $params, ); diff --git a/apps/bootstrap/config/main.php b/apps/basic/config/main.php similarity index 96% rename from apps/bootstrap/config/main.php rename to apps/basic/config/main.php index b5980da..9adfba6 100644 --- a/apps/bootstrap/config/main.php +++ b/apps/basic/config/main.php @@ -1,5 +1,5 @@ <?php - +$params = require(__DIR__ . '/params.php'); return array( 'id' => 'bootstrap', 'basePath' => dirname(__DIR__), @@ -34,5 +34,5 @@ return array( ), ), ), - 'params' => require(__DIR__ . '/params.php'), + 'params' => $params, ); diff --git a/apps/basic/config/params.php b/apps/basic/config/params.php new file mode 100644 index 0000000..1e197d0 --- /dev/null +++ b/apps/basic/config/params.php @@ -0,0 +1,5 @@ +<?php + +return array( + 'adminEmail' => 'admin@example.com', +); \ No newline at end of file diff --git a/apps/bootstrap/controllers/SiteController.php b/apps/basic/controllers/SiteController.php similarity index 100% rename from apps/bootstrap/controllers/SiteController.php rename to apps/basic/controllers/SiteController.php diff --git a/apps/bootstrap/models/ContactForm.php b/apps/basic/models/ContactForm.php similarity index 100% rename from apps/bootstrap/models/ContactForm.php rename to apps/basic/models/ContactForm.php diff --git a/apps/bootstrap/models/LoginForm.php b/apps/basic/models/LoginForm.php similarity index 100% rename from apps/bootstrap/models/LoginForm.php rename to apps/basic/models/LoginForm.php diff --git a/apps/bootstrap/models/User.php b/apps/basic/models/User.php similarity index 100% rename from apps/bootstrap/models/User.php rename to apps/basic/models/User.php diff --git a/apps/basic/requirements.php b/apps/basic/requirements.php new file mode 100644 index 0000000..c9e6493 --- /dev/null +++ b/apps/basic/requirements.php @@ -0,0 +1,103 @@ +<?php +/** + * Application requirement checker script. + * + * In order to run this script use the following console command: + * php requirements.php + * + * In order to run this script from the web, you should copy it to the web root. + * If you are using Linux you can create a hard link instead, using the following command: + * ln requirements.php ../requirements.php + */ + +// you may need to adjust this path to the correct Yii framework path +$frameworkPath = dirname(__FILE__) . '/vendor/yiisoft/yii2/yii'; + +if (!is_dir($frameworkPath)) { + echo '<h1>Error</h1>'; + echo '<p><strong>The path to yii framework seems to be incorrect.</strong></p>'; + echo '<p>You need to install Yii framework via composer or adjust the framework path in file <abbr title="' . __FILE__ . '">' . basename(__FILE__) .'</abbr>.</p>'; + echo '<p>Please refer to the <abbr title="' . dirname(__FILE__) . '/README.md">README</abbr> on how to install Yii.</p>'; +} + +require_once($frameworkPath . '/requirements/YiiRequirementChecker.php'); +$requirementsChecker = new YiiRequirementChecker(); + +/** + * Adjust requirements according to your application specifics. + */ +$requirements = array( + // Database : + array( + 'name' => 'PDO extension', + 'mandatory' => true, + 'condition' => extension_loaded('pdo'), + 'by' => 'All <a href="http://www.yiiframework.com/doc/api/#system.db">DB-related classes</a>', + ), + array( + 'name' => 'PDO SQLite extension', + 'mandatory' => false, + 'condition' => extension_loaded('pdo_sqlite'), + 'by' => 'All <a href="http://www.yiiframework.com/doc/api/#system.db">DB-related classes</a>', + 'memo' => 'Required for SQLite database.', + ), + array( + 'name' => 'PDO MySQL extension', + 'mandatory' => false, + 'condition' => extension_loaded('pdo_mysql'), + 'by' => 'All <a href="http://www.yiiframework.com/doc/api/#system.db">DB-related classes</a>', + 'memo' => 'Required for MySQL database.', + ), + // Cache : + array( + 'name' => 'Memcache extension', + 'mandatory' => false, + 'condition' => extension_loaded('memcache') || extension_loaded('memcached'), + 'by' => '<a href="http://www.yiiframework.com/doc/api/CMemCache">CMemCache</a>', + 'memo' => extension_loaded('memcached') ? 'To use memcached set <a href="http://www.yiiframework.com/doc/api/CMemCache#useMemcached-detail">CMemCache::useMemcached</a> to <code>true</code>.' : '' + ), + array( + 'name' => 'APC extension', + 'mandatory' => false, + 'condition' => extension_loaded('apc') || extension_loaded('apc'), + 'by' => '<a href="http://www.yiiframework.com/doc/api/CApcCache">CApcCache</a>', + ), + // Additional PHP extensions : + array( + 'name' => 'Mcrypt extension', + 'mandatory' => false, + 'condition' => extension_loaded('mcrypt'), + 'by' => '<a href="http://www.yiiframework.com/doc/api/CSecurityManager">CSecurityManager</a>', + 'memo' => 'Required by encrypt and decrypt methods.' + ), + // PHP ini : + 'phpSafeMode' => array( + 'name' => 'PHP safe mode', + 'mandatory' => false, + 'condition' => $requirementsChecker->checkPhpIniOff("safe_mode"), + 'by' => 'File uploading and console command execution', + 'memo' => '"safe_mode" should be disabled at php.ini', + ), + 'phpExposePhp' => array( + 'name' => 'Expose PHP', + 'mandatory' => false, + 'condition' => $requirementsChecker->checkPhpIniOff("expose_php"), + 'by' => 'Security reasons', + 'memo' => '"expose_php" should be disabled at php.ini', + ), + 'phpAllowUrlInclude' => array( + 'name' => 'PHP allow url include', + 'mandatory' => false, + 'condition' => $requirementsChecker->checkPhpIniOff("allow_url_include"), + 'by' => 'Security reasons', + 'memo' => '"allow_url_include" should be disabled at php.ini', + ), + 'phpSmtp' => array( + 'name' => 'PHP mail SMTP', + 'mandatory' => false, + 'condition' => strlen(ini_get('SMTP'))>0, + 'by' => 'Email sending', + 'memo' => 'PHP mail SMTP server required', + ), +); +$requirementsChecker->checkYii()->check($requirements)->render(); diff --git a/apps/basic/runtime/.gitignore b/apps/basic/runtime/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/apps/basic/runtime/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/apps/basic/vendor/.gitignore b/apps/basic/vendor/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/apps/basic/vendor/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/apps/basic/views/layouts/main.php b/apps/basic/views/layouts/main.php new file mode 100644 index 0000000..635e118 --- /dev/null +++ b/apps/basic/views/layouts/main.php @@ -0,0 +1,66 @@ +<?php +use yii\helpers\Html; +use yii\widgets\Menu; +use yii\widgets\Breadcrumbs; +use yii\debug\Toolbar; + +/** + * @var $this \yii\base\View + * @var $content string + */ +$this->registerAssetBundle('app'); +?> +<?php $this->beginPage(); ?> +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="utf-8"/> + <title><?php echo Html::encode($this->title); ?></title> + <?php $this->head(); ?> +</head> +<body> +<div class="container"> + <?php $this->beginBody(); ?> + <div class="masthead"> + <h3 class="muted">My Company</h3> + + <div class="navbar"> + <div class="navbar-inner"> + <div class="container"> + <?php echo Menu::widget(array( + 'options' => array('class' => 'nav'), + 'items' => array( + array('label' => 'Home', 'url' => array('/site/index')), + array('label' => 'About', 'url' => array('/site/about')), + array('label' => 'Contact', 'url' => array('/site/contact')), + Yii::$app->user->isGuest ? + array('label' => 'Login', 'url' => array('/site/login')) : + array('label' => 'Logout (' . Yii::$app->user->identity->username .')' , 'url' => array('/site/logout')), + ), + )); ?> + </div> + </div> + </div> + <!-- /.navbar --> + </div> + + <?php echo Breadcrumbs::widget(array( + 'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : array(), + )); ?> + <?php echo $content; ?> + + <hr> + + <div class="footer"> + <p>© My Company <?php echo date('Y'); ?></p> + <p> + <?php echo Yii::powered(); ?> + Template by <a href="http://twitter.github.io/bootstrap/">Twitter Bootstrap</a> + </p> + </div> + <?php $this->endBody(); ?> +</div> +<?php echo Toolbar::widget(); ?> +</body> +</html> +<?php $this->endPage(); ?> diff --git a/apps/basic/views/site/about.php b/apps/basic/views/site/about.php new file mode 100644 index 0000000..86e19e1 --- /dev/null +++ b/apps/basic/views/site/about.php @@ -0,0 +1,16 @@ +<?php +use yii\helpers\Html; +/** + * @var yii\base\View $this + */ +$this->title = 'About'; +$this->params['breadcrumbs'][] = $this->title; +?> +<h1><?php echo Html::encode($this->title); ?></h1> + +<p> + This is the About page. You may modify the following file to customize its content: +</p> + +<code><?php echo __FILE__; ?></code> + diff --git a/apps/basic/views/site/contact.php b/apps/basic/views/site/contact.php new file mode 100644 index 0000000..e740d0f --- /dev/null +++ b/apps/basic/views/site/contact.php @@ -0,0 +1,46 @@ +<?php +use yii\helpers\Html; +use yii\widgets\ActiveForm; +use yii\widgets\Captcha; + +/** + * @var yii\base\View $this + * @var yii\widgets\ActiveForm $form + * @var app\models\ContactForm $model + */ +$this->title = 'Contact'; +$this->params['breadcrumbs'][] = $this->title; +?> +<h1><?php echo Html::encode($this->title); ?></h1> + +<?php if (Yii::$app->session->hasFlash('contactFormSubmitted')): ?> +<div class="alert alert-success"> + Thank you for contacting us. We will respond to you as soon as possible. +</div> +<?php return; endif; ?> + +<p> + If you have business inquiries or other questions, please fill out the following form to contact us. Thank you. +</p> + +<?php $form = ActiveForm::begin(array( + 'options' => array('class' => 'form-horizontal'), + 'fieldConfig' => array('inputOptions' => array('class' => 'input-xlarge')), +)); ?> + <?php echo $form->field($model, 'name')->textInput(); ?> + <?php echo $form->field($model, 'email')->textInput(); ?> + <?php echo $form->field($model, 'subject')->textInput(); ?> + <?php echo $form->field($model, 'body')->textArea(array('rows' => 6)); ?> + <?php + $field = $form->field($model, 'verifyCode'); + echo $field->begin() + . $field->label() + . Captcha::widget() + . Html::activeTextInput($model, 'verifyCode', array('class' => 'input-medium')) + . $field->error() + . $field->end(); + ?> + <div class="form-actions"> + <?php echo Html::submitButton('Submit', null, null, array('class' => 'btn btn-primary')); ?> + </div> +<?php ActiveForm::end(); ?> diff --git a/apps/basic/views/site/index.php b/apps/basic/views/site/index.php new file mode 100644 index 0000000..158b61c --- /dev/null +++ b/apps/basic/views/site/index.php @@ -0,0 +1,47 @@ +<?php +/** + * @var yii\base\View $this + */ +$this->title = 'Welcome'; +?> +<div class="jumbotron"> + <h1>Welcome!</h1> + + <p class="lead">Cras justo odio, dapibus ac facilisis in, egestas eget quam. Fusce dapibus, tellus ac cursus + commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.</p> + <a class="btn btn-large btn-success" href="http://www.yiiframework.com">Get started with Yii</a> +</div> + +<hr> + +<!-- Example row of columns --> +<div class="row-fluid"> + <div class="span4"> + <h2>Heading</h2> + + <p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris + condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. + Donec sed odio dui. </p> + + <p><a class="btn" href="#">View details »</a></p> + </div> + <div class="span4"> + <h2>Heading</h2> + + <p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris + condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. + Donec sed odio dui. </p> + + <p><a class="btn" href="#">View details »</a></p> + </div> + <div class="span4"> + <h2>Heading</h2> + + <p>Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta + felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum + massa.</p> + + <p><a class="btn" href="#">View details »</a></p> + </div> +</div> + diff --git a/apps/basic/views/site/login.php b/apps/basic/views/site/login.php new file mode 100644 index 0000000..f676b98 --- /dev/null +++ b/apps/basic/views/site/login.php @@ -0,0 +1,24 @@ +<?php +use yii\helpers\Html; +use yii\widgets\ActiveForm; + +/** + * @var yii\base\View $this + * @var yii\widgets\ActiveForm $form + * @var app\models\LoginForm $model + */ +$this->title = 'Login'; +$this->params['breadcrumbs'][] = $this->title; +?> +<h1><?php echo Html::encode($this->title); ?></h1> + +<p>Please fill out the following fields to login:</p> + +<?php $form = ActiveForm::begin(array('options' => array('class' => 'form-horizontal'))); ?> + <?php echo $form->field($model, 'username')->textInput(); ?> + <?php echo $form->field($model, 'password')->passwordInput(); ?> + <?php echo $form->field($model, 'rememberMe')->checkbox(); ?> + <div class="form-actions"> + <?php echo Html::submitButton('Login', null, null, array('class' => 'btn btn-primary')); ?> + </div> +<?php ActiveForm::end(); ?> diff --git a/apps/basic/www/assets/.gitignore b/apps/basic/www/assets/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/apps/basic/www/assets/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/apps/basic/www/css/site.css b/apps/basic/www/css/site.css new file mode 100644 index 0000000..890a953 --- /dev/null +++ b/apps/basic/www/css/site.css @@ -0,0 +1,78 @@ +body { + padding-top: 20px; + padding-bottom: 60px; +} + +/* Custom container */ +.container { + margin: 0 auto; + max-width: 1000px; +} + +.container > hr { + margin: 60px 0; +} + +/* Main marketing message and sign up button */ +.jumbotron { + margin: 80px 0; + text-align: center; +} + +.jumbotron h1 { + font-size: 100px; + line-height: 1; +} + +.jumbotron .lead { + font-size: 24px; + line-height: 1.25; +} + +.jumbotron .btn { + font-size: 21px; + padding: 14px 24px; +} + +/* Supporting marketing content */ +.marketing { + margin: 60px 0; +} + +.marketing p + h4 { + margin-top: 28px; +} + +/* Customize the navbar links to be fill the entire space of the .navbar */ +.navbar .navbar-inner { + padding: 0; +} + +.navbar .nav { + margin: 0; + display: table; + width: 100%; +} + +.navbar .nav li { + display: table-cell; + width: 1%; + float: none; +} + +.navbar .nav li a { + font-weight: bold; + text-align: center; + border-left: 1px solid rgba(255, 255, 255, .75); + border-right: 1px solid rgba(0, 0, 0, .1); +} + +.navbar .nav li:first-child a { + border-left: 0; + border-radius: 3px 0 0 3px; +} + +.navbar .nav li:last-child a { + border-right: 0; + border-radius: 0 3px 3px 0; +} diff --git a/apps/bootstrap/www/index.php b/apps/basic/www/index.php similarity index 100% rename from apps/bootstrap/www/index.php rename to apps/basic/www/index.php diff --git a/apps/bootstrap/yii b/apps/basic/yii similarity index 100% rename from apps/bootstrap/yii rename to apps/basic/yii diff --git a/apps/basic/yii.bat b/apps/basic/yii.bat new file mode 100644 index 0000000..5e21e2e --- /dev/null +++ b/apps/basic/yii.bat @@ -0,0 +1,20 @@ +@echo off + +rem ------------------------------------------------------------- +rem Yii command line bootstrap script for Windows. +rem +rem @author Qiang Xue <qiang.xue@gmail.com> +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%yii" %* + +@endlocal diff --git a/apps/benchmark/LICENSE.md b/apps/benchmark/LICENSE.md new file mode 100644 index 0000000..6edcc4f --- /dev/null +++ b/apps/benchmark/LICENSE.md @@ -0,0 +1,32 @@ +The Yii framework is free software. It is released under the terms of +the following BSD License. + +Copyright © 2008-2013 by Yii Software LLC (http://www.yiisoft.com) +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of Yii Software LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/apps/benchmark/README.md b/apps/benchmark/README.md new file mode 100644 index 0000000..2aeb0ae --- /dev/null +++ b/apps/benchmark/README.md @@ -0,0 +1,56 @@ +Yii 2 Benchmark Application +=========================== + +**NOTE** Yii 2 and the relevant applications and extensions are still under heavy +development. We may make significant changes without prior notices. Please do not +use them for production. Please consider using [Yii v1.1](https://github.com/yiisoft/yii) +if you have a project to be deployed for production soon. + + +Yii 2 Benchmark Application is an application built to demonstrate the minimal overhead +introduced by the Yii framework. The application contains a single page which only renders +the "hello world" string. + +The application attempts to simulate the scenario in which you can achieve the best performance +when using Yii. It does so by assuming that both of the main application configuration and the page +content are cached in memory, and the application enables pretty URLs. + + +DIRECTORY STRUCTURE +------------------- + + protected/ contains application source code + controllers/ contains Web controller classes + index.php the entry script + + +REQUIREMENTS +------------ + +The minimum requirement by Yii is that your Web server supports PHP 5.3.?. + + +INSTALLATION +------------ + +If you do not have [Composer](http://getcomposer.org/), you may download it from +[http://getcomposer.org/](http://getcomposer.org/) or run the following command on Linux/Unix/MacOS: + +~~~ +curl -s http://getcomposer.org/installer | php +~~~ + +You can then install the Bootstrap Application using the following command: + +~~~ +php composer.phar create-project --stability=dev yiisoft/yii2-app-benchmark yii-benchmark +~~~ + +Now you should be able to access the benchmark page using the URL + +~~~ +http://localhost/yii-benchmark/index.php/site/hello +~~~ + +In the above, we assume `yii-benchmark` is directly under the document root of your Web server. + diff --git a/apps/benchmark/composer.json b/apps/benchmark/composer.json new file mode 100644 index 0000000..2b077e0 --- /dev/null +++ b/apps/benchmark/composer.json @@ -0,0 +1,23 @@ +{ + "name": "yiisoft/yii2-app-benchmark", + "description": "Yii 2 Benchmark Application", + "keywords": ["yii", "framework", "benchmark", "application"], + "homepage": "http://www.yiiframework.com/", + "type": "project", + "license": "BSD-3-Clause", + "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" + }, + "config": { + "vendor-dir": "protected/vendor" + }, + "minimum-stability": "dev", + "require": { + "php": ">=5.3.0", + "yiisoft/yii2": "dev-master" + } +} diff --git a/apps/benchmark/composer.lock b/apps/benchmark/composer.lock new file mode 100644 index 0000000..c7a0324 --- /dev/null +++ b/apps/benchmark/composer.lock @@ -0,0 +1,119 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" + ], + "hash": "5ce5f1ad2aa7d7e31c3e216b8ce23387", + "packages": [ + { + "name": "yiisoft/yii2", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/yiisoft/yii2-framework.git", + "reference": "f3c3d9d764de25fc46711bce2259274bcceade1c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/f3c3d9d764de25fc46711bce2259274bcceade1c", + "reference": "f3c3d9d764de25fc46711bce2259274bcceade1c", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "lib-pcre": "*", + "php": ">=5.3.7" + }, + "suggest": { + "ezyang/htmlpurifier": "Required by HtmlPurifier.", + "michelf/php-markdown": "Required by Markdown.", + "smarty/smarty": "Required by SmartyViewRenderer.", + "twig/twig": "Required by TwigViewRenderer." + }, + "type": "library", + "autoload": { + "psr-0": { + "yii\\": "/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "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": "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" + }, + { + "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" + } + ], + "description": "Yii2 Web Programming Framework", + "homepage": "http://www.yiiframework.com/", + "keywords": [ + "framework", + "yii" + ], + "time": "2013-05-26 21:57:00" + } + ], + "packages-dev": [ + + ], + "aliases": [ + + ], + "minimum-stability": "dev", + "stability-flags": { + "yiisoft/yii2": 20 + }, + "platform": { + "php": ">=5.3.0" + }, + "platform-dev": [ + + ] +} diff --git a/apps/benchmark/index.php b/apps/benchmark/index.php new file mode 100644 index 0000000..ddf6081 --- /dev/null +++ b/apps/benchmark/index.php @@ -0,0 +1,18 @@ +<?php + +defined('YII_DEBUG') or define('YII_DEBUG', false); + +require(__DIR__ . '/protected/vendor/yiisoft/yii2/yii/Yii.php'); + +$config = array( + 'id' => 'benchmark', + 'basePath' => __DIR__ . '/protected', + 'components' => array( + 'urlManager' => array( + 'enablePrettyUrl' => true, + ), + ) +); + +$application = new yii\web\Application($config); +$application->run(); diff --git a/apps/benchmark/protected/.htaccess b/apps/benchmark/protected/.htaccess new file mode 100644 index 0000000..e019832 --- /dev/null +++ b/apps/benchmark/protected/.htaccess @@ -0,0 +1 @@ +deny from all diff --git a/apps/benchmark/protected/controllers/SiteController.php b/apps/benchmark/protected/controllers/SiteController.php new file mode 100644 index 0000000..16089d0 --- /dev/null +++ b/apps/benchmark/protected/controllers/SiteController.php @@ -0,0 +1,13 @@ +<?php + +use yii\web\Controller; + +class SiteController extends Controller +{ + public $defaultAction = 'hello'; + + public function actionHello() + { + echo 'hello world'; + } +} diff --git a/apps/benchmark/protected/vendor/.gitignore b/apps/benchmark/protected/vendor/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/apps/benchmark/protected/vendor/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/docs/guide/bootstrap.md b/docs/guide/bootstrap.md index 47e3b8f..26a6bde 100644 --- a/docs/guide/bootstrap.md +++ b/docs/guide/bootstrap.md @@ -1,15 +1,30 @@ Bootstrap with Yii ================== -A ready-to-use Web application is distributed together with Yii. You may find -its source code under the `app` folder after you expand the Yii release file. -If you have installed Yii under a Web-accessible folder, you should be able to -access this application through the following URL: +Yii provides a few read-to-use application templates. Based on your needs, you may +choose one of them to bootstrap your project. + +In following, we describe how to get started with the "Yii 2 Basic Application Template". + + +### Install via Composer + +If you do not have [Composer](http://getcomposer.org/), you may download it from +[http://getcomposer.org/](http://getcomposer.org/) or run the following command on Linux/Unix/MacOS: ~~~ -http://localhost/yii/apps/bootstrap/index.php +curl -s http://getcomposer.org/installer | php ~~~ +You can then install the Bootstrap Application using the following command: + +~~~ +php composer.phar create-project --stability=dev yiisoft/yii2-app-basic yii-basic +~~~ + +Now you should be able to access the Bootstrap Application using the URL `http://localhost/yii-basic/www/`, +assuming `yii-basic` is directly under the document root of your Web server. + As you can see, the application has four pages: the homepage, the about page, the contact page and the login page. The contact page displays a contact @@ -20,42 +35,34 @@ and the login page allows users to be authenticated before accessing privileged The following diagram shows the directory structure of this application. ~~~ -app/ - index.php Web application entry script file - index-test.php entry script file for the functional tests - assets/ containing published resource files - css/ containing CSS files - img/ containing image files - themes/ containing application themes - protected/ containing protected application files - yii yii command line script for Unix/Linux - yii.bat yii command line script for Windows - commands/ containing customized yii console commands - components/ containing reusable user components - config/ containing configuration files - console.php the console application configuration - main.php the Web application configuration - controllers/ containing controller class files - SiteController.php the default controller class - data/ containing the sample database - schema.mysql.sql the DB schema for the sample MySQL database - schema.sqlite.sql the DB schema for the sample SQLite database - bootstrap.db the sample SQLite database file - vendor/ containing third-party extensions and libraries - messages/ containing translated messages - models/ containing model class files - User.php the User model - LoginForm.php the form model for 'login' action - ContactForm.php the form model for 'contact' action - runtime/ containing temporarily generated files - views/ containing controller view and layout files - layouts/ containing layout view files - main.php the base layout shared by all pages - site/ containing view files for the 'site' controller - about.php the view for the 'about' action - contact.php the view for the 'contact' action - index.php the view for the 'index' action - login.php the view for the 'login' action +yii-basic/ + yii yii command line script for Unix/Linux + yii.bat yii command line script for Windows + requirements.php the requirement checker script + commands/ containing customized yii console commands + config/ containing configuration files + console.php the console application configuration + main.php the Web application configuration + controllers/ containing controller class files + SiteController.php the default controller class + vendor/ containing third-party extensions and libraries + models/ containing model class files + User.php the User model + LoginForm.php the form model for 'login' action + ContactForm.php the form model for 'contact' action + runtime/ containing temporarily generated files + views/ containing controller view and layout files + layouts/ containing layout view files + main.php the base layout shared by all pages + site/ containing view files for the 'site' controller + about.php the view for the 'about' action + contact.php the view for the 'contact' action + index.php the view for the 'index' action + login.php the view for the 'login' action + www/ containing Web-accessible resources + index.php Web application entry script file + assets/ containing published resource files + css/ containing CSS files ~~~ diff --git a/extensions/smarty/composer.json b/extensions/smarty/composer.json new file mode 100644 index 0000000..b1e1681 --- /dev/null +++ b/extensions/smarty/composer.json @@ -0,0 +1,28 @@ +{ + "name": "yiisoft/yii2-smarty", + "description": "The Smarty integration for the Yii framework", + "keywords": ["yii", "smarty", "renderer"], + "type": "library", + "license": "BSD-3-Clause", + "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" + }, + "authors": [ + { + "name": "Alenxader Makarov", + "email": "sam@rmcreative.ru" + } + ], + "minimum-stability": "dev", + "require": { + "yiisoft/yii2": "*", + "smarty/smarty": "v3.1.13" + }, + "autoload": { + "psr-0": { "yii\\smarty": "" } + } +} diff --git a/framework/yii/renderers/SmartyViewRenderer.php b/extensions/smarty/yii/smarty/ViewRenderer.php similarity index 96% rename from framework/yii/renderers/SmartyViewRenderer.php rename to extensions/smarty/yii/smarty/ViewRenderer.php index ac66e0d..d8c5d30 100644 --- a/framework/yii/renderers/SmartyViewRenderer.php +++ b/extensions/smarty/yii/smarty/ViewRenderer.php @@ -7,13 +7,13 @@ * @license http://www.yiiframework.com/license/ */ -namespace yii\renderers; +namespace yii\smarty; use Yii; use Smarty; use yii\base\View; -use yii\base\ViewRenderer; use yii\helpers\Html; +use yii\base\ViewRenderer as BaseViewRenderer; /** * SmartyViewRenderer allows you to use Smarty templates in views. @@ -21,7 +21,7 @@ use yii\helpers\Html; * @author Alexander Makarov <sam@rmcreative.ru> * @since 2.0 */ -class SmartyViewRenderer extends ViewRenderer +class ViewRenderer extends BaseViewRenderer { /** * @var string the directory or path alias pointing to where Smarty cache will be stored. diff --git a/extensions/twig/composer.json b/extensions/twig/composer.json new file mode 100644 index 0000000..0ff7437 --- /dev/null +++ b/extensions/twig/composer.json @@ -0,0 +1,28 @@ +{ + "name": "yiisoft/yii2-twig", + "description": "The Twig integration for the Yii framework", + "keywords": ["yii", "twig", "renderer"], + "type": "library", + "license": "BSD-3-Clause", + "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" + }, + "authors": [ + { + "name": "Alenxader Makarov", + "email": "sam@rmcreative.ru" + } + ], + "minimum-stability": "dev", + "require": { + "yiisoft/yii2": "*", + "twig/twig": "v1.13.0" + }, + "autoload": { + "psr-0": { "yii\\twig": "" } + } +} diff --git a/framework/yii/renderers/TwigViewRenderer.php b/extensions/twig/yii/twig/ViewRenderer.php similarity index 96% rename from framework/yii/renderers/TwigViewRenderer.php rename to extensions/twig/yii/twig/ViewRenderer.php index de561d3..7498d86 100644 --- a/framework/yii/renderers/TwigViewRenderer.php +++ b/extensions/twig/yii/twig/ViewRenderer.php @@ -11,7 +11,7 @@ namespace yii\renderers; use Yii; use yii\base\View; -use yii\base\ViewRenderer; +use yii\base\ViewRenderer as BaseViewRenderer; use yii\helpers\Html; /** @@ -20,7 +20,7 @@ use yii\helpers\Html; * @author Alexander Makarov <sam@rmcreative.ru> * @since 2.0 */ -class TwigViewRenderer extends ViewRenderer +class ViewRenderer extends BaseViewRenderer { /** * @var string the directory or path alias pointing to where Twig cache will be stored. diff --git a/framework/composer.json b/framework/composer.json index 2f0e85f..3265c11 100644 --- a/framework/composer.json +++ b/framework/composer.json @@ -72,9 +72,9 @@ "psr-0": { "yii\\": "/" } }, "suggest": { - "michelf/php-markdown": "Required for Markdown helper.", - "twig/twig": "Required for TwigViewRenderer.", - "smarty/smarty": "Required for SmartyViewRenderer.", - "ezyang/htmlpurifier": "Required for Purifier helper." + "michelf/php-markdown": "Required by Markdown.", + "twig/twig": "Required by TwigViewRenderer.", + "smarty/smarty": "Required by SmartyViewRenderer.", + "ezyang/htmlpurifier": "Required by HtmlPurifier." } } diff --git a/framework/yii/base/Application.php b/framework/yii/base/Application.php index fb9a6c1..eb0a0d3 100644 --- a/framework/yii/base/Application.php +++ b/framework/yii/base/Application.php @@ -207,7 +207,8 @@ class Application extends Module /** * Returns the directory that stores vendor files. - * @return string the directory that stores vendor files. Defaults to 'protected/vendor'. + * @return string the directory that stores vendor files. + * Defaults to 'vendor' directory under applications [[basePath]]. */ public function getVendorPath() { diff --git a/framework/yii/base/ErrorHandler.php b/framework/yii/base/ErrorHandler.php index 8340723..8dc3fce 100644 --- a/framework/yii/base/ErrorHandler.php +++ b/framework/yii/base/ErrorHandler.php @@ -7,6 +7,8 @@ namespace yii\base; +use Yii; + /** * ErrorHandler handles uncaught PHP errors and exceptions. * @@ -14,6 +16,7 @@ namespace yii\base; * nature of the errors and the mode the application runs at. * * @author Qiang Xue <qiang.xue@gmail.com> + * @author Timur Ruziev <resurtm@gmail.com> * @since 2.0 */ class ErrorHandler extends Component @@ -31,49 +34,54 @@ class ErrorHandler extends Component */ public $discardExistingOutput = true; /** - * @var string the route (eg 'site/error') to the controller action that will be used to display external errors. - * Inside the action, it can retrieve the error information by \Yii::$app->errorHandler->error. - * This property defaults to null, meaning ErrorHandler will handle the error display. + * @var string the route (e.g. 'site/error') to the controller action that will be used + * to display external errors. Inside the action, it can retrieve the error information + * by Yii::$app->errorHandler->error. This property defaults to null, meaning ErrorHandler + * will handle the error display. */ public $errorAction; /** - * @var string the path of the view file for rendering exceptions + * @var string the path of the view file for rendering exceptions and errors. + */ + public $mainView = '@yii/views/errorHandler/main.php'; + /** + * @var string the path of the view file for rendering exceptions and errors call stack element. */ - public $exceptionView = '@yii/views/exception.php'; + public $callStackItemView = '@yii/views/errorHandler/callStackItem.php'; /** - * @var string the path of the view file for rendering errors + * @var string the path of the view file for rendering previous exceptions. */ - public $errorView = '@yii/views/error.php'; + public $previousExceptionView = '@yii/views/errorHandler/previousException.php'; /** - * @var \Exception the exception that is being handled currently + * @var \Exception the exception that is being handled currently. */ public $exception; /** - * Handles exception - * @param \Exception $exception + * Handles exception. + * @param \Exception $exception to be handled. */ public function handle($exception) { $this->exception = $exception; - if ($this->discardExistingOutput) { $this->clearOutput(); } - $this->renderException($exception); } /** - * Renders exception - * @param \Exception $exception + * Renders exception. + * @param \Exception $exception to be handled. */ protected function renderException($exception) { if ($this->errorAction !== null) { - \Yii::$app->runAction($this->errorAction); - } elseif (\Yii::$app instanceof \yii\web\Application) { + Yii::$app->runAction($this->errorAction); + } elseif (!(Yii::$app instanceof \yii\web\Application)) { + Yii::$app->renderException($exception); + } else { if (!headers_sent()) { if ($exception instanceof HttpException) { header('HTTP/1.0 ' . $exception->statusCode . ' ' . $exception->getName()); @@ -82,7 +90,7 @@ class ErrorHandler extends Component } } if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest') { - \Yii::$app->renderException($exception); + Yii::$app->renderException($exception); } else { // if there is an error during error rendering it's useful to // display PHP error in debug mode instead of a blank screen @@ -90,194 +98,179 @@ class ErrorHandler extends Component ini_set('display_errors', 1); } - $view = new View; - if (!YII_DEBUG || $exception instanceof UserException) { - $viewName = $this->errorView; - } else { - $viewName = $this->exceptionView; + $view = new View(); + $request = ''; + foreach (array('GET', 'POST', 'SERVER', 'FILES', 'COOKIE', 'SESSION', 'ENV') as $name) { + if (!empty($GLOBALS['_' . $name])) { + $request .= '$_' . $name . ' = ' . var_export($GLOBALS['_' . $name], true) . ";\n\n"; + } } - echo $view->renderFile($viewName, array( + $request = rtrim($request, "\n\n"); + echo $view->renderFile($this->mainView, array( 'exception' => $exception, + 'request' => $request, ), $this); } - } else { - \Yii::$app->renderException($exception); } } /** - * Returns server and Yii version information. - * @return string server version information. + * Converts special characters to HTML entities. + * @param string $text to encode. + * @return string encoded original text. */ - public function getVersionInfo() + public function htmlEncode($text) { - $version = '<a href="http://www.yiiframework.com/">Yii Framework</a>/' . \Yii::getVersion(); - if (isset($_SERVER['SERVER_SOFTWARE'])) { - $version = $_SERVER['SERVER_SOFTWARE'] . ' ' . $version; - } - return $version; + return htmlspecialchars($text, ENT_QUOTES, Yii::$app->charset); } /** - * Converts arguments array to its string representation - * - * @param array $args arguments array to be converted - * @return string string representation of the arguments array + * Removes all output echoed before calling this method. */ - public function argumentsToString($args) + public function clearOutput() { - $isAssoc = $args !== array_values($args); - $count = 0; - foreach ($args as $key => $value) { - $count++; - if ($count >= 5) { - if ($count > 5) { - unset($args[$key]); - } else { - $args[$key] = '...'; - } - continue; - } - - if (is_object($value)) { - $args[$key] = get_class($value); - } elseif (is_bool($value)) { - $args[$key] = $value ? 'true' : 'false'; - } elseif (is_string($value)) { - if (strlen($value) > 64) { - $args[$key] = '"' . substr($value, 0, 64) . '..."'; - } else { - $args[$key] = '"' . $value . '"'; - } - } elseif (is_array($value)) { - $args[$key] = 'array(' . $this->argumentsToString($value) . ')'; - } elseif ($value === null) { - $args[$key] = 'null'; - } elseif (is_resource($value)) { - $args[$key] = 'resource'; - } - - if (is_string($key)) { - $args[$key] = '"' . $key . '" => ' . $args[$key]; - } elseif ($isAssoc) { - $args[$key] = $key . ' => ' . $args[$key]; - } + // the following manual level counting is to deal with zlib.output_compression set to On + for ($level = ob_get_level(); $level > 0; --$level) { + @ob_end_clean(); } - return implode(', ', $args); } /** - * Returns a value indicating whether the call stack is from application code. - * @param array $trace the trace data - * @return boolean whether the call stack is from application code. + * Adds informational links to the given PHP type/class. + * @param string $code type/class name to be linkified. + * @return string linkified with HTML type/class name. */ - public function isCoreCode($trace) + public function addTypeLinks($code) { - if (isset($trace['file'])) { - return $trace['file'] === 'unknown' || strpos(realpath($trace['file']), YII_PATH . DIRECTORY_SEPARATOR) === 0; + $html = ''; + if (strpos($code, '\\') !== false) { + // namespaced class + foreach (explode('\\', $code) as $part) { + $html .= '<a href="http://yiiframework.com/doc/api/2.0/' . $this->htmlEncode($part) . '" target="_blank">' . $this->htmlEncode($part) . '</a>\\'; + } + $html = rtrim($html, '\\'); + } elseif (strpos($code, '()') !== false) { + // method/function call + $self = $this; + $html = preg_replace_callback('/^(.*)\(\)$/', function ($matches) use ($self) { + return '<a href="http://yiiframework.com/doc/api/2.0/' . $self->htmlEncode($matches[1]) . '" target="_blank">' . + $self->htmlEncode($matches[1]) . '</a>()'; + }, $code); } - return false; + return $html; } /** - * Renders the source code around the error line. - * @param string $file source file path - * @param integer $errorLine the error line number - * @param integer $maxLines maximum number of lines to display + * Creates HTML containing link to the page with the information on given HTTP status code. + * @param integer $statusCode to be used to generate information link. + * @param string $statusDescription Description to display after the the status code. + * @return string generated HTML with HTTP status code information. */ - public function renderSourceCode($file, $errorLine, $maxLines) + public function createHttpStatusLink($statusCode, $statusDescription) { - $errorLine--; // adjust line number to 0-based from 1-based - if ($errorLine < 0 || ($lines = @file($file)) === false || ($lineCount = count($lines)) <= $errorLine) { - return; - } - - $halfLines = (int)($maxLines / 2); - $beginLine = $errorLine - $halfLines > 0 ? $errorLine - $halfLines : 0; - $endLine = $errorLine + $halfLines < $lineCount ? $errorLine + $halfLines : $lineCount - 1; - $lineNumberWidth = strlen($endLine + 1); + return '<a href="http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#' . (int)$statusCode .'" target="_blank">HTTP ' . (int)$statusCode . ' – ' . $statusDescription . '</a>'; + } - $output = ''; - for ($i = $beginLine; $i <= $endLine; ++$i) { - $isErrorLine = $i === $errorLine; - $code = sprintf("<span class=\"ln" . ($isErrorLine ? ' error-ln' : '') . "\">%0{$lineNumberWidth}d</span> %s", $i + 1, $this->htmlEncode(str_replace("\t", ' ', $lines[$i]))); - if (!$isErrorLine) { - $output .= $code; - } else { - $output .= '<span class="error">' . $code . '</span>'; - } + /** + * Renders the previous exception stack for a given Exception. + * @param \Exception $exception the exception whose precursors should be rendered. + * @return string HTML content of the rendered previous exceptions. + * Empty string if there are none. + */ + public function renderPreviousExceptions($exception) + { + if (($previous = $exception->getPrevious()) === null) { + return ''; } - echo '<div class="code"><pre>' . $output . '</pre></div>'; + $view = new View(); + return $view->renderFile($this->previousExceptionView, array( + 'exception' => $previous, + 'previousHtml' => $this->renderPreviousExceptions($previous), + ), $this); } /** - * Renders calls stack trace - * @param array $trace + * Renders a single call stack element. + * @param string|null $file name where call has happened. + * @param integer|null $line number on which call has happened. + * @param string|null $class called class name. + * @param string|null $method called function/method name. + * @param integer $index number of the call stack element. + * @return string HTML content of the rendered call stack element. */ - public function renderTrace($trace) + public function renderCallStackItem($file, $line, $class, $method, $index) { - $count = 0; - echo "<table>\n"; - foreach ($trace as $n => $t) { - if ($this->isCoreCode($t)) { - $cssClass = 'core collapsed'; - } elseif (++$count > 3) { - $cssClass = 'app collapsed'; - } else { - $cssClass = 'app expanded'; + $lines = array(); + $begin = $end = 0; + if ($file !== null && $line !== null) { + $line--; // adjust line number from one-based to zero-based + $lines = @file($file); + if ($line < 0 || $lines === false || ($lineCount = count($lines)) < $line + 1) { + return ''; } - $hasCode = isset($t['file']) && $t['file'] !== 'unknown' && is_file($t['file']); - echo "<tr class=\"trace $cssClass\"><td class=\"number\">#$n</td><td class=\"content\">"; - echo '<div class="trace-file">'; - if ($hasCode) { - echo '<div class="plus">+</div><div class="minus">-</div>'; - } - echo ' '; - if (isset($t['file'])) { - echo $this->htmlEncode($t['file']) . '(' . $t['line'] . '): '; - } - if (!empty($t['class'])) { - echo '<strong>' . $t['class'] . '</strong>' . $t['type']; - } - echo '<strong>' . $t['function'] . '</strong>'; - echo '(' . (empty($t['args']) ? '' : $this->htmlEncode($this->argumentsToString($t['args']))) . ')'; - echo '</div>'; - if ($hasCode) { - $this->renderSourceCode($t['file'], $t['line'], $this->maxTraceSourceLines); - } - echo "</td></tr>\n"; + $half = (int)(($index == 0 ? $this->maxSourceLines : $this->maxTraceSourceLines) / 2); + $begin = $line - $half > 0 ? $line - $half : 0; + $end = $line + $half < $lineCount ? $line + $half : $lineCount - 1; } - echo '</table>'; + + $view = new View(); + return $view->renderFile($this->callStackItemView, array( + 'file' => $file, + 'line' => $line, + 'class' => $class, + 'method' => $method, + 'index' => $index, + 'lines' => $lines, + 'begin' => $begin, + 'end' => $end, + ), $this); } /** - * Converts special characters to HTML entities - * @param string $text text to encode - * @return string + * Determines whether given name of the file belongs to the framework. + * @param string $file name to be checked. + * @return boolean whether given name of the file belongs to the framework. */ - public function htmlEncode($text) + public function isCoreFile($file) { - return htmlspecialchars($text, ENT_QUOTES, \Yii::$app->charset); + return $file === null || strpos(realpath($file), YII_PATH . DIRECTORY_SEPARATOR) === 0; } - public function clearOutput() + /** + * Creates string containing HTML link which refers to the home page of determined web-server software + * and its full name. + * @return string server software information hyperlink. + */ + public function createServerInformationLink() { - // the following manual level counting is to deal with zlib.output_compression set to On - for ($level = ob_get_level(); $level > 0; --$level) { - @ob_end_clean(); + static $serverUrls = array( + 'http://httpd.apache.org/' => array('apache'), + 'http://nginx.org/' => array('nginx'), + 'http://lighttpd.net/' => array('lighttpd'), + 'http://gwan.com/' => array('g-wan', 'gwan'), + 'http://iis.net/' => array('iis', 'services'), + 'http://php.net/manual/en/features.commandline.webserver.php' => array('development'), + ); + if (isset($_SERVER['SERVER_SOFTWARE'])) { + foreach ($serverUrls as $url => $keywords) { + foreach ($keywords as $keyword) { + if (stripos($_SERVER['SERVER_SOFTWARE'], $keyword) !== false ) { + return '<a href="' . $url . '" target="_blank">' . $this->htmlEncode($_SERVER['SERVER_SOFTWARE']) . '</a>'; + } + } + } } + return ''; } /** - * @param \Exception $exception + * Creates string containing HTML link which refers to the page with the current version + * of the framework and version number text. + * @return string framework version information hyperlink. */ - public function renderAsHtml($exception) + public function createFrameworkVersionLink() { - $view = new View; - $name = !YII_DEBUG || $exception instanceof HttpException ? $this->errorView : $this->exceptionView; - echo $view->renderFile($name, array( - 'exception' => $exception, - ), $this); + return '<a href="http://github.com/yiisoft/yii2/" target="_blank">' . $this->htmlEncode(Yii::getVersion()) . '</a>'; } } diff --git a/framework/yii/base/HttpException.php b/framework/yii/base/HttpException.php index 2b014f7..4d63764 100644 --- a/framework/yii/base/HttpException.php +++ b/framework/yii/base/HttpException.php @@ -103,7 +103,7 @@ class HttpException extends UserException if (isset($httpCodes[$this->statusCode])) { return $httpCodes[$this->statusCode]; } else { - return \Yii::t('yii', 'Error'); + return 'Error'; } } } diff --git a/framework/yii/base/View.php b/framework/yii/base/View.php index 8bede3d..9d6b921 100644 --- a/framework/yii/base/View.php +++ b/framework/yii/base/View.php @@ -88,21 +88,23 @@ 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. - * 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. + * For example, the following configuration enables both Smarty and Twig view renderers: + * + * ~~~ + * array( + * 'tpl' => array( + * 'class' => 'yii\smarty\ViewRenderer', + * ), + * 'twig' => array( + * 'class' => 'yii\twig\ViewRenderer', + * ), + * ) + * ~~~ * * 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( - 'tpl' => array( - 'class' => 'yii\renderers\SmartyRenderer', - ), - 'twig' => array( - 'class' => 'yii\renderers\TwigRenderer', - ), - ); + public $renderers; /** * @var Theme|array the theme object or the configuration array for creating the theme object. * If not set, it means theming is not enabled. @@ -659,6 +661,8 @@ class View extends Component /** * Registers a JS file. + * Please note that when this file depends on other JS files to be registered before, + * for example jQuery, you should use [[registerAssetBundle]] instead. * @param string $url the JS file to be registered. * @param array $options the HTML attributes for the script tag. A special option * named "position" is supported which specifies where the JS script tag should be inserted diff --git a/framework/yii/behaviors/AutoTimestamp.php b/framework/yii/behaviors/AutoTimestamp.php index ea69963..7611712 100644 --- a/framework/yii/behaviors/AutoTimestamp.php +++ b/framework/yii/behaviors/AutoTimestamp.php @@ -83,17 +83,17 @@ class AutoTimestamp extends Behavior */ public function updateTimestamp($attributes) { + $timestamp = $this->evaluateTimestamp(); foreach ($attributes as $attribute) { - $this->owner->$attribute = $this->evaluateTimestamp($attribute); + $this->owner->$attribute = $timestamp; } } /** - * Gets the appropriate timestamp for the specified attribute. - * @param string $attribute attribute name + * Gets the current timestamp. * @return mixed the timestamp value */ - protected function evaluateTimestamp($attribute) + protected function evaluateTimestamp() { if ($this->timestamp instanceof Expression) { return $this->timestamp; diff --git a/framework/yii/bootstrap/Modal.php b/framework/yii/bootstrap/Modal.php index 3a4d08c..0608fbe 100644 --- a/framework/yii/bootstrap/Modal.php +++ b/framework/yii/bootstrap/Modal.php @@ -14,18 +14,6 @@ use yii\helpers\Html; /** * Modal renders a modal window that can be toggled by clicking on a button. * - * For example, - * - * ~~~php - * echo Modal::widget(array( - * 'header' => '<h2>Hello world</h2>', - * 'body' => 'Say hello...', - * 'toggleButton' => array( - * 'label' => 'click me', - * ), - * )); - * ~~~ - * * The following example will show the content enclosed between the [[begin()]] * and [[end()]] calls within the modal window: * @@ -54,12 +42,6 @@ class Modal extends Widget */ public $header; /** - * @var string the body content in the modal window. Note that anything between - * the [[begin()]] and [[end()]] calls of the Modal widget will also be treated - * as the body content, and will be rendered before this. - */ - public $body; - /** * @var string the footer content in the modal window. */ public $footer; @@ -154,7 +136,7 @@ class Modal extends Widget */ protected function renderBodyEnd() { - return $this->body . "\n" . Html::endTag('div'); + return Html::endTag('div'); } /** diff --git a/framework/yii/caching/Cache.php b/framework/yii/caching/Cache.php index efef048..fc1027c 100644 --- a/framework/yii/caching/Cache.php +++ b/framework/yii/caching/Cache.php @@ -7,7 +7,9 @@ namespace yii\caching; +use Yii; use yii\base\Component; +use yii\base\InvalidConfigException; use yii\helpers\StringHelper; /** @@ -52,10 +54,12 @@ use yii\helpers\StringHelper; abstract class Cache extends Component implements \ArrayAccess { /** - * @var string a string prefixed to every cache key so that it is unique. Defaults to null, meaning using - * the value of [[Application::id]] as the key prefix. You may set this property to be an empty string + * @var string a string prefixed to every cache key so that it is unique. If not set, + * it will use a prefix generated from [[Application::id]]. You may set this property to be an empty string * if you don't want to use key prefix. It is recommended that you explicitly set this property to some * static value if the cached data needs to be shared among multiple applications. + * + * To ensure interoperability, only use alphanumeric characters should be used. */ public $keyPrefix; /** @@ -78,35 +82,36 @@ abstract class Cache extends Component implements \ArrayAccess { parent::init(); if ($this->keyPrefix === null) { - $this->keyPrefix = \Yii::$app->id; + $this->keyPrefix = substr(md5(Yii::$app->id), 0, 5); + } elseif (!ctype_alnum($this->keyPrefix)) { + throw new InvalidConfigException(get_class($this) . '::keyPrefix should only contain alphanumeric characters.'); } } /** * Builds a normalized cache key from a given key. * - * The generated key contains letters and digits only, and its length is no more than 32. - * * If the given key is a string containing alphanumeric characters only and no more than 32 characters, - * then the key will be returned back without change. Otherwise, a normalized key - * is generated by serializing the given key and applying MD5 hashing. + * then the key will be returned back prefixed with [[keyPrefix]]. Otherwise, a normalized key + * is generated by serializing the given key, applying MD5 hashing, and prefixing with [[keyPrefix]]. * * The following example builds a cache key using three parameters: * * ~~~ - * $key = $cache->buildKey($className, $method, $id); + * $key = $cache->buildKey(array($className, $method, $id)); * ~~~ * - * @param array|string $key the key to be normalized + * @param mixed $key the key to be normalized * @return string the generated cache key */ public function buildKey($key) { if (is_string($key)) { - return ctype_alnum($key) && StringHelper::strlen($key) <= 32 ? $key : md5($key); + $key = ctype_alnum($key) && StringHelper::strlen($key) <= 32 ? $key : md5($key); } else { - return md5(json_encode($key)); + $key = md5(json_encode($key)); } + return $this->keyPrefix . $key; } /** @@ -117,7 +122,7 @@ abstract class Cache extends Component implements \ArrayAccess */ public function get($key) { - $key = $this->keyPrefix . $this->buildKey($key); + $key = $this->buildKey($key); $value = $this->getValue($key); if ($value === false || $this->serializer === false) { return $value; @@ -147,7 +152,7 @@ abstract class Cache extends Component implements \ArrayAccess { $keyMap = array(); foreach ($keys as $key) { - $keyMap[$key] = $this->keyPrefix . $this->buildKey($key); + $keyMap[$key] = $this->buildKey($key); } $values = $this->getValues(array_values($keyMap)); $results = array(); @@ -192,7 +197,7 @@ abstract class Cache extends Component implements \ArrayAccess } elseif ($this->serializer !== false) { $value = call_user_func($this->serializer[0], array($value, $dependency)); } - $key = $this->keyPrefix . $this->buildKey($key); + $key = $this->buildKey($key); return $this->setValue($key, $value, $expire); } @@ -217,7 +222,7 @@ abstract class Cache extends Component implements \ArrayAccess } elseif ($this->serializer !== false) { $value = call_user_func($this->serializer[0], array($value, $dependency)); } - $key = $this->keyPrefix . $this->buildKey($key); + $key = $this->buildKey($key); return $this->addValue($key, $value, $expire); } @@ -228,7 +233,7 @@ abstract class Cache extends Component implements \ArrayAccess */ public function delete($key) { - $key = $this->keyPrefix . $this->buildKey($key); + $key = $this->buildKey($key); return $this->deleteValue($key); } diff --git a/framework/yii/console/Application.php b/framework/yii/console/Application.php index 31580e8..6cc114a 100644 --- a/framework/yii/console/Application.php +++ b/framework/yii/console/Application.php @@ -113,7 +113,7 @@ class Application extends \yii\base\Application try { return parent::runAction($route, $params); } catch (InvalidRouteException $e) { - throw new Exception(\Yii::t('yii', 'Unknown command "{command}".', array('{command}' => $route))); + throw new Exception(\Yii::t('yii', 'Unknown command "{command}".', array('{command}' => $route)), 0, $e); } } @@ -127,7 +127,6 @@ class Application extends \yii\base\Application 'message' => 'yii\console\controllers\MessageController', 'help' => 'yii\console\controllers\HelpController', 'migrate' => 'yii\console\controllers\MigrateController', - 'app' => 'yii\console\controllers\AppController', 'cache' => 'yii\console\controllers\CacheController', 'asset' => 'yii\console\controllers\AssetController', ); diff --git a/framework/yii/console/Controller.php b/framework/yii/console/Controller.php index fe32daa..22ec39f 100644 --- a/framework/yii/console/Controller.php +++ b/framework/yii/console/Controller.php @@ -36,12 +36,35 @@ class Controller extends \yii\base\Controller public $interactive = true; /** - * @var bool whether to enable ANSI style in output. + * @var boolean whether to enable ANSI style in output. + * Defaults to null meaning auto-detect. + */ + private $_colors; + + /** + * Whether to enable ANSI style in output. + * * Setting this will affect [[ansiFormat()]], [[stdout()]] and [[stderr()]]. * If not set it will be auto detected using [[yii\helpers\Console::streamSupportsAnsiColors()]] with STDOUT * for [[ansiFormat()]] and [[stdout()]] and STDERR for [[stderr()]]. + * @param resource $stream + * @return boolean Whether to enable ANSI style in output. + */ + public function getColors($stream = STDOUT) + { + if ($this->_colors === null) { + return Console::streamSupportsAnsiColors($stream); + } + return $this->_colors; + } + + /** + * Whether to enable ANSI style in output. */ - public $colors; + public function setColors($value) + { + $this->_colors = (bool) $value; + } /** * Runs an action with the specified action ID and parameters. @@ -138,7 +161,7 @@ class Controller extends \yii\base\Controller */ public function ansiFormat($string) { - if ($this->ansi === true || $this->ansi === null && Console::streamSupportsAnsiColors(STDOUT)) { + if ($this->getColors()) { $args = func_get_args(); array_shift($args); $string = Console::ansiFormat($string, $args); @@ -162,7 +185,7 @@ class Controller extends \yii\base\Controller */ public function stdout($string) { - if ($this->ansi === true || $this->ansi === null && Console::streamSupportsAnsiColors(STDOUT)) { + if ($this->getColors()) { $args = func_get_args(); array_shift($args); $string = Console::ansiFormat($string, $args); @@ -186,7 +209,7 @@ class Controller extends \yii\base\Controller */ public function stderr($string) { - if ($this->ansi === true || $this->ansi === null && Console::streamSupportsAnsiColors(STDERR)) { + if ($this->getColors(STDERR)) { $args = func_get_args(); array_shift($args); $string = Console::ansiFormat($string, $args); @@ -259,6 +282,6 @@ class Controller extends \yii\base\Controller */ public function globalOptions() { - return array(); + return array('colors', 'interactive'); } } diff --git a/framework/yii/console/controllers/AppController.php b/framework/yii/console/controllers/AppController.php deleted file mode 100644 index 6baf019..0000000 --- a/framework/yii/console/controllers/AppController.php +++ /dev/null @@ -1,316 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\console\controllers; - -use yii\console\Controller; -use yii\base\Exception; - -/** - * This command creates an Yii Web application at the specified location. - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @author Alexander Makarov <sam@rmcreative.ru> - * @since 2.0 - */ -class AppController extends Controller -{ - private $_rootPath; - private $_config; - - /** - * @var string custom template path. If specified, templates will be - * searched there additionally to `framework/console/webapp`. - */ - public $templatesPath; - - /** - * @var string application type. If not specified default application - * skeleton will be used. - */ - public $type = 'default'; - - public function init() - { - parent::init(); - - if ($this->templatesPath && !is_dir($this->templatesPath)) { - throw new Exception('--templatesPath "'.$this->templatesPath.'" does not exist or can not be read.'); - } - } - - public function globalOptions() - { - return array('templatesPath', 'type'); - } - - public function actionIndex() - { - $this->forward('help/index', array('-args' => array('app/create'))); - } - - /** - * Generates Yii application at the path specified via appPath parameter. - * - * @param string $path the directory where the new application will be created. - * If the directory does not exist, it will be created. After the application - * is created, please make sure the directory has enough permissions. - * - * @throws \yii\base\Exception if path specified is not valid - * @return integer the exit status - */ - public function actionCreate($path) - { - $path = strtr($path, '/\\', DIRECTORY_SEPARATOR); - if (strpos($path, DIRECTORY_SEPARATOR) === false) { - $path = '.'.DIRECTORY_SEPARATOR.$path; - } - $dir = rtrim(realpath(dirname($path)), '\\/'); - if ($dir === false || !is_dir($dir)) { - throw new Exception("The directory '$path' is not valid. Please make sure the parent directory exists."); - } - - if (basename($path) === '.') { - $this->_rootPath = $path = $dir; - } else { - $this->_rootPath = $path = $dir.DIRECTORY_SEPARATOR.basename($path); - } - - if ($this->confirm("Create \"$this->type\" application under '$path'?")) { - $sourceDir = $this->getSourceDir(); - $config = $this->getConfig(); - - $list = $this->buildFileList($sourceDir, $path); - - if (is_array($config)) { - foreach ($config as $file => $settings) { - if (isset($settings['handler'])) { - $list[$file]['callback'] = $settings['handler']; - } - } - } - - $this->copyFiles($list); - - if (is_array($config)) { - foreach ($config as $file => $settings) { - if (isset($settings['permissions'])) { - @chmod($path.'/'.$file, $settings['permissions']); - } - } - } - - echo "\nYour application has been created successfully under {$path}.\n"; - } - } - - /** - * @throws \yii\base\Exception if source directory wasn't located - * @return string - */ - protected function getSourceDir() - { - $customSource = realpath($this->templatesPath.'/'.$this->type); - $defaultSource = realpath($this->getDefaultTemplatesPath().'/'.$this->type); - - if ($customSource) { - return $customSource; - } elseif ($defaultSource) { - return $defaultSource; - } else { - throw new Exception('Unable to locate the source directory for "'.$this->type.'".'); - } - } - - /** - * @return string default templates path - */ - protected function getDefaultTemplatesPath() - { - return realpath(__DIR__.'/../webapp'); - } - - /** - * @return array|null template configuration - */ - protected function getConfig() - { - if ($this->_config === null) { - $this->_config = require $this->getDefaultTemplatesPath() . '/config.php'; - if ($this->templatesPath && file_exists($this->templatesPath)) { - $this->_config = array_merge($this->_config, require $this->templatesPath . '/config.php'); - } - } - if (isset($this->_config[$this->type])) { - return $this->_config[$this->type]; - } - } - - /** - * @param string $source path to source file - * @param string $pathTo path to file we want to get relative path for - * @param string $varName variable name w/o $ to replace value with relative path for - * - * @return string target file contents - */ - public function replaceRelativePath($source, $pathTo, $varName) - { - $content = file_get_contents($source); - $relativeFile = str_replace($this->getSourceDir(), '', $source); - - $relativePath = $this->getRelativePath($pathTo, $this->_rootPath.$relativeFile); - $relativePath = str_replace('\\', '\\\\', $relativePath); - - return preg_replace('/\$'.$varName.'\s*=(.*?);/', "\$".$varName."=$relativePath;", $content); - } - - /** - * @param string $path1 absolute path - * @param string $path2 absolute path - * - * @return string relative path - */ - protected function getRelativePath($path1, $path2) - { - $segs1 = explode(DIRECTORY_SEPARATOR, $path1); - $segs2 = explode(DIRECTORY_SEPARATOR, $path2); - $n1 = count($segs1); - $n2 = count($segs2); - - for ($i = 0; $i < $n1 && $i < $n2; ++$i) { - if ($segs1[$i] !== $segs2[$i]) { - break; - } - } - - if ($i === 0) { - return "'" . $path1 . "'"; - } - $up = ''; - for ($j = $i; $j < $n2 - 1; ++$j) { - $up .= '/..'; - } - for(; $i < $n1 - 1; ++$i) { - $up .= '/' . $segs1[$i]; - } - - return '__DIR__.\'' . $up . '/' . basename($path1) . '\''; - } - - - /** - * 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: - * <ul> - * <li>source: required, the full path of the file/directory to be copied from</li> - * <li>target: required, the full path of the file/directory to be copied to</li> - * <li>callback: optional, the callback to be invoked when copying a file. The callback function - * should be declared as follows: - * <pre> - * function foo($source, $params) - * </pre> - * where $source parameter is the source file path, and the content returned - * by the function will be saved into the target file.</li> - * <li>params: optional, the parameters to be passed to the callback</li> - * </ul> - * @see buildFileList - */ - protected 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)) { - if (!is_dir($target)) { - mkdir($target, 0777, true); - } - 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; - } elseif (!strncasecmp($answer, 'y', 1)) { - echo " overwrite $name\n"; - } elseif (!strncasecmp($answer, 'a', 1)) { - echo " overwrite $name\n"; - $overwriteAll = true; - } else { - echo " skip $name\n"; - continue; - } - } - } - else { - if (!is_dir(dirname($target))) { - mkdir(dirname($target), 0777, true); - } - 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 - * @param array $ignoreFiles list of the names of files that should - * be ignored in list building process. - * @param array $renameMap hash array of file names that should be - * renamed. Example value: array('1.old.txt' => '2.new.txt'). - * @return array the file list (see {@link copyFiles}) - */ - protected function buildFileList($sourceDir, $targetDir, $baseDir='', $ignoreFiles=array(), $renameMap=array()) - { - $list = array(); - $handle = opendir($sourceDir); - while (($file = readdir($handle)) !== false) { - if (in_array($file, array('.', '..', '.svn', '.gitignore', '.hgignore')) || in_array($file, $ignoreFiles)) { - continue; - } - $sourcePath = $sourceDir.DIRECTORY_SEPARATOR.$file; - $targetPath = $targetDir.DIRECTORY_SEPARATOR.strtr($file, $renameMap); - $name = $baseDir === '' ? $file : $baseDir.'/'.$file; - $list[$name] = array( - 'source' => $sourcePath, - 'target' => $targetPath, - ); - if (is_dir($sourcePath)) { - $list = array_merge($list, self::buildFileList($sourcePath, $targetPath, $name, $ignoreFiles, $renameMap)); - } - } - closedir($handle); - return $list; - } -} diff --git a/framework/yii/console/controllers/AssetController.php b/framework/yii/console/controllers/AssetController.php index ca7896c..8e3de29 100644 --- a/framework/yii/console/controllers/AssetController.php +++ b/framework/yii/console/controllers/AssetController.php @@ -517,17 +517,77 @@ EOD */ public function combineCssFiles($inputFiles, $outputFile) { - // todo: adjust url() references in CSS files $content = ''; foreach ($inputFiles as $file) { $content .= "/*** BEGIN FILE: $file ***/\n" - . file_get_contents($file) + . $this->adjustCssUrl(file_get_contents($file), dirname($file), dirname($outputFile)) . "/*** END FILE: $file ***/\n"; } file_put_contents($outputFile, $content); } /** + * Adjusts CSS content allowing URL references pointing to the original resources. + * @param string $cssContent source CSS content. + * @param string $inputFilePath input CSS file name. + * @param string $outputFilePath output CSS file name. + * @return string adjusted CSS content. + */ + protected function adjustCssUrl($cssContent, $inputFilePath, $outputFilePath) + { + $sharedPathParts = array(); + $inputFilePathParts = explode('/', $inputFilePath); + $inputFilePathPartsCount = count($inputFilePathParts); + $outputFilePathParts = explode('/', $outputFilePath); + $outputFilePathPartsCount = count($outputFilePathParts); + for ($i =0; $i < $inputFilePathPartsCount && $i < $outputFilePathPartsCount; $i++) { + if ($inputFilePathParts[$i] == $outputFilePathParts[$i]) { + $sharedPathParts[] = $inputFilePathParts[$i]; + } else { + break; + } + } + $sharedPath = implode('/', $sharedPathParts); + + $inputFileRelativePath = trim(str_replace($sharedPath, '', $inputFilePath), '/'); + $outputFileRelativePath = trim(str_replace($sharedPath, '', $outputFilePath), '/'); + $inputFileRelativePathParts = explode('/', $inputFileRelativePath); + $outputFileRelativePathParts = explode('/', $outputFileRelativePath); + + $callback = function($matches) use ($inputFileRelativePathParts, $outputFileRelativePathParts) { + $fullMatch = $matches[0]; + $inputUrl = $matches[1]; + + if (preg_match('/https?:\/\//is', $inputUrl)) { + return $fullMatch; + } + + $outputUrlParts = array_fill(0, count($outputFileRelativePathParts), '..'); + $outputUrlParts = array_merge($outputUrlParts, $inputFileRelativePathParts); + + if (strpos($inputUrl, '/') !== false) { + $inputUrlParts = explode('/', $inputUrl); + foreach ($inputUrlParts as $key => $inputUrlPart) { + if ($inputUrlPart == '..') { + array_pop($outputUrlParts); + unset($inputUrlParts[$key]); + } + } + $outputUrlParts[] = implode('/', $inputUrlParts); + } else { + $outputUrlParts[] = $inputUrl; + } + $outputUrl = implode('/', $outputUrlParts); + + return str_replace($inputUrl, $outputUrl, $fullMatch); + }; + + $cssContent = preg_replace_callback('/url\(["\']?([^"]*)["\']?\)/is', $callback, $cssContent); + + return $cssContent; + } + + /** * Creates template of configuration file for [[actionCompress]]. * @param string $configFile output file name. */ diff --git a/framework/yii/console/controllers/HelpController.php b/framework/yii/console/controllers/HelpController.php index a729f78..9319163 100644 --- a/framework/yii/console/controllers/HelpController.php +++ b/framework/yii/console/controllers/HelpController.php @@ -13,6 +13,7 @@ use yii\base\InlineAction; use yii\console\Controller; use yii\console\Exception; use yii\console\Request; +use yii\helpers\Console; use yii\helpers\Inflector; /** @@ -56,7 +57,7 @@ class HelpController extends Controller $result = Yii::$app->createController($command); if ($result === false) { throw new Exception(Yii::t('yii', 'No help for unknown command "{command}".', array( - '{command}' => $command, + '{command}' => $this->ansiFormat($command, Console::FG_YELLOW), ))); } @@ -143,14 +144,15 @@ class HelpController extends Controller { $commands = $this->getCommands(); if (!empty($commands)) { - echo "The following commands are available:\n\n"; + $this->stdout("\nThe following commands are available:\n\n", Console::BOLD); foreach ($commands as $command) { - echo "* $command\n"; + echo "- " . $this->ansiFormat($command, Console::FG_YELLOW) . "\n"; } - echo "\nTo see the help of each command, enter:\n"; - echo "\n yii help <command-name>\n\n"; + $this->stdout("\nTo see the help of each command, enter:\n", Console::BOLD); + echo "\n yii " . $this->ansiFormat('help', Console::FG_YELLOW) . ' ' + . $this->ansiFormat('<command-name>', Console::FG_CYAN) . "\n\n"; } else { - echo "\nNo commands are found.\n"; + $this->stdout("\nNo commands are found.\n\n", Console::BOLD); } } @@ -167,19 +169,18 @@ class HelpController extends Controller } if ($comment !== '') { - echo "\nDESCRIPTION\n"; - echo "\n" . $comment . "\n\n"; + $this->stdout("\nDESCRIPTION\n", Console::BOLD); + echo "\n" . Console::renderColoredString($comment) . "\n\n"; } $actions = $this->getActions($controller); if (!empty($actions)) { - echo "\nSUB-COMMANDS\n\n"; + $this->stdout("\nSUB-COMMANDS\n\n", Console::BOLD); $prefix = $controller->getUniqueId(); foreach ($actions as $action) { + echo '- ' . $this->ansiFormat($prefix.'/'.$action, Console::FG_YELLOW); if ($action === $controller->defaultAction) { - echo "* $prefix/$action (default)"; - } else { - echo "* $prefix/$action"; + $this->stdout(' (default)', Console::FG_GREEN); } $summary = $this->getActionSummary($controller, $action); if ($summary !== '') { @@ -187,8 +188,9 @@ class HelpController extends Controller } echo "\n"; } - echo "\n\nTo see the detailed information about individual sub-commands, enter:\n"; - echo "\n yii help <sub-command>\n\n"; + echo "\nTo see the detailed information about individual sub-commands, enter:\n"; + echo "\n yii " . $this->ansiFormat('help', Console::FG_YELLOW) . ' ' + . $this->ansiFormat('<sub-command>', Console::FG_CYAN) . "\n\n"; } } @@ -253,25 +255,25 @@ class HelpController extends Controller $options = $this->getOptionHelps($controller); if ($tags['description'] !== '') { - echo "\nDESCRIPTION"; - echo "\n\n" . $tags['description'] . "\n\n"; + $this->stdout("\nDESCRIPTION\n", Console::BOLD); + echo "\n" . Console::renderColoredString($tags['description']) . "\n\n"; } - echo "\nUSAGE\n\n"; + $this->stdout("\nUSAGE\n\n", Console::BOLD); if ($action->id === $controller->defaultAction) { - echo 'yii ' . $controller->getUniqueId(); + echo 'yii ' . $this->ansiFormat($controller->getUniqueId(), Console::FG_YELLOW); } else { - echo "yii " . $action->getUniqueId(); + echo 'yii ' . $this->ansiFormat($action->getUniqueId(), Console::FG_YELLOW); } list ($required, $optional) = $this->getArgHelps($method, isset($tags['param']) ? $tags['param'] : array()); - if (!empty($required)) { - echo ' <' . implode('> <', array_keys($required)) . '>'; + foreach ($required as $arg => $description) { + $this->stdout(' <' . $arg . '>', Console::FG_CYAN); } - if (!empty($optional)) { - echo ' [' . implode('] [', array_keys($optional)) . ']'; + foreach ($optional as $arg => $description) { + $this->stdout(' [' . $arg . ']', Console::FG_CYAN); } if (!empty($options)) { - echo ' [...options...]'; + $this->stdout(' [...options...]', Console::FG_RED); } echo "\n\n"; @@ -281,7 +283,7 @@ class HelpController extends Controller $options = $this->getOptionHelps($controller); if (!empty($options)) { - echo "\nOPTIONS\n\n"; + $this->stdout("\nOPTIONS\n\n", Console::BOLD); echo implode("\n\n", $options) . "\n\n"; } } @@ -310,9 +312,9 @@ class HelpController extends Controller $comment = $tag; } if ($param->isDefaultValueAvailable()) { - $optional[$name] = $this->formatOptionHelp('* ' . $name, false, $type, $param->getDefaultValue(), $comment); + $optional[$name] = $this->formatOptionHelp('- ' . $this->ansiFormat($name, Console::FG_CYAN), false, $type, $param->getDefaultValue(), $comment); } else { - $required[$name] = $this->formatOptionHelp('* ' . $name, true, $type, null, $comment); + $required[$name] = $this->formatOptionHelp('- ' . $this->ansiFormat($name, Console::FG_CYAN), true, $type, null, $comment); } } @@ -352,9 +354,9 @@ class HelpController extends Controller $type = null; $comment = $doc; } - $options[$name] = $this->formatOptionHelp('--' . $name, false, $type, $defaultValue, $comment); + $options[$name] = $this->formatOptionHelp($this->ansiFormat('--' . $name, Console::FG_RED), false, $type, $defaultValue, $comment); } else { - $options[$name] = $this->formatOptionHelp('--' . $name, false, null, $defaultValue, ''); + $options[$name] = $this->formatOptionHelp($this->ansiFormat('--' . $name, Console::FG_RED), false, null, $defaultValue, ''); } } ksort($options); diff --git a/framework/yii/console/controllers/MessageController.php b/framework/yii/console/controllers/MessageController.php index 418062a..715fb5c 100644 --- a/framework/yii/console/controllers/MessageController.php +++ b/framework/yii/console/controllers/MessageController.php @@ -115,6 +115,13 @@ class MessageController extends Controller } } + /** + * Extracts messages from a file + * + * @param string $fileName name of the file to extract messages from + * @param string $translator name of the function used to translate messages + * @return array + */ protected function extractMessages($fileName, $translator) { echo "Extracting messages from $fileName...\n"; @@ -135,6 +142,15 @@ class MessageController extends Controller return $messages; } + /** + * Writes messages into file + * + * @param array $messages + * @param string $fileName name of the file to write to + * @param boolean $overwrite if existing file should be overwritten without backup + * @param boolean $removeOld if obsolete translations should be removed + * @param boolean $sort if translations should be sorted + */ protected function generateMessageFile($messages, $fileName, $overwrite, $removeOld, $sort) { echo "Saving messages to $fileName..."; diff --git a/framework/yii/console/controllers/MigrateController.php b/framework/yii/console/controllers/MigrateController.php index 0acc672..d3eb257 100644 --- a/framework/yii/console/controllers/MigrateController.php +++ b/framework/yii/console/controllers/MigrateController.php @@ -115,11 +115,13 @@ class MigrateController extends Controller } $this->migrationPath = $path; - if (is_string($this->db)) { - $this->db = Yii::$app->getComponent($this->db); - } - if (!$this->db instanceof Connection) { - throw new Exception("The 'db' option must refer to the application component ID of a DB connection."); + if($action->id!=='create') { + if (is_string($this->db)) { + $this->db = Yii::$app->getComponent($this->db); + } + if (!$this->db instanceof Connection) { + throw new Exception("The 'db' option must refer to the application component ID of a DB connection."); + } } $version = Yii::getVersion(); @@ -572,7 +574,7 @@ class MigrateController extends Controller */ protected function getMigrationHistory($limit) { - if ($this->db->schema->getTableSchema($this->migrationTable) === null) { + if ($this->db->schema->getTableSchema($this->migrationTable, true) === null) { $this->createMigrationHistoryTable(); } $query = new Query; diff --git a/framework/yii/db/ActiveRecord.php b/framework/yii/db/ActiveRecord.php index dd90782..58411f0 100644 --- a/framework/yii/db/ActiveRecord.php +++ b/framework/yii/db/ActiveRecord.php @@ -1152,7 +1152,7 @@ class ActiveRecord extends Model /** * Creates an active record object using a row of data. * This method is called by [[ActiveQuery]] to populate the query results - * into Active Records. + * into Active Records. It is not meant to be used to create new records. * @param array $row attribute values (name => value) * @return ActiveRecord the newly created active record. */ @@ -1215,7 +1215,7 @@ class ActiveRecord extends Model return $relation; } } catch (UnknownMethodException $e) { - throw new InvalidParamException(get_class($this) . ' has no relation named "' . $name . '".'); + throw new InvalidParamException(get_class($this) . ' has no relation named "' . $name . '".', 0, $e); } } diff --git a/framework/yii/db/Command.php b/framework/yii/db/Command.php index e05bde7..17accf4 100644 --- a/framework/yii/db/Command.php +++ b/framework/yii/db/Command.php @@ -148,7 +148,7 @@ class Command extends \yii\base\Component } catch (\Exception $e) { Yii::error($e->getMessage() . "\nFailed to prepare SQL: $sql", __METHOD__); $errorInfo = $e instanceof \PDOException ? $e->errorInfo : null; - throw new Exception($e->getMessage(), $errorInfo, (int)$e->getCode()); + throw new Exception($e->getMessage(), $errorInfo, (int)$e->getCode(), $e); } } } @@ -298,7 +298,7 @@ class Command extends \yii\base\Component Yii::error("$message\nFailed to execute SQL: $rawSql", __METHOD__); $errorInfo = $e instanceof \PDOException ? $e->errorInfo : null; - throw new Exception($message, $errorInfo, (int)$e->getCode()); + throw new Exception($message, $errorInfo, (int)$e->getCode(), $e); } } @@ -391,12 +391,12 @@ class Command extends \yii\base\Component } if (isset($cache) && $cache instanceof Cache) { - $cacheKey = $cache->buildKey(array( + $cacheKey = array( __CLASS__, $db->dsn, $db->username, $rawSql, - )); + ); if (($result = $cache->get($cacheKey)) !== false) { Yii::trace('Query result served from cache', __METHOD__); return $result; @@ -433,7 +433,7 @@ class Command extends \yii\base\Component $message = $e->getMessage(); Yii::error("$message\nCommand::$method() failed: $rawSql", __METHOD__); $errorInfo = $e instanceof \PDOException ? $e->errorInfo : null; - throw new Exception($message, $errorInfo, (int)$e->getCode()); + throw new Exception($message, $errorInfo, (int)$e->getCode(), $e); } } diff --git a/framework/yii/db/Connection.php b/framework/yii/db/Connection.php index d956691..e14eeb7 100644 --- a/framework/yii/db/Connection.php +++ b/framework/yii/db/Connection.php @@ -319,7 +319,7 @@ class Connection extends Component Yii::endProfile($token, __METHOD__); Yii::error("Failed to open DB connection ({$this->dsn}): " . $e->getMessage(), __METHOD__); $message = YII_DEBUG ? 'Failed to open DB connection: ' . $e->getMessage() : 'Failed to open DB connection.'; - throw new Exception($message, $e->errorInfo, (int)$e->getCode()); + throw new Exception($message, $e->errorInfo, (int)$e->getCode(), $e); } } } diff --git a/framework/yii/db/Schema.php b/framework/yii/db/Schema.php index 9538e4c..c961244 100644 --- a/framework/yii/db/Schema.php +++ b/framework/yii/db/Schema.php @@ -89,7 +89,7 @@ abstract class Schema extends \yii\base\Object /** @var $cache Cache */ $cache = is_string($db->schemaCache) ? Yii::$app->getComponent($db->schemaCache) : $db->schemaCache; if ($cache instanceof Cache) { - $key = $this->getCacheKey($cache, $name); + $key = $this->getCacheKey($name); if ($refresh || ($table = $cache->get($key)) === false) { $table = $this->loadTableSchema($realName); if ($table !== null) { @@ -104,18 +104,17 @@ abstract class Schema extends \yii\base\Object /** * Returns the cache key for the specified table name. - * @param Cache $cache the cache component * @param string $name the table name - * @return string the cache key + * @return mixed the cache key */ - public function getCacheKey($cache, $name) + public function getCacheKey($name) { - return $cache->buildKey(array( + return array( __CLASS__, $this->db->dsn, $this->db->username, $name, - )); + ); } /** @@ -178,7 +177,7 @@ abstract class Schema extends \yii\base\Object $cache = is_string($this->db->schemaCache) ? Yii::$app->getComponent($this->db->schemaCache) : $this->db->schemaCache; if ($this->db->enableSchemaCache && $cache instanceof Cache) { foreach ($this->_tables as $name => $table) { - $cache->delete($this->getCacheKey($cache, $name)); + $cache->delete($this->getCacheKey($name)); } } $this->_tableNames = array(); diff --git a/framework/yii/db/mssql/Schema.php b/framework/yii/db/mssql/Schema.php index 1991542..ad0f7d4 100644 --- a/framework/yii/db/mssql/Schema.php +++ b/framework/yii/db/mssql/Schema.php @@ -241,15 +241,11 @@ SQL; } foreach ($columns as $column) { $column = $this->loadColumnSchema($column); - if (is_array($table->primaryKey)) { - foreach ($table->primaryKey as $primaryKeyColumn) { - if (strcasecmp($column->name, $primaryKeyColumn) === 0) { - $column->isPrimaryKey = true; - break; - } + foreach ($table->primaryKey as $primaryKey) { + if (strcasecmp($column->name, $primaryKey) === 0) { + $column->isPrimaryKey = true; + break; } - } else { - $column->isPrimaryKey = strcasecmp($column->name, $table->primaryKey) === 0; } if ($column->isPrimaryKey && $column->autoIncrement) { $table->sequenceName = ''; diff --git a/framework/yii/helpers/Purifier.php b/framework/yii/helpers/HtmlPurifier.php similarity index 87% rename from framework/yii/helpers/Purifier.php rename to framework/yii/helpers/HtmlPurifier.php index b659531..1173091 100644 --- a/framework/yii/helpers/Purifier.php +++ b/framework/yii/helpers/HtmlPurifier.php @@ -8,18 +8,18 @@ namespace yii\helpers; /** - * Purifier provides an ability to clean up HTML from any harmful code. + * HtmlPurifier provides an ability to clean up HTML from any harmful code. * * Basic usage is the following: * * ```php - * $my_html = Purifier::process($my_text); + * echo HtmlPurifier::process($html); * ``` * * If you want to configure it: * * ```php - * $my_html = Purifier::process($my_text, array( + * echo HtmlPurifier::process($html, array( * 'Attr.EnableID' => true, * )); * ``` @@ -29,6 +29,6 @@ namespace yii\helpers; * @author Alexander Makarov <sam@rmcreative.ru> * @since 2.0 */ -class Purifier extends base\Purifier +class HtmlPurifier extends base\HtmlPurifier { } diff --git a/framework/yii/helpers/base/Console.php b/framework/yii/helpers/base/Console.php index b611919..6ad0b7b 100644 --- a/framework/yii/helpers/base/Console.php +++ b/framework/yii/helpers/base/Console.php @@ -45,6 +45,7 @@ class Console const BG_CYAN = 46; const BG_GREY = 47; + const RESET = 0; const NORMAL = 0; const BOLD = 1; const ITALIC = 3; @@ -240,34 +241,41 @@ class Console } /** - * Sets the ANSI format for any text that is printed afterwards. + * Returns the ANSI format code. * - * You can pass any of the FG_*, BG_* and TEXT_* constants and also [[xterm256ColorFg]] and [[xterm256ColorBg]]. + * @param array $format You can pass any of the FG_*, BG_* and TEXT_* constants and also [[xtermFgColor]] and [[xtermBgColor]]. * TODO: documentation + * @return string */ - public static function ansiFormatBegin() + public static function ansiFormatCode($format) { - echo "\033[" . implode(';', func_get_args()) . 'm'; + return "\033[" . implode(';', $format) . 'm'; } /** - * Resets any ANSI format set by previous method [[ansiFormatBegin()]] - * Any output after this is will have default text style. + * Sets the ANSI format for any text that is printed afterwards. + * + * @param array $format You can pass any of the FG_*, BG_* and TEXT_* constants and also [[xtermFgColor]] and [[xtermBgColor]]. + * TODO: documentation + * @see ansiFormatEnd() */ - public static function ansiFormatReset() + public static function beginAnsiFormat($format) { - echo "\033[0m"; + echo "\033[" . implode(';', $format) . 'm'; } /** - * Returns the ANSI format code. + * Resets any ANSI format set by previous method [[ansiFormatBegin()]] + * Any output after this is will have default text style. + * This is equal to * - * You can pass any of the FG_*, BG_* and TEXT_* constants and also [[xterm256ColorFg]] and [[xterm256ColorBg]]. - * TODO: documentation + * ```php + * echo Console::ansiFormatCode(array(Console::RESET)) + * ``` */ - public static function ansiFormatCode($format) + public static function endAnsiFormat() { - return "\033[" . implode(';', $format) . 'm'; + echo "\033[0m"; } /** @@ -275,7 +283,7 @@ class Console * * @param string $string the string to be formatted * @param array $format array containing formatting values. - * You can pass any of the FG_*, BG_* and TEXT_* constants and also [[xterm256ColorFg]] and [[xterm256ColorBg]]. + * You can pass any of the FG_*, BG_* and TEXT_* constants and also [[xtermFgColor]] and [[xtermBgColor]]. * @return string */ public static function ansiFormat($string, $format=array()) @@ -284,15 +292,32 @@ class Console return "\033[0m" . ($code !== '' ? "\033[" . $code . "m" : '') . $string . "\033[0m"; } - //const COLOR_XTERM256 = 38;// http://en.wikipedia.org/wiki/Talk:ANSI_escape_code#xterm-256colors - public static function xterm256ColorFg($i) // TODO naming! + /** + * Returns the ansi format code for xterm foreground color. + * You can pass the returnvalue of this to one of the formatting methods: + * [[ansiFormat]], [[ansiFormatCode]], [[beginAnsiFormat]] + * + * @param integer $colorCode xterm color code + * @return string + * @see http://en.wikipedia.org/wiki/Talk:ANSI_escape_code#xterm-256colors + */ + public static function xtermFgColor($colorCode) { - return '38;5;' . $i; + return '38;5;' . $colorCode; } - public static function xterm256ColorBg($i) // TODO naming! + /** + * Returns the ansi format code for xterm foreground color. + * You can pass the returnvalue of this to one of the formatting methods: + * [[ansiFormat]], [[ansiFormatCode]], [[beginAnsiFormat]] + * + * @param integer $colorCode xterm color code + * @return string + * @see http://en.wikipedia.org/wiki/Talk:ANSI_escape_code#xterm-256colors + */ + public static function xtermBgColor($colorCode) { - return '48;5;' . $i; + return '48;5;' . $colorCode; } /** @@ -303,7 +328,7 @@ class Console */ public static function stripAnsiFormat($string) { - return preg_replace('/\033\[[\d;]+m/', '', $string); // TODO currently only strips color + return preg_replace('/\033\[[\d;?]*\w/', '', $string); } // TODO refactor and review @@ -418,10 +443,11 @@ class Console } /** - * TODO syntax copied from https://github.com/pear/Console_Color2/blob/master/Console/Color2.php + * Converts a string to ansi formatted by replacing patterns like %y (for yellow) with ansi control codes * - * Converts colorcodes in the format %y (for yellow) into ansi-control - * codes. The conversion table is: ('bold' meaning 'light' on some + * // TODO documentation + * Uses almost the same syntax as https://github.com/pear/Console_Color2/blob/master/Console/Color2.php + * The conversion table is: ('bold' meaning 'light' on some * terminals). It's almost the same conversion table irssi uses. * <pre> * text text background @@ -450,7 +476,6 @@ class Console * * @param string $string String to convert * @param bool $colored Should the string be colored? - * * @return string */ public static function renderColoredString($string, $colored = true) @@ -508,22 +533,23 @@ class Console } /** - * Escapes % so they don't get interpreted as color codes - * - * @param string $string String to escape - * - * @access public - * @return string - */ + * Escapes % so they don't get interpreted as color codes when + * the string is parsed by [[renderColoredString]] + * + * @param string $string String to escape + * + * @access public + * @return string + */ public static function escape($string) { return str_replace('%', '%%', $string); } /** - * Returns true if the stream supports colorization. ANSI colors is disabled if not supported by the stream. + * Returns true if the stream supports colorization. ANSI colors are disabled if not supported by the stream. * - * - windows without asicon + * - windows without ansicon * - not tty consoles * * @param mixed $stream @@ -532,7 +558,7 @@ class Console public static function streamSupportsAnsiColors($stream) { return DIRECTORY_SEPARATOR == '\\' - ? null !== getenv('ANSICON') + ? getenv('ANSICON') !== false || getenv('ConEmuANSI') === 'ON' : function_exists('posix_isatty') && @posix_isatty($stream); } @@ -542,18 +568,50 @@ class Console */ public static function isRunningOnWindows() { - return strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'; + return DIRECTORY_SEPARATOR == '\\'; } /** * Usage: list($w, $h) = ConsoleHelper::getScreenSize(); * - * @return array + * @param bool $refresh whether to force checking and not re-use cached size value. + * This is useful to detect changing window size while the application is running but may + * not get up to date values on every terminal. + * @return array|boolean An array of ($width, $height) or false when it was not able to determine size. */ - public static function getScreenSize() + public static function getScreenSize($refresh = false) { - // TODO implement - return array(150, 50); + static $size; + if ($size !== null && !$refresh) { + return $size; + } + + if (static::isRunningOnWindows()) { + $output = array(); + exec('mode con', $output); + if(isset($output) && strpos($output[1], 'CON')!==false) { + return $size = array((int)preg_replace('~[^0-9]~', '', $output[3]), (int)preg_replace('~[^0-9]~', '', $output[4])); + } + } else { + + // try stty if available + $stty = array(); + if (exec('stty -a 2>&1', $stty) && preg_match('/rows\s+(\d+);\s*columns\s+(\d+);/mi', implode(' ', $stty), $matches)) { + return $size = array($matches[2], $matches[1]); + } + + // fallback to tput, which may not be updated on terminal resize + if (($width = (int) exec('tput cols 2>&1')) > 0 && ($height = (int) exec('tput lines 2>&1')) > 0) { + return $size = array($width, $height); + } + + // fallback to ENV variables, which may not be updated on terminal resize + if (($width = (int) getenv('COLUMNS')) > 0 && ($height = (int) getenv('LINES')) > 0) { + return $size = array($width, $height); + } + } + + return $size = false; } /** @@ -607,27 +665,23 @@ class Console /** * Prints text to STDOUT appended with a carriage return (PHP_EOL). * - * @param string $text - * @param bool $raw - * + * @param string $string * @return mixed Number of bytes printed or bool false on error */ - public static function output($text = null) + public static function output($string = null) { - return static::stdout($text . PHP_EOL); + return static::stdout($string . PHP_EOL); } /** * Prints text to STDERR appended with a carriage return (PHP_EOL). * - * @param string $text - * @param bool $raw - * + * @param string $string * @return mixed Number of bytes printed or false on error */ - public static function error($text = null) + public static function error($string = null) { - return static::stderr($text . PHP_EOL); + return static::stderr($string . PHP_EOL); } /** diff --git a/framework/yii/helpers/base/Purifier.php b/framework/yii/helpers/base/HtmlPurifier.php similarity index 61% rename from framework/yii/helpers/base/Purifier.php rename to framework/yii/helpers/base/HtmlPurifier.php index 2c5d334..799dabf 100644 --- a/framework/yii/helpers/base/Purifier.php +++ b/framework/yii/helpers/base/HtmlPurifier.php @@ -7,29 +7,22 @@ namespace yii\helpers\base; /** - * Purifier provides an ability to clean up HTML from any harmful code. + * HtmlPurifier is the concrete implementation of the [[yii\helpers\HtmlPurifier]] class. * - * Basic usage is the following: - * - * ```php - * $my_html = Purifier::process($my_text); - * ``` - * - * If you want to configure it: - * - * ```php - * $my_html = Purifier::process($my_text, array( - * 'Attr.EnableID' => true, - * )); - * ``` - * - * For more details please refer to HTMLPurifier documentation](http://htmlpurifier.org/). + * You should use [[yii\helpers\HtmlPurifier]] instead of this class in your application. * * @author Alexander Makarov <sam@rmcreative.ru> * @since 2.0 */ -class Purifier +class HtmlPurifier { + /** + * Passes markup through HTMLPurifier making it safe to output to end user + * + * @param string $content + * @param array|null $config + * @return string + */ public static function process($content, $config = null) { $purifier=\HTMLPurifier::instance($config); diff --git a/framework/yii/helpers/base/Markdown.php b/framework/yii/helpers/base/Markdown.php index 2e14da5..3e69015 100644 --- a/framework/yii/helpers/base/Markdown.php +++ b/framework/yii/helpers/base/Markdown.php @@ -37,6 +37,13 @@ class Markdown */ protected static $markdown; + /** + * Converts markdown into HTML + * + * @param string $content + * @param array $config + * @return string + */ public static function process($content, $config = array()) { if (static::$markdown === null) { diff --git a/framework/yii/helpers/base/StringHelper.php b/framework/yii/helpers/base/StringHelper.php index 5b854ac..5134bf6 100644 --- a/framework/yii/helpers/base/StringHelper.php +++ b/framework/yii/helpers/base/StringHelper.php @@ -18,20 +18,18 @@ class StringHelper { /** * Returns the number of bytes in the given string. - * This method ensures the string is treated as a byte array. - * It will use `mb_strlen()` if it is available. + * This method ensures the string is treated as a byte array by using `mb_strlen()`. * @param string $string the string being measured for length * @return integer the number of bytes in the given string. */ public static function strlen($string) { - return function_exists('mb_strlen') ? mb_strlen($string, '8bit') : strlen($string); + return mb_strlen($string, '8bit'); } /** * Returns the portion of string specified by the start and length parameters. - * This method ensures the string is treated as a byte array. - * It will use `mb_substr()` if it is available. + * This method ensures the string is treated as a byte array by using `mb_substr()`. * @param string $string the input string. Must be one character or longer. * @param integer $start the starting position * @param integer $length the desired portion length @@ -40,15 +38,14 @@ class StringHelper */ public static function substr($string, $start, $length) { - return function_exists('mb_substr') ? mb_substr($string, $start, $length, '8bit') : substr($string, $start, $length); + return mb_substr($string, $start, $length, '8bit'); } /** * Returns the trailing name component of a path. * This method does the same as the php function basename() except that it will * always use \ and / as directory separators, independent of the operating system. - * Note: basename() operates naively on the input string, and is not aware of the - * actual filesystem, or path components such as "..". + * Note: this method is not aware of the actual filesystem, or path components such as "..". * @param string $path A path string. * @param string $suffix If the name component ends in suffix this will also be cut off. * @return string the trailing name component of the given path. diff --git a/framework/yii/i18n/GettextMessageSource.php b/framework/yii/i18n/GettextMessageSource.php index 0eb7cb3..5e29487 100644 --- a/framework/yii/i18n/GettextMessageSource.php +++ b/framework/yii/i18n/GettextMessageSource.php @@ -31,6 +31,15 @@ class GettextMessageSource extends MessageSource */ public $useBigEndian = false; + /** + * Loads the message translation for the specified language and category. + * Child classes should override this method to return the message translations of + * the specified language and category. + * @param string $category the message category + * @param string $language the target language + * @return array the loaded messages. The keys are original messages, and the values + * are translated messages. + */ protected function loadMessages($category, $language) { $messageFile = Yii::getAlias($this->basePath) . '/' . $language . '/' . $this->catalog; diff --git a/framework/yii/jui/Accordion.php b/framework/yii/jui/Accordion.php index 6c5dd97..f36c981 100644 --- a/framework/yii/jui/Accordion.php +++ b/framework/yii/jui/Accordion.php @@ -8,7 +8,7 @@ namespace yii\jui; use yii\base\InvalidConfigException; -use yii\helpers\base\ArrayHelper; +use yii\helpers\ArrayHelper; use yii\helpers\Html; /** @@ -25,11 +25,27 @@ use yii\helpers\Html; * ), * array( * 'header' => 'Section 2', - * 'headerOptions' => array(...), + * 'headerOptions' => array( + * 'tag' => 'h3', + * ), * 'content' => 'Sed non urna. Phasellus eu ligula. Vestibulum sit amet purus...', - * 'options' => array(...), + * 'options' => array( + * 'tag' => 'div', + * ), * ), * ), + * 'options' => array( + * 'tag' => 'div', + * ), + * 'itemOptions' => array( + * 'tag' => 'div', + * ), + * 'headerOptions' => array( + * 'tag' => 'h3', + * ), + * 'clientOptions' => array( + * 'collapsible' => false, + * ), * )); * ``` * @@ -40,23 +56,40 @@ use yii\helpers\Html; class Accordion extends Widget { /** - * @var array list of sections in the accordion widget. Each array element represents a single - * section with the following structure: + * @var array the HTML attributes for the widget container tag. The following special options are recognized: + * + * - tag: string, defaults to "div", the tag name of the container tag of this widget + */ + public $options = array(); + /** + * @var array list of collapsible items. Each item can be an array of the following structure: * - * ```php + * ~~~ * array( - * // required, the header (HTML) of the section - * 'header' => 'Section label', - * // required, the content (HTML) of the section - * 'content' => 'Mauris mauris ante, blandit et, ultrices a, suscipit eget...', - * // optional the HTML attributes of the section content container - * 'options'=> array(...), - * // optional the HTML attributes of the section header container - * 'headerOptions'=> array(...), + * 'header' => 'Item header', + * 'content' => 'Item content', + * // the HTML attributes of the item header container tag. This will overwrite "headerOptions". + * 'headerOptions' => array(), + * // the HTML attributes of the item container tag. This will overwrite "itemOptions". + * 'options' => array(), * ) - * ``` + * ~~~ */ public $items = array(); + /** + * @var array list of HTML attributes for the item container tags. This will be overwritten + * by the "options" set in individual [[items]]. The following special options are recognized: + * + * - tag: string, defaults to "div", the tag name of the item container tags. + */ + public $itemOptions = array(); + /** + * @var array list of HTML attributes for the item header container tags. This will be overwritten + * by the "headerOptions" set in individual [[items]]. The following special options are recognized: + * + * - tag: string, defaults to "h3", the tag name of the item container tags. + */ + public $headerOptions = array(); /** @@ -64,20 +97,22 @@ class Accordion extends Widget */ public function run() { - echo Html::beginTag('div', $this->options) . "\n"; - echo $this->renderSections() . "\n"; - echo Html::endTag('div') . "\n"; + $options = $this->options; + $tag = ArrayHelper::remove($options, 'tag', 'div'); + echo Html::beginTag($tag, $options) . "\n"; + echo $this->renderItems() . "\n"; + echo Html::endTag($tag) . "\n"; $this->registerWidget('accordion'); } /** - * Renders collapsible sections as specified on [[items]]. + * Renders collapsible items as specified on [[items]]. * @return string the rendering result. * @throws InvalidConfigException. */ - protected function renderSections() + protected function renderItems() { - $sections = array(); + $items = array(); foreach ($this->items as $item) { if (!isset($item['header'])) { throw new InvalidConfigException("The 'header' option is required."); @@ -85,12 +120,14 @@ class Accordion extends Widget if (!isset($item['content'])) { throw new InvalidConfigException("The 'content' option is required."); } - $headerOptions = ArrayHelper::getValue($item, 'headerOptions', array()); - $sections[] = Html::tag('h3', $item['header'], $headerOptions); - $options = ArrayHelper::getValue($item, 'options', array()); - $sections[] = Html::tag('div', $item['content'], $options);; + $headerOptions = array_merge($this->headerOptions, ArrayHelper::getValue($item, 'headerOptions', array())); + $headerTag = ArrayHelper::remove($headerOptions, 'tag', 'h3'); + $items[] = Html::tag($headerTag, $item['header'], $headerOptions); + $options = array_merge($this->itemOptions, ArrayHelper::getValue($item, 'options', array())); + $tag = ArrayHelper::remove($options, 'tag', 'div'); + $items[] = Html::tag($tag, $item['content'], $options);; } - return implode("\n", $sections); + return implode("\n", $items); } } diff --git a/framework/yii/jui/AutoComplete.php b/framework/yii/jui/AutoComplete.php index f5bbae9..44ca23d 100644 --- a/framework/yii/jui/AutoComplete.php +++ b/framework/yii/jui/AutoComplete.php @@ -8,8 +8,6 @@ namespace yii\jui; use Yii; -use yii\base\InvalidConfigException; -use yii\base\Model; use yii\helpers\Html; /** @@ -42,51 +40,27 @@ use yii\helpers\Html; * @author Alexander Kochetov <creocoder@gmail.com> * @since 2.0 */ -class AutoComplete extends Widget +class AutoComplete extends InputWidget { /** - * @var \yii\base\Model the data model that this widget is associated with. - */ - public $model; - /** - * @var string the model attribute that this widget is associated with. - */ - public $attribute; - /** - * @var string the input name. This must be set if [[model]] and [[attribute]] are not set. - */ - public $name; - /** - * @var string the input value. - */ - public $value; - - - /** * Renders the widget. */ public function run() { - echo $this->renderField(); + echo $this->renderWidget(); $this->registerWidget('autocomplete'); } /** - * Renders the AutoComplete field. If [[model]] has been specified then it will render an active field. - * If [[model]] is null or not from an [[Model]] instance, then the field will be rendered according to - * the [[name]] attribute. + * Renders the AutoComplete widget. * @return string the rendering result. - * @throws InvalidConfigException when none of the required attributes are set to render the textInput. - * That is, if [[model]] and [[attribute]] are not set, then [[name]] is required. */ - public function renderField() + public function renderWidget() { - if ($this->model instanceof Model && $this->attribute !== null) { + if ($this->hasModel()) { return Html::activeTextInput($this->model, $this->attribute, $this->options); - } elseif ($this->name !== null) { - return Html::textInput($this->name, $this->value, $this->options); } else { - throw new InvalidConfigException("Either 'name' or 'model' and 'attribute' properties must be specified."); + return Html::textInput($this->name, $this->value, $this->options); } } } diff --git a/framework/yii/jui/DatePicker.php b/framework/yii/jui/DatePicker.php new file mode 100644 index 0000000..1138b73 --- /dev/null +++ b/framework/yii/jui/DatePicker.php @@ -0,0 +1,99 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\jui; + +use Yii; +use yii\helpers\Html; + +/** + * DatePicker renders an datepicker jQuery UI widget. + * + * For example: + * + * ```php + * echo DatePicker::widget(array( + * 'language' => 'ru', + * 'model' => $model, + * 'attribute' => 'country', + * 'clientOptions' => array( + * 'dateFormat' => 'yy-mm-dd', + * ), + * )); + * ``` + * + * The following example will use the name property instead: + * + * ```php + * echo DatePicker::widget(array( + * 'language' => 'ru', + * 'name' => 'country', + * 'clientOptions' => array( + * 'dateFormat' => 'yy-mm-dd', + * ), + * )); + *``` + * + * @see http://api.jqueryui.com/datepicker/ + * @author Alexander Kochetov <creocoder@gmail.com> + * @since 2.0 + */ +class DatePicker extends InputWidget +{ + /** + * @var string the locale ID (eg 'fr', 'de') for the language to be used by the date picker. + * If this property set to false, I18N will not be involved. That is, the date picker will show in English. + */ + public $language = false; + /** + * @var boolean If true, shows the widget as an inline calendar and the input as a hidden field. + */ + public $inline = false; + + + /** + * Renders the widget. + */ + public function run() + { + echo $this->renderWidget() . "\n"; + $this->registerWidget('datepicker'); + if ($this->language !== false) { + $this->getView()->registerAssetBundle("yii/jui/datepicker/i18n/$this->language"); + } + } + + /** + * Renders the DatePicker widget. + * @return string the rendering result. + */ + protected function renderWidget() + { + $contents = array(); + + if ($this->inline === false) { + if ($this->hasModel()) { + $contents[] = Html::activeTextInput($this->model, $this->attribute, $this->options); + } else { + $contents[] = Html::textInput($this->name, $this->value, $this->options); + } + } else { + if ($this->hasModel()) { + $contents[] = Html::activeHiddenInput($this->model, $this->attribute, $this->options); + $this->clientOptions['defaultDate'] = $this->model->{$this->attribute}; + } else { + $contents[] = Html::hiddenInput($this->name, $this->value, $this->options); + $this->clientOptions['defaultDate'] = $this->value; + } + $this->clientOptions['altField'] = '#' . $this->options['id']; + $this->options['id'] .= '-container'; + $contents[] = Html::tag('div', null, $this->options); + } + + return implode("\n", $contents); + } +} diff --git a/framework/yii/jui/Dialog.php b/framework/yii/jui/Dialog.php new file mode 100644 index 0000000..f4b3b12 --- /dev/null +++ b/framework/yii/jui/Dialog.php @@ -0,0 +1,52 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\jui; + +use yii\helpers\Html; + +/** + * Dialog renders an dialog jQuery UI widget. + * + * For example: + * + * ```php + * Dialog::begin(array( + * 'clientOptions' => array( + * 'modal' => true, + * ), + * )); + * + * echo 'Dialog contents here...'; + * + * Dialog::end(); + * ``` + * + * @see http://api.jqueryui.com/dialog/ + * @author Alexander Kochetov <creocoder@gmail.com> + * @since 2.0 + */ +class Dialog extends Widget +{ + /** + * Initializes the widget. + */ + public function init() + { + parent::init(); + echo Html::beginTag('div', $this->options) . "\n"; + } + + /** + * Renders the widget. + */ + public function run() + { + echo Html::endTag('div') . "\n"; + $this->registerWidget('dialog'); + } +} diff --git a/framework/yii/jui/InputWidget.php b/framework/yii/jui/InputWidget.php new file mode 100644 index 0000000..e100d6c --- /dev/null +++ b/framework/yii/jui/InputWidget.php @@ -0,0 +1,59 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\jui; + +use Yii; +use yii\base\Model; +use yii\base\InvalidConfigException; + +/** + * InputWidget is the base class for all jQuery UI input widgets. + * + * @author Alexander Kochetov <creocoder@gmail.com> + * @since 2.0 + */ +class InputWidget extends Widget +{ + /** + * @var Model the data model that this widget is associated with. + */ + public $model; + /** + * @var string the model attribute that this widget is associated with. + */ + public $attribute; + /** + * @var string the input name. This must be set if [[model]] and [[attribute]] are not set. + */ + public $name; + /** + * @var string the input value. + */ + public $value; + + + /** + * Initializes the widget. + * If you override this method, make sure you call the parent implementation first. + */ + public function init() + { + if (!$this->hasModel() && $this->name === null) { + throw new InvalidConfigException("Either 'name' or 'model' and 'attribute' properties must be specified."); + } + parent::init(); + } + + /** + * @return boolean whether this widget is associated with a data model. + */ + protected function hasModel() + { + return $this->model instanceof Model && $this->attribute !== null; + } +} diff --git a/framework/yii/jui/Menu.php b/framework/yii/jui/Menu.php index 0a84acf..d4e390c 100644 --- a/framework/yii/jui/Menu.php +++ b/framework/yii/jui/Menu.php @@ -8,7 +8,6 @@ namespace yii\jui; use Yii; -use yii\base\View; use yii\helpers\Json; diff --git a/framework/yii/jui/ProgressBar.php b/framework/yii/jui/ProgressBar.php new file mode 100644 index 0000000..a7697e5 --- /dev/null +++ b/framework/yii/jui/ProgressBar.php @@ -0,0 +1,62 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\jui; + +use yii\helpers\Html; + +/** + * ProgressBar renders an progressbar jQuery UI widget. + * + * For example: + * + * ```php + * echo ProgressBar::widget(array( + * 'clientOptions' => array( + * 'value' => 75, + * ), + * )); + * ``` + * + * The following example will show the content enclosed between the [[begin()]] + * and [[end()]] calls within the widget container: + * + * ~~~php + * ProgressBar::widget(array( + * 'clientOptions' => array( + * 'value' => 75, + * ), + * )); + * + * echo '<div class="progress-label">Loading...</div>'; + * + * ProgressBar::end(); + * ~~~ + * @see http://api.jqueryui.com/progressbar/ + * @author Alexander Kochetov <creocoder@gmail.com> + * @since 2.0 + */ +class ProgressBar extends Widget +{ + /** + * Initializes the widget. + */ + public function init() + { + parent::init(); + echo Html::beginTag('div', $this->options) . "\n"; + } + + /** + * Renders the widget. + */ + public function run() + { + echo Html::endTag('div') . "\n"; + $this->registerWidget('progressbar'); + } +} diff --git a/framework/yii/jui/Sortable.php b/framework/yii/jui/Sortable.php new file mode 100644 index 0000000..8524b5b --- /dev/null +++ b/framework/yii/jui/Sortable.php @@ -0,0 +1,116 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\jui; + +use yii\base\InvalidConfigException; +use yii\helpers\ArrayHelper; +use yii\helpers\Html; + +/** + * Sortable renders a sortable jQuery UI widget. + * + * For example: + * + * ```php + * echo Sortable::widget(array( + * 'items' => array( + * 'Item 1', + * array( + * 'content' => 'Item2', + * ), + * array( + * 'content' => 'Item3', + * 'options' => array( + * 'tag' => 'li', + * ), + * ), + * ), + * 'options' => array( + * 'tag' => 'ul', + * ), + * 'itemOptions' => array( + * 'tag' => 'li', + * ), + * 'clientOptions' => array( + * 'cursor' => 'move', + * ), + * )); + * ``` + * + * @see http://api.jqueryui.com/sortable/ + * @author Alexander Kochetov <creocoder@gmail.com> + * @since 2.0 + */ +class Sortable extends Widget +{ + /** + * @var array the HTML attributes for the widget container tag. The following special options are recognized: + * + * - tag: string, defaults to "ul", the tag name of the container tag of this widget + */ + public $options = array(); + /** + * @var array list of sortable items. Each item can be a string representing the item content + * or an array of the following structure: + * + * ~~~ + * array( + * 'content' => 'item content', + * // the HTML attributes of the item container tag. This will overwrite "itemOptions". + * 'options' => array(), + * ) + * ~~~ + */ + public $items = array(); + /** + * @var array list of HTML attributes for the item container tags. This will be overwritten + * by the "options" set in individual [[items]]. The following special options are recognized: + * + * - tag: string, defaults to "li", the tag name of the item container tags. + */ + public $itemOptions = array(); + + + /** + * Renders the widget. + */ + public function run() + { + $options = $this->options; + $tag = ArrayHelper::remove($options, 'tag', 'ul'); + echo Html::beginTag($tag, $options) . "\n"; + echo $this->renderItems() . "\n"; + echo Html::endTag($tag) . "\n"; + $this->registerWidget('sortable', false); + } + + /** + * Renders sortable items as specified on [[items]]. + * @return string the rendering result. + * @throws InvalidConfigException. + */ + public function renderItems() + { + $items = array(); + foreach ($this->items as $item) { + $options = $this->itemOptions; + $tag = ArrayHelper::remove($options, 'tag', 'li'); + if (is_array($item)) { + if (!isset($item['content'])) { + throw new InvalidConfigException("The 'content' option is required."); + } + $options = array_merge($options, ArrayHelper::getValue($item, 'options', array())); + $tag = ArrayHelper::remove($options, 'tag', $tag); + $items[] = Html::tag($tag, $item['content'], $options); + } else { + $items[] = Html::tag($tag, $item, $options); + } + } + return implode("\n", $items); + } +} diff --git a/framework/yii/jui/Tabs.php b/framework/yii/jui/Tabs.php index ca0b3da..052ffe7 100644 --- a/framework/yii/jui/Tabs.php +++ b/framework/yii/jui/Tabs.php @@ -8,7 +8,7 @@ namespace yii\jui; use yii\base\InvalidConfigException; -use yii\helpers\base\ArrayHelper; +use yii\helpers\ArrayHelper; use yii\helpers\Html; /** diff --git a/framework/yii/jui/Widget.php b/framework/yii/jui/Widget.php index f6efd91..d34a8bd 100644 --- a/framework/yii/jui/Widget.php +++ b/framework/yii/jui/Widget.php @@ -8,7 +8,6 @@ namespace yii\jui; use Yii; -use yii\base\View; use yii\helpers\Json; @@ -59,13 +58,16 @@ class Widget extends \yii\base\Widget /** * Registers a specific jQuery UI widget and the related events * @param string $name the name of the jQuery UI widget + * @param boolean $registerTheme whether register theme bundle */ - protected function registerWidget($name) + protected function registerWidget($name, $registerTheme = true) { $id = $this->options['id']; $view = $this->getView(); $view->registerAssetBundle("yii/jui/$name"); - $view->registerAssetBundle(static::$theme . "/$name"); + if ($registerTheme) { + $view->registerAssetBundle(static::$theme . "/$name"); + } if ($this->clientOptions !== false) { $options = empty($this->clientOptions) ? '' : Json::encode($this->clientOptions); diff --git a/framework/yii/jui/assets.php b/framework/yii/jui/assets.php index 285026c..d2d8f7c 100644 --- a/framework/yii/jui/assets.php +++ b/framework/yii/jui/assets.php @@ -559,7 +559,7 @@ return array( 'js' => array( 'jquery.ui.dialog.js', ), - 'depends' => array('yii/jui/core', 'yii/jui/widget', 'yii/jui/button', 'yii/jui/draggable', 'yii/jui/mouse', 'yii/jui/position', 'yii/jui/resizeable', 'yii/jui/effect/all'), + 'depends' => array('yii/jui/core', 'yii/jui/widget', 'yii/jui/button', 'yii/jui/draggable', 'yii/jui/mouse', 'yii/jui/position', 'yii/jui/resizable', 'yii/jui/effect/all'), ), 'yii/jui/draggable' => array( 'sourcePath' => __DIR__ . '/assets', @@ -803,7 +803,7 @@ return array( 'css' => array( 'themes/base/jquery.ui.dialog.css', ), - 'depends' => array('yii/jui/theme/base/core', 'yii/jui/theme/base/button', 'yii/jui/theme/base/resizeable'), + 'depends' => array('yii/jui/theme/base/core', 'yii/jui/theme/base/button', 'yii/jui/theme/base/resizable'), ), 'yii/jui/theme/base/menu' => array( 'sourcePath' => __DIR__ . '/assets', diff --git a/framework/yii/rbac/DbManager.php b/framework/yii/rbac/DbManager.php index 719ffa8..b7a5d4e 100644 --- a/framework/yii/rbac/DbManager.php +++ b/framework/yii/rbac/DbManager.php @@ -160,7 +160,8 @@ class DbManager extends Manager throw new InvalidCallException("Cannot add '$childName' as a child of '$itemName'. A loop has been detected."); } $this->db->createCommand() - ->insert($this->itemChildTable, array('parent' => $itemName, 'child' => $childName)); + ->insert($this->itemChildTable, array('parent' => $itemName, 'child' => $childName)) + ->execute(); return true; } else { throw new Exception("Either '$itemName' or '$childName' does not exist."); @@ -177,7 +178,8 @@ class DbManager extends Manager public function removeItemChild($itemName, $childName) { return $this->db->createCommand() - ->delete($this->itemChildTable, array('parent' => $itemName, 'child' => $childName)) > 0; + ->delete($this->itemChildTable, array('parent' => $itemName, 'child' => $childName)) + ->execute() > 0; } /** @@ -248,7 +250,8 @@ class DbManager extends Manager 'item_name' => $itemName, 'biz_rule' => $bizRule, 'data' => serialize($data), - )); + )) + ->execute(); return new Assignment(array( 'manager' => $this, 'userId' => $userId, @@ -267,7 +270,8 @@ class DbManager extends Manager public function revoke($userId, $itemName) { return $this->db->createCommand() - ->delete($this->assignmentTable, array('user_id' => $userId, 'item_name' => $itemName)) > 0; + ->delete($this->assignmentTable, array('user_id' => $userId, 'item_name' => $itemName)) + ->execute() > 0; } /** @@ -276,7 +280,7 @@ class DbManager extends Manager * @param string $itemName the item name * @return boolean whether the item has been assigned to the user. */ - public function isAssigned($itemName, $userId) + public function isAssigned($userId, $itemName) { $query = new Query; return $query->select(array('item_name')) @@ -358,7 +362,8 @@ class DbManager extends Manager ), array( 'user_id' => $assignment->userId, 'item_name' => $assignment->itemName, - )); + )) + ->execute(); } /** @@ -431,7 +436,8 @@ class DbManager extends Manager 'description' => $description, 'biz_rule' => $bizRule, 'data' => serialize($data), - )); + )) + ->execute(); return new Item(array( 'manager' => $this, 'name' => $name, @@ -451,12 +457,15 @@ class DbManager extends Manager { if ($this->usingSqlite()) { $this->db->createCommand() - ->delete($this->itemChildTable, array('or', 'parent=:name', 'child=:name'), array(':name' => $name)); + ->delete($this->itemChildTable, array('or', 'parent=:name', 'child=:name'), array(':name' => $name)) + ->execute(); $this->db->createCommand() - ->delete($this->assignmentTable, array('item_name' => $name)); + ->delete($this->assignmentTable, array('item_name' => $name)) + ->execute(); } return $this->db->createCommand() - ->delete($this->itemTable, array('name' => $name)) > 0; + ->delete($this->itemTable, array('name' => $name)) + ->execute() > 0; } /** @@ -497,11 +506,14 @@ class DbManager extends Manager { if ($this->usingSqlite() && $oldName !== null && $item->getName() !== $oldName) { $this->db->createCommand() - ->update($this->itemChildTable, array('parent' => $item->getName()), array('parent' => $oldName)); + ->update($this->itemChildTable, array('parent' => $item->getName()), array('parent' => $oldName)) + ->execute(); $this->db->createCommand() - ->update($this->itemChildTable, array('child' => $item->getName()), array('child' => $oldName)); + ->update($this->itemChildTable, array('child' => $item->getName()), array('child' => $oldName)) + ->execute(); $this->db->createCommand() - ->update($this->assignmentTable, array('item_name' => $item->getName()), array('item_name' => $oldName)); + ->update($this->assignmentTable, array('item_name' => $item->getName()), array('item_name' => $oldName)) + ->execute(); } $this->db->createCommand() @@ -513,7 +525,8 @@ class DbManager extends Manager 'data' => serialize($item->data), ), array( 'name' => $oldName === null ? $item->getName() : $oldName, - )); + )) + ->execute(); } /** @@ -529,8 +542,8 @@ class DbManager extends Manager public function clearAll() { $this->clearAssignments(); - $this->db->createCommand()->delete($this->itemChildTable); - $this->db->createCommand()->delete($this->itemTable); + $this->db->createCommand()->delete($this->itemChildTable)->execute(); + $this->db->createCommand()->delete($this->itemTable)->execute(); } /** @@ -538,7 +551,7 @@ class DbManager extends Manager */ public function clearAssignments() { - $this->db->createCommand()->delete($this->assignmentTable); + $this->db->createCommand()->delete($this->assignmentTable)->execute(); } /** diff --git a/framework/yii/requirements/views/web/index.php b/framework/yii/requirements/views/web/index.php index 6cd2594..1887f8b 100644 --- a/framework/yii/requirements/views/web/index.php +++ b/framework/yii/requirements/views/web/index.php @@ -56,8 +56,8 @@ <td> <?php echo $requirement['name']; ?> </td> - <td > - <?php echo $requirement['condition'] ? 'Passed' : ($requirement['mandatory'] ? 'Failed' : 'Warning'); ?> + <td> + <span class="result"><?php echo $requirement['condition'] ? 'Passed' : ($requirement['mandatory'] ? 'Failed' : 'Warning'); ?></span> </td> <td> <?php echo $requirement['by']; ?> @@ -79,4 +79,4 @@ </div> </div> </body> -</html> \ No newline at end of file +</html> diff --git a/framework/yii/validators/UniqueValidator.php b/framework/yii/validators/UniqueValidator.php index a4d8bff..b650693 100644 --- a/framework/yii/validators/UniqueValidator.php +++ b/framework/yii/validators/UniqueValidator.php @@ -9,6 +9,7 @@ namespace yii\validators; use Yii; use yii\base\InvalidConfigException; +use yii\db\ActiveRecord; /** * CUniqueValidator validates that the attribute value is unique in the corresponding database table. @@ -71,7 +72,7 @@ class UniqueValidator extends Validator $query = $className::find(); $query->where(array($column->name => $value)); - if ($object->getIsNewRecord()) { + if (!$object instanceof ActiveRecord || $object->getIsNewRecord()) { // if current $object isn't in the database yet then it's OK just to call exists() $exists = $query->exists(); } else { diff --git a/framework/yii/views/error.php b/framework/yii/views/error.php deleted file mode 100644 index 009050a..0000000 --- a/framework/yii/views/error.php +++ /dev/null @@ -1,67 +0,0 @@ -<?php -/** - * @var \Exception $exception - * @var \yii\base\ErrorHandler $context - */ -$context = $this->context; -$title = $context->htmlEncode($exception instanceof \yii\base\Exception ? $exception->getName() : get_class($exception)); -?> -<!DOCTYPE html> -<html> -<head> - <meta charset="utf-8" /> - <title><?php echo $title?></title> - - <style> - body { - font: normal 9pt "Verdana"; - color: #000; - background: #fff; - } - - h1 { - font: normal 18pt "Verdana"; - color: #f00; - margin-bottom: .5em; - } - - h2 { - font: normal 14pt "Verdana"; - color: #800000; - margin-bottom: .5em; - } - - h3 { - font: bold 11pt "Verdana"; - } - - p { - font: normal 9pt "Verdana"; - color: #000; - } - - .version { - color: gray; - font-size: 8pt; - border-top: 1px solid #aaa; - padding-top: 1em; - margin-bottom: 1em; - } - </style> -</head> - -<body> - <h1><?php echo $title?></h1> - <h2><?php echo nl2br($context->htmlEncode($exception->getMessage()))?></h2> - <p> - The above error occurred while the Web server was processing your request. - </p> - <p> - Please contact us if you think this is a server error. Thank you. - </p> - <div class="version"> - <?php echo date('Y-m-d H:i:s', time())?> - <?php echo YII_DEBUG ? $context->versionInfo : ''?> - </div> -</body> -</html> diff --git a/framework/yii/views/errorHandler/callStackItem.php b/framework/yii/views/errorHandler/callStackItem.php new file mode 100644 index 0000000..7514119 --- /dev/null +++ b/framework/yii/views/errorHandler/callStackItem.php @@ -0,0 +1,47 @@ +<?php +/** + * @var \yii\base\View $this + * @var string|null $file + * @var integer|null $line + * @var string|null $class + * @var string|null $method + * @var integer $index + * @var string[] $lines + * @var integer $begin + * @var integer $end + * @var \yii\base\ErrorHandler $context + */ +$context = $this->context; +?> +<li class="<?php if (!$context->isCoreFile($file) || $index === 1) echo 'application'; ?> call-stack-item" + data-line="<?php echo (int)($line - $begin); ?>"> + <div class="element-wrap"> + <div class="element"> + <span class="item-number"><?php echo (int)$index; ?>.</span> + <span class="text"><?php if ($file !== null) echo 'in ' . $context->htmlEncode($file); ?></span> + <?php if ($method !== null): ?> + <span class="call"> + <?php if ($file !== null) echo '–' ?> + <?php if ($class !== null) echo $context->addTypeLinks($class) . '→'; ?><?php echo $context->addTypeLinks($method . '()'); ?> + </span> + <?php endif; ?> + <span class="at"><?php if ($line !== null) echo 'at line'; ?></span> + <span class="line"><?php if ($line !== null) echo (int)$line + 1; ?></span> + </div> + </div> + <?php if (!empty($lines)): ?> + <div class="code-wrap"> + <div class="error-line"></div> + <?php for ($i = $begin; $i <= $end; ++$i): ?><div class="hover-line"></div><?php endfor; ?> + <div class="code"> + <?php for ($i = $begin; $i <= $end; ++$i): ?><span class="lines-item"><?php echo (int)($i + 1); ?></span><?php endfor; ?> + <pre><?php + // fill empty lines with a whitespace to avoid rendering problems in opera + for ($i = $begin; $i <= $end; ++$i) { + echo (trim($lines[$i]) == '') ? " \n" : $context->htmlEncode($lines[$i]); + } + ?></pre> + </div> + </div> + <?php endif; ?> +</li> diff --git a/framework/yii/views/errorHandler/main.php b/framework/yii/views/errorHandler/main.php new file mode 100644 index 0000000..d7bbb3d --- /dev/null +++ b/framework/yii/views/errorHandler/main.php @@ -0,0 +1,492 @@ +<?php +/** + * @var \yii\base\View $this + * @var \Exception $exception + * @var string $request + * @var \yii\base\ErrorHandler $context + */ +$context = $this->context; +?> +<!doctype html> +<html lang="en-us"> + +<head> + <meta charset="utf-8"/> + + <title><?php + if ($exception instanceof \yii\base\HttpException) { + echo (int) $exception->statusCode . ' ' . $context->htmlEncode($exception->getName()); + } elseif ($exception instanceof \yii\base\Exception) { + echo $context->htmlEncode($exception->getName() . ' – ' . get_class($exception)); + } else { + echo $context->htmlEncode(get_class($exception)); + } + ?></title> + + <style type="text/css"> +/* reset */ +html,body,div,span,h1,h2,h3,h4,h5,h6,p,pre,a,code,em,img,strong,b,i,ul,li{ + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +body{ + line-height: 1; +} +ul{ + list-style: none; +} + +/* base */ +::selection{ + color: #fff !important; + background-color: #e51717 !important; +} +::-moz-selection{ + color: #fff !important; + background-color: #e51717 !important; +} +a{ + text-decoration: none; +} +a:hover{ + text-decoration: underline; +} +h1,h2,h3,p,img,ul li{ + font-family: Arial,sans-serif; + color: #505050; +} +html,body{ + overflow-x: hidden; +} + +/* header */ +.header{ + min-width: 860px; /* 960px - 50px * 2 */ + margin: 40px auto 30px auto; + padding: 0 50px; +} +.header h1{ + font-size: 30px; + color: #e57373; + text-shadow: 0 1px 0 #cacaca; + margin-bottom: 30px; +} +.header h1 span, .header h1 span a{ + color: #e51717; +} +.header h1 a{ + color: #e57373; +} +.header h1 a:hover{ + color: #e51717; +} +.header img{ + float: right; + margin-top: -15px; +} +.header h2{ + font-size: 20px; + text-shadow: 0 1px 0 #cacaca; +} + +/* previous exceptions */ +.header .previous{ + margin: 20px 0; + padding-left: 30px; +} +.header .previous div{ + margin: 20px 0; +} +.header .previous .arrow{ + -moz-transform: scale(-1, 1); + -webkit-transform: scale(-1, 1); + -o-transform: scale(-1, 1); + transform: scale(-1, 1); + filter: progid:DXImageTransform.Microsoft.BasicImage(mirror=1); + font-size: 26px; + position: absolute; + margin-top: -5px; + margin-left: -25px; + color: #e51717; + text-shadow: 0 1px 0 #cacaca; +} +.header .previous h2{ + font-size: 20px; + color: #e57373; + text-shadow: 0 1px 0 #cacaca; + margin-bottom: 10px; +} +.header .previous h2 span{ + color: #e51717; +} +.header .previous h2 a{ + color: #e57373; +} +.header .previous h2 a:hover{ + color: #e51717; +} +.header .previous h3{ + font-size: 14px; + margin: 10px 0; +} +.header .previous p{ + font-size: 14px; + color: #aaa; +} + +/* call stack */ +.call-stack{ + margin-top: 30px; + margin-bottom: 40px; +} +.call-stack ul li{ + margin: 1px 0; +} +.call-stack ul li .element-wrap{ + cursor: pointer; + padding: 15px 0; +} +.call-stack ul li.application .element-wrap{ + background-color: #fafafa; +} +.call-stack ul li .element-wrap:hover{ + background-color: #edf9ff; +} +.call-stack ul li .element{ + min-width: 860px; /* 960px - 50px * 2 */ + margin: 0 auto; + padding: 0 50px; + position: relative; +} +.call-stack ul li a{ + color: #505050; +} +.call-stack ul li a:hover{ + color: #000; + text-shadow: 0 1px 0 #cacaca; +} +.call-stack ul li .item-number{ + width: 45px; + display: inline-block; +} +.call-stack ul li .text{ + color: #aaa; +} +.call-stack ul li.application .text{ + color: #505050; +} +.call-stack ul li .at{ + position: absolute; + right: 110px; /* 50px + 60px */ + color: #aaa; +} +.call-stack ul li.application .at{ + color: #505050; +} +.call-stack ul li .line{ + position: absolute; + right: 50px; + width: 60px; + text-align: right; +} +.call-stack ul li .code-wrap{ + display: none; + position: relative; +} +.call-stack ul li.application .code-wrap{ + display: block; +} +.call-stack ul li .error-line, +.call-stack ul li .hover-line{ + background-color: #ffebeb; + position: absolute; + width: 100%; + z-index: 100; + margin-top: -61px; +} +.call-stack ul li .hover-line{ + background: none; +} +.call-stack ul li .hover-line.hover, +.call-stack ul li .hover-line:hover{ + background: #edf9ff !important; +} +.call-stack ul li .code{ + min-width: 860px; /* 960px - 50px * 2 */ + margin: 15px auto; + padding: 0 50px; + position: relative; +} +.call-stack ul li .code .lines-item{ + position: absolute; + z-index: 200; + display: block; + width: 25px; + text-align: right; + color: #aaa; + line-height: 20px; + font-size: 12px; + margin-top: -63px; + font-family: Consolas, Courier New, monospace; +} +.call-stack ul li .code pre{ + position: relative; + z-index: 200; + left: 50px; + line-height: 20px; + font-size: 12px; + font-family: Consolas, Courier New, monospace; + display: inline; +} +@-moz-document url-prefix() { + .call-stack ul li .code pre{ + line-height: 20px; + } +} + +/* request */ +.request{ + background-color: #fafafa; + padding-top: 40px; + padding-bottom: 40px; + margin-top: 40px; + margin-bottom: 1px; +} +.request .code{ + min-width: 860px; /* 960px - 50px * 2 */ + margin: 0 auto; + padding: 15px 50px; +} +.request .code pre{ + font-size: 14px; + line-height: 18px; + font-family: Consolas, Courier New, monospace; + display: inline; + word-wrap: break-word; +} + +/* footer */ +.footer{ + position: relative; + height: 222px; + min-width: 860px; /* 960px - 50px * 2 */ + padding: 0 50px; + margin: 1px auto 0 auto; +} +.footer p{ + font-size: 16px; + padding-bottom: 10px; + text-shadow: 0 1px 0 #cacaca; +} +.footer p a{ + color: #505050; +} +.footer p a:hover{ + color: #000; +} +.footer .timestamp{ + font-size: 14px; + padding-top: 67px; + margin-bottom: 28px; +} +.footer img{ + position: absolute; + right: -50px; +} + +/* highlight.js */ +pre .subst,pre .title{ + font-weight: normal; + color: #505050; +} +pre .comment,pre .template_comment,pre .javadoc,pre .diff .header{ + color: #808080; + font-style: italic; +} +pre .annotation,pre .decorator,pre .preprocessor,pre .doctype,pre .pi,pre .chunk,pre .shebang,pre .apache .cbracket, +pre .prompt,pre .http .title{ + color: #808000; +} +pre .tag,pre .pi{ + background: #efefef; +} +pre .tag .title,pre .id,pre .attr_selector,pre .pseudo,pre .literal,pre .keyword,pre .hexcolor,pre .css .function, +pre .ini .title,pre .css .class,pre .list .title,pre .clojure .title,pre .nginx .title,pre .tex .command, +pre .request,pre .status{ + color: #000080; +} +pre .attribute,pre .rules .keyword,pre .number,pre .date,pre .regexp,pre .tex .special{ + color: #00a; +} +pre .number,pre .regexp{ + font-weight: normal; +} +pre .string,pre .value,pre .filter .argument,pre .css .function .params,pre .apache .tag{ + color: #0a0; +} +pre .symbol,pre .ruby .symbol .string,pre .char,pre .tex .formula{ + color: #505050; + background: #d0eded; + font-style: italic; +} +pre .phpdoc,pre .yardoctag,pre .javadoctag{ + text-decoration: underline; +} +pre .variable,pre .envvar,pre .apache .sqbracket,pre .nginx .built_in{ + color: #a00; +} +pre .addition{ + background: #baeeba; +} +pre .deletion{ + background: #ffc8bd; +} +pre .diff .change{ + background: #bccff9; +} + </style> +</head> + +<body> + <div class="header"> + <?php if ($exception instanceof \yii\base\ErrorException): ?> + <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEwAAABACAIAAAA/NwYHAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjU4QjIxQTgwQzU1MDExRTJBNEMxRURGMUEzMjA1M0UxIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjU4QjIxQTgxQzU1MDExRTJBNEMxRURGMUEzMjA1M0UxIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NThCMjFBN0VDNTUwMTFFMkE0QzFFREYxQTMyMDUzRTEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NThCMjFBN0ZDNTUwMTFFMkE0QzFFREYxQTMyMDUzRTEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz74UrDRAAAOWUlEQVR42txb2W8T1xf2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" alt="Gears"/> + <h1> + <span><?php echo $context->htmlEncode($exception->getName()); ?></span> + – <?php echo $context->addTypeLinks(get_class($exception)); ?> + </h1> + <?php else: ?> + <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAA6CAIAAACPssguAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjcwRURBOUQxQzQ3RDExRTJCRjVFODJDQkFGODFDN0VBIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjcwRURBOUQyQzQ3RDExRTJCRjVFODJDQkFGODFDN0VBIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NzBFREE5Q0ZDNDdEMTFFMkJGNUU4MkNCQUY4MUM3RUEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NzBFREE5RDBDNDdEMTFFMkJGNUU4MkNCQUY4MUM3RUEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4L6mYsAAAE9klEQVR42tRa227aTBDGiwkhJtD8EanSQ6RKqKpSBbWqql5U7QP0TfsSvWhvStMEO6cqDUpjRylSQ1QC+IQN/mdrhBIb413vgsheIDDr9XwzOzPfzFrwPC/Fe8Cauiybe3uersNPQZJylYr04oUgCNyfJXJfcTAYXH/5YoH0qjq66LbbTrtdfPcOITTXALDuf/40FSWlabeuq6rpeZkHD6Ryma8dOOvDdV19Zycg/XBoml6r9ft9vk9EnNV/dOT8+RM1oXdxof/4wdfrEF/1G7Va+uIiagL8BfaBafMIAHy38/17/+/fydNgAkyDyXMHwLEsS1EmqH9kBOvgACbPFwDQaPfr11S9TjT7+JijEfgAsFst8/CQYv7BQe/6el4AQGTsfPuGbqSt2CGcnrarVS4hlRUAxETz9+/eyQntjXAL3MgeUhG7+vXtbSr1Dx+sqoYss3sCYlS/cXLinJ8nu713emqoKqMRmAD4xAElBQA3GpA62DwBsagfiIN7ecnyeHZyIbKoP4o46KurY2+Rrq7CeQ08YenZs0wmM1MAI+KQDv3VKpd7r1+L2WwQsGH0q9VCyN3dZrOrKPdevUpGsxMCmEAcMqVSaWNjcXExcN00zRbQ7BAAWMSs1ZY2N7NLSzMCMJk4ZBFaXl4OAxBF0Yza68fH3Z2dzNu3Ceo1lMB3raurScQhgqjBDhGiibS1v98zzVlEIax+CJ0TM9fYqAIXB9HRBsiFXq0myGuIVv2xxGGQNCaCVcG2tCGVDkBi4jBU80ThYFlwLVojICr1kxAHFLHRsWoNgzu5oABASBw8247ygVScZJhcyDIVuUDk6tf39oiIg+exUAOwMNiZfAVSAI7jGPv7sSUvhwLl/Jyqc0EEwA+dsR2H+EFWy4OdwdqERiACQNhxGIaaaCcWyDY3Zni7u2BzPgDoOg64Yo92YvJ43e2CzUlCKmIlDsSZ2FcG4Qq4faQoJO0jRKJ+uszFq+tWr5PkNRRPHM7O2IWBpdxGYxrkAk2DODihbAqKdJpNkRIDJhdxniDGEgdavgpSWp8+XX7+HADg9vsJCnDcPnr+XHr0KKpeE6fScdA0l1P7AIwAu2BxfR3qIYotREEcpj8mkwsxkjjs7iYmDuRdCXJykSuXx3YuxEji0O2mE0lvlEpWpZK6vWsxv2s0BCjeE1kVk4ujo2KlEvYEkZE4jMlBCK1sbUFdf7NCx22YlRVHURKuCeSiVpM2N8NGEFmJwzgAIH2hUAhYAD6vGQ6J/bOpe2/eBDoXiJ04jNm1CAVsDT8Zj7ijyAViJQ6zHPV6Z3s7kNfQrcylqlCVpuZ42IeHgbMpdJM4QD2auFc+mxE+m0JUHQfSqHd2Fsg7mMxxWjxwNiX43yBzNT9+9GSZj6I2NsKZH5e5Y9+hSODQ79+vfvjgP0K8SRzSvCw9jgvxJBe/fpmaln/yBAe3WXYcOHYuRmdTCFctmsah4zBjhtdo+D08BGHVhs1zd9Q/MgKIDcJjCwzyefv+/dRdG86/zItzvri+7knS3ZLefPgQPX7sMxSUy+W8ly+NtbW7Ir2+tjaoVHL5fDqdFgFENpstlMtQ/bdlOc3pJZLpjX6x6G1t/ff0KegdH1v96yV7EEk7nU6r1bJtm+PrVPx9FyFQd7FYBMa+sLAwBODnMgirAAM+p/EqLDcuJAiwbaCsgU+fsf8vwADjle3ME1OGEwAAAABJRU5ErkJggg==" alt="Attention"/> + <h1><?php + if ($exception instanceof \yii\base\HttpException) { + echo '<span>' . $context->createHttpStatusLink($exception->statusCode, $context->htmlEncode($exception->getName())) . '</span>'; + echo ' – ' . $context->addTypeLinks(get_class($exception)); + } elseif ($exception instanceof \yii\base\Exception) { + echo '<span>' . $context->htmlEncode($exception->getName()) . '</span>'; + echo ' – ' . $context->addTypeLinks(get_class($exception)); + } else { + echo '<span>' . $context->htmlEncode(get_class($exception)) . '</span>'; + } + ?></h1> + <?php endif; ?> + <h2><?php echo $context->htmlEncode($exception->getMessage()); ?></h2> + <?php echo $context->renderPreviousExceptions($exception); ?> + </div> + + <div class="call-stack"> + <ul> + <?php echo $context->renderCallStackItem($exception->getFile(), $exception->getLine(), null, null, 1); ?> + <?php for ($i = 1, $trace = $exception->getTrace(), $length = count($trace); $i < $length; ++$i): ?> + <?php echo $context->renderCallStackItem(@$trace[$i]['file'] ?: null, @$trace[$i]['line'] ?: null, + @$trace[$i]['class'] ?: null, @$trace[$i]['function'] ?: null, $i + 1); ?> + <?php endfor; ?> + </ul> + </div> + + <div class="request"> + <div class="code"> + <pre><?php echo $context->htmlEncode($request); ?></pre> + </div> + </div> + + <div class="footer"> + <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAS4AAADeCAIAAAAMx3q5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkE5NkEyOTAzQzQ3RDExRTI4NjIzOEE2RkU5QTc4RTU0IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkE5NkEyOTA0QzQ3RDExRTI4NjIzOEE2RkU5QTc4RTU0Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QTk2QTI5MDFDNDdEMTFFMjg2MjM4QTZGRTlBNzhFNTQiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QTk2QTI5MDJDNDdEMTFFMjg2MjM4QTZGRTlBNzhFNTQiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4DYCAjAAAx4UlEQVR42ux9aZvjuJEm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" alt="Yii Framework"/> + <p class="timestamp"><?php echo date('Y-m-d, H:i:s'); ?></p> + <p><?php echo $context->createServerInformationLink(); ?></p> + <p><a href="http://yiiframework.com/">Yii Framework</a>/<?php echo $context->createFrameworkVersionLink(); ?></p> + </div> + + <script type="text/javascript"> +var hljs=new function(){function l(o){return o.replace(/&/gm,"&").replace(/</gm,"<").replace(/>/gm,">")}function b(p){for(var o=p.firstChild;o;o=o.nextSibling){if(o.nodeName=="CODE"){return o}if(!(o.nodeType==3&&o.nodeValue.match(/\s+/))){break}}}function h(p,o){return Array.prototype.map.call(p.childNodes,function(q){if(q.nodeType==3){return o?q.nodeValue.replace(/\n/g,""):q.nodeValue}if(q.nodeName=="BR"){return"\n"}return h(q,o)}).join("")}function a(q){var p=(q.className+" "+q.parentNode.className).split(/\s+/);p=p.map(function(r){return r.replace(/^language-/,"")});for(var o=0;o<p.length;o++){if(e[p[o]]||p[o]=="no-highlight"){return p[o]}}}function c(q){var o=[];(function p(r,s){for(var t=r.firstChild;t;t=t.nextSibling){if(t.nodeType==3){s+=t.nodeValue.length}else{if(t.nodeName=="BR"){s+=1}else{if(t.nodeType==1){o.push({event:"start",offset:s,node:t});s=p(t,s);o.push({event:"stop",offset:s,node:t})}}}}return s})(q,0);return o}function j(x,v,w){var p=0;var y="";var r=[];function t(){if(x.length&&v.length){if(x[0].offset!=v[0].offset){return(x[0].offset<v[0].offset)?x:v}else{return v[0].event=="start"?x:v}}else{return x.length?x:v}}function s(A){function z(B){return" "+B.nodeName+'="'+l(B.value)+'"'}return"<"+A.nodeName+Array.prototype.map.call(A.attributes,z).join("")+">"}while(x.length||v.length){var u=t().splice(0,1)[0];y+=l(w.substr(p,u.offset-p));p=u.offset;if(u.event=="start"){y+=s(u.node);r.push(u.node)}else{if(u.event=="stop"){var o,q=r.length;do{q--;o=r[q];y+=("</"+o.nodeName.toLowerCase()+">")}while(o!=u.node);r.splice(q,1);while(q<r.length){y+=s(r[q]);q++}}}}return y+l(w.substr(p))}function f(q){function o(s,r){return RegExp(s,"m"+(q.cI?"i":"")+(r?"g":""))}function p(y,w){if(y.compiled){return}y.compiled=true;var s=[];if(y.k){var r={};function z(A,t){t.split(" ").forEach(function(B){var C=B.split("|");r[C[0]]=[A,C[1]?Number(C[1]):1];s.push(C[0])})}y.lR=o(y.l||hljs.IR,true);if(typeof y.k=="string"){z("keyword",y.k)}else{for(var x in y.k){if(!y.k.hasOwnProperty(x)){continue}z(x,y.k[x])}}y.k=r}if(w){if(y.bWK){y.b="\\b("+s.join("|")+")\\s"}y.bR=o(y.b?y.b:"\\B|\\b");if(!y.e&&!y.eW){y.e="\\B|\\b"}if(y.e){y.eR=o(y.e)}y.tE=y.e||"";if(y.eW&&w.tE){y.tE+=(y.e?"|":"")+w.tE}}if(y.i){y.iR=o(y.i)}if(y.r===undefined){y.r=1}if(!y.c){y.c=[]}for(var v=0;v<y.c.length;v++){if(y.c[v]=="self"){y.c[v]=y}p(y.c[v],y)}if(y.starts){p(y.starts,w)}var u=[];for(var v=0;v<y.c.length;v++){u.push(y.c[v].b)}if(y.tE){u.push(y.tE)}if(y.i){u.push(y.i)}y.t=u.length?o(u.join("|"),true):{exec:function(t){return null}}}p(q)}function d(D,E){function o(r,M){for(var L=0;L<M.c.length;L++){var K=M.c[L].bR.exec(r);if(K&&K.index==0){return M.c[L]}}}function s(K,r){if(K.e&&K.eR.test(r)){return K}if(K.eW){return s(K.parent,r)}}function t(r,K){return K.i&&K.iR.test(r)}function y(L,r){var K=F.cI?r[0].toLowerCase():r[0];return L.k.hasOwnProperty(K)&&L.k[K]}function G(){var K=l(w);if(!A.k){return K}var r="";var N=0;A.lR.lastIndex=0;var L=A.lR.exec(K);while(L){r+=K.substr(N,L.index-N);var M=y(A,L);if(M){v+=M[1];r+='<span class="'+M[0]+'">'+L[0]+"</span>"}else{r+=L[0]}N=A.lR.lastIndex;L=A.lR.exec(K)}return r+K.substr(N)}function z(){if(A.sL&&!e[A.sL]){return l(w)}var r=A.sL?d(A.sL,w):g(w);if(A.r>0){v+=r.keyword_count;B+=r.r}return'<span class="'+r.language+'">'+r.value+"</span>"}function J(){return A.sL!==undefined?z():G()}function I(L,r){var K=L.cN?'<span class="'+L.cN+'">':"";if(L.rB){x+=K;w=""}else{if(L.eB){x+=l(r)+K;w=""}else{x+=K;w=r}}A=Object.create(L,{parent:{value:A}});B+=L.r}function C(K,r){w+=K;if(r===undefined){x+=J();return 0}var L=o(r,A);if(L){x+=J();I(L,r);return L.rB?0:r.length}var M=s(A,r);if(M){if(!(M.rE||M.eE)){w+=r}x+=J();do{if(A.cN){x+="</span>"}A=A.parent}while(A!=M.parent);if(M.eE){x+=l(r)}w="";if(M.starts){I(M.starts,"")}return M.rE?0:r.length}if(t(r,A)){throw"Illegal"}w+=r;return r.length||1}var F=e[D];f(F);var A=F;var w="";var B=0;var v=0;var x="";try{var u,q,p=0;while(true){A.t.lastIndex=p;u=A.t.exec(E);if(!u){break}q=C(E.substr(p,u.index-p),u[0]);p=u.index+q}C(E.substr(p));return{r:B,keyword_count:v,value:x,language:D}}catch(H){if(H=="Illegal"){return{r:0,keyword_count:0,value:l(E)}}else{throw H}}}function g(s){var o={keyword_count:0,r:0,value:l(s)};var q=o;for(var p in e){if(!e.hasOwnProperty(p)){continue}var r=d(p,s);r.language=p;if(r.keyword_count+r.r>q.keyword_count+q.r){q=r}if(r.keyword_count+r.r>o.keyword_count+o.r){q=o;o=r}}if(q.language){o.second_best=q}return o}function i(q,p,o){if(p){q=q.replace(/^((<[^>]+>|\t)+)/gm,function(r,v,u,t){return v.replace(/\t/g,p)})}if(o){q=q.replace(/\n/g,"<br>")}return q}function m(r,u,p){var v=h(r,p);var t=a(r);if(t=="no-highlight"){return}var w=t?d(t,v):g(v);t=w.language;var o=c(r);if(o.length){var q=document.createElement("pre");q.innerHTML=w.value;w.value=j(o,c(q),v)}w.value=i(w.value,u,p);var s=r.className;if(!s.match("(\\s|^)(language-)?"+t+"(\\s|$)")){s=s?(s+" "+t):t}r.innerHTML=w.value;r.className=s;r.result={language:t,kw:w.keyword_count,re:w.r};if(w.second_best){r.second_best={language:w.second_best.language,kw:w.second_best.keyword_count,re:w.second_best.r}}}function n(){if(n.called){return}n.called=true;Array.prototype.map.call(document.getElementsByTagName("pre"),b).filter(Boolean).forEach(function(o){m(o,hljs.tabReplace)})}function k(){window.addEventListener("DOMContentLoaded",n,false);window.addEventListener("load",n,false)}var e={};this.LANGUAGES=e;this.highlight=d;this.highlightAuto=g;this.fixMarkup=i;this.highlightBlock=m;this.initHighlighting=n;this.initHighlightingOnLoad=k;this.IR="[a-zA-Z][a-zA-Z0-9_]*";this.UIR="[a-zA-Z_][a-zA-Z0-9_]*";this.NR="\\b\\d+(\\.\\d+)?";this.CNR="(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)";this.BNR="\\b(0b[01]+)";this.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|\\.|-|-=|/|/=|:|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~";this.BE={b:"\\\\[\\s\\S]",r:0};this.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[this.BE],r:0};this.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[this.BE],r:0};this.CLCM={cN:"comment",b:"//",e:"$"};this.CBLCLM={cN:"comment",b:"/\\*",e:"\\*/"};this.HCM={cN:"comment",b:"#",e:"$"};this.NM={cN:"number",b:this.NR,r:0};this.CNM={cN:"number",b:this.CNR,r:0};this.BNM={cN:"number",b:this.BNR,r:0};this.inherit=function(q,r){var o={};for(var p in q){o[p]=q[p]}if(r){for(var p in r){o[p]=r[p]}}return o}}();hljs.LANGUAGES.php=function(a){var e={cN:"variable",b:"\\$+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*"};var b=[a.inherit(a.ASM,{i:null}),a.inherit(a.QSM,{i:null}),{cN:"string",b:'b"',e:'"',c:[a.BE]},{cN:"string",b:"b'",e:"'",c:[a.BE]}];var c=[a.BNM,a.CNM];var d={cN:"title",b:a.UIR};return{cI:true,k:"and include_once list abstract global private echo interface as static endswitch array null if endwhile or const for endforeach self var while isset public protected exit foreach throw elseif include __FILE__ empty require_once do xor return implements parent clone use __CLASS__ __LINE__ else break print eval new catch __METHOD__ case exception php_user_filter default die require __FUNCTION__ enddeclare final try this switch continue endfor endif declare unset true false namespace trait goto instanceof insteadof __DIR__ __NAMESPACE__ __halt_compiler",c:[a.CLCM,a.HCM,{cN:"comment",b:"/\\*",e:"\\*/",c:[{cN:"phpdoc",b:"\\s@[A-Za-z]+"}]},{cN:"comment",eB:true,b:"__halt_compiler.+?;",eW:true},{cN:"string",b:"<<<['\"]?\\w+['\"]?$",e:"^\\w+;",c:[a.BE]},{cN:"preprocessor",b:"<\\?php",r:10},{cN:"preprocessor",b:"\\?>"},e,{cN:"function",bWK:true,e:"{",k:"function",i:"\\$|\\[|%",c:[d,{cN:"params",b:"\\(",e:"\\)",c:["self",e,a.CBLCLM].concat(b).concat(c)}]},{cN:"class",bWK:true,e:"{",k:"class",i:"[:\\(\\$]",c:[{bWK:true,eW:true,k:"extends",c:[d]},d]},{b:"=>"}].concat(b).concat(c)}}(hljs); + </script> + + <script type="text/javascript"> +/*! Sizzle v1.9.4-pre | (c) 2013 jQuery Foundation, Inc. | jquery.org/license +//@ sourceMappingURL=sizzle.min.map +*/(function(e,t){function n(e,t,n,r){var o,i,u,l,a,c,s,f,p,d;if((t?t.ownerDocument||t:U)!==H&&q(t),t=t||H,n=n||[],!e||"string"!=typeof e)return n;if(1!==(l=t.nodeType)&&9!==l)return[];if(O&&!r){if(o=Ct.exec(e))if(u=o[1]){if(9===l){if(i=t.getElementById(u),!i||!i.parentNode)return n;if(i.id===u)return n.push(i),n}else if(t.ownerDocument&&(i=t.ownerDocument.getElementById(u))&&j(t,i)&&i.id===u)return n.push(i),n}else{if(o[2])return ot.apply(n,t.getElementsByTagName(e)),n;if((u=o[3])&&S.getElementsByClassName&&t.getElementsByClassName)return ot.apply(n,t.getElementsByClassName(u)),n}if(S.qsa&&(!k||!k.test(e))){if(f=s=G,p=t,d=9===l&&e,1===l&&"object"!==t.nodeName.toLowerCase()){for(c=g(e),(s=t.getAttribute("id"))?f=s.replace(Tt,"\\$&"):t.setAttribute("id",f),f="[id='"+f+"'] ",a=c.length;a--;)c[a]=f+m(c[a]);p=mt.test(e)&&t.parentNode||t,d=c.join(",")}if(d)try{return ot.apply(n,p.querySelectorAll(d)),n}catch(h){}finally{s||t.removeAttribute("id")}}}return w(e.replace(dt,"$1"),t,n,r)}function r(e){return xt.test(e+"")}function o(){function e(n,r){return t.push(n+=" ")>L.cacheLength&&delete e[t.shift()],e[n]=r}var t=[];return e}function i(e){return e[G]=!0,e}function u(e){var t=H.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function l(e,t,n){e=e.split("|");for(var r,o=e.length,i=n?null:t;o--;)(r=L.attrHandle[e[o]])&&r!==t||(L.attrHandle[e[o]]=i)}function a(e,t){var n=e.getAttributeNode(t);return n&&n.specified?n.value:e[t]===!0?t.toLowerCase():null}function c(e,t){return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}function s(e){return"input"===e.nodeName.toLowerCase()?e.defaultValue:t}function f(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||_)-(~e.sourceIndex||_);if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function p(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function d(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function h(e){return i(function(t){return t=+t,i(function(n,r){for(var o,i=e([],n.length,t),u=i.length;u--;)n[o=i[u]]&&(n[o]=!(r[o]=n[o]))})})}function g(e,t){var r,o,i,u,l,a,c,s=K[e+" "];if(s)return t?0:s.slice(0);for(l=e,a=[],c=L.preFilter;l;){(!r||(o=ht.exec(l)))&&(o&&(l=l.slice(o[0].length)||l),a.push(i=[])),r=!1,(o=gt.exec(l))&&(r=o.shift(),i.push({value:r,type:o[0].replace(dt," ")}),l=l.slice(r.length));for(u in L.filter)!(o=bt[u].exec(l))||c[u]&&!(o=c[u](o))||(r=o.shift(),i.push({value:r,type:u,matches:o}),l=l.slice(r.length));if(!r)break}return t?l.length:l?n.error(e):K(e,a).slice(0)}function m(e){for(var t=0,n=e.length,r="";n>t;t++)r+=e[t].value;return r}function y(e,t,n){var r=t.dir,o=n&&"parentNode"===r,i=X++;return t.first?function(t,n,i){for(;t=t[r];)if(1===t.nodeType||o)return e(t,n,i)}:function(t,n,u){var l,a,c,s=V+" "+i;if(u){for(;t=t[r];)if((1===t.nodeType||o)&&e(t,n,u))return!0}else for(;t=t[r];)if(1===t.nodeType||o)if(c=t[G]||(t[G]={}),(a=c[r])&&a[0]===s){if((l=a[1])===!0||l===D)return l===!0}else if(a=c[r]=[s],a[1]=e(t,n,u)||D,a[1]===!0)return!0}}function v(e){return e.length>1?function(t,n,r){for(var o=e.length;o--;)if(!e[o](t,n,r))return!1;return!0}:e[0]}function N(e,t,n,r,o){for(var i,u=[],l=0,a=e.length,c=null!=t;a>l;l++)(i=e[l])&&(!n||n(i,r,o))&&(u.push(i),c&&t.push(l));return u}function b(e,t,n,r,o,u){return r&&!r[G]&&(r=b(r)),o&&!o[G]&&(o=b(o,u)),i(function(i,u,l,a){var c,s,f,p=[],d=[],h=u.length,g=i||E(t||"*",l.nodeType?[l]:l,[]),m=!e||!i&&t?g:N(g,p,e,l,a),y=n?o||(i?e:h||r)?[]:u:m;if(n&&n(m,y,l,a),r)for(c=N(y,d),r(c,[],l,a),s=c.length;s--;)(f=c[s])&&(y[d[s]]=!(m[d[s]]=f));if(i){if(o||e){if(o){for(c=[],s=y.length;s--;)(f=y[s])&&c.push(m[s]=f);o(null,y=[],c,a)}for(s=y.length;s--;)(f=y[s])&&(c=o?ut.call(i,f):p[s])>-1&&(i[c]=!(u[c]=f))}}else y=N(y===u?y.splice(h,y.length):y),o?o(null,u,y,a):ot.apply(u,y)})}function x(e){for(var t,n,r,o=e.length,i=L.relative[e[0].type],u=i||L.relative[" "],l=i?1:0,a=y(function(e){return e===t},u,!0),c=y(function(e){return ut.call(t,e)>-1},u,!0),s=[function(e,n,r){return!i&&(r||n!==P)||((t=n).nodeType?a(e,n,r):c(e,n,r))}];o>l;l++)if(n=L.relative[e[l].type])s=[y(v(s),n)];else{if(n=L.filter[e[l].type].apply(null,e[l].matches),n[G]){for(r=++l;o>r&&!L.relative[e[r].type];r++);return b(l>1&&v(s),l>1&&m(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(dt,"$1"),n,r>l&&x(e.slice(l,r)),o>r&&x(e=e.slice(r)),o>r&&m(e))}s.push(n)}return v(s)}function C(e,t){var r=0,o=t.length>0,u=e.length>0,l=function(i,l,a,c,s){var f,p,d,h=[],g=0,m="0",y=i&&[],v=null!=s,b=P,x=i||u&&L.find.TAG("*",s&&l.parentNode||l),C=V+=null==b?1:Math.random()||.1;for(v&&(P=l!==H&&l,D=r);null!=(f=x[m]);m++){if(u&&f){for(p=0;d=e[p++];)if(d(f,l,a)){c.push(f);break}v&&(V=C,D=++r)}o&&((f=!d&&f)&&g--,i&&y.push(f))}if(g+=m,o&&m!==g){for(p=0;d=t[p++];)d(y,h,l,a);if(i){if(g>0)for(;m--;)y[m]||h[m]||(h[m]=nt.call(c));h=N(h)}ot.apply(c,h),v&&!i&&h.length>0&&g+t.length>1&&n.uniqueSort(c)}return v&&(V=C,P=b),y};return o?i(l):l}function E(e,t,r){for(var o=0,i=t.length;i>o;o++)n(e,t[o],r);return r}function w(e,t,n,r){var o,i,u,l,a,c=g(e);if(!r&&1===c.length){if(i=c[0]=c[0].slice(0),i.length>2&&"ID"===(u=i[0]).type&&S.getById&&9===t.nodeType&&O&&L.relative[i[1].type]){if(t=(L.find.ID(u.matches[0].replace(At,St),t)||[])[0],!t)return n;e=e.slice(i.shift().value.length)}for(o=bt.needsContext.test(e)?0:i.length;o--&&(u=i[o],!L.relative[l=u.type]);)if((a=L.find[l])&&(r=a(u.matches[0].replace(At,St),mt.test(i[0].type)&&t.parentNode||t))){if(i.splice(o,1),e=r.length&&m(i),!e)return ot.apply(n,r),n;break}}return R(e,c)(r,t,!O,n,mt.test(e)),n}function T(){}var A,S,D,L,B,I,R,P,$,q,H,M,O,k,F,z,j,G="sizzle"+-new Date,U=e.document,V=0,X=0,J=o(),K=o(),Q=o(),W=!1,Y=function(){return 0},Z=typeof t,_=1<<31,et={}.hasOwnProperty,tt=[],nt=tt.pop,rt=tt.push,ot=tt.push,it=tt.slice,ut=tt.indexOf||function(e){for(var t=0,n=this.length;n>t;t++)if(this[t]===e)return t;return-1},lt="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",at="[\\x20\\t\\r\\n\\f]",ct="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",st=ct.replace("w","w#"),ft="\\["+at+"*("+ct+")"+at+"*(?:([*^$|!~]?=)"+at+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+st+")|)|)"+at+"*\\]",pt=":("+ct+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+ft.replace(3,8)+")*)|.*)\\)|)",dt=RegExp("^"+at+"+|((?:^|[^\\\\])(?:\\\\.)*)"+at+"+$","g"),ht=RegExp("^"+at+"*,"+at+"*"),gt=RegExp("^"+at+"*([>+~]|"+at+")"+at+"*"),mt=RegExp(at+"*[+~]"),yt=RegExp("="+at+"*([^\\]'\"]*)"+at+"*\\]","g"),vt=RegExp(pt),Nt=RegExp("^"+st+"$"),bt={ID:RegExp("^#("+ct+")"),CLASS:RegExp("^\\.("+ct+")"),TAG:RegExp("^("+ct.replace("w","w*")+")"),ATTR:RegExp("^"+ft),PSEUDO:RegExp("^"+pt),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+at+"*(even|odd|(([+-]|)(\\d*)n|)"+at+"*(?:([+-]|)"+at+"*(\\d+)|))"+at+"*\\)|)","i"),bool:RegExp("^(?:"+lt+")$","i"),needsContext:RegExp("^"+at+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+at+"*((?:-\\d)?\\d*)"+at+"*\\)|)(?=[^-]|$)","i")},xt=/^[^{]+\{\s*\[native \w/,Ct=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,Et=/^(?:input|select|textarea|button)$/i,wt=/^h\d$/i,Tt=/'|\\/g,At=RegExp("\\\\([\\da-f]{1,6}"+at+"?|("+at+")|.)","ig"),St=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(55296|r>>10,56320|1023&r)};try{ot.apply(tt=it.call(U.childNodes),U.childNodes),tt[U.childNodes.length].nodeType}catch(Dt){ot={apply:tt.length?function(e,t){rt.apply(e,it.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}I=n.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},S=n.support={},q=n.setDocument=function(e){var n=e?e.ownerDocument||e:U;return n!==H&&9===n.nodeType&&n.documentElement?(H=n,M=n.documentElement,O=!I(n),S.attributes=u(function(e){return e.innerHTML="<a href='#'></a>",l("type|href|height|width",c,"#"===e.firstChild.getAttribute("href")),l(lt,a,null==e.getAttribute("disabled")),e.className="i",!e.getAttribute("className")}),S.input=u(function(e){return e.innerHTML="<input>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")}),l("value",s,S.attributes&&S.input),S.getElementsByTagName=u(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),S.getElementsByClassName=u(function(e){return e.innerHTML="<div class='a'></div><div class='a i'></div>",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),S.getById=u(function(e){return M.appendChild(e).id=G,!n.getElementsByName||!n.getElementsByName(G).length}),S.getById?(L.find.ID=function(e,t){if(typeof t.getElementById!==Z&&O){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},L.filter.ID=function(e){var t=e.replace(At,St);return function(e){return e.getAttribute("id")===t}}):(delete L.find.ID,L.filter.ID=function(e){var t=e.replace(At,St);return function(e){var n=typeof e.getAttributeNode!==Z&&e.getAttributeNode("id");return n&&n.value===t}}),L.find.TAG=S.getElementsByTagName?function(e,n){return typeof n.getElementsByTagName!==Z?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],o=0,i=t.getElementsByTagName(e);if("*"===e){for(;n=i[o++];)1===n.nodeType&&r.push(n);return r}return i},L.find.CLASS=S.getElementsByClassName&&function(e,n){return typeof n.getElementsByClassName!==Z&&O?n.getElementsByClassName(e):t},F=[],k=[],(S.qsa=r(n.querySelectorAll))&&(u(function(e){e.innerHTML="<select><option selected=''></option></select>",e.querySelectorAll("[selected]").length||k.push("\\["+at+"*(?:value|"+lt+")"),e.querySelectorAll(":checked").length||k.push(":checked")}),u(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&k.push("[*^$]="+at+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||k.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),k.push(",.*:")})),(S.matchesSelector=r(z=M.webkitMatchesSelector||M.mozMatchesSelector||M.oMatchesSelector||M.msMatchesSelector))&&u(function(e){S.disconnectedMatch=z.call(e,"div"),z.call(e,"[s!='']:x"),F.push("!=",pt)}),k=k.length&&RegExp(k.join("|")),F=F.length&&RegExp(F.join("|")),j=r(M.contains)||M.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},S.sortDetached=u(function(e){return 1&e.compareDocumentPosition(n.createElement("div"))}),Y=M.compareDocumentPosition?function(e,t){if(e===t)return W=!0,0;var r=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t);return r?1&r||!S.sortDetached&&t.compareDocumentPosition(e)===r?e===n||j(U,e)?-1:t===n||j(U,t)?1:$?ut.call($,e)-ut.call($,t):0:4&r?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,o=0,i=e.parentNode,u=t.parentNode,l=[e],a=[t];if(e===t)return W=!0,0;if(!i||!u)return e===n?-1:t===n?1:i?-1:u?1:$?ut.call($,e)-ut.call($,t):0;if(i===u)return f(e,t);for(r=e;r=r.parentNode;)l.unshift(r);for(r=t;r=r.parentNode;)a.unshift(r);for(;l[o]===a[o];)o++;return o?f(l[o],a[o]):l[o]===U?-1:a[o]===U?1:0},n):H},n.matches=function(e,t){return n(e,null,null,t)},n.matchesSelector=function(e,t){if((e.ownerDocument||e)!==H&&q(e),t=t.replace(yt,"='$1']"),!(!S.matchesSelector||!O||F&&F.test(t)||k&&k.test(t)))try{var r=z.call(e,t);if(r||S.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(o){}return n(t,H,null,[e]).length>0},n.contains=function(e,t){return(e.ownerDocument||e)!==H&&q(e),j(e,t)},n.attr=function(e,n){(e.ownerDocument||e)!==H&&q(e);var r=L.attrHandle[n.toLowerCase()],o=r&&et.call(L.attrHandle,n.toLowerCase())?r(e,n,!O):t;return o===t?S.attributes||!O?e.getAttribute(n):(o=e.getAttributeNode(n))&&o.specified?o.value:null:o},n.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},n.uniqueSort=function(e){var t,n=[],r=0,o=0;if(W=!S.detectDuplicates,$=!S.sortStable&&e.slice(0),e.sort(Y),W){for(;t=e[o++];)t===e[o]&&(r=n.push(o));for(;r--;)e.splice(n[r],1)}return e},B=n.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=B(e)}else if(3===o||4===o)return e.nodeValue}else for(;t=e[r];r++)n+=B(t);return n},L=n.selectors={cacheLength:50,createPseudo:i,match:bt,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(At,St),e[3]=(e[4]||e[5]||"").replace(At,St),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||n.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&n.error(e[0]),e},PSEUDO:function(e){var n,r=!e[5]&&e[2];return bt.CHILD.test(e[0])?null:(e[3]&&e[4]!==t?e[2]=e[4]:r&&vt.test(r)&&(n=g(r,!0))&&(n=r.indexOf(")",r.length-n)-r.length)&&(e[0]=e[0].slice(0,n),e[2]=r.slice(0,n)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(At,St).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=J[e+" "];return t||(t=RegExp("(^|"+at+")"+e+"("+at+"|$)"))&&J(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==Z&&e.getAttribute("class")||"")})},ATTR:function(e,t,r){return function(o){var i=n.attr(o,e);return null==i?"!="===t:t?(i+="","="===t?i===r:"!="===t?i!==r:"^="===t?r&&0===i.indexOf(r):"*="===t?r&&i.indexOf(r)>-1:"$="===t?r&&i.slice(-r.length)===r:"~="===t?(" "+i+" ").indexOf(r)>-1:"|="===t?i===r||i.slice(0,r.length+1)===r+"-":!1):!0}},CHILD:function(e,t,n,r,o){var i="nth"!==e.slice(0,3),u="last"!==e.slice(-4),l="of-type"===t;return 1===r&&0===o?function(e){return!!e.parentNode}:function(t,n,a){var c,s,f,p,d,h,g=i!==u?"nextSibling":"previousSibling",m=t.parentNode,y=l&&t.nodeName.toLowerCase(),v=!a&&!l;if(m){if(i){for(;g;){for(f=t;f=f[g];)if(l?f.nodeName.toLowerCase()===y:1===f.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[u?m.firstChild:m.lastChild],u&&v){for(s=m[G]||(m[G]={}),c=s[e]||[],d=c[0]===V&&c[1],p=c[0]===V&&c[2],f=d&&m.childNodes[d];f=++d&&f&&f[g]||(p=d=0)||h.pop();)if(1===f.nodeType&&++p&&f===t){s[e]=[V,d,p];break}}else if(v&&(c=(t[G]||(t[G]={}))[e])&&c[0]===V)p=c[1];else for(;(f=++d&&f&&f[g]||(p=d=0)||h.pop())&&((l?f.nodeName.toLowerCase()!==y:1!==f.nodeType)||!++p||(v&&((f[G]||(f[G]={}))[e]=[V,p]),f!==t)););return p-=o,p===r||0===p%r&&p/r>=0}}},PSEUDO:function(e,t){var r,o=L.pseudos[e]||L.setFilters[e.toLowerCase()]||n.error("unsupported pseudo: "+e);return o[G]?o(t):o.length>1?(r=[e,e,"",t],L.setFilters.hasOwnProperty(e.toLowerCase())?i(function(e,n){for(var r,i=o(e,t),u=i.length;u--;)r=ut.call(e,i[u]),e[r]=!(n[r]=i[u])}):function(e){return o(e,0,r)}):o}},pseudos:{not:i(function(e){var t=[],n=[],r=R(e.replace(dt,"$1"));return r[G]?i(function(e,t,n,o){for(var i,u=r(e,null,o,[]),l=e.length;l--;)(i=u[l])&&(e[l]=!(t[l]=i))}):function(e,o,i){return t[0]=e,r(t,null,i,n),!n.pop()}}),has:i(function(e){return function(t){return n(e,t).length>0}}),contains:i(function(e){return function(t){return(t.textContent||t.innerText||B(t)).indexOf(e)>-1}}),lang:i(function(e){return Nt.test(e||"")||n.error("unsupported lang: "+e),e=e.replace(At,St).toLowerCase(),function(t){var n;do if(n=O?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===M},focus:function(e){return e===H.activeElement&&(!H.hasFocus||H.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!L.pseudos.empty(e)},header:function(e){return wt.test(e.nodeName)},input:function(e){return Et.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:h(function(){return[0]}),last:h(function(e,t){return[t-1]}),eq:h(function(e,t,n){return[0>n?n+t:n]}),even:h(function(e,t){for(var n=0;t>n;n+=2)e.push(n);return e}),odd:h(function(e,t){for(var n=1;t>n;n+=2)e.push(n);return e}),lt:h(function(e,t,n){for(var r=0>n?n+t:n;--r>=0;)e.push(r);return e}),gt:h(function(e,t,n){for(var r=0>n?n+t:n;t>++r;)e.push(r);return e})}};for(A in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})L.pseudos[A]=p(A);for(A in{submit:!0,reset:!0})L.pseudos[A]=d(A);R=n.compile=function(e,t){var n,r=[],o=[],i=Q[e+" "];if(!i){for(t||(t=g(e)),n=t.length;n--;)i=x(t[n]),i[G]?r.push(i):o.push(i);i=Q(e,C(o,r))}return i},L.pseudos.nth=L.pseudos.eq,T.prototype=L.filters=L.pseudos,L.setFilters=new T,S.sortStable=G.split("").sort(Y).join("")===G,q(),[0,0].sort(Y),S.detectDuplicates=W,"function"==typeof define&&define.amd?define(function(){return n}):e.Sizzle=n})(window); + </script> + + <script type="text/javascript"> +window.onload = function() { + var codeBlocks = Sizzle('pre'), + callStackItems = Sizzle('.call-stack-item'); + + // highlight code blocks + for (var i = 0, imax = codeBlocks.length; i < imax; ++i) { + hljs.highlightBlock(codeBlocks[i], ' '); + } + + // code block hover line + document.onmousemove = function(e) { + var event = e || window.event, + clientY = event.clientY, + lineFound = false, + hoverLines = Sizzle('.hover-line'); + + for (var i = 0, imax = codeBlocks.length - 1; i < imax; ++i) { + var lines = codeBlocks[i].getClientRects(); + for (var j = 0, jmax = lines.length; j < jmax; ++j) { + if (clientY >= lines[j].top && clientY <= lines[j].bottom) { + lineFound = true; + break; + } + } + if (lineFound) { + break; + } + } + + for (var k = 0, kmax = hoverLines.length; k < kmax; ++k) { + hoverLines[k].className = 'hover-line'; + } + if (lineFound) { + var line = Sizzle('.call-stack-item:eq(' + i + ') .hover-line:eq(' + j + ')')[0]; + if (line) { + line.className = 'hover-line hover'; + } + } + }; + + var refreshCallStackItemCode = function(callStackItem) { + if (!Sizzle('pre', callStackItem)[0]) { + return; + } + var top = callStackItem.offsetTop - window.pageYOffset, + lines = Sizzle('pre', callStackItem)[0].getClientRects(), + lineNumbers = Sizzle('.lines-item', callStackItem), + errorLine = Sizzle('.error-line', callStackItem)[0], + hoverLines = Sizzle('.hover-line', callStackItem); + for (var i = 0, imax = lines.length; i < imax; ++i) { + if (!lineNumbers[i]) { + continue; + } + lineNumbers[i].style.top = parseInt(lines[i].top - top) + 'px'; + hoverLines[i].style.top = parseInt(lines[i].top - top - 3) + 'px'; + hoverLines[i].style.height = parseInt(lines[i].bottom - lines[i].top + 6) + 'px'; + if (parseInt(callStackItem.getAttribute('data-line')) == i) { + errorLine.style.top = parseInt(lines[i].top - top - 3) + 'px'; + errorLine.style.height = parseInt(lines[i].bottom - lines[i].top + 6) + 'px'; + } + } + }; + + for (var i = 0, imax = callStackItems.length; i < imax; ++i) { + refreshCallStackItemCode(callStackItems[i]); + + // toggle code block visibility + Sizzle('.element-wrap', callStackItems[i])[0].addEventListener('click', function() { + var callStackItem = this.parentNode, + code = Sizzle('.code-wrap', callStackItem)[0]; + code.style.display = window.getComputedStyle(code).display == 'block' ? 'none' : 'block'; + refreshCallStackItemCode(callStackItem); + }); + } +}; + </script> +</body> + +</html> diff --git a/framework/yii/views/errorHandler/previousException.php b/framework/yii/views/errorHandler/previousException.php new file mode 100644 index 0000000..1cdf3c5 --- /dev/null +++ b/framework/yii/views/errorHandler/previousException.php @@ -0,0 +1,24 @@ +<?php +/** + * @var \yii\base\View $this + * @var \yii\base\Exception $exception + * @var string $previousHtml + * @var \yii\base\ErrorHandler $context + */ +$context = $this->context; +?> +<div class="previous"> + <span class="arrow">↵</span> + <h2> + <span>Caused by:</span> + <?php if ($exception instanceof \yii\base\Exception): ?> + <span><?php echo $context->htmlEncode($exception->getName()); ?></span> – + <?php echo $context->addTypeLinks(get_class($exception)); ?> + <?php else: ?> + <span><?php echo $context->htmlEncode(get_class($exception)); ?></span> + <?php endif; ?> + </h2> + <h3><?php echo $context->htmlEncode($exception->getMessage()); ?></h3> + <p>in <span class="file"><?php echo $exception->getFile(); ?></span> at line <span class="line"><?php echo $exception->getLine(); ?></span></p> + <?php echo $previousHtml; ?> +</div> diff --git a/framework/yii/views/exception.php b/framework/yii/views/exception.php deleted file mode 100644 index 0f26ed7..0000000 --- a/framework/yii/views/exception.php +++ /dev/null @@ -1,210 +0,0 @@ -<?php -/** - * @var \Exception $exception - * @var \yii\base\ErrorHandler $context - */ -$context = $this->context; -$title = $context->htmlEncode($exception instanceof \yii\base\Exception ? $exception->getName().' ('.get_class($exception).')' : get_class($exception)); -?> -<!DOCTYPE html> -<html> -<head> - <meta charset="utf-8" /> - <title><?php echo $title?></title> - <style> - html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{border:0;outline:0;font-size:100%;vertical-align:baseline;background:transparent;margin:0;padding:0;} - body{line-height:1;} - ol,ul{list-style:none;} - blockquote,q{quotes:none;} - blockquote:before,blockquote:after,q:before,q:after{content:none;} - :focus{outline:0;} - ins{text-decoration:none;} - del{text-decoration:line-through;} - table{border-collapse:collapse;border-spacing:0;} - - body { - font: normal 9pt "Verdana"; - color: #000; - background: #fff; - } - - h1 { - font: normal 18pt "Verdana"; - color: #f00; - margin-bottom: .5em; - } - - h2 { - font: normal 14pt "Verdana"; - color: #800000; - margin-bottom: .5em; - } - - h3 { - font: bold 11pt "Verdana"; - } - - pre { - font: normal 11pt Menlo, Consolas, "Lucida Console", Monospace; - } - - pre span.error { - display: block; - background: #fce3e3; - } - - pre span.ln { - color: #999; - padding-right: 0.5em; - border-right: 1px solid #ccc; - } - - pre span.error-ln { - font-weight: bold; - } - - .container { - margin: 1em 4em; - } - - .version { - color: gray; - font-size: 8pt; - border-top: 1px solid #aaa; - padding-top: 1em; - margin-bottom: 1em; - } - - .message { - color: #000; - padding: 1em; - font-size: 11pt; - background: #f3f3f3; - -webkit-border-radius: 10px; - -moz-border-radius: 10px; - border-radius: 10px; - margin-bottom: 1em; - line-height: 160%; - } - - .source { - margin-bottom: 1em; - } - - .code pre { - background-color: #ffe; - margin: 0.5em 0; - padding: 0.5em; - line-height: 125%; - border: 1px solid #eee; - } - - .source .file { - margin-bottom: 1em; - font-weight: bold; - } - - .traces { - margin: 2em 0; - } - - .trace { - margin: 0.5em 0; - padding: 0.5em; - } - - .trace.app { - border: 1px dashed #c00; - } - - .trace .number { - text-align: right; - width: 2em; - padding: 0.5em; - } - - .trace .content { - padding: 0.5em; - } - - .trace .plus, - .trace .minus { - display: inline; - vertical-align: middle; - text-align: center; - border: 1px solid #000; - color: #000; - font-size: 10px; - line-height: 10px; - margin: 0; - padding: 0 1px; - width: 10px; - height: 10px; - } - - .trace.collapsed .minus, - .trace.expanded .plus, - .trace.collapsed pre { - display: none; - } - - .trace-file { - cursor: pointer; - padding: 0.2em; - } - - .trace-file:hover { - background: #f0ffff; - } - </style> -</head> - -<body> -<div class="container"> - <h1><?php echo $title?></h1> - - <p class="message"> - <?php echo nl2br($context->htmlEncode($exception->getMessage()))?> - </p> - - <div class="source"> - <p class="file"> - <?php echo $context->htmlEncode($exception->getFile()) . '(' . $exception->getLine() . ')'?> - </p> - <?php if (YII_DEBUG) $context->renderSourceCode($exception->getFile(), $exception->getLine(), $context->maxSourceLines)?> - </div> - - <?php if (YII_DEBUG):?> - <div class="traces"> - <h2>Stack Trace</h2> - <?php $context->renderTrace($exception->getTrace())?> - </div> - <?php endif?> - - <div class="version"> - <?php echo date('Y-m-d H:i:s', time())?> - <?php echo YII_DEBUG ? $context->getVersionInfo() : ''?> - </div> -</div> - -<script> -var traceReg = new RegExp("(^|\\s)trace-file(\\s|$)"); -var collapsedReg = new RegExp("(^|\\s)collapsed(\\s|$)"); - -var e = document.getElementsByTagName('div'); -for (var j = 0, len = e.length; j < len; j++) { - if (traceReg.test(e[j].className)) { - e[j].onclick = function() { - var trace = this.parentNode.parentNode; - if (collapsedReg.test(trace.className)) { - trace.className = trace.className.replace('collapsed', 'expanded'); - } else { - trace.className = trace.className.replace('expanded', 'collapsed'); - } - } - } -} -</script> - -</body> -</html> diff --git a/framework/yii/web/AssetManager.php b/framework/yii/web/AssetManager.php index 95dcbd2..dfec576 100644 --- a/framework/yii/web/AssetManager.php +++ b/framework/yii/web/AssetManager.php @@ -282,6 +282,9 @@ class AssetManager extends Component */ public function getPublishedPath($path) { + if (isset($this->_published[$path])) { + return $this->_published[$path][0]; + } if (($path = realpath($path)) !== false) { $base = $this->basePath . DIRECTORY_SEPARATOR; if (is_file($path)) { @@ -304,7 +307,7 @@ class AssetManager extends Component public function getPublishedUrl($path) { if (isset($this->_published[$path])) { - return $this->_published[$path]; + return $this->_published[$path][1]; } if (($path = realpath($path)) !== false) { if (is_file($path)) { diff --git a/framework/yii/web/CacheSession.php b/framework/yii/web/CacheSession.php index c125f01..79acd74 100644 --- a/framework/yii/web/CacheSession.php +++ b/framework/yii/web/CacheSession.php @@ -97,10 +97,10 @@ class CacheSession extends Session /** * Generates a unique key used for storing session data in cache. * @param string $id session variable name - * @return string a safe cache key associated with the session variable name + * @return mixed a safe cache key associated with the session variable name */ protected function calculateKey($id) { - return $this->cache->buildKey(array(__CLASS__, $id)); + return array(__CLASS__, $id); } } diff --git a/framework/yii/web/UrlManager.php b/framework/yii/web/UrlManager.php index 5a3c391..47f5c5d 100644 --- a/framework/yii/web/UrlManager.php +++ b/framework/yii/web/UrlManager.php @@ -103,7 +103,7 @@ class UrlManager extends Component $this->cache = Yii::$app->getComponent($this->cache); } if ($this->cache instanceof Cache) { - $key = $this->cache->buildKey(__CLASS__); + $key = __CLASS__; $hash = md5(json_encode($this->rules)); if (($data = $this->cache->get($key)) !== false && isset($data[1]) && $data[1] === $hash) { $this->rules = $data[0]; diff --git a/framework/yii/widgets/ActiveField.php b/framework/yii/widgets/ActiveField.php index 45faf9d..e3e7f2b 100644 --- a/framework/yii/widgets/ActiveField.php +++ b/framework/yii/widgets/ActiveField.php @@ -6,6 +6,7 @@ */ namespace yii\widgets; +use Yii; use yii\base\Component; use yii\db\ActiveRecord; use yii\helpers\Html; @@ -101,6 +102,11 @@ class ActiveField extends Component */ public $selectors; + + /** + * Renders the opening tag of the field container. + * @return string the rendering result. + */ public function begin() { $options = $this->getClientOptions(); @@ -124,11 +130,19 @@ class ActiveField extends Component return Html::beginTag($this->tag, $options); } + /** + * Renders the closing tag of the field container. + * @return string the rendering result. + */ public function end() { return Html::endTag($this->tag); } + /** + * Returns the JS options for the field. + * @return array the JS options + */ protected function getClientOptions() { $enableClientValidation = $this->enableClientValidation || $this->enableClientValidation === null && $this->form->enableClientValidation; @@ -237,7 +251,7 @@ class ActiveField extends Component } /** - * Generates an input tag for the given model attribute. + * Renders a field containing an input field. * @param string $type the input type (e.g. 'text', 'password') * @param array $options the tag options in terms of name-value pairs. These will be rendered as * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. @@ -250,12 +264,12 @@ class ActiveField extends Component } /** - * Generates a text input tag for the given model attribute. + * Renders a field containing a text input. * This method will generate the "name" and "value" tag attributes automatically for the model attribute * unless they are explicitly specified in `$options`. * @param array $options the tag options in terms of name-value pairs. These will be rendered as * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. - * @return string the generated input tag + * @return string the rendering result */ public function textInput($options = array()) { @@ -264,12 +278,12 @@ class ActiveField extends Component } /** - * Generates a hidden input tag for the given model attribute. + * Renders a field containing a hidden input. * This method will generate the "name" and "value" tag attributes automatically for the model attribute * unless they are explicitly specified in `$options`. * @param array $options the tag options in terms of name-value pairs. These will be rendered as * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. - * @return string the generated input tag + * @return string the rendering result */ public function hiddenInput($options = array()) { @@ -278,12 +292,12 @@ class ActiveField extends Component } /** - * Generates a password input tag for the given model attribute. + * Renders a field containing a password input. * This method will generate the "name" and "value" tag attributes automatically for the model attribute * unless they are explicitly specified in `$options`. * @param array $options the tag options in terms of name-value pairs. These will be rendered as * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. - * @return string the generated input tag + * @return string the rendering result */ public function passwordInput($options = array()) { @@ -292,12 +306,12 @@ class ActiveField extends Component } /** - * Generates a file input tag for the given model attribute. + * Renders a field containing a file input. * This method will generate the "name" and "value" tag attributes automatically for the model attribute * unless they are explicitly specified in `$options`. * @param array $options the tag options in terms of name-value pairs. These will be rendered as * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. - * @return string the generated input tag + * @return string the rendering result */ public function fileInput($options = array()) { @@ -306,11 +320,11 @@ class ActiveField extends Component } /** - * Generates a textarea tag for the given model attribute. + * Renders a field containing a text area. * The model attribute value will be used as the content in the textarea. * @param array $options the tag options in terms of name-value pairs. These will be rendered as * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. - * @return string the generated textarea tag + * @return string the rendering result */ public function textarea($options = array()) { @@ -319,7 +333,7 @@ class ActiveField extends Component } /** - * Generates a radio button tag for the given model attribute. + * Renders a field containing a radio button. * This method will generate the "name" tag attribute automatically unless it is explicitly specified in `$options`. * This method will generate the "checked" tag attribute according to the model attribute value. * @param array $options the tag options in terms of name-value pairs. The following options are specially handled: @@ -334,7 +348,7 @@ class ActiveField extends Component * @param boolean $enclosedByLabel whether to enclose the radio within the label. * If true, the method will still use [[template]] to layout the checkbox and the error message * except that the radio is enclosed by the label tag. - * @return string the generated radio button tag + * @return string the rendering result */ public function radio($options = array(), $enclosedByLabel = true) { @@ -358,7 +372,7 @@ class ActiveField extends Component } /** - * Generates a checkbox tag for the given model attribute. + * Renders a field containing a checkbox. * This method will generate the "name" tag attribute automatically unless it is explicitly specified in `$options`. * This method will generate the "checked" tag attribute according to the model attribute value. * @param array $options the tag options in terms of name-value pairs. The following options are specially handled: @@ -373,7 +387,7 @@ class ActiveField extends Component * @param boolean $enclosedByLabel whether to enclose the checkbox within the label. * If true, the method will still use [[template]] to layout the checkbox and the error message * except that the checkbox is enclosed by the label tag. - * @return string the generated checkbox tag + * @return string the rendering result */ public function checkbox($options = array(), $enclosedByLabel = true) { @@ -397,7 +411,7 @@ class ActiveField extends Component } /** - * Generates a drop-down list for the given model attribute. + * Renders a field containing a drop-down list. * The selection of the drop-down list is taken from the value of the model attribute. * @param array $items the option data items. The array keys are option values, and the array values * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too). @@ -426,7 +440,7 @@ class ActiveField extends Component * The rest of the options will be rendered as the attributes of the resulting tag. The values will * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered. * - * @return string the generated drop-down list tag + * @return string the rendering result */ public function dropDownList($items, $options = array()) { @@ -435,7 +449,7 @@ class ActiveField extends Component } /** - * Generates a list box. + * Renders a field containing a list box. * The selection of the list box is taken from the value of the model attribute. * @param array $items the option data items. The array keys are option values, and the array values * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too). @@ -467,7 +481,7 @@ class ActiveField extends Component * The rest of the options will be rendered as the attributes of the resulting tag. The values will * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered. * - * @return string the generated list box tag + * @return string the rendering result */ public function listBox($items, $options = array()) { @@ -476,7 +490,7 @@ class ActiveField extends Component } /** - * Generates a list of checkboxes. + * Renders a field containing a list of checkboxes. * A checkbox list allows multiple selection, like [[listBox()]]. * As a result, the corresponding submitted value is an array. * The selection of the checkbox list is taken from the value of the model attribute. @@ -498,7 +512,7 @@ class ActiveField extends Component * where $index is the zero-based index of the checkbox in the whole list; $label * is the label for the checkbox; and $name, $value and $checked represent the name, * value and the checked status of the checkbox input. - * @return string the generated checkbox list + * @return string the rendering result */ public function checkboxList($items, $options = array()) { @@ -510,7 +524,7 @@ class ActiveField extends Component } /** - * Generates a list of radio buttons. + * Renders a field containing a list of radio buttons. * A radio button list is like a checkbox list, except that it only allows single selection. * The selection of the radio buttons is taken from the value of the model attribute. * @param array $items the data item used to generate the radio buttons. @@ -531,7 +545,7 @@ class ActiveField extends Component * where $index is the zero-based index of the radio button in the whole list; $label * is the label for the radio button; and $name, $value and $checked represent the name, * value and the checked status of the radio button input. - * @return string the generated radio button list + * @return string the rendering result */ public function radioList($items, $options = array()) { @@ -541,4 +555,16 @@ class ActiveField extends Component . '</div>' ); } + + /** + * Renders a field containing a widget. + * @param string $class the widget class name + * @param array $config name-value pairs that will be used to initialize the widget + * @return string the rendering result + */ + public function widget($class, $config = array()) + { + /** @var \yii\base\Widget $class */ + return $this->render($class::widget($config)); + } } diff --git a/framework/yii/widgets/FragmentCache.php b/framework/yii/widgets/FragmentCache.php index aa24acd..8445955 100644 --- a/framework/yii/widgets/FragmentCache.php +++ b/framework/yii/widgets/FragmentCache.php @@ -159,7 +159,7 @@ class FragmentCache extends Widget /** * Generates a unique key used for storing the content in cache. * The key generated depends on both [[id]] and [[variations]]. - * @return string a valid cache key + * @return mixed a valid cache key */ protected function calculateKey() { @@ -169,6 +169,6 @@ class FragmentCache extends Widget $factors[] = $factor; } } - return $this->cache->buildKey($factors); + return $factors; } } diff --git a/framework/yii/widgets/Menu.php b/framework/yii/widgets/Menu.php index d63f202..08088d3 100644 --- a/framework/yii/widgets/Menu.php +++ b/framework/yii/widgets/Menu.php @@ -63,7 +63,8 @@ class Menu extends Widget * - template: string, optional, the template used to render the content of this menu item. * The token `{url}` will be replaced by the URL associated with this menu item, * and the token `{label}` will be replaced by the label of the menu item. - * If this option is not set, [[linkTemplate]] or [[labelTemplate]] will be used instead. + * If this option is not set, [[linkTemplate]] or [[labelTemplate]] will be used instead. + * - options: array, optional, the HTML attributes for the menu container tag. */ public $items = array(); /** @@ -163,7 +164,7 @@ class Menu extends Widget $n = count($items); $lines = array(); foreach ($items as $i => $item) { - $options = isset($item['itemOptions']) ? $item['itemOptions'] : array(); + $options = isset($item['options']) ? $item['options'] : array(); $class = array(); if ($item['active']) { $class[] = $this->activeCssClass; diff --git a/tests/unit/MysqlTestCase.php b/tests/unit/MysqlTestCase.php deleted file mode 100644 index 3c16e03..0000000 --- a/tests/unit/MysqlTestCase.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -namespace yiiunit; - -class MysqlTestCase extends TestCase -{ - protected function setUp() - { - parent::setUp(); - if (!extension_loaded('pdo') || !extension_loaded('pdo_mysql')) { - $this->markTestSkipped('pdo and pdo_mysql extensions are required.'); - } - } - - /** - * @param bool $reset whether to clean up the test database - * @return \yii\db\Connection - */ - public function getConnection($reset = true) - { - $params = $this->getParam('mysql'); - $db = new \yii\db\Connection; - $db->dsn = $params['dsn']; - $db->username = $params['username']; - $db->password = $params['password']; - if ($reset) { - $db->open(); - $lines = explode(';', file_get_contents($params['fixture'])); - foreach ($lines as $line) { - if (trim($line) !== '') { - $db->pdo->exec($line); - } - } - } - return $db; - } -} diff --git a/tests/unit/TestCase.php b/tests/unit/TestCase.php index 44cb238..479f85d 100644 --- a/tests/unit/TestCase.php +++ b/tests/unit/TestCase.php @@ -5,7 +5,7 @@ namespace yiiunit; /** * This is the base class for all yii framework unit tests. */ -class TestCase extends \yii\test\TestCase +abstract class TestCase extends \yii\test\TestCase { public static $params; diff --git a/tests/unit/framework/caching/ApcCacheTest.php b/tests/unit/framework/caching/ApcCacheTest.php index c059554..17bcb6e 100644 --- a/tests/unit/framework/caching/ApcCacheTest.php +++ b/tests/unit/framework/caching/ApcCacheTest.php @@ -1,12 +1,11 @@ <?php namespace yiiunit\framework\caching; use yii\caching\ApcCache; -use yiiunit\TestCase; /** * Class for testing APC cache backend */ -class ApcCacheTest extends CacheTest +class ApcCacheTest extends CacheTestCase { private $_cacheInstance = null; diff --git a/tests/unit/framework/caching/CacheTest.php b/tests/unit/framework/caching/CacheTestCase.php similarity index 97% rename from tests/unit/framework/caching/CacheTest.php rename to tests/unit/framework/caching/CacheTestCase.php index 4b34de0..81ac5cc 100644 --- a/tests/unit/framework/caching/CacheTest.php +++ b/tests/unit/framework/caching/CacheTestCase.php @@ -7,7 +7,7 @@ namespace yii\caching; * @return int */ function time() { - return \yiiunit\framework\caching\CacheTest::$time ?: \time(); + return \yiiunit\framework\caching\CacheTestCase::$time ?: \time(); } namespace yiiunit\framework\caching; @@ -18,7 +18,7 @@ use yii\caching\Cache; /** * Base class for testing cache backends */ -abstract class CacheTest extends TestCase +abstract class CacheTestCase extends TestCase { /** * @var int virtual time to be returned by mocked time() function. @@ -65,7 +65,7 @@ abstract class CacheTest extends TestCase { $cache = $this->getCacheInstance(); $this->assertNotNull(\Yii::$app->id); - $this->assertEquals(\Yii::$app->id, $cache->keyPrefix); + $this->assertNotNull($cache->keyPrefix); } public function testSet() diff --git a/tests/unit/framework/caching/DbCacheTest.php b/tests/unit/framework/caching/DbCacheTest.php index f5bbba5..3a26595 100644 --- a/tests/unit/framework/caching/DbCacheTest.php +++ b/tests/unit/framework/caching/DbCacheTest.php @@ -3,12 +3,11 @@ namespace yiiunit\framework\caching; use yii\caching\DbCache; -use yiiunit\TestCase; /** * Class for testing file cache backend */ -class DbCacheTest extends CacheTest +class DbCacheTest extends CacheTestCase { private $_cacheInstance; private $_connection; diff --git a/tests/unit/framework/caching/FileCacheTest.php b/tests/unit/framework/caching/FileCacheTest.php index b3ac8b7..62f8637 100644 --- a/tests/unit/framework/caching/FileCacheTest.php +++ b/tests/unit/framework/caching/FileCacheTest.php @@ -1,12 +1,11 @@ <?php namespace yiiunit\framework\caching; use yii\caching\FileCache; -use yiiunit\TestCase; /** * Class for testing file cache backend */ -class FileCacheTest extends CacheTest +class FileCacheTest extends CacheTestCase { private $_cacheInstance = null; diff --git a/tests/unit/framework/caching/MemCacheTest.php b/tests/unit/framework/caching/MemCacheTest.php index 2855e2c..d110d26 100644 --- a/tests/unit/framework/caching/MemCacheTest.php +++ b/tests/unit/framework/caching/MemCacheTest.php @@ -1,12 +1,11 @@ <?php namespace yiiunit\framework\caching; use yii\caching\MemCache; -use yiiunit\TestCase; /** * Class for testing memcache cache backend */ -class MemCacheTest extends CacheTest +class MemCacheTest extends CacheTestCase { private $_cacheInstance = null; diff --git a/tests/unit/framework/caching/MemCachedTest.php b/tests/unit/framework/caching/MemCachedTest.php index bce23ba..8eb8466 100644 --- a/tests/unit/framework/caching/MemCachedTest.php +++ b/tests/unit/framework/caching/MemCachedTest.php @@ -1,12 +1,11 @@ <?php namespace yiiunit\framework\caching; use yii\caching\MemCache; -use yiiunit\TestCase; /** * Class for testing memcached cache backend */ -class MemCachedTest extends CacheTest +class MemCachedTest extends CacheTestCase { private $_cacheInstance = null; diff --git a/tests/unit/framework/caching/WinCacheTest.php b/tests/unit/framework/caching/WinCacheTest.php index 1b3399c..373145d 100644 --- a/tests/unit/framework/caching/WinCacheTest.php +++ b/tests/unit/framework/caching/WinCacheTest.php @@ -1,12 +1,11 @@ <?php namespace yiiunit\framework\caching; -use yii\caching\FileCache; -use yiiunit\TestCase; +use yii\caching\WinCache; /** * Class for testing wincache backend */ -class WinCacheTest extends CacheTest +class WinCacheTest extends CacheTestCase { private $_cacheInstance = null; diff --git a/tests/unit/framework/caching/XCacheTest.php b/tests/unit/framework/caching/XCacheTest.php index 5e952af..2749295 100644 --- a/tests/unit/framework/caching/XCacheTest.php +++ b/tests/unit/framework/caching/XCacheTest.php @@ -1,12 +1,11 @@ <?php namespace yiiunit\framework\caching; use yii\caching\XCache; -use yiiunit\TestCase; /** * Class for testing xcache backend */ -class XCacheTest extends CacheTest +class XCacheTest extends CacheTestCase { private $_cacheInstance = null; diff --git a/tests/unit/framework/caching/ZendDataCacheTest.php b/tests/unit/framework/caching/ZendDataCacheTest.php index 20aa3fc..f630e05 100644 --- a/tests/unit/framework/caching/ZendDataCacheTest.php +++ b/tests/unit/framework/caching/ZendDataCacheTest.php @@ -1,12 +1,12 @@ <?php namespace yiiunit\framework\caching; +use yii\caching\Cache; use yii\caching\ZendDataCache; -use yiiunit\TestCase; /** * Class for testing Zend cache backend */ -class ZendDataCacheTest extends CacheTest +class ZendDataCacheTest extends CacheTestCase { private $_cacheInstance = null; diff --git a/tests/unit/framework/console/controllers/AssetControllerTest.php b/tests/unit/framework/console/controllers/AssetControllerTest.php index d792c9e..db6d2a7 100644 --- a/tests/unit/framework/console/controllers/AssetControllerTest.php +++ b/tests/unit/framework/console/controllers/AssetControllerTest.php @@ -169,6 +169,23 @@ class AssetControllerTest extends TestCase } } + /** + * Invokes the asset controller method even if it is protected. + * @param string $methodName name of the method to be invoked. + * @param array $args method arguments. + * @return mixed method invoke result. + */ + protected function invokeAssetControllerMethod($methodName, array $args = array()) + { + $controller = $this->createAssetController(); + $controllerClassReflection = new ReflectionClass(get_class($controller)); + $methodReflection = $controllerClassReflection->getMethod($methodName); + $methodReflection->setAccessible(true); + $result = $methodReflection->invokeArgs($controller, $args); + $methodReflection->setAccessible(false); + return $result; + } + // Tests : public function testActionTemplate() @@ -237,4 +254,65 @@ class AssetControllerTest extends TestCase $this->assertContains($content, $compressedJsFileContent, "Source of '{$name}' is missing in combined file!"); } } + + /** + * Data provider for [[testAdjustCssUrl()]]. + * @return array test data. + */ + public function adjustCssUrlDataProvider() + { + return array( + array( + '.published-same-dir-class {background-image: url(published_same_dir.png);}', + '/test/base/path/assets/input', + '/test/base/path/assets/output', + '.published-same-dir-class {background-image: url(../input/published_same_dir.png);}', + ), + array( + '.published-relative-dir-class {background-image: url(../img/published_relative_dir.png);}', + '/test/base/path/assets/input', + '/test/base/path/assets/output', + '.published-relative-dir-class {background-image: url(../img/published_relative_dir.png);}', + ), + array( + '.static-same-dir-class {background-image: url(\'static_same_dir.png\');}', + '/test/base/path/css', + '/test/base/path/assets/output', + '.static-same-dir-class {background-image: url(\'../../css/static_same_dir.png\');}', + ), + array( + '.static-relative-dir-class {background-image: url("../img/static_relative_dir.png");}', + '/test/base/path/css', + '/test/base/path/assets/output', + '.static-relative-dir-class {background-image: url("../../img/static_relative_dir.png");}', + ), + array( + '.absolute-url-class {background-image: url(http://domain.com/img/image.gif);}', + '/test/base/path/assets/input', + '/test/base/path/assets/output', + '.absolute-url-class {background-image: url(http://domain.com/img/image.gif);}', + ), + array( + '.absolute-url-secure-class {background-image: url(https://secure.domain.com/img/image.gif);}', + '/test/base/path/assets/input', + '/test/base/path/assets/output', + '.absolute-url-secure-class {background-image: url(https://secure.domain.com/img/image.gif);}', + ), + ); + } + + /** + * @dataProvider adjustCssUrlDataProvider + * + * @param $cssContent + * @param $inputFilePath + * @param $outputFilePath + * @param $expectedCssContent + */ + public function testAdjustCssUrl($cssContent, $inputFilePath, $outputFilePath, $expectedCssContent) + { + $adjustedCssContent = $this->invokeAssetControllerMethod('adjustCssUrl', array($cssContent, $inputFilePath, $outputFilePath)); + + $this->assertEquals($expectedCssContent, $adjustedCssContent, 'Unable to adjust CSS correctly!'); + } } diff --git a/tests/unit/framework/db/ActiveRecordTest.php b/tests/unit/framework/db/ActiveRecordTest.php index f86ca8e..c510cb0 100644 --- a/tests/unit/framework/db/ActiveRecordTest.php +++ b/tests/unit/framework/db/ActiveRecordTest.php @@ -1,5 +1,4 @@ <?php - namespace yiiunit\framework\db; use yii\db\Query; @@ -10,7 +9,7 @@ use yiiunit\data\ar\OrderItem; use yiiunit\data\ar\Order; use yiiunit\data\ar\Item; -class ActiveRecordTest extends \yiiunit\DatabaseTestCase +class ActiveRecordTest extends DatabaseTestCase { protected function setUp() { diff --git a/tests/unit/framework/db/CommandTest.php b/tests/unit/framework/db/CommandTest.php index 498df2a..946c31f 100644 --- a/tests/unit/framework/db/CommandTest.php +++ b/tests/unit/framework/db/CommandTest.php @@ -7,7 +7,7 @@ use yii\db\Command; use yii\db\Query; use yii\db\DataReader; -class CommandTest extends \yiiunit\DatabaseTestCase +class CommandTest extends DatabaseTestCase { function testConstruct() { diff --git a/tests/unit/framework/db/ConnectionTest.php b/tests/unit/framework/db/ConnectionTest.php index c837b95..d5aa26a 100644 --- a/tests/unit/framework/db/ConnectionTest.php +++ b/tests/unit/framework/db/ConnectionTest.php @@ -4,7 +4,7 @@ namespace yiiunit\framework\db; use yii\db\Connection; -class ConnectionTest extends \yiiunit\DatabaseTestCase +class ConnectionTest extends DatabaseTestCase { function testConstruct() { diff --git a/tests/unit/DatabaseTestCase.php b/tests/unit/framework/db/DatabaseTestCase.php similarity index 94% rename from tests/unit/DatabaseTestCase.php rename to tests/unit/framework/db/DatabaseTestCase.php index 9d54173..429d961 100644 --- a/tests/unit/DatabaseTestCase.php +++ b/tests/unit/framework/db/DatabaseTestCase.php @@ -1,8 +1,9 @@ <?php +namespace yiiunit\framework\db; -namespace yiiunit; +use yiiunit\TestCase as TestCase; -class DatabaseTestCase extends TestCase +abstract class DatabaseTestCase extends TestCase { protected $database; protected $driverName = 'mysql'; diff --git a/tests/unit/framework/db/QueryTest.php b/tests/unit/framework/db/QueryTest.php index 9486acb..8362906 100644 --- a/tests/unit/framework/db/QueryTest.php +++ b/tests/unit/framework/db/QueryTest.php @@ -7,7 +7,7 @@ use yii\db\Command; use yii\db\Query; use yii\db\DataReader; -class QueryTest extends \yiiunit\DatabaseTestCase +class QueryTest extends DatabaseTestCase { function testSelect() { diff --git a/tests/unit/framework/db/mssql/MssqlActiveRecordTest.php b/tests/unit/framework/db/mssql/MssqlActiveRecordTest.php index 2cf0b01..76c30b9 100644 --- a/tests/unit/framework/db/mssql/MssqlActiveRecordTest.php +++ b/tests/unit/framework/db/mssql/MssqlActiveRecordTest.php @@ -2,7 +2,9 @@ namespace yiiunit\framework\db\mssql; -class MssqlActiveRecordTest extends \yiiunit\framework\db\ActiveRecordTest +use yiiunit\framework\db\ActiveRecordTest; + +class MssqlActiveRecordTest extends ActiveRecordTest { protected function setUp() { diff --git a/tests/unit/framework/db/mssql/MssqlCommandTest.php b/tests/unit/framework/db/mssql/MssqlCommandTest.php index 11d7565..f3c66c1 100644 --- a/tests/unit/framework/db/mssql/MssqlCommandTest.php +++ b/tests/unit/framework/db/mssql/MssqlCommandTest.php @@ -2,7 +2,9 @@ namespace yiiunit\framework\db\mssql; -class MssqlCommandTest extends \yiiunit\framework\db\CommandTest +use yiiunit\framework\db\CommandTest; + +class MssqlCommandTest extends CommandTest { public function setUp() { diff --git a/tests/unit/framework/db/mssql/MssqlConnectionTest.php b/tests/unit/framework/db/mssql/MssqlConnectionTest.php index 870af0a..42ac380 100644 --- a/tests/unit/framework/db/mssql/MssqlConnectionTest.php +++ b/tests/unit/framework/db/mssql/MssqlConnectionTest.php @@ -2,7 +2,9 @@ namespace yiiunit\framework\db\mssql; -class MssqlConnectionTest extends \yiiunit\framework\db\ConnectionTest +use yiiunit\framework\db\ConnectionTest; + +class MssqlConnectionTest extends ConnectionTest { public function setUp() { diff --git a/tests/unit/framework/db/mssql/MssqlQueryTest.php b/tests/unit/framework/db/mssql/MssqlQueryTest.php index 17d6681..08af19d 100644 --- a/tests/unit/framework/db/mssql/MssqlQueryTest.php +++ b/tests/unit/framework/db/mssql/MssqlQueryTest.php @@ -2,7 +2,9 @@ namespace yiiunit\framework\db\mssql; -class MssqlQueryTest extends \yiiunit\framework\db\QueryTest +use yiiunit\framework\db\QueryTest; + +class MssqlQueryTest extends QueryTest { public function setUp() { diff --git a/tests/unit/framework/db/sqlite/SqliteActiveRecordTest.php b/tests/unit/framework/db/sqlite/SqliteActiveRecordTest.php index 4779f85..573108e 100644 --- a/tests/unit/framework/db/sqlite/SqliteActiveRecordTest.php +++ b/tests/unit/framework/db/sqlite/SqliteActiveRecordTest.php @@ -1,8 +1,9 @@ <?php - namespace yiiunit\framework\db\sqlite; -class SqliteActiveRecordTest extends \yiiunit\framework\db\ActiveRecordTest +use yiiunit\framework\db\ActiveRecordTest; + +class SqliteActiveRecordTest extends ActiveRecordTest { protected function setUp() { diff --git a/tests/unit/framework/db/sqlite/SqliteCommandTest.php b/tests/unit/framework/db/sqlite/SqliteCommandTest.php index 3898aa2..750ea11 100644 --- a/tests/unit/framework/db/sqlite/SqliteCommandTest.php +++ b/tests/unit/framework/db/sqlite/SqliteCommandTest.php @@ -1,8 +1,9 @@ <?php - namespace yiiunit\framework\db\sqlite; -class SqliteCommandTest extends \yiiunit\framework\db\CommandTest +use yiiunit\framework\db\CommandTest; + +class SqliteCommandTest extends CommandTest { protected function setUp() { diff --git a/tests/unit/framework/db/sqlite/SqliteConnectionTest.php b/tests/unit/framework/db/sqlite/SqliteConnectionTest.php index 5685137..e33c5ad 100644 --- a/tests/unit/framework/db/sqlite/SqliteConnectionTest.php +++ b/tests/unit/framework/db/sqlite/SqliteConnectionTest.php @@ -1,8 +1,9 @@ <?php - namespace yiiunit\framework\db\sqlite; -class SqliteConnectionTest extends \yiiunit\framework\db\ConnectionTest +use yiiunit\framework\db\ConnectionTest; + +class SqliteConnectionTest extends ConnectionTest { protected function setUp() { diff --git a/tests/unit/framework/db/sqlite/SqliteQueryTest.php b/tests/unit/framework/db/sqlite/SqliteQueryTest.php index 2a3a831..66068d2 100644 --- a/tests/unit/framework/db/sqlite/SqliteQueryTest.php +++ b/tests/unit/framework/db/sqlite/SqliteQueryTest.php @@ -1,16 +1,9 @@ <?php -/** - * Created by JetBrains PhpStorm. - * User: RusMaxim - * Date: 09.05.13 - * Time: 21:41 - * To change this template use File | Settings | File Templates. - */ - namespace yiiunit\framework\db\sqlite; +use yiiunit\framework\db\QueryTest; -class SqliteQueryTest extends \yiiunit\framework\db\QueryTest +class SqliteQueryTest extends QueryTest { protected function setUp() { diff --git a/tests/unit/framework/helpers/ArrayHelperTest.php b/tests/unit/framework/helpers/ArrayHelperTest.php index 60a129f..84277d6 100644 --- a/tests/unit/framework/helpers/ArrayHelperTest.php +++ b/tests/unit/framework/helpers/ArrayHelperTest.php @@ -3,10 +3,10 @@ namespace yiiunit\framework\helpers; use yii\helpers\ArrayHelper; -use yii\helpers\VarDumper; +use yii\test\TestCase; use yii\web\Sort; -class ArrayHelperTest extends \yii\test\TestCase +class ArrayHelperTest extends TestCase { public function testMerge() { diff --git a/tests/unit/framework/helpers/ConsoleTest.php b/tests/unit/framework/helpers/ConsoleTest.php new file mode 100644 index 0000000..3bfa56d --- /dev/null +++ b/tests/unit/framework/helpers/ConsoleTest.php @@ -0,0 +1,79 @@ +<?php + +namespace yiiunit\framework\helpers; + +use Yii; +use yii\helpers\Console; +use yiiunit\TestCase; + +class ConsoleTest extends TestCase +{ + public function testStripAnsiFormat() + { + ob_start(); + ob_implicit_flush(false); + echo 'a'; + Console::moveCursorForward(1); + echo 'a'; + Console::moveCursorDown(1); + echo 'a'; + Console::moveCursorUp(1); + echo 'a'; + Console::moveCursorBackward(1); + echo 'a'; + Console::moveCursorNextLine(1); + echo 'a'; + Console::moveCursorPrevLine(1); + echo 'a'; + Console::moveCursorTo(1); + echo 'a'; + Console::moveCursorTo(1, 2); + echo 'a'; + Console::clearLine(); + echo 'a'; + Console::clearLineAfterCursor(); + echo 'a'; + Console::clearLineBeforeCursor(); + echo 'a'; + Console::clearScreen(); + echo 'a'; + Console::clearScreenAfterCursor(); + echo 'a'; + Console::clearScreenBeforeCursor(); + echo 'a'; + Console::scrollDown(); + echo 'a'; + Console::scrollUp(); + echo 'a'; + Console::hideCursor(); + echo 'a'; + Console::showCursor(); + echo 'a'; + Console::saveCursorPosition(); + echo 'a'; + Console::restoreCursorPosition(); + echo 'a'; + Console::beginAnsiFormat(array(Console::FG_GREEN, Console::BG_BLUE, Console::UNDERLINE)); + echo 'a'; + Console::endAnsiFormat(); + echo 'a'; + Console::beginAnsiFormat(array(Console::xtermBgColor(128), Console::xtermFgColor(55))); + echo 'a'; + Console::endAnsiFormat(); + echo 'a'; + $ouput = Console::stripAnsiFormat(ob_get_clean()); + ob_implicit_flush(true); + // $output = str_replace("\033", 'X003', $ouput );// uncomment for debugging + $this->assertEquals(str_repeat('a', 25), $ouput); + } + +/* public function testScreenSize() + { + for($i = 1; $i < 20; $i++) { + echo implode(', ', Console::getScreenSize(true)) . "\n"; + ob_flush(); + sleep(1); + } + }*/ + +} diff --git a/tests/unit/framework/helpers/JsonTest.php b/tests/unit/framework/helpers/JsonTest.php index 1795ce6..5504985 100644 --- a/tests/unit/framework/helpers/JsonTest.php +++ b/tests/unit/framework/helpers/JsonTest.php @@ -4,9 +4,10 @@ namespace yiiunit\framework\helpers; use yii\helpers\Json; +use yii\test\TestCase; use yii\web\JsExpression; -class JsonTest extends \yii\test\TestCase +class JsonTest extends TestCase { public function testEncode() { diff --git a/tests/unit/framework/helpers/StringHelperTest.php b/tests/unit/framework/helpers/StringHelperTest.php index ac6ff34..b7f422a 100644 --- a/tests/unit/framework/helpers/StringHelperTest.php +++ b/tests/unit/framework/helpers/StringHelperTest.php @@ -1,11 +1,12 @@ <?php namespace yiiunit\framework\helpers; use \yii\helpers\StringHelper as StringHelper; +use yii\test\TestCase; /** * StringHelperTest */ -class StringHelperTest extends \yii\test\TestCase +class StringHelperTest extends TestCase { public function testStrlen() { diff --git a/tests/unit/framework/helpers/VarDumperTest.php b/tests/unit/framework/helpers/VarDumperTest.php index e734863..7cd04a5 100644 --- a/tests/unit/framework/helpers/VarDumperTest.php +++ b/tests/unit/framework/helpers/VarDumperTest.php @@ -1,8 +1,9 @@ <?php namespace yiiunit\framework\helpers; use \yii\helpers\VarDumper; +use yii\test\TestCase; -class VarDumperTest extends \yii\test\TestCase +class VarDumperTest extends TestCase { public function testDumpObject() { diff --git a/tests/unit/framework/rbac/ManagerTestBase.php b/tests/unit/framework/rbac/ManagerTestCase.php similarity index 99% rename from tests/unit/framework/rbac/ManagerTestBase.php rename to tests/unit/framework/rbac/ManagerTestCase.php index 1476f9d..7cb4941 100644 --- a/tests/unit/framework/rbac/ManagerTestBase.php +++ b/tests/unit/framework/rbac/ManagerTestCase.php @@ -6,7 +6,7 @@ use yii\rbac\Assignment; use yii\rbac\Item; use yiiunit\TestCase; -abstract class ManagerTestBase extends TestCase +abstract class ManagerTestCase extends TestCase { /** @var \yii\rbac\PhpManager|\yii\rbac\DbManager */ protected $auth; diff --git a/tests/unit/framework/rbac/PhpManagerTest.php b/tests/unit/framework/rbac/PhpManagerTest.php index d5ab41b..b3b7c4f 100644 --- a/tests/unit/framework/rbac/PhpManagerTest.php +++ b/tests/unit/framework/rbac/PhpManagerTest.php @@ -5,9 +5,7 @@ namespace yiiunit\framework\rbac; use Yii; use yii\rbac\PhpManager; -//require_once(__DIR__ . '/ManagerTestBase.php'); - -class PhpManagerTest extends ManagerTestBase +class PhpManagerTest extends ManagerTestCase { protected function setUp() { diff --git a/tests/unit/framework/web/UrlManagerTest.php b/tests/unit/framework/web/UrlManagerTest.php index 553b163..0f08790 100644 --- a/tests/unit/framework/web/UrlManagerTest.php +++ b/tests/unit/framework/web/UrlManagerTest.php @@ -3,8 +3,9 @@ namespace yiiunit\framework\web; use yii\web\Request; use yii\web\UrlManager; +use yiiunit\TestCase; -class UrlManagerTest extends \yiiunit\TestCase +class UrlManagerTest extends TestCase { public function testCreateUrl() { diff --git a/tests/unit/framework/web/UrlRuleTest.php b/tests/unit/framework/web/UrlRuleTest.php index e0761ba..f697805 100644 --- a/tests/unit/framework/web/UrlRuleTest.php +++ b/tests/unit/framework/web/UrlRuleTest.php @@ -5,8 +5,9 @@ namespace yiiunit\framework\web; use yii\web\UrlManager; use yii\web\UrlRule; use yii\web\Request; +use yiiunit\TestCase; -class UrlRuleTest extends \yiiunit\TestCase +class UrlRuleTest extends TestCase { public function testCreateUrl() {