diff --git a/.htaccess b/.htaccess index 2998f48f7addce87c68296ed33d68837bd439b6b..404488eb7ff0ecc6197ab8c100555c79eb1cf0ce 100644 --- a/.htaccess +++ b/.htaccess @@ -1,3 +1,8 @@ +############################################ +## uncomment the line below to enable developer mode + +# SetEnv MAGE_MODE developer + ############################################ ## uncomment these lines for CGI mode ## make sure to specify the correct cgi php binary file name @@ -167,6 +172,8 @@ ## http://developer.yahoo.com/performance/rules.html#expires ExpiresDefault "access plus 1 year" + ExpiresByType text/html A0 + ExpiresByType text/plain A0 </IfModule> diff --git a/.htaccess.sample b/.htaccess.sample index 3a19430211147f23f6e460addf435d771380b944..133ce7de2c59badd5df4a159bb921e8a99f05eb7 100644 --- a/.htaccess.sample +++ b/.htaccess.sample @@ -1,3 +1,8 @@ +############################################ +## uncomment the line below to enable developer mode + +# SetEnv MAGE_MODE developer + ############################################ ## uncomment these lines for CGI mode ## make sure to specify the correct cgi php binary file name @@ -164,6 +169,8 @@ ## http://developer.yahoo.com/performance/rules.html#expires ExpiresDefault "access plus 1 year" + ExpiresByType text/html A0 + ExpiresByType text/plain A0 </IfModule> diff --git a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/checkbox.phtml b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/checkbox.phtml index 00938f3c70c0f58427e5b1302dc9c77f3ee6ed0c..505a554fe65a1a36a891522dcbc8e9e505efb825 100644 --- a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/checkbox.phtml +++ b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/checkbox.phtml @@ -29,7 +29,7 @@ <input class="bundle-option-<?php echo $_option->getId() ?> checkbox product bundle option change-container-classname" id="bundle-option-<?php echo $_option->getId() ?>-<?php echo $_selection->getSelectionId() ?>" type="checkbox" - <?php if ($_option->getRequired()) echo 'data-validate="{\'validate-one-required-by-name\':true}"'?> + <?php if ($_option->getRequired()) echo 'data-validate="{\'validate-one-required-by-name\':\'input[name^="bundle_option[' . $_option->getId() . ']"]:checked\'}"'?> name="bundle_option[<?php echo $_option->getId() ?>][<?php echo $_selection->getId() ?>]" <?php if ($block->isSelected($_selection)) echo ' checked="checked"' ?> <?php if (!$_selection->isSaleable()) echo ' disabled="disabled"' ?> diff --git a/app/code/Magento/Cron/Model/Observer.php b/app/code/Magento/Cron/Model/Observer.php index 0c4330b236445e5158ced871f4c4de452a4d53e4..800e1e732710d455b0a12a728df25a01f486ba5f 100644 --- a/app/code/Magento/Cron/Model/Observer.php +++ b/app/code/Magento/Cron/Model/Observer.php @@ -345,6 +345,13 @@ class Observer return $this; } + // check how long the record should stay unprocessed before marked as MISSED + $scheduleLifetime = (int)$this->_scopeConfig->getValue( + 'system/cron/' . $groupId . '/' . self::XML_PATH_SCHEDULE_LIFETIME, + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + $scheduleLifetime = $scheduleLifetime * self::SECONDS_IN_MINUTE; + /** * @var \Magento\Cron\Model\Resource\Schedule\Collection $history */ @@ -370,7 +377,9 @@ class Observer $now = $this->timezone->scopeTimeStamp(); /** @var Schedule $record */ foreach ($history as $record) { - if (strtotime($record->getExecutedAt()) < $now - $historyLifetimes[$record->getStatus()]) { + $checkTime = $record->getExecutedAt() ? strtotime($record->getExecutedAt()) : + strtotime($record->getScheduledAt()) + $scheduleLifetime; + if ($checkTime < $now - $historyLifetimes[$record->getStatus()]) { $record->delete(); } } diff --git a/app/code/Magento/Cron/Test/Unit/Model/ObserverTest.php b/app/code/Magento/Cron/Test/Unit/Model/ObserverTest.php index 1b9c5cd798101a15d4fd8ab122dacc1afa67bba0..1f67cb67065623e9bc84a9e41c12baa9bb4a8ae7 100644 --- a/app/code/Magento/Cron/Test/Unit/Model/ObserverTest.php +++ b/app/code/Magento/Cron/Test/Unit/Model/ObserverTest.php @@ -5,6 +5,8 @@ */ namespace Magento\Cron\Test\Unit\Model; +use Magento\Cron\Model\Schedule; + /** * Class \Magento\Cron\Test\Unit\Model\ObserverTest * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -607,4 +609,84 @@ class ObserverTest extends \PHPUnit_Framework_TestCase $this->_observer->dispatch(''); } + + public function testMissedJobsCleanedInTime() + { + /* 1. Initialize dependencies of _generate() method which is called first */ + $jobConfig = [ + 'test_group' => ['test_job1' => ['instance' => 'CronJob', 'method' => 'execute']], + ]; + + // This item was scheduled 2 days ago + $schedule1 = $this->getMockBuilder( + 'Magento\Cron\Model\Schedule' + )->disableOriginalConstructor()->setMethods( + ['getExecutedAt', 'getScheduledAt', 'getStatus', 'delete', '__wakeup'] + )->getMock(); + $schedule1->expects($this->any())->method('getExecutedAt')->will($this->returnValue(null)); + $schedule1->expects($this->any())->method('getScheduledAt')->will($this->returnValue('-2 day -1 hour')); + $schedule1->expects($this->any())->method('getStatus')->will($this->returnValue(Schedule::STATUS_MISSED)); + //we expect this job be deleted from the list + $schedule1->expects($this->once())->method('delete')->will($this->returnValue(true)); + + // This item was scheduled 1 day ago + $schedule2 = $this->getMockBuilder( + 'Magento\Cron\Model\Schedule' + )->disableOriginalConstructor()->setMethods( + ['getExecutedAt', 'getScheduledAt', 'getStatus', 'delete', '__wakeup'] + )->getMock(); + $schedule2->expects($this->any())->method('getExecutedAt')->will($this->returnValue(null)); + $schedule2->expects($this->any())->method('getScheduledAt')->will($this->returnValue('-1 day')); + $schedule2->expects($this->any())->method('getStatus')->will($this->returnValue(Schedule::STATUS_MISSED)); + //we don't expect this job be deleted from the list + $schedule2->expects($this->never())->method('delete'); + + $this->_collection->addItem($schedule1); + $this->_config->expects($this->once())->method('getJobs')->will($this->returnValue($jobConfig)); + + //get configuration value CACHE_KEY_LAST_HISTORY_CLEANUP_AT in the "_generate()" + $this->_cache->expects($this->at(0))->method('load')->will($this->returnValue(time() + 10000000)); + //get configuration value CACHE_KEY_LAST_HISTORY_CLEANUP_AT in the "_cleanup()" + $this->_cache->expects($this->at(1))->method('load')->will($this->returnValue(time() - 10000000)); + + $this->_scopeConfig->expects($this->at(0))->method('getValue') + ->with($this->equalTo('system/cron/test_group/use_separate_process')) + ->will($this->returnValue(0)); + $this->_scopeConfig->expects($this->at(1))->method('getValue') + ->with($this->equalTo('system/cron/test_group/schedule_generate_every')) + ->will($this->returnValue(0)); + $this->_scopeConfig->expects($this->at(2))->method('getValue') + ->with($this->equalTo('system/cron/test_group/history_cleanup_every')) + ->will($this->returnValue(0)); + $this->_scopeConfig->expects($this->at(3))->method('getValue') + ->with($this->equalTo('system/cron/test_group/schedule_lifetime')) + ->will($this->returnValue(2*24*60)); + $this->_scopeConfig->expects($this->at(4))->method('getValue') + ->with($this->equalTo('system/cron/test_group/history_success_lifetime')) + ->will($this->returnValue(0)); + $this->_scopeConfig->expects($this->at(5))->method('getValue') + ->with($this->equalTo('system/cron/test_group/history_failure_lifetime')) + ->will($this->returnValue(0)); + + /* 2. Initialize dependencies of _cleanup() method which is called second */ + $scheduleMock = $this->getMockBuilder('Magento\Cron\Model\Schedule')->disableOriginalConstructor()->getMock(); + $scheduleMock->expects($this->any())->method('getCollection')->will($this->returnValue($this->_collection)); + $this->_scheduleFactory->expects($this->at(0))->method('create')->will($this->returnValue($scheduleMock)); + + $collection = $this->getMockBuilder( + 'Magento\Cron\Model\Resource\Schedule\Collection' + )->setMethods( + ['addFieldToFilter', 'load', '__wakeup'] + )->disableOriginalConstructor()->getMock(); + $collection->expects($this->any())->method('addFieldToFilter')->will($this->returnSelf()); + $collection->expects($this->any())->method('load')->will($this->returnSelf()); + $collection->addItem($schedule1); + $collection->addItem($schedule2); + + $scheduleMock = $this->getMockBuilder('Magento\Cron\Model\Schedule')->disableOriginalConstructor()->getMock(); + $scheduleMock->expects($this->any())->method('getCollection')->will($this->returnValue($collection)); + $this->_scheduleFactory->expects($this->at(1))->method('create')->will($this->returnValue($scheduleMock)); + + $this->_observer->dispatch(''); + } } diff --git a/app/code/Magento/Customer/Model/AccountManagement.php b/app/code/Magento/Customer/Model/AccountManagement.php index 129f46990118ab34ef0bd442198d93f5a5d325c3..59fbcb7f9f09fd5e28a2bc50faaff02255938ecc 100644 --- a/app/code/Magento/Customer/Model/AccountManagement.php +++ b/app/code/Magento/Customer/Model/AccountManagement.php @@ -839,6 +839,7 @@ class AccountManagement implements AccountManagementInterface $storeId = $this->getWebsiteStoreId($customer); } + $customerEmailData = $this->getFullCustomerObject($customer); /** @var \Magento\Framework\Mail\TransportInterface $transport */ $transport = $this->transportBuilder->setTemplateIdentifier( $this->scopeConfig->getValue( @@ -849,7 +850,7 @@ class AccountManagement implements AccountManagementInterface )->setTemplateOptions( ['area' => \Magento\Framework\App\Area::AREA_FRONTEND, 'store' => $storeId] )->setTemplateVars( - ['customer' => $customer, 'store' => $this->storeManager->getStore($storeId)] + ['customer' => $customerEmailData, 'store' => $this->storeManager->getStore($storeId)] )->setFrom( $this->scopeConfig->getValue( self::XML_PATH_FORGOT_EMAIL_IDENTITY, diff --git a/app/code/Magento/Customer/etc/data_source/customer_address.xml b/app/code/Magento/Customer/etc/data_source/customer_address.xml index 3f3cdd4b0e30a273062e46dcf0fd9c770cd3a14f..e3e88b578a12604b97419639750b8317019d0e51 100644 --- a/app/code/Magento/Customer/etc/data_source/customer_address.xml +++ b/app/code/Magento/Customer/etc/data_source/customer_address.xml @@ -49,7 +49,7 @@ </field> <field name="region" source="eav" formElement="input" visible="false"/> - <field name="postcode" source="eav" formElement="post_code_fix" > + <field name="postcode" source="eav" formElement="post_code" > <constraints> <validate name="required-entry"/> </constraints> diff --git a/app/code/Magento/Developer/Model/Less/FileGenerator/PublicationDecorator.php b/app/code/Magento/Developer/Model/Less/FileGenerator/PublicationDecorator.php new file mode 100644 index 0000000000000000000000000000000000000000..d99f32d515e38db2784169e6f21c5fa0fe4034ed --- /dev/null +++ b/app/code/Magento/Developer/Model/Less/FileGenerator/PublicationDecorator.php @@ -0,0 +1,49 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Developer\Model\Less\FileGenerator; + +use Magento\Framework\Less\FileGenerator\RelatedGenerator; +use Magento\Framework\View\Asset\LocalInterface; + +/** + * Class PublicationDecorator + * Decorates generator of related assets and publishes them + * + * @package Magento\Developer\Model\Less\FileGenerator + */ +class PublicationDecorator extends RelatedGenerator +{ + /** + * @var \Magento\Framework\App\View\Asset\Publisher + */ + private $publisher; + + /** + * @param \Magento\Framework\Filesystem $filesystem + * @param \Magento\Framework\View\Asset\Repository $assetRepo + * @param \Magento\Framework\Less\File\Temporary $temporaryFile + * @param \Magento\Framework\App\View\Asset\Publisher $publisher + */ + public function __construct( + \Magento\Framework\Filesystem $filesystem, + \Magento\Framework\View\Asset\Repository $assetRepo, + \Magento\Framework\Less\File\Temporary $temporaryFile, + \Magento\Framework\App\View\Asset\Publisher $publisher + ) { + parent::__construct($filesystem, $assetRepo, $temporaryFile); + $this->publisher = $publisher; + } + + /** + * {inheritdoc} + */ + protected function generateRelatedFile($relatedFileId, LocalInterface $asset) + { + $relatedAsset = parent::generateRelatedFile($relatedFileId, $asset); + $this->publisher->publish($relatedAsset); + return $relatedAsset; + } +} diff --git a/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/AddComment.php b/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/AddComment.php index 8e8662624c7776b8c7878fd32779b9f25880efd2..70310d404c6fa4273b42a117d9d705fd0fd2d9e2 100644 --- a/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/AddComment.php +++ b/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/AddComment.php @@ -8,6 +8,7 @@ namespace Magento\Shipping\Controller\Adminhtml\Order\Shipment; use Magento\Sales\Model\Order\Email\Sender\ShipmentSender; use Magento\Backend\App\Action; +use Magento\Framework\View\Result\LayoutFactory; class AddComment extends \Magento\Backend\App\Action { @@ -21,18 +22,26 @@ class AddComment extends \Magento\Backend\App\Action */ protected $shipmentSender; + /** + * @var LayoutFactory + */ + protected $resultLayoutFactory; + /** * @param Action\Context $context * @param \Magento\Shipping\Controller\Adminhtml\Order\ShipmentLoader $shipmentLoader * @param ShipmentSender $shipmentSender + * @param LayoutFactory $resultLayoutFactory */ public function __construct( Action\Context $context, \Magento\Shipping\Controller\Adminhtml\Order\ShipmentLoader $shipmentLoader, - ShipmentSender $shipmentSender + ShipmentSender $shipmentSender, + LayoutFactory $resultLayoutFactory ) { $this->shipmentLoader = $shipmentLoader; $this->shipmentSender = $shipmentSender; + $this->resultLayoutFactory = $resultLayoutFactory; parent::__construct($context); } @@ -72,10 +81,9 @@ class AddComment extends \Magento\Backend\App\Action $this->shipmentSender->send($shipment, !empty($data['is_customer_notified']), $data['comment']); $shipment->save(); - - $this->_view->loadLayout(false); - $this->_view->getPage()->getConfig()->getTitle()->prepend(__('Shipments')); - $response = $this->_view->getLayout()->getBlock('shipment_comments')->toHtml(); + $resultLayout = $this->resultLayoutFactory->create(); + $resultLayout->addDefaultHandle(); + $response = $resultLayout->getLayout()->getBlock('shipment_comments')->toHtml(); } catch (\Magento\Framework\Exception\LocalizedException $e) { $response = ['error' => true, 'message' => $e->getMessage()]; } catch (\Exception $e) { diff --git a/app/code/Magento/Shipping/Test/Unit/Controller/Adminhtml/Order/Shipment/AddCommentTest.php b/app/code/Magento/Shipping/Test/Unit/Controller/Adminhtml/Order/Shipment/AddCommentTest.php index 80749ec0001723fc287a435a806f6988324e331b..4bc12ca36516585e31b7e146693ef2641dcbe5b7 100644 --- a/app/code/Magento/Shipping/Test/Unit/Controller/Adminhtml/Order/Shipment/AddCommentTest.php +++ b/app/code/Magento/Shipping/Test/Unit/Controller/Adminhtml/Order/Shipment/AddCommentTest.php @@ -30,21 +30,11 @@ class AddCommentTest extends \PHPUnit_Framework_TestCase */ protected $responseMock; - /** - * @var \Magento\Framework\View\Page\Title|\PHPUnit_Framework_MockObject_MockObject - */ - protected $titleMock; - /** * @var \PHPUnit_Framework_MockObject_MockObject */ protected $resultPageMock; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $pageConfigMock; - /** * @var \Magento\Sales\Model\Order\Shipment|\PHPUnit_Framework_MockObject_MockObject */ @@ -55,6 +45,11 @@ class AddCommentTest extends \PHPUnit_Framework_TestCase */ protected $viewInterfaceMock; + /** + * @var \Magento\Framework\View\Result\LayoutFactory|\PHPUnit_Framework_MockObject_MockObject + */ + protected $resultLayoutFactoryMock; + /** * @var \Magento\Framework\ObjectManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ @@ -95,9 +90,9 @@ class AddCommentTest extends \PHPUnit_Framework_TestCase '', false ); - $this->titleMock = $this->getMock( - 'Magento\Framework\View\Page\Title', - ['prepend', '__wakeup'], + $this->resultLayoutFactoryMock = $this->getMock( + 'Magento\Framework\View\Result\LayoutFactory', + ['create'], [], '', false @@ -106,9 +101,6 @@ class AddCommentTest extends \PHPUnit_Framework_TestCase $this->resultPageMock = $this->getMockBuilder('Magento\Framework\View\Result\Page') ->disableOriginalConstructor() ->getMock(); - $this->pageConfigMock = $this->getMockBuilder('Magento\Framework\View\Page\Config') - ->disableOriginalConstructor() - ->getMock(); $this->shipmentMock = $this->getMock( 'Magento\Sales\Model\Order\Shipment', @@ -136,15 +128,9 @@ class AddCommentTest extends \PHPUnit_Framework_TestCase $this->viewInterfaceMock->expects($this->any())->method('getPage')->will( $this->returnValue($this->resultPageMock) ); - $this->resultPageMock->expects($this->any())->method('getConfig')->will( - $this->returnValue($this->pageConfigMock) - ); - - $this->pageConfigMock->expects($this->any())->method('getTitle')->will($this->returnValue($this->titleMock)); $contextMock->expects($this->any())->method('getRequest')->will($this->returnValue($this->requestMock)); $contextMock->expects($this->any())->method('getResponse')->will($this->returnValue($this->responseMock)); - $contextMock->expects($this->any())->method('getTitle')->will($this->returnValue($this->titleMock)); $contextMock->expects($this->any())->method('getView')->will($this->returnValue($this->viewInterfaceMock)); $contextMock->expects($this->any()) ->method('getObjectManager') @@ -153,7 +139,8 @@ class AddCommentTest extends \PHPUnit_Framework_TestCase $this->controller = new \Magento\Shipping\Controller\Adminhtml\Order\Shipment\AddComment( $contextMock, $this->shipmentLoaderMock, - $this->shipmentSenderMock + $this->shipmentSenderMock, + $this->resultLayoutFactoryMock ); } @@ -189,15 +176,19 @@ class AddCommentTest extends \PHPUnit_Framework_TestCase $shipment = []; $tracking = []; - $layoutMock = $this->getMock('Magento\Framework\View\Layout', ['getBlock'], [], '', false); - $blockMock = $this->getMock('Magento\Shipping\Block\Adminhtml\View\Comments', ['toHtml'], [], '', false); + $resultLayoutMock = $this->getMock( + 'Magento\Framework\View\Result\Layout', + ['getBlock', 'getDefaultLayoutHandle', 'addDefaultHandle', 'getLayout'], + [], + '', + false + ); $this->requestMock->expects($this->once())->method('setParam')->with('shipment_id', $shipmentId); $this->requestMock->expects($this->once()) ->method('getPost') ->with('comment') ->will($this->returnValue($data)); - $this->titleMock->expects($this->once())->method('prepend'); $this->requestMock->expects($this->any()) ->method('getParam') ->will( @@ -221,10 +212,15 @@ class AddCommentTest extends \PHPUnit_Framework_TestCase $this->shipmentMock->expects($this->once())->method('addComment'); $this->shipmentSenderMock->expects($this->once())->method('send'); $this->shipmentMock->expects($this->once())->method('save'); - $this->viewInterfaceMock->expects($this->once())->method('loadLayout')->with(false); - $this->viewInterfaceMock->expects($this->once())->method('getLayout')->will($this->returnValue($layoutMock)); - $layoutMock->expects($this->once())->method('getBlock')->will($this->returnValue($blockMock)); - $blockMock->expects($this->once())->method('toHtml')->will($this->returnValue($result)); + $layoutMock = $this->getMock('Magento\Framework\View\Layout', ['getBlock'], [], '', false); + $blockMock = $this->getMock('Magento\Shipping\Block\Adminhtml\View\Comments', ['toHtml'], [], '', false); + $blockMock->expects($this->once())->method('toHtml')->willReturn($result); + $layoutMock->expects($this->once())->method('getBlock') + ->with('shipment_comments')->willReturn($blockMock); + $resultLayoutMock->expects($this->once())->method('getLayout')->willReturn($layoutMock); + $resultLayoutMock->expects($this->once())->method('addDefaultHandle'); + $this->resultLayoutFactoryMock->expects($this->once())->method('create') + ->will($this->returnValue($resultLayoutMock)); $this->responseMock->expects($this->once())->method('setBody')->with($result); $this->assertNull($this->controller->execute()); diff --git a/app/etc/di.xml b/app/etc/di.xml index a3da2e85a1954b11a9415d002266b285fad6b676..cb45113bb261c8ce90486a5a1d4be251fc658744 100755 --- a/app/etc/di.xml +++ b/app/etc/di.xml @@ -557,13 +557,6 @@ <argument name="publisher" xsi:type="object">developerPublisher</argument> </arguments> </type> - <type name="Magento\Framework\View\Asset\SourceFileGeneratorPool"> - <arguments> - <argument name="fileGeneratorTypes" xsi:type="array"> - <item name="less" xsi:type="object">Magento\Framework\Less\FileGenerator</item> - </argument> - </arguments> - </type> <virtualType name="developerPublisher" type="Magento\Framework\App\View\Asset\Publisher"> <arguments> <argument name="materializationStrategyFactory" xsi:type="object">developerMaterialization</argument> @@ -577,24 +570,23 @@ </argument> </arguments> </virtualType> - <virtualType name="lessFileGeneratorMaterialization" type="Magento\Framework\App\View\Asset\MaterializationStrategy\Factory"> + <type name="Magento\Framework\View\Asset\SourceFileGeneratorPool"> <arguments> - <argument name="strategiesList" xsi:type="array"> - <item name="view_preprocessed" xsi:type="object">Magento\Framework\App\View\Asset\MaterializationStrategy\Symlink</item> - <item name="default" xsi:type="object">Magento\Framework\App\View\Asset\MaterializationStrategy\Copy</item> + <argument name="fileGeneratorTypes" xsi:type="array"> + <item name="less" xsi:type="object">fileassemblerFileGenerator</item> </argument> </arguments> - </virtualType> - <virtualType name="lessFileGeneratorPublisher" type="Magento\Framework\App\View\Asset\Publisher"> + </type> + <virtualType name="fileassemblerFileGenerator" type="Magento\Framework\Less\FileGenerator"> <arguments> - <argument name="materializationStrategyFactory" xsi:type="object">lessFileGeneratorMaterialization</argument> + <argument name="relatedGenerator" xsi:type="object">fileassemblerRelatedFilesGenerator</argument> </arguments> </virtualType> - <type name="Magento\Framework\Less\FileGenerator"> + <virtualType name="fileassemblerRelatedFilesGenerator" type="Magento\Developer\Model\Less\FileGenerator\PublicationDecorator"> <arguments> - <argument name="publisher" xsi:type="object">lessFileGeneratorPublisher</argument> + <argument name="publisher" xsi:type="object">developerPublisher</argument> </arguments> - </type> + </virtualType> <virtualType name="fallbackResolverSimpleWithGroupedCache" type="Magento\Framework\View\Design\FileResolution\Fallback\Resolver\Simple"> <arguments> <argument name="cache" xsi:type="object">Magento\Framework\View\Design\FileResolution\Fallback\CacheData\Grouped</argument> diff --git a/dev/tests/functional/.htaccess b/dev/tests/functional/.htaccess index db0b8f66ad0008c5f765391d0b93515d711ab1b7..0fe8af43b87597383c5904481c15b751e52630d2 100644 --- a/dev/tests/functional/.htaccess +++ b/dev/tests/functional/.htaccess @@ -170,6 +170,8 @@ ## http://developer.yahoo.com/performance/rules.html#expires ExpiresDefault "access plus 1 year" + ExpiresByType text/html A0 + ExpiresByType text/plain A0 </IfModule> @@ -191,4 +193,4 @@ ## If running in cluster environment, uncomment this ## http://developer.yahoo.com/performance/rules.html#etags - #FileETag none \ No newline at end of file + #FileETag none diff --git a/dev/tools/Magento/Tools/Webdev/App/FileAssembler.php b/dev/tools/Magento/Tools/Webdev/App/FileAssembler.php index fb8529dd7596966ce189816f5276a222fb7bd4fa..0a07540a19c94695f0a9454f0f5f4b3b86c12182 100644 --- a/dev/tools/Magento/Tools/Webdev/App/FileAssembler.php +++ b/dev/tools/Magento/Tools/Webdev/App/FileAssembler.php @@ -9,6 +9,7 @@ namespace Magento\Tools\Webdev\App; use Magento\Framework\App; use Magento\Framework\App\State; use Magento\Framework\AppInterface; +use Magento\Framework\Filesystem; use Magento\Tools\Webdev\CliParams; use Magento\Tools\View\Deployer\Log; use Magento\Framework\View\Asset\Source; @@ -18,6 +19,7 @@ use Magento\Framework\ObjectManagerInterface; use Magento\Framework\App\ObjectManager\ConfigLoader; use Magento\Framework\View\Asset\SourceFileGeneratorPool; use Magento\Framework\View\Asset\PreProcessor\ChainFactoryInterface; +use Magento\Framework\App\Filesystem\DirectoryList; /** * Class FileAssembler @@ -60,7 +62,7 @@ class FileAssembler implements AppInterface /** * @var \Magento\Framework\Less\FileGenerator */ - private $sourceFileGeneratorPoll; + private $sourceFileGeneratorPool; /** * @var \Magento\Framework\View\Asset\Source @@ -77,6 +79,11 @@ class FileAssembler implements AppInterface */ private $chainFactory; + /** + * @var Filesystem + */ + private $filesystem; + /** * @param ObjectManagerInterface $objectManager * @param Response $response @@ -88,6 +95,7 @@ class FileAssembler implements AppInterface * @param \Magento\Framework\View\Asset\SourceFileGeneratorPool $sourceFileGeneratorPoll * @param \Magento\Tools\View\Deployer\Log $logger * @param ChainFactoryInterface $chainFactory + * @param Filesystem $filesystem * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -101,7 +109,8 @@ class FileAssembler implements AppInterface Source $assetSource, SourceFileGeneratorPool $sourceFileGeneratorPoll, Log $logger, - ChainFactoryInterface $chainFactory + ChainFactoryInterface $chainFactory, + Filesystem $filesystem ) { $this->response = $response; $this->params = $params; @@ -109,10 +118,11 @@ class FileAssembler implements AppInterface $this->objectManager = $objectManager; $this->configLoader = $configLoader; $this->assetRepo = $assetRepo; - $this->sourceFileGeneratorPoll = $sourceFileGeneratorPoll; + $this->sourceFileGeneratorPool = $sourceFileGeneratorPoll; $this->assetSource = $assetSource; $this->logger = $logger; $this->chainFactory = $chainFactory; + $this->filesystem = $filesystem; } /** @@ -125,7 +135,7 @@ class FileAssembler implements AppInterface $this->state->setAreaCode($this->params->getArea()); $this->objectManager->configure($this->configLoader->load($this->params->getArea())); - $sourceFileGenerator = $this->sourceFileGeneratorPoll->create($this->params->getExt()); + $sourceFileGenerator = $this->sourceFileGeneratorPool->create($this->params->getExt()); foreach ($this->params->getFiles() as $file) { $file .= '.' . $this->params->getExt(); @@ -152,7 +162,13 @@ class FileAssembler implements AppInterface ] ); - $sourceFileGenerator->generateFileTree($chain); + $processedCoreFile = $sourceFileGenerator->generateFileTree($chain); + + $targetDir = $this->filesystem->getDirectoryWrite(DirectoryList::STATIC_VIEW); + $rootDir = $this->filesystem->getDirectoryWrite(DirectoryList::ROOT); + $source = $rootDir->getRelativePath($processedCoreFile); + $destination = $asset->getPath(); + $rootDir->copyFile($source, $destination, $targetDir); $this->logger->logMessage("Done"); } diff --git a/dev/tools/Magento/Tools/Webdev/file_assembler.php b/dev/tools/Magento/Tools/Webdev/file_assembler.php index 72ec5b0458fdf12637221079aca0457872a60bb4..669ffa8d535856606b1a86ebbf5d0882aa99f353 100644 --- a/dev/tools/Magento/Tools/Webdev/file_assembler.php +++ b/dev/tools/Magento/Tools/Webdev/file_assembler.php @@ -35,15 +35,15 @@ try { $logger = new Log($params->getVerbose()); } catch (\Zend_Console_Getopt_Exception $e) { - echo $e->getUsageMessage(); - echo 'Please, use quotes(") for wrapping strings.' . "\n"; + echo $e->getMessage() . PHP_EOL; + echo 'Please, use quotes(") for wrapping strings.' . PHP_EOL; exit(1); } $bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $_SERVER); /** @var \Magento\Framework\App\Http $app */ $app = $bootstrap->createApplication( - 'Magento\Tools\WebDev\App\FileAssembler', + 'Magento\Tools\Webdev\App\FileAssembler', ['params' => $params, 'logger' => $logger] ); $bootstrap->run($app); diff --git a/lib/internal/Magento/Framework/Less/Config.php b/lib/internal/Magento/Framework/Less/Config.php new file mode 100644 index 0000000000000000000000000000000000000000..88f6c27e9ee412d67f4c414f5165e449ed42fa14 --- /dev/null +++ b/lib/internal/Magento/Framework/Less/Config.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\Less; + +use Magento\Framework\App\Filesystem\DirectoryList; + +class Config +{ + /** + * Temporary directory prefix + */ + const TMP_LESS_DIR = 'less'; + + /** + * Returns relative path to less materialization directory + * + * @return string + */ + public function getLessMaterializationRelativePath() + { + return DirectoryList::TMP_MATERIALIZATION_DIR . '/' . self::TMP_LESS_DIR; + } +} diff --git a/lib/internal/Magento/Framework/Less/File/Temporary.php b/lib/internal/Magento/Framework/Less/File/Temporary.php new file mode 100644 index 0000000000000000000000000000000000000000..8da016ecde585d76b509d3e4f74e3ca9069de16a --- /dev/null +++ b/lib/internal/Magento/Framework/Less/File/Temporary.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\Less\File; + +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Filesystem; +use Magento\Framework\Less\Config; + +class Temporary +{ + /** + * @var Config + */ + private $config; + + /** + * @var Filesystem\Directory\WriteInterface + */ + private $tmpDirectory; + + /** + * @param Filesystem $filesystem + * @param Config $config + */ + public function __construct( + Filesystem $filesystem, + Config $config + ) { + $this->tmpDirectory = $filesystem->getDirectoryWrite(DirectoryList::VAR_DIR); + $this->config = $config; + } + + /** + * Write down contents to a temporary file and return its absolute path + * + * @param string $relativePath + * @param string $contents + * @return string + */ + public function createFile($relativePath, $contents) + { + $filePath = $this->config->getLessMaterializationRelativePath() . '/' . $relativePath; + + if (!$this->tmpDirectory->isExist($filePath)) { + $this->tmpDirectory->writeFile($filePath, $contents); + } + return $this->tmpDirectory->getAbsolutePath($filePath); + } +} diff --git a/lib/internal/Magento/Framework/Less/FileGenerator.php b/lib/internal/Magento/Framework/Less/FileGenerator.php index f45d462e7eaaeeff23b95633c658bf40d526f650..ab7141d29dcc196d32324778d6e8df1b114fc747 100644 --- a/lib/internal/Magento/Framework/Less/FileGenerator.php +++ b/lib/internal/Magento/Framework/Less/FileGenerator.php @@ -7,17 +7,16 @@ namespace Magento\Framework\Less; use Magento\Framework\App\Filesystem\DirectoryList; -use Magento\Framework\View\Asset\LocalInterface; use Magento\Framework\View\Asset\PreProcessor\Chain; use Magento\Framework\View\Asset\SourceFileGeneratorInterface; +/** + * Class FileGenerator + * @package Magento\Framework\Less + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class FileGenerator implements SourceFileGeneratorInterface { - /** - * Temporary directory prefix - */ - const TMP_LESS_DIR = 'less'; - /** * Max execution (locking) time for generation process (in seconds) */ @@ -28,11 +27,6 @@ class FileGenerator implements SourceFileGeneratorInterface */ const LOCK_FILE = 'less.lock'; - /** - * @var string - */ - protected $lessDirectory; - /** * @var \Magento\Framework\Filesystem\Directory\WriteInterface */ @@ -59,17 +53,29 @@ class FileGenerator implements SourceFileGeneratorInterface private $importProcessor; /** - * @var \Magento\Framework\App\View\Asset\Publisher + * @var FileGenerator\RelatedGenerator + */ + private $relatedGenerator; + + /** + * @var Config */ - private $publisher; + private $config; + + /** + * @var File\Temporary + */ + private $temporaryFile; /** * @param \Magento\Framework\Filesystem $filesystem * @param \Magento\Framework\View\Asset\Repository $assetRepo - * @param \Magento\Framework\Less\PreProcessor\Instruction\MagentoImport $magentoImportProcessor - * @param \Magento\Framework\Less\PreProcessor\Instruction\Import $importProcessor + * @param PreProcessor\Instruction\MagentoImport $magentoImportProcessor + * @param PreProcessor\Instruction\Import $importProcessor * @param \Magento\Framework\View\Asset\Source $assetSource - * @param \Magento\Framework\App\View\Asset\Publisher $publisher + * @param FileGenerator\RelatedGenerator $relatedGenerator + * @param Config $config + * @param File\Temporary $temporaryFile */ public function __construct( \Magento\Framework\Filesystem $filesystem, @@ -77,17 +83,19 @@ class FileGenerator implements SourceFileGeneratorInterface \Magento\Framework\Less\PreProcessor\Instruction\MagentoImport $magentoImportProcessor, \Magento\Framework\Less\PreProcessor\Instruction\Import $importProcessor, \Magento\Framework\View\Asset\Source $assetSource, - \Magento\Framework\App\View\Asset\Publisher $publisher + \Magento\Framework\Less\FileGenerator\RelatedGenerator $relatedGenerator, + Config $config, + File\Temporary $temporaryFile ) { $this->tmpDirectory = $filesystem->getDirectoryWrite(DirectoryList::VAR_DIR); - $this->pubDirectory = $filesystem->getDirectoryWrite(DirectoryList::PUB); - $this->lessDirectory = DirectoryList::TMP_MATERIALIZATION_DIR . '/' . self::TMP_LESS_DIR; $this->assetRepo = $assetRepo; $this->assetSource = $assetSource; $this->magentoImportProcessor = $magentoImportProcessor; $this->importProcessor = $importProcessor; - $this->publisher = $publisher; + $this->relatedGenerator = $relatedGenerator; + $this->config = $config; + $this->temporaryFile = $temporaryFile; } /** @@ -109,16 +117,14 @@ class FileGenerator implements SourceFileGeneratorInterface while ($this->isProcessLocked()) { sleep(1); } - $lockFilePath = $this->lessDirectory . '/' . self::LOCK_FILE; + $lockFilePath = $this->config->getLessMaterializationRelativePath() . '/' . self::LOCK_FILE; $this->tmpDirectory->writeFile($lockFilePath, time()); $this->magentoImportProcessor->process($chain); $this->importProcessor->process($chain); - $this->generateRelatedFiles(); + $this->relatedGenerator->generate($this->importProcessor); $lessRelativePath = preg_replace('#\.css$#', '.less', $chain->getAsset()->getPath()); - $tmpFilePath = $this->createFile($lessRelativePath, $chain->getContent()); - - $this->createFileMain($lessRelativePath, $chain->getContent()); + $tmpFilePath = $this->temporaryFile->createFile($lessRelativePath, $chain->getContent()); $this->tmpDirectory->delete($lockFilePath); return $tmpFilePath; @@ -131,7 +137,7 @@ class FileGenerator implements SourceFileGeneratorInterface */ protected function isProcessLocked() { - $lockFilePath = $this->lessDirectory . '/' . self::LOCK_FILE; + $lockFilePath = $this->config->getLessMaterializationRelativePath() . '/' . self::LOCK_FILE; if ($this->tmpDirectory->isExist($lockFilePath)) { $lockTime = time() - (int)$this->tmpDirectory->readFile($lockFilePath); if ($lockTime >= self::MAX_LOCK_TIME) { @@ -142,69 +148,4 @@ class FileGenerator implements SourceFileGeneratorInterface } return false; } - - /** - * Create all asset files, referenced from already processed ones - * - * @return void - */ - protected function generateRelatedFiles() - { - do { - $relatedFiles = $this->importProcessor->getRelatedFiles(); - $this->importProcessor->resetRelatedFiles(); - foreach ($relatedFiles as $relatedFileInfo) { - list($relatedFileId, $asset) = $relatedFileInfo; - $this->generateRelatedFile($relatedFileId, $asset); - } - } while ($relatedFiles); - } - - /** - * Create file, referenced relatively to an asset - * - * @param string $relatedFileId - * @param LocalInterface $asset - * @return void - */ - protected function generateRelatedFile($relatedFileId, LocalInterface $asset) - { - $relatedAsset = $this->assetRepo->createRelated($relatedFileId, $asset); - $relatedAsset->getFilePath(); - - $this->createFile($relatedAsset->getPath(), $relatedAsset->getContent()); - $relatedAsset->getSourceFile(); - $this->publisher->publish($relatedAsset); - } - - /** - * Write down contents to a temporary file and return its absolute path - * - * @param string $relativePath - * @param string $contents - * @return string - */ - private function createFile($relativePath, $contents) - { - $filePath = $this->lessDirectory . '/' . $relativePath; - - if (!$this->tmpDirectory->isExist($filePath)) { - $this->tmpDirectory->writeFile($filePath, $contents); - } - return $this->tmpDirectory->getAbsolutePath($filePath); - } - - /** - * @param string $relativePath - * @param string $contents - * - * @return void - */ - private function createFileMain($relativePath, $contents) - { - $filePath = '/static/' . $relativePath; - $contents .= '@urls-resolved: true;' . PHP_EOL . PHP_EOL; - $this->pubDirectory->writeFile($filePath, $contents); - return; - } } diff --git a/lib/internal/Magento/Framework/Less/FileGenerator/RelatedGenerator.php b/lib/internal/Magento/Framework/Less/FileGenerator/RelatedGenerator.php new file mode 100644 index 0000000000000000000000000000000000000000..64564ce4c0e9f3221710fe44a40bb316db4d7cf1 --- /dev/null +++ b/lib/internal/Magento/Framework/Less/FileGenerator/RelatedGenerator.php @@ -0,0 +1,79 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\Less\FileGenerator; + +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Less\PreProcessor\Instruction\Import; +use Magento\Framework\View\Asset\LocalInterface; + +class RelatedGenerator +{ + /** + * @var \Magento\Framework\Filesystem\Directory\WriteInterface + */ + protected $tmpDirectory; + + /** + * @var \Magento\Framework\View\Asset\Repository + */ + private $assetRepo; + + /** + * @var \Magento\Framework\Less\File\Temporary + */ + private $temporaryFile; + + /** + * @param \Magento\Framework\Filesystem $filesystem + * @param \Magento\Framework\View\Asset\Repository $assetRepo + * @param \Magento\Framework\Less\File\Temporary $temporaryFile + */ + public function __construct( + \Magento\Framework\Filesystem $filesystem, + \Magento\Framework\View\Asset\Repository $assetRepo, + \Magento\Framework\Less\File\Temporary $temporaryFile + ) { + $this->tmpDirectory = $filesystem->getDirectoryWrite(DirectoryList::VAR_DIR); + $this->assetRepo = $assetRepo; + + $this->temporaryFile = $temporaryFile; + } + + /** + * Create all asset files, referenced from already processed ones + * + * @param Import $importGenerator + * + * @return void + */ + public function generate(Import $importGenerator) + { + do { + $relatedFiles = $importGenerator->getRelatedFiles(); + $importGenerator->resetRelatedFiles(); + foreach ($relatedFiles as $relatedFileInfo) { + list($relatedFileId, $asset) = $relatedFileInfo; + + $this->generateRelatedFile($relatedFileId, $asset); + } + } while ($relatedFiles); + } + + /** + * Create file, referenced relatively to an asset + * + * @param string $relatedFileId + * @param LocalInterface $asset + * @return \Magento\Framework\View\Asset\File + */ + protected function generateRelatedFile($relatedFileId, LocalInterface $asset) + { + $relatedAsset = $this->assetRepo->createRelated($relatedFileId, $asset); + $this->temporaryFile->createFile($relatedAsset->getPath(), $relatedAsset->getContent()); + + return $relatedAsset; + } +} diff --git a/lib/internal/Magento/Framework/Less/Test/Unit/FileGeneratorTest.php b/lib/internal/Magento/Framework/Less/Test/Unit/FileGeneratorTest.php index 60293981b8f761c070b06930b9461640e90b450b..f5985fbd30f6042f10cc4bed359f95453521d678 100644 --- a/lib/internal/Magento/Framework/Less/Test/Unit/FileGeneratorTest.php +++ b/lib/internal/Magento/Framework/Less/Test/Unit/FileGeneratorTest.php @@ -4,8 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - namespace Magento\Framework\Less\Test\Unit; class FileGeneratorTest extends \PHPUnit_Framework_TestCase @@ -41,58 +39,105 @@ class FileGeneratorTest extends \PHPUnit_Framework_TestCase private $object; /** - * @var \Magento\Framework\App\View\Asset\Publisher|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Less\FileGenerator\RelatedGenerator|\PHPUnit_Framework_MockObject_MockObject + */ + private $relatedGenerator; + + /** + * @var \Magento\Framework\Less\Config|\PHPUnit_Framework_MockObject_MockObject */ - private $publisher; + private $config; + + /** + * @var \Magento\Framework\Less\File\Temporary|\PHPUnit_Framework_MockObject_MockObject + */ + private $temporaryFile; protected function setUp() { - $this->tmpDirectory = $this->getMockForAbstractClass('\Magento\Framework\Filesystem\Directory\WriteInterface'); - $this->rootDirectory = $this->getMockForAbstractClass('\Magento\Framework\Filesystem\Directory\ReadInterface'); + $this->tmpDirectory = $this->getMockForAbstractClass('Magento\Framework\Filesystem\Directory\WriteInterface'); + $this->rootDirectory = $this->getMockForAbstractClass('Magento\Framework\Filesystem\Directory\ReadInterface'); $this->rootDirectory->expects($this->any()) ->method('getRelativePath') ->will($this->returnArgument(0)); $this->rootDirectory->expects($this->any()) ->method('readFile') - ->will($this->returnCallback(function ($file) { - return "content of '$file'"; - })); + ->will( + $this->returnCallback( + function ($file) { + return "content of '$file'"; + } + ) + ); $filesystem = $this->getMock('\Magento\Framework\Filesystem', [], [], '', false); - $filesystem->expects($this->exactly(2)) + $filesystem->expects($this->once()) ->method('getDirectoryWrite') - //->with(DirectoryList::VAR_DIR) ->will($this->returnValue($this->tmpDirectory)); $this->assetRepo = $this->getMock('\Magento\Framework\View\Asset\Repository', [], [], '', false); $this->magentoImport = $this->getMock( - '\Magento\Framework\Less\PreProcessor\Instruction\MagentoImport', [], [], '', false + 'Magento\Framework\Less\PreProcessor\Instruction\MagentoImport', + [], + [], + '', + false ); $this->import = $this->getMock( - '\Magento\Framework\Less\PreProcessor\Instruction\Import', [], [], '', false + 'Magento\Framework\Less\PreProcessor\Instruction\Import', + [], + [], + '', + false ); $assetSource = $this->getMock( - 'Magento\Framework\View\Asset\Source', [], [], '', false + 'Magento\Framework\View\Asset\Source', + [], + [], + '', + false ); - $this->publisher = $this->getMock('Magento\Framework\App\View\Asset\Publisher', [], [], '', false); - + $this->relatedGenerator = $this->getMockBuilder('Magento\Framework\Less\FileGenerator\RelatedGenerator') + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); + $this->config = $this->getMockBuilder('Magento\Framework\Less\Config') + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); + $this->temporaryFile = $this->getMockBuilder('Magento\Framework\Less\File\Temporary') + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); $this->object = new \Magento\Framework\Less\FileGenerator( - $filesystem, $this->assetRepo, $this->magentoImport, $this->import, $assetSource, $this->publisher + $filesystem, + $this->assetRepo, + $this->magentoImport, + $this->import, + $assetSource, + $this->relatedGenerator, + $this->config, + $this->temporaryFile ); } public function testGenerateLessFileTree() { - $originalContent = 'original content'; + $lessDirectory = 'path/to/less'; $expectedContent = 'updated content'; - $expectedRelativePath = 'view_preprocessed/less/some/file.less'; - $expectedPath = '/var/view_preprocessed/less/some/file.less'; + $expectedRelativePath = 'some/file.less'; + $expectedPath = $lessDirectory . '/some/file.less'; - $asset = $this->getMock('\Magento\Framework\View\Asset\File', [], [], '', false); - $asset->expects($this->exactly(2)) - ->method('getPath') - ->will($this->returnValue('some/file.css')); - $chain = new \Magento\Framework\View\Asset\PreProcessor\Chain($asset, $originalContent, 'less'); + + $asset = $this->getMock('Magento\Framework\View\Asset\File', [], [], '', false); + $chain = $this->getMock('Magento\Framework\View\Asset\PreProcessor\Chain', [], [], '', false); + + $this->config->expects($this->any()) + ->method('getLessDirectory') + ->willReturn($lessDirectory); + $this->tmpDirectory->expects($this->once()) + ->method('isExist') + ->willReturn(true); $this->magentoImport->expects($this->once()) ->method('process') @@ -100,59 +145,28 @@ class FileGeneratorTest extends \PHPUnit_Framework_TestCase $this->import->expects($this->once()) ->method('process') ->with($chain); + $this->relatedGenerator->expects($this->once()) + ->method('generate') + ->with($this->import); - $relatedAssetOne = $this->getMock('\Magento\Framework\View\Asset\File', [], [], '', false); - $relatedAssetOne->expects($this->any()) + $asset->expects($this->once()) ->method('getPath') - ->will($this->returnValue('related/file_one.css')); - $relatedAssetOne->expects($this->any()) - ->method('getContent') - ->will($this->returnValue("content of 'related/file_one.css'")); - $relatedAssetTwo = $this->getMock('\Magento\Framework\View\Asset\File', [], [], '', false); - $relatedAssetTwo->expects($this->any()) - ->method('getPath') - ->will($this->returnValue('related/file_two.css')); - $relatedAssetTwo->expects($this->any()) + ->will($this->returnValue('some/file.css')); + $chain->expects($this->once()) ->method('getContent') - ->will($this->returnValue("content of 'related/file_two.css'")); - $assetsMap = [ - ['related/file_one.css', $asset, $relatedAssetOne], - ['related/file_two.css', $asset, $relatedAssetTwo], - ]; - $this->assetRepo->expects($this->any()) - ->method('createRelated') - ->will($this->returnValueMap($assetsMap)); - - $relatedFilesOne = [['related/file_one.css', $asset]]; - $this->import->expects($this->at(1)) - ->method('getRelatedFiles') - ->will($this->returnValue($relatedFilesOne)); - $relatedFilesTwo = [['related/file_two.css', $asset]]; - $this->import->expects($this->at(3)) - ->method('getRelatedFiles') - ->will($this->returnValue($relatedFilesTwo)); - $this->import->expects($this->at(5)) - ->method('getRelatedFiles') - ->will($this->returnValue([])); - - $writeMap = [ - [$expectedRelativePath, $expectedContent], - ['related/file_one.css', "content of 'related/file_one.css'"], - ['related/file_two.css', "content of 'related/file_two.css'"], - ]; - $pathsMap = [ - [$expectedRelativePath, $expectedPath], - ['related/file_one.css', '/var/view_preprocessed/less/related/file_one.css'], - ['related/file_two.css', '/var/view_preprocessed/less/related/file_two.css'], - ]; - $this->tmpDirectory->expects($this->any()) - ->method('writeFile') - ->will($this->returnValueMap($writeMap)); - $this->tmpDirectory->expects($this->any()) - ->method('getAbsolutePath') - ->will($this->returnValueMap($pathsMap)); - - $actual = $this->object->generateFileTree($chain); - $this->assertSame($expectedPath, $actual); + ->willReturn($expectedContent); + $chain->expects($this->once()) + ->method('getAsset') + ->willReturn($asset); + + $this->temporaryFile->expects($this->once()) + ->method('createFile') + ->with( + $expectedRelativePath, + $expectedContent + ) + ->willReturn($expectedPath); + + $this->assertSame($expectedPath, $this->object->generateFileTree($chain)); } } diff --git a/lib/web/mage/backend/notification.js b/lib/web/mage/backend/notification.js index eb688214155efc8314dabc25cba88f44bfd839e8..7232e0310cddd185b88cbf5128cbf2502ba0172a 100644 --- a/lib/web/mage/backend/notification.js +++ b/lib/web/mage/backend/notification.js @@ -13,7 +13,7 @@ define([ $.widget('mage.notification', { options: { templates: { - global: '<div class="messages"><div class="message <%- if (data.error) { %>error<% } %>"><div><%- data.message %></div></div></div>' + global: '<div class="messages"><div class="message <% if (data.error) { %>error<% } %>"><div><%- data.message %></div></div></div>' } }, diff --git a/lib/web/mage/backend/suggest.js b/lib/web/mage/backend/suggest.js index 70dd802284562e8d43d54be0e28b5d38f7e27ac2..dfb3ef72d8c4d8424c949ea44dfba20b3a8f01be 100644 --- a/lib/web/mage/backend/suggest.js +++ b/lib/web/mage/backend/suggest.js @@ -811,15 +811,19 @@ * @private */ _renderMultiselect: function () { + var that = this; this.element.wrap(this.options.multiSuggestWrapper); this.elementWrapper = this.element.closest('[data-role="parent-choice-element"]'); - this._getOptions().each($.proxy(function (i, option) { - option = $(option); - this._createOption({ - id: option.val(), - label: option.text() - }); - }, this)); + $(function () { + that._getOptions() + .each(function (i, option) { + option = $(option); + that._createOption({ + id: option.val(), + label: option.text() + }); + }); + }); }, /** diff --git a/lib/web/mage/validation.js b/lib/web/mage/validation.js index 7632ce9b84977587975da37348f383f39395c89e..fbcf394becd3215ce0484cbefb6ddb585879c73c 100644 --- a/lib/web/mage/validation.js +++ b/lib/web/mage/validation.js @@ -916,10 +916,10 @@ 'This is a required field.' ], "validate-one-required-by-name": [ - function(v, elm) { + function(v, elm, selector) { var name = elm.name.replace(/([\\"])/g, '\\$1'), container = this.currentForm, - selector = 'input[name="' + name + '"]:checked'; + selector = selector === true ? 'input[name="' + name + '"]:checked' : selector; return !!container.querySelectorAll(selector).length; }, diff --git a/nginx.conf.sample b/nginx.conf.sample index 1ea8ee9e97f28f40107edb14b4fe53e66589b40a..2ea2b6ce5848867deae4c3c6287234706039e57f 100644 --- a/nginx.conf.sample +++ b/nginx.conf.sample @@ -127,6 +127,7 @@ location / { } location ~ (index|get|static|report|404|503)\.php$ { + expires -1; fastcgi_pass fastcgi_backend; fastcgi_param PHP_FLAG "session.auto_start=off \n suhosin.session.cryptua=off"; diff --git a/pub/.htaccess b/pub/.htaccess index b3f9b82f3723b106829e136d1609134ed4fdc196..7cac070b85bf7b33c187cfcefd3bf6e8e456306b 100755 --- a/pub/.htaccess +++ b/pub/.htaccess @@ -1,3 +1,8 @@ +############################################ +## uncomment the line below to enable developer mode + +# SetEnv MAGE_MODE developer + ############################################ ## uncomment these lines for CGI mode ## make sure to specify the correct cgi php binary file name @@ -153,6 +158,8 @@ ## http://developer.yahoo.com/performance/rules.html#expires ExpiresDefault "access plus 1 year" + ExpiresByType text/html A0 + ExpiresByType text/plain A0 </IfModule>