Skip to content

Commit 1ade3b7

Browse files
Merge pull request #385 from magento-frontend/bugs-pr
Bugs MAGETWO-57656 Base url should be processed while sending email
2 parents fecf38c + 43a9594 commit 1ade3b7

File tree

6 files changed

+296
-25
lines changed

6 files changed

+296
-25
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
/**
3+
* Copyright © 2016 Magento. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Email\Model\Template\Css;
7+
8+
use Magento\Framework\View\Asset\NotationResolver\Variable;
9+
use Magento\Framework\View\Asset\Repository;
10+
11+
class Processor
12+
{
13+
/**
14+
* @var Repository
15+
*/
16+
private $assetRepository;
17+
18+
/**
19+
* @param Repository $assetRepository
20+
*/
21+
public function __construct(Repository $assetRepository)
22+
{
23+
$this->assetRepository = $assetRepository;
24+
}
25+
26+
/**
27+
* Process css placeholders
28+
*
29+
* @param string $css
30+
* @return string
31+
*/
32+
public function process($css)
33+
{
34+
$matches = [];
35+
if (preg_match_all(Variable::VAR_REGEX, $css, $matches, PREG_SET_ORDER)) {
36+
$replacements = [];
37+
foreach ($matches as $match) {
38+
if (!isset($replacements[$match[0]])) {
39+
$replacements[$match[0]] = $this->getPlaceholderValue($match[1]);
40+
}
41+
}
42+
$css = str_replace(array_keys($replacements), $replacements, $css);
43+
}
44+
return $css;
45+
}
46+
47+
/**
48+
* Retrieve placeholder value
49+
*
50+
* @param string $placeholder
51+
* @return string
52+
*/
53+
private function getPlaceholderValue($placeholder)
54+
{
55+
/** @var \Magento\Framework\View\Asset\File\FallbackContext $context */
56+
$context = $this->assetRepository->getStaticViewFileContext();
57+
58+
switch ($placeholder) {
59+
case 'base_url_path':
60+
return $context->getBaseUrl();
61+
case 'locale':
62+
return $context->getLocale();
63+
default:
64+
return '';
65+
}
66+
}
67+
}

app/code/Magento/Email/Model/Template/Filter.php

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
*/
66
namespace Magento\Email\Model\Template;
77

8+
use Magento\Framework\App\ObjectManager;
9+
use Magento\Framework\Filesystem;
10+
use Magento\Framework\Filesystem\Directory\ReadInterface;
811
use Magento\Framework\View\Asset\ContentProcessorException;
912
use Magento\Framework\View\Asset\ContentProcessorInterface;
1013

@@ -153,6 +156,16 @@ class Filter extends \Magento\Framework\Filter\Template
153156
*/
154157
protected $configVariables;
155158

159+
/**
160+
* @var \Magento\Email\Model\Template\Css\Processor
161+
*/
162+
private $cssProcessor;
163+
164+
/**
165+
* @var ReadInterface
166+
*/
167+
private $pubDirectory;
168+
156169
/**
157170
* @param \Magento\Framework\Stdlib\StringUtils $string
158171
* @param \Psr\Log\LoggerInterface $logger
@@ -203,6 +216,31 @@ public function __construct(
203216
parent::__construct($string, $variables);
204217
}
205218

219+
/**
220+
* @deprecated
221+
* @return Css\Processor
222+
*/
223+
private function getCssProcessor()
224+
{
225+
if (!$this->cssProcessor) {
226+
$this->cssProcessor = ObjectManager::getInstance()->get(Css\Processor::class);
227+
}
228+
return $this->cssProcessor;
229+
}
230+
231+
/**
232+
* @deprecated
233+
* @param string $dirType
234+
* @return ReadInterface
235+
*/
236+
private function getPubDirectory($dirType)
237+
{
238+
if (!$this->pubDirectory) {
239+
$this->pubDirectory = ObjectManager::getInstance()->get(Filesystem::class)->getDirectoryRead($dirType);
240+
}
241+
return $this->pubDirectory;
242+
}
243+
206244
/**
207245
* Set use absolute links flag
208246
*
@@ -788,7 +826,9 @@ public function cssDirective($construction)
788826
return '/* ' . __('"file" parameter must be specified') . ' */';
789827
}
790828

791-
$css = $this->getCssFilesContent([$params['file']]);
829+
$css = $this->getCssProcessor()->process(
830+
$this->getCssFilesContent([$params['file']])
831+
);
792832

793833
if (strpos($css, ContentProcessorInterface::ERROR_MESSAGE_PREFIX) !== false) {
794834
// Return compilation error wrapped in CSS comment
@@ -889,7 +929,12 @@ public function getCssFilesContent(array $files)
889929
try {
890930
foreach ($files as $file) {
891931
$asset = $this->_assetRepo->createAsset($file, $designParams);
892-
$css .= $asset->getContent();
932+
$pubDirectory = $this->getPubDirectory($asset->getContext()->getBaseDirType());
933+
if ($pubDirectory->isExist($asset->getPath())) {
934+
$css .= $pubDirectory->readFile($asset->getPath());
935+
} else {
936+
$css .= $asset->getContent();
937+
}
893938
}
894939
} catch (ContentProcessorException $exception) {
895940
$css = $exception->getMessage();
@@ -914,6 +959,8 @@ public function applyInlineCss($html)
914959
$cssToInline = $this->getCssFilesContent(
915960
$this->getInlineCssFiles()
916961
);
962+
$cssToInline = $this->getCssProcessor()->process($cssToInline);
963+
917964
// Only run Emogrify if HTML and CSS contain content
918965
if ($html && $cssToInline) {
919966
try {
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
/**
3+
* Copyright © 2016 Magento. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Email\Test\Unit\Model\Template\Css;
7+
8+
use Magento\Email\Model\Template\Css\Processor;
9+
use Magento\Framework\View\Asset\File\FallbackContext;
10+
use Magento\Framework\View\Asset\Repository;
11+
12+
class ProcessorTest extends \PHPUnit_Framework_TestCase
13+
{
14+
/**
15+
* @var Processor
16+
*/
17+
protected $processor;
18+
19+
/**
20+
* @var Repository|\PHPUnit_Framework_MockObject_MockObject
21+
*/
22+
protected $assetRepository;
23+
24+
/**
25+
* @var FallbackContext|\PHPUnit_Framework_MockObject_MockObject
26+
*/
27+
protected $fallbackContext;
28+
29+
public function setUp()
30+
{
31+
$this->assetRepository = $this->getMockBuilder(Repository::class)
32+
->disableOriginalConstructor()
33+
->getMock();
34+
$this->fallbackContext = $this->getMockBuilder(FallbackContext::class)
35+
->disableOriginalConstructor()
36+
->getMock();
37+
38+
$this->processor = new Processor($this->assetRepository);
39+
}
40+
41+
public function testProcess()
42+
{
43+
$url = 'http://magento.local/pub/static/';
44+
$locale = 'en_US';
45+
$css = '@import url("{{base_url_path}}frontend/_view/{{locale}}/css/email.css");';
46+
$expectedCss = '@import url("' . $url . 'frontend/_view/' . $locale . '/css/email.css");';
47+
48+
$this->assetRepository->expects($this->exactly(2))
49+
->method('getStaticViewFileContext')
50+
->willReturn($this->fallbackContext);
51+
$this->fallbackContext->expects($this->once())
52+
->method('getBaseUrl')
53+
->willReturn($url);
54+
$this->fallbackContext->expects($this->once())
55+
->method('getLocale')
56+
->willReturn($locale);
57+
$this->assertEquals($expectedCss, $this->processor->process($css));
58+
}
59+
}

app/code/Magento/Email/Test/Unit/Model/Template/FilterTest.php

Lines changed: 93 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@
55
*/
66
namespace Magento\Email\Test\Unit\Model\Template;
77

8+
use Magento\Email\Model\Template\Css\Processor;
9+
use Magento\Email\Model\Template\Filter;
10+
use Magento\Framework\App\Area;
11+
use Magento\Framework\App\Filesystem\DirectoryList;
12+
use Magento\Framework\Filesystem\Directory\ReadInterface;
13+
use Magento\Framework\View\Asset\File\FallbackContext;
14+
815
/**
916
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
1017
*/
@@ -94,7 +101,6 @@ protected function setUp()
94101

95102
$this->escaper = $this->getMockBuilder(\Magento\Framework\Escaper::class)
96103
->disableOriginalConstructor()
97-
->enableProxyingToOriginalMethods()
98104
->getMock();
99105

100106
$this->assetRepo = $this->getMockBuilder(\Magento\Framework\View\Asset\Repository::class)
@@ -138,7 +144,7 @@ protected function setUp()
138144

139145
/**
140146
* @param array|null $mockedMethods Methods to mock
141-
* @return \Magento\Email\Model\Template\Filter|\PHPUnit_Framework_MockObject_MockObject
147+
* @return Filter|\PHPUnit_Framework_MockObject_MockObject
142148
*/
143149
protected function getModel($mockedMethods = null)
144150
{
@@ -252,13 +258,23 @@ public function transDirectiveDataProvider()
252258
public function testApplyInlineCss($html, $css, $expectedResults)
253259
{
254260
$filter = $this->getModel(['getCssFilesContent']);
261+
$cssProcessor = $this->getMockBuilder(Processor::class)
262+
->disableOriginalConstructor()
263+
->getMock();
264+
$reflectionClass = new \ReflectionClass(Filter::class);
265+
$reflectionProperty = $reflectionClass->getProperty('cssProcessor');
266+
$reflectionProperty->setAccessible(true);
267+
$reflectionProperty->setValue($filter, $cssProcessor);
268+
$cssProcessor->expects($this->any())
269+
->method('process')
270+
->willReturnArgument(0);
255271

256272
$filter->expects($this->exactly(count($expectedResults)))
257273
->method('getCssFilesContent')
258274
->will($this->returnValue($css));
259275

260276
$designParams = [
261-
'area' => \Magento\Framework\App\Area::AREA_FRONTEND,
277+
'area' => Area::AREA_FRONTEND,
262278
'theme' => 'themeId',
263279
'locale' => 'localeId',
264280
];
@@ -269,6 +285,60 @@ public function testApplyInlineCss($html, $css, $expectedResults)
269285
}
270286
}
271287

288+
public function testGetCssFilesContent()
289+
{
290+
$file = 'css/email.css';
291+
$path = Area::AREA_FRONTEND . '/themeId/localeId';
292+
$css = 'p{color:black}';
293+
$designParams = [
294+
'area' => Area::AREA_FRONTEND,
295+
'theme' => 'themeId',
296+
'locale' => 'localeId',
297+
];
298+
$filter = $this->getModel();
299+
300+
$asset = $this->getMockBuilder(\Magento\Framework\View\Asset\File::class)
301+
->disableOriginalConstructor()
302+
->getMock();
303+
304+
$fallbackContext = $this->getMockBuilder(FallbackContext::class)
305+
->disableOriginalConstructor()
306+
->getMock();
307+
$fallbackContext->expects($this->once())
308+
->method('getBaseDirType')
309+
->willReturn(DirectoryList::STATIC_VIEW);
310+
$asset->expects($this->atLeastOnce())
311+
->method('getContext')
312+
->willReturn($fallbackContext);
313+
314+
$asset->expects($this->atLeastOnce())
315+
->method('getPath')
316+
->willReturn($path . DIRECTORY_SEPARATOR . $file);
317+
$this->assetRepo->expects($this->once())
318+
->method('createAsset')
319+
->with($file, $designParams)
320+
->willReturn($asset);
321+
322+
$pubDirectory = $this->getMockBuilder(ReadInterface::class)
323+
->getMockForAbstractClass();
324+
$reflectionClass = new \ReflectionClass(Filter::class);
325+
$reflectionProperty = $reflectionClass->getProperty('pubDirectory');
326+
$reflectionProperty->setAccessible(true);
327+
$reflectionProperty->setValue($filter, $pubDirectory);
328+
$pubDirectory->expects($this->once())
329+
->method('isExist')
330+
->with($path . DIRECTORY_SEPARATOR . $file)
331+
->willReturn(true);
332+
$pubDirectory->expects($this->once())
333+
->method('readFile')
334+
->with($path . DIRECTORY_SEPARATOR . $file)
335+
->willReturn($css);
336+
337+
$filter->setDesignParams($designParams);
338+
339+
$this->assertEquals($css, $filter->getCssFilesContent([$file]));
340+
}
341+
272342
/**
273343
* @return array
274344
*/
@@ -301,7 +371,19 @@ public function applyInlineCssDataProvider()
301371
*/
302372
public function testApplyInlineCssThrowsExceptionWhenDesignParamsNotSet()
303373
{
304-
$this->getModel()->applyInlineCss('test');
374+
$filter = $this->getModel();
375+
$cssProcessor = $this->getMockBuilder(Processor::class)
376+
->disableOriginalConstructor()
377+
->getMock();
378+
$reflectionClass = new \ReflectionClass(Filter::class);
379+
$reflectionProperty = $reflectionClass->getProperty('cssProcessor');
380+
$reflectionProperty->setAccessible(true);
381+
$reflectionProperty->setValue($filter, $cssProcessor);
382+
$cssProcessor->expects($this->any())
383+
->method('process')
384+
->willReturnArgument(0);
385+
386+
$filter->applyInlineCss('test');
305387
}
306388

307389
/**
@@ -348,7 +430,10 @@ public function testConfigDirectiveAvailable()
348430
$construction = ["{{config path={$path}}}", 'config', " path={$path}"];
349431
$scopeConfigValue = 'value';
350432

351-
$storeMock = $this->getMock(\Magento\Store\Api\Data\StoreInterface::class, [], [], '', false);
433+
$storeMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class)
434+
->disableOriginalConstructor()
435+
->getMock();
436+
352437
$this->storeManager->expects($this->once())->method('getStore')->willReturn($storeMock);
353438
$storeMock->expects($this->once())->method('getId')->willReturn(1);
354439

@@ -369,7 +454,9 @@ public function testConfigDirectiveUnavailable()
369454
$construction = ["{{config path={$path}}}", 'config', " path={$path}"];
370455
$scopeConfigValue = '';
371456

372-
$storeMock = $this->getMock(\Magento\Store\Api\Data\StoreInterface::class, [], [], '', false);
457+
$storeMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class)
458+
->disableOriginalConstructor()
459+
->getMock();
373460
$this->storeManager->expects($this->once())->method('getStore')->willReturn($storeMock);
374461
$storeMock->expects($this->once())->method('getId')->willReturn(1);
375462

0 commit comments

Comments
 (0)