Commit f6cef5eb by Antonio Ramirez

Refactored based on comments and feedback (3rd round)

parents 9f832afb 9af5466b
...@@ -67,32 +67,32 @@ If you do not have [Composer](http://getcomposer.org/), you may download it from ...@@ -67,32 +67,32 @@ If you do not have [Composer](http://getcomposer.org/), you may download it from
curl -s http://getcomposer.org/installer | php curl -s http://getcomposer.org/installer | php
~~~ ~~~
You can then install the Bootstrap Application using the following command: You can then install the application using the following command:
~~~ ~~~
php composer.phar create-project --stability=dev yiisoft/yii2-app-advanced yii-advanced php composer.phar create-project --stability=dev yiisoft/yii2-app-advanced yii-advanced
~~~ ~~~
Now you should be able to access:
- the frontend using the URL `http://localhost/yii-advanced/frontend/www/`
- the backstage using the URL `http://localhost/yii-advanced/backstage/www/`
assuming `yii-advanced` is directly under the document root of your Web server.
### Install from an Archive File ### Install from an Archive File
This is not currently available. We will provide it when Yii 2 is formally released. This is not currently available. We will provide it when Yii 2 is formally released.
GETTING STARTED GETTING STARTED
--------------- ---------------
After template application and its dependencies are downloaded you need to initialize it and set some config values to After you install the application, you have to conduct the following steps to initialize
match your application requirements. the installed application. You only need to do these once for all.
1. Execute `install` command selecting `dev` as environment. 1. Execute the `init` command and select `dev` as environment.
2. Set `id` value in `console/config/main.php`, `frontend/config/main.php`, `backstage/config/main.php`. 2. Create a new database. It is assumed that MySQL InnoDB is used. If not, adjust `console/migrations/m130524_201442_init.php`.
3. Create new database. It is assumed that MySQL InnoDB is used. If not, adjust `console/migrations/m130524_201442_init.php`. 3. In `common/config/params.php` set your database details in `components.db` values.
4. In `common/config/params.php` set your database details in `components.db` values.
Now you should be able to access:
- the frontend using the URL `http://localhost/yii-advanced/frontend/www/`
- the backstage using the URL `http://localhost/yii-advanced/backstage/www/`
assuming `yii-advanced` is directly under the document root of your Web server.
...@@ -9,7 +9,7 @@ $params = array_merge( ...@@ -9,7 +9,7 @@ $params = array_merge(
); );
return array( return array(
'id' => 'change-me', 'id' => 'app-backend',
'basePath' => dirname(__DIR__), 'basePath' => dirname(__DIR__),
'vendorPath' => dirname(dirname(__DIR__)) . '/vendor', 'vendorPath' => dirname(dirname(__DIR__)) . '/vendor',
'preload' => array('log'), 'preload' => array('log'),
......
...@@ -36,9 +36,6 @@ ...@@ -36,9 +36,6 @@
"frontend/runtime", "frontend/runtime",
"frontend/www/assets" "frontend/www/assets"
],
"yii-install-executable": [
"yii"
] ]
} }
} }
...@@ -9,7 +9,7 @@ $params = array_merge( ...@@ -9,7 +9,7 @@ $params = array_merge(
); );
return array( return array(
'id' => 'change-me', 'id' => 'app-console',
'basePath' => dirname(__DIR__), 'basePath' => dirname(__DIR__),
'vendorPath' => dirname(dirname(__DIR__)) . '/vendor', 'vendorPath' => dirname(dirname(__DIR__)) . '/vendor',
'preload' => array('log'), 'preload' => array('log'),
......
<?php <?php
use yii\db\Schema;
class m130524_201442_init extends \yii\db\Migration class m130524_201442_init extends \yii\db\Migration
{ {
public function up() public function up()
......
...@@ -9,7 +9,7 @@ $params = array_merge( ...@@ -9,7 +9,7 @@ $params = array_merge(
); );
return array( return array(
'id' => 'change-me', 'id' => 'app-frontend',
'basePath' => dirname(__DIR__), 'basePath' => dirname(__DIR__),
'vendorPath' => dirname(dirname(__DIR__)) . '/vendor', 'vendorPath' => dirname(dirname(__DIR__)) . '/vendor',
'preload' => array('log'), 'preload' => array('log'),
......
...@@ -4,27 +4,27 @@ $root = str_replace('\\', '/', __DIR__); ...@@ -4,27 +4,27 @@ $root = str_replace('\\', '/', __DIR__);
$envs = require("$root/environments/index.php"); $envs = require("$root/environments/index.php");
$envNames = array_keys($envs); $envNames = array_keys($envs);
echo "Yii Application Installation Tool v1.0\n\n"; echo "Yii Application Init Tool v1.0\n\n";
echo "Which environment do you want to install the application to?\n\n"; echo "Which environment do you want the application to be initialized in?\n\n";
foreach ($envNames as $i => $name) { foreach ($envNames as $i => $name) {
echo " [$i] $name\n"; echo " [$i] $name\n";
} }
echo "\n Your choice [0-" . (count($envs) - 1) . ', or "q" to quit] '; echo "\n Your choice [0-" . (count($envs) - 1) . ', or "q" to quit] ';
$answer = trim(fgets(STDIN)); $answer = trim(fgets(STDIN));
if (!ctype_digit($answer) || !isset($envNames[$answer])) { if (!ctype_digit($answer) || !isset($envNames[$answer])) {
echo "\n Quit installation.\n"; echo "\n Quit initialization.\n";
return; return;
} }
$env = $envs[$envNames[$answer]]; $env = $envs[$envNames[$answer]];
echo "\n Install the application under '{$envNames[$answer]}' environment? [yes|no] "; echo "\n Initialize the application under '{$envNames[$answer]}' environment? [yes|no] ";
$answer = trim(fgets(STDIN)); $answer = trim(fgets(STDIN));
if (strncasecmp($answer, 'y', 1)) { if (strncasecmp($answer, 'y', 1)) {
echo "\n Quit installation.\n"; echo "\n Quit initialization.\n";
return; return;
} }
echo "\n Start installation ...\n\n"; echo "\n Start initialization ...\n\n";
$files = getFileList("$root/environments/{$env['path']}"); $files = getFileList("$root/environments/{$env['path']}");
$all = false; $all = false;
foreach ($files as $file) { foreach ($files as $file) {
...@@ -47,7 +47,7 @@ if (isset($env['executable'])) { ...@@ -47,7 +47,7 @@ if (isset($env['executable'])) {
} }
} }
echo "\n ... installation completed.\n\n"; echo "\n ... initialization completed.\n\n";
function getFileList($root, $basePath = '') function getFileList($root, $basePath = '')
{ {
......
...@@ -13,7 +13,7 @@ The most obvious change in Yii 2.0 is the use of namespaces. Almost every core c ...@@ -13,7 +13,7 @@ The most obvious change in Yii 2.0 is the use of namespaces. Almost every core c
is namespaced, e.g., `yii\web\Request`. The "C" prefix is no longer used in class names. is namespaced, e.g., `yii\web\Request`. The "C" prefix is no longer used in class names.
The naming of the namespaces follows the directory structure. For example, `yii\web\Request` The naming of the namespaces follows the directory structure. For example, `yii\web\Request`
indicates the corresponding class file is `web/Request.php` under the Yii framework folder. indicates the corresponding class file is `web/Request.php` under the Yii framework folder.
You can use any core class without explicitly include that class file, thanks to the Yii You can use any core class without explicitly including that class file, thanks to the Yii
class loader. class loader.
...@@ -117,17 +117,17 @@ supported in most places in the Yii core code. For example, `FileCache::cachePat ...@@ -117,17 +117,17 @@ supported in most places in the Yii core code. For example, `FileCache::cachePat
both a path alias and a normal directory path. both a path alias and a normal directory path.
Path alias is also closely related with class namespaces. It is recommended that a path Path alias is also closely related with class namespaces. It is recommended that a path
alias defined for each root namespace so that you can use Yii class autoloader without alias be defined for each root namespace so that you can use Yii the class autoloader without
any further configuration. For example, because `@yii` refers to the Yii installation directory, any further configuration. For example, because `@yii` refers to the Yii installation directory,
a class like `yii\web\Request` can be autoloaded by Yii. If you use a third party library a class like `yii\web\Request` can be autoloaded by Yii. If you use a third party library
such as Zend Framework, you may define a path alias `@Zend` which refers to its installation directory. such as Zend Framework, you may define a path alias `@Zend` which refers to its installation
And Yii will be able to autoload any class in this library. directory and Yii will be able to autoload any class in this library.
View View
---- ----
Yii 2.0 introduces a `View` class to represent the view part in the MVC pattern. Yii 2.0 introduces a `View` class to represent the view part of the MVC pattern.
It can be configured globally through the "view" application component. It is also It can be configured globally through the "view" application component. It is also
accessible in any view file via `$this`. This is one of the biggest changes compared to 1.1: accessible in any view file via `$this`. This is one of the biggest changes compared to 1.1:
**`$this` in a view file no longer refers to the controller or widget object.** **`$this` in a view file no longer refers to the controller or widget object.**
...@@ -159,7 +159,7 @@ extension for your Smarty views, or `twig` for Twig views. You may also configur ...@@ -159,7 +159,7 @@ extension for your Smarty views, or `twig` for Twig views. You may also configur
Models Models
------ ------
A model is now associated with a form name returned its `formName()` method. This is A model is now associated with a form name returned by its `formName()` method. This is
mainly used when using HTML forms to collect user inputs for a model. Previously in 1.1, mainly used when using HTML forms to collect user inputs for a model. Previously in 1.1,
this is usually hardcoded as the class name of the model. this is usually hardcoded as the class name of the model.
...@@ -235,7 +235,7 @@ Previously in 1.1, you would have to enter the widget class names as strings via ...@@ -235,7 +235,7 @@ Previously in 1.1, you would have to enter the widget class names as strings via
Themes Themes
------ ------
Theme works completely different in 2.0. It is now based on a path map to "translate" a source Themes work completely different in 2.0. They are now based on a path map to "translate" a source
view into a themed view. For example, if the path map for a theme is view into a themed view. For example, if the path map for a theme is
`array('/www/views' => '/www/themes/basic')`, then the themed version for a view file `array('/www/views' => '/www/themes/basic')`, then the themed version for a view file
`/www/views/site/index.php` will be `/www/themes/basic/site/index.php`. `/www/views/site/index.php` will be `/www/themes/basic/site/index.php`.
...@@ -250,7 +250,7 @@ application component. ...@@ -250,7 +250,7 @@ application component.
Console Applications Console Applications
-------------------- --------------------
Console applications are now composed by controllers, too, like Web applications. In fact, Console applications are now composed by controllers, like Web applications. In fact,
console controllers and Web controllers share the same base controller class. console controllers and Web controllers share the same base controller class.
Each console controller is like `CConsoleCommand` in 1.1. It consists of one or several Each console controller is like `CConsoleCommand` in 1.1. It consists of one or several
...@@ -300,7 +300,7 @@ public function behaviors() ...@@ -300,7 +300,7 @@ public function behaviors()
Assets Assets
------ ------
Yii 2.0 introduces a new concept called *asset bundle*. It is a bit similar to script Yii 2.0 introduces a new concept called *asset bundle*. It is similar to script
packages (managed by `CClientScript`) in 1.1, but with better support. packages (managed by `CClientScript`) in 1.1, but with better support.
An asset bundle is a collection of asset files (e.g. JavaScript files, CSS files, image files, etc.) An asset bundle is a collection of asset files (e.g. JavaScript files, CSS files, image files, etc.)
...@@ -315,7 +315,7 @@ Static Helpers ...@@ -315,7 +315,7 @@ Static Helpers
Yii 2.0 introduces many commonly used static helper classes, such as `Html`, `ArrayHelper`, Yii 2.0 introduces many commonly used static helper classes, such as `Html`, `ArrayHelper`,
`StringHelper`. These classes are designed to be easily extended. Note that static classes `StringHelper`. These classes are designed to be easily extended. Note that static classes
are usually hard to be extended because of the fixed class name references. But Yii 2.0 are usually hard to extend because of the fixed class name references. But Yii 2.0
introduces the class map (via `Yii::$classMap`) to overcome this difficulty. introduces the class map (via `Yii::$classMap`) to overcome this difficulty.
...@@ -343,7 +343,7 @@ Query Builder ...@@ -343,7 +343,7 @@ Query Builder
In 1.1, query building is scattered among several classes, including `CDbCommand`, In 1.1, query building is scattered among several classes, including `CDbCommand`,
`CDbCriteria`, and `CDbCommandBuilder`. Yii 2.0 uses `Query` to represent a DB query `CDbCriteria`, and `CDbCommandBuilder`. Yii 2.0 uses `Query` to represent a DB query
and `QueryBuilder` to generate SQL statements from query objects. For example, and `QueryBuilder` to generate SQL statements from query objects. For example:
```php ```php
$query = new \yii\db\Query; $query = new \yii\db\Query;
...@@ -365,7 +365,7 @@ ActiveRecord ...@@ -365,7 +365,7 @@ ActiveRecord
------------ ------------
ActiveRecord has undergone significant changes in Yii 2.0. The most important one ActiveRecord has undergone significant changes in Yii 2.0. The most important one
is about relational ActiveRecord query. In 1.1, you have to declare the relations is the relational ActiveRecord query. In 1.1, you have to declare the relations
in the `relations()` method. In 2.0, this is done via getter methods that return in the `relations()` method. In 2.0, this is done via getter methods that return
an `ActiveQuery` object. For example, the following method declares an "orders" relation: an `ActiveQuery` object. For example, the following method declares an "orders" relation:
...@@ -392,7 +392,7 @@ by filtering with the primary keys of the primary records. ...@@ -392,7 +392,7 @@ by filtering with the primary keys of the primary records.
Yii 2.0 no longer uses the `model()` method when performing queries. Instead, you Yii 2.0 no longer uses the `model()` method when performing queries. Instead, you
use the `find()` method like the following: use the `find()` method:
```php ```php
// to retrieve all *active* customers and order them by their ID: // to retrieve all *active* customers and order them by their ID:
...@@ -410,14 +410,14 @@ Therefore, you can use all query methods of `Query`. ...@@ -410,14 +410,14 @@ Therefore, you can use all query methods of `Query`.
Instead of returning ActiveRecord objects, you may call `ActiveQuery::asArray()` to Instead of returning ActiveRecord objects, you may call `ActiveQuery::asArray()` to
return results in terms of arrays. This is more efficient and is especially useful return results in terms of arrays. This is more efficient and is especially useful
when you need to return large number of records. For example, when you need to return a large number of records:
```php ```php
$customers = Customer::find()->asArray()->all(); $customers = Customer::find()->asArray()->all();
``` ```
By default, ActiveRecord now only saves dirty attributes. In 1.1, all attributes By default, ActiveRecord now only saves dirty attributes. In 1.1, all attributes
would be saved to database when you call `save()`, regardless they are changed or not, are saved to database when you call `save()`, regardless of having changed or not,
unless you explicitly list the attributes to save. unless you explicitly list the attributes to save.
...@@ -427,7 +427,7 @@ Auto-quoting Table and Column Names ...@@ -427,7 +427,7 @@ Auto-quoting Table and Column Names
Yii 2.0 supports automatic quoting of database table and column names. A name enclosed Yii 2.0 supports automatic quoting of database table and column names. A name enclosed
within double curly brackets is treated as a table name, and a name enclosed within within double curly brackets is treated as a table name, and a name enclosed within
double square brackets is treated as a column name. They will be quoted according to double square brackets is treated as a column name. They will be quoted according to
the database driver being used. For example, the database driver being used:
```php ```php
$command = $connection->createCommand('SELECT [[id]] FROM {{posts}}'); $command = $connection->createCommand('SELECT [[id]] FROM {{posts}}');
......
...@@ -48,4 +48,11 @@ return array( ...@@ -48,4 +48,11 @@ return array(
YII_DEBUG ? 'punycode/punycode.js' : 'punycode/punycode.min.js', YII_DEBUG ? 'punycode/punycode.js' : 'punycode/punycode.min.js',
), ),
), ),
'yii/maskedinput' => array(
'sourcePath' => __DIR__ . '/assets',
'js' => array(
'jquery.maskedinput.js',
),
'depends' => array('yii/jquery'),
),
); );
/*
Masked Input plugin for jQuery
Copyright (c) 2007-2013 Josh Bush (digitalbush.com)
Licensed under the MIT license (http://digitalbush.com/projects/masked-input-plugin/#license)
Version: 1.3.1
*/
(function($) {
function getPasteEvent() {
var el = document.createElement('input'),
name = 'onpaste';
el.setAttribute(name, '');
return (typeof el[name] === 'function')?'paste':'input';
}
var pasteEventName = getPasteEvent() + ".mask",
ua = navigator.userAgent,
iPhone = /iphone/i.test(ua),
android=/android/i.test(ua),
caretTimeoutId;
$.mask = {
//Predefined character definitions
definitions: {
'9': "[0-9]",
'a': "[A-Za-z]",
'*': "[A-Za-z0-9]"
},
dataName: "rawMaskFn",
placeholder: '_',
};
$.fn.extend({
//Helper Function for Caret positioning
caret: function(begin, end) {
var range;
if (this.length === 0 || this.is(":hidden")) {
return;
}
if (typeof begin == 'number') {
end = (typeof end === 'number') ? end : begin;
return this.each(function() {
if (this.setSelectionRange) {
this.setSelectionRange(begin, end);
} else if (this.createTextRange) {
range = this.createTextRange();
range.collapse(true);
range.moveEnd('character', end);
range.moveStart('character', begin);
range.select();
}
});
} else {
if (this[0].setSelectionRange) {
begin = this[0].selectionStart;
end = this[0].selectionEnd;
} else if (document.selection && document.selection.createRange) {
range = document.selection.createRange();
begin = 0 - range.duplicate().moveStart('character', -100000);
end = begin + range.text.length;
}
return { begin: begin, end: end };
}
},
unmask: function() {
return this.trigger("unmask");
},
mask: function(mask, settings) {
var input,
defs,
tests,
partialPosition,
firstNonMaskPos,
len;
if (!mask && this.length > 0) {
input = $(this[0]);
return input.data($.mask.dataName)();
}
settings = $.extend({
placeholder: $.mask.placeholder, // Load default placeholder
completed: null
}, settings);
defs = $.mask.definitions;
tests = [];
partialPosition = len = mask.length;
firstNonMaskPos = null;
$.each(mask.split(""), function(i, c) {
if (c == '?') {
len--;
partialPosition = i;
} else if (defs[c]) {
tests.push(new RegExp(defs[c]));
if (firstNonMaskPos === null) {
firstNonMaskPos = tests.length - 1;
}
} else {
tests.push(null);
}
});
return this.trigger("unmask").each(function() {
var input = $(this),
buffer = $.map(
mask.split(""),
function(c, i) {
if (c != '?') {
return defs[c] ? settings.placeholder : c;
}
}),
focusText = input.val();
function seekNext(pos) {
while (++pos < len && !tests[pos]);
return pos;
}
function seekPrev(pos) {
while (--pos >= 0 && !tests[pos]);
return pos;
}
function shiftL(begin,end) {
var i,
j;
if (begin<0) {
return;
}
for (i = begin, j = seekNext(end); i < len; i++) {
if (tests[i]) {
if (j < len && tests[i].test(buffer[j])) {
buffer[i] = buffer[j];
buffer[j] = settings.placeholder;
} else {
break;
}
j = seekNext(j);
}
}
writeBuffer();
input.caret(Math.max(firstNonMaskPos, begin));
}
function shiftR(pos) {
var i,
c,
j,
t;
for (i = pos, c = settings.placeholder; i < len; i++) {
if (tests[i]) {
j = seekNext(i);
t = buffer[i];
buffer[i] = c;
if (j < len && tests[j].test(t)) {
c = t;
} else {
break;
}
}
}
}
function keydownEvent(e) {
var k = e.which,
pos,
begin,
end;
//backspace, delete, and escape get special treatment
if (k === 8 || k === 46 || (iPhone && k === 127)) {
pos = input.caret();
begin = pos.begin;
end = pos.end;
if (end - begin === 0) {
begin=k!==46?seekPrev(begin):(end=seekNext(begin-1));
end=k===46?seekNext(end):end;
}
clearBuffer(begin, end);
shiftL(begin, end - 1);
e.preventDefault();
} else if (k == 27) {//escape
input.val(focusText);
input.caret(0, checkVal());
e.preventDefault();
}
}
function keypressEvent(e) {
var k = e.which,
pos = input.caret(),
p,
c,
next;
if (e.ctrlKey || e.altKey || e.metaKey || k < 32) {//Ignore
return;
} else if (k) {
if (pos.end - pos.begin !== 0){
clearBuffer(pos.begin, pos.end);
shiftL(pos.begin, pos.end-1);
}
p = seekNext(pos.begin - 1);
if (p < len) {
c = String.fromCharCode(k);
if (tests[p].test(c)) {
shiftR(p);
buffer[p] = c;
writeBuffer();
next = seekNext(p);
if(android){
setTimeout($.proxy($.fn.caret,input,next),0);
}else{
input.caret(next);
}
if (settings.completed && next >= len) {
settings.completed.call(input);
}
}
}
e.preventDefault();
}
}
function clearBuffer(start, end) {
var i;
for (i = start; i < end && i < len; i++) {
if (tests[i]) {
buffer[i] = settings.placeholder;
}
}
}
function writeBuffer() { input.val(buffer.join('')); }
function checkVal(allow) {
//try to place characters where they belong
var test = input.val(),
lastMatch = -1,
i,
c;
for (i = 0, pos = 0; i < len; i++) {
if (tests[i]) {
buffer[i] = settings.placeholder;
while (pos++ < test.length) {
c = test.charAt(pos - 1);
if (tests[i].test(c)) {
buffer[i] = c;
lastMatch = i;
break;
}
}
if (pos > test.length) {
break;
}
} else if (buffer[i] === test.charAt(pos) && i !== partialPosition) {
pos++;
lastMatch = i;
}
}
if (allow) {
writeBuffer();
} else if (lastMatch + 1 < partialPosition) {
input.val("");
clearBuffer(0, len);
} else {
writeBuffer();
input.val(input.val().substring(0, lastMatch + 1));
}
return (partialPosition ? i : firstNonMaskPos);
}
input.data($.mask.dataName,function(){
return $.map(buffer, function(c, i) {
return tests[i]&&c!=settings.placeholder ? c : null;
}).join('');
});
if (!input.attr("readonly"))
input
.one("unmask", function() {
input
.unbind(".mask")
.removeData($.mask.dataName);
})
.bind("focus.mask", function() {
clearTimeout(caretTimeoutId);
var pos,
moveCaret;
focusText = input.val();
pos = checkVal();
caretTimeoutId = setTimeout(function(){
writeBuffer();
if (pos == mask.length) {
input.caret(0, pos);
} else {
input.caret(pos);
}
}, 10);
})
.bind("blur.mask", function() {
checkVal();
if (input.val() != focusText)
input.change();
})
.bind("keydown.mask", keydownEvent)
.bind("keypress.mask", keypressEvent)
.bind(pasteEventName, function() {
setTimeout(function() {
var pos=checkVal(true);
input.caret(pos);
if (settings.completed && pos == input.val().length)
settings.completed.call(input);
}, 0);
});
checkVal(); //Perform initial check for existing values
});
}
});
})(jQuery);
\ No newline at end of file
...@@ -30,8 +30,8 @@ class ActionFilter extends Behavior ...@@ -30,8 +30,8 @@ class ActionFilter extends Behavior
public function events() public function events()
{ {
return array( return array(
'beforeAction' => 'beforeFilter', Controller::EVENT_BEFORE_ACTION => 'beforeFilter',
'afterAction' => 'afterFilter', Controller::EVENT_AFTER_ACTION => 'afterFilter',
); );
} }
......
...@@ -279,6 +279,15 @@ class Application extends Module ...@@ -279,6 +279,15 @@ class Application extends Module
} }
/** /**
* Returns the formatter component.
* @return \yii\base\Formatter the formatter application component.
*/
public function getFormatter()
{
return $this->getComponent('formatter');
}
/**
* Returns the request component. * Returns the request component.
* @return \yii\web\Request|\yii\console\Request the request component * @return \yii\web\Request|\yii\console\Request the request component
*/ */
...@@ -333,6 +342,9 @@ class Application extends Module ...@@ -333,6 +342,9 @@ class Application extends Module
'errorHandler' => array( 'errorHandler' => array(
'class' => 'yii\base\ErrorHandler', 'class' => 'yii\base\ErrorHandler',
), ),
'formatter' => array(
'class' => 'yii\base\Formatter',
),
'i18n' => array( 'i18n' => array(
'class' => 'yii\i18n\I18N', 'class' => 'yii\i18n\I18N',
), ),
......
...@@ -151,43 +151,13 @@ class Controller extends Component ...@@ -151,43 +151,13 @@ class Controller extends Component
/** /**
* Binds the parameters to the action. * Binds the parameters to the action.
* This method is invoked by [[Action]] when it begins to run with the given parameters. * This method is invoked by [[Action]] when it begins to run with the given parameters.
* This method will check the parameter names that the action requires and return
* the provided parameters according to the requirement. If there is any missing parameter,
* an exception will be thrown.
* @param Action $action the action to be bound with parameters * @param Action $action the action to be bound with parameters
* @param array $params the parameters to be bound to the action * @param array $params the parameters to be bound to the action
* @return array the valid parameters that the action can run with. * @return array the valid parameters that the action can run with.
* @throws InvalidRequestException if there are missing parameters.
*/ */
public function bindActionParams($action, $params) public function bindActionParams($action, $params)
{ {
if ($action instanceof InlineAction) { return array();
$method = new \ReflectionMethod($this, $action->actionMethod);
} else {
$method = new \ReflectionMethod($action, 'run');
}
$args = array();
$missing = array();
foreach ($method->getParameters() as $param) {
$name = $param->getName();
if (array_key_exists($name, $params)) {
$args[] = $params[$name];
unset($params[$name]);
} elseif ($param->isDefaultValueAvailable()) {
$args[] = $param->getDefaultValue();
} else {
$missing[] = $name;
}
}
if (!empty($missing)) {
throw new InvalidRequestException(Yii::t('yii', 'Missing required parameters: {params}', array(
'{params}' => implode(', ', $missing),
)));
}
return $args;
} }
/** /**
...@@ -272,18 +242,6 @@ class Controller extends Component ...@@ -272,18 +242,6 @@ class Controller extends Component
} }
/** /**
* Validates the parameter being bound to actions.
* This method is invoked when parameters are being bound to the currently requested action.
* Child classes may override this method to throw exceptions when there are missing and/or unknown parameters.
* @param Action $action the currently requested action
* @param array $missingParams the names of the missing parameters
* @param array $unknownParams the unknown parameters (name => value)
*/
public function validateActionParams($action, $missingParams, $unknownParams)
{
}
/**
* @return string the controller ID that is prefixed with the module ID (if any). * @return string the controller ID that is prefixed with the module ID (if any).
*/ */
public function getUniqueId() public function getUniqueId()
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\base;
use Yii;
use DateTime;
use yii\helpers\HtmlPurifier;
use yii\helpers\Html;
/**
* Formatter provides a set of commonly used data formatting methods.
*
* The formatting methods provided by Formatter are all named in the form of `asXyz()`.
* The behavior of some of them may be configured via the properties of Formatter. For example,
* by configuring [[dateFormat]], one may control how [[asDate()]] formats the value into a date string.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Formatter extends Component
{
/**
* @var string the default format string to be used to format a date using PHP date() function.
*/
public $dateFormat = 'Y/m/d';
/**
* @var string the default format string to be used to format a time using PHP date() function.
*/
public $timeFormat = 'h:i:s A';
/**
* @var string the default format string to be used to format a date and time using PHP date() function.
*/
public $datetimeFormat = 'Y/m/d h:i:s A';
/**
* @var array the text to be displayed when formatting a boolean value. The first element corresponds
* to the text display for false, the second element for true. Defaults to <code>array('No', 'Yes')</code>.
*/
public $booleanFormat;
/**
* @var string the character displayed as the decimal point when formatting a number.
*/
public $decimalSeparator = '.';
/**
* @var string the character displayed as the thousands separator character when formatting a number.
*/
public $thousandSeparator = ',';
/**
* Initializes the component.
*/
public function init()
{
if (empty($this->booleanFormat)) {
$this->booleanFormat = array(Yii::t('yii', 'No'), Yii::t('yii', 'Yes'));
}
}
/**
* Formats the value as is without any formatting.
* This method simply returns back the parameter without any format.
* @param mixed $value the value to be formatted
* @return string the formatted result
*/
public function asRaw($value)
{
return $value;
}
/**
* Formats the value as an HTML-encoded plain text.
* @param mixed $value the value to be formatted
* @return string the formatted result
*/
public function asText($value)
{
return Html::encode($value);
}
/**
* Formats the value as an HTML-encoded plain text with newlines converted into breaks.
* @param mixed $value the value to be formatted
* @return string the formatted result
*/
public function asNtext($value)
{
return nl2br(Html::encode($value));
}
/**
* Formats the value as HTML-encoded text paragraphs.
* Each text paragraph is enclosed within a `<p>` tag.
* One or multiple consecutive empty lines divide two paragraphs.
* @param mixed $value the value to be formatted
* @return string the formatted result
*/
public function asParagraphs($value)
{
return str_replace('<p></p>', '',
'<p>' . preg_replace('/[\r\n]{2,}/', "</p>\n<p>", Html::encode($value)) . '</p>'
);
}
/**
* Formats the value as HTML text.
* The value will be purified using [[HtmlPurifier]] to avoid XSS attacks.
* Use [[asRaw()]] if you do not want any purification of the value.
* @param mixed $value the value to be formatted
* @param array|null $config the configuration for the HTMLPurifier class.
* @return string the formatted result
*/
public function asHtml($value, $config = null)
{
return HtmlPurifier::process($value, $config);
}
/**
* Formats the value as a mailto link.
* @param mixed $value the value to be formatted
* @return string the formatted result
*/
public function asEmail($value)
{
return Html::mailto($value);
}
/**
* Formats the value as an image tag.
* @param mixed $value the value to be formatted
* @return string the formatted result
*/
public function asImage($value)
{
return Html::img($value);
}
/**
* Formats the value as a hyperlink.
* @param mixed $value the value to be formatted
* @return string the formatted result
*/
public function asUrl($value)
{
$url = $value;
if (strpos($url, 'http://') !== 0 && strpos($url, 'https://') !== 0) {
$url = 'http://' . $url;
}
return Html::a(Html::encode($value), $url);
}
/**
* Formats the value as a boolean.
* @param mixed $value the value to be formatted
* @return string the formatted result
* @see booleanFormat
*/
public function asBoolean($value)
{
return $value ? $this->booleanFormat[1] : $this->booleanFormat[0];
}
/**
* Formats the value as a date.
* @param integer|string|DateTime $value the value to be formatted. The following
* types of value are supported:
*
* - an integer representing a UNIX timestamp
* - a string that can be parsed into a UNIX timestamp via `strtotime()`
* - a PHP DateTime object
*
* @param string $format the format used to convert the value into a date string.
* If null, [[dateFormat]] will be used. The format string should be one
* that can be recognized by the PHP `date()` function.
* @return string the formatted result
* @see dateFormat
*/
public function asDate($value, $format = null)
{
$value = $this->normalizeDatetimeValue($value);
return date($format === null ? $this->dateFormat : $format, $value);
}
/**
* Formats the value as a time.
* @param integer|string|DateTime $value the value to be formatted. The following
* types of value are supported:
*
* - an integer representing a UNIX timestamp
* - a string that can be parsed into a UNIX timestamp via `strtotime()`
* - a PHP DateTime object
*
* @param string $format the format used to convert the value into a date string.
* If null, [[timeFormat]] will be used. The format string should be one
* that can be recognized by the PHP `date()` function.
* @return string the formatted result
* @see timeFormat
*/
public function asTime($value, $format = null)
{
$value = $this->normalizeDatetimeValue($value);
return date($format === null ? $this->timeFormat : $format, $value);
}
/**
* Formats the value as a datetime.
* @param integer|string|DateTime $value the value to be formatted. The following
* types of value are supported:
*
* - an integer representing a UNIX timestamp
* - a string that can be parsed into a UNIX timestamp via `strtotime()`
* - a PHP DateTime object
*
* @param string $format the format used to convert the value into a date string.
* If null, [[datetimeFormat]] will be used. The format string should be one
* that can be recognized by the PHP `date()` function.
* @return string the formatted result
* @see datetimeFormat
*/
public function asDatetime($value, $format = null)
{
$value = $this->normalizeDatetimeValue($value);
return date($format === null ? $this->datetimeFormat : $format, $value);
}
/**
* Normalizes the given datetime value as one that can be taken by various date/time formatting methods.
* @param mixed $value the datetime value to be normalized.
* @return mixed the normalized datetime value
*/
protected function normalizeDatetimeValue($value)
{
if (is_string($value)) {
if (ctype_digit($value) || $value[0] === '-' && ctype_digit(substr($value, 1))) {
return (int)$value;
} else {
return strtotime($value);
}
} elseif ($value instanceof DateTime) {
return $value->getTimestamp();
} else {
return (int)$value;
}
}
/**
* Formats the value as an integer.
* @param mixed $value the value to be formatted
* @return string the formatting result.
*/
public function asInteger($value)
{
if (is_string($value) && preg_match('/^(-?\d+)/', $value, $matches)) {
return $matches[1];
} else {
$value = (int)$value;
return "$value";
}
}
/**
* Formats the value as a double number.
* Property [[decimalSeparator]] will be used to represent the decimal point.
* @param mixed $value the value to be formatted
* @param integer $decimals the number of digits after the decimal point
* @return string the formatting result.
* @see decimalSeparator
*/
public function asDouble($value, $decimals = 2)
{
return str_replace('.', $this->decimalSeparator, sprintf("%.{$decimals}f", $value));
}
/**
* Formats the value as a number with decimal and thousand separators.
* This method calls the PHP number_format() function to do the formatting.
* @param mixed $value the value to be formatted
* @param integer $decimals the number of digits after the decimal point
* @return string the formatted result
* @see decimalSeparator
* @see thousandSeparator
*/
public function asNumber($value, $decimals = 0)
{
return number_format($value, $decimals, $this->decimalSeparator, $this->thousandSeparator);
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\base;
/**
* InvalidRequestException represents an exception caused by incorrect end user request.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class InvalidRequestException extends UserException
{
/**
* @return string the user-friendly name of this exception
*/
public function getName()
{
return \Yii::t('yii', 'Invalid Request');
}
}
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
namespace yii\bootstrap; namespace yii\bootstrap;
use yii\base\InvalidConfigException; use yii\base\InvalidConfigException;
use yii\helpers\base\ArrayHelper; use yii\helpers\ArrayHelper;
use yii\helpers\Html; use yii\helpers\Html;
...@@ -32,17 +32,22 @@ class Dropdown extends Widget ...@@ -32,17 +32,22 @@ class Dropdown extends Widget
* // optional, url of the item link * // optional, url of the item link
* 'url' => '', * 'url' => '',
* // optional the HTML attributes of the item link * // optional the HTML attributes of the item link
* 'urlOptions'=> array(...), * 'linkOptions'=> array(...),
* // optional the HTML attributes of the item * // optional the HTML attributes of the item
* 'options'=> array(...), * 'options'=> array(...),
* // optional, an array of items that configure a sub menu of the item * // optional, an array of items that configure a sub menu of the item
* // note: if `items` is set, then `url` of the parent item will be ignored and automatically set to "#" * // note: if `items` is set, then `url` of the parent item will be ignored and automatically set to "#"
* // important: there is an issue with sub-dropdown menus, and as of 3.0, bootstrap won't support sub-dropdown
* // @see https://github.com/twitter/bootstrap/issues/5050#issuecomment-11741727
* 'items'=> array(...) * 'items'=> array(...)
* ) * )
* ``` * ```
* Additionally, you can also configure a dropdown item as string.
*/ */
public $items = array(); public $items = array();
/**
* @var boolean whether the labels for header items should be HTML-encoded.
*/
public $encodeLabels = true;
/** /**
...@@ -60,43 +65,41 @@ class Dropdown extends Widget ...@@ -60,43 +65,41 @@ class Dropdown extends Widget
*/ */
public function run() public function run()
{ {
echo Html::beginTag('ul', $this->options) . "\n"; echo $this->renderItems() . "\n";
echo $this->renderContents() . "\n";
echo Html::endTag('ul') . "\n";
$this->registerPlugin('dropdown'); $this->registerPlugin('dropdown');
} }
/** /**
* Renders dropdown contents as specified on [[items]]. * Renders dropdown items as specified on [[items]].
* @return string the rendering result. * @return string the rendering result.
* @throws InvalidConfigException * @throws InvalidConfigException
*/ */
protected function renderContents() protected function renderItems()
{ {
$contents = array(); $items = array();
foreach ($this->items as $item) { foreach ($this->items as $item) {
if (is_string($item)) { if (is_string($item)) {
$contents[] = $item; $items[] = $item;
continue; continue;
} }
if (!isset($item['label'])) { if (!isset($item['label'])) {
throw new InvalidConfigException("The 'label' option is required."); throw new InvalidConfigException("The 'label' option is required.");
} }
$label = $this->encodeLabels ? Html::encode($item['label']) : $item['label'];
$options = ArrayHelper::getValue($item, 'options', array()); $options = ArrayHelper::getValue($item, 'options', array());
$urlOptions = ArrayHelper::getValue($item, 'urlOptions', array()); $linkOptions = ArrayHelper::getValue($item, 'linkOptions', array());
$urlOptions['tabindex'] = '-1'; $linkOptions['tabindex'] = '-1';
if (isset($item['items'])) { if (isset($item['items'])) {
$this->addCssClass($options, 'dropdown-submenu'); $this->addCssClass($options, 'dropdown-submenu');
$content = Html::a($item['label'], '#', $urlOptions) . $this->dropdown($item['items']); $content = Html::a($label, '#', $linkOptions) . $this->dropdown($item['items']);
} else { } else {
$content = Html::a($item['label'], ArrayHelper::getValue($item, 'url', '#'), $urlOptions); $content = Html::a($label, ArrayHelper::getValue($item, 'url', '#'), $linkOptions);
} }
$contents[] = Html::tag('li', $content , $options); $items[] = Html::tag('li', $content , $options);
} }
return implode("\n", $contents); return Html::tag('ul', implode("\n", $items), $this->options);
} }
/** /**
......
...@@ -344,7 +344,7 @@ class Html ...@@ -344,7 +344,7 @@ class Html
/** /**
* Generates a hyperlink tag. * Generates a hyperlink tag.
* @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code * @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code
* such as an image tag. If this is is coming from end users, you should consider [[encode()]] * such as an image tag. If this is coming from end users, you should consider [[encode()]]
* it to prevent XSS attacks. * it to prevent XSS attacks.
* @param array|string|null $url the URL for the hyperlink tag. This parameter will be processed by [[url()]] * @param array|string|null $url the URL for the hyperlink tag. This parameter will be processed by [[url()]]
* and will be used for the "href" attribute of the tag. If this parameter is null, the "href" attribute * and will be used for the "href" attribute of the tag. If this parameter is null, the "href" attribute
...@@ -366,7 +366,7 @@ class Html ...@@ -366,7 +366,7 @@ class Html
/** /**
* Generates a mailto hyperlink. * Generates a mailto hyperlink.
* @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code * @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code
* such as an image tag. If this is is coming from end users, you should consider [[encode()]] * such as an image tag. If this is coming from end users, you should consider [[encode()]]
* it to prevent XSS attacks. * it to prevent XSS attacks.
* @param string $email email address. If this is null, the first parameter (link body) will be treated * @param string $email email address. If this is null, the first parameter (link body) will be treated
* as the email address and used. * as the email address and used.
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\i18n;
use Yii;
use IntlDateFormatter;
use NumberFormatter;
use DateTime;
use yii\base\InvalidConfigException;
/**
* Formatter is the localized version of [[\yii\base\Formatter]].
*
* Formatter requires the PHP "intl" extension to be installed. Formatter supports localized
* formatting of date, time and numbers, based on the current [[locale]].
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Formatter extends \yii\base\Formatter
{
/**
* @var string the locale ID that is used to localize the date and number formatting.
* If not set, [[\yii\base\Application::language]] will be used.
*/
public $locale;
/**
* @var string the default format string to be used to format a date using PHP date() function.
*/
public $dateFormat = 'short';
/**
* @var string the default format string to be used to format a time using PHP date() function.
*/
public $timeFormat = 'short';
/**
* @var string the default format string to be used to format a date and time using PHP date() function.
*/
public $datetimeFormat = 'short';
/**
* @var array the options to be set for the NumberFormatter objects. Please refer to
*/
public $numberFormatOptions = array();
/**
* Initializes the component.
* This method will check if the "intl" PHP extension is installed and set the
* default value of [[locale]].
* @throws InvalidConfigException if the "intl" PHP extension is not installed.
*/
public function init()
{
if (!extension_loaded('intl')) {
throw new InvalidConfigException('The "intl" PHP extension is not install. It is required to format data values in localized formats.');
}
if ($this->locale === null) {
$this->locale = Yii::$app->language;
}
parent::init();
}
private $_dateFormats = array(
'short' => IntlDateFormatter::SHORT,
'medium' => IntlDateFormatter::MEDIUM,
'long' => IntlDateFormatter::LONG,
'full' => IntlDateFormatter::FULL,
);
/**
* Formats the value as a date.
* @param integer|string|DateTime $value the value to be formatted. The following
* types of value are supported:
*
* - an integer representing a UNIX timestamp
* - a string that can be parsed into a UNIX timestamp via `strtotime()`
* - a PHP DateTime object
*
* @param string $format the format used to convert the value into a date string.
* If null, [[dateFormat]] will be used. The format string should be the one
* that can be recognized by the PHP `date()` function.
* @return string the formatted result
* @see dateFormat
*/
public function asDate($value, $format = null)
{
$value = $this->normalizeDatetimeValue($value);
if ($format === null) {
$format = $this->dateFormat;
}
if (isset($this->_dateFormats[$format])) {
$formatter = new IntlDateFormatter($this->locale, $this->_dateFormats[$format], IntlDateFormatter::NONE);
} else {
$formatter = new IntlDateFormatter($this->locale, IntlDateFormatter::NONE, IntlDateFormatter::NONE);
$formatter->setPattern($format);
}
return $formatter->format($value);
}
/**
* Formats the value as a time.
* @param integer|string|DateTime $value the value to be formatted. The following
* types of value are supported:
*
* - an integer representing a UNIX timestamp
* - a string that can be parsed into a UNIX timestamp via `strtotime()`
* - a PHP DateTime object
*
* @param string $format the format used to convert the value into a date string.
* If null, [[dateFormat]] will be used. The format string should be the one
* that can be recognized by the PHP `date()` function.
* @return string the formatted result
* @see timeFormat
*/
public function asTime($value, $format = null)
{
$value = $this->normalizeDatetimeValue($value);
if ($format === null) {
$format = $this->timeFormat;
}
if (isset($this->_dateFormats[$format])) {
$formatter = new IntlDateFormatter($this->locale, IntlDateFormatter::NONE, $this->_dateFormats[$format]);
} else {
$formatter = new IntlDateFormatter($this->locale, IntlDateFormatter::NONE, IntlDateFormatter::NONE);
$formatter->setPattern($format);
}
return $formatter->format($value);
}
/**
* Formats the value as a datetime.
* @param integer|string|DateTime $value the value to be formatted. The following
* types of value are supported:
*
* - an integer representing a UNIX timestamp
* - a string that can be parsed into a UNIX timestamp via `strtotime()`
* - a PHP DateTime object
*
* @param string $format the format used to convert the value into a date string.
* If null, [[dateFormat]] will be used. The format string should be the one
* that can be recognized by the PHP `date()` function.
* @return string the formatted result
* @see datetimeFormat
*/
public function asDatetime($value, $format = null)
{
$value = $this->normalizeDatetimeValue($value);
if ($format === null) {
$format = $this->datetimeFormat;
}
if (isset($this->_dateFormats[$format])) {
$formatter = new IntlDateFormatter($this->locale, $this->_dateFormats[$format], $this->_dateFormats[$format]);
} else {
$formatter = new IntlDateFormatter($this->locale, IntlDateFormatter::NONE, IntlDateFormatter::NONE);
$formatter->setPattern($format);
}
return $formatter->format($value);
}
/**
* Formats the value as a decimal number.
* @param mixed $value the value to be formatted
* @param string $format the format to be used. Please refer to [ICU manual](http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details)
* for details on how to specify a format.
* @return string the formatted result.
*/
public function asDecimal($value, $format = null)
{
return $this->createNumberFormatter(NumberFormatter::DECIMAL, $format)->format($value);
}
/**
* Formats the value as a currency number.
* @param mixed $value the value to be formatted
* @param string $currency the 3-letter ISO 4217 currency code indicating the currency to use.
* @param string $format the format to be used. Please refer to [ICU manual](http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details)
* for details on how to specify a format.
* @return string the formatted result.
*/
public function asCurrency($value, $currency = 'USD', $format = null)
{
return $this->createNumberFormatter(NumberFormatter::CURRENCY, $format)->formatCurrency($value, $currency);
}
/**
* Formats the value as a percent number.
* @param mixed $value the value to be formatted
* @param string $format the format to be used. Please refer to [ICU manual](http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details)
* for details on how to specify a format.
* @return string the formatted result.
*/
public function asPercent($value, $format = null)
{
return $this->createNumberFormatter(NumberFormatter::PERCENT, $format)->format($value);
}
/**
* Formats the value as a scientific number.
* @param mixed $value the value to be formatted
* @param string $format the format to be used. Please refer to [ICU manual](http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details)
* for details on how to specify a format.
* @return string the formatted result.
*/
public function asScientific($value, $format = null)
{
return $this->createNumberFormatter(NumberFormatter::SCIENTIFIC, $format)->format($value);
}
/**
* Creates a number formatter based on the given type and format.
* @param integer $type the type of the number formatter
* @param string $format the format to be used
* @return NumberFormatter the created formatter instance
*/
protected function createNumberFormatter($type, $format)
{
$formatter = new NumberFormatter($this->locale, $type);
if ($format !== null) {
$formatter->setPattern($format);
}
if (!empty($this->numberFormatOptions)) {
foreach ($this->numberFormatOptions as $name => $attribute) {
$formatter->setAttribute($name, $attribute);
}
}
return $formatter;
}
}
...@@ -43,6 +43,6 @@ return array( ...@@ -43,6 +43,6 @@ return array(
'mandatory' => false, 'mandatory' => false,
'condition' => $this->checkPhpExtensionVersion('intl', '1.0.2'), 'condition' => $this->checkPhpExtensionVersion('intl', '1.0.2'),
'by' => '<a href="http://www.php.net/manual/en/book.intl.php">Internationalization</a> support', 'by' => '<a href="http://www.php.net/manual/en/book.intl.php">Internationalization</a> support',
'memo' => 'PHP Intl extension 1.0.2 or higher is required when you want to use <abbr title="Internationalized domain names">IDN</abbr>-feature of EmailValidator or UrlValidator.' 'memo' => 'PHP Intl extension 1.0.2 or higher is required when you want to use <abbr title="Internationalized domain names">IDN</abbr>-feature of EmailValidator or UrlValidator or the <code>yii\i18n\Formatter</code> class.'
), ),
); );
\ No newline at end of file
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
namespace yii\web; namespace yii\web;
use Yii; use Yii;
use yii\base\HttpException;
use yii\base\InlineAction;
/** /**
* Controller is the base class of Web controllers. * Controller is the base class of Web controllers.
...@@ -19,6 +21,48 @@ use Yii; ...@@ -19,6 +21,48 @@ use Yii;
class Controller extends \yii\base\Controller class Controller extends \yii\base\Controller
{ {
/** /**
* Binds the parameters to the action.
* This method is invoked by [[Action]] when it begins to run with the given parameters.
* This method will check the parameter names that the action requires and return
* the provided parameters according to the requirement. If there is any missing parameter,
* an exception will be thrown.
* @param \yii\base\Action $action the action to be bound with parameters
* @param array $params the parameters to be bound to the action
* @return array the valid parameters that the action can run with.
* @throws HttpException if there are missing parameters.
*/
public function bindActionParams($action, $params)
{
if ($action instanceof InlineAction) {
$method = new \ReflectionMethod($this, $action->actionMethod);
} else {
$method = new \ReflectionMethod($action, 'run');
}
$args = array();
$missing = array();
foreach ($method->getParameters() as $param) {
$name = $param->getName();
if (array_key_exists($name, $params)) {
$args[] = $params[$name];
unset($params[$name]);
} elseif ($param->isDefaultValueAvailable()) {
$args[] = $param->getDefaultValue();
} else {
$missing[] = $name;
}
}
if (!empty($missing)) {
throw new HttpException(400, Yii::t('yii', 'Missing required parameters: {params}', array(
'{params}' => implode(', ', $missing),
)));
}
return $args;
}
/**
* Creates a URL using the given route and parameters. * Creates a URL using the given route and parameters.
* *
* This method enhances [[UrlManager::createUrl()]] by supporting relative routes. * This method enhances [[UrlManager::createUrl()]] by supporting relative routes.
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\web;
use Yii;
use yii\base\ActionEvent;
use yii\base\Behavior;
use yii\base\HttpException;
/**
* VerbFilter is an action filter that filters by HTTP request methods.
*
* It allows to define allowed HTTP request methods for each action and will throw
* an HTTP 405 error when the method is not allowed.
*
* To use VerbFilter, declare it in the `behaviors()` method of your controller class.
* For example, the following declarations will define a typical set of allowed
* request methods for REST CRUD actions.
*
* ~~~
* public function behaviors()
* {
* return array(
* 'verbs' => array(
* 'class' => \yii\web\VerbFilter::className(),
* 'actions' => array(
* 'index' => array('get'),
* 'view' => array('get'),
* 'create' => array('get', 'post'),
* 'update' => array('get', 'put', 'post'),
* 'delete' => array('post', 'delete'),
* ),
* ),
* );
* }
* ~~~
*
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.7
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class VerbFilter extends Behavior
{
/**
* @var array this property defines the allowed request methods for each action.
* For each action that should only support limited set of request methods
* you add an entry with the action id as array key and an array of
* allowed methods (e.g. GET, HEAD, PUT) as the value.
* If an action is not listed all request methods are considered allowed.
*/
public $actions = array();
/**
* Declares event handlers for the [[owner]]'s events.
* @return array events (array keys) and the corresponding event handler methods (array values).
*/
public function events()
{
return array(
Controller::EVENT_BEFORE_ACTION => 'beforeAction',
);
}
/**
* @param ActionEvent $event
* @return boolean
* @throws \yii\base\HttpException when the request method is not allowed.
*/
public function beforeAction($event)
{
$action = $event->action->id;
if (isset($this->actions[$action])) {
$verb = Yii::$app->getRequest()->getRequestMethod();
$allowed = array_map('strtoupper', $this->actions[$action]);
if (!in_array($verb, $allowed)) {
$event->isValid = false;
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.7
header('Allow: ' . implode(', ', $allowed));
throw new HttpException(405, 'Method Not Allowed. This url can only handle the following request methods: ' . implode(', ', $allowed));
}
}
return $event->isValid;
}
}
...@@ -134,8 +134,8 @@ class ActiveForm extends Widget ...@@ -134,8 +134,8 @@ class ActiveForm extends Widget
$id = $this->options['id']; $id = $this->options['id'];
$options = Json::encode($this->getClientOptions()); $options = Json::encode($this->getClientOptions());
$attributes = Json::encode($this->attributes); $attributes = Json::encode($this->attributes);
$this->view->registerAssetBundle('yii/form'); $this->getView()->registerAssetBundle('yii/form');
$this->view->registerJs("jQuery('#$id').yiiActiveForm($attributes, $options);"); $this->getView()->registerJs("jQuery('#$id').yiiActiveForm($attributes, $options);");
} }
echo Html::endForm(); echo Html::endForm();
} }
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\widgets;
use Yii;
use yii\base\Widget;
use yii\base\Model;
use yii\base\InvalidConfigException;
/**
* InputWidget is the base class for widgets that collect user inputs.
*
* An input widget can be associated with a data model and an attribute,
* or a name and a value. If the former, the name and the value will
* be generated automatically.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class InputWidget extends Widget
{
/**
* @var Model the data model that this widget is associated with.
*/
public $model;
/**
* @var string the model attribute that this widget is associated with.
*/
public $attribute;
/**
* @var string the input name. This must be set if [[model]] and [[attribute]] are not set.
*/
public $name;
/**
* @var string the input value.
*/
public $value;
/**
* Initializes the widget.
* If you override this method, make sure you call the parent implementation first.
*/
public function init()
{
if (!$this->hasModel() && $this->name === null) {
throw new InvalidConfigException("Either 'name' or 'model' and 'attribute' properties must be specified.");
}
parent::init();
}
/**
* @return boolean whether this widget is associated with a data model.
*/
protected function hasModel()
{
return $this->model instanceof Model && $this->attribute !== null;
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\widgets;
use yii\base\InvalidConfigException;
use yii\helpers\Html;
use yii\helpers\Json;
use yii\web\JsExpression;
/**
* MaskedInput generates a masked text input.
*
* MaskedInput is similar to [[Html::textInput()]] except that
* an input mask will be used to force users to enter properly formatted data,
* such as phone numbers, social security numbers.
*
* To use MaskedInput, you must set the [[mask]] property. The following example
* shows how to use MaskedInput to collect phone numbers:
*
* ~~~
* echo MaskedInput::widget(array(
* 'name' => 'phone',
* 'mask' => '999-999-9999',
* ));
* ~~~
*
* The masked text field is implemented based on the [jQuery masked input plugin](http://digitalbush.com/projects/masked-input-plugin).
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class MaskedInput extends InputWidget
{
/**
* @var string the input mask (e.g. '99/99/9999' for date input). The following characters are predefined:
*
* - `a`: represents an alpha character (A-Z,a-z)
* - `9`: represents a numeric character (0-9)
* - `*`: represents an alphanumeric character (A-Z,a-z,0-9)
* - `?`: anything listed after '?' within the mask is considered optional user input
*
* Additional characters can be defined by specifying the [[charMap]] property.
*/
public $mask;
/**
* @var array the mapping between mask characters and the corresponding patterns.
* For example, `array('~' => '[+-]')` specifies that the '~' character expects '+' or '-' input.
* Defaults to null, meaning using the map as described in [[mask]].
*/
public $charMap;
/**
* @var string the character prompting for user input. Defaults to underscore '_'.
*/
public $placeholder;
/**
* @var string a JavaScript function callback that will be invoked when user finishes the input.
*/
public $completed;
/**
* @var array the HTML attributes for the input tag.
*/
public $options = array();
/**
* Initializes the widget.
* @throws InvalidConfigException if the "mask" property is not set.
*/
public function init()
{
parent::init();
if (empty($this->mask)) {
throw new InvalidConfigException('The "mask" property must be set.');
}
if (!isset($this->options['id'])) {
$this->options['id'] = $this->hasModel() ? Html::getInputId($this->model, $this->attribute) : $this->getId();
}
}
/**
* Runs the widget.
*/
public function run()
{
if ($this->hasModel()) {
echo Html::activeTextInput($this->model, $this->attribute, $this->options);
} else {
echo Html::textInput($this->name, $this->value, $this->options);
}
$this->registerClientScript();
}
/**
* Registers the needed JavaScript.
*/
public function registerClientScript()
{
$options = $this->getClientOptions();
$options = empty($options) ? '' : ',' . Json::encode($options);
$js = '';
if (is_array($this->charMap) && !empty($this->charMap)) {
$js .= 'jQuery.mask.definitions=' . Json::encode($this->charMap) . ";\n";
}
$id = $this->options['id'];
$js .= "jQuery(\"#{$id}\").mask(\"{$this->mask}\"{$options});";
$this->getView()->registerAssetBundle('yii/maskedinput');
$this->getView()->registerJs($js);
}
/**
* @return array the options for the text field
*/
protected function getClientOptions()
{
$options = array();
if ($this->placeholder !== null) {
$options['placeholder'] = $this->placeholder;
}
if ($this->completed !== null) {
if ($this->completed instanceof JsExpression) {
$options['completed'] = $this->completed;
} else {
$options['completed'] = new JsExpression($this->completed);
}
}
return $options;
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yiiunit\framework\base;
use yii\base\Formatter;
use yiiunit\TestCase;
/**
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class FormatterTest extends TestCase
{
/**
* @var Formatter
*/
protected $formatter;
protected function setUp()
{
parent::setUp();
$this->mockApplication();
$this->formatter = new Formatter();
}
protected function tearDown()
{
parent::tearDown();
$this->formatter = null;
}
public function testAsRaw()
{
$value = '123';
$this->assertSame($value, $this->formatter->asRaw($value));
$value = 123;
$this->assertSame($value, $this->formatter->asRaw($value));
$value = '<>';
$this->assertSame($value, $this->formatter->asRaw($value));
}
public function testAsText()
{
$value = '123';
$this->assertSame($value, $this->formatter->asText($value));
$value = 123;
$this->assertSame("$value", $this->formatter->asText($value));
$value = '<>';
$this->assertSame('&lt;&gt;', $this->formatter->asText($value));
}
public function testAsNtext()
{
$value = '123';
$this->assertSame($value, $this->formatter->asNtext($value));
$value = 123;
$this->assertSame("$value", $this->formatter->asNtext($value));
$value = '<>';
$this->assertSame('&lt;&gt;', $this->formatter->asNtext($value));
$value = "123\n456";
$this->assertSame("123<br />\n456", $this->formatter->asNtext($value));
}
public function testAsParagraphs()
{
$value = '123';
$this->assertSame("<p>$value</p>", $this->formatter->asParagraphs($value));
$value = 123;
$this->assertSame("<p>$value</p>", $this->formatter->asParagraphs($value));
$value = '<>';
$this->assertSame('<p>&lt;&gt;</p>', $this->formatter->asParagraphs($value));
$value = "123\n456";
$this->assertSame("<p>123\n456</p>", $this->formatter->asParagraphs($value));
$value = "123\n\n456";
$this->assertSame("<p>123</p>\n<p>456</p>", $this->formatter->asParagraphs($value));
$value = "123\n\n\n456";
$this->assertSame("<p>123</p>\n<p>456</p>", $this->formatter->asParagraphs($value));
}
public function testAsHtml()
{
// todo: dependency on HtmlPurifier
}
public function testAsEmail()
{
$value = 'test@sample.com';
$this->assertSame("<a href=\"mailto:$value\">$value</a>", $this->formatter->asEmail($value));
}
public function testAsImage()
{
$value = 'http://sample.com/img.jpg';
$this->assertSame("<img src=\"$value\" alt=\"\" />", $this->formatter->asImage($value));
}
public function testAsBoolean()
{
$value = true;
$this->assertSame('Yes', $this->formatter->asBoolean($value));
$value = false;
$this->assertSame('No', $this->formatter->asBoolean($value));
$value = "111";
$this->assertSame('Yes', $this->formatter->asBoolean($value));
$value = "";
$this->assertSame('No', $this->formatter->asBoolean($value));
}
public function testAsDate()
{
$value = time();
$this->assertSame(date('Y/m/d', $value), $this->formatter->asDate($value));
$this->assertSame(date('Y-m-d', $value), $this->formatter->asDate($value, 'Y-m-d'));
}
public function testAsTime()
{
$value = time();
$this->assertSame(date('h:i:s A', $value), $this->formatter->asTime($value));
$this->assertSame(date('h:i:s', $value), $this->formatter->asTime($value, 'h:i:s'));
}
public function testAsDatetime()
{
$value = time();
$this->assertSame(date('Y/m/d h:i:s A', $value), $this->formatter->asDatetime($value));
$this->assertSame(date('Y-m-d h:i:s', $value), $this->formatter->asDatetime($value, 'Y-m-d h:i:s'));
}
public function testAsInteger()
{
$value = 123;
$this->assertSame("$value", $this->formatter->asInteger($value));
$value = 123.23;
$this->assertSame("123", $this->formatter->asInteger($value));
$value = 'a';
$this->assertSame("0", $this->formatter->asInteger($value));
$value = -123.23;
$this->assertSame("-123", $this->formatter->asInteger($value));
$value = "-123abc";
$this->assertSame("-123", $this->formatter->asInteger($value));
}
public function testAsDouble()
{
$value = 123.12;
$this->assertSame("123.12", $this->formatter->asDouble($value));
$this->assertSame("123.1", $this->formatter->asDouble($value, 1));
$this->assertSame("123", $this->formatter->asDouble($value, 0));
$value = 123;
$this->assertSame("123.00", $this->formatter->asDouble($value));
$this->formatter->decimalSeparator = ',';
$value = 123.12;
$this->assertSame("123,12", $this->formatter->asDouble($value));
$this->assertSame("123,1", $this->formatter->asDouble($value, 1));
$this->assertSame("123", $this->formatter->asDouble($value, 0));
$value = 123123.123;
$this->assertSame("123123,12", $this->formatter->asDouble($value));
}
public function testAsNumber()
{
$value = 123123.123;
$this->assertSame("123,123", $this->formatter->asNumber($value));
$this->assertSame("123,123.12", $this->formatter->asNumber($value, 2));
$this->formatter->decimalSeparator = ',';
$this->formatter->thousandSeparator = ' ';
$this->assertSame("123 123", $this->formatter->asNumber($value));
$this->assertSame("123 123,12", $this->formatter->asNumber($value, 2));
$this->formatter->thousandSeparator = '';
$this->assertSame("123123", $this->formatter->asNumber($value));
$this->assertSame("123123,12", $this->formatter->asNumber($value, 2));
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yiiunit\framework\i18n;
use yii\i18n\Formatter;
use yiiunit\TestCase;
/**
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class FormatterTest extends TestCase
{
/**
* @var Formatter
*/
protected $formatter;
protected function setUp()
{
parent::setUp();
if (!extension_loaded('intl')) {
$this->markTestSkipped('intl extension is required.');
}
$this->mockApplication();
$this->formatter = new Formatter(array(
'locale' => 'en_US',
));
}
protected function tearDown()
{
parent::tearDown();
$this->formatter = null;
}
public function testAsDecimal()
{
$value = '123';
$this->assertSame($value, $this->formatter->asDecimal($value));
$value = '123456';
$this->assertSame("123,456", $this->formatter->asDecimal($value));
$value = '-123456.123';
$this->assertSame("-123,456.123", $this->formatter->asDecimal($value));
}
public function testAsPercent()
{
$value = '123';
$this->assertSame('12,300%', $this->formatter->asPercent($value));
$value = '0.1234';
$this->assertSame("12%", $this->formatter->asPercent($value));
$value = '-0.009343';
$this->assertSame("-1%", $this->formatter->asPercent($value));
}
public function testAsScientific()
{
$value = '123';
$this->assertSame('1.23E2', $this->formatter->asScientific($value));
$value = '123456';
$this->assertSame("1.23456E5", $this->formatter->asScientific($value));
$value = '-123456.123';
$this->assertSame("-1.23456123E5", $this->formatter->asScientific($value));
}
public function testAsCurrency()
{
$value = '123';
$this->assertSame('$123.00', $this->formatter->asCurrency($value));
$value = '123.456';
$this->assertSame("$123.46", $this->formatter->asCurrency($value));
$value = '-123456.123';
$this->assertSame("($123,456.12)", $this->formatter->asCurrency($value));
}
public function testDate()
{
$time = time();
$this->assertSame(date('n/j/y', $time), $this->formatter->asDate($time));
$this->assertSame(date('M j, Y', $time), $this->formatter->asDate($time, 'long'));
}
}
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