Commit 1d3df026 by Qiang Xue

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

parents eb2d4fbd ac729274
...@@ -517,17 +517,77 @@ EOD ...@@ -517,17 +517,77 @@ EOD
*/ */
public function combineCssFiles($inputFiles, $outputFile) public function combineCssFiles($inputFiles, $outputFile)
{ {
// todo: adjust url() references in CSS files
$content = ''; $content = '';
foreach ($inputFiles as $file) { foreach ($inputFiles as $file) {
$content .= "/*** BEGIN FILE: $file ***/\n" $content .= "/*** BEGIN FILE: $file ***/\n"
. file_get_contents($file) . $this->adjustCssUrl(file_get_contents($file), dirname($file), dirname($outputFile))
. "/*** END FILE: $file ***/\n"; . "/*** END FILE: $file ***/\n";
} }
file_put_contents($outputFile, $content); file_put_contents($outputFile, $content);
} }
/** /**
* Adjusts CSS content allowing URL references pointing to the original resources.
* @param string $cssContent source CSS content.
* @param string $inputFilePath input CSS file name.
* @param string $outputFilePath output CSS file name.
* @return string adjusted CSS content.
*/
protected function adjustCssUrl($cssContent, $inputFilePath, $outputFilePath)
{
$sharedPathParts = array();
$inputFilePathParts = explode('/', $inputFilePath);
$inputFilePathPartsCount = count($inputFilePathParts);
$outputFilePathParts = explode('/', $outputFilePath);
$outputFilePathPartsCount = count($outputFilePathParts);
for ($i =0; $i < $inputFilePathPartsCount && $i < $outputFilePathPartsCount; $i++) {
if ($inputFilePathParts[$i] == $outputFilePathParts[$i]) {
$sharedPathParts[] = $inputFilePathParts[$i];
} else {
break;
}
}
$sharedPath = implode('/', $sharedPathParts);
$inputFileRelativePath = trim(str_replace($sharedPath, '', $inputFilePath), '/');
$outputFileRelativePath = trim(str_replace($sharedPath, '', $outputFilePath), '/');
$inputFileRelativePathParts = explode('/', $inputFileRelativePath);
$outputFileRelativePathParts = explode('/', $outputFileRelativePath);
$callback = function($matches) use ($inputFileRelativePathParts, $outputFileRelativePathParts) {
$fullMatch = $matches[0];
$inputUrl = $matches[1];
if (preg_match('/https?:\/\//is', $inputUrl)) {
return $fullMatch;
}
$outputUrlParts = array_fill(0, count($outputFileRelativePathParts), '..');
$outputUrlParts = array_merge($outputUrlParts, $inputFileRelativePathParts);
if (strpos($inputUrl, '/') !== false) {
$inputUrlParts = explode('/', $inputUrl);
foreach ($inputUrlParts as $key => $inputUrlPart) {
if ($inputUrlPart == '..') {
array_pop($outputUrlParts);
unset($inputUrlParts[$key]);
}
}
$outputUrlParts[] = implode('/', $inputUrlParts);
} else {
$outputUrlParts[] = $inputUrl;
}
$outputUrl = implode('/', $outputUrlParts);
return str_replace($inputUrl, $outputUrl, $fullMatch);
};
$cssContent = preg_replace_callback('/url\(["\']?([^"]*)["\']?\)/is', $callback, $cssContent);
return $cssContent;
}
/**
* Creates template of configuration file for [[actionCompress]]. * Creates template of configuration file for [[actionCompress]].
* @param string $configFile output file name. * @param string $configFile output file name.
*/ */
......
...@@ -56,15 +56,38 @@ use yii\helpers\Html; ...@@ -56,15 +56,38 @@ use yii\helpers\Html;
class Accordion extends Widget class Accordion extends Widget
{ {
/** /**
* @var array list of collapsible sections. * @var array the HTML attributes for the widget container tag. The following special options are recognized:
*
* - tag: string, defaults to "div", the tag name of the container tag of this widget
*/
public $options = array();
/**
* @var array list of collapsible items. Each item can be an array of the following structure:
*
* ~~~
* array(
* 'header' => 'Item header',
* 'content' => 'Item content',
* // the HTML attributes of the item header container tag. This will overwrite "headerOptions".
* 'headerOptions' => array(),
* // the HTML attributes of the item container tag. This will overwrite "itemOptions".
* 'options' => array(),
* )
* ~~~
*/ */
public $items = array(); public $items = array();
/** /**
* @var array list of individual collabsible section default options. * @var array list of HTML attributes for the item container tags. This will be overwritten
* by the "options" set in individual [[items]]. The following special options are recognized:
*
* - tag: string, defaults to "div", the tag name of the item container tags.
*/ */
public $itemOptions = array(); public $itemOptions = array();
/** /**
* @var array list of individual collabsible section header default options. * @var array list of HTML attributes for the item header container tags. This will be overwritten
* by the "headerOptions" set in individual [[items]]. The following special options are recognized:
*
* - tag: string, defaults to "h3", the tag name of the item container tags.
*/ */
public $headerOptions = array(); public $headerOptions = array();
...@@ -83,7 +106,7 @@ class Accordion extends Widget ...@@ -83,7 +106,7 @@ class Accordion extends Widget
} }
/** /**
* Renders collapsible sections as specified on [[items]]. * Renders collapsible items as specified on [[items]].
* @return string the rendering result. * @return string the rendering result.
* @throws InvalidConfigException. * @throws InvalidConfigException.
*/ */
......
...@@ -169,6 +169,23 @@ class AssetControllerTest extends TestCase ...@@ -169,6 +169,23 @@ class AssetControllerTest extends TestCase
} }
} }
/**
* Invokes the asset controller method even if it is protected.
* @param string $methodName name of the method to be invoked.
* @param array $args method arguments.
* @return mixed method invoke result.
*/
protected function invokeAssetControllerMethod($methodName, array $args = array())
{
$controller = $this->createAssetController();
$controllerClassReflection = new ReflectionClass(get_class($controller));
$methodReflection = $controllerClassReflection->getMethod($methodName);
$methodReflection->setAccessible(true);
$result = $methodReflection->invokeArgs($controller, $args);
$methodReflection->setAccessible(false);
return $result;
}
// Tests : // Tests :
public function testActionTemplate() public function testActionTemplate()
...@@ -237,4 +254,65 @@ class AssetControllerTest extends TestCase ...@@ -237,4 +254,65 @@ class AssetControllerTest extends TestCase
$this->assertContains($content, $compressedJsFileContent, "Source of '{$name}' is missing in combined file!"); $this->assertContains($content, $compressedJsFileContent, "Source of '{$name}' is missing in combined file!");
} }
} }
/**
* Data provider for [[testAdjustCssUrl()]].
* @return array test data.
*/
public function adjustCssUrlDataProvider()
{
return array(
array(
'.published-same-dir-class {background-image: url(published_same_dir.png);}',
'/test/base/path/assets/input',
'/test/base/path/assets/output',
'.published-same-dir-class {background-image: url(../input/published_same_dir.png);}',
),
array(
'.published-relative-dir-class {background-image: url(../img/published_relative_dir.png);}',
'/test/base/path/assets/input',
'/test/base/path/assets/output',
'.published-relative-dir-class {background-image: url(../img/published_relative_dir.png);}',
),
array(
'.static-same-dir-class {background-image: url(\'static_same_dir.png\');}',
'/test/base/path/css',
'/test/base/path/assets/output',
'.static-same-dir-class {background-image: url(\'../../css/static_same_dir.png\');}',
),
array(
'.static-relative-dir-class {background-image: url("../img/static_relative_dir.png");}',
'/test/base/path/css',
'/test/base/path/assets/output',
'.static-relative-dir-class {background-image: url("../../img/static_relative_dir.png");}',
),
array(
'.absolute-url-class {background-image: url(http://domain.com/img/image.gif);}',
'/test/base/path/assets/input',
'/test/base/path/assets/output',
'.absolute-url-class {background-image: url(http://domain.com/img/image.gif);}',
),
array(
'.absolute-url-secure-class {background-image: url(https://secure.domain.com/img/image.gif);}',
'/test/base/path/assets/input',
'/test/base/path/assets/output',
'.absolute-url-secure-class {background-image: url(https://secure.domain.com/img/image.gif);}',
),
);
}
/**
* @dataProvider adjustCssUrlDataProvider
*
* @param $cssContent
* @param $inputFilePath
* @param $outputFilePath
* @param $expectedCssContent
*/
public function testAdjustCssUrl($cssContent, $inputFilePath, $outputFilePath, $expectedCssContent)
{
$adjustedCssContent = $this->invokeAssetControllerMethod('adjustCssUrl', array($cssContent, $inputFilePath, $outputFilePath));
$this->assertEquals($expectedCssContent, $adjustedCssContent, 'Unable to adjust CSS correctly!');
}
} }
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