Merge remote-tracking branch 'upstream/master' into transalting

parents 97c5c219 8f7c4672
......@@ -100,7 +100,7 @@ TESTING
-------
Install additional composer packages:
* `php composer.phar require --dev "codeception/codeception: 1.8.*@dev" "codeception/specify: *" "codeception/verify: *" "yiisoft/yii2-faker: *"`
* `php composer.phar require --dev "codeception/codeception: 2.0.*" "codeception/specify: *" "codeception/verify: *" "yiisoft/yii2-faker: *"`
This application boilerplate use database in testing, so you should create three databases that are used in tests:
* `yii2_advanced_unit` - database for unit tests;
......
<?php
use yii\helpers\Html;
/* @var $this \yii\web\View */
/* @var $content string */
/* @var $this \yii\web\View view component instance */
/* @var $message \yii\mail\MessageInterface the message being composed */
/* @var $content string main view render result */
?>
<?php $this->beginPage() ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
......
<?php
namespace common\models;
use Yii;
use yii\base\NotSupportedException;
use yii\behaviors\TimestampBehavior;
use yii\db\ActiveRecord;
use Yii;
use yii\web\IdentityInterface;
/**
......@@ -25,7 +26,6 @@ class User extends ActiveRecord implements IdentityInterface
{
const STATUS_DELETED = 0;
const STATUS_ACTIVE = 10;
const ROLE_USER = 10;
/**
......@@ -42,13 +42,7 @@ class User extends ActiveRecord implements IdentityInterface
public function behaviors()
{
return [
'timestamp' => [
'class' => 'yii\behaviors\TimestampBehavior',
'attributes' => [
ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],
ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at'],
],
],
TimestampBehavior::className(),
];
}
......@@ -85,7 +79,7 @@ class User extends ActiveRecord implements IdentityInterface
/**
* Finds user by username
*
* @param string $username
* @param string $username
* @return static|null
*/
public static function findByUsername($username)
......@@ -96,12 +90,12 @@ class User extends ActiveRecord implements IdentityInterface
/**
* Finds user by password reset token
*
* @param string $token password reset token
* @param string $token password reset token
* @return static|null
*/
public static function findByPasswordResetToken($token)
{
$expire = \Yii::$app->params['user.passwordResetTokenExpire'];
$expire = Yii::$app->params['user.passwordResetTokenExpire'];
$parts = explode('_', $token);
$timestamp = (int) end($parts);
if ($timestamp + $expire < time()) {
......@@ -142,7 +136,7 @@ class User extends ActiveRecord implements IdentityInterface
/**
* Validates password
*
* @param string $password password to validate
* @param string $password password to validate
* @return boolean if password provided is valid for current user
*/
public function validatePassword($password)
......
......@@ -25,7 +25,7 @@
"yiisoft/yii2-gii": "*"
},
"suggest": {
"codeception/codeception": "Codeception, 1.8.*@dev is currently works well with Yii.",
"codeception/codeception": "Codeception, 2.0.* is currently works well with Yii.",
"codeception/specify": "BDD style code blocks for PHPUnit and Codeception",
"codeception/verify": "BDD Assertions for PHPUnit and Codeception",
"yiisoft/yii2-faker": "Fixtures generator for Yii2 based on Faker lib"
......
......@@ -14,7 +14,7 @@ return yii\helpers\ArrayHelper::merge(
'controllerMap' => [
'fixture' => [
'class' => 'yii\faker\FixtureController',
'fixtureDataPath' => '@console/tests/unit/fixtures/data',
'fixtureDataPath' => '@frontend/tests/unit/fixtures/data',
'templatePath' => '@common/tests/templates/fixtures'
],
],
......
......@@ -69,14 +69,6 @@ $requirements = array(
'condition' => extension_loaded('apc'),
'by' => '<a href="http://www.yiiframework.com/doc-2.0/yii-caching-apccache.html">ApcCache</a>',
),
// Additional PHP extensions :
array(
'name' => 'Mcrypt extension',
'mandatory' => false,
'condition' => extension_loaded('mcrypt'),
'by' => '<a href="http://www.yiiframework.com/doc-2.0/yii-helpers-security.html">Security Helper</a>',
'memo' => 'Required by encrypt and decrypt methods.'
),
// PHP ini :
'phpSafeMode' => array(
'name' => 'PHP safe mode',
......
......@@ -25,7 +25,7 @@
"yiisoft/yii2-gii": "*"
},
"suggest": {
"codeception/codeception": "Codeception, 1.8.*@dev is currently works well with Yii.",
"codeception/codeception": "Codeception, 2.0.* is currently works well with Yii.",
"codeception/specify": "BDD style code blocks for PHPUnit and Codeception",
"codeception/verify": "BDD Assertions for PHPUnit and Codeception"
},
......
<?php
use yii\helpers\Html;
/* @var $this \yii\web\View */
/* @var $content string */
/* @var $this \yii\web\View view component instance */
/* @var $message \yii\mail\MessageInterface the message being composed */
/* @var $content string main view render result */
?>
<?php $this->beginPage() ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
......
......@@ -69,14 +69,6 @@ $requirements = array(
'condition' => extension_loaded('apc'),
'by' => '<a href="http://www.yiiframework.com/doc-2.0/yii-caching-apccache.html">ApcCache</a>',
),
// Additional PHP extensions :
array(
'name' => 'Mcrypt extension',
'mandatory' => false,
'condition' => extension_loaded('mcrypt'),
'by' => '<a href="http://www.yiiframework.com/doc-2.0/yii-helpers-security.html">Security Helper</a>',
'memo' => 'Required by encrypt and decrypt methods.'
),
// PHP ini :
'phpSafeMode' => array(
'name' => 'PHP safe mode',
......
......@@ -6,7 +6,7 @@ After creating the basic application, follow these steps to prepare for the test
1. Install additional composer packages:
```
php composer.phar require --dev "codeception/codeception: 1.8.*@dev" "codeception/specify: *" "codeception/verify: *"
php composer.phar require --dev "codeception/codeception: 2.0.*" "codeception/specify: *" "codeception/verify: *"
```
2. In the file `_bootstrap.php`, modify the definition of the constant `TEST_ENTRY_URL` so
that it points to the correct entry script URL.
......
Componentes
===========
Los componentes son los principales bloques de construcción de las aplicaciones Yii. Los componentes son instancias de
[[yii\base\Component]] o de una clase extendida. Las tres características principales que los componentes proporcionan
a las otras clases son:
* [Propiedades](concept-properties.md)
* [Eventos](concept-events.md)
* [Comportamientos](concept-behaviors.md)
Por separado y combinadas, estas características hacen que las clases Yii sean mucho mas personalizables y sean mucho
más fáciles de usar. Por ejemplo, el incluido [[yii\jui\DatePicker|widget de selección de fecha]], un componente de la
interfaz de usuario, puede ser utilizado en una [vista](structure-view.md) para generar un selector de fechas interactivo:
```php
use yii\jui\DatePicker;
echo DatePicker::widget([
'language' => 'ru',
'name' => 'country',
'clientOptions' => [
'dateFormat' => 'yy-mm-dd',
],
]);
```
Las propiedades del widget son facilmente modificables porque la clase se extiende de [[yii\base\Component]].
Mientras que los componentes son muy potentes, son un poco más pesados que los objetos normales, debido al hecho de que
necesitan más memoria y tiempo de CPU para poder soportar [eventos](concept-events.md) y [comportamientos](concept-behaviors.md) en particular.
Si tus componentes no necesitan estas dos características, deberías considerar extender tu componente directamente de
[[yii\base\Object]] en vez de [[yii\base\Component]]. De esta manera harás que tus componentes sean mucho más eficientes que
que objetos PHP normales, pero con el añadido soporte para [propiedades](concept-properties.md).
Cuando extiendes tu clase de [[yii\base\Component]] o [[yii\base\Object]], se recomienda que sigas las siguientes
convenciones:
- Si sobrescribes el constructor, especifica un parámetro `$config` como el *último* parámetro del constructor, y después
pasa este parámetro al constructor de la clase "padre".
- Siempre llama al constructor del "padre" al *final* de su propio constructor.
- Si sobrescribes el método [[yii\base\Object::init()]], asegúrate de que llamas a la implementación de la clase "padre"
*al principio* de tu método `init`.
Por ejemplo:
```php
namespace yii\components\MyClass;
use yii\base\Object;
class MyClass extends Object
{
public $prop1;
public $prop2;
public function __construct($param1, $param2, $config = [])
{
// ... inicialización antes de la configuración está siendo aplicada
parent::__construct($config);
}
public function init()
{
parent::init();
// ... inicialización despues de la configuración esta siendo aplicada
}
}
```
Siguiendo esas directrices hará que tus componentes sean [configurables](concept-configurations.md) cuando son creados. Por ejemplo:
```php
$component = new MyClass(1, 2, ['prop1' => 3, 'prop2' => 4]);
// alternativamente
$component = \Yii::createObject([
'class' => MyClass::className(),
'prop1' => 3,
'prop2' => 4,
], [1, 2]);
```
> Información: Mientras que el enfoque de llamar [[Yii::createObject()]] parece mucho más complicado, es mucho más potente
debido al hecho de que se implementa en la parte superior de un [contenedor de inyección de dependencia](concept-di-container.md).
La clase [[yii\base\Object]] hace cumplir el siguiente ciclo de vida del objeto:
1. Pre-inicialización en el constructor. Puedes establecer los valores predeterminados de propiedades aquí.
2. Configuración del objeto a través de `$config`. La configuración puede sobrescribir los valores prdeterminados dentro
del constructor.
3. Post-inicialización dentro de [[yii\base\Object::init()|init()]]. Puedes sobrescribir este método para realizar
comprobaciones de validez y normalización de las propiedades.
4. LLamadas a métodos del objeto.
Los tres primeros pasos ocurren dentro del constructor del objeto. Esto significa que una vez obtengas la instancia de
un objeto, ésta ha sido inicializada para que puedas utilizarla adecuadamente.
Service Locator
===============
Service Locator является объектом, который знает, как обеспечить всевозможные службы (или компоненты), которые могут понадобиться в приложении.
В пределах Service Locator'а, каждый компонент имеет только один экземпляр, который уникально определяется с помощью идентификатора (ID).
Уникальный идентификатор (ID) может быть использован для извлечения компонента из Service Locator'а.
В Yii Service Locator является экземпляром класса [[yii\di\ServiceLocator]] или его дочернего класса.
Наиболее часто используемый Service Locator в Yii - это объект *приложения*, который можно получить через
`\Yii::$app`. Обеспечиваемые им службы называют *компонентами приложения*, такие, как компоненты `запрос`, `ответ`, `UrlManager`.
Вы легко можете настроить эти компоненты или даже заменить их собственными реализациями,
благодаря функциональным службам, предоставляемым Service Locator'ом.
Помимо объекта приложения, объект каждого модуля так же является Service Locator'ом.
Для использования Service Locator'а первым шагом является регистрация компонентов.
Компонент может быть зарегистрирован с помощью [[yii\di\ServiceLocator::set()]].
Следующий код демонстрирует различные способы регистрации компонентов:
```php
use yii\di\ServiceLocator;
use yii\caching\FileCache;
$locator = new ServiceLocator;
// Зарегистрирует "cache", используя имя класса, которое может быть использовано для создания компонента.
$locator->set('cache', 'yii\caching\ApcCache');
// Зарегистрирует "db", используя конфигурационный массив, который может быть использован для создания компонента.
$locator->set('db', [
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=localhost;dbname=demo',
'username' => 'root',
'password' => '',
]);
// Зарегистрирует "search", используя анонимную функцию, которая создаёт компонент
$locator->set('search', function () {
return new app\components\SolrService;
});
// Зарегистрирует "pageCache", используя компонент
$locator->set('pageCache', new FileCache);
```
После того, как компонент зарегистрирован, вы можете получить к нему доступ, используя уникальный идентификатор (ID),
одним из двух следующих способов:
```php
$cache = $locator->get('cache');
// или альтернативный
$cache = $locator->cache;
```
Как видно выше, [[yii\di\ServiceLocator]] позволяет обратиться к компоненту, как к свойству,
при помощи идентификатора (ID) компонента.
При обращении к компоненту впервые, [[yii\di\ServiceLocator]] будет использовать информацию о регистрации компонента,
что бы создать новый экземпляр компонента и вернёт его.
В дальнейшем при обращении к компоненту снова, Service Locator вернёт тот же экземпляр.
Что бы проверить, был ли идентификатор (ID) компонента уже зарегистрирован, можно использовать [[yii\di\ServiceLocator::has()]].
Если вы вызовете [[yii\di\ServiceLocator::get()]] с недопустимым идентификатором (ID), тогда будет выброшено исключение.
Поскольку Service Locator`ы зачастую создаются с [конфигурациями](concept-configurations.md),
записываемое свойство с именем [[yii\di\ServiceLocator::setComponents()|components]] предоставляется так,
что Вы можете его настроить и зарегистрировать несколько компонентов одновременно.
Следующий код демонстрирует конфигурационный массив,
который может использоваться для настройки приложения и регистрации компонентов "db", "cache" и "search" :
```php
return [
// ...
'components' => [
'db' => [
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=localhost;dbname=demo',
'username' => 'root',
'password' => '',
],
'cache' => 'yii\caching\ApcCache',
'search' => function () {
return new app\components\SolrService;
},
],
];
```
Що таке Yii?
============
Yii – це високопродуктивний компонентний PHP фреймворк, призначений для швидкої розробки сучасних веб додатків.
Слово Yii (вимовляється як `Йі` `[ji:]`) в китайській мові означає "простий та еволюційний".
Також Yii може розшифровуватись акронім **Yes It Is**!
Для яких завдань найбільше підходить Yii?
-----------------------------------------
Yii – це універсальний фреймворк і може бути задіяний у всіх типах веб додатків. Завдяки його компонентній структурі
і відмінній підтримці кешування, фреймворк особливо підходить для розробки таких великих проектів як портали,
форуми, CMS, магазини або RESTful-додатки.
Порівняння Yii з іншими фреймворками
------------------------------------
- Як і багато інших PHP фреймворків, для організації коду Yii використовує модель MVC (Model-View-Controller).
- Yii дотримується філософії простого й елегантного коду, не намагаючись ускладнювати дизайн тільки заради слідування
будь-яким шаблонами проектування.
- Yii є full-stack фреймворком і включає в себе перевірені можливості, які добре себе зарекомендували,
такі як ActiveRecord для реляційних та NoSQL баз даних, підтримку REST API, багаторівневе кешування та інші.
- Yii відмінно розширюваний. Ви можете налаштувати або замінити практично будь-яку частину основного коду.
Використовуючи архітектуру розширень - легко ділитися кодом або використовувати код спільноти.
- Висока продуктивність завжди є головною ціллю Yii.
Yii — не проект однієї людини. Він підтримується і розвивається [сильною командою][] і великою спільнотою розробників,
які їй допомагають. Розробники фреймворка стежать за тенденціями веб розробки і розвитком інших проектів.
Найбільш значимі можливості і кращі практики регулярно впроваджуються у фреймворк у вигляді простих і елегантних інтерфейсів.
[сильна команда розробників]: http://www.yiiframework.com/about/
Версії Yii
----------
На даний момент існує дві основні гілки Yii: 1.1 та 2.0. Гілка 1.1 є попереднім поколінням і знаходиться у стані підтримки.
Версія 2.0 - це повністю переписаний Yii, що використовує останні технології і протоколи, такі як Composer, PSR, простори імен,
типажі (traits) і багато іншого. 2.0 - останнє покоління фреймворка. На цій версії будуть зосереджені основні зусилля
кілька наступних років. Даний посібник призначений в основному версії 2.0.
Вимоги до ПЗ і знань
--------------------
Yii 2.0 потребує PHP 5.4.0 та вище. Щоб дізнатися вимоги для окремих можливостей ви можете запустити скрипт перевірки вимог,
який поставляється із кожним релізом фреймворка.
Для розробки на Yii буде потрібне загальне розуміння ООП, так як фреймворк повністю слідує цій парадигмі.
Також слід вивчити такі сучасні можливості PHP як [простори імен](http://www.php.net/manual/en/language.namespaces.php)
і [типажі](http://www.php.net/manual/en/language.oop5.traits.php).
......@@ -109,7 +109,7 @@ The following is the list of the predefined aliases:
- `@yii`: the directory where the `BaseYii.php` file is located (also called the framework directory).
- `@app`: the [[yii\base\Application::basePath|base path]] of the currently running application.
- `@runtime`: the [[yii\base\Application::runtimePath|runtime path]] of the currently running application.
- `@vendor`: the [[yii\base\Application::vendorPath|Composer vendor directory].
- `@vendor`: the [[yii\base\Application::vendorPath|Composer vendor directory]].
- `@webroot`: the Web root directory of the currently running Web application.
- `@web`: the base URL of the currently running Web application.
......
......@@ -3,7 +3,7 @@ Extending Yii
> Note: This section is under development.
The Yii framework was designed to be easily extendable. Additional features can be added to your project and then reused, either by yourself on other projects or by sharing your work as a formal Yii extension.
The Yii framework was designed to be easily extensible. Additional features can be added to your project and then reused, either by yourself on other projects or by sharing your work as a formal Yii extension.
Code style
----------
......
Helper Classes
==============
> Note: This section is under development.
Yii provides many classes that help simplify common coding tasks, such as string or array manipulations,
HTML code generation, and so forth. These helper classes are organized under the `yii\helpers` namespace and
are all static classes (meaning they contain only static properties and methods and should not be instantiated).
You use a helper class by directly calling one of its static methods:
```
use yii\helpers\ArrayHelper;
$c = ArrayHelper::merge($a, $b);
```
Extending Helper Classes
------------------------
To make helper classes easier to extend, Yii breaks each helper class into two classes: a base class (e.g. `BaseArrayHelper`)
and a concrete class (e.g. `ArrayHelper`). When you use a helper, you should only use the concrete version, never use the base class.
If you want to customize a helper, perform the following steps (using `ArrayHelper` as an example):
1. Name your class the same as the concrete class provided by Yii, including the namespace: `yii\helpers\ArrayHelper`
2. Extend your class from the base class: `class ArrayHelper extends \yii\helpers\BaseArrayHelper`.
3. In your class, override any method or property as needed, or add new methods or properties.
4. Tell your application to use your version of the helper class by including the following line of code in the bootstrap script:
```php
Yii::$classMap['yii\helpers\ArrayHelper'] = 'path/to/ArrayHelper.php';
```
Step 4 above will instruct the Yii class autoloader to load your version of the helper class instead of the one included in the Yii distribution.
> Tip: You can use `Yii::$classMap` to replace ANY core Yii class with your own customized version, not just helper classes.
......@@ -22,6 +22,20 @@ curl -s http://getcomposer.org/installer | php
We strongly recommend a global composer installation.
Installing Composer Class Autoloader
------------------------------------
Make sure the [entry script](concept-entry-scripts.md) of your application contains the following lines of code:
```php
// install Composer's class autoloader
require(__DIR__ . '/../vendor/autoload.php');
// include Yii class file
require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');
```
Working with composer
---------------------
......@@ -52,7 +66,7 @@ composer.phar update
As an example, packages on `dev-master` will constantly get new updates when you run `update`, while running `install` won't, unless you've pulled an update of the `composer.lock` file.
There are several paramaters available to the above commands. Very commonly used ones are `--no-dev`, which would skip packages in the `require-dev` section and `--prefer-dist`, which downloads archives if available, instead of checking out repositories to your `vendor` folder.
There are several parameters available to the above commands. Very commonly used ones are `--no-dev`, which would skip packages in the `require-dev` section and `--prefer-dist`, which downloads archives if available, instead of checking out repositories to your `vendor` folder.
> Composer commands must be executed within your Yii project's directory, where the `composer.json` file can be found.
Depending upon your operating system and setup, you may need to provide paths to the PHP executable and
......@@ -100,7 +114,7 @@ afterwards.
> Depending on the package additional configuration may be required (eg. you have to register a module in the config), but autoloading of the classes should be handled by composer.
Using a specifc version of a package
Using a specific version of a package
------------------------------------
Yii always comes with the latest version of a required library that it is compatible with, but allows you to use an older version if you need to.
......
......@@ -3,14 +3,26 @@ Helpers
> Note: This section is under development.
Helper classes typically contain static methods only and are used as follows:
Yii provides many classes that help simplify common coding tasks, such as string or array manipulations,
HTML code generation, and so on. These helper classes are organized under the `yii\helpers` namespace and
are all static classes (meaning they contain only static properties and methods and should not be instantiated).
You use a helper class by directly calling one of its static methods, like the following:
```php
use \yii\helpers\Html;
use yii\helpers\Html;
echo Html::encode('Test > test');
```
There are several classes provided by framework:
> Note: To support [extending helper classes](#extending-helper-classes), Yii breaks each core helper class
into two classes: a base class (e.g. `BaseArrayHelper`) and a concrete class (e.g. `ArrayHelper`).
When you use a helper, you should only use the concrete version and never use the base class.
## Core Helper Classes
The following core helper classes are provided in the Yii releases:
- ArrayHelper
- Console
......@@ -25,3 +37,39 @@ There are several classes provided by framework:
- StringHelper
- Url
- VarDumper
## Extending Helper Classes
To custom a core helper class (e.g. `yii\helpers\ArrayHelper`), you should extend from its corresponding base class
(e.g. `yii\helpers\BaseArrayHelper`) and name your class the same as the corresponding concrete class
(e.g. `yii\helpers\ArrayHelper`), including its namespace.
The following example shows how to customize the [[yii\helpers\ArrayHelper::merge()|merge()]] method of the
[[yii\helpers\ArrayHelper]] class:
```php
namespace yii\helpers;
use yii\helpers\BaseArrayHelper;
class ArrayHelper extends BaseArrayHelper
{
public static function merge($a, $b)
{
// your custom implementation
}
}
```
Save your class in a file named `ArrayHelper.php`. The file can be in any directory, such as `@app/components`.
Next, in your application's [entry script](structure-entry-scripts.md), add the following line of code
after including the `yii.php` file:
```php
Yii::$classMap['yii\helpers\ArrayHelper'] = 'path/to/ArrayHelper.php';
```
The above line instructs the [Yii class autoloader](concept-autoloading.md) to load your version of the helper
class, instead of the one included in the Yii releases.
......@@ -15,7 +15,7 @@ with response formatting:
## Content Negotiation <a name="content-negotiation"></a>
Yii supports content negotiation via the [[yii\filters\ContentNegotiator]] filter. The the RESTful API base
Yii supports content negotiation via the [[yii\filters\ContentNegotiator]] filter. The RESTful API base
controller class [[yii\rest\Controller]] is equipped with this filter under the name of `contentNegotiator`.
The filer provides response format negotiation as well as language negotiation. For example, if a RESTful
API request contains the following header,
......
......@@ -141,6 +141,7 @@ Layout can be used to setup mail CSS styles or other shared content:
use yii\helpers\Html;
/* @var $this \yii\web\View view component instance */
/* @var $message \yii\mail\MessageInterface the message being composed */
/* @var $content string main view render result */
?>
<?php $this->beginPage() ?>
......@@ -227,4 +228,4 @@ another one for the 'Message'.
You can use `yii\mail\BaseMailer` and `yii\mail\BaseMessage` as a base classes for your solution. These classes
already contains basic logic, which is described in this guide. However, their usage is not mandatory, it is enough
to implement `yii\mail\MailerInterface` and `yii\mail\MessageInterface` interfaces.
Then you need to implement all abstract methods to build you solution.
\ No newline at end of file
Then you need to implement all abstract methods to build you solution.
......@@ -63,7 +63,7 @@ class SiteController extends Controller
'class' => 'yii\authclient\AuthAction',
'successCallback' => [$this, 'successCallback'],
],
]
];
}
public function successCallback($client)
......
......@@ -9,6 +9,7 @@ Yii Framework 2 bootstrap extension Change Log
- Bug #3749: Fixed invalid plugin registration and ensure clickable links in dropdown (kartik-v)
- Enh #4024: Added ability to `yii\bootstrap\Tabs` to encode each `Tabs::items['label']` separately (creocoder, umneeq)
- Chg #3036: Upgraded Twitter Bootstrap to 3.1.x (qiangxue)
- Enh #4120: Added ability for each item to choose it's encoding option in `Dropdown` and `Nav` (Alex-Code)
2.0.0-beta April 13, 2014
-------------------------
......
......@@ -80,7 +80,8 @@ class Dropdown extends Widget
if (!isset($item['label'])) {
throw new InvalidConfigException("The 'label' option is required.");
}
$label = $this->encodeLabels ? Html::encode($item['label']) : $item['label'];
$encodeLabel = isset($item['encode']) ? $item['encode'] : $this->encodeLabels;
$label = $encodeLabel ? Html::encode($item['label']) : $item['label'];
$options = ArrayHelper::getValue($item, 'options', []);
$linkOptions = ArrayHelper::getValue($item, 'linkOptions', []);
$linkOptions['tabindex'] = '-1';
......
......@@ -148,7 +148,8 @@ class Nav extends Widget
if (!isset($item['label'])) {
throw new InvalidConfigException("The 'label' option is required.");
}
$label = $this->encodeLabels ? Html::encode($item['label']) : $item['label'];
$encodeLabel = isset($item['encode']) ? $item['encode'] : $this->encodeLabels;
$label = $encodeLabel ? Html::encode($item['label']) : $item['label'];
$options = ArrayHelper::getValue($item, 'options', []);
$items = ArrayHelper::getValue($item, 'items');
$url = ArrayHelper::getValue($item, 'url', '#');
......
......@@ -416,8 +416,9 @@ class ActiveRecord extends BaseActiveRecord
$this->_version = $response['_version'];
$this->_score = null;
$changedAttributes = array_fill_keys(array_keys($values), null);
$this->setOldAttributes($values);
$this->afterSave(true, $values);
$this->afterSave(true, $changedAttributes);
return true;
}
......
......@@ -7,6 +7,7 @@ Yii Framework 2 elasticsearch extension Change Log
- Bug #3587: Fixed an issue with storing empty records (cebe)
- Enh #3520: Added `unlinkAll()`-method to active record to remove all records of a model relation (NmDimas, samdark, cebe)
- Enh #3527: Added `highlight` property to Query and ActiveRecord. (Borales)
- Enh #4086: changedAttributes of afterSave Event now contain old values (dizews)
- Enh: Make error messages more readable in HTML output (cebe)
- Chg: asArray in ActiveQuery is now equal to using the normal Query. This means, that the output structure has changed and `with` is supported anymore. (cebe)
- Chg: Deletion of a record is now also considered successful if the record did not exist. (cebe)
......
......@@ -433,23 +433,23 @@ class Connection extends Component
}
}
/**
* Try to decode error information if it is valid json, return it if not.
* @param $body
* @return mixed
*/
protected function decodeErrorBody($body)
{
try {
$decoded = Json::decode($body);
if (isset($decoded['error'])) {
$decoded['error'] = preg_replace('/\b\w+?Exception\[/', "<span style=\"color: red;\">\\0</span>\n ", $decoded['error']);
}
return $decoded;
} catch(InvalidParamException $e) {
return $body;
}
}
/**
* Try to decode error information if it is valid json, return it if not.
* @param $body
* @return mixed
*/
protected function decodeErrorBody($body)
{
try {
$decoded = Json::decode($body);
if (isset($decoded['error'])) {
$decoded['error'] = preg_replace('/\b\w+?Exception\[/', "<span style=\"color: red;\">\\0</span>\n ", $decoded['error']);
}
return $decoded;
} catch(InvalidParamException $e) {
return $body;
}
}
public function getNodeInfo()
{
......
......@@ -224,8 +224,9 @@ abstract class ActiveRecord extends BaseActiveRecord
$this->setAttribute('_id', $newId);
$values['_id'] = $newId;
$changedAttributes = array_fill_keys(array_keys($values), null);
$this->setOldAttributes($values);
$this->afterSave(true, $values);
$this->afterSave(true, $changedAttributes);
return true;
}
......@@ -260,10 +261,12 @@ abstract class ActiveRecord extends BaseActiveRecord
throw new StaleObjectException('The object being updated is outdated.');
}
$changedAttributes = [];
foreach ($values as $name => $value) {
$this->setOldAttribute($name, $this->getAttribute($name));
$changedAttributes[$name] = $this->getOldAttribute($name);
$this->setOldAttribute($name, $value);
}
$this->afterSave(false, $values);
$this->afterSave(false, $changedAttributes);
return $rows;
}
......
......@@ -8,6 +8,7 @@ Yii Framework 2 mongodb extension Change Log
- Enh #3520: Added `unlinkAll()`-method to active record to remove all records of a model relation (NmDimas, samdark, cebe)
- Enh #3778: Gii generator for Active Record model added (klimov-paul)
- Enh #3947: Migration support added (klimov-paul)
- Enh #4086: changedAttributes of afterSave Event now contain old values (dizews)
2.0.0-beta April 13, 2014
......
......@@ -127,8 +127,9 @@ abstract class ActiveRecord extends \yii\mongodb\ActiveRecord
$this->setAttribute('_id', $newId);
$values['_id'] = $newId;
$changedAttributes = array_fill_keys(array_keys($values), null);
$this->setOldAttributes($values);
$this->afterSave(true, $values);
$this->afterSave(true, $changedAttributes);
return true;
}
......@@ -196,10 +197,12 @@ abstract class ActiveRecord extends \yii\mongodb\ActiveRecord
}
}
$changedAttributes = [];
foreach ($values as $name => $value) {
$this->setOldAttribute($name, $this->getAttribute($name));
$changedAttributes[$name] = $this->getOldAttribute($name);
$this->setOldAttribute($name, $value);
}
$this->afterSave(false, $values);
$this->afterSave(false, $changedAttributes);
return $rows;
}
......
......@@ -124,8 +124,9 @@ class ActiveRecord extends BaseActiveRecord
}
$db->executeCommand('HMSET', $args);
$changedAttributes = array_fill_keys(array_keys($values), null);
$this->setOldAttributes($values);
$this->afterSave(true, $values);
$this->afterSave(true, $changedAttributes);
return true;
}
......
......@@ -5,6 +5,7 @@ Yii Framework 2 redis extension Change Log
--------------------------
- Enh #3520: Added `unlinkAll()`-method to active record to remove all records of a model relation (NmDimas, samdark, cebe)
- Enh #4086: changedAttributes of afterSave Event now contain old values (dizews)
2.0.0-beta April 13, 2014
......
......@@ -20,7 +20,7 @@ use yii\helpers\Inflector;
* When the server needs authentication, you can set the [[password]] property to
* authenticate with the server after connect.
*
* The ecexution of [redis commands](http://redis.io/commands) is possible with via [[executeCommand()]].
* The execution of [redis commands](http://redis.io/commands) is possible with via [[executeCommand()]].
*
* @method mixed set($key, $value) Set the string value of a key
* @method mixed get($key) Set the string value of a key
......
......@@ -395,8 +395,9 @@ abstract class ActiveRecord extends BaseActiveRecord
return false;
}
$changedAttributes = array_fill_keys(array_keys($values), null);
$this->setOldAttributes($values);
$this->afterSave(true, $values);
$this->afterSave(true, $changedAttributes);
return true;
}
......@@ -530,10 +531,12 @@ abstract class ActiveRecord extends BaseActiveRecord
}
}
$changedAttributes = [];
foreach ($values as $name => $value) {
$this->setOldAttribute($name, $this->getAttribute($name));
$changedAttributes[$name] = $this->getOldAttribute($name);
$this->setOldAttribute($name, $value);
}
$this->afterSave(false, $values);
$this->afterSave(false, $changedAttributes);
return $rows;
}
......
......@@ -6,8 +6,9 @@ Yii Framework 2 sphinx extension Change Log
- Bug #3668: Escaping of the special characters at 'MATCH' statement added (klimov-paul)
- Bug #4018: AR relation eager loading does not work with db models (klimov-paul)
- Enh: Added support for using sub-queries when building a DB query with `IN` condition (qiangxue)
- Enh #3520: Added `unlinkAll()`-method to active record to remove all records of a model relation (NmDimas, samdark, cebe)
- Enh #4086: changedAttributes of afterSave Event now contain old values (dizews)
- Enh: Added support for using sub-queries when building a DB query with `IN` condition (qiangxue)
- Chg #2287: Split `yii\sphinx\ColumnSchema::typecast()` into two methods `phpTypecast()` and `dbTypecast()` to allow specifying PDO type explicitly (cebe)
......
......@@ -62,6 +62,9 @@ Yii Framework 2 Change Log
- Bug #3989: Fixed yii\log\FileTarget::$rotateByCopy to avoid any rename (cebe)
- Bug #3996: Traversing `Yii::$app->session` may cause a PHP error (qiangxue)
- Bug #4020: OCI column detection did not work so gii and other things failed (Sanya1991)
- Bug #4123: Trace level in logger had no effect in Targets, traces where not logged (cebe)
- Bug #4127: `CaptchaValidator` clientside error message wasn't formed properly (samdark)
- Bug #4162: Fixed bug where schema name was not used in ’SHOW CREATE TABLE’ query in `yii\db\mysql\Schema` (stevekr)
- Bug: Fixed inconsistent return of `\yii\console\Application::runAction()` (samdark)
- Bug: URL encoding for the route parameter added to `\yii\web\UrlManager` (klimov-paul)
- Bug: Fixed the bug that requesting protected or private action methods would cause 500 error instead of 404 (qiangxue)
......@@ -120,8 +123,17 @@ Yii Framework 2 Change Log
- Removed character maps for non-latin languages.
- Improved overall slug results.
- Added note about the fact that intl is required for non-latin languages to requirements checker.
- Enh #3992: In mail layouts you can now access the message object via `$message` variable (qiangxue)
- Enh #4028: Added ability to `yii\widgets\Menu` to encode each item's label separately (creocoder, umneeq)
- Enh #4072: `\yii\rbac\PhpManager` adjustments (samdark)
- Data is now stored in three separate files for items, assignments and rules. File format is simpler.
- Removed `authFile`. Added `itemFile`, `assignmentFile` and `ruleFile`.
- `createdAt` and `updatedAt` are now properly filled with corresponding file modification time.
- `save()` and `load()` are now protected instead of public.
- Added unit test for saving and loading data.
- Enh #4080: Added proper handling and support of the symlinked directories in `FileHelper`, added $options parameter in `FileHelper::removeDirectory()` (resurtm)
- Enh #4086: changedAttributes of afterSave Event now contain old values (dizews)
- Enh #4114: Added `Security::generateRandomBytes()`, improved tests (samdark)
- Enh: Added support for using sub-queries when building a DB query with `IN` condition (qiangxue)
- Enh: Supported adding a new response formatter without the need to reconfigure existing formatters (qiangxue)
- Enh: Added `yii\web\UrlManager::addRules()` to simplify adding new URL rules (qiangxue)
......@@ -154,6 +166,7 @@ Yii Framework 2 Change Log
- Chg #3956: Flash messages set via `Yii::$app->session->setFlash()` will be removed only if they are accessed (qiangxue)
- Chg #3989: The default value for `yii\log\FileTarget::$rotateByCopy` now defaults to true to work on windows by default (cebe)
- Chg #4071: `mail` component renamed to `mailer`, `yii\log\EmailTarget::$mail` renamed to `yii\log\EmailTarget::$mailer` (samdark)
- Chg #4147: `BaseMailer::compose()` will not overwrite the `message` parameter if it is explicitly provided (qiangxue)
- Chg: Replaced `clearAll()` and `clearAllAssignments()` in `yii\rbac\ManagerInterface` with `removeAll()`, `removeAllRoles()`, `removeAllPermissions()`, `removeAllRules()` and `removeAllAssignments()` (qiangxue)
- Chg: Added `$user` as the first parameter of `yii\rbac\Rule::execute()` (qiangxue)
- Chg: `yii\grid\DataColumn::getDataCellValue()` visibility is now `public` to allow accessing the value from a GridView directly (cebe)
......@@ -161,6 +174,7 @@ Yii Framework 2 Change Log
- Chg: Removed `yii\rest\ActiveController::$transactional` property and connected functionality (samdark)
- Chg: Changed the default value of the `keyPrefix` property of cache components to be null (qiangxue)
- Chg: Added `prefix` column to `yii\log\DbTarget` to have the same amount of information logged as in files and emails (cebe)
- Chg: Use `limit(null)` instead of `limit(-1)` in migration controller to be compatible to more backends (cebe)
- New #3911: Added `yii\behaviors\SluggableBehavior` that fills the specified model attribute with the transliterated and adjusted version to use in URLs (creocoder)
......
......@@ -61,6 +61,7 @@ Upgrade from Yii 2.0 Beta
* The behavior and signature of `ActiveRecord::afterSave()` has changed. `ActiveRecord::$isNewRecord` will now always be
false in afterSave and also dirty attributes are not available. This change has been made to have a more consistent and
expected behavior. The changed attributes are now available in the new parameter of afterSave() `$changedAttributes`.
`$changedAttributes` contains the old values of attributes that had changed and were saved.
* `ActiveRecord::updateAttributes()` has been changed to not trigger events and not respect optimistic locking anymore to
differentiate it more from calling `update(false)` and to ensure it can be used in `afterSave()` without triggering infinite
......@@ -73,6 +74,54 @@ Upgrade from Yii 2.0 Beta
* `mail` component was renamed to `mailer`, `yii\log\EmailTarget::$mail` was renamed to `yii\log\EmailTarget::$mailer`.
Please update all references in the code and config files.
* `\yii\rbac\PhpManager` now stores data in three separate files instead of one. In order to convert old file to
new ones save the following code as `convert.php` that should be placed in the same directory your `rbac.php` is in:
```php
<?php
$oldFile = 'rbac.php';
$itemsFile = 'items.php';
$assignmentsFile = 'assignments.php';
$rulesFile = 'rules.php';
$oldData = include $oldFile;
function saveToFile($data, $fileName) {
$out = var_export($data, true);
$out = "<?php\nreturn " . $out . ";";
$out = str_replace(['array (', ')'], ['[', ']'], $out);
file_put_contents($fileName, $out);
}
$items = [];
$assignments = [];
if (isset($oldData['items'])) {
foreach ($oldData['items'] as $name => $data) {
if (isset($data['assignments'])) {
foreach ($data['assignments'] as $userId => $assignmentData) {
$assignments[$userId] = $assignmentData['roleName'];
}
unset($data['assignments']);
}
$items[$name] = $data;
}
}
$rules = [];
if (isset($oldData['rules'])) {
$rules = $oldData['rules'];
}
saveToFile($items, $itemsFile);
saveToFile($assignments, $assignmentsFile);
saveToFile($rules, $rulesFile);
echo "Done!\n";
```
Run it once, delete `rbac.php`. If you've configured `authFile` property, remove the line from config and instead
configure `itemFile`, `assignmentFile` and `ruleFile`.
* Static helper `yii\helpers\Security` has been converted into an application component. You should change all usage of
its methods to a new syntax, for example: instead of `yii\helpers\Security::hashData()` use `Yii::$app->getSecurity()->hashData()`.
Default encryption and hash parameters has been upgraded. If you need to decrypt/validate data that was encrypted/hashed
......@@ -87,10 +136,9 @@ Upgrade from Yii 2.0 Beta
'deriveKeyStrategy' => 'hmac', // for PHP version < 5.5.0
//'deriveKeyStrategy' => 'pbkdf2', // for PHP version >= 5.5.0
'useDeriveKeyUniqueSalt' => false,
'autoGenerateSecretKey' => true,
],
// ...
],
// ...
];
```
\ No newline at end of file
```
......@@ -212,7 +212,7 @@ class Security extends Component
if (function_exists('hash_pbkdf2')) {
return hash_pbkdf2($this->derivationHash, $password, $salt, $this->derivationIterations, $this->cryptKeySize, true);
} else {
throw new InvalidConfigException('Derive key strategy "pbkdf2" requires PHP >= 5.5.0, either upgrade your environment or use another strategy.');
throw new InvalidConfigException('Security::$deriveKeyStrategy is set to "pbkdf2", which requires PHP >= 5.5.0. Either upgrade your run-time environment or use another strategy.');
}
}
......@@ -298,22 +298,46 @@ class Security extends Component
}
if (!isset($this->_keys[$name]) || $regenerate) {
$this->_keys[$name] = utf8_encode(static::generateRandomKey($length));
$this->_keys[$name] = $this->generateRandomKey($length);
file_put_contents($keyFile, json_encode($this->_keys));
}
return utf8_decode($this->_keys[$name]);
return $this->_keys[$name];
}
/**
* Generates a random key.
* Note the generated key is a binary string with the specified number of bytes in it.
* @param integer $length the length of the key that should be generated
* Generates specified number of random bytes.
* Note that output may not be ASCII.
* @see generateRandomKey() if you need a string.
*
* @param integer $length the number of bytes to generate
* @throws Exception on failure.
* @return string the generated random bytes
*/
public function generateRandomBytes($length = 32)
{
if (!extension_loaded('mcrypt')) {
throw new InvalidConfigException('The mcrypt PHP extension is not installed.');
}
$bytes = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
if ($bytes === false) {
throw new Exception('Unable to generate random bytes.');
}
return $bytes;
}
/**
* Generates a random string of specified length.
* The string generated matches [A-Za-z0-9_.-]+
*
* @param integer $length the length of the key in characters
* @throws Exception Exception on failure.
* @return string the generated random key
*/
public function generateRandomKey($length = 32)
{
return mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
$bytes = $this->generateRandomBytes($length);
return strtr(StringHelper::byteSubstr(base64_encode($bytes), 0, $length), '+/=', '_-.');
}
/**
......@@ -448,7 +472,7 @@ class Security extends Component
}
// Get 20 * 8bits of random entropy
$rand = $this->generateRandomKey(20);
$rand = $this->generateRandomBytes(20);
// Add the microtime for a little more entropy.
$rand .= microtime(true);
......
......@@ -92,9 +92,9 @@ class CaptchaValidator extends Validator
'hash' => $hash,
'hashKey' => 'yiiCaptcha/' . $this->captchaAction,
'caseSensitive' => $this->caseSensitive,
'message' => strtr($this->message, [
'message' => Yii::$app->getI18n()->format($this->message, [
'attribute' => $object->getAttributeLabel($attribute),
]),
], Yii::$app->language),
];
if ($this->skipOnEmpty) {
$options['skipOnEmpty'] = 1;
......
......@@ -328,7 +328,7 @@ abstract class BaseMigrateController extends Controller
}
// try mark down
$migrations = array_keys($this->getMigrationHistory(-1));
$migrations = array_keys($this->getMigrationHistory(null));
foreach ($migrations as $i => $migration) {
if (strpos($migration, $version . '_') === 0) {
if ($i === 0) {
......@@ -544,7 +544,7 @@ abstract class BaseMigrateController extends Controller
protected function migrateToTime($time)
{
$count = 0;
$migrations = array_values($this->getMigrationHistory(-1));
$migrations = array_values($this->getMigrationHistory(null));
while ($count < count($migrations) && $migrations[$count] > $time) {
++$count;
}
......@@ -575,7 +575,7 @@ abstract class BaseMigrateController extends Controller
}
// try migrate down
$migrations = array_keys($this->getMigrationHistory(-1));
$migrations = array_keys($this->getMigrationHistory(null));
foreach ($migrations as $i => $migration) {
if (strpos($migration, $version . '_') === 0) {
if ($i === 0) {
......@@ -598,7 +598,7 @@ abstract class BaseMigrateController extends Controller
protected function getNewMigrations()
{
$applied = [];
foreach ($this->getMigrationHistory(-1) as $version => $time) {
foreach ($this->getMigrationHistory(null) as $version => $time) {
$applied[substr($version, 1, 13)] = true;
}
......@@ -621,7 +621,7 @@ abstract class BaseMigrateController extends Controller
/**
* Returns the migration history.
* @param integer $limit the maximum number of records in the history to be returned
* @param integer $limit the maximum number of records in the history to be returned. `null` for "no limit".
* @return array the migration history
*/
abstract protected function getMigrationHistory($limit);
......
......@@ -462,8 +462,9 @@ class ActiveRecord extends BaseActiveRecord
}
}
$changedAttributes = array_fill_keys(array_keys($values), null);
$this->setOldAttributes($values);
$this->afterSave(true, $values);
$this->afterSave(true, $changedAttributes);
return true;
}
......
......@@ -714,10 +714,12 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
throw new StaleObjectException('The object being updated is outdated.');
}
$changedAttributes = [];
foreach ($values as $name => $value) {
$this->_oldAttributes[$name] = $this->_attributes[$name];
$changedAttributes[$name] = isset($this->_oldAttributes[$name]) ? $this->_oldAttributes[$name] : null;
$this->_oldAttributes[$name] = $value;
}
$this->afterSave(false, $values);
$this->afterSave(false, $changedAttributes);
return $rows;
}
......@@ -875,7 +877,11 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
* the event is triggered.
* @param boolean $insert whether this method called while inserting a record.
* If false, it means the method is called while updating a record.
* @param array $changedAttributes The attribute values that had changed and were saved.
* @param array $changedAttributes The old values of attributes that had changed and were saved.
* You can use this parameter to take action based on the changes made for example send an email
* when the password had changed or implement audit trail that tracks all the changes.
* `$changedAttributes` gives you the old attribute values while the active record (`$this`) has
* already the new, updated values.
*/
public function afterSave($insert, $changedAttributes)
{
......
......@@ -298,27 +298,27 @@ class Schema extends \yii\db\Schema
return isset($typeMap[$type]) ? $typeMap[$type] : \PDO::PARAM_STR;
}
/**
* @inheritdoc
* @see http://www.cubrid.org/manual/91/en/sql/transaction.html#database-concurrency
*/
public function setTransactionIsolationLevel($level)
{
// translate SQL92 levels to CUBRID levels:
switch ($level) {
case Transaction::SERIALIZABLE:
$level = '6'; // SERIALIZABLE
break;
case Transaction::REPEATABLE_READ:
$level = '5'; // REPEATABLE READ CLASS with REPEATABLE READ INSTANCES
break;
case Transaction::READ_COMMITTED:
$level = '4'; // REPEATABLE READ CLASS with READ COMMITTED INSTANCES
break;
case Transaction::READ_UNCOMMITTED:
$level = '3'; // REPEATABLE READ CLASS with READ UNCOMMITTED INSTANCES
break;
}
parent::setTransactionIsolationLevel($level);
}
/**
* @inheritdoc
* @see http://www.cubrid.org/manual/91/en/sql/transaction.html#database-concurrency
*/
public function setTransactionIsolationLevel($level)
{
// translate SQL92 levels to CUBRID levels:
switch ($level) {
case Transaction::SERIALIZABLE:
$level = '6'; // SERIALIZABLE
break;
case Transaction::REPEATABLE_READ:
$level = '5'; // REPEATABLE READ CLASS with REPEATABLE READ INSTANCES
break;
case Transaction::READ_COMMITTED:
$level = '4'; // REPEATABLE READ CLASS with READ COMMITTED INSTANCES
break;
case Transaction::READ_UNCOMMITTED:
$level = '3'; // REPEATABLE READ CLASS with READ UNCOMMITTED INSTANCES
break;
}
parent::setTransactionIsolationLevel($level);
}
}
......@@ -225,7 +225,7 @@ class Schema extends \yii\db\Schema
*/
protected function getCreateTableSql($table)
{
$row = $this->db->createCommand('SHOW CREATE TABLE ' . $this->quoteSimpleTableName($table->name))->queryOne();
$row = $this->db->createCommand('SHOW CREATE TABLE ' . $this->quoteTableName($table->fullName))->queryOne();
if (isset($row['Create Table'])) {
$sql = $row['Create Table'];
} else {
......
......@@ -47,6 +47,6 @@ class QueryParamAuth extends AuthMethod
*/
public function handleFailure($response)
{
throw new UnauthorizedHttpException('You are requesting with an invalid access token.');
throw new UnauthorizedHttpException(Yii::t('yii', 'You are requesting with an invalid access token.'));
}
}
......@@ -8,6 +8,7 @@
namespace yii\helpers;
use Yii;
use yii\base\InvalidConfigException;
use yii\base\InvalidParamException;
/**
......@@ -117,9 +118,17 @@ class BaseFileHelper
* @param boolean $checkExtension whether to use the file extension to determine the MIME type in case
* `finfo_open()` cannot determine it.
* @return string the MIME type (e.g. `text/plain`). Null is returned if the MIME type cannot be determined.
* @throws InvalidConfigException when the `fileinfo` PHP extension is not installed and `$checkExtension` is `false`.
*/
public static function getMimeType($file, $magicFile = null, $checkExtension = true)
{
if (!extension_loaded('fileinfo')) {
if ($checkExtension) {
return static::getMimeTypeByExtension($file, $magicFile);
} else {
throw new InvalidConfigException('The fileinfo PHP extension is not installed.');
}
}
$info = finfo_open(FILEINFO_MIME_TYPE, $magicFile);
if ($info) {
......@@ -131,7 +140,7 @@ class BaseFileHelper
}
}
return $checkExtension ? static::getMimeTypeByExtension($file) : null;
return $checkExtension ? static::getMimeTypeByExtension($file, $magicFile) : null;
}
/**
......
......@@ -9,6 +9,7 @@ namespace yii\log;
use Yii;
use yii\base\Component;
use yii\base\ErrorHandler;
/**
* Dispatcher manages a set of [[Target|log targets]].
......@@ -183,7 +184,7 @@ class Dispatcher extends Component
} catch (\Exception $e) {
$target->enabled = false;
$targetErrors[] = [
'Unable to send log via '. get_class($target) .': ' . $e->getMessage(),
'Unable to send log via ' . get_class($target) . ': ' . ErrorHandler::convertExceptionToString($e),
Logger::LEVEL_WARNING,
__METHOD__,
microtime(true),
......
......@@ -235,9 +235,16 @@ abstract class Target extends Component
if (!is_string($text)) {
$text = VarDumper::export($text);
}
$traces = [];
if (isset($message[4])) {
foreach($message[4] as $trace) {
$traces[] = "in {$trace['file']}:{$trace['line']}";
}
}
$prefix = $this->getMessagePrefix($message);
return date('Y-m-d H:i:s', $timestamp) . " {$prefix}[$level][$category] $text";
return date('Y-m-d H:i:s', $timestamp) . " {$prefix}[$level][$category] $text"
. (empty($traces) ? '' : "\n " . implode("\n ", $traces));
}
/**
......
......@@ -144,6 +144,8 @@ abstract class BaseMailer extends Component implements MailerInterface, ViewCont
return Yii::createObject($config);
}
private $_message;
/**
* Creates a new message instance and optionally composes its body content via view rendering.
*
......@@ -167,30 +169,41 @@ abstract class BaseMailer extends Component implements MailerInterface, ViewCont
public function compose($view = null, array $params = [])
{
$message = $this->createMessage();
if ($view !== null) {
if ($view === null) {
return $message;
}
if (!array_key_exists('message', $params)) {
$params['message'] = $message;
if (is_array($view)) {
if (isset($view['html'])) {
$html = $this->render($view['html'], $params, $this->htmlLayout);
}
if (isset($view['text'])) {
$text = $this->render($view['text'], $params, $this->textLayout);
}
} else {
$html = $this->render($view, $params, $this->htmlLayout);
}
$this->_message = $message;
if (is_array($view)) {
if (isset($view['html'])) {
$html = $this->render($view['html'], $params, $this->htmlLayout);
}
if (isset($html)) {
$message->setHtmlBody($html);
if (isset($view['text'])) {
$text = $this->render($view['text'], $params, $this->textLayout);
}
if (isset($text)) {
$message->setTextBody($text);
} elseif (isset($html)) {
if (preg_match('|<body[^>]*>(.*?)</body>|is', $html, $match)) {
$html = $match[1];
}
$html = preg_replace('|<style[^>]*>(.*?)</style>|is', '', $html);
$message->setTextBody(strip_tags($html));
} else {
$html = $this->render($view, $params, $this->htmlLayout);
}
$this->_message = null;
if (isset($html)) {
$message->setHtmlBody($html);
}
if (isset($text)) {
$message->setTextBody($text);
} elseif (isset($html)) {
if (preg_match('|<body[^>]*>(.*?)</body>|is', $html, $match)) {
$html = $match[1];
}
$html = preg_replace('|<style[^>]*>(.*?)</style>|is', '', $html);
$message->setTextBody(strip_tags($html));
}
return $message;
}
......@@ -277,7 +290,7 @@ abstract class BaseMailer extends Component implements MailerInterface, ViewCont
{
$output = $this->getView()->render($view, $params, $this);
if ($layout !== false) {
return $this->getView()->render($layout, ['content' => $output], $this);
return $this->getView()->render($layout, ['content' => $output, 'message' => $this->_message], $this);
} else {
return $output;
}
......
......@@ -20,6 +20,7 @@ return [
'(not set)' => '(brak wartości)',
'An internal server error occurred.' => 'Wystąpił wewnętrzny błąd serwera.',
'Delete' => 'Usuń',
'Are you sure you want to delete this item?' => 'Czy na pewno usunąć ten element?',
'Error' => 'Błąd',
'File upload failed.' => 'Wgrywanie pliku nie powiodło się.',
'Home' => 'Strona domowa',
......
......@@ -64,11 +64,11 @@ return [
'in {delta, plural, =1{a month} other{# months}}' => 'ใน {delta} เดือน',
'in {delta, plural, =1{a year} other{# years}}' => 'ใน {delta} ปี',
'the input value' => 'ค่าป้อนที่เข้ามา',
'{attribute} "{value}" has already been taken.' => '{attribute} "{value}" ได้รับการบันทึก',
'{attribute} "{value}" has already been taken.' => '{attribute} "{value}" มีอยู่แล้ว',
'{attribute} cannot be blank.' => '{attribute} ต้องไม่ว่างเปล่า',
'{attribute} is invalid.' => '{attribute} ไม่ถูกต้อง',
'{attribute} is not a valid URL.' => '{attribute} URL ไม่ถูกต้อง',
'{attribute} is not a valid email address.' => '{attribute} รูปแบบอีเมล์ไม่ถูกต้อง',
'{attribute} is not a valid URL.' => '{attribute} ไม่ใช่รูปแบบ URL ที่ถูกต้อง',
'{attribute} is not a valid email address.' => '{attribute} ไม่ใช่รูปแบบอีเมลที่ถูกต้อง',
'{attribute} must be "{requiredValue}".' => '{attribute} ต้องการ "{requiredValue}"',
'{attribute} must be a number.' => '{attribute} ต้องเป็นตัวเลขเท่านั้น',
'{attribute} must be a string.' => '{attribute} ต้องเป็นตัวอักขระเท่านั้น',
......
......@@ -13,8 +13,6 @@ use yii\base\Object;
/**
* Assignment represents an assignment of a role to a user.
*
* It includes additional assignment information including [[ruleName]] and [[data]].
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @author Alexander Kochetov <creocoder@gmail.com>
* @since 2.0
......
......@@ -39,6 +39,13 @@ return array(
'memo' => 'Required for multibyte encoding string processing.'
),
array(
'name' => 'Mcrypt extension',
'mandatory' => false,
'condition' => extension_loaded('mcrypt'),
'by' => '<a href="http://www.yiiframework.com/doc-2.0/yii-base-security.html">Security Component</a>',
'memo' => 'Required by encrypt and decrypt methods.'
),
array(
'name' => 'Intl extension',
'mandatory' => false,
'condition' => $this->checkPhpExtensionVersion('intl', '1.0.2', '>='),
......@@ -50,7 +57,7 @@ return array(
),
array(
'name' => 'Fileinfo extension',
'mandatory' => true,
'mandatory' => false,
'condition' => extension_loaded('fileinfo'),
'by' => '<a href="http://www.php.net/manual/en/book.fileinfo.php">File Information</a>',
'memo' => 'Required for files upload to detect correct file mime-types.'
......
......@@ -9,8 +9,9 @@ echo "<?php\n";
?>
use yii\db\Schema;
use yii\db\Migration;
class <?= $className ?> extends \yii\db\Migration
class <?= $className ?> extends Migration
{
public function up()
{
......
......@@ -7,14 +7,14 @@ else
# basic application:
composer install --dev --prefer-dist -d apps/basic
cd apps/basic && composer require --dev codeception/codeception:1.8.*@dev codeception/specify:* codeception/verify:*
cd apps/basic && composer require --dev codeception/codeception:2.0.* codeception/specify:* codeception/verify:*
php vendor/bin/codecept build && cd ../..
# advanced application:
composer install --dev --prefer-dist -d apps/advanced
cd apps/advanced && composer require --dev codeception/codeception:1.8.*@dev codeception/specify:* codeception/verify:*
cd apps/advanced && composer require --dev codeception/codeception:2.0.* codeception/specify:* codeception/verify:*
./init --env=Development
sed -i s/root/travis/ common/config/main-local.php
cd backend && php ../vendor/bin/codecept build
......
......@@ -827,12 +827,14 @@ trait ActiveRecordTestTrait
$customerClass = $this->getCustomerClass();
/* @var $this TestCase|ActiveRecordTestTrait */
// save
/* @var $customer Customer */
$customer = $customerClass::findOne(2);
$this->assertTrue($customer instanceof $customerClass);
$this->assertEquals('user2', $customer->name);
$this->assertFalse($customer->isNewRecord);
static::$afterSaveNewRecord = null;
static::$afterSaveInsert = null;
$this->assertEmpty($customer->dirtyAttributes);
$customer->name = 'user2x';
$customer->save();
......
......@@ -131,4 +131,19 @@ class SecurityTest extends TestCase
$decryptedData = $this->security->decrypt($encryptedData, $key);
$this->assertEquals($data, $decryptedData);
}
public function testGenerateRandomBytes()
{
$length = 21;
$key = $this->security->generateRandomBytes($length);
$this->assertEquals($length, strlen($key));
}
public function testGenerateRandomKey()
{
$length = 21;
$key = $this->security->generateRandomKey($length);
$this->assertEquals($length, strlen($key));
$this->assertEquals(1, preg_match('/[A-Za-z0-9_.-]+/', $key));
}
}
<?php
namespace yiiunit\framework\rbac;
use yii\rbac\PhpManager;
/**
* Exposes protected properties and methods to inspect from outside
*/
class ExposedPhpManager extends PhpManager
{
/**
* @var \yii\rbac\Item[]
*/
public $items = []; // itemName => item
/**
* @var array
*/
public $children = []; // itemName, childName => child
/**
* @var \yii\rbac\Assignment[]
*/
public $assignments = []; // userId, itemName => assignment
/**
* @var \yii\rbac\Rule[]
*/
public $rules = []; // ruleName => rule
public function load()
{
parent::load();
}
public function save()
{
parent::save();
}
}
\ No newline at end of file
......@@ -7,6 +7,9 @@ use yii\rbac\Permission;
use yii\rbac\Role;
use yiiunit\TestCase;
/**
* ManagerTestCase
*/
abstract class ManagerTestCase extends TestCase
{
/**
......@@ -14,7 +17,7 @@ abstract class ManagerTestCase extends TestCase
*/
protected $auth;
public function testCreateRoleAndPermission()
public function testCreateRole()
{
$role = $this->auth->createRole('admin');
$this->assertTrue($role instanceof Role);
......@@ -57,174 +60,6 @@ abstract class ManagerTestCase extends TestCase
$this->auth->addChild($user, $changeName);
$this->assertCount(1, $this->auth->getChildren($user->name));
}
/*
public function testRemove()
{
}
public function testUpdate()
{
}
public function testCreateItem()
{
$type = Item::TYPE_TASK;
$name = 'editUser';
$description = 'edit a user';
$ruleName = 'isAuthor';
$data = [1, 2, 3];
$item = $this->auth->createItem($name, $type, $description, $ruleName, $data);
$this->assertTrue($item instanceof Item);
$this->assertEquals($item->type, $type);
$this->assertEquals($item->name, $name);
$this->assertEquals($item->description, $description);
$this->assertEquals($item->ruleName, $ruleName);
$this->assertEquals($item->data, $data);
// test shortcut
$name2 = 'createUser';
$item2 = $this->auth->createRole($name2, $description, $ruleName, $data);
$this->assertEquals($item2->type, Item::TYPE_ROLE);
// test adding an item with the same name
$this->setExpectedException('\yii\base\Exception');
$this->auth->createItem($name, $type, $description, $ruleName, $data);
}
public function testGetItem()
{
$this->assertTrue($this->auth->getItem('readPost') instanceof Item);
$this->assertTrue($this->auth->getItem('reader') instanceof Item);
$this->assertNull($this->auth->getItem('unknown'));
}
public function testRemoveItem()
{
$this->assertTrue($this->auth->getItem('updatePost') instanceof Item);
$this->assertTrue($this->auth->removeItem('updatePost'));
$this->assertNull($this->auth->getItem('updatePost'));
$this->assertFalse($this->auth->removeItem('updatePost'));
}
public function testChangeItemName()
{
$item = $this->auth->getItem('readPost');
$this->assertTrue($item instanceof Item);
$this->assertTrue($this->auth->hasItemChild('reader', 'readPost'));
$item->name = 'readPost2';
$item->save();
$this->assertNull($this->auth->getItem('readPost'));
$this->assertEquals($this->auth->getItem('readPost2'), $item);
$this->assertFalse($this->auth->hasItemChild('reader', 'readPost'));
$this->assertTrue($this->auth->hasItemChild('reader', 'readPost2'));
}
public function testAddItemChild()
{
$this->auth->addItemChild('createPost', 'updatePost');
// test adding upper level item to lower one
$this->setExpectedException('\yii\base\Exception');
$this->auth->addItemChild('readPost', 'reader');
}
public function testAddItemChild2()
{
// test adding inexistent items
$this->setExpectedException('\yii\base\Exception');
$this->assertFalse($this->auth->addItemChild('createPost2', 'updatePost'));
}
public function testRemoveItemChild()
{
$this->assertTrue($this->auth->hasItemChild('reader', 'readPost'));
$this->assertTrue($this->auth->removeItemChild('reader', 'readPost'));
$this->assertFalse($this->auth->hasItemChild('reader', 'readPost'));
$this->assertFalse($this->auth->removeItemChild('reader', 'readPost'));
}
public function testGetItemChildren()
{
$this->assertEquals([], $this->auth->getItemChildren('readPost'));
$children = $this->auth->getItemChildren('author');
$this->assertEquals(3, count($children));
$this->assertTrue(reset($children) instanceof Item);
}
public function testAssign()
{
$auth = $this->auth->assign('new user', 'createPost', 'isAuthor', 'data');
$this->assertTrue($auth instanceof Assignment);
$this->assertEquals($auth->userId, 'new user');
$this->assertEquals($auth->itemName, 'createPost');
$this->assertEquals($auth->ruleName, 'isAuthor');
$this->assertEquals($auth->data, 'data');
$this->setExpectedException('\yii\base\Exception');
$this->auth->assign('new user', 'createPost2', 'rule', 'data');
}
public function testRevoke()
{
$this->assertTrue($this->auth->isAssigned('author B', 'author'));
$auth = $this->auth->getAssignment('author B', 'author');
$this->assertTrue($auth instanceof Assignment);
$this->assertTrue($this->auth->revoke('author B', 'author'));
$this->assertFalse($this->auth->isAssigned('author B', 'author'));
$this->assertFalse($this->auth->revoke('author B', 'author'));
}
public function testRevokeAll()
{
$this->assertTrue($this->auth->revokeAll('reader E'));
$this->assertFalse($this->auth->isAssigned('reader E', 'reader'));
}
public function testGetAssignments()
{
$this->auth->assign('author B', 'deletePost');
$auths = $this->auth->getAssignments('author B');
$this->assertEquals(2, count($auths));
$this->assertTrue(reset($auths) instanceof Assignment);
}
public function testGetItems()
{
$this->assertEquals(count($this->auth->getRoles()), 4);
$this->assertEquals(count($this->auth->getOperations()), 4);
$this->assertEquals(count($this->auth->getTasks()), 1);
$this->assertEquals(count($this->auth->getItems()), 9);
$this->assertEquals(count($this->auth->getItems('author B', null)), 1);
$this->assertEquals(count($this->auth->getItems('author C', null)), 0);
$this->assertEquals(count($this->auth->getItems('author B', Item::TYPE_ROLE)), 1);
$this->assertEquals(count($this->auth->getItems('author B', Item::TYPE_OPERATION)), 0);
}
public function testClearAll()
{
$this->auth->clearAll();
$this->assertEquals(count($this->auth->getRoles()), 0);
$this->assertEquals(count($this->auth->getOperations()), 0);
$this->assertEquals(count($this->auth->getTasks()), 0);
$this->assertEquals(count($this->auth->getItems()), 0);
$this->assertEquals(count($this->auth->getAssignments('author B')), 0);
}
public function testClearAssignments()
{
$this->auth->clearAssignments();
$this->assertEquals(count($this->auth->getAssignments('author B')), 0);
}
public function testDetectLoop()
{
$this->setExpectedException('\yii\base\Exception');
$this->auth->addItemChild('readPost', 'readPost');
}
*/
public function testGetRule()
{
......
......@@ -3,38 +3,74 @@
namespace yiiunit\framework\rbac;
use Yii;
use yii\rbac\PhpManager;
/**
* @group rbac
* @property \yii\rbac\PhpManager $auth
* @property ExposedPhpManager $auth
*/
class PhpManagerTest extends ManagerTestCase
{
protected function getItemFile()
{
return Yii::$app->getRuntimePath() . '/rbac-items.php';
}
protected function getAssignmentFile()
{
return Yii::$app->getRuntimePath() . '/rbac-assignments.php';
}
protected function getRuleFile()
{
return Yii::$app->getRuntimePath() . '/rbac-rules.php';
}
protected function removeDataFiles()
{
@unlink($this->getItemFile());
@unlink($this->getAssignmentFile());
@unlink($this->getRuleFile());
}
protected function createManager()
{
return new ExposedPhpManager([
'itemFile' => $this->getItemFile(),
'assignmentFile' => $this->getAssignmentFile(),
'ruleFile' => $this->getRuleFile(),
]);
}
protected function setUp()
{
parent::setUp();
$this->mockApplication();
$authFile = Yii::$app->getRuntimePath() . '/rbac.php';
@unlink($authFile);
$this->auth = new PhpManager();
$this->auth->authFile = $authFile;
$this->auth->init();
$this->removeDataFiles();
$this->auth = $this->createManager();
}
protected function tearDown()
{
$this->removeDataFiles();
parent::tearDown();
@unlink($this->auth->authFile);
}
public function testSaveLoad()
{
$this->prepareData();
$items = $this->auth->items;
$children = $this->auth->children;
$assignments = $this->auth->assignments;
$rules = $this->auth->rules;
$this->auth->save();
$this->auth->removeAll();
$this->auth = $this->createManager();
$this->auth->load();
// TODO : Check if loaded and saved data are the same.
}
$this->assertEquals($items, $this->auth->items);
$this->assertEquals($children, $this->auth->children);
$this->assertEquals($assignments, $this->auth->assignments);
$this->assertEquals($rules, $this->auth->rules);
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment