Browse Source

Merge branch 'master' of git://github.com/yiisoft/yii2 into 9562-add-char-datatype

tags/2.0.8
Chris Harris 9 years ago
parent
commit
0ba1f5bba5
  1. 0
      .github/CONTRIBUTING.md
  2. 14
      .github/ISSUE_TEMPLATE.md
  3. 7
      .github/PULL_REQUEST_TEMPLATE.md
  4. 3
      build/controllers/DevController.php
  5. 90
      build/controllers/PhpDocController.php
  6. 498
      build/controllers/ReleaseController.php
  7. 2
      docs/guide-ja/concept-behaviors.md
  8. 6
      docs/guide-ja/helper-array.md
  9. 26
      docs/guide-ja/helper-url.md
  10. 4
      docs/guide-ja/output-pagination.md
  11. 2
      docs/guide-ja/output-sorting.md
  12. 6
      docs/guide-ja/runtime-handling-errors.md
  13. 26
      docs/guide-ja/runtime-routing.md
  14. 5
      docs/guide-ja/security-authorization.md
  15. 4
      docs/guide-ja/start-databases.md
  16. 2
      docs/guide-ja/start-forms.md
  17. 2
      docs/guide-ja/start-gii.md
  18. 2
      docs/guide-ja/start-hello.md
  19. 2
      docs/guide-ja/start-workflow.md
  20. 2
      docs/guide-ja/structure-views.md
  21. 39
      docs/guide-ja/tutorial-console.md
  22. 1
      docs/guide-ja/tutorial-i18n.md
  23. 2
      docs/guide-ru/db-dao.md
  24. 26
      docs/guide/helper-url.md
  25. 4
      docs/guide/output-pagination.md
  26. 2
      docs/guide/output-sorting.md
  27. 26
      docs/guide/runtime-routing.md
  28. 4
      docs/guide/start-databases.md
  29. 2
      docs/guide/start-forms.md
  30. 2
      docs/guide/start-gii.md
  31. 2
      docs/guide/start-hello.md
  32. 2
      docs/guide/structure-views.md
  33. 10
      framework/CHANGELOG.md
  34. 2
      framework/UPGRADE.md
  35. 2
      framework/assets/yii.js
  36. 3
      framework/grid/CheckboxColumn.php
  37. 5
      framework/helpers/BaseConsole.php
  38. 2
      framework/helpers/BaseHtmlPurifier.php
  39. 2
      framework/helpers/BaseJson.php
  40. 12
      framework/helpers/BaseStringHelper.php
  41. 22
      framework/helpers/BaseUrl.php
  42. 1
      framework/messages/hu/yii.php
  43. 10
      framework/rbac/DbManager.php
  44. 10
      framework/rbac/ManagerInterface.php
  45. 16
      framework/rbac/PhpManager.php
  46. 7
      framework/validators/EachValidator.php
  47. 4
      framework/validators/Validator.php
  48. 3
      framework/web/UploadedFile.php
  49. 6
      framework/web/UrlManager.php
  50. 8
      framework/widgets/ActiveField.php
  51. 29
      tests/framework/grid/CheckboxColumnTest.php
  52. 9
      tests/framework/helpers/StringHelperTest.php
  53. 65
      tests/framework/rbac/ManagerTestCase.php
  54. 22
      tests/framework/validators/EachValidatorTest.php
  55. 11
      tests/framework/validators/FileValidatorTest.php
  56. 30
      tests/framework/validators/data/mimeType/test.svg

0
CONTRIBUTING.md → .github/CONTRIBUTING.md

14
.github/ISSUE_TEMPLATE.md

@ -0,0 +1,14 @@
### What steps will reproduce the problem?
### What is the expected result?
### What do you get instead?
### Additional info
| Q | A
| ---------------- | ---
| Yii version |
| PHP version |
| Operating system |

7
.github/PULL_REQUEST_TEMPLATE.md

@ -0,0 +1,7 @@
| Q | A
| ------------- | ---
| Is bugfix? | yes/no
| New feature? | yes/no
| Breaks BC? | yes/no
| Tests pass? | yes/no
| Fixed issues | comma-separated list of tickets # fixed by the PR, if any

3
build/controllers/DevController.php

@ -47,10 +47,13 @@ class DevController extends Controller
'elasticsearch' => 'git@github.com:yiisoft/yii2-elasticsearch.git',
'faker' => 'git@github.com:yiisoft/yii2-faker.git',
'gii' => 'git@github.com:yiisoft/yii2-gii.git',
'httpclient' => 'git@github.com:yiisoft/yii2-httpclient.git',
'imagine' => 'git@github.com:yiisoft/yii2-imagine.git',
'jui' => 'git@github.com:yiisoft/yii2-jui.git',
'mongodb' => 'git@github.com:yiisoft/yii2-mongodb.git',
'queue' => 'git@github.com:yiisoft/yii2-queue.git',
'redis' => 'git@github.com:yiisoft/yii2-redis.git',
'shell' => 'git@github.com:yiisoft/yii2-shell.git',
'smarty' => 'git@github.com:yiisoft/yii2-smarty.git',
'sphinx' => 'git@github.com:yiisoft/yii2-sphinx.git',
'swiftmailer' => 'git@github.com:yiisoft/yii2-swiftmailer.git',

90
build/controllers/PhpDocController.php

@ -72,7 +72,7 @@ class PhpDocController extends Controller
*/
public function actionFix($root = null)
{
$files = $this->findFiles($root);
$files = $this->findFiles($root, false);
$nFilesTotal = 0;
$nFilesUpdated = 0;
@ -108,9 +108,39 @@ class PhpDocController extends Controller
return array_merge(parent::options($actionID), ['updateFiles']);
}
protected function findFiles($root)
protected function findFiles($root, $needsInclude = true)
{
$except = [];
if ($needsInclude) {
$extensionExcept = [
'apidoc' => [
'/helpers/PrettyPrinter.php',
'/extensions/apidoc/helpers/ApiIndexer.php',
'/extensions/apidoc/helpers/ApiMarkdownLaTeX.php',
],
'codeception' => [
'/TestCase.php',
'/DbTestCase.php',
],
'gii' => [
'/components/DiffRendererHtmlInline.php',
'/generators/extension/default/AutoloadExample.php',
],
'swiftmailer' => [
'/Logger.php',
],
'twig' => [
'/Extension.php',
'/Optimizer.php',
'/Template.php',
'/TwigSimpleFileLoader.php',
'/ViewRendererStaticClassProxy.php',
],
];
} else {
$extensionExcept = [];
}
if ($root === null) {
$root = dirname(YII2_PATH);
$extensionPath = "$root/extensions";
@ -121,29 +151,49 @@ class PhpDocController extends Controller
}
$except = [
'.git/',
'/apps/',
'/build/',
'/docs/',
'/extensions/apidoc/helpers/PrettyPrinter.php',
'/extensions/apidoc/helpers/ApiIndexer.php',
'/extensions/apidoc/helpers/ApiMarkdownLaTeX.php',
'/extensions/codeception/TestCase.php',
'/extensions/codeception/DbTestCase.php',
'/extensions/composer/',
'/extensions/gii/components/DiffRendererHtmlInline.php',
'/extensions/gii/generators/extension/default/*',
'/extensions/twig/Extension.php',
'/extensions/twig/Optimizer.php',
'/extensions/twig/Template.php',
'/extensions/twig/TwigSimpleFileLoader.php',
'/extensions/twig/ViewRendererStaticClassProxy.php',
'/framework/BaseYii.php',
'/framework/Yii.php',
'assets/',
'tests/',
'vendor/',
];
foreach($extensionExcept as $ext => $paths) {
foreach($paths as $path) {
$except[] = "/extensions/$ext$path";
}
}
} elseif (preg_match('~extensions/([\w\d-]+)[\\\\/]?$~', $root, $matches)) {
$extensionPath = dirname(rtrim($root, '\\/'));
foreach (scandir($extensionPath) as $extension) {
if (ctype_alpha($extension) && is_dir($extensionPath . '/' . $extension)) {
Yii::setAlias("@yii/$extension", "$extensionPath/$extension");
}
}
list(, $extension) = $matches;
Yii::setAlias("@yii/$extension", "$root");
if (is_file($autoloadFile = Yii::getAlias("@yii/$extension/vendor/autoload.php"))) {
include($autoloadFile);
}
if (isset($extensionExcept[$extension])) {
foreach($extensionExcept[$extension] as $path) {
$except[] = $path;
}
}
$except[] = '/vendor/';
$except[] = '/tests/';
$except[] = '/docs/';
// // composer extension does not contain yii code
// if ($extension === 'composer') {
// return [];
// }
}
$root = FileHelper::normalizePath($root);
$options = [
@ -159,6 +209,7 @@ class PhpDocController extends Controller
},
'only' => ['*.php'],
'except' => array_merge($except, [
'.git/',
'views/',
'requirements/',
'gii/generators/',
@ -288,6 +339,7 @@ class PhpDocController extends Controller
$propertiesOnly = false;
// remove blank lines between properties
$skip = true;
$level = 0;
foreach($lines as $i => $line) {
if (strpos($line, 'class ') !== false) {
$skip = false;
@ -295,8 +347,16 @@ class PhpDocController extends Controller
if ($skip) {
continue;
}
// keep spaces in multi line arrays
if (strpos($line, '*') === false && strncmp(trim($line), "'SQLSTATE[", 10) !== 0) {
$level += substr_count($line, '[') - substr_count($line, ']');
}
if (trim($line) === '') {
if ($level == 0) {
unset($lines[$i]);
}
} elseif (ltrim($line)[0] !== '*' && strpos($line, 'function ') !== false) {
break;
} elseif (trim($line) === '}') {

498
build/controllers/ReleaseController.php

@ -11,6 +11,7 @@ use Yii;
use yii\base\Exception;
use yii\console\Controller;
use yii\helpers\ArrayHelper;
use yii\helpers\Console;
/**
* ReleaseController is there to help preparing releases
@ -20,44 +21,437 @@ use yii\helpers\ArrayHelper;
*/
class ReleaseController extends Controller
{
public $defaultAction = 'help';
public $defaultAction = 'release';
/**
* Usage:
*
* ```
* ./build/build release/prepare framework 2.0.0-beta
* ./build/build release/prepare redis 2.0.0-beta
* ```
*
* @var string base path to use for releases.
*/
public function actionPrepare(array $what, $version)
public $basePath;
/**
* @var bool whether to make actual changes. If true, it will run without changing or pushing anything.
*/
public $dryRun = false;
/**
* @var bool whether to fetch latest tags.
*/
public $update = false;
public function options($actionID)
{
$this->resortChangelogs($what, $version);
$this->closeChangelogs($what, $version);
$this->composerSetStability($what, $version);
if (in_array('framework', $what)) {
$this->updateYiiVersion($version);
$options = ['basePath'];
if ($actionID === 'release') {
$options[] = 'dryRun';
} elseif ($actionID === 'info') {
$options[] = 'update';
}
return array_merge(parent::options($actionID), $options);
}
public function beforeAction($action)
{
if (!$this->interactive) {
throw new Exception('Sorry, but releases should be run interactively to ensure you actually verify what you are doing ;)');
}
if ($this->basePath === null) {
$this->basePath = dirname(dirname(__DIR__));
}
$this->basePath = rtrim($this->basePath, '\\/');
return parent::beforeAction($action);
}
/**
* Shows information about current framework and extension versions.
*/
public function actionInfo()
{
$extensions = [
'framework',
];
$extensionPath = "{$this->basePath}/extensions";
foreach (scandir($extensionPath) as $extension) {
if (ctype_alpha($extension) && is_dir($extensionPath . '/' . $extension)) {
$extensions[] = $extension;
}
}
if ($this->update) {
foreach($extensions as $extension) {
if ($extension === 'framework') {
continue;
}
$this->stdout("fetching tags for $extension...");
$this->gitFetchTags("{$this->basePath}/extensions/$extension");
$this->stdout("done.\n", Console::FG_GREEN, Console::BOLD);
}
} else {
$this->stdout("\nInformation may be outdated, re-run with `--update` to fetch latest tags.\n\n");
}
$versions = $this->getCurrentVersions($extensions);
$nextVersions = $this->getNextVersions($versions, self::PATCH);
// print version table
$w = $this->minWidth(array_keys($versions));
$this->stdout(str_repeat(' ', $w + 2) . "Current Version Next Version\n", Console::BOLD);
foreach($versions as $ext => $version) {
$this->stdout($ext . str_repeat(' ', $w + 3 - mb_strlen($ext)) . $version . "");
$this->stdout(str_repeat(' ', 17 - mb_strlen($version)) . $nextVersions[$ext] . "\n");
}
}
private function minWidth($a)
{
$w = 1;
foreach($a as $s) {
if (($l = mb_strlen($s)) > $w) {
$w = $l;
}
}
return $w;
}
/**
* Automation tool for making Yii framework and official extension releases.
*
* Usage:
*
* To make a release, make sure your git is clean (no uncommitted changes) and run the following command in
* the yii dev repo root:
*
* ```
* ./build/build release framework
* ```
*
* or
*
* ```
* ./build/build release redis,bootstrap,apidoc
* ```
*
* You may use the `--dryRun` switch to test the command without changing or pushing anything:
*
* ```
* ./build/build release/done framework 2.0.0-dev 2.0.0-rc
* ./build/build release/done redis 2.0.0-dev 2.0.0-rc
* ./build/build release redis --dryRun
* ```
*
* The command will guide you through the complete release process including changing of files,
* committing and pushing them. Each git command must be confirmed and can be skipped individually.
* You may adjust changes in a separate shell or your IDE while the command is waiting for confirmation.
*
* @param array $what what do you want to release? this can either be an extension name such as `redis` or `bootstrap`,
* or `framework` if you want to release a new version of the framework itself.
* @return int
*/
public function actionDone(array $what, $devVersion, $nextVersion)
public function actionRelease(array $what)
{
$this->openChangelogs($what, $nextVersion);
$this->composerSetStability($what, 'dev');
if (in_array('framework', $what)) {
$this->updateYiiVersion($devVersion);
if (count($what) > 1) {
$this->stdout("Currently only one simultaneous release is supported.\n");
return 1;
}
$this->stdout("This is the Yii release manager\n\n", Console::BOLD);
if ($this->dryRun) {
$this->stdout("Running in \"dry-run\" mode, nothing will actually be changed.\n\n", Console::BOLD, Console::FG_GREEN);
}
$this->validateWhat($what);
$versions = $this->getCurrentVersions($what);
$newVersions = $this->getNextVersions($versions, self::PATCH);// TODO add support for minor
$this->stdout("You are about to prepare a new release for the following things:\n\n");
$this->printWhat($what, $newVersions, $versions);
$this->stdout("\n");
$this->stdout("Before you make a release briefly go over the changes and check if you spot obvious mistakes:\n\n", Console::BOLD);
$this->stdout("- no accidentally added CHANGELOG lines for other versions than this one?\n");
$this->stdout("- are all new `@since` tags for this relase version?\n");
$travisUrl = reset($what) === 'framework' ? '' : '-'.reset($what);
$this->stdout("- are unit tests passing on travis? https://travis-ci.org/yiisoft/yii2$travisUrl/builds\n");
$this->stdout("- other issues with code changes?\n");
$this->stdout("- also make sure the milestone on github is complete and no issues or PRs are left open.\n\n");
$this->printWhatUrls($what, $versions);
$this->stdout("\n");
if (!$this->confirm('When you continue, this tool will run cleanup jobs and update the changelog as well as other files (locally). Continue?', false)) {
$this->stdout("Canceled.\n");
return 1;
}
foreach($what as $ext) {
if ($ext === 'framework') {
$this->releaseFramework("{$this->basePath}/framework", $newVersions['framework']);
} else {
$this->releaseExtension($ext, "{$this->basePath}/extensions/$ext", $newVersions[$ext]);
}
}
return 0;
}
protected function printWhat(array $what, $newVersions, $versions)
{
foreach($what as $ext) {
if ($ext === 'framework') {
$this->stdout(" - Yii Framework version ");
} else {
$this->stdout(" - ");
$this->stdout($ext, Console::FG_RED);
$this->stdout(" extension version ");
}
$this->stdout($newVersions[$ext], Console::BOLD);
$this->stdout(", last release was {$versions[$ext]}\n");
}
}
protected function printWhatUrls(array $what, $oldVersions)
{
foreach($what as $ext) {
if ($ext === 'framework') {
$this->stdout("framework: https://github.com/yiisoft/yii2-framework/compare/{$oldVersions[$ext]}...master\n");
$this->stdout("app-basic: https://github.com/yiisoft/yii2-app-basic/compare/{$oldVersions[$ext]}...master\n");
$this->stdout("app-advanced: https://github.com/yiisoft/yii2-app-advanced/compare/{$oldVersions[$ext]}...master\n");
} else {
$this->stdout($ext, Console::FG_RED);
$this->stdout(": https://github.com/yiisoft/yii2-$ext/compare/{$oldVersions[$ext]}...master\n");
}
}
}
protected function validateWhat(array $what)
{
foreach($what as $w) {
if ($w === 'framework') {
if (!is_dir($fwPath = "{$this->basePath}/framework")) {
throw new Exception("Framework path does not exist: \"{$this->basePath}/framework\"\n");
}
$this->ensureGitClean($fwPath);
} else {
if (!is_dir($extPath = "{$this->basePath}/extensions/$w")) {
throw new Exception("Extension path for \"$w\" does not exist: \"{$this->basePath}/extensions/$w\"\n");
}
$this->ensureGitClean($extPath);
}
}
}
protected function releaseFramework($frameworkPath, $version)
{
throw new Exception('NOT IMPLEMENTED COMPLETELY YET');
$this->stdout("\n");
$this->stdout($h = "Preparing framework release version $version", Console::BOLD);
$this->stdout("\n" . str_repeat('-', strlen($h)) . "\n\n", Console::BOLD);
if ($this->confirm('Run `git checkout master`?', true)) {
chdir($frameworkPath);
exec('git checkout master', $output, $ret); // TODO add compatibility for other release branches
if ($ret != 0) {
throw new Exception('Command "git checkout master" failed with code ' . $ret);
}
}
if ($this->confirm('Run `git pull`?', true)) {
chdir($frameworkPath);
exec('git pull', $output, $ret);
if ($ret != 0) {
throw new Exception('Command "git pull" failed with code ' . $ret);
}
}
// checks
$this->stdout('check if framework composer.json matches yii2-dev composer.json...');
$this->checkComposer($frameworkPath);
$this->stdout("done.\n", Console::FG_GREEN, Console::BOLD);
// adjustments
$this->stdout('prepare classmap...');
$this->dryRun || Yii::$app->runAction('classmap', [$frameworkPath]);
$this->stdout("done.\n", Console::FG_GREEN, Console::BOLD);
$this->stdout('fixing various PHPdoc style issues...');
$this->dryRun || Yii::$app->runAction('php-doc/fix', [$frameworkPath]);
$this->stdout("done.\n", Console::FG_GREEN, Console::BOLD);
$this->stdout('updating PHPdoc @property annotations...');
$this->dryRun || Yii::$app->runAction('php-doc/property', [$frameworkPath]);
$this->stdout("done.\n", Console::FG_GREEN, Console::BOLD);
$this->stdout('updating mimetype magic file...');
$this->dryRun || Yii::$app->runAction('mime-type', ["$frameworkPath/helpers/mimeTypes.php"]);
$this->stdout("done.\n", Console::FG_GREEN, Console::BOLD);
$this->stdout('sorting changelogs...');
$this->dryRun || $this->resortChangelogs(['framework'], $version);
$this->stdout("done.\n", Console::FG_GREEN, Console::BOLD);
$this->stdout('closing changelogs...');
$this->dryRun || $this->closeChangelogs(['framework'], $version);
$this->stdout("done.\n", Console::FG_GREEN, Console::BOLD);
$this->stdout('updating Yii version...');
$this->dryRun || $this->updateYiiVersion($frameworkPath, $version);;
$this->stdout("done.\n", Console::FG_GREEN, Console::BOLD);
// TODO Commit and push
// TODO tag and push
// TODO release applications
// $this->composerSetStability($what, $version);
// $this->resortChangelogs($what, $version);
// $this->closeChangelogs($what, $version);
// $this->composerSetStability($what, $version);
// if (in_array('framework', $what)) {
// $this->updateYiiVersion($version);
// }
// if done:
// * ./build/build release/done framework 2.0.0-dev 2.0.0-rc
// * ./build/build release/done redis 2.0.0-dev 2.0.0-rc
// $this->openChangelogs($what, $nextVersion);
// $this->composerSetStability($what, 'dev');
// if (in_array('framework', $what)) {
// $this->updateYiiVersion($devVersion);
// }
}
protected function releaseExtension($name, $path, $version)
{
$this->stdout("\n");
$this->stdout($h = "Preparing release for extension $name version $version", Console::BOLD);
$this->stdout("\n" . str_repeat('-', strlen($h)) . "\n\n", Console::BOLD);
$this->runGit('git checkout master', $path); // TODO add compatibility for other release branches
$this->runGit('git pull', $path); // TODO add compatibility for other release branches
// adjustments
$this->stdout("fixing various PHPdoc style issues...\n", Console::BOLD);
$this->dryRun || Yii::$app->runAction('php-doc/fix', [$path]);
$this->stdout("done.\n", Console::FG_GREEN, Console::BOLD);
$this->stdout("updating PHPdoc @property annotations...\n", Console::BOLD);
$this->dryRun || Yii::$app->runAction('php-doc/property', [$path]);
$this->stdout("done.\n", Console::FG_GREEN, Console::BOLD);
$this->stdout('sorting changelogs...', Console::BOLD);
$this->dryRun || $this->resortChangelogs([$name], $version);
$this->stdout("done.\n", Console::FG_GREEN, Console::BOLD);
$this->stdout('closing changelogs...', Console::BOLD);
$this->dryRun || $this->closeChangelogs([$name], $version);
$this->stdout("done.\n", Console::FG_GREEN, Console::BOLD);
$this->stdout("\nIn the following you can check the above changes using git diff.\n\n");
do {
$this->runGit("git diff --color", $path);
$this->stdout("\n\n\nCheck whether the above diff is okay, if not you may change things as needed before continuing.\n");
$this->stdout("You may abort the program with Ctrl + C and reset the changes by running `git checkout -- .` in the repo.\n\n");
} while(!$this->confirm("Type `yes` to continue, `no` to view git diff again. Continue?"));
$this->stdout("\n\n");
$this->stdout(" **** RELEASE TIME! ****\n", Console::FG_YELLOW, Console::BOLD);
$this->stdout(" **** Commit, Tag and Push it! ****\n", Console::FG_YELLOW, Console::BOLD);
$this->stdout("\n\nHint: if you decide 'no' for any of the following, the command will not be executed. You may manually run them later if needed. E.g. try the release locally without pushing it.\n\n");
$this->runGit("git commit -a -m \"release version $version\"", $path);
$this->runGit("git tag -a $version -m\"version $version\"", $path);
$this->runGit("git push origin master", $path);
$this->runGit("git push --tags", $path);
$this->stdout("\n\n");
$this->stdout("CONGRATULATIONS! You have just released extension ", Console::FG_YELLOW, Console::BOLD);
$this->stdout($name, Console::FG_RED, Console::BOLD);
$this->stdout(" version ", Console::FG_YELLOW, Console::BOLD);
$this->stdout($version, Console::BOLD);
$this->stdout("!\n\n", Console::FG_YELLOW, Console::BOLD);
// prepare next release
$this->stdout("Time to prepare the next release...\n\n", Console::FG_YELLOW, Console::BOLD);
$this->stdout('opening changelogs...', Console::BOLD);
$nextVersion = $this->getNextVersions([$name => $version], self::PATCH); // TODO support other versions
$this->dryRun || $this->openChangelogs([$name], $nextVersion[$name]);
$this->stdout("done.\n", Console::FG_GREEN, Console::BOLD);
$this->stdout("\n");
$this->runGit("git diff --color", $path);
$this->stdout("\n\n");
$this->runGit("git commit -a -m \"prepare for next release\"", $path);
$this->runGit("git push origin master", $path);
$this->stdout("\n\nDONE!", Console::FG_YELLOW, Console::BOLD);
$this->stdout("\n\nThe following steps are left for you to do manually:\n\n");
$nextVersion2 = $this->getNextVersions($nextVersion, self::PATCH); // TODO support other versions
$this->stdout("- close the $version milestone on github and open new ones for {$nextVersion[$name]} and {$nextVersion2[$name]}: https://github.com/yiisoft/yii2-$name/milestones\n");
$this->stdout("- release news and announcement.\n");
$this->stdout("- update the website (will be automated soon and is only relevant for the new website).\n");
$this->stdout("\n");
}
protected function runGit($cmd, $path)
{
if ($this->confirm("Run `$cmd`?", true)) {
if ($this->dryRun) {
$this->stdout("dry run, command `$cmd` not executed.\n");
return;
}
chdir($path);
exec($cmd, $output, $ret);
echo implode("\n", $output);
if ($ret != 0) {
throw new Exception("Command \"$cmd\" failed with code " . $ret);
}
echo "\n";
}
}
protected function ensureGitClean($path)
{
chdir($path);
exec('git status --porcelain -uno', $changes, $ret);
if ($ret != 0) {
throw new Exception('Command "git status --porcelain -uno" failed with code ' . $ret);
}
if (!empty($changes)) {
throw new Exception("You have uncommitted changes in $path: " . print_r($changes, true));
}
}
protected function gitFetchTags($path)
{
chdir($path);
exec('git fetch --tags', $output, $ret);
if ($ret != 0) {
throw new Exception('Command "git fetch --tags" failed with code ' . $ret);
}
}
protected function checkComposer($fwPath)
{
if (!$this->confirm("\nNot yet automated: Please check if composer.json dependencies in framework dir match the on in repo root. Continue?", false)) {
exit;
}
}
protected function closeChangelogs($what, $version)
{
$v = str_replace('\\-', '[\\- ]', preg_quote($version, '/'));
@ -72,7 +466,7 @@ class ReleaseController extends Controller
protected function openChangelogs($what, $version)
{
$headline = "\n$version under development\n";
$headline .= str_repeat('-', strlen($headline) - 2) . "\n\n";
$headline .= str_repeat('-', strlen($headline) - 2) . "\n\n- no changes in this release.\n";
foreach($this->getChangelogs($what) as $file) {
$lines = explode("\n", file_get_contents($file));
$hl = [
@ -162,12 +556,12 @@ class ReleaseController extends Controller
protected function getFrameworkChangelog()
{
return YII2_PATH . '/CHANGELOG.md';
return $this->basePath . '/framework/CHANGELOG.md';
}
protected function getExtensionChangelogs($what)
{
return array_filter(glob(dirname(YII2_PATH) . '/extensions/*/CHANGELOG.md'), function($elem) use ($what) {
return array_filter(glob($this->basePath . '/extensions/*/CHANGELOG.md'), function($elem) use ($what) {
foreach($what as $ext) {
if (strpos($elem, "extensions/$ext/CHANGELOG.md") !== false) {
return true;
@ -181,13 +575,13 @@ class ReleaseController extends Controller
{
$apps = [];
if (in_array('app-advanced', $what)) {
$apps[] = dirname(YII2_PATH) . '/apps/advanced/composer.json';
$apps[] = $this->basePath . '/apps/advanced/composer.json';
}
if (in_array('app-basic', $what)) {
$apps[] = dirname(YII2_PATH) . '/apps/basic/composer.json';
$apps[] = $this->basePath . '/apps/basic/composer.json';
}
if (in_array('app-benchmark', $what)) {
$apps[] = dirname(YII2_PATH) . '/apps/benchmark/composer.json';
$apps[] = $this->basePath . '/apps/benchmark/composer.json';
}
if (empty($apps)) {
return;
@ -211,12 +605,12 @@ class ReleaseController extends Controller
);
}
protected function updateYiiVersion($version)
protected function updateYiiVersion($frameworkPath, $version)
{
$this->sed(
'/function getVersion\(\)\n \{\n return \'(.+?)\';/',
"function getVersion()\n {\n return '$version';",
YII2_PATH . '/BaseYii.php');
$frameworkPath . '/BaseYii.php');
}
protected function sed($pattern, $replace, $files)
@ -225,4 +619,50 @@ class ReleaseController extends Controller
file_put_contents($file, preg_replace($pattern, $replace, file_get_contents($file)));
}
}
protected function getCurrentVersions(array $what)
{
$versions = [];
foreach($what as $ext) {
if ($ext === 'framework') {
chdir("{$this->basePath}/framework");
} else {
chdir("{$this->basePath}/extensions/$ext");
}
$tags = [];
exec('git tag', $tags, $ret);
if ($ret != 0) {
throw new Exception('Command "git tag" failed with code ' . $ret);
}
rsort($tags, SORT_NATURAL); // TODO this can not deal with alpha/beta/rc...
$versions[$ext] = reset($tags);
}
return $versions;
}
const MINOR = 'minor';
const PATCH = 'patch';
protected function getNextVersions(array $versions, $type)
{
foreach($versions as $k => $v) {
if (empty($v)) {
$versions[$k] = '2.0.0';
continue;
}
$parts = explode('.', $v);
switch($type) {
case self::MINOR:
$parts[1]++;
break;
case self::PATCH:
$parts[2]++;
break;
default:
throw new Exception('Unknown version type.');
}
$versions[$k] = implode('.', $parts);
}
return $versions;
}
}

2
docs/guide-ja/concept-behaviors.md

@ -288,6 +288,8 @@ class User extends ActiveRecord
* 挿入されるとき、ビヘイビアは現在の UNIX タイムスタンプを `created_at``updated_at` 属性に割り当てます
* 更新されるとき、ビヘイビアは現在の UNIX タイムスタンプを `updated_at` 属性に割り当てます
> Note: 上記の実装が MySQL データベースで動作するようにするためには、`created_at` と `updated_at` のカラムを UNIX タイムスタンプ になるように int(11) として宣言してください。
このコードが所定の位置にあれば、例えば `User` オブジェクトがあって、それを保存しようとしたら、そこで、
`created_at``updated_at` が自動的に現在の UNIX タイムスタンプで埋められます。

6
docs/guide-ja/helper-array.md

@ -129,6 +129,7 @@ $result = ArrayHelper::index($array, 'id');
```
結果は、`id` 属性の値をキーとする連想配列になります。
```php
[
'123' => ['id' => '123', 'data' => 'abc', 'device' => 'laptop'],
@ -138,6 +139,7 @@ $result = ArrayHelper::index($array, 'id');
```
`$key` として無名関数を渡しても同じ結果になります。
```php
$result = ArrayHelper::index($array, function ($element) {
return $element['id'];
@ -145,11 +147,13 @@ $result = ArrayHelper::index($array, function ($element) {
```
`id` を3番目の引数として渡すと、`$array` を `id` によってグループ化することが出来ます。
```php
$result = ArrayHelper::index($array, null, 'id');
```
結果は、最初のレベルが `id` でグループ化され、第2のレベルはインデックスされていない連想配列になります。
```php
[
'123' => [
@ -163,6 +167,7 @@ $result = ArrayHelper::index($array, null, 'id');
```
無名関数を配列のグループ化に使うことも出来ます。
```php
$result = ArrayHelper::index($array, 'data', [function ($element) {
return $element['id'];
@ -170,6 +175,7 @@ $result = ArrayHelper::index($array, 'data', [function ($element) {
```
結果は、最初のレベルが `id` でグループ化され、第2のレベルが `device` でグループ化され、第3のレベルが `data` でインデックスされた連想配列になります。
```php
[
'123' => [

26
docs/guide-ja/helper-url.md

@ -45,14 +45,14 @@ $url = Url::toRoute(['product/view', 'id' => 42]);
配列の形式は、以下のようにしなければなりません。
```php
// /index.php?r=site/index&param1=value1&param2=value2 を生成
// /index.php?r=site%2Findex&param1=value1&param2=value2 を生成
['site/index', 'param1' => 'value1', 'param2' => 'value2']
```
アンカーの付いた URL を生成したい場合は、`#` パラメータを持つ配列を使うことが出来ます。例えば、
```php
// /index.php?r=site/index&param1=value1#name を生成
// /index.php?r=site%2Findex&param1=value1#name を生成
['site/index', 'param1' => 'value1', '#' => 'name']
```
@ -70,19 +70,19 @@ $url = Url::toRoute(['product/view', 'id' => 42]);
以下に、このメソッドの使用例をいくつか挙げます。
```php
// /index.php?r=site/index
// /index.php?r=site%2Findex
echo Url::toRoute('site/index');
// /index.php?r=site/index&src=ref1#name
// /index.php?r=site%2Findex&src=ref1#name
echo Url::toRoute(['site/index', 'src' => 'ref1', '#' => 'name']);
// /index.php?r=post/edit&id=100 エイリアス "@postEdit" は "post/edit" と定義されていると仮定
// /index.php?r=post%2Fedit&id=100 エイリアス "@postEdit" は "post/edit" と定義されていると仮定
echo Url::toRoute(['@postEdit', 'id' => 100]);
// http://www.example.com/index.php?r=site/index
// http://www.example.com/index.php?r=site%2Findex
echo Url::toRoute('site/index', true);
// https://www.example.com/index.php?r=site/index
// https://www.example.com/index.php?r=site%2Findex
echo Url::toRoute('site/index', 'https');
```
@ -104,13 +104,13 @@ echo Url::toRoute('site/index', 'https');
下記にいくつかの用例を挙げます。
```php
// /index.php?r=site/index
// /index.php?r=site%2Findex
echo Url::to(['site/index']);
// /index.php?r=site/index&src=ref1#name
// /index.php?r=site%2Findex&src=ref1#name
echo Url::to(['site/index', 'src' => 'ref1', '#' => 'name']);
// /index.php?r=post/edit&id=100 エイリアス "@postEdit" が "post/edit" と定義されていると仮定
// /index.php?r=post%2Fedit&id=100 エイリアス "@postEdit" が "post/edit" と定義されていると仮定
echo Url::to(['@postEdit', 'id' => 100]);
// 現在リクエストされている URL
@ -136,12 +136,12 @@ echo Url::to('@web/images/logo.gif', 'https');
```php
// $_GET が ['id' => 123, 'src' => 'google'] であり、現在のルートが "post/view" であると仮定
// /index.php?r=post/view&id=123&src=google
// /index.php?r=post%2Fview&id=123&src=google
echo Url::current();
// /index.php?r=post/view&id=123
// /index.php?r=post%2Fview&id=123
echo Url::current(['src' => null]);
// /index.php?r=post/view&id=100&src=google
// /index.php?r=post%2Fview&id=100&src=google
echo Url::current(['id' => 100]);
```

4
docs/guide-ja/output-pagination.md

@ -63,10 +63,10 @@ UI 要素を手動で構築したい場合は、[[yii\data\Pagination::createUrl
// 指定しない場合は、現在リクエストされているルートが使用される
$pagination->route = 'article/index';
// /index.php?r=article/index&page=100 を表示
// /index.php?r=article%2Findex&page=100 を表示
echo $pagination->createUrl(100);
// /index.php?r=article/index&page=101 を表示
// /index.php?r=article%2Findex&page=101 を表示
echo $pagination->createUrl(101);
```

2
docs/guide-ja/output-sorting.md

@ -80,7 +80,7 @@ $sort->route = 'article/index';
// 氏名による並べ替えと年齢による並べ替えを実行するリンクを表示
echo $sort->link('name') . ' | ' . $sort->link('age');
// /index.php?r=article/index&sort=age を表示
// /index.php?r=article%2Findex&sort=age を表示
echo $sort->createUrl('age');
```

6
docs/guide-ja/runtime-handling-errors.md

@ -137,6 +137,12 @@ public function actionError()
> Info: あなたが [ベーシックプロジェクトテンプレート](start-installation.md) または [アドバンストプロジェクトテンプレート](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide-ja/README.md) を使っている場合は、エラーアクションとエラービューは、既にあなたのために定義されています。
> Note: エラーハンドラの中でリダイレクトする必要がある場合は、次のようにしてください。
> ```php
> Yii::$app->getResponse()->redirect($url)->send();
> return;
> ```
### エラーのレスポンス形式をカスタマイズする <span id="error-format"></span>

26
docs/guide-ja/runtime-routing.md

@ -26,7 +26,7 @@ $url = Url::to(['post/view', 'id' => 100]);
そして、こうして生成された URL が後でリクエストされた場合には、解析されて元のルートとクエリパラメータの値に戻されます。
```
/index.php?r=post/view&id=100
/index.php?r=post%2Fview&id=100
/index.php/post/100
/posts/100
```
@ -117,19 +117,19 @@ Yii は、与えられたルートとそれに結び付けられたクエリパ
```php
use yii\helpers\Url;
// ルートへの URL を生成する: /index.php?r=post/index
// ルートへの URL を生成する: /index.php?r=post%2Findex
echo Url::to(['post/index']);
// パラメータを持つルートへの URL を生成する: /index.php?r=post/view&id=100
// パラメータを持つルートへの URL を生成する: /index.php?r=post%2Fview&id=100
echo Url::to(['post/view', 'id' => 100]);
// アンカー付きの URL を生成する: /index.php?r=post/view&id=100#content
// アンカー付きの URL を生成する: /index.php?r=post%2Fview&id=100#content
echo Url::to(['post/view', 'id' => 100, '#' => 'content']);
// 絶対 URL を生成する: http://www.example.com/index.php?r=post/index
// 絶対 URL を生成する: http://www.example.com/index.php?r=post%2Findex
echo Url::to(['post/index'], true);
// https スキームを使って絶対 URL を生成する: https://www.example.com/index.php?r=post/index
// https スキームを使って絶対 URL を生成する: https://www.example.com/index.php?r=post%2Findex
echo Url::to(['post/index'], 'https');
```
@ -151,19 +151,19 @@ echo Url::to(['post/index'], 'https');
```php
use yii\helpers\Url;
// 現在リクエストされているルート: /index.php?r=admin/post/index
// 現在リクエストされているルート: /index.php?r=admin%2Fpost%2Findex
echo Url::to(['']);
// アクション ID だけの相対ルート: /index.php?r=admin/post/index
// アクション ID だけの相対ルート: /index.php?r=admin%2Fpost%2Findex
echo Url::to(['index']);
// 相対ルート: /index.php?r=admin/post/index
// 相対ルート: /index.php?r=admin%2Fpost%2Findex
echo Url::to(['post/index']);
// 絶対ルート: /index.php?r=post/index
// 絶対ルート: /index.php?r=post%2Findex
echo Url::to(['/post/index']);
// /index.php?r=post/index エイリアス "@posts" が "/post/index" と定義されていると仮定
// /index.php?r=post%2Findex エイリアス "@posts" が "/post/index" と定義されていると仮定
echo Url::to(['@posts']);
```
@ -176,7 +176,7 @@ echo Url::to(['@posts']);
```php
use yii\helpers\Url;
// 現在リクエストされている URL: /index.php?r=admin/post/index
// 現在リクエストされている URL: /index.php?r=admin%2Fpost%2Findex
echo Url::to();
// エイリアス化された URL: http://example.com
@ -193,7 +193,7 @@ echo Url::to('/images/logo.gif', true);
```php
use yii\helpers\Url;
// ホームページの URL: /index.php?r=site/index
// ホームページの URL: /index.php?r=site%2Findex
echo Url::home();
// ベース URL。アプリケーションがウェブルートのサブディレクトリに配置されているときに便利

5
docs/guide-ja/security-authorization.md

@ -190,6 +190,7 @@ Yii は二種類の権限付与マネージャを提供しています。すな
前者は権限付与データを保存するのに PHP スクリプトファイルを使いますが、後者は権限付与データをデータベースに保存します。
あなたのアプリケーションが非常に動的なロールと許可の管理を必要とするのでなければ、前者を使うことを考慮するのが良いでしょう。
#### `PhpManager` を使用する <span id="using-php-manager"></span>
次のコードは、アプリケーションの構成情報で [[yii\rbac\PhpManager]] クラスを使って `authManager` を構成する方法を示すものです。
@ -211,6 +212,7 @@ return [
デフォルトでは、[[yii\rbac\PhpManager]] は RBAC データを `@app/rbac/` ディレクトリの下のファイルに保存します。
権限の階層をオンラインで変更する必要がある場合は、必ず、ウェブサーバのプロセスがこのディレクトリとその中の全てのファイルに対する書き込み権限を有するようにしてください。
#### `DbManager` を使用する <span id="using-db-manager"></span>
次のコードは、アプリケーションの構成情報で [[yii\rbac\DbManager]] クラスを使って `authManager` を構成する方法を示すものです。
@ -228,6 +230,9 @@ return [
];
```
> Note: yii2-basic-app テンプレートを使おうとする場合は、`config/web.php` に加えて、`config/console.php` 構成ファイルにおいても `uathManager` を宣言する必要があります。
> yii2-advanced-app の場合は、`authManager` は `common/config/main.php` で一度だけ宣言されなければなりません。
`DbManager` は四つのデータベーステーブルを使ってデータを保存します。
- [[yii\rbac\DbManager::$itemTable|itemTable]]: 権限アイテムを保存するためのテーブル。デフォルトは "auth_item"。

4
docs/guide-ja/start-databases.md

@ -221,7 +221,7 @@ use yii\widgets\LinkPager;
上記のコード全てがどのように動作するかを見るために、ブラウザで下記の URL をアクセスします。
```
http://hostname/index.php?r=country/index
http://hostname/index.php?r=country%2Findex
```
![国リスト](images/start-country-list.png)
@ -232,7 +232,7 @@ http://hostname/index.php?r=country/index
注意深く観察すると、ブラウザの URL も次のように変ったことに気付くでしょう。
```
http://hostname/index.php?r=country/index&page=2
http://hostname/index.php?r=country%2Findex&page=2
```
舞台裏では、[[yii\data\Pagination|Pagination]] が、データセットをページ付けするのに必要な全ての機能を提供しています。

2
docs/guide-ja/start-forms.md

@ -181,7 +181,7 @@ use yii\widgets\ActiveForm;
どのように動作するかを見るために、ブラウザで下記の URL にアクセスしてください。
```
http://hostname/index.php?r=site/entry
http://hostname/index.php?r=site%2Fentry
```
二つのインプットフィールドを持つフォームを表示するページが表示されるでしょう。

2
docs/guide-ja/start-gii.md

@ -114,7 +114,7 @@ Gii を使って CRUD 機能を作成するためには、"CRUD Generator" を
どのように動作するかを見るために、ブラウザを使って下記の URL にアクセスしてください。
```
http://hostname/index.php?r=country/index
http://hostname/index.php?r=country%2Findex
```
データグリッドがデータベーステーブルから取得した国を表示しているページが表示されます。

2
docs/guide-ja/start-hello.md

@ -100,7 +100,7 @@ use yii\helpers\Html;
アクションとビューを作成したら、下記の URL で新しいページにアクセスすることが出来ます。
```
http://hostname/index.php?r=site/say&message=Hello+World
http://hostname/index.php?r=site%2Fsay&message=Hello+World
```
![Hello World](images/start-hello-world.png)

2
docs/guide-ja/start-workflow.md

@ -32,7 +32,7 @@ Yii のインストールが終ると、実際に動く Yii のアプリケー
これは Yii によって提供される便利な [デバッグツールバー](https://github.com/yiisoft/yii2-debug/blob/master/docs/guide-ja/README.md) であり、たくさんのデバッグ情報、例えば、ログメッセージ、レスポンスのステータス、実行されたデータベースクエリなどを記録して表示するものです。
ウェブアプリケーションに加えて、`yii` というコンソールスクリプトがアプリケーションのベースディレクトリにあります。
このスクリプトバックグラウンドのタスクまたはメンテナンスのタスクを実行するために使用することが出来ます。
このスクリプトは、バックグラウンドのタスクまたはメンテナンスのタスクを実行するために使用することが出来ます。
これについては、[コンソールアプリケーションの節](tutorial-console.md) で説明されています。
アプリケーションの構造 <span id="application-structure"></span>

2
docs/guide-ja/structure-views.md

@ -651,7 +651,7 @@ class SiteController extends Controller
このようにすると、ディレクトリ `@app/views/site/pages` の下に `about` という名前のビューを作成したときに、次の URL によってこのビューを表示することが出来るようになります。
```
http://localhost/index.php?r=site/page&view=about
http://localhost/index.php?r=site%2Fpage&view=about
```
`view` という `GET` パラメータが、どのビューがリクエストされているかを [[yii\web\ViewAction]] に教えます。

39
docs/guide-ja/tutorial-console.md

@ -124,6 +124,45 @@ exit($exitCode);
オプションのデフォルト値が配列型である場合、実行時にこのオプションをセットすると、オプションの値は、入力文字列をカンマで分離することによって、配列に変換されます。
### オプションのエイリアス
バージョン 2.0.8 以降、コンソールコマンドは、コマンドにエイリアスを追加するための [[yii\console\Controller::optionAliases()]] メソッドを提供しています。
エイリアスを定義するためには、コントローラで [[yii\console\Controller::optionAliases()]] をオーバーライドします。
例えば、
```php
namespace app\commands;
use yii\console\Controller;
class HelloController extends Controller
{
public $message;
public function options()
{
return ['message'];
}
public function optionAliases()
{
return ['m' => 'message'];
}
public function actionIndex()
{
echo $message . "\n";
}
}
```
これで、次の構文を使ってコマンドを走らせることが出来るようになります。
```
./yii hello -m=hola
```
### 引数
オプションに加えてに、コマンドは引数を取ることも出来ます。

1
docs/guide-ja/tutorial-i18n.md

@ -755,6 +755,7 @@ ICU のバージョンが異なると、日付や数値のフォーマットの
<?php
echo "PHP: " . PHP_VERSION . "\n";
echo "ICU: " . INTL_ICU_VERSION . "\n";
echo "ICU Data: " . INTL_ICU_DATA_VERSION . "\n";
```
さらに、バージョン 49 以上の ICU を使用する事も推奨されます。

2
docs/guide-ru/db-dao.md

@ -313,7 +313,7 @@ Yii::$app->db->transaction(function($db) {
});
```
Код выше эквивалентен приведённму ниже. Разница в том, что в данном случае мы получаем больше контроля над обработкой
Код выше эквивалентен приведённому ниже. Разница в том, что в данном случае мы получаем больше контроля над обработкой
ошибок:
```php

26
docs/guide/helper-url.md

@ -42,14 +42,14 @@ You may specify the route as a string, e.g., `site/index`. You may also use an a
query parameters for the URL being created. The array format must be:
```php
// generates: /index.php?r=site/index&param1=value1&param2=value2
// generates: /index.php?r=site%2Findex&param1=value1&param2=value2
['site/index', 'param1' => 'value1', 'param2' => 'value2']
```
If you want to create a URL with an anchor, you can use the array format with a `#` parameter. For example,
```php
// generates: /index.php?r=site/index&param1=value1#name
// generates: /index.php?r=site%2Findex&param1=value1#name
['site/index', 'param1' => 'value1', '#' => 'name']
```
@ -69,19 +69,19 @@ to the above rules.
Below are some examples of using this method:
```php
// /index.php?r=site/index
// /index.php?r=site%2Findex
echo Url::toRoute('site/index');
// /index.php?r=site/index&src=ref1#name
// /index.php?r=site%2Findex&src=ref1#name
echo Url::toRoute(['site/index', 'src' => 'ref1', '#' => 'name']);
// /index.php?r=post/edit&id=100 assume the alias "@postEdit" is defined as "post/edit"
// /index.php?r=post%2Fedit&id=100 assume the alias "@postEdit" is defined as "post/edit"
echo Url::toRoute(['@postEdit', 'id' => 100]);
// http://www.example.com/index.php?r=site/index
// http://www.example.com/index.php?r=site%2Findex
echo Url::toRoute('site/index', true);
// https://www.example.com/index.php?r=site/index
// https://www.example.com/index.php?r=site%2Findex
echo Url::toRoute('site/index', 'https');
```
@ -105,13 +105,13 @@ will be replaced with the specified one.
Below are some usage examples:
```php
// /index.php?r=site/index
// /index.php?r=site%2Findex
echo Url::to(['site/index']);
// /index.php?r=site/index&src=ref1#name
// /index.php?r=site%2Findex&src=ref1#name
echo Url::to(['site/index', 'src' => 'ref1', '#' => 'name']);
// /index.php?r=post/edit&id=100 assume the alias "@postEdit" is defined as "post/edit"
// /index.php?r=post%2Fedit&id=100 assume the alias "@postEdit" is defined as "post/edit"
echo Url::to(['@postEdit', 'id' => 100]);
// the currently requested URL
@ -137,12 +137,12 @@ passing a `$params` parameter to the method. For example,
```php
// assume $_GET = ['id' => 123, 'src' => 'google'], current route is "post/view"
// /index.php?r=post/view&id=123&src=google
// /index.php?r=post%2Fview&id=123&src=google
echo Url::current();
// /index.php?r=post/view&id=123
// /index.php?r=post%2Fview&id=123
echo Url::current(['src' => null]);
// /index.php?r=post/view&id=100&src=google
// /index.php?r=post%2Fview&id=100&src=google
echo Url::current(['id' => 100]);
```

4
docs/guide/output-pagination.md

@ -61,10 +61,10 @@ containing the page parameter. For example,
// If you do not specify this, the currently requested route will be used
$pagination->route = 'article/index';
// displays: /index.php?r=article/index&page=100
// displays: /index.php?r=article%2Findex&page=100
echo $pagination->createUrl(100);
// displays: /index.php?r=article/index&page=101
// displays: /index.php?r=article%2Findex&page=101
echo $pagination->createUrl(101);
```

2
docs/guide/output-sorting.md

@ -80,7 +80,7 @@ $sort->route = 'article/index';
// display links leading to sort by name and age, respectively
echo $sort->link('name') . ' | ' . $sort->link('age');
// displays: /index.php?r=article/index&sort=age
// displays: /index.php?r=article%2Findex&sort=age
echo $sort->createUrl('age');
```

26
docs/guide/runtime-routing.md

@ -30,7 +30,7 @@ Depending on the `urlManager` configuration, the created URL may look like one o
And if the created URL is requested later, it will still be parsed back into the original route and query parameter value.
```
/index.php?r=post/view&id=100
/index.php?r=post%2Fview&id=100
/index.php/post/100
/posts/100
```
@ -133,19 +133,19 @@ their associated query parameters. For example,
```php
use yii\helpers\Url;
// creates a URL to a route: /index.php?r=post/index
// creates a URL to a route: /index.php?r=post%2Findex
echo Url::to(['post/index']);
// creates a URL to a route with parameters: /index.php?r=post/view&id=100
// creates a URL to a route with parameters: /index.php?r=post%2Fview&id=100
echo Url::to(['post/view', 'id' => 100]);
// creates an anchored URL: /index.php?r=post/view&id=100#content
// creates an anchored URL: /index.php?r=post%2Fview&id=100#content
echo Url::to(['post/view', 'id' => 100, '#' => 'content']);
// creates an absolute URL: http://www.example.com/index.php?r=post/index
// creates an absolute URL: http://www.example.com/index.php?r=post%2Findex
echo Url::to(['post/index'], true);
// creates an absolute URL using the https scheme: https://www.example.com/index.php?r=post/index
// creates an absolute URL using the https scheme: https://www.example.com/index.php?r=post%2Findex
echo Url::to(['post/index'], 'https');
```
@ -170,19 +170,19 @@ For example, assume the current module is `admin` and the current controller is
```php
use yii\helpers\Url;
// currently requested route: /index.php?r=admin/post/index
// currently requested route: /index.php?r=admin%2Fpost%2Findex
echo Url::to(['']);
// a relative route with action ID only: /index.php?r=admin/post/index
// a relative route with action ID only: /index.php?r=admin%2Fpost%2Findex
echo Url::to(['index']);
// a relative route: /index.php?r=admin/post/index
// a relative route: /index.php?r=admin%2Fpost%2Findex
echo Url::to(['post/index']);
// an absolute route: /index.php?r=post/index
// an absolute route: /index.php?r=post%2Findex
echo Url::to(['/post/index']);
// /index.php?r=post/index assume the alias "@posts" is defined as "/post/index"
// /index.php?r=post%2Findex assume the alias "@posts" is defined as "/post/index"
echo Url::to(['@posts']);
```
@ -197,7 +197,7 @@ Instead of passing an array as its first parameter, you should pass a string in
```php
use yii\helpers\Url;
// currently requested URL: /index.php?r=admin/post/index
// currently requested URL: /index.php?r=admin%2Fpost%2Findex
echo Url::to();
// an aliased URL: http://example.com
@ -214,7 +214,7 @@ methods. For example,
```php
use yii\helpers\Url;
// home page URL: /index.php?r=site/index
// home page URL: /index.php?r=site%2Findex
echo Url::home();
// the base URL, useful if the application is deployed in a sub-folder of the Web root

4
docs/guide/start-databases.md

@ -225,7 +225,7 @@ Trying it Out <span id="trying-it-out"></span>
To see how all of the above code works, use your browser to access the following URL:
```
http://hostname/index.php?r=country/index
http://hostname/index.php?r=country%2Findex
```
![Country List](images/start-country-list.png)
@ -235,7 +235,7 @@ If you click on the button "2", you will see the page display another five count
Observe more carefully and you will find that the URL in the browser also changes to
```
http://hostname/index.php?r=country/index&page=2
http://hostname/index.php?r=country%2Findex&page=2
```
Behind the scenes, [[yii\data\Pagination|Pagination]] is providing all of the necessary functionality to paginate a data set:

2
docs/guide/start-forms.md

@ -187,7 +187,7 @@ Trying it Out <span id="trying-it-out"></span>
To see how it works, use your browser to access the following URL:
```
http://hostname/index.php?r=site/entry
http://hostname/index.php?r=site%2Fentry
```
You will see a page displaying a form with two input fields. In front of each input field, a label indicates what data is to be entered. If you click the submit button without

2
docs/guide/start-gii.md

@ -107,7 +107,7 @@ Trying it Out <span id="trying-it-out"></span>
To see how it works, use your browser to access the following URL:
```
http://hostname/index.php?r=country/index
http://hostname/index.php?r=country%2Findex
```
You will see a data grid showing the countries from the database table. You may sort the grid,

2
docs/guide/start-hello.md

@ -102,7 +102,7 @@ Trying it Out <span id="trying-it-out"></span>
After creating the action and the view, you may access the new page by accessing the following URL:
```
http://hostname/index.php?r=site/say&message=Hello+World
http://hostname/index.php?r=site%2Fsay&message=Hello+World
```
![Hello World](images/start-hello-world.png)

2
docs/guide/structure-views.md

@ -694,7 +694,7 @@ Now if you create a view named `about` under the directory `@app/views/site/page
display this view by the following URL:
```
http://localhost/index.php?r=site/page&view=about
http://localhost/index.php?r=site%2Fpage&view=about
```
The `GET` parameter `view` tells [[yii\web\ViewAction]] which view is requested. The action will then look

10
framework/CHANGELOG.md

@ -13,8 +13,13 @@ Yii Framework 2 Change Log
- Bug #10969: Fixed generator migration tool with decimal params in column (pana1990)
- Bug #10974: `yii.js` - fixed error in ajaxPrefilter event handler, caused by blocked frame (maximal)
- Bug #11038: Fixed handling of intervals of 0 seconds in `yii\i18n\Formatter::asDuration()` (VirtualRJ)
- Bug #11052: Fixed `HtmlPurifier` configuration sequence (samdark)
- Bug #11066: `yii.js` - fixed `getQueryParams()` function to handle URLs with anchors correctly (DrDeath72)
- Bug #11093: Fixed `yii\db\QueryBuilder::buildAndCondition()` to add query params passed directly by `yii\db\Expression` (CedricYii, silverfire)
- Bug #11125: Fixed `JSON_ERROR_SYNTAX` for `json_decode(null)` in PHP 7 (fps01)
- Bug #11012: Fixed `yii\web\UploadedFile::getBaseName()` to work with UTF-8 file names (hiscaler, silverfire)
- Bug: SQlite querybuilder did not create primary key with bigint for `TYPE_BIGPK` (cebe)
- Bug #10784: Fixed `yii\grid\CheckboxColumn` to set correct value when `yii\grid\CheckboxColumn::$checkboxOptions` closure is used (nukkumatti)
- Enh #5469: Add mimetype validation by mask in FileValidator (kirsenn, samdark, silverfire)
- Enh #8602: `yii\validators\DateValidator` skip validation for `timestampAttribute`, if it is already in correct format (klimov-paul)
- Enh #8639: Improve ActiveRecord to not create new instances of classes when objects are available (cebe)
@ -24,7 +29,9 @@ Yii Framework 2 Change Log
- Enh #10610: Added `BaseUrl::$urlManager` to be able to set URL manager used for creating URLs (samdark)
- Enh #10764: `yii\helpers\Html::tag()` and `::beginTag()` return content without any HTML when the `$tag` attribute is `false` or `null` (pana1990)
- Enh #10941: Added `yii\helpers\ArrayHelper::isTraversable`, added support for traversable selections for dropdownList, radioList and checkboxList in `yii\helpers\Html` (sammousa)
- Eng #10976: `Inflector::transliterate()` now uses `strtr` instead of `str_replace` (DrDeath72)
- Enh #10976: `Inflector::transliterate()` now uses `strtr` instead of `str_replace` (DrDeath72)
- Enh #11139: `yii\validators\EachValidator` injects specific attribute value in error message parameters (silverfire)
- Enh: Added `StringHelper::countWords()` that given a string returns number of words in it (samdark)
- Bug #11040: Check parameter 'recursive' and disable recursive copying with option 'recursive' => false in method BaseFileHelper::copyDirectory (Ni-san)
- Chg: HTMLPurifier dependency updated to `~4.6` (samdark)
- Enh #10889: Allows unsigned primary key column definitions (df2)
@ -32,6 +39,7 @@ Yii Framework 2 Change Log
- Enh #9562: Adds `char` datatype to framework (df2)
- Enh #9340: Adds `after()` and `first()` column schema builder modifiers (df2)
- Enh #10921: `__toString()` of column schema builder now adapts to column types (df2)
- Chg #10726: Added `yii\rbac\ManagerInterface::canAddChild()` (dkhlystov, samdark)
- Chg #11071: `yii\helpers\BaseArrayHelper::isIn()` and `isTraversable()` since now throw `\yii\base\InvalidParamException` instead of `\InvalidArgumentException` (nukkumatti)
2.0.7 February 14, 2016

2
framework/UPGRADE.md

@ -22,6 +22,8 @@ ______________________
* `yii\helpers\BaseArrayHelper` methods `isIn()` and `isSubset()` throw `\yii\base\InvalidParamException`
instead of `\InvalidArgumentException`. If you wrap calls of these methods in try/catch block, change expected
exception class.
- `yii\rbac\ManagerInterface::canAddChild()` method was added. If you have custom backend for RBAC you need to implement
it.
* The signature of `yii\db\ColumnSchemaBuilder::__construct()` was changed. The method has got an extra optional parameter `$db`. In case you are instantiating this class yourself and using the `$config` parameter, you will need to move it to the right by one.

2
framework/assets/yii.js

@ -277,7 +277,7 @@ yii = (function ($) {
return {};
}
var pairs = url.substring(pos + 1).split('&'),
var pairs = url.substring(pos + 1).split('#')[0].split('&'),
params = {},
pair,
i;

3
framework/grid/CheckboxColumn.php

@ -125,10 +125,11 @@ class CheckboxColumn extends Column
$options = call_user_func($this->checkboxOptions, $model, $key, $index, $this);
} else {
$options = $this->checkboxOptions;
}
if (!isset($options['value'])) {
$options['value'] = is_array($key) ? json_encode($key, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) : $key;
}
}
return Html::checkbox($this->name, !empty($options['checked']), $options);
}

5
framework/helpers/BaseConsole.php

@ -19,6 +19,7 @@ use yii\console\Markdown;
*/
class BaseConsole
{
// foreground color control codes
const FG_BLACK = 30;
const FG_RED = 31;
const FG_GREEN = 32;
@ -27,7 +28,7 @@ class BaseConsole
const FG_PURPLE = 35;
const FG_CYAN = 36;
const FG_GREY = 37;
// background color control codes
const BG_BLACK = 40;
const BG_RED = 41;
const BG_GREEN = 42;
@ -36,7 +37,7 @@ class BaseConsole
const BG_PURPLE = 45;
const BG_CYAN = 46;
const BG_GREY = 47;
// fonts style control codes
const RESET = 0;
const NORMAL = 0;
const BOLD = 1;

2
framework/helpers/BaseHtmlPurifier.php

@ -50,10 +50,10 @@ class BaseHtmlPurifier
$purifier->config->set('Cache.SerializerPath', \Yii::$app->getRuntimePath());
$purifier->config->set('Cache.SerializerPermissions', 0775);
static::configure($configInstance);
if ($config instanceof \Closure) {
call_user_func($config, $configInstance);
}
static::configure($configInstance);
return $purifier->purify($content);
}

2
framework/helpers/BaseJson.php

@ -90,6 +90,8 @@ class BaseJson
{
if (is_array($json)) {
throw new InvalidParamException('Invalid JSON data.');
} elseif ($json === null || $json === '') {
return null;
}
$decode = json_decode((string) $json, $asArray);
static::handleJsonError(json_last_error());

12
framework/helpers/BaseStringHelper.php

@ -270,4 +270,16 @@ class BaseStringHelper
}
return $result;
}
/**
* Counts words in a string
* @since 2.0.8
*
* @param string $string
* @return integer
*/
public static function countWords($string)
{
return count(preg_split('/\s+/u', $string, null, PREG_SPLIT_NO_EMPTY));
}
}

22
framework/helpers/BaseUrl.php

@ -65,19 +65,19 @@ class BaseUrl
* Below are some examples of using this method:
*
* ```php
* // /index.php?r=site/index
* // /index.php?r=site%2Findex
* echo Url::toRoute('site/index');
*
* // /index.php?r=site/index&src=ref1#name
* // /index.php?r=site%2Findex&src=ref1#name
* echo Url::toRoute(['site/index', 'src' => 'ref1', '#' => 'name']);
*
* // http://www.example.com/index.php?r=site/index
* // http://www.example.com/index.php?r=site%2Findex
* echo Url::toRoute('site/index', true);
*
* // https://www.example.com/index.php?r=site/index
* // https://www.example.com/index.php?r=site%2Findex
* echo Url::toRoute('site/index', 'https');
*
* // /index.php?r=post/index assume the alias "@posts" is defined as "post/index"
* // /index.php?r=post%2Findex assume the alias "@posts" is defined as "post/index"
* echo Url::toRoute('@posts');
* ```
*
@ -167,13 +167,13 @@ class BaseUrl
* Below are some examples of using this method:
*
* ```php
* // /index.php?r=site/index
* // /index.php?r=site%2Findex
* echo Url::to(['site/index']);
*
* // /index.php?r=site/index&src=ref1#name
* // /index.php?r=site%2Findex&src=ref1#name
* echo Url::to(['site/index', 'src' => 'ref1', '#' => 'name']);
*
* // /index.php?r=post/index assume the alias "@posts" is defined as "/post/index"
* // /index.php?r=post%2Findex assume the alias "@posts" is defined as "/post/index"
* echo Url::to(['@posts']);
*
* // the currently requested URL
@ -361,13 +361,13 @@ class BaseUrl
* ```php
* // assume $_GET = ['id' => 123, 'src' => 'google'], current route is "post/view"
*
* // /index.php?r=post/view&id=123&src=google
* // /index.php?r=post%2Fview&id=123&src=google
* echo Url::current();
*
* // /index.php?r=post/view&id=123
* // /index.php?r=post%2Fview&id=123
* echo Url::current(['src' => null]);
*
* // /index.php?r=post/view&id=100&src=google
* // /index.php?r=post%2Fview&id=100&src=google
* echo Url::current(['id' => 100]);
* ```
*

1
framework/messages/hu/yii.php

@ -82,6 +82,7 @@ return array (
'{attribute} must be no less than {min}.' => '{attribute} nem lehet kisebb, mint {min}.',
'{attribute} must be repeated exactly.' => 'Ismételje meg pontosan a(z) {attribute} mezőbe írtakat.',
'{attribute} must not be equal to "{compareValue}".' => '{attribute} nem lehet egyenlő ezzel: "{compareValue}".',
'{attribute} must be equal to "{compareValueOrAttribute}".' => '"{attribute}" mezőnek azonosnak kell lennie a "{compareValueOrAttribute}" mezőbe írtakkal.',
'{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} minimum {min, number} karakter kell, hogy legyen.',
'{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} maximum {max, number} karakter lehet.',
'{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} pontosan {length, number} kell, hogy legyen.',

10
framework/rbac/DbManager.php

@ -679,6 +679,14 @@ class DbManager extends BaseManager
/**
* @inheritdoc
*/
public function canAddChild($parent, $child)
{
return !$this->detectLoop($parent, $child);
}
/**
* @inheritdoc
*/
public function addChild($parent, $child)
{
if ($parent->name === $child->name) {
@ -892,7 +900,7 @@ class DbManager extends BaseManager
{
if (!$this->supportsCascadeUpdate()) {
$this->db->createCommand()
->update($this->itemTable, ['ruleName' => null])
->update($this->itemTable, ['rule_name' => null])
->execute();
}

10
framework/rbac/ManagerInterface.php

@ -129,6 +129,16 @@ interface ManagerInterface
public function getRules();
/**
* Checks the possibility of adding a child to parent
* @param Item $parent the parent item
* @param Item $child the child item to be added to the hierarchy
* @return boolean possibility of adding
*
* @since 2.0.8
*/
public function canAddChild($parent, $child);
/**
* Adds an item as a child of another item.
* @param Item $parent
* @param Item $child

16
framework/rbac/PhpManager.php

@ -151,6 +151,14 @@ class PhpManager extends BaseManager
/**
* @inheritdoc
*/
public function canAddChild($parent, $child)
{
return !$this->detectLoop($parent, $child);
}
/**
* @inheritdoc
*/
public function addChild($parent, $child)
{
if (!isset($this->items[$parent->name], $this->items[$child->name])) {
@ -532,9 +540,11 @@ class PhpManager extends BaseManager
return;
}
foreach ($this->assignments as $i => $assignment) {
foreach ($this->assignments as $i => $assignments) {
foreach ($assignments as $n => $assignment) {
if (isset($names[$assignment->roleName])) {
unset($this->assignments[$i]);
unset($this->assignments[$i][$n]);
}
}
}
foreach ($this->children as $name => $children) {
@ -817,7 +827,7 @@ class PhpManager extends BaseManager
$result = [];
foreach ($this->assignments as $userID => $assignments) {
foreach ($assignments as $userAssignment) {
if ($userAssignment->roleName === $roleName && $userAssignment->userId === $userID) {
if ($userAssignment->roleName === $roleName && $userAssignment->userId == $userID) {
$result[] = (string)$userID;
}
}

7
framework/validators/EachValidator.php

@ -146,7 +146,12 @@ class EachValidator extends Validator
}
$result = $validator->validateValue($v);
if ($result !== null) {
return $this->allowMessageFromRule ? $result : [$this->message, []];
if ($this->allowMessageFromRule) {
$result[1]['value'] = $v;
return $result;
} else {
return [$this->message, ['value' => $v]];
}
}
}

4
framework/validators/Validator.php

@ -356,9 +356,11 @@ class Validator extends Component
*/
public function addError($model, $attribute, $message, $params = [])
{
$value = $model->$attribute;
$params['attribute'] = $model->getAttributeLabel($attribute);
if (!isset($params['value'])) {
$value = $model->$attribute;
$params['value'] = is_array($value) ? 'array()' : $value;
}
$model->addError($attribute, Yii::$app->getI18n()->format($message, $params, Yii::$app->language));
}

3
framework/web/UploadedFile.php

@ -171,7 +171,8 @@ class UploadedFile extends Object
*/
public function getBaseName()
{
return pathinfo($this->name, PATHINFO_FILENAME);
// https://github.com/yiisoft/yii2/issues/11012
return mb_substr(pathinfo('_' . $this->name, PATHINFO_FILENAME), 1, null, '8bit');
}
/**

6
framework/web/UrlManager.php

@ -46,7 +46,7 @@ class UrlManager extends Component
* @var boolean whether to enable pretty URLs. Instead of putting all parameters in the query
* string part of a URL, pretty URLs allow using path info to represent some of the parameters
* and can thus produce more user-friendly URLs, such as "/news/Yii-is-released", instead of
* "/index.php?r=news/view&id=100".
* "/index.php?r=news%2Fview&id=100".
*/
public $enablePrettyUrl = false;
/**
@ -282,7 +282,7 @@ class UrlManager extends Component
* array format must be:
*
* ```php
* // generates: /index.php?r=site/index&param1=value1&param2=value2
* // generates: /index.php?r=site%2Findex&param1=value1&param2=value2
* ['site/index', 'param1' => 'value1', 'param2' => 'value2']
* ```
*
@ -290,7 +290,7 @@ class UrlManager extends Component
* For example,
*
* ```php
* // generates: /index.php?r=site/index&param1=value1#name
* // generates: /index.php?r=site%2Findex&param1=value1#name
* ['site/index', 'param1' => 'value1', '#' => 'name']
* ```
*

8
framework/widgets/ActiveField.php

@ -274,18 +274,16 @@ class ActiveField extends Component
/**
* Generates a tag that contains the first validation error of [[attribute]].
* Note that even if there is no validation error, this method will still return an empty error tag.
* @param array|boolean $options the tag options in terms of name-value pairs. It will be merged with [[errorOptions]].
* @param array|false $options the tag options in terms of name-value pairs. It will be merged with [[errorOptions]].
* The options will be rendered as the attributes of the resulting tag. The values will be HTML-encoded
* using [[Html::encode()]]. If a value is null, the corresponding attribute will not be rendered.
* using [[Html::encode()]]. If this parameter is false, no error tag will be rendered.
*
* The following options are specially handled:
*
* - tag: this specifies the tag name. If not set, "div" will be used.
*
* If this parameter is false, no error tag will be rendered.
*
* If you set a custom `id` for the error element, you may need to adjust the [[$selectors]] accordingly.
*
* @see $errorOptions
* @return $this the field object itself
*/
public function error($options = [])

29
tests/framework/grid/CheckboxColumnTest.php

@ -54,6 +54,35 @@ class CheckboxColumnTest extends TestCase
$this->assertContains('name="MyForm[grid1][key_all]"', $column->renderHeaderCell());
}
public function testInputValue()
{
$column = new CheckboxColumn();
$this->assertContains('value="1"', $column->renderDataCell([], 1, 0));
$this->assertContains('value="42"', $column->renderDataCell([], 42, 0));
$this->assertContains('value="[1,42]"', $column->renderDataCell([], [1, 42], 0));
$column = new CheckboxColumn(['checkboxOptions' => ['value' => 42]]);
$this->assertNotContains('value="1"', $column->renderDataCell([], 1, 0));
$this->assertContains('value="42"', $column->renderDataCell([], 1, 0));
$column = new CheckboxColumn([
'checkboxOptions' => function ($model, $key, $index, $column) {
return [];
}
]);
$this->assertContains('value="1"', $column->renderDataCell([], 1, 0));
$this->assertContains('value="42"', $column->renderDataCell([], 42, 0));
$this->assertContains('value="[1,42]"', $column->renderDataCell([], [1, 42], 0));
$column = new CheckboxColumn([
'checkboxOptions' => function ($model, $key, $index, $column) {
return ['value' => 42];
}
]);
$this->assertNotContains('value="1"', $column->renderDataCell([], 1, 0));
$this->assertContains('value="42"', $column->renderDataCell([], 1, 0));
}
/**
* @return GridView a mock gridview
*/

9
tests/framework/helpers/StringHelperTest.php

@ -240,4 +240,13 @@ class StringHelperTest extends TestCase
$this->assertEquals(['It/', ' is?', ' a', ' test with rtrim'], StringHelper::explode("It/, is?, a , test with rtrim", ',', 'rtrim'));
$this->assertEquals(['It', ' is', ' a ', ' test with closure'], StringHelper::explode("It/, is?, a , test with closure", ',', function ($value) { return trim($value, '/?'); }));
}
public function testWordCount()
{
$this->assertEquals(3, StringHelper::countWords('china 中国 ㄍㄐㄋㄎㄌ'));
$this->assertEquals(4, StringHelper::countWords('и много тут слов?'));
$this->assertEquals(4, StringHelper::countWords("и\rмного\r\nтут\nслов?"));
$this->assertEquals(1, StringHelper::countWords('крем-брюле'));
$this->assertEquals(1, StringHelper::countWords(' слово '));
}
}

65
tests/framework/rbac/ManagerTestCase.php

@ -4,7 +4,6 @@ namespace yiiunit\framework\rbac;
use yii\rbac\Item;
use yii\rbac\Permission;
use yii\rbac\PhpManager;
use yii\rbac\Role;
use yiiunit\TestCase;
@ -113,6 +112,16 @@ abstract class ManagerTestCase extends TestCase
$rule = $this->auth->getRule('newName');
$this->assertEquals(true, $rule->reallyReally);
$item = $this->auth->getPermission('createPost');
$item->name = 'new createPost';
$this->auth->update('createPost', $item);
$item = $this->auth->getPermission('createPost');
$this->assertEquals(null, $item);
$item = $this->auth->getPermission('new createPost');
$this->assertEquals('new createPost', $item->name);
}
public function testGetRules()
@ -141,6 +150,10 @@ abstract class ManagerTestCase extends TestCase
$rules = $this->auth->getRules();
$this->assertEmpty($rules);
$this->auth->remove($this->auth->getPermission('createPost'));
$item = $this->auth->getPermission('createPost');
$this->assertNull($item);
}
public function testCheckAccess()
@ -166,6 +179,8 @@ abstract class ManagerTestCase extends TestCase
'readPost' => true,
'updatePost' => false,
'updateAnyPost' => true,
'blablabla' => false,
null => false,
],
];
@ -312,4 +327,52 @@ abstract class ManagerTestCase extends TestCase
$this->assertEquals(['author B'], $this->auth->getUserIdsByRole('author'));
$this->assertEquals(['admin C'], $this->auth->getUserIdsByRole('admin'));
}
public function testCanAddChild()
{
$this->prepareData();
$author = $this->auth->createRole('author');
$reader = $this->auth->createRole('reader');
$this->assertTrue($this->auth->canAddChild($author, $reader));
$this->assertFalse($this->auth->canAddChild($reader, $author));
}
public function testRemoveAllRules()
{
$this->prepareData();
$this->auth->removeAllRules();
$this->assertEmpty($this->auth->getRules());
$this->assertNotEmpty($this->auth->getRoles());
$this->assertNotEmpty($this->auth->getPermissions());
}
public function testRemoveAllRoles()
{
$this->prepareData();
$this->auth->removeAllRoles();
$this->assertEmpty($this->auth->getRoles());
$this->assertNotEmpty($this->auth->getRules());
$this->assertNotEmpty($this->auth->getPermissions());
}
public function testRemoveAllPermissions()
{
$this->prepareData();
$this->auth->removeAllPermissions();
$this->assertEmpty($this->auth->getPermissions());
$this->assertNotEmpty($this->auth->getRules());
$this->assertNotEmpty($this->auth->getRoles());
}
}

22
tests/framework/validators/EachValidatorTest.php

@ -74,6 +74,28 @@ class EachValidatorTest extends TestCase
}
/**
* @depends testValidate
*/
public function testCustomMessageValue()
{
$model = FakedValidationModel::createWithAttributes([
'attr_one' => [
'TEXT',
],
]);
$validator = new EachValidator(['rule' => ['integer', 'message' => '{value} is not an integer']]);
$validator->validateAttribute($model, 'attr_one');
$this->assertSame('TEXT is not an integer', $model->getFirstError('attr_one'));
$model->clearErrors();
$validator->allowMessageFromRule = false;
$validator->message = '{value} is invalid';
$validator->validateAttribute($model, 'attr_one');
$this->assertEquals('TEXT is invalid', $model->getFirstError('attr_one'));
}
/**
* @see https://github.com/yiisoft/yii2/issues/10825
*
* @depends testValidate

11
tests/framework/validators/FileValidatorTest.php

@ -346,6 +346,16 @@ class FileValidatorTest extends TestCase
$this->assertTrue(stripos(current($m->getErrors('attr_exe')), 'Only files with these extensions ') !== false);
}
public function testIssue11012()
{
$baseName = '飛兒樂團光茫';
/** @var UploadedFile $file */
$file = $this->createTestFiles([
['name' => $baseName . '.txt'],
]);
$this->assertEquals($baseName, $file->getBaseName());
}
/**
* @param string $fileName
* @param string $mask
@ -373,6 +383,7 @@ class FileValidatorTest extends TestCase
public function validMimeTypes()
{
return [
['test.svg', 'image/*'],
['test.jpg', 'image/*'],
['test.png', 'image/*'],
['test.png', 'IMAGE/*'],

30
tests/framework/validators/data/mimeType/test.svg

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.0"
x="0.00000000"
y="0.00000000"
width="466.00000"
height="466.00000"
id="svg2">
<defs
id="defs4" />
<g
id="layer1">
<g
transform="translate(-14.31153,-9.654345)"
id="g2100">
<path
d="M 574.28571 350.93362 A 230.00000 230.00000 0 1 1 114.28571,350.93362 A 230.00000 230.00000 0 1 1 574.28571 350.93362 z"
transform="translate(-95.71429,-108.5714)"
style="opacity:1.0000000;fill:#ffffff;fill-opacity:1.0000000;stroke:none;stroke-width:2.0000000;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000"
id="path1366" />
<path
d="M 231.48189,473.26283 C 131.83469,468.06658 42.098062,390.44395 21.653942,292.99899 C 2.8527423,211.64305 32.299602,121.62080 96.083632,67.582430 C 141.33822,28.222080 202.15620,7.4760400 262.06397,11.991470 C 356.93917,15.843450 443.80335,86.158270 469.35800,177.35283 C 479.83354,210.86634 479.80411,246.84493 475.57362,281.36221 C 461.15785,369.25654 392.02150,445.16887 305.33917,466.41185 C 281.37157,472.30240 256.12409,475.36915 231.48189,473.26283 z M 195.98189,459.07832 C 153.70976,438.32840 127.57657,390.25793 133.57107,343.48796 C 138.36082,288.35497 188.51696,242.79478 243.64749,241.80851 C 289.80353,243.23178 335.32348,214.53137 352.09275,171.21770 C 372.17245,123.85997 354.81639,64.420260 312.15547,35.497910 C 277.18597,9.7192000 230.39701,15.186720 190.59735,24.146350 C 98.599342,46.695330 27.467842,132.85978 22.328202,227.37156 C 15.617372,312.66393 62.578582,398.76397 137.71937,439.58565 C 156.53626,449.74840 183.25724,461.62547 200.86096,461.86871 L 198.91377,460.67719 L 195.98190,459.07832 L 195.98189,459.07832 z M 240.98189,156.44487 C 217.68690,151.96726 210.63785,117.76008 230.72475,104.79451 C 251.31111,88.759900 283.63841,110.57202 275.52356,135.98196 C 271.79700,150.60792 255.61257,159.83414 240.98189,156.44487 z M 252.66158,383.23027 C 281.22410,379.65834 281.98701,333.88297 253.46715,329.56000 C 230.89663,323.64667 211.28225,351.85522 224.59048,371.08172 C 230.24130,379.64095 242.27756,386.42957 252.66158,383.23027 z "
style="fill:#000000"
id="path1351" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

Loading…
Cancel
Save