Commit 9e9c52e9 by Klimov Paul

Merge branch 'master' of github.com:yiisoft/yii2

parents 801ed458 ce335616
......@@ -23,6 +23,7 @@
"yiisoft/yii2-codeception": "*",
"codeception/codeception": "*",
"codeception/specify": "*",
"codeception/verify": "*",
"yiisoft/yii2-debug": "*",
"yiisoft/yii2-gii": "*"
},
......
......@@ -39,15 +39,16 @@ class ContactFormTest extends TestCase
$model->contact('admin@example.com');
$this->specify('email should be send', function () {
$this->assertFileExists($this->getMessageFile(), 'email file should exist');
expect('email file should exist', file_exists($this->getMessageFile()))->true();
});
$this->specify('message should contain correct data', function () use($model) {
$emailMessage = file_get_contents($this->getMessageFile());
$this->assertContains($model->name, $emailMessage, 'email should contain user name');
$this->assertContains($model->email, $emailMessage, 'email should contain sender email');
$this->assertContains($model->subject, $emailMessage, 'email should contain subject');
$this->assertContains($model->body, $emailMessage, 'email should contain body');
expect('email should contain user name', $emailMessage)->contains($model->name);
expect('email should contain sender email', $emailMessage)->contains($model->email);
expect('email should contain subject', $emailMessage)->contains($model->subject);
expect('email should contain body', $emailMessage)->contains($model->body);
});
}
......
......@@ -19,8 +19,8 @@ class LoginFormTest extends TestCase
$model->password = 'some_password';
$this->specify('user should not be able to login, when there is no identity' , function () use ($model) {
$this->assertFalse($model->login());
$this->assertTrue(Yii::$app->user->isGuest,'user should not be logged in');
expect('model should not login user', $model->login())->false();
expect('user should not be logged in', Yii::$app->user->isGuest)->true();
});
}
......@@ -32,9 +32,9 @@ class LoginFormTest extends TestCase
$model->password = 'wrong-password';
$this->specify('user should not be able to login with wrong password', function () use ($model) {
$this->assertFalse($model->login());
$this->assertArrayHasKey('password', $model->errors);
$this->assertTrue(Yii::$app->user->isGuest, 'user should not be logged in');
expect('model should not login user', $model->login())->false();
expect('error message should be set', $model->errors)->hasKey('password');
expect('user should not be logged in', Yii::$app->user->isGuest)->true();
});
}
......@@ -46,9 +46,9 @@ class LoginFormTest extends TestCase
$model->password = 'demo';
$this->specify('user should be able to login with correct credentials', function() use ($model) {
$this->assertTrue($model->login());
$this->assertArrayNotHasKey('password', $model->errors);
$this->assertFalse(Yii::$app->user->isGuest,'user should be logged in');
expect('model should login user', $model->login())->true();
expect('error message should not be set', $model->errors)->hasntKey('password');
expect('user should be logged in', Yii::$app->user->isGuest)->false();
});
}
......
......@@ -76,8 +76,7 @@ There are two ActiveRecord methods for querying data from database:
- [[ActiveRecord::find()]]
- [[ActiveRecord::findBySql()]]
Both methods return an [[ActiveQuery]] instance, which extends [[Query]], and thus supports
the same set of flexible and powerful DB query methods. The following examples demonstrate some of the possibilities.
Both methods return an [[ActiveQuery]] instance, which extends [[Query]], and thus supports the same set of flexible and powerful DB query methods. The following examples demonstrate some of the possibilities.
```php
// to retrieve all *active* customers and order them by their ID:
......@@ -119,8 +118,7 @@ Accessing Column Data
---------------------
ActiveRecord maps each column of the corresponding database table row to an attribute in the ActiveRecord
object. The attribute behaves like any regular object public property. The attribute's name will be the same as the corresponding column
name, and is case-sensitive.
object. The attribute behaves like any regular object public property. The attribute's name will be the same as the corresponding column name, and is case-sensitive.
To read the value of a column, you can use the following syntax:
......@@ -151,9 +149,7 @@ ActiveRecord provides the following methods to insert, update and delete data in
- [[ActiveRecord::updateAllCounters()|updateAllCounters()]]
- [[ActiveRecord::deleteAll()|deleteAll()]]
Note that [[ActiveRecord::updateAll()|updateAll()]], [[ActiveRecord::updateAllCounters()|updateAllCounters()]]
and [[ActiveRecord::deleteAll()|deleteAll()]] are static methods that apply to the whole database
table. The other methods only apply to the row associated with the ActiveRecord object through which the method is being called.
Note that [[ActiveRecord::updateAll()|updateAll()]], [[ActiveRecord::updateAllCounters()|updateAllCounters()]] and [[ActiveRecord::deleteAll()|deleteAll()]] are static methods that apply to the whole database table. The other methods only apply to the row associated with the ActiveRecord object through which the method is being called.
```php
// to insert a new customer record
......@@ -477,7 +473,7 @@ of the corresponding JOIN query. For example,
```php
// SELECT tbl_user.* FROM tbl_user LEFT JOIN tbl_item ON tbl_item.owner_id=tbl_user.id AND category_id=1
// SELECT * FROM tbl_item WHERE owner_id IN (...) AND category_id=1
$users = User::model()->joinWith('books')->all();
$users = User::find()->joinWith('books')->all();
```
Note that if you use eager loading via [[ActiveQuery::with()]] or lazy loading, the on-condition will be put
......@@ -485,7 +481,7 @@ in the WHERE part of the corresponding SQL statement, because there is no JOIN q
```php
// SELECT * FROM tbl_user WHERE id=10
$user = User::model(10);
$user = User::find(10);
// SELECT * FROM tbl_item WHERE owner_id=10 AND category_id=1
$books = $user->books;
```
......@@ -547,33 +543,61 @@ Finally when calling [[delete()]] to delete an ActiveRecord, we will have the fo
3. [[afterDelete()]]: will trigger an [[EVENT_AFTER_DELETE]] event
Scopes
------
Custom scopes
-------------
A scope is a method that customizes a given [[ActiveQuery]] object. Scope methods are static and are defined
in the ActiveRecord classes. They can be invoked through the [[ActiveQuery]] object that is created
via [[find()]] or [[findBySql()]]. The following is an example:
When [[find()]] or [[findBySql()]] Active Record method is being called without parameters it returns an [[ActiveQuery]]
instance. This object holds all the parameters and conditions for a future query and also allows you to customize these
using a set of methods that are called scopes. By default there is a good set of such methods some of which we've
already used above: `where`, `orderBy`, `limit` etc.
In many cases it is convenient to wrap extra conditions into custom scope methods. In order to do so you need two things.
First is creating a custom query class for your model. For example, a `Comment` may have a `CommentQuery`:
```php
class Comment extends \yii\db\ActiveRecord
namespace app\models;
use yii\db\ActiveQuery;
class CommentQuery extends ActiveQuery
{
// ...
public function active($state = true)
{
$this->andWhere(['active' => $state]);
return $this;
}
}
```
/**
* @param ActiveQuery $query
*/
public static function active($query)
Important points are:
1. Class should extend from `yii\db\ActiveQuery` (or another `ActiveQuery` such as `yii\mongodb\ActiveQuery`).
2. A method should be `public` and should return `$this` in order to allow method chaining. It may accept parameters.
3. Check `ActiveQuery` methods that are very useful for modifying query conditions.
The second step is to use `CommentQuery` instead of regular `ActiveQuery` for `Comment` model:
```
namespace app\models;
use yii\db\ActiveRecord;
class Comment extends ActiveRecord
{
public static function createQuery()
{
$query->andWhere('status = 1');
return new CommentQuery(['modelClass' => get_called_class()]);
}
}
```
That's it. Now you can use your custom scope methods:
```php
$comments = Comment::find()->active()->all();
$inactiveComments = Comment::find()->active(false)->all();
```
In the above, the `active()` method is defined in `Comment` while we are calling it
through `ActiveQuery` returned by `Comment::find()`.
You can also use scopes when defining relations. For example,
```php
......@@ -597,29 +621,32 @@ $posts = Post::find()->with([
])->all();
```
Scopes can be parameterized. For example, we can define and use the following `olderThan` scope:
### Making it IDE-friendly
In order to make most modern IDE autocomplete happy you need to override return types for some methods of both model
and query like the following:
```php
class Customer extends \yii\db\ActiveRecord
/**
* @method \app\models\CommentQuery|static|null find($q = null) static
* @method \app\models\CommentQuery findBySql($sql, $params = []) static
*/
class Comment extends ActiveRecord
{
// ...
/**
* @param ActiveQuery $query
* @param integer $age
*/
public static function olderThan($query, $age = 30)
{
$query->andWhere('age > :age', [':age' => $age]);
}
}
$customers = Customer::find()->olderThan(50)->all();
```
The parameters should follow after the `$query` parameter when defining the scope method, and they
can take default values like shown above.
```php
/**
* @method \app\models\Comment|array|null one($db = null)
* @method \app\models\Comment[]|array all($db = null)
*/
class CommentQuery extends ActiveQuery
{
// ...
}
```
Transactional operations
------------------------
......@@ -632,10 +659,10 @@ TODO: FIXME: WIP, TBD, https://github.com/yiisoft/yii2/issues/226
,
[[afterSave()]], [[beforeDelete()]] and/or [[afterDelete()]] life cycle methods. Developer may come
to the solution of overriding ActiveRecord [[save()]] method with database transaction wrapping or
even using transaction in controller action, which is strictly speaking doesn't seems to be a good
practice (recall skinny-controller fat-model fundamental rule).
even using transaction in controller action, which is strictly speaking doesn't seem to be a good
practice (recall "skinny-controller / fat-model" fundamental rule).
Here these ways are (**DO NOT** use them unless you're sure what are you actually doing). Models:
Here these ways are (**DO NOT** use them unless you're sure what you are actually doing). Models:
```php
class Feature extends \yii\db\ActiveRecord
......
......@@ -90,7 +90,7 @@ There are multiple problems with straightforward approach to configuration:
- Defining all configuration options for each case is very repetitive and takes too much time to maintain.
In order to solve these issues Yii introduces environments concept that is very simple. Each environment is represented
by a set of files under `environments` directory. `init` command is used to switch between these. What is really does is
by a set of files under `environments` directory. `init` command is used to switch between these. What it really does is
just copying everything from environment directory over the root directory where all applications are.
Typically environment contains application bootstrap files such as `index.php` and config files suffixed with
......
......@@ -248,7 +248,7 @@ public function editArticle($id)
throw new NotFoundHttpException;
}
if (!\Yii::$app->user->checkAccess('edit_article', ['article' => $article])) {
throw new AccessDeniedHttpException;
throw new ForbiddenHttpException;
}
// ...
}
......
......@@ -260,7 +260,7 @@ $connection->createCommand()->createTable('tbl_post', [
'id' => 'pk',
'title' => 'string',
'text' => 'text',
];
]);
```
For the full reference check [[\yii\db\Command]].
......@@ -40,6 +40,8 @@ Format is `ll-CC` where `ll` is two- or three-letter lowercase code for a langu
[ISO-639](http://www.loc.gov/standards/iso639-2/) and `CC` is country code according to
[ISO-3166](http://www.iso.org/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html).
If there's no translation for `ru-RU` Yii will try `ru` as well before failing.
> **Note**: you can further customize details specifying language
> [as documented in ICU project](http://userguide.icu-project.org/locale#TOC-The-Locale-Concept).
......@@ -64,7 +66,7 @@ Yii tries to load appropriate translation from one of the message sources define
'app*' => [
'class' => 'yii\i18n\PhpMessageSource',
//'basePath' => '@app/messages',
//'sourceLanguage' => 'en-US',
//'sourceLanguage' => 'en',
'fileMap' => [
'app' => 'app.php',
'app/error' => 'error.php',
......@@ -273,8 +275,8 @@ You can use i18n in your views to provide support for different languages. For e
you want to create special case for russian language, you create `ru-RU` folder under the view path of current controller/widget and
put there file for russian language as follows `views/site/ru-RU/index.php`.
> **Note**: You should note that in **Yii2** language id style has changed, now it use dash **ru-RU, en-US, pl-PL** instead of underscore, because of
> php **intl** library.
> **Note**: If language is specified as `en-US` and there are no corresponding views, Yii will try views under `en`
> before using original ones.
Formatters
----------
......
......@@ -88,7 +88,7 @@ class UserProfileFixture extends ActiveFixture
public $modelClass = 'app\models\UserProfile';
public $depends = ['app\tests\fixtures\UserFixture'];
}
```
In the above, we have shown how to define a fixture about a DB table. To define a fixture not related with DB
(e.g. a fixture about certain files and directories), you may extend from the more general base class
......
......@@ -459,6 +459,8 @@ By default, ActiveRecord now only saves dirty attributes. In 1.1, all attributes
are saved to database when you call `save()`, regardless of having changed or not,
unless you explicitly list the attributes to save.
Scopes are now defined in a custom `ActiveQuery` class instead of model directly.
See [active record docs](active-record.md) for more details.
......
......@@ -12,18 +12,20 @@ defined('YII_DEBUG') or define('YII_DEBUG', false);
$composerAutoload = [
__DIR__ . '/vendor/autoload.php', // standalone with "composer install" run
__DIR__ . '/../../../../autoload.php', // script is installed as a composer binary
__DIR__ . '/../../autoload.php', // script is installed as a composer binary
];
$vendorPath = null;
foreach($composerAutoload as $autoload) {
if (file_exists($autoload)) {
require($autoload);
$vendorPath = dirname($autoload);
break;
}
}
$yiiDirs = [
__DIR__ . '/../../../framework', // in yii2-dev repo
__DIR__ . '/../../framework', // in yii2-dev repo
__DIR__ . '/vendor/yiisoft/yii2', // standalone with "composer install" run
__DIR__ . '/../../../../yiisoft/yii2', // script is installed as a composer binary
__DIR__ . '/../../yiisoft/yii2', // script is installed as a composer binary
];
foreach($yiiDirs as $dir) {
if (file_exists($dir . '/Yii.php')) {
......@@ -45,5 +47,8 @@ $application = new yii\console\Application([
'controllerNamespace' => 'yii\\apidoc\\commands',
'controllerPath' => '@yii/apidoc/commands',
]);
if ($vendorPath !== null) {
$application->setVendorPath($vendorPath);
}
$exitCode = $application->run();
exit($exitCode);
......@@ -20,7 +20,8 @@
"minimum-stability": "dev",
"require": {
"yiisoft/yii2": "*",
"phpdocumentor/reflection": "1.0.2"
"yiisoft/yii2-bootstrap": "*",
"phpdocumentor/reflection": "dev-master | >1.0.2"
},
"autoload": {
"psr-4": { "yii\\apidoc\\": "" }
......
......@@ -154,7 +154,7 @@ class TypeDoc extends BaseDoc
{
parent::__construct($reflector, $config);
$this->namespace = StringHelper::basename($this->name);
$this->namespace = StringHelper::dirname($this->name);
if ($reflector === null) {
return;
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\templates\bootstrap;
use yii\apidoc\models\Context;
use yii\console\Controller;
use Yii;
use yii\helpers\Console;
use yii\helpers\FileHelper;
/**
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class Renderer extends \yii\apidoc\templates\html\Renderer
{
public $layout = '@yii/apidoc/templates/bootstrap/views/bootstrap.php';
public $indexView = '@yii/apidoc/templates/bootstrap/views/index.php';
public $pageTitle = 'Yii Framework 2.0 API Documentation';
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\templates\bootstrap;
use Yii;
use yii\base\InvalidConfigException;
use yii\bootstrap\BootstrapAsset;
use yii\helpers\ArrayHelper;
use yii\helpers\Html;
/**
* Nav renders a nav HTML component.
*
* For example:
*
* ```php
* echo Nav::widget([
* 'items' => [
* [
* 'label' => 'Home',
* 'url' => ['site/index'],
* 'linkOptions' => [...],
* ],
* [
* 'label' => 'Dropdown',
* 'items' => [
* ['label' => 'Level 1 - Dropdown A', 'url' => '#'],
* '<li class="divider"></li>',
* '<li class="dropdown-header">Dropdown Header</li>',
* ['label' => 'Level 1 - Dropdown B', 'url' => '#'],
* ],
* ],
* ],
* ]);
* ```
*
* Note: Multilevel dropdowns beyond Level 1 are not supported in Bootstrap 3.
*
* @see http://getbootstrap.com/components.html#dropdowns
* @see http://getbootstrap.com/components/#nav
*
* @author Antonio Ramirez <amigo.cobos@gmail.com>
* @since 2.0
*/
class SideNavWidget extends \yii\bootstrap\Widget
{
/**
* @var array list of items in the nav widget. Each array element represents a single
* menu item which can be either a string or an array with the following structure:
*
* - label: string, required, the nav item label.
* - url: optional, the item's URL. Defaults to "#".
* - visible: boolean, optional, whether this menu item is visible. Defaults to true.
* - linkOptions: array, optional, the HTML attributes of the item's link.
* - options: array, optional, the HTML attributes of the item container (LI).
* - active: boolean, optional, whether the item should be on active state or not.
* - items: array|string, optional, the configuration array for creating a [[Dropdown]] widget,
* or a string representing the dropdown menu. Note that Bootstrap does not support sub-dropdown menus.
*
* If a menu item is a string, it will be rendered directly without HTML encoding.
*/
public $items = [];
/**
* @var boolean whether the nav items labels should be HTML-encoded.
*/
public $encodeLabels = true;
/**
* @var string the route used to determine if a menu item is active or not.
* If not set, it will use the route of the current request.
* @see params
* @see isItemActive
*/
public $activeUrl;
/**
* Initializes the widget.
*/
public function init()
{
parent::init();
if (!isset($this->options['class'])) {
Html::addCssClass($this->options, 'list-group');
}
}
/**
* Renders the widget.
*/
public function run()
{
echo $this->renderItems();
BootstrapAsset::register($this->getView());
}
/**
* Renders widget items.
*/
public function renderItems()
{
$items = [];
foreach ($this->items as $i => $item) {
if (isset($item['visible']) && !$item['visible']) {
unset($items[$i]);
continue;
}
$items[] = $this->renderItem($item);
}
return Html::tag('div', implode("\n", $items), $this->options);
}
/**
* Renders a widget's item.
* @param string|array $item the item to render.
* @return string the rendering result.
* @throws InvalidConfigException
*/
public function renderItem($item)
{
if (is_string($item)) {
return $item;
}
if (!isset($item['label'])) {
throw new InvalidConfigException("The 'label' option is required.");
}
$label = $this->encodeLabels ? Html::encode($item['label']) : $item['label'];
// $options = ArrayHelper::getValue($item, 'options', []);
$items = ArrayHelper::getValue($item, 'items');
$url = Html::url(ArrayHelper::getValue($item, 'url', '#'));
$linkOptions = ArrayHelper::getValue($item, 'linkOptions', []);
Html::addCssClass($linkOptions, 'list-group-item');
if (isset($item['active'])) {
$active = ArrayHelper::remove($item, 'active', false);
} else {
$active = ($url == $this->activeUrl);
}
if ($items !== null) {
$linkOptions['data-toggle'] = 'collapse';
$linkOptions['data-parent'] = '#' . $this->id;
$id = $this->id . '-' . static::$counter++;
$url = '#' . $id;
$label .= ' ' . Html::tag('b', '', ['class' => 'caret']);
if (is_array($items)) {
if ($active === false) {
foreach($items as $subItem) {
if (isset($subItem['active']) && $subItem['active']) {
$active = true;
}
}
}
$items = static::widget([
'id' => $id,
'items' => $items,
'encodeLabels' => $this->encodeLabels,
'view' => $this->getView(),
'options' => [
'class' => "submenu panel-collapse collapse" . ($active ? ' in' : '')
]
]);
}
}
if ($active) {
Html::addCssClass($linkOptions, 'active');
}
return Html::a($label, $url, $linkOptions) . $items;
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\templates\bootstrap\assets;
use yii\web\JqueryAsset;
use yii\web\View;
/**
* The asset bundle for the offline template.
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class AssetBundle extends \yii\web\AssetBundle
{
public $sourcePath = '@yii/apidoc/templates/bootstrap/assets/css';
public $css = [
// 'api.css',
'style.css',
];
public $depends = [
'yii\web\JqueryAsset',
'yii\bootstrap\BootstrapAsset',
'yii\bootstrap\BootstrapPluginAsset',
];
public $jsOptions = [
'position' => View::POS_HEAD,
];
}
pre {
color: #000000;
background-color: #FFF5E6;
font-family: "courier new", "times new roman", monospace;
line-height: 1.3em;
/* Put a nice border around it. */
padding: 1px;
width: 90%;
/* Don't wrap its contents, and show scrollbars. */
/* white-space: nowrap;*/
overflow: auto;
/* Stop after about 24 lines, and just show a scrollbar. */
/* max-height: 24em; */
margin: 5px;
padding-left: 20px;
border: 1px solid #FFE6BF;
border-left: 6px solid #FFE6BF;
}
code {
color: #000000;
background-color: #FFF5E6;
padding: 1px;
}
div.code {
display: none;
color: #000000;
background-color: #FFF5E6;
font-family: "courier new", "times new roman", monospace;
line-height: 1.3em;
/* Put a nice border around it. */
padding: 1px;
width: 90%;
/* Don't wrap its contents, and show scrollbars. */
/* white-space: nowrap;*/
overflow: auto;
/* Stop after about 24 lines, and just show a scrollbar. */
/* max-height: 24em; */
margin: 5px;
padding-left: 20px;
border-left: 6px solid #FFE6BF;
}
table.summaryTable {
background: #E6ECFF;
border-collapse: collapse;
width: 100%;
}
table.summaryTable th, table.summaryTable td {
border: 1px #BFCFFF solid;
padding: 0.2em;
}
table.summaryTable th {
background: #CCD9FF;
text-align: left;
}
#nav {
padding: 3px;
margin: 0 0 10px 0;
border-top: 1px #BFCFFF solid;
}
#classDescription {
padding: 5px;
margin: 10px 0 20px 0;
border-bottom: 1px solid #BFCFFF;
}
.detailHeader {
font-weight: bold;
font-size: 12pt;
margin: 30px 0 5px 0;
border-bottom: 1px solid #BFCFFF;
}
.detailHeaderTag {
font-weight: normal;
font-size: 10pt;
}
.paramNameCol {
width: 12%;
font-weight: bold;
}
.paramTypeCol {
width: 12%;
}
.sourceCode {
margin: 5px 0;
padding:5px;
background:#FFF5E6;
}
\ No newline at end of file
html,
body {
height: 100%;
}
.wrap {
min-height: 100%;
height: auto;
width: auto;
margin: 60px 30px 0 30px;
padding: 0;
}
.footer {
height: 60px;
background-color: #f5f5f5;
border-top: 1px solid #ddd;
padding: 20px 30px;
}
#navigation {
margin-top: 20px;
}
.submenu a {
background: #f5f5f5;
border-radius: 0;
}
.submenu a:hover, .submenu a:active,
.submenu a.active, .submenu a.active:hover, .submenu a.active:active {
background: #44b5f6;
border-color: #44b5f6;
border-radius: 0;
color: #fff;
}
.signature, .signature2 {
padding: 3px;
color: #000000;
font-family: "courier new", "times new roman", monospace;
line-height: 1.3em;
white-space: pre-line;
word-wrap: break-word;
word-break: break-all;
}
.signature {
margin: 10px 0 10px 0;
background: #E6ECFF;
border: 1px #BFCFFF solid;
}
<?php
use yii\apidoc\templates\bootstrap\SideNavWidget;
use yii\bootstrap\Nav;
use yii\bootstrap\NavBar;
use yii\helpers\Html;
use yii\helpers\StringHelper;
use yii\widgets\Menu;
/**
* @var yii\web\View $this
*/
\yii\apidoc\templates\bootstrap\assets\AssetBundle::register($this);
$this->beginPage();
?>
<!DOCTYPE html>
<html lang="<?= Yii::$app->language ?>">
<head>
<meta charset="<?= Yii::$app->charset ?>"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="language" content="en" />
<?php $this->head() ?>
<title><?= Html::encode($this->context->pageTitle) ?></title>
</head>
<body>
<?php $this->beginBody() ?>
<div class="wrap">
<?php
NavBar::begin([
'brandLabel' => $this->context->pageTitle,
'brandUrl' => './index.html',
'options' => [
'class' => 'navbar-inverse navbar-fixed-top',
],
'padded' => false,
'view' => $this,
]);
echo Nav::widget([
'options' => ['class' => 'navbar-nav'],
'items' => [
['label' => 'Class reference', 'url' => './index.html'],
// ['label' => 'Application API', 'url' => '/site/about'],
// ['label' => 'Guide', 'url' => './guide_index.html'],
],
'view' => $this,
]);
NavBar::end();
?>
<!-- <div class="container">-->
<div class="row">
<div class="col-md-2">
<?php
ksort($types);
$nav = [];
foreach($types as $i=>$class) {
$namespace = $class->namespace;
if (empty($namespace)) {
$namespace = 'Not namespaced classes';
}
if (!isset($nav[$namespace])) {
$nav[$namespace] = [
'label' => $namespace,
'url' => '#',
'items' => [],
];
}
$nav[$namespace]['items'][] = [
'label' => StringHelper::basename($class->name),
'url' => './' . $this->context->generateUrl($class->name),
'active' => isset($type) && ($class->name == $type->name),
];
} ?>
<?= SideNavWidget::widget([
'id' => 'navigation',
'items' => $nav,
// 'route' => 'wtf',
'view' => $this,
])?>
</div>
<div class="col-md-9" role="main">
<?= $content ?>
</div>
</div>
<!-- </div>-->
</div>
<footer class="footer">
<?php /* <p class="pull-left">&copy; My Company <?= date('Y') ?></p> */ ?>
<p class="pull-right"><?= Yii::powered() ?></p>
</footer>
<script type="text/javascript">
/*<![CDATA[*/
$("a.toggle").on('click', function() {
var $this = $(this);
if ($this.hasClass('properties-hidden')) {
$this.text($this.text().replace(/Show/,'Hide'));
$this.parents(".summary").find(".inherited").show();
$this.removeClass('properties-hidden');
} else {
$this.text($this.text().replace(/Hide/,'Show'));
$this.parents(".summary").find(".inherited").hide();
$this.addClass('properties-hidden');
}
return false;
});
/*
$(".sourceCode a.show").toggle(function(){
$(this).text($(this).text().replace(/show/,'hide'));
$(this).parents(".sourceCode").find("div.code").show();
},function(){
$(this).text($(this).text().replace(/hide/,'show'));
$(this).parents(".sourceCode").find("div.code").hide();
});
$("a.sourceLink").click(function(){
$(this).attr('target','_blank');
});
*/
/*]]>*/
</script>
<?php $this->endBody() ?>
</body>
</html>
<?php $this->endPage() ?>
\ No newline at end of file
<?php
use yii\apidoc\models\ClassDoc;
use yii\apidoc\models\InterfaceDoc;
use yii\apidoc\models\TraitDoc;
/**
* @var ClassDoc[]|InterfaceDoc[]|TraitDoc[] $types
* @var yii\web\View $this
*/
?><h1>Class Reference</h1>
<table class="summaryTable docIndex table table-bordered table-striped table-hover">
<colgroup>
<col class="col-package" />
<col class="col-class" />
<col class="col-description" />
</colgroup>
<tr>
<th>Class</th>
<th>Description</th>
</tr>
<?php
ksort($types);
foreach($types as $i=>$class):
?>
<tr>
<td><?= $this->context->typeLink($class, $class->name) ?></td>
<td><?= \yii\apidoc\helpers\Markdown::process($class->shortDescription, $class) ?></td>
</tr>
<?php endforeach; ?>
</table>
......@@ -96,7 +96,7 @@ abstract class Renderer extends BaseRenderer implements ViewContextInterface
$this->context = $context;
$dir = Yii::getAlias($this->targetDir);
if (!is_dir($dir)) {
mkdir($dir);
mkdir($dir, 0777, true);
}
$types = array_merge($context->classes, $context->interfaces, $context->traits);
......@@ -107,6 +107,7 @@ abstract class Renderer extends BaseRenderer implements ViewContextInterface
$fileContent = $this->renderWithLayout($this->typeView, [
'type' => $type,
'docContext' => $context,
'types' => $types,
]);
file_put_contents($dir . '/' . $this->generateFileName($type->name), $fileContent);
Console::updateProgress(++$done, $typeCount);
......@@ -166,7 +167,7 @@ abstract class Renderer extends BaseRenderer implements ViewContextInterface
$links[] = Html::a(
$type->name,
null,
['href' => $this->generateFileName($type->name)]
['href' => $this->generateUrl($type->name)]
) . $postfix;
}
}
......@@ -191,7 +192,7 @@ abstract class Renderer extends BaseRenderer implements ViewContextInterface
if (($type = $this->context->getType($subject->definedBy)) === null) {
return $subject->name;
} else {
$link = $this->generateFileName($type->name);
$link = $this->generateUrl($type->name);
if ($subject instanceof MethodDoc) {
$link .= '#' . $subject->name . '()';
} else {
......@@ -336,6 +337,11 @@ abstract class Renderer extends BaseRenderer implements ViewContextInterface
. ' )';
}
public function generateUrl($typeName)
{
return $this->generateFileName($typeName);
}
protected function generateFileName($typeName)
{
return strtolower(str_replace('\\', '_', $typeName)) . '.html';
......
......@@ -15,7 +15,7 @@ if (empty($type->constants)) {
<p><a href="#" class="toggle">Hide inherited constants</a></p>
<table class="summaryTable">
<table class="summaryTable table table-striped table-bordered table-hover">
<colgroup>
<col class="col-const" />
<col class="col-description" />
......
......@@ -13,9 +13,9 @@ if (empty($events)) {
} ?>
<h2>Event Details</h2>
<?php foreach($events as $event): ?>
<div class="detailHeader" id="<?= $event->name.'-detail' ?>">
<div class="detailHeader h3" id="<?= $event->name.'-detail' ?>">
<?php echo $event->name; ?>
<span class="detailHeaderTag">
<span class="detailHeaderTag small">
event
<?php if(!empty($event->since)): ?>
(available since version <?= $event->since ?>)
......
......@@ -15,7 +15,7 @@ if (empty($type->events)) {
<p><a href="#" class="toggle">Hide inherited events</a></p>
<table class="summaryTable">
<table class="summaryTable table table-striped table-bordered table-hover">
<colgroup>
<col class="col-event" />
<col class="col-description" />
......
......@@ -16,9 +16,9 @@ if (empty($methods)) {
<?php foreach($methods as $method): ?>
<div class="detailHeader" id="<?= $method->name . '()-detail' ?>">
<div class="detailHeader h3" id="<?= $method->name . '()-detail' ?>">
<?= $method->name ?>()
<span class="detailHeaderTag">
<span class="detailHeaderTag small">
method
<?php if (!empty($method->since)): ?>
(available since version <?php echo $method->since; ?>)
......@@ -26,11 +26,9 @@ if (empty($methods)) {
</span>
</div>
<table class="summaryTable">
<table class="summaryTable table table-striped table-bordered table-hover">
<tr><td colspan="3">
<div class="signature2">
<?= $this->context->renderMethodSignature($method) ?>
</div>
<div class="signature2"><?= $this->context->renderMethodSignature($method) ?></div>
</td></tr>
<?php if(!empty($method->params) || !empty($method->return) || !empty($method->exceptions)): ?>
<?php foreach($method->params as $param): ?>
......
......@@ -19,7 +19,7 @@ if ($protected && count($type->getProtectedMethods()) == 0 || !$protected && cou
<p><a href="#" class="toggle">Hide inherited methods</a></p>
<table class="summaryTable">
<table class="summaryTable table table-striped table-bordered table-hover">
<colgroup>
<col class="col-method" />
<col class="col-description" />
......
......@@ -16,9 +16,9 @@ if (empty($properties)) {
<?php foreach($properties as $property): ?>
<div class="detailHeader" id="<?= $property->name.'-detail' ?>">
<div class="detailHeader h3" id="<?= $property->name.'-detail' ?>">
<?php echo $property->name; ?>
<span class="detailHeaderTag">
<span class="detailHeaderTag small">
property
<?php if($property->getIsReadOnly()) echo ' <em>read-only</em> '; ?>
<?php if($property->getIsWriteOnly()) echo ' <em>write-only</em> '; ?>
......@@ -28,9 +28,7 @@ if (empty($properties)) {
</span>
</div>
<div class="signature">
<?php echo $this->context->renderPropertySignature($property); ?>
</div>
<div class="signature"><?php echo $this->context->renderPropertySignature($property); ?></div>
<p><?= Markdown::process($property->description, $type) ?></p>
......
......@@ -18,7 +18,7 @@ if ($protected && count($type->getProtectedProperties()) == 0 || !$protected &&
<p><a href="#" class="toggle">Hide inherited properties</a></p>
<table class="summaryTable">
<table class="summaryTable table table-striped table-bordered table-hover">
<colgroup>
<col class="col-property" />
<col class="col-type" />
......
......@@ -44,7 +44,7 @@ $renderer = $this->context;
<?php endif; ?>
</div>
<table class="summaryTable docClass">
<table class="summaryTable docClass table table-bordered">
<colgroup>
<col class="col-name" />
<col class="col-value" />
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\templates\online;
use yii\apidoc\models\Context;
use yii\apidoc\models\TypeDoc;
use yii\console\Controller;
use Yii;
use yii\helpers\Console;
use yii\helpers\FileHelper;
use yii\helpers\StringHelper;
/**
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class Renderer extends \yii\apidoc\templates\html\Renderer
{
public $layout = false;
public $indexView = '@yii/apidoc/templates/online/views/index.php';
public $pageTitle = 'Yii Framework 2.0 API Documentation';
/**
* Renders a given [[Context]].
*
* @param Context $context the api documentation context to render.
* @param Controller $controller the apidoc controller instance. Can be used to control output.
*/
public function render($context, $controller)
{
parent::render($context, $controller);
$dir = Yii::getAlias($this->targetDir);
$controller->stdout("writing packages file...");
$packages = [];
$notNamespaced = [];
foreach(array_merge($context->classes, $context->interfaces, $context->traits) as $type) {
/** @var TypeDoc $type */
if (empty($type->namespace)) {
$notNamespaced[] = str_replace('\\', '-', $type->name);
} else {
$packages[$type->namespace][] = str_replace('\\', '-', $type->name);
}
}
ksort($packages);
$packages = array_merge(['Not namespaced' => $notNamespaced], $packages);
foreach($packages as $name => $classes) {
sort($packages[$name]);
}
file_put_contents($dir . '/packages.txt', serialize($packages));
$controller->stdout('done.' . PHP_EOL, Console::FG_GREEN);
}
protected function generateUrl($typeName)
{
return strtolower(str_replace('\\', '-', $typeName));
}
protected function generateFileName($typeName)
{
return $this->generateUrl($typeName) . '.html';
}
}
\ No newline at end of file
<?php
use yii\apidoc\models\ClassDoc;
use yii\apidoc\models\InterfaceDoc;
use yii\apidoc\models\TraitDoc;
/**
* @var ClassDoc[]|InterfaceDoc[]|TraitDoc[] $types
* @var yii\web\View $this
*/
?><h1>Class Reference</h1>
<table class="summaryTable docIndex">
<colgroup>
<col class="col-package" />
<col class="col-class" />
<col class="col-description" />
</colgroup>
<tr>
<th>Class</th>
<th>Description</th>
</tr>
<?php
ksort($types);
foreach($types as $i=>$class):
?>
<tr>
<td><?= $this->context->typeLink($class, $class->name) ?></td>
<td><?= \yii\apidoc\helpers\Markdown::process($class->shortDescription, $class) ?></td>
</tr>
<?php endforeach; ?>
</table>
......@@ -87,6 +87,7 @@ class ButtonDropdown extends Widget
'label' => '<span class="caret"></span>',
'encodeLabel' => false,
'options' => $this->options,
'view' => $this->getView(),
]);
} else {
$label .= ' <span class="caret"></span>';
......@@ -103,6 +104,7 @@ class ButtonDropdown extends Widget
'label' => $label,
'options' => $options,
'encodeLabel' => false,
'view' => $this->getView(),
]) . "\n" . $splitButton;
}
......@@ -114,6 +116,7 @@ class ButtonDropdown extends Widget
{
$config = $this->dropdown;
$config['clientOptions'] = false;
$config['view'] = $this->getView();
return Dropdown::widget($config);
}
}
......@@ -86,7 +86,8 @@ class ButtonGroup extends Widget
$buttons[] = Button::widget([
'label' => $label,
'options' => $options,
'encodeLabel' => $this->encodeLabels
'encodeLabel' => $this->encodeLabels,
'view' => $this->getView()
]);
} else {
$buttons[] = $button;
......
......@@ -171,6 +171,7 @@ class Nav extends Widget
'items' => $items,
'encodeLabels' => $this->encodeLabels,
'clientOptions' => false,
'view' => $this->getView(),
]);
}
}
......@@ -202,7 +203,7 @@ class Nav extends Widget
unset($item['url']['#']);
if (count($item['url']) > 1) {
foreach (array_splice($item['url'], 1) as $name => $value) {
if (!isset($this->params[$name]) || $this->params[$name] != $value) {
if ($value !== null && (!isset($this->params[$name]) || $this->params[$name] != $value)) {
return false;
}
}
......
......@@ -134,7 +134,7 @@ class Tabs extends Widget
}
$header = Html::a($label, "#", ['class' => 'dropdown-toggle', 'data-toggle' => 'dropdown']) . "\n"
. Dropdown::widget(['items' => $item['items'], 'clientOptions' => false]);
. Dropdown::widget(['items' => $item['items'], 'clientOptions' => false, 'view' => $this->getView()]);
} elseif (isset($item['content'])) {
$options = array_merge($this->itemOptions, ArrayHelper::getValue($item, 'options', []));
$options['id'] = ArrayHelper::getValue($options, 'id', $this->options['id'] . '-tab' . $n);
......
......@@ -10,7 +10,7 @@ namespace yii\debug;
use Yii;
use yii\base\Application;
use yii\web\View;
use yii\web\AccessDeniedHttpException;
use yii\web\ForbiddenHttpException;
/**
* The Yii Debug Module provides the debug toolbar and debugger
......@@ -80,7 +80,7 @@ class Module extends \yii\base\Module
} elseif ($action->id === 'toolbar') {
return false;
} else {
throw new AccessDeniedHttpException('You are not allowed to access this page.');
throw new ForbiddenHttpException('You are not allowed to access this page.');
}
}
......
......@@ -36,13 +36,15 @@ class RequestPanel extends Panel
public function save()
{
if (function_exists('apache_request_headers')) {
$requestHeaders = apache_request_headers();
} elseif (function_exists('http_get_request_headers')) {
$requestHeaders = http_get_request_headers();
} else {
$requestHeaders = [];
$headers = Yii::$app->getRequest()->getHeaders();
foreach ($headers as $name => $value) {
if (is_array($value) && count($value) == 1) {
$requestHeaders[$name] = current($value);
} else {
$requestHeaders[$name] = $value;
}
}
$responseHeaders = [];
foreach (headers_list() as $header) {
if (($pos = strpos($header, ':')) !== false) {
......
......@@ -39,6 +39,9 @@ use yii\db\ActiveQueryTrait;
* $customers = Customer::find()->with('orders')->asArray()->all();
* ~~~
*
* NOTE: elasticsearch limits the number of records returned to 10 records by default.
* If you expect to get more records you should specify limit explicitly.
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
......
......@@ -22,6 +22,11 @@ use yii\db\ActiveRelationTrait;
*
* If a relation involves a pivot table, it may be specified by [[via()]] method.
*
* NOTE: elasticsearch limits the number of records returned by any query to 10 records by default.
* If you expect to get more records you should specify limit explicitly in relation definition.
* This is also important for relations that use [[via()]] so that if via records are limited to 10
* the relations records can also not be more than 10.
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
......
......@@ -46,6 +46,9 @@ use yii\db\QueryTrait;
* - [[column()]]: returns the value of the first column in the query result.
* - [[exists()]]: returns a value indicating whether the query result has data or not.
*
* NOTE: elasticsearch limits the number of records returned to 10 records by default.
* If you expect to get more records you should specify limit explicitly.
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
......
......@@ -48,6 +48,13 @@ Using the Query
TBD
> **NOTE:** elasticsearch limits the number of records returned by any query to 10 records by default.
> If you expect to get more records you should specify limit explicitly in relation definition.
* This is also important for relations that use [[via()]] so that if via records are limited to 10
* the relations records can also not be more than 10.
*
Using the ActiveRecord
----------------------
......@@ -113,6 +120,12 @@ It supports the same interface and features except the following limitations and
on how to compose `query` and `filter` parts.
- It is also possible to define relations from elasticsearch ActiveRecords to normal ActiveRecord classes and vice versa.
> **NOTE:** elasticsearch limits the number of records returned by any query to 10 records by default.
> If you expect to get more records you should specify limit explicitly in query **and also** relation definition.
> This is also important for relations that use via() so that if via records are limited to 10
> the relations records can also not be more than 10.
Usage example:
```php
......
......@@ -8,7 +8,7 @@
namespace yii\gii;
use Yii;
use yii\web\AccessDeniedHttpException;
use yii\web\ForbiddenHttpException;
/**
* This is the main module class for the Gii module.
......@@ -110,7 +110,7 @@ class Module extends \yii\base\Module
if ($this->checkAccess()) {
return parent::beforeAction($action);
} else {
throw new AccessDeniedHttpException('You are not allowed to access this page.');
throw new ForbiddenHttpException('You are not allowed to access this page.');
}
}
......
......@@ -305,16 +305,22 @@ class Generator extends \yii\gii\Generator
*/
public function generateSearchLabels()
{
$model = new $this->modelClass();
$attributeLabels = $model->attributeLabels();
$labels = [];
foreach ($this->getColumnNames() as $name) {
if (!strcasecmp($name, 'id')) {
$labels[$name] = 'ID';
if (isset($attributeLabels[$name])) {
$labels[$name] = $attributeLabels[$name];
} else {
$label = Inflector::camel2words($name);
if (strcasecmp(substr($label, -3), ' id') === 0) {
$label = substr($label, 0, -3) . ' ID';
if (!strcasecmp($name, 'id')) {
$labels[$name] = 'ID';
} else {
$label = Inflector::camel2words($name);
if (strcasecmp(substr($label, -3), ' id') === 0) {
$label = substr($label, 0, -3) . ' ID';
}
$labels[$name] = $label;
}
$labels[$name] = $label;
}
}
return $labels;
......
......@@ -77,8 +77,16 @@ Yii Framework 2 Change Log
- Enh #1973: `yii message/extract` is now able to generate `.po` files (SergeiKutanov, samdark)
- Enh #1984: ActionFilter will now mark event as handled when action run is aborted (cebe)
- Enh #2003: Added `filter` property to `ExistValidator` and `UniqueValidator` to support adding additional filtering conditions (qiangxue)
- Enh #2008: `yii message/extract` is now able to save translation strings to database (kate-kate, samdark)
- Enh #2043: Added support for custom request body parsers (danschmidt5189, cebe)
- Enh #2051: Do not save null data into database when using RBAC (qiangxue)
- Enh #2079:
- i18n now falls back to `en` from `en-US` if message translation isn't found (samdark)
- View now falls back to `en` from `en-US` if file not found (samdark)
- Default `sourceLanguage` and `language` are now `en` (samdark)
- Enh #2101: Gii is now using model labels when generating search (thiagotalma)
- Enh #2103: Renamed AccessDeniedHttpException to ForbiddenHttpException, added new commonly used HTTP exception classes (danschmidt5189)
- Enh #2124: Added support for UNION ALL queries (Ivan Pomortsev, iworker)
- Enh: Added `favicon.ico` and `robots.txt` to default application templates (samdark)
- Enh: Added `Widget::autoIdPrefix` to support prefixing automatically generated widget IDs (qiangxue)
- Enh: Support for file aliases in console command 'message' (omnilight)
......@@ -91,6 +99,8 @@ Yii Framework 2 Change Log
- Enh #1839: Added support for getting file extension and basename from uploaded file (anfrantic)
- Enh: yii\codeception\TestCase now supports loading and using fixtures via Yii fixture framework (qiangxue)
- Enh: Added support to parse json request data to Request::getRestParams() (cebe)
- Enh: Added ability to get incoming headers (dizews)
- Enh: Added `beforeRun()` and `afterRun()` to `yii\base\Action` (qiangxue)
- Chg #1519: `yii\web\User::loginRequired()` now returns the `Response` object instead of exiting the application (qiangxue)
- Chg #1586: `QueryBuilder::buildLikeCondition()` will now escape special characters and use percentage characters by default (qiangxue)
- Chg #1610: `Html::activeCheckboxList()` and `Html::activeRadioList()` will submit an empty string if no checkbox/radio is selected (qiangxue)
......@@ -100,6 +110,8 @@ Yii Framework 2 Change Log
- Chg #1821: Changed default values for yii\db\Connection username and password to null (cebe)
- Chg #1844: `Response::sendFile()` and other file sending methods will not send the response (qiangxue)
- Chg #1852: DbConnection::tablePrefix default value now 'tbl_' (creocoder)
- Chg #1958: `beforeSubmit` in `yii.activeform` is now executed after validation and before form submission (6pblcb)
- Chg #2025: Removed ability to declare scopes in ActiveRecord (samdark)
- Chg #2057: AutoTimestamp attributes defaults changed from `create_time` and `update_time` to `created_at` and `updated_at` (creocoder)
- Chg #2063: Removed `yii\web\Request::acceptTypes` and renamed `yii\web\Request::acceptedContentTypes` to `acceptableContentTypes` (qiangxue)
- Chg: Renamed `yii\jui\Widget::clientEventsMap` to `clientEventMap` (qiangxue)
......@@ -113,6 +125,7 @@ Yii Framework 2 Change Log
`index-test.php` and `yii` files to point to the new location of `Yii.php` (qiangxue, cebe)
- Chg: Advanced app template: moved database connection DSN, login and password to `-local` config not to expose it to VCS (samdark)
- Chg: Renamed `yii\web\Request::acceptedLanguages` to `acceptableLanguages` (qiangxue)
- Chg: Removed implementation of `Arrayable` from `yii\Object` (qiangxue)
- New #66: [Auth client library](https://github.com/yiisoft/yii2-authclient) OpenId, OAuth1, OAuth2 clients (klimov-paul)
- New #1393: [Codeception testing framework integration](https://github.com/yiisoft/yii2-codeception) (Ragazzo)
- New #1438: [MongoDB integration](https://github.com/yiisoft/yii2-mongodb) ActiveRecord and Query (klimov-paul)
......
......@@ -127,6 +127,13 @@
var $form = $(this),
data = $form.data('yiiActiveForm');
if (data.validated) {
if (data.settings.beforeSubmit !== undefined) {
if (data.settings.beforeSubmit($form) == false) {
data.validated = false;
data.submitting = false;
return false;
}
}
// continue submitting the form since validation passes
return true;
}
......@@ -135,40 +142,36 @@
clearTimeout(data.settings.timer);
}
data.submitting = true;
if (!data.settings.beforeSubmit || data.settings.beforeSubmit($form)) {
validate($form, function (messages) {
var errors = [];
$.each(data.attributes, function () {
if (updateInput($form, this, messages)) {
errors.push(this.input);
}
});
updateSummary($form, messages);
if (errors.length) {
var top = $form.find(errors.join(',')).first().offset().top;
var wtop = $(window).scrollTop();
if (top < wtop || top > wtop + $(window).height) {
$(window).scrollTop(top);
}
} else {
data.validated = true;
var $button = data.submitObject || $form.find(':submit:first');
// TODO: if the submission is caused by "change" event, it will not work
if ($button.length) {
$button.click();
} else {
// no submit button in the form
$form.submit();
}
return;
validate($form, function (messages) {
var errors = [];
$.each(data.attributes, function () {
if (updateInput($form, this, messages)) {
errors.push(this.input);
}
data.submitting = false;
}, function () {
data.submitting = false;
});
} else {
updateSummary($form, messages);
if (errors.length) {
var top = $form.find(errors.join(',')).first().offset().top;
var wtop = $(window).scrollTop();
if (top < wtop || top > wtop + $(window).height) {
$(window).scrollTop(top);
}
} else {
data.validated = true;
var $button = data.submitObject || $form.find(':submit:first');
// TODO: if the submission is caused by "change" event, it will not work
if ($button.length) {
$button.click();
} else {
// no submit button in the form
$form.submit();
}
return;
}
data.submitting = false;
}
}, function () {
data.submitting = false;
});
return false;
},
......
......@@ -86,4 +86,23 @@ class Action extends Component
}
return call_user_func_array([$this, 'run'], $args);
}
/**
* This method is called right before `run()` is executed.
* You may override this method to do preparation work for the action run.
* If the method returns false, it will cancel the action.
* @return boolean whether to run the action.
*/
protected function beforeRun()
{
return true;
}
/**
* This method is called right after `run()` is executed.
* You may override this method to do post-processing work for the action run.
*/
protected function afterRun()
{
}
}
......@@ -11,7 +11,7 @@ namespace yii\base;
* ActionFilter provides a base implementation for action filters that can be added to a controller
* to handle the `beforeAction` event.
*
* Check implementation of [[AccessControl]], [[PageCache]] and [[HttpCache]] as examples on how to use it.
* Check implementation of [[\yii\web\AccessControl]], [[\yii\web\PageCache]] and [[\yii\web\HttpCache]] as examples on how to use it.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
......
......@@ -80,13 +80,13 @@ abstract class Application extends Module
* @var string the language that is meant to be used for end users.
* @see sourceLanguage
*/
public $language = 'en-US';
public $language = 'en';
/**
* @var string the language that the application is written in. This mainly refers to
* the language that the messages and view files are written in.
* @see language
*/
public $sourceLanguage = 'en-US';
public $sourceLanguage = 'en';
/**
* @var Controller the currently active controller instance
*/
......
......@@ -52,7 +52,7 @@ use yii\validators\Validator;
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Model extends Component implements IteratorAggregate, ArrayAccess
class Model extends Component implements IteratorAggregate, ArrayAccess, Arrayable
{
/**
* The name of the default scenario.
......
......@@ -17,7 +17,7 @@ use Yii;
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Object implements Arrayable
class Object
{
/**
* @return string the fully qualified name of this class.
......@@ -227,14 +227,4 @@ class Object implements Arrayable
{
return method_exists($this, $name);
}
/**
* Converts the object into an array.
* The default implementation will return all public property values as an array.
* @return array the array representation of the object
*/
public function toArray()
{
return Yii::getObjectVars($this);
}
}
......@@ -196,7 +196,7 @@ return [
'yii\validators\ValidationAsset' => YII_PATH . '/validators/ValidationAsset.php',
'yii\validators\Validator' => YII_PATH . '/validators/Validator.php',
'yii\web\AccessControl' => YII_PATH . '/web/AccessControl.php',
'yii\web\AccessDeniedHttpException' => YII_PATH . '/web/AccessDeniedHttpException.php',
'yii\web\ForbiddenHttpException' => YII_PATH . '/web/ForbiddenHttpException.php',
'yii\web\AccessRule' => YII_PATH . '/web/AccessRule.php',
'yii\web\Application' => YII_PATH . '/web/Application.php',
'yii\web\AssetBundle' => YII_PATH . '/web/AssetBundle.php',
......
......@@ -15,7 +15,7 @@ use yii\base\InvalidRouteException;
/**
* Application represents a console application.
*
* Application extends from [[yii\base\Application]] by providing functionalities that are
* Application extends from [[\yii\base\Application]] by providing functionalities that are
* specific to console requests. In particular, it deals with console requests
* through a command-based approach:
*
......
......@@ -15,9 +15,12 @@ use yii\helpers\FileHelper;
/**
* This command extracts messages to be translated from source files.
* The extracted messages are saved either as PHP message source files
* or ".po" files under the specified directory. Format depends on `format`
* setting in config file.
* The extracted messages can be saved the following depending on `format`
* setting in config file:
*
* - PHP message source files.
* - ".po" files.
* - Database.
*
* Usage:
* 1. Create a configuration file using the 'message/config' command:
......@@ -91,14 +94,16 @@ class MessageController extends Controller
if (!is_dir($config['sourcePath'])) {
throw new Exception("The source path {$config['sourcePath']} is not a valid directory.");
}
if (!is_dir($config['messagePath'])) {
throw new Exception("The message path {$config['messagePath']} is not a valid directory.");
if (in_array($config['format'], ['php', 'po'])) {
if (!is_dir($config['messagePath'])) {
throw new Exception("The message path {$config['messagePath']} is not a valid directory.");
}
}
if (empty($config['languages'])) {
throw new Exception("Languages cannot be empty.");
}
if (empty($config['format']) || !in_array($config['format'], ['php', 'po'])) {
throw new Exception('Format should be either "php" or "po".');
if (empty($config['format']) || !in_array($config['format'], ['php', 'po', 'db'])) {
throw new Exception('Format should be either "php", "po" or "db".');
}
$files = FileHelper::findFiles(realpath($config['sourcePath']), $config);
......@@ -107,24 +112,116 @@ class MessageController extends Controller
foreach ($files as $file) {
$messages = array_merge_recursive($messages, $this->extractMessages($file, $config['translator']));
}
if (in_array($config['format'], ['php', 'po'])) {
foreach ($config['languages'] as $language) {
$dir = $config['messagePath'] . DIRECTORY_SEPARATOR . $language;
if (!is_dir($dir)) {
@mkdir($dir);
}
foreach ($messages as $category => $msgs) {
$file = str_replace("\\", '/', "$dir/$category." . $config['format']);
$path = dirname($file);
if (!is_dir($path)) {
mkdir($path, 0755, true);
}
$msgs = array_values(array_unique($msgs));
$this->generateMessageFile($msgs, $file, $config['overwrite'], $config['removeUnused'], $config['sort'], $config['format']);
}
}
} elseif ($config['format'] === 'db') {
$db = \Yii::$app->getComponent(isset($config['db']) ? $config['db'] : 'db');
if (!$db instanceof \yii\db\Connection) {
throw new Exception('The "db" option must refer to a valid database application component.');
}
$sourceMessageTable = isset($config['sourceMessageTable']) ? $config['sourceMessageTable'] : '{{%source_message}}';
$this->saveMessagesToDb(
$messages,
$db,
$sourceMessageTable,
$config['removeUnused']
);
}
}
/**
* Saves messages to database
*
* @param array $messages
* @param \yii\db\Connection $db
* @param string $sourceMessageTable
* @param boolean $removeUnused
*/
protected function saveMessagesToDb($messages, $db, $sourceMessageTable, $removeUnused)
{
$q = new \yii\db\Query;
$current = [];
foreach ($q->select(['id', 'category', 'message'])->from($sourceMessageTable)->all() as $row) {
$current[$row['category']][$row['id']] = $row['message'];
}
$new = [];
$obsolete = [];
foreach ($config['languages'] as $language) {
$dir = $config['messagePath'] . DIRECTORY_SEPARATOR . $language;
if (!is_dir($dir)) {
@mkdir($dir);
foreach ($messages as $category => $msgs) {
$msgs = array_unique($msgs);
if (isset($current[$category])) {
$new[$category] = array_diff($msgs, $current[$category]);
$obsolete = array_diff($current[$category], $msgs);
} else {
$new[$category] = $msgs;
}
foreach ($messages as $category => $msgs) {
$file = str_replace("\\", '/', "$dir/$category." . $config['format']);
$path = dirname($file);
if (!is_dir($path)) {
mkdir($path, 0755, true);
}
foreach (array_diff(array_keys($current), array_keys($messages)) as $category) {
$obsolete += $current[$category];
}
if (!$removeUnused) {
foreach ($obsolete as $pk => $m) {
if (mb_substr($m, 0, 2) === '@@' && mb_substr($m, -2) === '@@') {
unset($obsolete[$pk]);
}
$msgs = array_values(array_unique($msgs));
$this->generateMessageFile($msgs, $file, $config['overwrite'], $config['removeUnused'], $config['sort'], $config['format']);
}
}
$obsolete = array_keys($obsolete);
echo "Inserting new messages...";
$savedFlag = false;
foreach ($new as $category => $msgs) {
foreach ($msgs as $m) {
$savedFlag = true;
$db->createCommand()
->insert($sourceMessageTable, ['category' => $category, 'message' => $m])->execute();
}
}
echo $savedFlag ? "saved.\n" : "nothing new...skipped.\n";
echo $removeUnused ? "Deleting obsoleted messages..." : "Updating obsoleted messages...";
if (empty($obsolete)) {
echo "nothing obsoleted...skipped.\n";
} else {
if ($removeUnused) {
$db->createCommand()
->delete($sourceMessageTable, ['in', 'id', $obsolete])->execute();
echo "deleted.\n";
} else {
$db->createCommand()
->update(
$sourceMessageTable,
['message' => new \yii\db\Expression("CONCAT('@@',message,'@@')")],
['in', 'id', $obsolete]
)->execute();
echo "updated.\n";
}
}
}
/**
* Extracts messages from a file
*
......@@ -221,20 +318,20 @@ class MessageController extends Controller
$fileName .= '.merged';
}
if ($format === 'po'){
$out_str = '';
$output = '';
foreach ($merged as $k => $v){
$k = preg_replace('/(\")|(\\\")/', "\\\"", $k);
$v = preg_replace('/(\")|(\\\")/', "\\\"", $v);
if (substr($v, 0, 2) === '@@' && substr($v, -2) === '@@') {
$out_str .= "#msgid \"$k\"\n";
$out_str .= "#msgstr \"$v\"\n";
$output .= "#msgid \"$k\"\n";
$output .= "#msgstr \"$v\"\n";
} else {
$out_str .= "msgid \"$k\"\n";
$out_str .= "msgstr \"$v\"\n";
$output .= "msgid \"$k\"\n";
$output .= "msgstr \"$v\"\n";
}
$out_str .= "\n";
$output .= "\n";
}
$merged = $out_str;
$merged = $output;
}
echo "translation merged.\n";
} else {
......
......@@ -60,7 +60,7 @@ class ActiveQuery extends Query implements ActiveQueryInterface
* Executes query and returns all results as an array.
* @param Connection $db the DB connection used to create the DB command.
* If null, the DB connection returned by [[modelClass]] will be used.
* @return array the query results. If the query results in nothing, an empty array will be returned.
* @return array|ActiveRecord[] the query results. If the query results in nothing, an empty array will be returned.
*/
public function all($db = null)
{
......
......@@ -31,32 +31,6 @@ trait ActiveQueryTrait
*/
public $asArray;
/**
* PHP magic method.
* This method allows calling static method defined in [[modelClass]] via this query object.
* It is mainly implemented for supporting the feature of scope.
*
* @param string $name the method name to be called
* @param array $params the parameters passed to the method
* @throws \yii\base\InvalidCallException
* @return mixed the method return result
*/
public function __call($name, $params)
{
if (method_exists($this->modelClass, $name)) {
$method = new \ReflectionMethod($this->modelClass, $name);
if (!$method->isStatic() || !$method->isPublic()) {
throw new InvalidCallException("The scope method \"{$this->modelClass}::$name()\" must be public and static.");
}
array_unshift($params, $this);
call_user_func_array([$this->modelClass, $name], $params);
return $this;
} else {
return parent::__call($name, $params);
}
}
/**
* Sets the [[asArray]] property.
* @param boolean $value whether to return the query results in terms of arrays instead of Active Records.
......@@ -175,7 +149,7 @@ trait ActiveQueryTrait
* Finds records corresponding to one or multiple relations and populates them into the primary models.
* @param array $with a list of relations that this query should be performed with. Please
* refer to [[with()]] for details about specifying this parameter.
* @param array $models the primary models (can be either AR instances or arrays)
* @param array|ActiveRecord[] $models the primary models (can be either AR instances or arrays)
*/
public function findWith($with, &$models)
{
......
......@@ -102,7 +102,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
* - an array of name-value pairs: query by a set of column values and return a single record matching all of them.
* - null: return a new [[ActiveQuery]] object for further query purpose.
*
* @return ActiveQuery|ActiveRecord|null When `$q` is null, a new [[ActiveQuery]] instance
* @return ActiveQuery|static|null When `$q` is null, a new [[ActiveQuery]] instance
* is returned; when `$q` is a scalar or an array, an ActiveRecord object matching it will be
* returned (null will be returned if there is no matching).
* @throws InvalidConfigException if the AR class does not have a primary key
......
......@@ -635,11 +635,12 @@ class Query extends Component implements QueryInterface
/**
* Appends a SQL statement using UNION operator.
* @param string|Query $sql the SQL statement to be appended using UNION
* @param bool $all TRUE if using UNION ALL and FALSE if using UNION
* @return static the query object itself
*/
public function union($sql)
public function union($sql, $all = false)
{
$this->union[] = $sql;
$this->union[] = [ 'query' => $sql, 'all' => $all ];
return $this;
}
......
......@@ -739,16 +739,23 @@ class QueryBuilder extends \yii\base\Object
if (empty($unions)) {
return '';
}
$result = '';
foreach ($unions as $i => $union) {
if ($union instanceof Query) {
$query = $union['query'];
if ($query instanceof Query) {
// save the original parameters so that we can restore them later to prevent from modifying the query object
$originalParams = $union->params;
$union->addParams($params);
list ($unions[$i], $params) = $this->build($union);
$union->params = $originalParams;
$originalParams = $query->params;
$query->addParams($params);
list ($unions[$i]['query'], $params) = $this->build($query);
$query->params = $originalParams;
}
$result .= 'UNION ' . ($union['all'] ? 'ALL ' : '') . '( ' . $unions[$i]['query'] . ' ) ';
}
return "UNION (\n" . implode("\n) UNION (\n", $unions) . "\n)";
return trim($result);
}
/**
......
......@@ -42,9 +42,9 @@ class BaseFileHelper
* The searching is based on the specified language code. In particular,
* a file with the same name will be looked for under the subdirectory
* whose name is the same as the language code. For example, given the file "path/to/view.php"
* and language code "zh_CN", the localized file will be looked for as
* "path/to/zh_CN/view.php". If the file is not found, the original file
* will be returned.
* and language code "zh-CN", the localized file will be looked for as
* "path/to/zh-CN/view.php". If the file is not found, it will try a fallback with just a language code that is
* "zh" i.e. "path/to/zh/view.php". If it is not found as well the original file will be returned.
*
* If the target and the source language codes are the same,
* the original file will be returned.
......@@ -69,7 +69,16 @@ class BaseFileHelper
return $file;
}
$desiredFile = dirname($file) . DIRECTORY_SEPARATOR . $language . DIRECTORY_SEPARATOR . basename($file);
return is_file($desiredFile) ? $desiredFile : $file;
if (is_file($desiredFile)) {
return $desiredFile;
} else {
$language = substr($language, 0, 2);
if ($language === $sourceLanguage) {
return $file;
}
$desiredFile = dirname($file) . DIRECTORY_SEPARATOR . $language . DIRECTORY_SEPARATOR . basename($file);
return is_file($desiredFile) ? $desiredFile : $file;
}
}
/**
......
......@@ -85,7 +85,7 @@ class BaseStringHelper
if ($pos !== false) {
return mb_substr($path, 0, $pos);
} else {
return $path;
return '';
}
}
......
......@@ -22,7 +22,7 @@ use yii\db\Query;
*
* ~~~
* CREATE TABLE tbl_source_message (
* id INTEGER PRIMARY KEY,
* id INTEGER PRIMARY KEY AUTO_INCREMENT,
* category VARCHAR(32),
* message TEXT
* );
......@@ -111,8 +111,9 @@ class DbMessageSource extends MessageSource
/**
* 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.
* If translation for specific locale code such as `en-US` isn't found it
* tries more generic `en`.
*
* @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
......@@ -146,13 +147,25 @@ class DbMessageSource extends MessageSource
*/
protected function loadMessagesFromDb($category, $language)
{
$query = new Query();
$messages = $query->select(['t1.message message', 't2.translation translation'])
$mainQuery = new Query();
$mainQuery->select(['t1.message message', 't2.translation translation'])
->from([$this->sourceMessageTable . ' t1', $this->messageTable . ' t2'])
->where('t1.id = t2.id AND t1.category = :category AND t2.language = :language')
->params([':category' => $category, ':language' => $language])
->createCommand($this->db)
->queryAll();
->params([':category' => $category, ':language' => $language]);
$fallbackLanguage = substr($language, 0, 2);
if ($fallbackLanguage != $language) {
$fallbackQuery = new Query();
$fallbackQuery->select(['t1.message message', 't2.translation translation'])
->from([$this->sourceMessageTable . ' t1', $this->messageTable . ' t2'])
->where('t1.id = t2.id AND t1.category = :category AND t2.language = :fallbackLanguage')
->andWhere('t2.id NOT IN (SELECT id FROM '.$this->messageTable.' WHERE language = :language)')
->params([':category' => $category, ':language' => $language, ':fallbackLanguage' => $fallbackLanguage]);
$mainQuery->union($fallbackQuery, true);
}
$messages = $mainQuery->createCommand($this->db)->queryAll();
return ArrayHelper::map($messages, 'message', 'translation');
}
}
......@@ -50,8 +50,9 @@ class GettextMessageSource extends MessageSource
/**
* 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.
* If translation for specific locale code such as `en-US` isn't found it
* tries more generic `en`.
*
* @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
......@@ -59,13 +60,59 @@ class GettextMessageSource extends MessageSource
*/
protected function loadMessages($category, $language)
{
$messageFile = $this->getMessageFilePath($category, $language);
$messages = $this->loadMessagesFromFile($messageFile);
$fallbackLanguage = substr($language, 0, 2);
if ($fallbackLanguage != $language) {
$fallbackMessageFile = $this->getMessageFilePath($category, $fallbackLanguage);
$fallbackMessages = $this->loadMessagesFromFile($fallbackMessageFile);
if ($messages === null && $fallbackMessages === null && $fallbackLanguage != $this->sourceLanguage) {
Yii::error("The message file for category '$category' does not exist: $messageFile Fallback file does not exist as well: $fallbackMessageFile", __METHOD__);
} else if (empty($messages)) {
return $fallbackMessages;
} else if (!empty($fallbackMessages)) {
foreach ($fallbackMessages as $key => $value) {
if (!empty($value) && empty($messages[$key])) {
$messages[$key] = $fallbackMessages[$key];
}
}
}
} else {
if ($messages === null) {
Yii::error("The message file for category '$category' does not exist: $messageFile", __METHOD__);
}
}
return (array)$messages;
}
/**
* Returns message file path for the specified language and category.
*
* @param string $category the message category
* @param string $language the target language
* @return string path to message file
*/
protected function getMessageFilePath($category, $language)
{
$messageFile = Yii::getAlias($this->basePath) . '/' . $language . '/' . $this->catalog;
if ($this->useMoFile) {
$messageFile .= static::MO_FILE_EXT;
} else {
$messageFile .= static::PO_FILE_EXT;
}
return $messageFile;
}
/**
* Loads the message translation for the specified language and category or returns null if file doesn't exist.
*
* @param $messageFile string path to message file
* @return array|null array of messages or null if file not found
*/
protected function loadMessagesFromFile($messageFile)
{
if (is_file($messageFile)) {
if ($this->useMoFile) {
$gettextFile = new GettextMoFile(['useBigEndian' => $this->useBigEndian]);
......@@ -78,8 +125,7 @@ class GettextMessageSource extends MessageSource
}
return $messages;
} else {
Yii::error("The message file for category '$category' does not exist: $messageFile", __METHOD__);
return [];
return null;
}
}
}
......@@ -54,14 +54,14 @@ class I18N extends Component
if (!isset($this->translations['yii'])) {
$this->translations['yii'] = [
'class' => 'yii\i18n\PhpMessageSource',
'sourceLanguage' => 'en-US',
'sourceLanguage' => 'en',
'basePath' => '@yii/messages',
];
}
if (!isset($this->translations['app'])) {
$this->translations['app'] = [
'class' => 'yii\i18n\PhpMessageSource',
'sourceLanguage' => 'en-US',
'sourceLanguage' => 'en',
'basePath' => '@app/messages',
];
}
......
......@@ -53,8 +53,9 @@ class MessageSource extends Component
/**
* 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.
* If translation for specific locale code such as `en-US` isn't found it
* tries more generic `en`.
*
* @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
......
......@@ -53,18 +53,69 @@ class PhpMessageSource extends MessageSource
/**
* Loads the message translation for the specified language and category.
* If translation for specific locale code such as `en-US` isn't found it
* tries more generic `en`.
*
* @param string $category the message category
* @param string $language the target language
* @return array the loaded messages
* @return array the loaded messages. The keys are original messages, and the values
* are translated messages.
*/
protected function loadMessages($category, $language)
{
$messageFile = $this->getMessageFilePath($category, $language);
$messages = $this->loadMessagesFromFile($messageFile);
$fallbackLanguage = substr($language, 0, 2);
if ($fallbackLanguage != $language) {
$fallbackMessageFile = $this->getMessageFilePath($category, $fallbackLanguage);
$fallbackMessages = $this->loadMessagesFromFile($fallbackMessageFile);
if ($messages === null && $fallbackMessages === null && $fallbackLanguage != $this->sourceLanguage) {
Yii::error("The message file for category '$category' does not exist: $messageFile Fallback file does not exist as well: $fallbackMessageFile", __METHOD__);
} else if (empty($messages)) {
return $fallbackMessages;
} else if (!empty($fallbackMessages)) {
foreach ($fallbackMessages as $key => $value) {
if (!empty($value) && empty($messages[$key])) {
$messages[$key] = $fallbackMessages[$key];
}
}
}
} else {
if ($messages === null) {
Yii::error("The message file for category '$category' does not exist: $messageFile", __METHOD__);
}
}
return (array)$messages;
}
/**
* Returns message file path for the specified language and category.
*
* @param string $category the message category
* @param string $language the target language
* @return string path to message file
*/
protected function getMessageFilePath($category, $language)
{
$messageFile = Yii::getAlias($this->basePath) . "/$language/";
if (isset($this->fileMap[$category])) {
$messageFile .= $this->fileMap[$category];
} else {
$messageFile .= str_replace('\\', '/', $category) . '.php';
}
return $messageFile;
}
/**
* Loads the message translation for the specified language and category or returns null if file doesn't exist.
*
* @param $messageFile string path to message file
* @return array|null array of messages or null if file not found
*/
protected function loadMessagesFromFile($messageFile)
{
if (is_file($messageFile)) {
$messages = include($messageFile);
if (!is_array($messages)) {
......@@ -72,8 +123,7 @@ class PhpMessageSource extends MessageSource
}
return $messages;
} else {
Yii::error("The message file for category '$category' does not exist: $messageFile", __METHOD__);
return [];
return null;
}
}
}
......@@ -7,7 +7,7 @@ return [
'messagePath' => __DIR__,
// array, required, list of language codes that the extracted messages
// should be translated to. For example, ['zh-CN', 'de'].
'languages' => ['ar', 'da', 'de', 'es', 'fa-IR', 'fr', 'it', 'ja', 'pl', 'pt-BR', 'pt-PT', 'ro', 'ru', 'sk', 'sr', 'sr-Latn', 'uk', 'zh-CN'],
'languages' => ['ar', 'da', 'de', 'el', 'es', 'fa-IR', 'fr', 'it', 'ja', 'pl', 'pt-BR', 'pt-PT', 'ro', 'ru', 'sk', 'sr', 'sr-Latn', 'uk', 'zh-CN'],
// string, the name of the function for translating messages.
// Defaults to 'Yii::t'. This is used as a mark to find the messages to be
// translated. You may use a string for single function name or an array for
......@@ -42,6 +42,8 @@ return [
'.hgkeep',
'/messages',
],
// Generated file format. Can be either "php" or "po".
// Generated file format. Can be either "php", "po" or "db".
'format' => 'php',
// Connection component ID for "db".
//'connectionID' => 'db',
];
<?php
/**
* Message translations.
*
* This file is automatically generated by 'yii message' command.
* It contains the localizable messages extracted from source code.
* You may modify this file by translating the extracted messages.
*
* Each array element represents the translation (value) of a message (key).
* If the value is empty, the message is considered as not translated.
* Messages that no longer need translation will have their translations
* enclosed between a pair of '@@' marks.
*
* Message string can be used with plural forms format. Check i18n section
* of the guide for details.
*
* NOTE: this file must be saved in UTF-8 encoding.
*/
return array (
'Home' => 'Αρχική',
'Invalid data received for parameter "{param}".' => 'Μη έγκυρα δεδομένα για την παράμετρο "{param}".',
'Login Required' => 'Απαιτείται είσοδος',
'Missing required arguments: {params}' => 'Απουσιάζουν απαραίτητες επιλογές: {params}',
'Missing required parameters: {params}' => 'Απουσιάζουν απαραίτητες παράμετροι: {params}',
'No help for unknown command "{command}".' => 'Δεν υπάρχει τεκμηρίωση για την εντολή "{command}".',
'No help for unknown sub-command "{command}".' => 'Δεν υπάρχει τεκμηρίωση για την υπό-εντολή "{command}".',
'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Παρουσιάζονται <b>{begin, number}-{end, number}</b> από <b>{totalCount, number}</b>.',
'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Συνολικά <b>{count, number}</b> {count, plural, one{αντικείμενο} few{αντικείμενα} many{αντικείμενα} other{αντικείμενα}}.',
'Unable to verify your data submission.' => 'Δεν ήταν δυνατή η επαλήθευση των απεσταλμένων δεδομένων.',
'Unknown command "{command}".' => 'Άγνωστη εντολή "{command}".',
'Unknown option: --{name}' => 'Άγνωστη επιλογή : --{name}',
'View' => 'Προβολή',
'the input value' => 'η τιμή εισόδου',
'(not set)' => '(μη ορισμένο)',
'An internal server error occurred.' => 'Σφάλμα διακομιστή.',
'Are you sure to delete this item?' => 'Είστε σίγουρος για τη διαγραφή;',
'Delete' => 'Διαγραφή',
'Error' => 'Σφάλμα',
'File upload failed.' => 'Η μεταφόρτωση απέτυχε.',
'Invalid Configuration' => 'Invalid Configuration',
'No' => 'Όχι',
'No results found.' => 'Δεν βρέθηκαν αποτελέσματα.',
'Not Supported' => 'Δεν υποστηρίζεται',
'Only files with these extensions are allowed: {extensions}.' => 'Επιτρέπονται αρχεία μόνο με καταλήξεις: {extensions}.',
'Only files with these mimeTypes are allowed: {mimeTypes}.' => 'Επιτρέπονται αρχεία μόνο με MIME τύπο: {mimeTypes}.',
'Page not found.' => 'Η σελίδα δεν βρέθηκε.',
'Please fix the following errors:' => 'Παρακαλώ διορθώστε τα παρακάτω σφάλματα:',
'Please upload a file.' => 'Παρακαλώ μεταφορτώστε ένα αρχείο.',
'The file "{file}" is not an image.' => 'Το αρχείο «{file}» δεν είναι εικόνα.',
'The file "{file}" is too big. Its size cannot exceed {limit, number} {limit, plural, one{byte} other{bytes}}.' => 'Το αρχείο «{file}» είναι πολύ μεγάλο . Το μέγεθος του δεν μπορεί να είναι πάνω από {limit, number} {limit, plural, one{byte} other{bytes}}.',
'The image "{file}" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Το αρχείο «{file}» είναι πολύ μεγάλο. Το ύψος δεν μπορεί να είναι μεγαλύτερο από {limit, number} {limit, plural, one{pixel} few{pixels} many{pixels} other{pixels}}.',
'The image "{file}" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Το αρχείο «{file}» είναι πολύ μεγάλο. Το πλάτος δεν μπορεί να είναι μεγαλύτερο από {limit, number} {limit, plural, one{pixel} few{pixels} many{pixels} other{pixels}}.',
'The file "{file}" is too small. Its size cannot be smaller than {limit, number} {limit, plural, one{byte} other{bytes}}.' => 'Το αρχείο «{file}» είναι πολύ μικρό. Το μέγεθος του δεν μπορεί να είναι μικρότερο από {limit, number} {limit, plural, one{byte} few{bytes} many{bytes} other{bytes}}.',
'The image "{file}" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Το αρχείο «{file}» είναι πολύ μικρό. To ύψος δεν μπορεί να είναι μικρότερο από {limit, number} {limit, plural, one{pixel} few{pixels} many{pixels} other{pixels}}.',
'The image "{file}" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Το αρχείο «{file}» είναι πολύ μικρό. Το πλάτος του δεν μπορεί να είναι μικρότερο από {limit, number} {limit, plural, one{pixel} few{pixels} many{pixels} other{pixels}}.',
'The format of {attribute} is invalid.' => 'Ο τύπος του «{attribute}» δεν είναι έγκυρος.',
'The verification code is incorrect.' => 'Ο κωδικός επαλήθευσης είναι εσφαλμένος.',
'Update' => 'Ενημέρωση',
'User Error' => 'Σφάλμα χρήστη',
'User Warning' => 'Προειδοποίηση χρήστη',
'Warning' => 'Προειδοποίηση',
'Yes' => 'Ναι',
'You are not allowed to perform this action.' => 'Δεν επιτρέπεται να εκτελέσετε αυτή τη δράση.',
'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Μπορείτε να μεταφορτώσετε το πολύ {limit, number} {limit, plural, one{αρχείο} few{αρχεία} many{αρχεία} other{αρχεία}}.',
'{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.' => 'Η διεύθυνση email «{attribute}» δεν είναι έγκυρη .',
'{attribute} must be "{requiredValue}".' => 'Το «{attribute}» πρέπει να είναι «{requiredValue}».',
'{attribute} must be a number.' => 'Το «{attribute}» πρέπει να είναι αριθμός.',
'{attribute} must be a string.' => 'Το «{attribute}» πρέπει να είναι συμβολοσειρά.',
'{attribute} must be an integer.' => 'Το «{attribute}» πρέπει να είναι ακέραιος.',
'{attribute} must be either "{true}" or "{false}".' => 'Το «{attribute}» πρέπει να είναι «{true}» ή «{false}».',
'{attribute} must be greater than "{compareValue}".' => 'Το «{attribute}» πρέπει να είναι μεγαλύτερο από «{compareValue}».',
'{attribute} must be greater than or equal to "{compareValue}".' => 'Το «{attribute}» πρέπει να είναι μεγαλύτερο ή ίσο με «{compareValue}».',
'{attribute} must be less than "{compareValue}".' => 'Το «{attribute}» πρέπει να είναι μικρότερο από «{compareValue}».',
'{attribute} must be less than or equal to "{compareValue}".' => 'Το «{attribute}» πρέπει να είναι μικρότερο ή ίσο με «{compareValue}».',
'{attribute} must be no greater than {max}.' => 'Το «{attribute}» πρέπει να μην ξεπερνά το {max}.',
'{attribute} must be no less than {min}.' => 'Το «{attribute}» πρέπει να μην είναι λιγότερο από {min}.',
'{attribute} must be repeated exactly.' => 'Το «{attribute}» πρέπει να επαναληφθεί ακριβώς.',
'{attribute} must not be equal to "{compareValue}".' => 'Το «{attribute}» πρέπει να μην είναι ίσο με «{compareValue}».',
'{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => 'Το «{attribute}» πρέπει να περιέχει το λιγότερο {min, number} {min, plural, one{χαρακτήρα} few{χαρακτήρες} many{χαρακτήρες} other{χαρακτήρες}}.',
'{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => 'Το «{attribute}» πρέπει να περιέχει το πολύ {max, number} {max, plural, one{χαρακτήρα} few{χαρακτήρες} many{χαρακτήρες} other{χαρακτήρες}}.',
'{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => 'Το «{attribute}» πρέπει να περιέχει {length, number} {length, plural, one{χαρακτήρα} few{χαρακτήρες} many{χαρακτήρες} other{χαρακτήρες}}.',
);
......@@ -17,12 +17,11 @@
* NOTE: this file must be saved in UTF-8 encoding.
*/
return array (
'View' => '',
'(not set)' => '(não definido)',
'An internal server error occurred.' => 'Ocorreu um erro interno do servidor.',
'Are you sure to delete this item?' => 'Tem certeza de que deseja excluir este item?',
'Delete' => 'Excluir',
'Error' => 'Error',
'Error' => 'Erro',
'File upload failed.' => 'O upload do arquivo falhou.',
'Home' => 'Página Inicial',
'Invalid data received for parameter "{param}".' => 'Dados inválidos recebidos para o parâmetro “{param}”.',
......@@ -33,29 +32,30 @@ return array (
'No help for unknown command "{command}".' => 'Não há ajuda para o comando desconhecido “{command}”.',
'No help for unknown sub-command "{command}".' => 'Não há ajuda para o sub-comando desconhecido “{command}”.',
'No results found.' => 'Nenhum resultado foi encontrado.',
'Only files with these extensions are allowed: {extensions}.' => 'Só são permitidos arquivos com as seguintes extensões: {extensions}.',
'Only files with these mimeTypes are allowed: {mimeTypes}.' => 'Só são permitidos arquivos com os seguintes mimeTypes: {mimeTypes}.',
'Only files with these extensions are allowed: {extensions}.' => 'São permitidos somente arquivos com as seguintes extensões: {extensions}.',
'Only files with these mimeTypes are allowed: {mimeTypes}.' => 'São permitidos somente arquivos com os seguintes mimeTypes: {mimeTypes}.',
'Page not found.' => 'Página não encontrada.',
'Please fix the following errors:' => 'Por favor, corrija os seguintes erros:',
'Please upload a file.' => 'Por favor suba um arquivo.',
'Please upload a file.' => 'Por favor, faça upload de um arquivo.',
'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Exibindo <b>{begin, number}-{end, number}</b> de <b>{totalCount, number}</b> {totalCount, plural, one{item} other{itens}}.',
'The file "{file}" is not an image.' => 'O arquivo “{file}” não é uma imagem.',
'The file "{file}" is too big. Its size cannot exceed {limit, number} {limit, plural, one{byte} other{bytes}}.' => 'O arquivo “{file}” é grande demais. Seu tamanho não pode exceder {limit, number} {limit, plural, one{byte} other{bytes}}.',
'The file "{file}" is too small. Its size cannot be smaller than {limit, number} {limit, plural, one{byte} other{bytes}}.' => 'O arquivo “{file}” é pequeno demais. Seu tamanho não pode ser menor do que {limit, number} {limit, plural, one{byte} other{bytes}}.',
'The file "{file}" is too small. Its size cannot be smaller than {limit, number} {limit, plural, one{byte} other{bytes}}.' => 'O arquivo “{file}” é pequeno demais. Seu tamanho não pode ser menor que {limit, number} {limit, plural, one{byte} other{bytes}}.',
'The format of {attribute} is invalid.' => 'O formato de “{attribute}” é inválido.',
'The image "{file}" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'O arquivo “{file}” é grande demais. A altura não pode ser maior do que {limit, number} {limit, plural, one{pixel} other{pixels}}.',
'The image "{file}" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'O arquivo “{file}” é grande demais. A largura não pode ser maior do que {limit, number} {limit, plural, one{pixel} other{pixels}}.',
'The image "{file}" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'O arquivo “{file}” é pequeno demais. A altura não pode ser menor do que {limit, number} {limit, plural, one{pixel} other{pixels}}.',
'The image "{file}" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'O arquivo “{file}” é pequeno demais. A largura não pode ser menor do que {limit, number} {limit, plural, one{pixel} other{pixels}}.',
'The image "{file}" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'O arquivo “{file}” é grande demais. A altura não pode ser maior que {limit, number} {limit, plural, one{pixel} other{pixels}}.',
'The image "{file}" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'O arquivo “{file}” é grande demais. A largura não pode ser maior que {limit, number} {limit, plural, one{pixel} other{pixels}}.',
'The image "{file}" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'O arquivo “{file}” é pequeno demais. A altura não pode ser menor que {limit, number} {limit, plural, one{pixel} other{pixels}}.',
'The image "{file}" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'O arquivo “{file}” é pequeno demais. A largura não pode ser menor que {limit, number} {limit, plural, one{pixel} other{pixels}}.',
'The verification code is incorrect.' => 'O código de verificação está incorreto.',
'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Total <b>{count, number}</b> {count, plural, one{item} other{itens}}.',
'Unable to verify your data submission.' => 'Não foi possível verificar a sua submissão de dados.',
'Unable to verify your data submission.' => 'Não foi possível verificar o seu envio de dados.',
'Unknown command "{command}".' => 'Comando desconhecido “{command}”.',
'Unknown option: --{name}' => 'Opção desconhecida : --{name}',
'Update' => 'Atualizar',
'Update' => 'Alterar',
'View' => 'Exibir',
'Yes' => 'Sim',
'You are not allowed to perform this action.' => 'Você não está autorizado a realizar essa ação.',
'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Você pode fazer o upload de no máximo {limit, number} {limit, plural, one{arquivo} other{arquivos}}.',
'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Você pode fazer o upload de, no máximo, {limit, number} {limit, plural, one{arquivo} other{arquivos}}.',
'the input value' => 'o valor de entrada',
'{attribute} "{value}" has already been taken.' => '{attribute} “{value}” já foi utilizado.',
'{attribute} cannot be blank.' => '“{attribute}” não pode ficar em branco.',
......@@ -64,15 +64,15 @@ return array (
'{attribute} is not a valid email address.' => '“{attribute}” não é um endereço de e-mail válido.',
'{attribute} must be "{requiredValue}".' => '“{attribute}” deve ser “{requiredValue}”.',
'{attribute} must be a number.' => '“{attribute}” deve ser um número.',
'{attribute} must be a string.' => '“{attribute}” deve ser uma string.',
'{attribute} must be a string.' => '“{attribute}” deve ser um texto.',
'{attribute} must be an integer.' => '“{attribute}” deve ser um número inteiro.',
'{attribute} must be either "{true}" or "{false}".' => '“{attribute}” deve ser “{true}” ou “{false}”.',
'{attribute} must be greater than "{compareValue}".' => '“{attribute}” deve ser maior do que “{compareValue}”.',
'{attribute} must be greater than "{compareValue}".' => '“{attribute}” deve ser maior que “{compareValue}”.',
'{attribute} must be greater than or equal to "{compareValue}".' => '“{attribute}” deve ser maior ou igual a “{compareValue}”.',
'{attribute} must be less than "{compareValue}".' => '“{attribute}” deve ser menor do que “{compareValue}”.',
'{attribute} must be less than "{compareValue}".' => '“{attribute}” deve ser menor que “{compareValue}”.',
'{attribute} must be less than or equal to "{compareValue}".' => '“{attribute}” deve ser menor ou igual a “{compareValue}”.',
'{attribute} must be no greater than {max}.' => '“{attribute}” não pode ser maior do que {max}.',
'{attribute} must be no less than {min}.' => '“{attribute}” não pode ser menor do que {min}.',
'{attribute} must be no greater than {max}.' => '“{attribute}” não pode ser maior que {max}.',
'{attribute} must be no less than {min}.' => '“{attribute}” não pode ser menor que {min}.',
'{attribute} must be repeated exactly.' => '“{attribute}” deve ser repetido exatamente.',
'{attribute} must not be equal to "{compareValue}".' => '“{attribute}” não deve ser igual a “{compareValue}”.',
'{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '“{attribute}” deve conter pelo menos {min, number} {min, plural, one{caractere} other{caracteres}}.',
......
......@@ -6,7 +6,7 @@
?>
<?php if (method_exists($this, 'beginPage')) $this->beginPage(); ?>
<!doctype html>
<html lang="en-us">
<html lang="en">
<head>
<meta charset="utf-8"/>
......
......@@ -42,6 +42,9 @@ return [
'.hgkeep',
'/messages',
],
// Generated file format. Can be either "php" or "po".
// Generated file format. Can be either "php", "po" or "db".
'format' => 'php',
// When format is "db", you may specify the following two options
//'db' => 'db',
//'sourceMessageTable' => '{{%source_message}}',
];
......@@ -130,14 +130,14 @@ class AccessControl extends ActionFilter
* The default implementation will redirect the user to the login page if he is a guest;
* if the user is already logged, a 403 HTTP exception will be thrown.
* @param User $user the current user
* @throws AccessDeniedHttpException if the user is already logged in.
* @throws ForbiddenHttpException if the user is already logged in.
*/
protected function denyAccess($user)
{
if ($user->getIsGuest()) {
$user->loginRequired();
} else {
throw new AccessDeniedHttpException(Yii::t('yii', 'You are not allowed to perform this action.'));
throw new ForbiddenHttpException(Yii::t('yii', 'You are not allowed to perform this action.'));
}
}
}
......@@ -10,6 +10,12 @@ namespace yii\web;
/**
* BadRequestHttpException represents a "Bad Request" HTTP exception with status code 400.
*
* Use this exception to represent a generic client error. In many cases, there
* may be an HTTP exception that more precisely describes the error. In that
* case, consider using the more precise exception to provide the user with
* additional information.
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
......
......@@ -8,12 +8,13 @@
namespace yii\web;
/**
* AccessDeniedHttpException represents an "Access Denied" HTTP exception with status code 403.
* ConflictHttpException represents a "Conflict" HTTP exception with status code 409
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.10
* @author Dan Schmidt <danschmidt5189@gmail.com>
* @since 2.0
*/
class AccessDeniedHttpException extends HttpException
class ConflictHttpException extends HttpException
{
/**
* Constructor.
......@@ -23,6 +24,6 @@ class AccessDeniedHttpException extends HttpException
*/
public function __construct($message = null, $code = 0, \Exception $previous = null)
{
parent::__construct(403, $message, $code, $previous);
parent::__construct(409, $message, $code, $previous);
}
}
......@@ -9,6 +9,7 @@ namespace yii\web;
use Yii;
use ArrayIterator;
use yii\base\Arrayable;
use yii\base\InvalidCallException;
use yii\base\Object;
......@@ -22,7 +23,7 @@ use yii\base\Object;
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class CookieCollection extends Object implements \IteratorAggregate, \ArrayAccess, \Countable
class CookieCollection extends Object implements \IteratorAggregate, \ArrayAccess, \Countable, Arrayable
{
/**
* @var boolean whether this collection is read only.
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\web;
/**
* ForbiddenHttpException represents a "Forbidden" HTTP exception with status code 403.
*
* Use this exception when a user has been authenticated but is not allowed to
* perform the requested action. If the user is not authenticated, consider
* using a 401 [[UnauthorizedHttpException]]. If you do not want to
* expose authorization information to the user, it is valid to respond with a
* 404 [[NotFoundHttpException]].
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.4
* @author Dan Schmidt <danschmidt5189@gmail.com>
* @since 2.0
*/
class ForbiddenHttpException extends HttpException
{
/**
* Constructor.
* @param string $message error message
* @param integer $code error code
* @param \Exception $previous The previous exception used for the exception chaining.
*/
public function __construct($message = null, $code = 0, \Exception $previous = null)
{
parent::__construct(403, $message, $code, $previous);
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\web;
/**
* GoneHttpException represents a "Gone" HTTP exception with status code 410
*
* Throw a GoneHttpException when a user requests a resource that no longer exists
* at the requested url. For example, after a record is deleted, future requests
* for that record should return a 410 GoneHttpException instead of a 404
* [[NotFoundHttpException]].
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.11
* @author Dan Schmidt <danschmidt5189@gmail.com>
* @since 2.0
*/
class GoneHttpException extends HttpException
{
/**
* Constructor.
* @param string $message error message
* @param integer $code error code
* @param \Exception $previous The previous exception used for the exception chaining.
*/
public function __construct($message = null, $code = 0, \Exception $previous = null)
{
parent::__construct(410, $message, $code, $previous);
}
}
......@@ -8,6 +8,7 @@
namespace yii\web;
use Yii;
use yii\base\Arrayable;
use yii\base\Object;
use ArrayIterator;
......@@ -21,7 +22,7 @@ use ArrayIterator;
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class HeaderCollection extends Object implements \IteratorAggregate, \ArrayAccess, \Countable
class HeaderCollection extends Object implements \IteratorAggregate, \ArrayAccess, \Countable, Arrayable
{
/**
* @var array the headers in this collection (indexed by the header names)
......
......@@ -11,7 +11,7 @@ use yii\base\InvalidParamException;
use yii\helpers\Json;
/**
* Parses a raw HTTP request using [[yii\helpers\Json::decode()]]
* Parses a raw HTTP request using [[\yii\helpers\Json::decode()]]
*
* @author Dan Schmidt <danschmidt5189@gmail.com>
* @since 2.0
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\web;
/**
* NotAcceptableHttpException represents a "Not Acceptable" HTTP exception with status code 406
*
* Use this exception when the client requests a Content-Type that your
* application cannot return. Note that, according to the HTTP 1.1 specification,
* you are not required to respond with this status code in this situation.
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7
* @author Dan Schmidt <danschmidt5189@gmail.com>
* @since 2.0
*/
class NotAcceptableHttpException extends HttpException
{
/**
* Constructor.
* @param string $message error message
* @param integer $code error code
* @param \Exception $previous The previous exception used for the exception chaining.
*/
public function __construct($message = null, $code = 0, \Exception $previous = null)
{
parent::__construct(406, $message, $code, $previous);
}
}
......@@ -151,6 +151,10 @@ class Request extends \yii\base\Request
private $_cookies;
/**
* @var array the headers in this collection (indexed by the header names)
*/
private $_headers;
/**
* Resolves the current request into a route and the associated parameters.
......@@ -170,6 +174,37 @@ class Request extends \yii\base\Request
}
/**
* Returns the header collection.
* The header collection contains incoming HTTP headers.
* @return HeaderCollection the header collection
*/
public function getHeaders()
{
if ($this->_headers === null) {
$this->_headers = new HeaderCollection;
$headers = [];
if (function_exists('getallheaders')) {
$headers = getallheaders();
} elseif (function_exists('http_get_request_headers')) {
$headers = http_get_request_headers();
} else {
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) == 'HTTP_') {
$name = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))));
$this->_headers->add($name, $value);
}
}
return $this->_headers;
}
foreach ($headers as $name => $value) {
$this->_headers->add($name, $value);
}
}
return $this->_headers;
}
/**
* Returns the method of the current request (e.g. GET, POST, HEAD, PUT, PATCH, DELETE).
* @return string request method, such as GET, POST, HEAD, PUT, PATCH, DELETE.
* The value returned is turned into upper case.
......
......@@ -8,6 +8,7 @@
namespace yii\web;
use Yii;
use yii\base\Arrayable;
use yii\base\Component;
use yii\base\InvalidConfigException;
use yii\base\InvalidParamException;
......@@ -71,7 +72,7 @@ use yii\base\InvalidParamException;
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Countable
class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Countable, Arrayable
{
/**
* @var boolean whether the session should be automatically started when the session component is initialized.
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\web;
/**
* TooManyRequestsHttpException represents a "Too Many Requests" HTTP exception with status code 429
*
* Use this exception to indicate that a client has made too many requests in a
* given period of time. For example, you would throw this exception when
* 'throttling' an API user.
*
* @link http://tools.ietf.org/search/rfc6585#section-4
* @author Dan Schmidt <danschmidt5189@gmail.com>
* @since 2.0
*/
class TooManyRequestsHttpException extends HttpException
{
/**
* Constructor.
* @param string $message error message
* @param integer $code error code
* @param \Exception $previous The previous exception used for the exception chaining.
*/
public function __construct($message = null, $code = 0, \Exception $previous = null)
{
parent::__construct(429, $message, $code, $previous);
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\web;
/**
* UnauthorizedHttpException represents an "Unauthorized" HTTP exception with status code 401
*
* Use this exception to indicate that a client needs to authenticate or login
* to perform the requested action. If the client is already authenticated and
* is simply not allowed to perform the action, consider using a 403
* [[ForbiddenHttpException]] or 404 [[NotFoundHttpException]] instead.
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2
* @author Dan Schmidt <danschmidt5189@gmail.com>
* @since 2.0
*/
class UnauthorizedHttpException extends HttpException
{
/**
* Constructor.
* @param string $message error message
* @param integer $code error code
* @param \Exception $previous The previous exception used for the exception chaining.
*/
public function __construct($message = null, $code = 0, \Exception $previous = null)
{
parent::__construct(401, $message, $code, $previous);
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\web;
/**
* UnsupportedMediaTypeHttpException represents an "Unsupported Media Type" HTTP exception with status code 415
*
* Use this exception when the client sends data in a format that your
* application does not understand. For example, you would throw this exception
* if the client POSTs XML data to an action or controller that only accepts
* JSON.
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.16
* @author Dan Schmidt <danschmidt5189@gmail.com>
* @since 2.0
*/
class UnsupportedMediaTypeHttpException extends HttpException
{
/**
* Constructor.
* @param string $message error message
* @param integer $code error code
* @param \Exception $previous The previous exception used for the exception chaining.
*/
public function __construct($message = null, $code = 0, \Exception $previous = null)
{
parent::__construct(415, $message, $code, $previous);
}
}
......@@ -323,7 +323,7 @@ class User extends Component
* Note that when [[loginUrl]] is set, calling this method will NOT terminate the application execution.
*
* @return Response the redirection response if [[loginUrl]] is set
* @throws AccessDeniedHttpException the "Access Denied" HTTP exception if [[loginUrl]] is not set
* @throws ForbiddenHttpException the "Access Denied" HTTP exception if [[loginUrl]] is not set
*/
public function loginRequired()
{
......@@ -334,7 +334,7 @@ class User extends Component
if ($this->loginUrl !== null) {
return Yii::$app->getResponse()->redirect($this->loginUrl);
} else {
throw new AccessDeniedHttpException(Yii::t('yii', 'Login Required'));
throw new ForbiddenHttpException(Yii::t('yii', 'Login Required'));
}
}
......
......@@ -193,8 +193,10 @@ abstract class BaseListView extends Widget
}
/** @var LinkPager $class */
$class = ArrayHelper::remove($this->pager, 'class', LinkPager::className());
$this->pager['pagination'] = $pagination;
return $class::widget($this->pager);
$pager = $this->pager;
$pager['pagination'] = $pagination;
$pager['view'] = $this->getView();
return $class::widget($pager);
}
/**
......@@ -209,7 +211,9 @@ abstract class BaseListView extends Widget
}
/** @var LinkSorter $class */
$class = ArrayHelper::remove($this->sorter, 'class', LinkSorter::className());
$this->sorter['sort'] = $sort;
return $class::widget($this->sorter);
$sorter = $this->sorter;
$sorter['sort'] = $sort;
$sorter['view'] = $this->getView();
return $class::widget($sorter);
}
}
......@@ -295,7 +295,7 @@ class Menu extends Widget
unset($item['url']['#']);
if (count($item['url']) > 1) {
foreach (array_splice($item['url'], 1) as $name => $value) {
if (!isset($this->params[$name]) || $this->params[$name] != $value) {
if ($value !== null && (!isset($this->params[$name]) || $this->params[$name] != $value)) {
return false;
}
}
......
<?php
namespace yiiunit\data\ar;
use yii\db\ActiveQuery;
use yiiunit\framework\db\ActiveRecordTest;
/**
......@@ -29,15 +30,15 @@ class Customer extends ActiveRecord
return $this->hasMany(Order::className(), ['customer_id' => 'id'])->orderBy('id');
}
public static function active($query)
{
$query->andWhere('status=1');
}
public function afterSave($insert)
{
ActiveRecordTest::$afterSaveInsert = $insert;
ActiveRecordTest::$afterSaveNewRecord = $this->isNewRecord;
parent::afterSave($insert);
}
public static function createQuery()
{
return new CustomerQuery(['modelClass' => get_called_class()]);
}
}
<?php
namespace yiiunit\data\ar;
use yii\db\ActiveQuery;
/**
* CustomerQuery
*/
class CustomerQuery extends ActiveQuery
{
public function active()
{
$this->andWhere('status=1');
return $this;
}
}
\ No newline at end of file
......@@ -35,11 +35,6 @@ class Customer extends ActiveRecord
return $this->hasMany(Order::className(), ['customer_id' => 'id'])->orderBy('created_at');
}
public static function active($query)
{
$query->andWhere(['status' => 1]);
}
public function afterSave($insert)
{
ActiveRecordTest::$afterSaveInsert = $insert;
......@@ -67,4 +62,9 @@ class Customer extends ActiveRecord
]);
}
public static function createQuery()
{
return new CustomerQuery(['modelClass' => get_called_class()]);
}
}
<?php
namespace yiiunit\data\ar\elasticsearch;
use yii\elasticsearch\ActiveQuery;
/**
* CustomerQuery
*/
class CustomerQuery extends ActiveQuery
{
public function active()
{
$this->andWhere(array('status' => 1));
return $this;
}
}
\ No newline at end of file
......@@ -2,6 +2,8 @@
namespace yiiunit\data\ar\mongodb;
use yii\mongodb\ActiveQuery;
class Customer extends ActiveRecord
{
public static function collectionName()
......@@ -20,13 +22,13 @@ class Customer extends ActiveRecord
];
}
public static function activeOnly($query)
public function getOrders()
{
$query->andWhere(['status' => 2]);
return $this->hasMany(CustomerOrder::className(), ['customer_id' => '_id']);
}
public function getOrders()
public static function createQuery()
{
return $this->hasMany(CustomerOrder::className(), ['customer_id' => '_id']);
return new CustomerQuery(['modelClass' => get_called_class()]);
}
}
\ No newline at end of file
<?php
namespace yiiunit\data\ar\mongodb;
use yii\mongodb\ActiveQuery;
/**
* CustomerQuery
*/
class CustomerQuery extends ActiveQuery
{
public function activeOnly()
{
$this->andWhere(['status' => 2]);
return $this;
}
}
\ No newline at end of file
......@@ -20,8 +20,8 @@ class CustomerFile extends ActiveRecord
);
}
public static function activeOnly($query)
public static function createQuery()
{
$query->andWhere(['status' => 2]);
return new CustomerFileQuery(['modelClass' => get_called_class()]);
}
}
\ No newline at end of file
<?php
namespace yiiunit\data\ar\mongodb\file;
use yii\mongodb\file\ActiveQuery;
/**
* CustomerFileQuery
*/
class CustomerFileQuery extends ActiveQuery
{
public function activeOnly()
{
$this->andWhere(['status' => 2]);
return $this;
}
}
\ No newline at end of file
......@@ -2,6 +2,7 @@
namespace yiiunit\data\ar\redis;
use yii\redis\ActiveQuery;
use yiiunit\extensions\redis\ActiveRecordTest;
class Customer extends ActiveRecord
......@@ -24,15 +25,15 @@ class Customer extends ActiveRecord
return $this->hasMany(Order::className(), ['customer_id' => 'id']);
}
public static function active($query)
{
$query->andWhere(['status' => 1]);
}
public function afterSave($insert)
{
ActiveRecordTest::$afterSaveInsert = $insert;
ActiveRecordTest::$afterSaveNewRecord = $this->isNewRecord;
parent::afterSave($insert);
}
public static function createQuery()
{
return new CustomerQuery(['modelClass' => get_called_class()]);
}
}
\ No newline at end of file
<?php
namespace yiiunit\data\ar\redis;
use yii\redis\ActiveQuery;
/**
* CustomerQuery
*/
class CustomerQuery extends ActiveQuery
{
public function active()
{
$this->andWhere(['status' => 1]);
return $this;
}
}
\ No newline at end of file
<?php
namespace yiiunit\data\ar\sphinx;
use yii\db\ActiveRelation;
use yii\sphinx\ActiveQuery;
class ArticleIndex extends ActiveRecord
{
......@@ -13,11 +12,6 @@ class ArticleIndex extends ActiveRecord
return 'yii2_test_article_index';
}
public static function favoriteAuthor($query)
{
$query->andWhere('author_id=1');
}
public function getSource()
{
return $this->hasOne(ArticleDb::className(), ['id' => 'id']);
......@@ -32,4 +26,9 @@ class ArticleIndex extends ActiveRecord
{
return $this->source->content;
}
public static function createQuery()
{
return new ArticleIndexQuery(['modelClass' => get_called_class()]);
}
}
\ No newline at end of file
<?php
namespace yiiunit\data\ar\sphinx;
use yii\sphinx\ActiveQuery;
/**
* ArticleIndexQuery
*/
class ArticleIndexQuery extends ActiveQuery
{
public function favoriteAuthor()
{
$this->andWhere('author_id=1');
return $this;
}
}
\ No newline at end of file
<?php
/**
*
*/
return [
'Hello world!' => 'Hallo Welt!',
];
\ 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