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 @@ + '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 @@ +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 @@ +registerAssetBundle('app'); +?> +beginPage(); ?> + + + + + <?php echo Html::encode($this->title); ?> + head(); ?> + + +
+ beginBody(); ?> +
+

My Company

+ + + +
+ + isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : array(), + )); ?> + + +
+ + + endBody(); ?> +
+ + + +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 @@ + '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 @@ +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 @@ + 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 @@ + '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 @@ + '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 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 @@ +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 @@ + 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 @@ +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 @@ + 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 @@ +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 +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 @@ + 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 @@ +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 @@ +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 +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 @@ + 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 @@ + '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 @@ + '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 @@ + 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 @@ + '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 @@ +title = 'Welcome'; +?> +
+

Welcome!

+ +

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.

+ Get started with Yii +
+ +
+ + +
+
+

Heading

+ +

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.

+ +

View details »

+
+
+

Heading

+ +

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.

+ +

View details »

+
+
+

Heading

+ +

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.

+ +

View details »

+
+
+ 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 @@ +title = 'Login'; +$this->params['breadcrumbs'][] = $this->title; +?> +

title); ?>

+ +

Please fill out the following fields to login:

+ + array('class' => 'form-horizontal'))); ?> + field($model, 'username')->textInput(); ?> + field($model, 'password')->passwordInput(); ?> + field($model, 'rememberMe')->checkbox(); ?> +
+ 'btn btn-primary')); ?> +
+ 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 + $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 +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 87% 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 '

Error

'; + echo '

The path to yii framework seems to be incorrect.

'; + echo '

You need to install Yii framework via composer or adjust the framework path in file ' . basename(__FILE__) .'.

'; + echo '

Please refer to the README on how to install Yii.

'; +} 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 72% 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 83% 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 @@ + 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 88% 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 @@ '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 92% 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 @@ '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 @@ + '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 @@ +Error'; + echo '

The path to yii framework seems to be incorrect.

'; + echo '

You need to install Yii framework via composer or adjust the framework path in file ' . basename(__FILE__) .'.

'; + echo '

Please refer to the README on how to install Yii.

'; +} + +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 DB-related classes', + ), + array( + 'name' => 'PDO SQLite extension', + 'mandatory' => false, + 'condition' => extension_loaded('pdo_sqlite'), + 'by' => 'All DB-related classes', + 'memo' => 'Required for SQLite database.', + ), + array( + 'name' => 'PDO MySQL extension', + 'mandatory' => false, + 'condition' => extension_loaded('pdo_mysql'), + 'by' => 'All DB-related classes', + 'memo' => 'Required for MySQL database.', + ), + // Cache : + array( + 'name' => 'Memcache extension', + 'mandatory' => false, + 'condition' => extension_loaded('memcache') || extension_loaded('memcached'), + 'by' => 'CMemCache', + 'memo' => extension_loaded('memcached') ? 'To use memcached set CMemCache::useMemcached to true.' : '' + ), + array( + 'name' => 'APC extension', + 'mandatory' => false, + 'condition' => extension_loaded('apc') || extension_loaded('apc'), + 'by' => 'CApcCache', + ), + // Additional PHP extensions : + array( + 'name' => 'Mcrypt extension', + 'mandatory' => false, + 'condition' => extension_loaded('mcrypt'), + 'by' => 'CSecurityManager', + '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 @@ +registerAssetBundle('app'); +?> +beginPage(); ?> + + + + + <?php echo Html::encode($this->title); ?> + head(); ?> + + +
+ beginBody(); ?> +
+

My Company

+ + + +
+ + isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : array(), + )); ?> + + +
+ + + endBody(); ?> +
+ + + +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 @@ +title = 'About'; +$this->params['breadcrumbs'][] = $this->title; +?> +

title); ?>

+ +

+ This is the About page. You may modify the following file to customize its content: +

+ + + 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 @@ +title = 'Contact'; +$this->params['breadcrumbs'][] = $this->title; +?> +

title); ?>

+ +session->hasFlash('contactFormSubmitted')): ?> +
+ Thank you for contacting us. We will respond to you as soon as possible. +
+ + +

+ If you have business inquiries or other questions, please fill out the following form to contact us. Thank you. +

+ + array('class' => 'form-horizontal'), + 'fieldConfig' => array('inputOptions' => array('class' => 'input-xlarge')), +)); ?> + field($model, 'name')->textInput(); ?> + field($model, 'email')->textInput(); ?> + field($model, 'subject')->textInput(); ?> + field($model, 'body')->textArea(array('rows' => 6)); ?> + field($model, 'verifyCode'); + echo $field->begin() + . $field->label() + . Captcha::widget() + . Html::activeTextInput($model, 'verifyCode', array('class' => 'input-medium')) + . $field->error() + . $field->end(); + ?> +
+ 'btn btn-primary')); ?> +
+ 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 @@ +title = 'Welcome'; +?> +
+

Welcome!

+ +

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.

+ Get started with Yii +
+ +
+ + +
+
+

Heading

+ +

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.

+ +

View details »

+
+
+

Heading

+ +

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.

+ +

View details »

+
+
+

Heading

+ +

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.

+ +

View details »

+
+
+ 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 @@ +title = 'Login'; +$this->params['breadcrumbs'][] = $this->title; +?> +

title); ?>

+ +

Please fill out the following fields to login:

+ + array('class' => 'form-horizontal'))); ?> + field($model, 'username')->textInput(); ?> + field($model, 'password')->passwordInput(); ?> + field($model, 'rememberMe')->checkbox(); ?> +
+ 'btn btn-primary')); ?> +
+ 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 +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 @@ + '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 @@ + * @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 95% 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 * @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 + * @author Timur Ruziev * @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 = 'Yii Framework/' . \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 .= '' . $this->htmlEncode($part) . '\\'; + } + $html = rtrim($html, '\\'); + } elseif (strpos($code, '()') !== false) { + // method/function call + $self = $this; + $html = preg_replace_callback('/^(.*)\(\)$/', function ($matches) use ($self) { + return '' . + $self->htmlEncode($matches[1]) . '()'; + }, $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 'HTTP ' . (int)$statusCode . ' – ' . $statusDescription . ''; + } - $output = ''; - for ($i = $beginLine; $i <= $endLine; ++$i) { - $isErrorLine = $i === $errorLine; - $code = sprintf("%0{$lineNumberWidth}d %s", $i + 1, $this->htmlEncode(str_replace("\t", ' ', $lines[$i]))); - if (!$isErrorLine) { - $output .= $code; - } else { - $output .= '' . $code . ''; - } + /** + * 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 '
' . $output . '
'; + $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 "\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 "\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 '
#$n"; - echo '
'; - if ($hasCode) { - echo '
+
-
'; - } - echo ' '; - if (isset($t['file'])) { - echo $this->htmlEncode($t['file']) . '(' . $t['line'] . '): '; - } - if (!empty($t['class'])) { - echo '' . $t['class'] . '' . $t['type']; - } - echo '' . $t['function'] . ''; - echo '(' . (empty($t['args']) ? '' : $this->htmlEncode($this->argumentsToString($t['args']))) . ')'; - echo '
'; - if ($hasCode) { - $this->renderSourceCode($t['file'], $t['line'], $this->maxTraceSourceLines); - } - echo "
'; + + $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 '' . $this->htmlEncode($_SERVER['SERVER_SOFTWARE']) . ''; + } + } + } } + 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 '' . $this->htmlEncode(Yii::getVersion()) . ''; } } 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' => '

Hello world

', - * '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 @@ - - * @author Alexander Makarov - * @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: - *
    - *
  • source: required, the full path of the file/directory to be copied from
  • - *
  • target: required, the full path of the file/directory to be copied to
  • - *
  • callback: optional, the callback to be invoked when copying a file. The callback function - * should be declared as follows: - *
    -	 *   function foo($source, $params)
    -	 *   
    - * where $source parameter is the source file path, and the content returned - * by the function will be saved into the target file.
  • - *
  • params: optional, the parameters to be passed to the callback
  • - *
- * @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 \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('', 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 \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('', 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 71% 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 * @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. *
 	 *                  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 53%
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 
  * @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 
  * @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 @@
+ '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 
+ * @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 @@
+ array(
+ *         'modal' => true,
+ *     ),
+ * ));
+ *
+ * echo 'Dialog contents here...';
+ *
+ * Dialog::end();
+ * ```
+ *
+ * @see http://api.jqueryui.com/dialog/
+ * @author Alexander Kochetov 
+ * @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 @@
+
+ * @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 @@
+ 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 '
Loading...
'; + * + * ProgressBar::end(); + * ~~~ + * @see http://api.jqueryui.com/progressbar/ + * @author Alexander Kochetov + * @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 @@ + 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 + * @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 @@ - - + + @@ -79,4 +79,4 @@ - \ No newline at end of file + 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 @@ -context; -$title = $context->htmlEncode($exception instanceof \yii\base\Exception ? $exception->getName() : get_class($exception)); -?> - - - - - <?php echo $title?> - - - - - -

-

htmlEncode($exception->getMessage()))?>

-

- The above error occurred while the Web server was processing your request. -

-

- Please contact us if you think this is a server error. Thank you. -

-
- - versionInfo : ''?> -
- - 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 @@ +context; +?> +
  • +
    +
    + . + htmlEncode($file); ?> + + + + addTypeLinks($class) . '→'; ?>addTypeLinks($method . '()'); ?> + + + + +
    +
    + +
    +
    +
    +
    + +
    htmlEncode($lines[$i]);
    +					}
    +				?>
    +
    +
    + +
  • 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 @@ +context; +?> + + + + + + + <?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)); + } + ?> + + + + + +
    + + Gears +

    + htmlEncode($exception->getName()); ?> + – addTypeLinks(get_class($exception)); ?> +

    + + Attention +

    ' . $context->createHttpStatusLink($exception->statusCode, $context->htmlEncode($exception->getName())) . ''; + echo ' – ' . $context->addTypeLinks(get_class($exception)); + } elseif ($exception instanceof \yii\base\Exception) { + echo '' . $context->htmlEncode($exception->getName()) . ''; + echo ' – ' . $context->addTypeLinks(get_class($exception)); + } else { + echo '' . $context->htmlEncode(get_class($exception)) . ''; + } + ?>

    + +

    htmlEncode($exception->getMessage()); ?>

    + renderPreviousExceptions($exception); ?> +
    + +
    +
      + renderCallStackItem($exception->getFile(), $exception->getLine(), null, null, 1); ?> + getTrace(), $length = count($trace); $i < $length; ++$i): ?> + renderCallStackItem(@$trace[$i]['file'] ?: null, @$trace[$i]['line'] ?: null, + @$trace[$i]['class'] ?: null, @$trace[$i]['function'] ?: null, $i + 1); ?> + +
    +
    + +
    +
    +
    htmlEncode($request); ?>
    +
    +
    + + + + + + + + + + + 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 @@ +context; +?> + 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 @@ -context; -$title = $context->htmlEncode($exception instanceof \yii\base\Exception ? $exception->getName().' ('.get_class($exception).')' : get_class($exception)); -?> - - - - - <?php echo $title?> - - - - -
    -

    - -

    - htmlEncode($exception->getMessage()))?> -

    - -
    -

    - htmlEncode($exception->getFile()) . '(' . $exception->getLine() . ')'?> -

    - renderSourceCode($exception->getFile(), $exception->getLine(), $context->maxSourceLines)?> -
    - - -
    -

    Stack Trace

    - renderTrace($exception->getTrace())?> -
    - - -
    - - getVersionInfo() : ''?> -
    -
    - - - - - 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 . '' ); } + + /** + * 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 @@ -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 @@ 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 @@ 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 @@ 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 @@