diff --git a/.htaccess b/.htaccess index 7684da23781d2f73e36324ef2a99c1277b059f6b..404488eb7ff0ecc6197ab8c100555c79eb1cf0ce 100644 --- a/.htaccess +++ b/.htaccess @@ -172,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 f3c9f68f514fb71bfb6062b230e899aef43a3769..133ce7de2c59badd5df4a159bb921e8a99f05eb7 100644 --- a/.htaccess.sample +++ b/.htaccess.sample @@ -169,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 35f0c1d6ab2170576b1bc61d98f10209a7a1c372..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,8 @@ class Observer $now = $this->timezone->scopeTimeStamp(); /** @var Schedule $record */ foreach ($history as $record) { - $checkTime = strtotime($record->getExecutedAt() ? $record->getExecutedAt() : $record->getScheduledAt()); + $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/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/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/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 445b6c50516f866adea5465226272816985e320f..7cac070b85bf7b33c187cfcefd3bf6e8e456306b 100755 --- a/pub/.htaccess +++ b/pub/.htaccess @@ -158,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>