Commit dc31ea98 by sensorario

Fixed merge

parents c699213c a87e64c6
......@@ -13,4 +13,4 @@ nbproject
Thumbs.db
# composer vendor dir
vendor
\ No newline at end of file
/yii/vendor
\ No newline at end of file
<?php
// comment out the following line to disable debug mode
defined('YII_DEBUG') or define('YII_DEBUG', true);
require(__DIR__ . '/../../framework/yii.php');
$frameworkPath = __DIR__ . '/../../yii';
require($frameworkPath . '/Yii.php');
// Register Composer autoloader
@include($frameworkPath . '/vendor/autoload.php');
$config = require(__DIR__ . '/protected/config/main.php');
$application = new yii\web\Application($config);
......
......@@ -3,6 +3,12 @@
return array(
'id' => 'hello',
'basePath' => dirname(__DIR__),
'preload' => array('log'),
'modules' => array(
'debug' => array(
'class' => 'yii\debug\Module',
)
),
'components' => array(
'cache' => array(
'class' => 'yii\caching\FileCache',
......@@ -14,6 +20,15 @@ return array(
'assetManager' => array(
'bundles' => require(__DIR__ . '/assets.php'),
),
'log' => array(
'class' => 'yii\logging\Router',
'targets' => array(
'file' => array(
'class' => 'yii\logging\FileTarget',
'levels' => array('error', 'warning'),
),
),
),
),
'params' => array(
'adminEmail' => 'admin@example.com',
......
......@@ -6,6 +6,15 @@ use app\models\ContactForm;
class SiteController extends Controller
{
public function actions()
{
return array(
'captcha' => array(
'class' => 'yii\web\CaptchaAction',
),
);
}
public function actionIndex()
{
echo $this->render('index');
......
......@@ -26,7 +26,7 @@ class ContactForm extends Model
// email has to be a valid email address
array('email', 'email'),
// verifyCode needs to be entered correctly
//array('verifyCode', 'captcha', 'allowEmpty' => !Captcha::checkRequirements()),
array('verifyCode', 'captcha'),
);
}
......
<?php
use yii\helpers\Html;
use yii\widgets\Menu;
/**
* @var $this \yii\base\View
* @var $content string
*/
use yii\helpers\Html;
$this->registerAssetBundle('app');
?>
<?php $this->beginPage(); ?>
......@@ -23,16 +25,17 @@ $this->registerAssetBundle('app');
<div class="navbar">
<div class="navbar-inner">
<div class="container">
<ul class="nav">
<li><?php echo Html::a('Home', Yii::$app->homeUrl); ?></li>
<li><?php echo Html::a('About', array('/site/about')); ?></li>
<li><?php echo Html::a('Contact', array('/site/contact')); ?></li>
<?php if (Yii::$app->user->isGuest): ?>
<li><?php echo Html::a('Login', array('/site/login')); ?></li>
<?php else: ?>
<li><?php echo Html::a('Logout (' . Html::encode(Yii::$app->user->identity->username) . ')', array('/site/logout')); ?></li>
<?php endif; ?>
</ul>
<?php $this->widget(Menu::className(), array(
'options' => array('class' => 'nav'),
'items' => array(
array('label' => 'Home', 'url' => array('/site/index')),
array('label' => 'About', 'url' => array('/site/about')),
array('label' => 'Contact', 'url' => array('/site/contact')),
Yii::$app->user->isGuest ?
array('label' => 'Login', 'url' => array('/site/login')) :
array('label' => 'Logout (' . Yii::$app->user->identity->username .')' , 'url' => array('/site/logout')),
),
)); ?>
</div>
</div>
</div>
......@@ -55,6 +58,7 @@ $this->registerAssetBundle('app');
</div>
<?php $this->endBody(); ?>
</div>
<?php $this->widget('yii\debug\Toolbar'); ?>
</body>
</html>
<?php $this->endPage(); ?>
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
use yii\widgets\Captcha;
/**
* @var yii\base\View $this
* @var yii\widgets\ActiveForm $form
......@@ -20,7 +23,7 @@ $this->params['breadcrumbs'][] = $this->title;
If you have business inquiries or other questions, please fill out the following form to contact us. Thank you.
</p>
<?php $form = $this->beginWidget('yii\widgets\ActiveForm', array(
<?php $form = $this->beginWidget(ActiveForm::className(), array(
'options' => array('class' => 'form-horizontal'),
'fieldConfig' => array('inputOptions' => array('class' => 'input-xlarge')),
)); ?>
......@@ -28,6 +31,15 @@ $this->params['breadcrumbs'][] = $this->title;
<?php echo $form->field($model, 'email')->textInput(); ?>
<?php echo $form->field($model, 'subject')->textInput(); ?>
<?php echo $form->field($model, 'body')->textArea(array('rows' => 6)); ?>
<?php
$field = $form->field($model, 'verifyCode');
echo $field->begin();
echo $field->label();
$this->widget(Captcha::className());
echo Html::activeTextInput($model, 'verifyCode', array('class' => 'input-medium'));
echo $field->error();
echo $field->end();
?>
<div class="form-actions">
<?php echo Html::submitButton('Submit', null, null, array('class' => 'btn btn-primary')); ?>
</div>
......
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
/**
* @var yii\base\View $this
* @var yii\widgets\ActiveForm $form
......@@ -12,7 +14,7 @@ $this->params['breadcrumbs'][] = $this->title;
<p>Please fill out the following fields to login:</p>
<?php $form = $this->beginWidget('yii\widgets\ActiveForm', array('options' => array('class' => 'form-horizontal'))); ?>
<?php $form = $this->beginWidget(ActiveForm::className(), array('options' => array('class' => 'form-horizontal'))); ?>
<?php echo $form->field($model, 'username')->textInput(); ?>
<?php echo $form->field($model, 'password')->passwordInput(); ?>
<?php echo $form->field($model, 'rememberMe')->checkbox(); ?>
......
......@@ -11,7 +11,7 @@
// fcgi doesn't have STDIN defined by default
defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
require(__DIR__ . '/../framework/yii.php');
require(__DIR__ . '/../framework/Yii.php');
$id = 'yiic-build';
$basePath = __DIR__;
......
......@@ -63,13 +63,17 @@
"irc": "irc://irc.freenode.net/yii",
"source": "https://github.com/yiisoft/yii2"
},
"config": {
"vendor-dir": "yii/vendor"
},
"bin": [
"framework/yiic"
"yii/yiic"
],
"require": {
"php": ">=5.3.0",
"michelf/php-markdown": "1.3",
"twig/twig": "1.12.*",
"smarty/smarty": "3.1.*"
"smarty/smarty": "3.1.*",
"ezyang/htmlpurifier": "v4.5.0"
}
}
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file"
],
"hash": "7d46ce9c4d8d5f4ecae1611ea8f0b49c",
"packages": [
{
"name": "ezyang/htmlpurifier",
"version": "v4.5.0",
"source": {
"type": "git",
"url": "https://github.com/ezyang/htmlpurifier.git",
"reference": "v4.5.0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/v4.5.0",
"reference": "v4.5.0",
"shasum": ""
},
"require": {
"php": ">=5.2"
},
"type": "library",
"autoload": {
"psr-0": {
"HTMLPurifier": "library/"
},
"files": [
"library/HTMLPurifier.composer.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL"
],
"authors": [
{
"name": "Edward Z. Yang",
"email": "admin@htmlpurifier.org",
"homepage": "http://ezyang.com"
}
],
"description": "Standards compliant HTML filter written in PHP",
"homepage": "http://htmlpurifier.org/",
"keywords": [
"html"
],
"time": "2013-02-18 00:04:08"
},
{
"name": "michelf/php-markdown",
"version": "1.3",
"source": {
"type": "git",
"url": "https://github.com/michelf/php-markdown.git",
"reference": "1.3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/michelf/php-markdown/zipball/1.3",
"reference": "1.3",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-lib": "1.3.x-dev"
}
},
"autoload": {
"psr-0": {
"Michelf": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Michel Fortin",
"email": "michel.fortin@michelf.ca",
"homepage": "http://michelf.ca/",
"role": "Developer"
},
{
"name": "John Gruber",
"homepage": "http://daringfireball.net/"
}
],
"description": "PHP Markdown",
"homepage": "http://michelf.ca/projects/php-markdown/",
"keywords": [
"markdown"
],
"time": "2013-04-11 18:53:11"
},
{
"name": "smarty/smarty",
"version": "v3.1.13",
"source": {
"type": "svn",
"url": "http://smarty-php.googlecode.com/svn",
"reference": "/tags/v3.1.13/@4699"
},
"require": {
"php": ">=5.2"
},
"type": "library",
"autoload": {
"classmap": [
"distribution/libs/Smarty.class.php",
"distribution/libs/SmartyBC.class.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-3.0"
],
"authors": [
{
"name": "Monte Ohrt",
"email": "monte@ohrt.com"
},
{
"name": "Uwe Tews",
"email": "uwe.tews@googlemail.com"
},
{
"name": "Rodney Rehm",
"email": "rodney.rehm@medialize.de"
}
],
"description": "Smarty - the compiling PHP template engine",
"homepage": "http://www.smarty.net",
"keywords": [
"templating"
],
"time": "2013-01-26 12:03:52"
},
{
"name": "twig/twig",
"version": "v1.12.3",
"source": {
"type": "git",
"url": "https://github.com/fabpot/Twig.git",
"reference": "v1.12.3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fabpot/Twig/zipball/v1.12.3",
"reference": "v1.12.3",
"shasum": ""
},
"require": {
"php": ">=5.2.4"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.12-dev"
}
},
"autoload": {
"psr-0": {
"Twig_": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Armin Ronacher",
"email": "armin.ronacher@active-4.com"
}
],
"description": "Twig, the flexible, fast, and secure template language for PHP",
"homepage": "http://twig.sensiolabs.org",
"keywords": [
"templating"
],
"time": "2013-04-08 12:40:11"
}
],
"packages-dev": [
],
"aliases": [
],
"minimum-stability": "stable",
"stability-flags": [
],
"platform": {
"php": ">=5.3.0"
},
"platform-dev": [
]
}
<?php
namespace yiiunit;
class DatabaseTestCase extends TestCase
{
protected $database;
protected $driverName = 'mysql';
protected $db;
protected function setUp()
{
$databases = $this->getParam('databases');
$this->database = $databases[$this->driverName];
$pdo_database = 'pdo_'.$this->driverName;
if (!extension_loaded('pdo') || !extension_loaded($pdo_database)) {
$this->markTestSkipped('pdo and pdo_'.$pdo_database.' extension are required.');
}
}
/**
* @param bool $reset whether to clean up the test database
* @param bool $open whether to open and populate test database
* @return \yii\db\Connection
*/
public function getConnection($reset = true, $open = true)
{
if (!$reset && $this->db) {
return $this->db;
}
$db = new \yii\db\Connection;
$db->dsn = $this->database['dsn'];
if (isset($this->database['username'])) {
$db->username = $this->database['username'];
$db->password = $this->database['password'];
}
if ($open) {
$db->open();
$lines = explode(';', file_get_contents($this->database['fixture']));
foreach ($lines as $line) {
if (trim($line) !== '') {
$db->pdo->exec($line);
}
}
}
$this->db = $db;
return $db;
}
}
......@@ -5,7 +5,7 @@ define('YII_DEBUG', true);
$_SERVER['SCRIPT_NAME'] = '/' . __DIR__;
$_SERVER['SCRIPT_FILENAME'] = __FILE__;
require_once(__DIR__ . '/../../framework/yii.php');
require_once(__DIR__ . '/../../yii/Yii.php');
Yii::setAlias('@yiiunit', __DIR__);
......
<?php
return array(
'mysql' => array(
'dsn' => 'mysql:host=127.0.0.1;dbname=yiitest',
'username' => 'travis',
'password' => '',
'fixture' => __DIR__ . '/mysql.sql',
),
'databases' => array(
'mysql' => array(
'dsn' => 'mysql:host=127.0.0.1;dbname=yiitest',
'username' => 'travis',
'password' => '',
'fixture' => __DIR__ . '/mysql.sql',
),
'sqlite' => array(
'dsn' => 'sqlite::memory:',
'fixture' => __DIR__ . '/sqlite.sql',
),
)
);
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
"Last-Translator: resurtm <resurtm@gmail.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.5.5\n"
msgctxt "context1"
msgid ""
"Aliquam tempus elit vel purus molestie placerat. In sollicitudin tincidunt\n"
"aliquet. Integer tincidunt gravida tempor. In convallis blandit dui vel "
"malesuada.\n"
"Nunc vel sapien nunc, a pretium nulla."
msgstr ""
"Олицетворение однократно. Представленный лексико-семантический анализ "
"является\n"
"психолингвистическим в своей основе, но механизм сочленений полидисперсен. "
"Впечатление\n"
"однократно. Различное расположение выбирает сюжетный механизм сочленений."
msgctxt "context1"
msgid "String number two."
msgstr "Строка номер два."
msgctxt "context2"
msgid ""
"The other\n"
"\n"
"context.\n"
msgstr ""
"Другой\n"
"\n"
"контекст.\n"
msgctxt "context1"
msgid ""
"Missing\n"
"\r\t\"translation."
msgstr ""
msgctxt "context1"
msgid ""
"Nunc vel sapien nunc, a pretium nulla.\n"
"Pellentesque habitant morbi tristique senectus et netus et malesuada fames "
"ac turpis egestas."
msgstr "Короткий перевод."
msgid "contextless"
msgstr ""
msgctxt "context2"
msgid ""
"test1\\ntest2\n"
"\\\n"
"test3"
msgstr ""
"тест1\\nтест2\n"
"\\\n"
"тест3"
/**
* This is the database schema for testing MySQL support of Yii DAO and Active Record.
* The following database setup is required to perform then relevant tests:
* Database name: yiitest
* username: test
* password: test
* charset: utf8
* The database setup in config.php is required to perform then relevant tests:
*/
DROP TABLE IF EXISTS tbl_order_item CASCADE;
......
......@@ -47,7 +47,6 @@ class YiiBaseTest extends TestCase
public function testGetVersion()
{
echo Yii::getVersion();
$this->assertTrue((boolean)preg_match('~\d+\.\d+(?:\.\d+)?(?:-\w+)?~', \Yii::getVersion()));
}
......
......@@ -15,13 +15,13 @@ class ApcCacheTest extends CacheTest
*/
protected function getCacheInstance()
{
if(!extension_loaded("apc")) {
if (!extension_loaded("apc")) {
$this->markTestSkipped("APC not installed. Skipping.");
} else if ('cli' === PHP_SAPI && !ini_get('apc.enable_cli')) {
} elseif ('cli' === PHP_SAPI && !ini_get('apc.enable_cli')) {
$this->markTestSkipped("APC cli is not enabled. Skipping.");
}
if($this->_cacheInstance === null) {
if ($this->_cacheInstance === null) {
$this->_cacheInstance = new ApcCache();
}
return $this->_cacheInstance;
......
......@@ -35,7 +35,8 @@ class DbCacheTest extends CacheTest
function getConnection($reset = true)
{
if($this->_connection === null) {
$params = $this->getParam('mysql');
$databases = $this->getParam('databases');
$params = $databases['mysql'];
$db = new \yii\db\Connection;
$db->dsn = $params['dsn'];
$db->username = $params['username'];
......
......@@ -10,10 +10,11 @@ use yiiunit\data\ar\OrderItem;
use yiiunit\data\ar\Order;
use yiiunit\data\ar\Item;
class ActiveRecordTest extends \yiiunit\MysqlTestCase
class ActiveRecordTest extends \yiiunit\DatabaseTestCase
{
public function setUp()
{
parent::setUp();
ActiveRecord::$db = $this->getConnection();
}
......
......@@ -7,7 +7,7 @@ use yii\db\Command;
use yii\db\Query;
use yii\db\DataReader;
class CommandTest extends \yiiunit\MysqlTestCase
class CommandTest extends \yiiunit\DatabaseTestCase
{
function testConstruct()
{
......
......@@ -4,12 +4,12 @@ namespace yiiunit\framework\db;
use yii\db\Connection;
class ConnectionTest extends \yiiunit\MysqlTestCase
class ConnectionTest extends \yiiunit\DatabaseTestCase
{
function testConstruct()
{
$connection = $this->getConnection(false);
$params = $this->getParam('mysql');
$params = $this->database;
$this->assertEquals($params['dsn'], $connection->dsn);
$this->assertEquals($params['username'], $connection->username);
......@@ -18,7 +18,7 @@ class ConnectionTest extends \yiiunit\MysqlTestCase
function testOpenClose()
{
$connection = $this->getConnection(false);
$connection = $this->getConnection(false, false);
$this->assertFalse($connection->isActive);
$this->assertEquals(null, $connection->pdo);
......@@ -39,9 +39,8 @@ class ConnectionTest extends \yiiunit\MysqlTestCase
function testGetDriverName()
{
$connection = $this->getConnection(false);
$this->assertEquals('mysql', $connection->driverName);
$this->assertFalse($connection->isActive);
$connection = $this->getConnection(false, false);
$this->assertEquals($this->driverName, $connection->driverName);
}
function testQuoteValue()
......
......@@ -7,7 +7,7 @@ use yii\db\Command;
use yii\db\Query;
use yii\db\DataReader;
class QueryTest extends \yiiunit\MysqlTestCase
class QueryTest extends \yiiunit\DatabaseTestCase
{
function testSelect()
{
......
<?php
namespace yiiunit\framework\db\sqlite;
class SqliteActiveRecordTest extends \yiiunit\framework\db\ActiveRecordTest
{
public function setUp()
{
$this->driverName = 'sqlite';
parent::setUp();
}
}
<?php
namespace yiiunit\framework\db\sqlite;
class SqliteCommandTest extends \yiiunit\framework\db\CommandTest
{
public function setUp()
{
$this->driverName = 'sqlite';
parent::setUp();
}
function testAutoQuoting()
{
$db = $this->getConnection(false);
$sql = 'SELECT [[id]], [[t.name]] FROM {{tbl_customer}} t';
$command = $db->createCommand($sql);
$this->assertEquals("SELECT \"id\", 't'.\"name\" FROM 'tbl_customer' t", $command->sql);
}
}
<?php
namespace yiiunit\framework\db\sqlite;
class SqliteConnectionTest extends \yiiunit\framework\db\ConnectionTest
{
public function setUp()
{
$this->driverName = 'sqlite';
parent::setUp();
}
function testConstruct()
{
$connection = $this->getConnection(false);
$params = $this->database;
$this->assertEquals($params['dsn'], $connection->dsn);
}
function testQuoteValue()
{
$connection = $this->getConnection(false);
$this->assertEquals(123, $connection->quoteValue(123));
$this->assertEquals("'string'", $connection->quoteValue('string'));
$this->assertEquals("'It''s interesting'", $connection->quoteValue("It's interesting"));
}
function testQuoteTableName()
{
$connection = $this->getConnection(false);
$this->assertEquals("'table'", $connection->quoteTableName('table'));
$this->assertEquals("'schema'.'table'", $connection->quoteTableName('schema.table'));
$this->assertEquals('{{table}}', $connection->quoteTableName('{{table}}'));
$this->assertEquals('(table)', $connection->quoteTableName('(table)'));
}
function testQuoteColumnName()
{
$connection = $this->getConnection(false);
$this->assertEquals('"column"', $connection->quoteColumnName('column'));
$this->assertEquals("'table'.\"column\"", $connection->quoteColumnName('table.column'));
$this->assertEquals('[[column]]', $connection->quoteColumnName('[[column]]'));
$this->assertEquals('{{column}}', $connection->quoteColumnName('{{column}}'));
$this->assertEquals('(column)', $connection->quoteColumnName('(column)'));
}
}
<?php
/**
* Created by JetBrains PhpStorm.
* User: RusMaxim
* Date: 09.05.13
* Time: 21:41
* To change this template use File | Settings | File Templates.
*/
namespace yiiunit\framework\db\sqlite;
class SqliteQueryTest extends \yiiunit\framework\db\QueryTest
{
public function setUp()
{
$this->driverName = 'sqlite';
parent::setUp();
}
}
......@@ -12,6 +12,16 @@ class ArrayHelperTest extends \yii\test\TestCase
}
public function testRemove()
{
$array = array('name' => 'b', 'age' => 3);
$name = ArrayHelper::remove($array, 'name');
$this->assertEquals($name, 'b');
$this->assertEquals($array, array('age' => 3));
}
public function testMultisort()
{
// single key
......
......@@ -70,4 +70,48 @@ class StringHelperTest extends \yii\test\TestCase
$this->assertEquals('PostTag', StringHelper::id2camel('post-tag'));
$this->assertEquals('PostTag', StringHelper::id2camel('post_tag', '_'));
}
public function testBasename()
{
$this->assertEquals('', StringHelper::basename(''));
$this->assertEquals('file', StringHelper::basename('file'));
$this->assertEquals('file.test', StringHelper::basename('file.test', '.test2'));
$this->assertEquals('file', StringHelper::basename('file.test', '.test'));
$this->assertEquals('file', StringHelper::basename('/file'));
$this->assertEquals('file.test', StringHelper::basename('/file.test', '.test2'));
$this->assertEquals('file', StringHelper::basename('/file.test', '.test'));
$this->assertEquals('file', StringHelper::basename('/path/to/file'));
$this->assertEquals('file.test', StringHelper::basename('/path/to/file.test', '.test2'));
$this->assertEquals('file', StringHelper::basename('/path/to/file.test', '.test'));
$this->assertEquals('file', StringHelper::basename('\file'));
$this->assertEquals('file.test', StringHelper::basename('\file.test', '.test2'));
$this->assertEquals('file', StringHelper::basename('\file.test', '.test'));
$this->assertEquals('file', StringHelper::basename('C:\file'));
$this->assertEquals('file.test', StringHelper::basename('C:\file.test', '.test2'));
$this->assertEquals('file', StringHelper::basename('C:\file.test', '.test'));
$this->assertEquals('file', StringHelper::basename('C:\path\to\file'));
$this->assertEquals('file.test', StringHelper::basename('C:\path\to\file.test', '.test2'));
$this->assertEquals('file', StringHelper::basename('C:\path\to\file.test', '.test'));
// mixed paths
$this->assertEquals('file.test', StringHelper::basename('/path\to/file.test'));
$this->assertEquals('file.test', StringHelper::basename('/path/to\file.test'));
$this->assertEquals('file.test', StringHelper::basename('\path/to\file.test'));
// \ and / in suffix
$this->assertEquals('file', StringHelper::basename('/path/to/filete/st', 'te/st'));
$this->assertEquals('st', StringHelper::basename('/path/to/filete/st', 'te\st'));
$this->assertEquals('file', StringHelper::basename('/path/to/filete\st', 'te\st'));
$this->assertEquals('st', StringHelper::basename('/path/to/filete\st', 'te/st'));
// http://www.php.net/manual/en/function.basename.php#72254
$this->assertEquals('foo', StringHelper::basename('/bar/foo/'));
$this->assertEquals('foo', StringHelper::basename('\\bar\\foo\\'));
}
}
<?php
namespace yiiunit\framework\i18n;
use yii\i18n\GettextMessageSource;
use yiiunit\TestCase;
class GettextMessageSourceTest extends TestCase
{
public function testLoadMessages()
{
$this->markTestSkipped();
}
}
<?php
namespace yiiunit\framework\i18n;
use yii\i18n\GettextMoFile;
use yiiunit\TestCase;
class GettextMoFileTest extends TestCase
{
public function testLoad()
{
$moFile = new GettextMoFile();
$moFilePath = __DIR__ . '/../../data/i18n/test.mo';
$context1 = $moFile->load($moFilePath, 'context1');
$context2 = $moFile->load($moFilePath, 'context2');
// item count
$this->assertCount(3, $context1);
$this->assertCount(2, $context2);
// original messages
$this->assertArrayNotHasKey("Missing\n\r\t\"translation.", $context1);
$this->assertArrayHasKey("Aliquam tempus elit vel purus molestie placerat. In sollicitudin tincidunt\naliquet. Integer tincidunt gravida tempor. In convallis blandit dui vel malesuada.\nNunc vel sapien nunc, a pretium nulla.", $context1);
$this->assertArrayHasKey("String number two.", $context1);
$this->assertArrayHasKey("Nunc vel sapien nunc, a pretium nulla.\nPellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.", $context1);
$this->assertArrayHasKey("The other\n\ncontext.\n", $context2);
$this->assertArrayHasKey("test1\\ntest2\n\\\ntest3", $context2);
// translated messages
$this->assertFalse(in_array("", $context1));
$this->assertTrue(in_array("Олицетворение однократно. Представленный лексико-семантический анализ является\nпсихолингвистическим в своей основе, но механизм сочленений полидисперсен. Впечатление\nоднократно. Различное расположение выбирает сюжетный механизм сочленений.", $context1));
$this->assertTrue(in_array('Строка номер два.', $context1));
$this->assertTrue(in_array('Короткий перевод.', $context1));
$this->assertTrue(in_array("Другой\n\nконтекст.\n", $context2));
$this->assertTrue(in_array("тест1\\nтест2\n\\\nтест3", $context2));
}
public function testSave()
{
// initial data
$s = chr(4);
$messages = array(
'Hello!' => 'Привет!',
"context1{$s}Hello?" => 'Привет?',
'Hello!?' => '',
"context1{$s}Hello!?!" => '',
"context2{$s}\"Quotes\"" => '"Кавычки"',
"context2{$s}\nNew lines\n" => "\nПереносы строк\n",
"context2{$s}\tTabs\t" => "\tТабы\t",
"context2{$s}\rCarriage returns\r" => "\rВозвраты кареток\r",
);
// create temporary directory and dump messages
$poFileDirectory = __DIR__ . '/../../runtime/i18n';
if (!is_dir($poFileDirectory)) {
mkdir($poFileDirectory);
}
if (is_file($poFileDirectory . '/test.mo')) {
unlink($poFileDirectory . '/test.mo');
}
$moFile = new GettextMoFile();
$moFile->save($poFileDirectory . '/test.mo', $messages);
// load messages
$context1 = $moFile->load($poFileDirectory . '/test.mo', 'context1');
$context2 = $moFile->load($poFileDirectory . '/test.mo', 'context2');
// context1
$this->assertCount(2, $context1);
$this->assertArrayHasKey('Hello?', $context1);
$this->assertTrue(in_array('Привет?', $context1));
$this->assertArrayHasKey('Hello!?!', $context1);
$this->assertTrue(in_array('', $context1));
// context2
$this->assertCount(4, $context2);
$this->assertArrayHasKey("\"Quotes\"", $context2);
$this->assertTrue(in_array('"Кавычки"', $context2));
$this->assertArrayHasKey("\nNew lines\n", $context2);
$this->assertTrue(in_array("\nПереносы строк\n", $context2));
$this->assertArrayHasKey("\tTabs\t", $context2);
$this->assertTrue(in_array("\tТабы\t", $context2));
$this->assertArrayHasKey("\rCarriage returns\r", $context2);
$this->assertTrue(in_array("\rВозвраты кареток\r", $context2));
}
}
<?php
namespace yiiunit\framework\i18n;
use yii\i18n\GettextPoFile;
use yiiunit\TestCase;
class GettextPoFileTest extends TestCase
{
public function testLoad()
{
$poFile = new GettextPoFile();
$poFilePath = __DIR__ . '/../../data/i18n/test.po';
$context1 = $poFile->load($poFilePath, 'context1');
$context2 = $poFile->load($poFilePath, 'context2');
// item count
$this->assertCount(4, $context1);
$this->assertCount(2, $context2);
// original messages
$this->assertArrayHasKey("Missing\n\r\t\"translation.", $context1);
$this->assertArrayHasKey("Aliquam tempus elit vel purus molestie placerat. In sollicitudin tincidunt\naliquet. Integer tincidunt gravida tempor. In convallis blandit dui vel malesuada.\nNunc vel sapien nunc, a pretium nulla.", $context1);
$this->assertArrayHasKey("String number two.", $context1);
$this->assertArrayHasKey("Nunc vel sapien nunc, a pretium nulla.\nPellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.", $context1);
$this->assertArrayHasKey("The other\n\ncontext.\n", $context2);
$this->assertArrayHasKey("test1\\\ntest2\n\\\\\ntest3", $context2);
// translated messages
$this->assertTrue(in_array("", $context1));
$this->assertTrue(in_array("Олицетворение однократно. Представленный лексико-семантический анализ является\nпсихолингвистическим в своей основе, но механизм сочленений полидисперсен. Впечатление\nоднократно. Различное расположение выбирает сюжетный механизм сочленений.", $context1));
$this->assertTrue(in_array('Строка номер два.', $context1));
$this->assertTrue(in_array('Короткий перевод.', $context1));
$this->assertTrue(in_array("Другой\n\nконтекст.\n", $context2));
$this->assertTrue(in_array("тест1\\\nтест2\n\\\\\nтест3", $context2));
}
public function testSave()
{
// initial data
$s = chr(4);
$messages = array(
'Hello!' => 'Привет!',
"context1{$s}Hello?" => 'Привет?',
'Hello!?' => '',
"context1{$s}Hello!?!" => '',
"context2{$s}\"Quotes\"" => '"Кавычки"',
"context2{$s}\nNew lines\n" => "\nПереносы строк\n",
"context2{$s}\tTabs\t" => "\tТабы\t",
"context2{$s}\rCarriage returns\r" => "\rВозвраты кареток\r",
);
// create temporary directory and dump messages
$poFileDirectory = __DIR__ . '/../../runtime/i18n';
if (!is_dir($poFileDirectory)) {
mkdir($poFileDirectory);
}
if (is_file($poFileDirectory . '/test.po')) {
unlink($poFileDirectory . '/test.po');
}
$poFile = new GettextPoFile();
$poFile->save($poFileDirectory . '/test.po', $messages);
// load messages
$context1 = $poFile->load($poFileDirectory . '/test.po', 'context1');
$context2 = $poFile->load($poFileDirectory . '/test.po', 'context2');
// context1
$this->assertCount(2, $context1);
$this->assertArrayHasKey('Hello?', $context1);
$this->assertTrue(in_array('Привет?', $context1));
$this->assertArrayHasKey('Hello!?!', $context1);
$this->assertTrue(in_array('', $context1));
// context2
$this->assertCount(4, $context2);
$this->assertArrayHasKey("\"Quotes\"", $context2);
$this->assertTrue(in_array('"Кавычки"', $context2));
$this->assertArrayHasKey("\nNew lines\n", $context2);
$this->assertTrue(in_array("\nПереносы строк\n", $context2));
$this->assertArrayHasKey("\tTabs\t", $context2);
$this->assertTrue(in_array("\tТабы\t", $context2));
$this->assertArrayHasKey("\rCarriage returns\r", $context2);
$this->assertTrue(in_array("\rВозвраты кареток\r", $context2));
}
}
<?php
namespace yiiunit\framework\rbac;
use yii\rbac\Assignment;
use yii\rbac\Item;
use yiiunit\TestCase;
abstract class ManagerTestBase extends TestCase
{
/** @var \yii\rbac\PhpManager|\yii\rbac\DbManager */
protected $auth;
public function testCreateItem()
{
$type = Item::TYPE_TASK;
$name = 'editUser';
$description = 'edit a user';
$bizRule = 'checkUserIdentity()';
$data = array(1, 2, 3);
$item = $this->auth->createItem($name, $type, $description, $bizRule, $data);
$this->assertTrue($item instanceof Item);
$this->assertEquals($item->type, $type);
$this->assertEquals($item->name, $name);
$this->assertEquals($item->description, $description);
$this->assertEquals($item->bizRule, $bizRule);
$this->assertEquals($item->data, $data);
// test shortcut
$name2 = 'createUser';
$item2 = $this->auth->createRole($name2, $description, $bizRule, $data);
$this->assertEquals($item2->type, Item::TYPE_ROLE);
// test adding an item with the same name
$this->setExpectedException('Exception');
$this->auth->createItem($name, $type, $description, $bizRule, $data);
}
public function testGetItem()
{
$this->assertTrue($this->auth->getItem('readPost') instanceof Item);
$this->assertTrue($this->auth->getItem('reader') instanceof Item);
$this->assertNull($this->auth->getItem('unknown'));
}
public function testRemoveAuthItem()
{
$this->assertTrue($this->auth->getItem('updatePost') instanceof Item);
$this->assertTrue($this->auth->removeItem('updatePost'));
$this->assertNull($this->auth->getItem('updatePost'));
$this->assertFalse($this->auth->removeItem('updatePost'));
}
public function testChangeItemName()
{
$item = $this->auth->getItem('readPost');
$this->assertTrue($item instanceof Item);
$this->assertTrue($this->auth->hasItemChild('reader', 'readPost'));
$item->name = 'readPost2';
$this->assertNull($this->auth->getItem('readPost'));
$this->assertEquals($this->auth->getItem('readPost2'), $item);
$this->assertFalse($this->auth->hasItemChild('reader', 'readPost'));
$this->assertTrue($this->auth->hasItemChild('reader', 'readPost2'));
}
public function testAddItemChild()
{
$this->auth->addItemChild('createPost', 'updatePost');
// test adding upper level item to lower one
$this->setExpectedException('Exception');
$this->auth->addItemChild('readPost', 'reader');
}
public function testAddItemChild2()
{
// test adding inexistent items
$this->setExpectedException('Exception');
$this->assertFalse($this->auth->addItemChild('createPost2', 'updatePost'));
}
public function testRemoveItemChild()
{
$this->assertTrue($this->auth->hasItemChild('reader', 'readPost'));
$this->assertTrue($this->auth->removeItemChild('reader', 'readPost'));
$this->assertFalse($this->auth->hasItemChild('reader', 'readPost'));
$this->assertFalse($this->auth->removeItemChild('reader', 'readPost'));
}
public function testGetItemChildren()
{
$this->assertEquals(array(), $this->auth->getItemChildren('readPost'));
$children = $this->auth->getItemChildren('author');
$this->assertEquals(3, count($children));
$this->assertTrue(reset($children) instanceof Item);
}
public function testAssign()
{
$auth = $this->auth->assign('new user', 'createPost', 'rule', 'data');
$this->assertTrue($auth instanceof Assignment);
$this->assertEquals($auth->userId, 'new user');
$this->assertEquals($auth->itemName, 'createPost');
$this->assertEquals($auth->bizRule, 'rule');
$this->assertEquals($auth->data, 'data');
$this->setExpectedException('Exception');
$this->auth->assign('new user', 'createPost2', 'rule', 'data');
}
public function testRevoke()
{
$this->assertTrue($this->auth->isAssigned('author B', 'author'));
$auth = $this->auth->getAssignment('author B', 'author');
$this->assertTrue($auth instanceof Assignment);
$this->assertTrue($this->auth->revoke('author B', 'author'));
$this->assertFalse($this->auth->isAssigned('author B', 'author'));
$this->assertFalse($this->auth->revoke('author B', 'author'));
}
public function testGetAssignments()
{
$this->auth->assign('author B', 'deletePost');
$auths = $this->auth->getAssignments('author B');
$this->assertEquals(2, count($auths));
$this->assertTrue(reset($auths) instanceof Assignment);
}
public function testGetItems()
{
$this->assertEquals(count($this->auth->getRoles()), 4);
$this->assertEquals(count($this->auth->getOperations()), 4);
$this->assertEquals(count($this->auth->getTasks()), 1);
$this->assertEquals(count($this->auth->getItems()), 9);
$this->assertEquals(count($this->auth->getItems('author B', null)), 1);
$this->assertEquals(count($this->auth->getItems('author C', null)), 0);
$this->assertEquals(count($this->auth->getItems('author B', Item::TYPE_ROLE)), 1);
$this->assertEquals(count($this->auth->getItems('author B', Item::TYPE_OPERATION)), 0);
}
public function testClearAll()
{
$this->auth->clearAll();
$this->assertEquals(count($this->auth->getRoles()), 0);
$this->assertEquals(count($this->auth->getOperations()), 0);
$this->assertEquals(count($this->auth->getTasks()), 0);
$this->assertEquals(count($this->auth->getItems()), 0);
$this->assertEquals(count($this->auth->getAssignments('author B')), 0);
}
public function testClearAssignments()
{
$this->auth->clearAssignments();
$this->assertEquals(count($this->auth->getAssignments('author B')), 0);
}
public function testDetectLoop()
{
$this->setExpectedException('Exception');
$this->auth->addItemChild('readPost', 'readPost');
}
public function testExecuteBizRule()
{
$this->assertTrue($this->auth->executeBizRule(null, array(), null));
$this->assertTrue($this->auth->executeBizRule('return 1==true;', array(), null));
$this->assertTrue($this->auth->executeBizRule('return $params[0]==$params[1];', array(1, '1'), null));
$this->assertFalse($this->auth->executeBizRule('invalid', array(), null));
}
public function testCheckAccess()
{
$results = array(
'reader A' => array(
'createPost' => false,
'readPost' => true,
'updatePost' => false,
'updateOwnPost' => false,
'deletePost' => false,
),
'author B' => array(
'createPost' => true,
'readPost' => true,
'updatePost' => true,
'updateOwnPost' => true,
'deletePost' => false,
),
'editor C' => array(
'createPost' => false,
'readPost' => true,
'updatePost' => true,
'updateOwnPost' => false,
'deletePost' => false,
),
'admin D' => array(
'createPost' => true,
'readPost' => true,
'updatePost' => true,
'updateOwnPost' => false,
'deletePost' => true,
),
);
$params = array('authorID' => 'author B');
foreach (array('reader A', 'author B', 'editor C', 'admin D') as $user) {
$params['userID'] = $user;
foreach (array('createPost', 'readPost', 'updatePost', 'updateOwnPost', 'deletePost') as $operation) {
$result = $this->auth->checkAccess($user, $operation, $params);
$this->assertEquals($results[$user][$operation], $result);
}
}
}
protected function prepareData()
{
$this->auth->createOperation('createPost', 'create a post');
$this->auth->createOperation('readPost', 'read a post');
$this->auth->createOperation('updatePost', 'update a post');
$this->auth->createOperation('deletePost', 'delete a post');
$task = $this->auth->createTask('updateOwnPost', 'update a post by author himself', 'return $params["authorID"]==$params["userID"];');
$task->addChild('updatePost');
$role = $this->auth->createRole('reader');
$role->addChild('readPost');
$role = $this->auth->createRole('author');
$role->addChild('reader');
$role->addChild('createPost');
$role->addChild('updateOwnPost');
$role = $this->auth->createRole('editor');
$role->addChild('reader');
$role->addChild('updatePost');
$role = $this->auth->createRole('admin');
$role->addChild('editor');
$role->addChild('author');
$role->addChild('deletePost');
$this->auth->assign('reader A', 'reader');
$this->auth->assign('author B', 'author');
$this->auth->assign('editor C', 'editor');
$this->auth->assign('admin D', 'admin');
}
}
<?php
namespace yiiunit\framework\rbac;
use Yii;
use yii\rbac\PhpManager;
require_once(__DIR__ . '/ManagerTestBase.php');
class PhpManagerTest extends ManagerTestBase
{
public function setUp()
{
$authFile = Yii::$app->getRuntimePath() . '/rbac.php';
@unlink($authFile);
$this->auth = new PhpManager;
$this->auth->authFile = $authFile;
$this->auth->init();
$this->prepareData();
}
public function tearDown()
{
@unlink($this->auth->authFile);
}
public function testSaveLoad()
{
$this->auth->save();
$this->auth->clearAll();
$this->auth->load();
$this->testCheckAccess();
}
}
<?php
require(__DIR__ . '/../../../framework/yii.php');
require(__DIR__ . '/../../../yii/Yii.php');
$application = new yii\web\Application('test', __DIR__ . '/protected');
$application->run();
......@@ -158,8 +158,8 @@ class YiiBase
{
foreach ($namespaces as $name => $path) {
if ($name !== '') {
$name = '@' . str_replace('\\', '/', $name);
static::setAlias($name, $path);
$name = trim(strtr($name, array('\\' => '/', '_' => '/')), '/');
static::setAlias('@' . $name, rtrim($path, '/\\') . '/' . $name);
}
}
}
......@@ -370,7 +370,8 @@ class YiiBase
include($classFile);
if (class_exists($className, false) || interface_exists($className, false)) {
if (class_exists($className, false) || interface_exists($className, false) ||
function_exists('trait_exists') && trait_exists($className, false)) {
return true;
} else {
throw new UnknownClassException("Unable to find '$className' in file: $classFile");
......@@ -451,12 +452,12 @@ class YiiBase
}
$args = func_get_args();
array_shift($args); // remove $config
if ($config !== array()) {
if (!empty($config)) {
$args[] = $config;
}
return $reflection->newInstanceArgs($args);
} else {
return $config === array() ? new $class : new $class($config);
return empty($config) ? new $class : new $class($config);
}
}
......
......@@ -28,4 +28,18 @@ return array(
),
'depends' => array('yii', 'yii/validation'),
),
'yii/captcha' => array(
'sourcePath' => __DIR__ . '/assets',
'js' => array(
'yii.captcha.js',
),
'depends' => array('yii'),
),
'yii/debug' => array(
'sourcePath' => __DIR__ . '/assets',
'js' => array(
'yii.debug.js',
),
'depends' => array('yii'),
),
);
......@@ -116,8 +116,8 @@
});
},
options: function() {
return this.data('yiiActiveForm').settings;
data: function() {
return this.data('yiiActiveForm');
},
submitForm: function () {
......@@ -384,4 +384,4 @@
}
};
})(window.jQuery);
\ No newline at end of file
})(window.jQuery);
/**
* Yii Captcha widget.
*
* This is the JavaScript widget used by the yii\widgets\Captcha widget.
*
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
(function ($) {
$.fn.yiiCaptcha = function (method) {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || !method) {
return methods.init.apply(this, arguments);
} else {
$.error('Method ' + method + ' does not exist on jQuery.yiiCaptcha');
return false;
}
};
var defaults = {
refreshUrl: undefined,
hashKey: undefined
};
var methods = {
init: function (options) {
return this.each(function () {
var $e = $(this);
var settings = $.extend({}, defaults, options || {});
$e.data('yiiCaptcha', {
settings: settings
});
$e.on('click.yiiCaptcha', function() {
methods.refresh.apply($e);
return false;
});
});
},
refresh: function () {
var $e = this,
settings = this.data('yiiCaptcha').settings;
$.ajax({
url: $e.data('yiiCaptcha').settings.refreshUrl,
dataType: 'json',
cache: false,
success: function(data) {
$e.attr('src', data['url']);
$('body').data(settings.hashKey, [data['hash1'], data['hash2']]);
}
});
},
destroy: function () {
return this.each(function () {
$(window).unbind('.yiiCaptcha');
$(this).removeData('yiiCaptcha');
});
},
data: function() {
return this.data('yiiCaptcha');
}
};
})(window.jQuery);
/**
* Yii debug module.
*
* This JavaScript module provides the functions needed by the Yii debug toolbar.
*
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
yii.debug = (function ($) {
return {
load: function (id, url) {
$.ajax({
url: url,
//dataType: 'json',
success: function(data) {
var $e = $('#' + id);
$e.html(data);
}
});
}
};
})(jQuery);
/**
* Yii validation module.
*
* This JavaScript module provides the validation methods for the built-in validaotrs.
* This JavaScript module provides the validation methods for the built-in validators.
*
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
......
......@@ -8,7 +8,6 @@
namespace yii\base;
use Yii;
use yii\helpers\FileHelper;
/**
* Application is the base class for all application classes.
......@@ -85,6 +84,13 @@ class Application extends Module
} else {
throw new InvalidConfigException('The "basePath" configuration is required.');
}
if (isset($config['timeZone'])) {
$this->setTimeZone($config['timeZone']);
unset($config['timeZone']);
} elseif (!ini_get('date.timezone')) {
$this->setTimeZone('UTC');
}
$this->registerErrorHandlers();
$this->registerCoreComponents();
......@@ -223,6 +229,8 @@ class Application extends Module
/**
* Returns the time zone used by this application.
* This is a simple wrapper of PHP function date_default_timezone_get().
* If time zone is not configured in php.ini or application config,
* it will be set to UTC by default.
* @return string the time zone used by this application.
* @see http://php.net/manual/en/function.date-default-timezone-get.php
*/
......@@ -306,12 +314,12 @@ class Application extends Module
}
/**
* @return null|Component
* @todo
* Returns the auth manager for this application.
* @return \yii\rbac\Manager the auth manager for this application.
*/
public function getAuthManager()
{
return $this->getComponent('auth');
return $this->getComponent('authManager');
}
/**
......
......@@ -8,7 +8,6 @@
namespace yii\base;
use Yii;
use yii\helpers\FileHelper;
use yii\helpers\StringHelper;
/**
......@@ -183,7 +182,7 @@ class Controller extends Component
}
}
if ($missing !== array()) {
if (!empty($missing)) {
throw new InvalidRequestException(Yii::t('yii|Missing required parameters: {params}', array(
'{params}' => implode(', ', $missing),
)));
......@@ -204,7 +203,7 @@ class Controller extends Component
public function forward($route, $params = array())
{
$status = $this->run($route, $params);
exit($status);
Yii::$app->end($status);
}
/**
......
......@@ -51,7 +51,7 @@ class Dictionary extends Object implements \IteratorAggregate, \ArrayAccess, \Co
*/
public function __construct($data = array(), $config = array())
{
if ($data !== array()) {
if (!empty($data)) {
$this->copyFrom($data);
}
parent::__construct($config);
......@@ -187,7 +187,7 @@ class Dictionary extends Object implements \IteratorAggregate, \ArrayAccess, \Co
public function copyFrom($data)
{
if (is_array($data) || $data instanceof \Traversable) {
if ($this->_d !== array()) {
if (!empty($this->_d)) {
$this->removeAll();
}
if ($data instanceof self) {
......
......@@ -15,6 +15,14 @@ namespace yii\base;
class Object
{
/**
* @return string the fully qualified name of this class.
*/
public static function className()
{
return get_called_class();
}
/**
* Constructor.
* The default implementation does two things:
*
......
......@@ -58,7 +58,7 @@ class Vector extends Object implements \IteratorAggregate, \ArrayAccess, \Counta
*/
public function __construct($data = array(), $config = array())
{
if ($data !== array()) {
if (!empty($data)) {
$this->copyFrom($data);
}
parent::__construct($config);
......
......@@ -23,6 +23,14 @@ use yii\helpers\Html;
class View extends Component
{
/**
* @event ViewEvent an event that is triggered by [[beginPage()]].
*/
const EVENT_BEGIN_PAGE = 'beginPage';
/**
* @event ViewEvent an event that is triggered by [[endPage()]].
*/
const EVENT_END_PAGE = 'endPage';
/**
* @event ViewEvent an event that is triggered by [[renderFile()]] right before it renders a view file.
*/
const EVENT_BEFORE_RENDER = 'beforeRender';
......@@ -555,6 +563,8 @@ class View extends Component
{
ob_start();
ob_implicit_flush(false);
$this->trigger(self::EVENT_BEGIN_PAGE);
}
/**
......@@ -562,6 +572,8 @@ class View extends Component
*/
public function endPage()
{
$this->trigger(self::EVENT_END_PAGE);
$content = ob_get_clean();
echo strtr($content, array(
self::PL_HEAD => $this->renderHeadHtml(),
......
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